记账本
环境准备

步骤

创建项目
npx-create-react-app react-bill-test
切换到此文件夹内
npm i @reduxjs/toolkit react-redux react-router-dom dayjs classnames antd-mobile axios
删除没必要的文件,删除不需要的代码




启动项目
npm run start
成功

提交本地git仓库

配置别名路径@

路径解析配置

安装craco
npm i -D @craco/craco
根目录创建craco.config.js
const path = require('path');
module.exports={
webpack: {
alias: {
'@': path.resolve(__dirname, 'src')
},
}
}
更改启动命令

联想路径配置

根目录创建jsconfig.json文件
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": [
"src/*"
]
}
}
}
测试一下



启动一下没问题

数据Mock实现

步骤

安装依赖
npm i -D json-server
创建data.json文件

{
"ka":[
{
"type": "pay",
"money": -100,
"date": "2022-01-01 10:00:00",
"useFor": "drinks",
"id": 1
},
{
"type": "pay",
"money": 80,
"date": "2022-01-01 10:30:00",
"useFor": "iong",
"id": 1
},
{
"type": "pay",
"money": -9,
"date": "2022-01-31 8:00:00",
"useFor": "desserts",
"id": 1
},
{
"type": "income",
"money": -10,
"date": "2022-01-31 8:30:00",
"useFor": "drinks",
"id": 1
}
]
}
添加启动命令

"server": "json-server ./server/data.json --port 8888"
启动服务


提交本地git

整体路由设计
创建页面

都为
const xxx = () => {
return <div>xxx</div>;
}
export default xxx;
//xxx更换为每个文件对应页面名字
创建router.js文件

import Layout from "@/pages/Layout";
import Month from "@/pages/Month";
import New from "@/pages/New";
import Year from "@/pages/Year";
import { createBrowserRouter } from "react-router-dom";
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{
path: "/",
element: <Month />
},
{
path: "/year",
element: <Year />
}
]
},
{
path: "/new",
element: <New />
},
])
export default router;
引入路由文件

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import { RouterProvider } from 'react-router-dom';
import router from './router';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<RouterProvider router={router} />
);
添加子路由出口

运行,路由正常切换


git提交

antD-mobile主题定制
官网路径https://mobile.ant.design/zh/
主题https://mobile.ant.design/zh/guide/theming
因为我前面已经下载过依赖了,所以这里不需要下载了
创建css文件配置全局主题

:root:root {
--adm-color-primary: #a062d4;
}
引入主题文件

应用
import { Outlet } from "react-router-dom";
import { Button } from "antd-mobile";
const Layout = () => {
return (
<div>
<Outlet />
{/* 测试 */}
<Button color="primary">
测试全局样式
</Button>
我是layout页面
</div>
);
}
export default Layout;


补充:局部样式这样引用


我们的主题色是

换一下主题色完成

Redux管理账目列表
创建store文件

在billSore.js文件内配置action
//账单相关store
import { createSlice } from "@reduxjs/toolkit";
import axios from "axios";
const billStore = createSlice({
name: "billStore",
initialState: {
billList: [],
},
reducers: {
//同步修改方法
setBillList(state, action) {
state.billList = action.payload;
}
}
})
const { setBillList } = billStore.actions;
//编写异步
const getBillList=()=> {
return async (dispatch)=>{
const res = await axios.get("http://localhost:8888/ka");
dispatch(setBillList(res.data));
}
}
export { getBillList };
const reducer = billStore.reducer;
export default reducer;
在store主文件里引入

//组合子模块 导出store实例
import { configureStore } from "@reduxjs/toolkit";
import billReducer from "@/store/modules/billStore";
const store = configureStore({
reducer: {
billStore: billReducer,
},
});
export default store;
在主引入文件引入store

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import { RouterProvider } from 'react-router-dom';
import router from './router';
import store from './store';
import { Provider } from 'react-redux';
//导入定制主题文件
import '@/theme.css';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<RouterProvider router={router} />
</Provider>
);
通过action获取数据
import { Outlet } from "react-router-dom";
import { Button } from "antd-mobile";
import { useEffect } from "react";
import { useDispatch } from "react-redux";
import { getBillList } from "@/store/modules/billStore";
const Layout = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(getBillList());
}, [dispatch]);
return (
<div>
<Outlet />
{/* 测试 */}
<Button color="primary">
测试全局样式
</Button>
我是layout页面
</div>
}
export default Layout;
获取成功

TabBar功能实现
静态引入

import { TabBar } from "antd-mobile"
import { useEffect } from "react"
import { Outlet } from "react-router-dom"
import { useDispatch } from 'react-redux'
import { getBillList } from "@/store/modules/billStore"
import './index.scss'
import {
BillOutline,
CalculatorOutline,
AddCircleOutline
} from 'antd-mobile-icons'
const tabs = [
{
key: '/month',
title: '月度账单',
icon: <BillOutline />,
},
{
key: '/new',
title: '记账',
icon: <AddCircleOutline />,
},
{
key: '/year',
title: '年度账单',
icon: <CalculatorOutline />,
},
]
const Layout = () => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(getBillList())
}, [dispatch])
return (
<div className="layout">
<div className="container">
<Outlet />
</div>
<div className="footer">
<TabBar>
{tabs.map(item => (
<TabBar.Item key={item.key} icon={item.icon} title={item.title} />
))}
</TabBar>
</div>
</div>
)
}
export default Layout
引入scss依赖和样式
npm i -D sass

.layout {
.container {
position: fixed;
top: 0;
bottom: 50px;
}
.footer {
position: fixed;
bottom: 0;
width: 100%;
}
}

切换路由实现
layout文件
const Layout = () => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(getBillList())
}, [dispatch])
const navigate = useNavigate();
const switchRoute=(path)=>{
navigate(path)
}
return (
<div className="layout">
<div className="container">
<Outlet />
</div>
<div className="footer">
<TabBar onChange={switchRoute}>
{tabs.map(item => (
<TabBar.Item key={item.key} icon={item.icon} title={item.title} />
))}
</TabBar>
</div>
</div>
)
}
全部代码
import { TabBar } from "antd-mobile"
import { useEffect } from "react"
import { Outlet, useNavigate } from "react-router-dom"
import { useDispatch } from 'react-redux'
import { getBillList } from "@/store/modules/billStore"
import './index.scss'
import {
BillOutline,
CalculatorOutline,
AddCircleOutline
} from 'antd-mobile-icons'
const tabs = [
{
key: '/month',
title: '月度账单',
icon: <BillOutline />,
},
{
key: '/new',
title: '记账',
icon: <AddCircleOutline />,
},
{
key: '/year',
title: '年度账单',
icon: <CalculatorOutline />,
},
]
const Layout = () => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(getBillList())
}, [dispatch])
const navigate = useNavigate();
const switchRoute=(path)=>{
navigate(path)
}
return (
<div className="layout">
<div className="container">
<Outlet />
</div>
<div className="footer">
<TabBar onChange={switchRoute}>
{tabs.map(item => (
<TabBar.Item key={item.key} icon={item.icon} title={item.title} />
))}
</TabBar>
</div>
</div>
)
}
export default Layout
完成
