diff --git a/nkebao/package.json b/nkebao/package.json index cfa0f01b..f9f4ac6e 100644 --- a/nkebao/package.json +++ b/nkebao/package.json @@ -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", diff --git a/nkebao/src/App.tsx b/nkebao/src/App.tsx index bb423eff..4437b140 100644 --- a/nkebao/src/App.tsx +++ b/nkebao/src/App.tsx @@ -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 ( - - - } /> - } /> - + <> +
Antd-Mobile 按钮
-
+ ); } diff --git a/nkebao/src/api/request.ts b/nkebao/src/api/request.ts index aaa2dca1..bd7a086a 100644 --- a/nkebao/src/api/request.ts +++ b/nkebao/src/api/request.ts @@ -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(); @@ -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); diff --git a/nkebao/src/router/index.tsx b/nkebao/src/router/index.tsx new file mode 100644 index 00000000..0c58db7e --- /dev/null +++ b/nkebao/src/router/index.tsx @@ -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: ( + + {route.element} + + ), + }; + } + return route; +} + +const routes = [ + ...homeRoutes.map(wrapWithPermission), + ...aboutRoutes.map(wrapWithPermission), +]; + +const AppRoutes = () => useRoutes(routes); + +const AppRouter: React.FC = () => ( + + + +); + +export default AppRouter; diff --git a/nkebao/src/router/module/about.tsx b/nkebao/src/router/module/about.tsx new file mode 100644 index 00000000..d76d7e08 --- /dev/null +++ b/nkebao/src/router/module/about.tsx @@ -0,0 +1,10 @@ +import About from "@/pages/About"; + +const aboutRoutes = [ + { + path: "/about", + element: , + }, +]; + +export default aboutRoutes; diff --git a/nkebao/src/router/module/home.tsx b/nkebao/src/router/module/home.tsx new file mode 100644 index 00000000..a4c5247a --- /dev/null +++ b/nkebao/src/router/module/home.tsx @@ -0,0 +1,17 @@ +import Home from "@/pages/Home"; + +const homeRoutes = [ + { + path: "/", + element: , + auth: false, // 不需要权限 + }, + { + path: "/dashboard", + element: , + auth: true, // 需要登录 + requiredRole: "admin", // 需要 admin 角色 + }, +]; + +export default homeRoutes; diff --git a/nkebao/src/router/permissionRoute.tsx b/nkebao/src/router/permissionRoute.tsx new file mode 100644 index 00000000..66b0b3dc --- /dev/null +++ b/nkebao/src/router/permissionRoute.tsx @@ -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 = ({ children, requiredRole }) => { + const user = useUserStore((state) => state.user); + const location = useLocation(); + + // 未登录 + if (!user) { + return ; + } + + // 有角色要求但不满足 + if (requiredRole && user.role !== requiredRole) { + return ; + } + + // 通过 + return <>{children}; +}; + +export default PermissionRoute; diff --git a/nkebao/src/store/index.ts b/nkebao/src/store/index.ts new file mode 100644 index 00000000..8edf9c6e --- /dev/null +++ b/nkebao/src/store/index.ts @@ -0,0 +1,2 @@ +export * from './module/user'; +// 未来可继续合并其他模块 \ No newline at end of file diff --git a/nkebao/src/store/module/user.ts b/nkebao/src/store/module/user.ts new file mode 100644 index 00000000..63202e04 --- /dev/null +++ b/nkebao/src/store/module/user.ts @@ -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((set) => ({ + user: null, + setUser: (user) => set({ user }), + clearUser: () => set({ user: null }), +})); \ No newline at end of file