feat: 本次提交更新内容如下

store 和 router 封装完毕
This commit is contained in:
笔记本里的永平
2025-07-17 22:19:37 +08:00
parent 6a4e99885d
commit 9d18af22f1
9 changed files with 137 additions and 14 deletions

View File

@@ -8,7 +8,8 @@
"axios": "^1.6.7",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.20.0"
"react-router-dom": "^6.20.0",
"zustand": "^5.0.6"
},
"devDependencies": {
"@types/node": "^24.0.14",

View File

@@ -1,22 +1,17 @@
import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import AppRouter from "@/router";
import { Button } from "antd";
import { Button as MobileButton } from "antd-mobile";
import Home from "./pages/Home";
import About from "./pages/About";
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
<>
<AppRouter />
<div style={{ margin: 16 }}>
<Button type="primary">Antd </Button>
<MobileButton color="primary">Antd-Mobile </MobileButton>
</div>
</BrowserRouter>
</>
);
}

View File

@@ -1,4 +1,5 @@
import axios, { AxiosInstance, AxiosRequestConfig, Method, AxiosResponse } from 'axios';
import { Toast } from 'antd-mobile';
const DEBOUNCE_GAP = 1000;
const debounceMap = new Map<string, number>();
@@ -22,14 +23,28 @@ instance.interceptors.request.use(config => {
instance.interceptors.response.use(
(res: AxiosResponse) => {
if (res.data && (res.data.code === 200 || res.data.success)) {
const { code, success, msg } = res.data || {};
if (code === 200 || success) {
return res.data.data ?? res.data;
}
window?.alert?.(res.data?.msg || '接口错误');
return Promise.reject(res.data?.msg || '接口错误');
// 业务错误统一提示
Toast.show({ content: msg || '接口错误', position: 'top' });
// 分类处理
if (code === 401) {
// 未登录或登录失效
// 可跳转登录页或清除 token
localStorage.removeItem('token');
} else if (code === 403) {
// 无权限
// 可做特殊处理
} else if (code === 500) {
// 服务端异常
}
return Promise.reject(msg || '接口错误');
},
err => {
window?.alert?.(err.message || '网络异常');
// 网络错误、超时等
Toast.show({ content: err.message || '网络异常', position: 'top' });
return Promise.reject(err);
}
);
@@ -44,6 +59,7 @@ export function request(
const now = Date.now();
const last = debounceMap.get(key) || 0;
if (now - last < DEBOUNCE_GAP) {
Toast.show({ content: '请求过于频繁,请稍后再试', position: 'top' });
return Promise.reject('请求过于频繁,请稍后再试');
}
debounceMap.set(key, now);

View File

@@ -0,0 +1,34 @@
import React from "react";
import { BrowserRouter, useRoutes } from "react-router-dom";
import homeRoutes from "./module/home";
import aboutRoutes from "./module/about";
import PermissionRoute from "./permissionRoute";
function wrapWithPermission(route) {
if (route.auth) {
return {
...route,
element: (
<PermissionRoute requiredRole={route.requiredRole}>
{route.element}
</PermissionRoute>
),
};
}
return route;
}
const routes = [
...homeRoutes.map(wrapWithPermission),
...aboutRoutes.map(wrapWithPermission),
];
const AppRoutes = () => useRoutes(routes);
const AppRouter: React.FC = () => (
<BrowserRouter>
<AppRoutes />
</BrowserRouter>
);
export default AppRouter;

View File

@@ -0,0 +1,10 @@
import About from "@/pages/About";
const aboutRoutes = [
{
path: "/about",
element: <About />,
},
];
export default aboutRoutes;

View File

@@ -0,0 +1,17 @@
import Home from "@/pages/Home";
const homeRoutes = [
{
path: "/",
element: <Home />,
auth: false, // 不需要权限
},
{
path: "/dashboard",
element: <Dashboard />,
auth: true, // 需要登录
requiredRole: "admin", // 需要 admin 角色
},
];
export default homeRoutes;

View File

@@ -0,0 +1,28 @@
import React from "react";
import { Navigate, useLocation } from "react-router-dom";
import { useUserStore } from "@/store/module/user"; // 假设你用 zustand 管理用户状态
interface Props {
children: React.ReactNode;
requiredRole?: string; // 可选:需要的角色
}
const PermissionRoute: React.FC<Props> = ({ children, requiredRole }) => {
const user = useUserStore((state) => state.user);
const location = useLocation();
// 未登录
if (!user) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
// 有角色要求但不满足
if (requiredRole && user.role !== requiredRole) {
return <Navigate to="/403" replace />;
}
// 通过
return <>{children}</>;
};
export default PermissionRoute;

View File

@@ -0,0 +1,2 @@
export * from './module/user';
// 未来可继续合并其他模块

View File

@@ -0,0 +1,20 @@
// src/store/user.ts
import { create } from 'zustand';
export interface User {
name: string;
role: string;
token: string;
}
interface UserState {
user: User | null;
setUser: (user: User) => void;
clearUser: () => void;
}
export const useUserStore = create<UserState>((set) => ({
user: null,
setUser: (user) => set({ user }),
clearUser: () => set({ user: null }),
}));