# GoalfyMax Admin Web 前端项目探索指南 ## 项目概览 **项目路径**: `/Users/youziba/goalfyagent/goalfymax-admin-web` **技术栈**: - **框架**: React 18.3.1 - **路由**: React Router DOM 7.9.4 - **UI库**: Ant Design 5.27.4 - **状态管理**: Jotai 2.15.0 - **构建工具**: Vite 5.4.20 - **HTTP客户端**: Axios 1.12.2 - **语言**: TypeScript **项目结构**: ``` src/ ├── pages/ # 页面组件 ├── components/ # 通用组件 ├── services/ # API 服务层 ├── types/ # TypeScript 类型定义 ├── hooks/ # 自定义 hooks ├── atoms/ # Jotai 状态管理 ├── routes/ # 路由配置 ├── assets/ # 静态资源 ├── utils/ # 工具函数 ├── App.tsx # 主应用入口 └── main.tsx # 应用启动文件 ``` --- ## 1. 前端项目框架 ### 框架:React 18.3.1 + TypeScript **主入口文件**: `/Users/youziba/goalfyagent/goalfymax-admin-web/src/main.tsx` ```typescript import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import { Provider } from 'jotai' import { BrowserRouter } from 'react-router-dom' import './App.css' import 'antd/dist/reset.css' import App from './App.tsx' import AuthGuard from './components/AuthGuard' const root = createRoot(document.getElementById('root')!) root.render( ) ``` **关键特点**: - 使用 Jotai 进行全局状态管理 - React Router 管理路由 - AuthGuard 包裹整个应用,确保认证 - Ant Design 提供 UI 组件 --- ## 2. 现有配置管理页面实现 ### 用户等级配置页面 (参考实现) **文件路径**: `/Users/youziba/goalfyagent/goalfymax-admin-web/src/pages/UserLevelConfigs.tsx` **文件大小**: 283 行 **页面功能**: - 列表展示(分页) - 创建新配置 - 编辑配置 - 删除配置 - 切换状态(启用/禁用) **关键代码片段**: ```typescript import React, { useEffect, useState } from 'react'; import { Table, Button, Modal, Form, Input, InputNumber, Tag, Space, Popconfirm, message, Row, Col, Card, } from 'antd'; import { PlusOutlined, EditOutlined, DeleteOutlined, CheckCircleOutlined, StopOutlined, } from '@ant-design/icons'; export default function UserLevelConfigs() { const [loading, setLoading] = useState(false); const [list, setList] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [size, setSize] = useState(10); const [editOpen, setEditOpen] = useState(false); const [editing, setEditing] = useState(null); const [createOpen, setCreateOpen] = useState(false); const [form] = Form.useForm(); const [createForm] = Form.useForm(); // 获取列表 const fetchList = async () => { setLoading(true); try { const res = await getUserLevelConfigList({ page, size }); setList(res?.data ?? []); setTotal(res?.total ?? 0); } catch (e) { message.error('获取列表失败'); } finally { setLoading(false); } }; useEffect(() => { fetchList(); }, [page, size]); // 打开编辑弹窗 const openEdit = (config: UserLevelConfig) => { setEditing(config); form.setFieldsValue({ level_name: config.level_name, project_limit: config.project_limit, description: config.description, sort_order: config.sort_order, }); setEditOpen(true); }; // 提交编辑 const submitEdit = async () => { try { const values = await form.validateFields(); if (!editing) return; await updateUserLevelConfig(editing.id, values); message.success('更新成功'); setEditOpen(false); fetchList(); } catch (error) { message.error('更新失败'); } }; // 创建配置 const submitCreate = async () => { try { const values = await createForm.validateFields(); await createUserLevelConfig(values); message.success('创建成功'); setCreateOpen(false); fetchList(); } catch (error: any) { message.error(error?.response?.data?.message || '创建失败'); } }; // 删除配置 const handleDelete = async (config: UserLevelConfig) => { try { await deleteUserLevelConfig(config.id); message.success('删除成功'); fetchList(); } catch (error) { message.error('删除失败'); } }; // 切换状态 const handleToggleStatus = async (config: UserLevelConfig) => { try { const newStatus = config.status === 1 ? 0 : 1; await updateUserLevelConfigStatus(config.id, { status: newStatus }); message.success(newStatus === 1 ? '已启用' : '已禁用'); fetchList(); } catch (error) { message.error('状态更新失败'); } }; // 定义表格列 const columns = [ { title: '等级名称', dataIndex: 'level_name', key: 'level_name', }, { title: '等级代码', dataIndex: 'level_code', key: 'level_code', }, { title: '项目数限制', dataIndex: 'project_limit', key: 'project_limit', render: (value: number) => (value === 0 ? '不限' : value), }, { title: '描述', dataIndex: 'description', key: 'description', }, { title: '排序', dataIndex: 'sort_order', key: 'sort_order', }, { title: '状态', dataIndex: 'status', key: 'status', render: (value: number) => value === 1 ? 启用 : 禁用, }, { title: '操作', key: 'action', render: (_: any, config: UserLevelConfig) => ( } onClick={() => openEdit(config)}> 编辑 : } onClick={() => handleToggleStatus(config)} > {config.status === 1 ? '禁用' : '启用'} handleDelete(config)}> }> 删除 ), }, ]; return ( } onClick={openCreate}> 新建等级配置 { setPage(p); setSize(s); }, }} /> {/* 编辑弹窗 */} setEditOpen(false)} > {/* 创建弹窗 */} setCreateOpen(false)} > ); } ``` --- ## 3. 路由配置 ### 路由配置方式 **主路由文件**: `/Users/youziba/goalfyagent/goalfymax-admin-web/src/App.tsx` ```typescript import React from 'react'; import { Routes, Route, Navigate } from 'react-router-dom'; import Layout from './components/Layout'; import Dashboard from './pages/Dashboard'; import Overview from './pages/Overview'; import Operations from './pages/Operations'; import Monitoring from './pages/Monitoring'; import Finance from './pages/Finance'; import TokenHistoryPage from './pages/TokenHistory'; import TokenAnalytics from './pages/TokenAnalytics'; import SystemHealth from './pages/SystemHealth'; import QuotaRulesPage from './pages/QuotaRules'; import UserProjectQuotaWrapper from './pages/UserProjectQuota'; import UserManagement from './pages/UserManagement'; import RoleManagement from './pages/RoleManagement'; import GoalfyMaxUsers from './pages/GoalfyMaxUsers'; import UserFeedback from './pages/UserFeedback'; import MessagePush from './pages/MessagePush'; import VendorModelPricing from './pages/VendorModelPricing'; function App() { return ( {/* 默认重定向到仪表盘 */} } /> {/* 仪表盘 */} } /> {/* 总览页面 */} } /> {/* 运营页面 - 嵌套路由 */} } /> } /> } /> } /> {/* 监控页面 - 嵌套路由 */} } /> } /> } /> } /> {/* 财务页面 */} } /> {/* 系统管理页面 - 嵌套路由 */} } /> } /> } /> } /> } /> } /> {/* 404页面 */} } /> ); } export default App; ``` **新页面的添加步骤**: 1. 在 `src/pages/` 目录下创建新页面组件 2. 在 `App.tsx` 中导入页面组件 3. 在 `Routes` 中添加 `` 配置 4. 如果需要在菜单中显示,更新 `Layout.tsx` 中的导航配置 --- ## 4. 菜单配置 ### 菜单配置方式 **主菜单文件**: `/Users/youziba/goalfyagent/goalfymax-admin-web/src/components/Layout.tsx` 菜单配置使用静态定义的方式,在 Layout 组件中硬编码: ```typescript // Layout.tsx 中的导航菜单代码(简化版) {/* 总览 */} {accessiblePages.includes('/overview') && ( { e.preventDefault(); navigate('/overview'); }} > ⬤ 总览 Overview )} {/* 运营 */} {accessiblePages.includes('/operations') && ( { e.preventDefault(); navigate('/operations'); }} > ⬤ 运营 Operations )} {/* 系统管理 */} {accessiblePages.includes('/system') && ( { e.preventDefault(); navigate('/system'); }} > ⬤ 系统管理 Admin )} ``` ### 子菜单配置(以系统管理为例) ```typescript // Layout.tsx 中的系统管理子菜单代码 {activeTab === 'admin' && ( navigate('/system/quota-rules')} > 配额/套餐 navigate('/system/user-project-quota')} > 用户项目配额 navigate('/system/user-management')} > 系统用户管理 navigate('/system/role-management')} > 角色管理 navigate('/system/goalfymax-users')} > GoalfyMax用户 )} ``` **菜单的权限控制**: 菜单项通过 `usePagePermissions` hook 获取用户权限,使用 `accessiblePages.includes()` 来判断是否显示: ```typescript import { usePagePermissions } from '../hooks/usePagePermissions'; const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => { const { getAccessiblePages } = usePagePermissions(); const accessiblePages = getAccessiblePages(); // 根据权限显示菜单项 if (!accessiblePages.includes('/overview')) { // 不显示总览菜单 } } ``` --- ## 5. API 调用的封装方式 ### API 客户端配置 **文件路径**: `/Users/youziba/goalfyagent/goalfymax-admin-web/src/services/api.ts` **关键特点**: - 使用 Axios 作为 HTTP 客户端 - 自动处理 Token 认证和刷新 - 支持请求/响应拦截 - 处理 401 错误自动刷新 Token ```typescript import axios from 'axios'; class ApiClient { private instance: any; private isRefreshing = false; private failedQueue: Array<{ resolve: (value: any) => void; reject: (reason: any) => void; }> = []; constructor(baseURL: string = '/api') { this.instance = axios.create({ baseURL, timeout: 60000, headers: { 'Content-Type': 'application/json', }, }); this.setupInterceptors(); } private setupInterceptors() { // 请求拦截器 - 添加 Authorization header this.instance.interceptors.request.use( config => { const token = localStorage.getItem('token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, error => { return Promise.reject(error); } ); // 响应拦截器 - 处理 401 错误和 Token 刷新 this.instance.interceptors.response.use( (response: any) => { return response; }, error => { if (error.response?.status === 401) { const originalRequest = error.config; if (originalRequest._retry) { // 重试失败,触发重新登录 const event = new CustomEvent('auth:relogin'); window.dispatchEvent(event); return Promise.reject(error); } if (this.isRefreshing) { // 如果正在刷新token,将请求加入队列 return new Promise((resolve, reject) => { this.failedQueue.push({ resolve, reject }); }) .then(token => { originalRequest.headers.Authorization = token; return this.instance(originalRequest); }) .catch(err => { return Promise.reject(err); }); } originalRequest._retry = true; this.isRefreshing = true; return this.handleTokenRefresh() .then(refreshResult => { const { access_token, refresh_token, expires_in } = refreshResult; this.processQueue(null, `Bearer ${access_token}`); originalRequest.headers.Authorization = `Bearer ${access_token}`; // 触发 token 更新事件 const event = new CustomEvent('auth:tokenRefreshed', { detail: { access_token, refresh_token, expires_in, }, }); window.dispatchEvent(event); return this.instance(originalRequest); }) .catch(refreshError => { this.processQueue(refreshError, null); // 刷新失败,重新登录 const event = new CustomEvent('auth:relogin'); window.dispatchEvent(event); return Promise.reject(refreshError); }) .finally(() => { this.isRefreshing = false; }); } return Promise.reject(error); } ); } // GET 请求 async get(url: string, config?: any): Promise { const response = await this.instance.get(url, config); return response.data; } // POST 请求 async post( url: string, data?: any, config?: any ): Promise { const response = await this.instance.post(url, data, config); return response.data; } // PUT 请求 async put( url: string, data?: any, config?: any ): Promise { const response = await this.instance.put(url, data, config); return response.data; } // DELETE 请求 async delete( url: string, data?: any, config?: any ): Promise { const response = await this.instance.delete(url, { ...config, data, }); return response.data; } } export const apiClient = new ApiClient(); ``` ### API 服务层示例 **文件路径**: `/Users/youziba/goalfyagent/goalfymax-admin-web/src/services/userLevelConfigApi.ts` ```typescript import { apiClient } from './api'; import type { UserLevelConfig, UserLevelConfigListRequest, UserLevelConfigListResponse, UserLevelConfigCreateRequest, UserLevelConfigUpdateRequest, UserLevelConfigStatusRequest, } from '../types/userLevelConfig'; // 获取用户等级配置列表 export const getUserLevelConfigList = async ( params: UserLevelConfigListRequest ): Promise => { const response = await apiClient.get('/admin/user-level-configs', { params }); return response.data; }; // 获取用户等级配置详情 export const getUserLevelConfigById = async (id: number): Promise => { const response = await apiClient.get(`/admin/user-level-configs/${id}`); return response.data; }; // 创建用户等级配置 export const createUserLevelConfig = async ( data: UserLevelConfigCreateRequest ): Promise => { const response = await apiClient.post('/admin/user-level-configs', data); return response.data; }; // 更新用户等级配置 export const updateUserLevelConfig = async ( id: number, data: UserLevelConfigUpdateRequest ): Promise => { const response = await apiClient.put(`/admin/user-level-configs/${id}`, data); return response.data; }; // 删除用户等级配置 export const deleteUserLevelConfig = async (id: number): Promise => { await apiClient.delete(`/admin/user-level-configs/${id}`); }; // 更新用户等级配置状态 export const updateUserLevelConfigStatus = async ( id: number, data: UserLevelConfigStatusRequest ): Promise => { await apiClient.put(`/admin/user-level-configs/${id}/status`, data); }; ``` **API 命名规范**: - 列表接口: `/admin/xxx` - 详情接口: `/admin/xxx/:id` - 创建接口: `POST /admin/xxx` - 更新接口: `PUT /admin/xxx/:id` - 删除接口: `DELETE /admin/xxx/:id` - 状态变更: `PUT /admin/xxx/:id/status` --- ## 6. 表单组件和表格组件的使用方式 ### Ant Design Form 组件使用 ```typescript import { Form, Input, InputNumber, Button, Modal } from 'antd'; // 基本表单使用 const [form] = Form.useForm(); const submitForm = async () => { try { const values = await form.validateFields(); // 处理表单数据 console.log(values); } catch (error) { console.error('表单验证失败:', error); } }; // 在 JSX 中使用 ``` ### Ant Design Table 组件使用 ```typescript import { Table, Button, Space, Tag } from 'antd'; import { EditOutlined, DeleteOutlined } from '@ant-design/icons'; // 定义表格列 const columns = [ { title: '等级名称', dataIndex: 'level_name', key: 'level_name', }, { title: '项目数限制', dataIndex: 'project_limit', key: 'project_limit', render: (value: number) => (value === 0 ? '不限' : value), }, { title: '状态', dataIndex: 'status', key: 'status', render: (value: number) => value === 1 ? 启用 : 禁用, }, { title: '操作', key: 'action', render: (_: any, record: UserLevelConfig) => ( } onClick={() => openEdit(record)}> 编辑 } onClick={() => handleDelete(record)}> 删除 ), }, ]; // 在 JSX 中使用 { setPage(p); setSize(s); }, }} /> ``` ### Modal 弹窗组件使用 ```typescript import { Modal, Button, Form } from 'antd'; import { PlusOutlined } from '@ant-design/icons'; const [createOpen, setCreateOpen] = useState(false); const [createForm] = Form.useForm(); const openCreate = () => { createForm.resetFields(); setCreateOpen(true); }; const submitCreate = async () => { try { const values = await createForm.validateFields(); // 提交创建请求 setCreateOpen(false); } catch (error) { message.error('创建失败'); } }; // 在 JSX 中使用 } onClick={openCreate}> 新建配置 setCreateOpen(false)} > {/* 表单项 */} ``` --- ## 7. 类型定义 ### TypeScript 类型定义位置 **文件路径**: `/Users/youziba/goalfyagent/goalfymax-admin-web/src/types/userLevelConfig.ts` ```typescript export interface UserLevelConfig { id: number; level_name: string; level_code: string; project_limit: number; description: string; sort_order: number; status: number; // 1-启用 0-禁用 created_at: string; updated_at: string; } export interface UserLevelConfigListRequest { level_name?: string; status?: number; page?: number; size?: number; } export interface UserLevelConfigListResponse { data: UserLevelConfig[]; total: number; page: number; size: number; } export interface UserLevelConfigCreateRequest { level_name: string; level_code: string; project_limit: number; description?: string; sort_order?: number; } export interface UserLevelConfigUpdateRequest { level_name: string; project_limit: number; description?: string; sort_order?: number; } export interface UserLevelConfigStatusRequest { status: number; } ``` --- ## 8. 权限管理 ### 权限检查 Hook **文件路径**: `/Users/youziba/goalfyagent/goalfymax-admin-web/src/hooks/usePagePermissions.ts` ```typescript import { useAtom } from 'jotai'; import { authStateAtom } from '../atoms/auth'; export const usePagePermissions = () => { const [authState] = useAtom(authStateAtom); // 检查页面权限 const hasPagePermission = (pagePath: string, action: string = 'read'): boolean => { if (!authState.user?.pages) { return false; } const hasAccess = authState.user.pages.some(page => page.path === pagePath && page.is_active !== false ); return hasAccess; }; // 获取页面操作权限 const getPageActions = (pagePath: string): string[] => { if (!authState.user?.pages) { return []; } const hasAccess = authState.user.pages.some(page => page.path === pagePath); if (!hasAccess) { return []; } return ['read', 'create', 'update', 'delete']; }; // 获取用户可访问的所有页面 const getAccessiblePages = (): string[] => { if (!authState.user?.pages) { return []; } const pages = authState.user.pages .filter(page => page.is_active !== false) .map(page => page.path); return pages; }; return { hasPagePermission, getPageActions, getAccessiblePages }; }; ``` **使用方式**: ```typescript import { usePagePermissions } from '../hooks/usePagePermissions'; function MyComponent() { const { getAccessiblePages, hasPagePermission } = usePagePermissions(); const accessiblePages = getAccessiblePages(); // 检查权限 if (!hasPagePermission('/system')) { return 无权访问; } return 有权访问; } ``` --- ## 9. 其他关键概念 ### 状态管理 (Jotai) **文件路径**: `/Users/youziba/goalfyagent/goalfymax-admin-web/src/atoms/auth.ts` Jotai 用于管理全局认证状态和用户权限信息。 ### 国际化和本地化 整个项目使用中文进行开发,暂无国际化配置。 ### 样式处理 - 使用 Ant Design 组件样式 - 主样式文件: `src/App.css` - 各组件可使用 Ant Design 的 `style` prop 进行内联样式 --- ## 创建新的配置管理页面的完整步骤 ### 第1步:创建 TypeScript 类型定义 创建文件 `/Users/youziba/goalfyagent/goalfymax-admin-web/src/types/systemConfig.ts`: ```typescript export interface SystemConfig { id: number; config_name: string; config_value: string; description: string; status: number; // 1-启用 0-禁用 created_at: string; updated_at: string; } export interface SystemConfigListRequest { config_name?: string; status?: number; page?: number; size?: number; } export interface SystemConfigListResponse { data: SystemConfig[]; total: number; page: number; size: number; } export interface SystemConfigCreateRequest { config_name: string; config_value: string; description?: string; } export interface SystemConfigUpdateRequest { config_name: string; config_value: string; description?: string; } ``` ### 第2步:创建 API 服务 创建文件 `/Users/youziba/goalfyagent/goalfymax-admin-web/src/services/systemConfigApi.ts`: ```typescript import { apiClient } from './api'; import type { SystemConfig, SystemConfigListRequest, SystemConfigListResponse, SystemConfigCreateRequest, SystemConfigUpdateRequest, } from '../types/systemConfig'; // 获取系统配置列表 export const getSystemConfigList = async ( params: SystemConfigListRequest ): Promise => { const response = await apiClient.get('/admin/system-configs', { params }); return response.data; }; // 获取系统配置详情 export const getSystemConfigById = async (id: number): Promise => { const response = await apiClient.get(`/admin/system-configs/${id}`); return response.data; }; // 创建系统配置 export const createSystemConfig = async ( data: SystemConfigCreateRequest ): Promise => { const response = await apiClient.post('/admin/system-configs', data); return response.data; }; // 更新系统配置 export const updateSystemConfig = async ( id: number, data: SystemConfigUpdateRequest ): Promise => { const response = await apiClient.put(`/admin/system-configs/${id}`, data); return response.data; }; // 删除系统配置 export const deleteSystemConfig = async (id: number): Promise => { await apiClient.delete(`/admin/system-configs/${id}`); }; // 更新系统配置状态 export const updateSystemConfigStatus = async ( id: number, status: number ): Promise => { await apiClient.put(`/admin/system-configs/${id}/status`, { status }); }; ``` ### 第3步:创建页面组件 创建文件 `/Users/youziba/goalfyagent/goalfymax-admin-web/src/pages/SystemConfigs.tsx`: ```typescript import React, { useEffect, useState } from 'react'; import { Table, Button, Modal, Form, Input, Tag, Space, Popconfirm, message, Row, Col, Card, } from 'antd'; import { PlusOutlined, EditOutlined, DeleteOutlined, CheckCircleOutlined, StopOutlined, } from '@ant-design/icons'; import type { SystemConfig } from '../types/systemConfig'; import { getSystemConfigList, createSystemConfig, updateSystemConfig, deleteSystemConfig, updateSystemConfigStatus, } from '../services/systemConfigApi'; export default function SystemConfigs() { const [loading, setLoading] = useState(false); const [list, setList] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [size, setSize] = useState(10); const [editOpen, setEditOpen] = useState(false); const [editing, setEditing] = useState(null); const [createOpen, setCreateOpen] = useState(false); const [form] = Form.useForm(); const [createForm] = Form.useForm(); // 获取列表 const fetchList = async () => { setLoading(true); try { const res = await getSystemConfigList({ page, size }); setList(res?.data ?? []); setTotal(res?.total ?? 0); } catch (e) { message.error('获取列表失败'); } finally { setLoading(false); } }; useEffect(() => { fetchList(); }, [page, size]); // 打开编辑弹窗 const openEdit = (config: SystemConfig) => { setEditing(config); form.setFieldsValue({ config_name: config.config_name, config_value: config.config_value, description: config.description, }); setEditOpen(true); }; // 提交编辑 const submitEdit = async () => { try { const values = await form.validateFields(); if (!editing) return; await updateSystemConfig(editing.id, values); message.success('更新成功'); setEditOpen(false); fetchList(); } catch (error) { message.error('更新失败'); } }; // 打开创建弹窗 const openCreate = () => { createForm.resetFields(); setCreateOpen(true); }; // 提交创建 const submitCreate = async () => { try { const values = await createForm.validateFields(); await createSystemConfig(values); message.success('创建成功'); setCreateOpen(false); fetchList(); } catch (error: any) { message.error(error?.response?.data?.message || '创建失败'); } }; // 删除配置 const handleDelete = async (config: SystemConfig) => { try { await deleteSystemConfig(config.id); message.success('删除成功'); fetchList(); } catch (error) { message.error('删除失败'); } }; // 切换状态 const handleToggleStatus = async (config: SystemConfig) => { try { const newStatus = config.status === 1 ? 0 : 1; await updateSystemConfigStatus(config.id, newStatus); message.success(newStatus === 1 ? '已启用' : '已禁用'); fetchList(); } catch (error) { message.error('状态更新失败'); } }; // 定义表格列 const columns = [ { title: '配置名称', dataIndex: 'config_name', key: 'config_name', }, { title: '配置值', dataIndex: 'config_value', key: 'config_value', }, { title: '描述', dataIndex: 'description', key: 'description', }, { title: '状态', dataIndex: 'status', key: 'status', render: (value: number) => value === 1 ? 启用 : 禁用, }, { title: '操作', key: 'action', render: (_: any, config: SystemConfig) => ( } onClick={() => openEdit(config)}> 编辑 : } onClick={() => handleToggleStatus(config)} > {config.status === 1 ? '禁用' : '启用'} handleDelete(config)}> }> 删除 ), }, ]; return ( } onClick={openCreate}> 新建配置 { setPage(p); setSize(s); }, }} /> {/* 编辑弹窗 */} setEditOpen(false)} > {/* 创建弹窗 */} setCreateOpen(false)} > ); } ``` ### 第4步:添加路由 在 `/Users/youziba/goalfyagent/goalfymax-admin-web/src/App.tsx` 中添加: ```typescript import SystemConfigs from './pages/SystemConfigs'; // 在 Routes 中添加新路由 } /> ``` ### 第5步:添加菜单项 在 `/Users/youziba/goalfyagent/goalfymax-admin-web/src/components/Layout.tsx` 中的系统管理子菜单中添加: ```typescript navigate('/system/system-configs')} > 通用配置 ``` --- ## 总结 **核心文件清单**: | 文件类型 | 路径 | 说明 | |---------|------|------| | 类型定义 | `/src/types/*.ts` | TypeScript 接口定义 | | API 服务 | `/src/services/*Api.ts` | API 调用封装 | | 页面组件 | `/src/pages/*.tsx` | 页面级别组件 | | 通用组件 | `/src/components/*.tsx` | 可复用的组件 | | Hooks | `/src/hooks/*.ts` | 自定义 hooks | | 状态管理 | `/src/atoms/*.ts` | Jotai 原子状态 | | 路由配置 | `/src/App.tsx` | 路由定义 | | 菜单配置 | `/src/components/Layout.tsx` | 菜单和导航 | **最佳实践**: 1. 按照现有的目录结构和命名规范创建新文件 2. 使用 TypeScript 定义强类型 3. 在 API 层使用 `apiClient` 进行请求 4. 在页面中使用 Ant Design 组件 5. 通过 `usePagePermissions` hook 检查权限 6. 使用 Jotai 的 `useAtom` 进行状态管理