# 前端开发快速开始指南 ## 项目信息 - **技术栈**: React 18 + TypeScript + Vite + Ant Design 5 + React Router 7 - **项目路径**: `/Users/youziba/goalfyagent/goalfymax-admin-web` - **状态管理**: Jotai - **HTTP客户端**: Axios (自动处理 Token 认证和刷新) --- ## 快速创建新的配置管理页面 以创建"通用配置"管理页面为例,需要 5 个步骤: ### 步骤 1:创建类型定义文件 **文件**: `src/types/commonConfig.ts` ```typescript export interface CommonConfig { id: number; config_key: string; // 配置键 config_value: string; // 配置值 description: string; // 描述 status: number; // 1=启用, 0=禁用 created_at: string; updated_at: string; } export interface CommonConfigListRequest { config_key?: string; status?: number; page?: number; size?: number; } export interface CommonConfigListResponse { data: CommonConfig[]; total: number; page: number; size: number; } export interface CommonConfigCreateRequest { config_key: string; config_value: string; description?: string; } export interface CommonConfigUpdateRequest { config_key: string; config_value: string; description?: string; } ``` ### 步骤 2:创建 API 服务文件 **文件**: `src/services/commonConfigApi.ts` ```typescript import { apiClient } from './api'; import type { CommonConfig, CommonConfigListRequest, CommonConfigListResponse, CommonConfigCreateRequest, CommonConfigUpdateRequest, } from '../types/commonConfig'; export const getCommonConfigList = async ( params: CommonConfigListRequest ): Promise => { const response = await apiClient.get('/admin/common-configs', { params }); return response.data; }; export const getCommonConfigById = async (id: number): Promise => { const response = await apiClient.get(`/admin/common-configs/${id}`); return response.data; }; export const createCommonConfig = async ( data: CommonConfigCreateRequest ): Promise => { const response = await apiClient.post('/admin/common-configs', data); return response.data; }; export const updateCommonConfig = async ( id: number, data: CommonConfigUpdateRequest ): Promise => { const response = await apiClient.put(`/admin/common-configs/${id}`, data); return response.data; }; export const deleteCommonConfig = async (id: number): Promise => { await apiClient.delete(`/admin/common-configs/${id}`); }; export const updateCommonConfigStatus = async ( id: number, status: number ): Promise => { await apiClient.put(`/admin/common-configs/${id}/status`, { status }); }; ``` ### 步骤 3:创建页面组件 **文件**: `src/pages/CommonConfigs.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 { CommonConfig } from '../types/commonConfig'; import { getCommonConfigList, createCommonConfig, updateCommonConfig, deleteCommonConfig, updateCommonConfigStatus, } from '../services/commonConfigApi'; export default function CommonConfigs() { 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 getCommonConfigList({ page, size }); setList(res?.data ?? []); setTotal(res?.total ?? 0); } catch (e) { message.error('获取列表失败'); } finally { setLoading(false); } }; useEffect(() => { fetchList(); }, [page, size]); const openEdit = (config: CommonConfig) => { setEditing(config); form.setFieldsValue({ config_key: config.config_key, config_value: config.config_value, description: config.description, }); setEditOpen(true); }; const submitEdit = async () => { try { const values = await form.validateFields(); if (!editing) return; await updateCommonConfig(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 createCommonConfig(values); message.success('创建成功'); setCreateOpen(false); fetchList(); } catch (error: any) { message.error(error?.response?.data?.message || '创建失败'); } }; const handleDelete = async (config: CommonConfig) => { try { await deleteCommonConfig(config.id); message.success('删除成功'); fetchList(); } catch (error) { message.error('删除失败'); } }; const handleToggleStatus = async (config: CommonConfig) => { try { const newStatus = config.status === 1 ? 0 : 1; await updateCommonConfigStatus(config.id, newStatus); message.success(newStatus === 1 ? '已启用' : '已禁用'); fetchList(); } catch (error) { message.error('状态更新失败'); } }; const columns = [ { title: '配置键', dataIndex: 'config_key', key: 'config_key', }, { 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: CommonConfig) => ( handleDelete(config)}> ), }, ]; return (
{ setPage(p); setSize(s); }, }} /> {/* 编辑弹窗 */} setEditOpen(false)} >
{/* 创建弹窗 */} setCreateOpen(false)} >
); } ``` ### 步骤 4:在 App.tsx 中添加路由 在 `src/App.tsx` 中找到系统管理路由部分,添加: ```typescript import CommonConfigs from './pages/CommonConfigs'; // 在 Routes 中的系统管理部分添加: } /> ``` ### 步骤 5:在 Layout.tsx 中添加菜单项 在 `src/components/Layout.tsx` 中找到系统管理子菜单部分(`data-tabs="admin"`),添加: ```typescript ``` --- ## 核心文件速查表 | 功能 | 文件位置 | 说明 | |------|---------|------| | 全局路由 | `src/App.tsx` | 所有页面路由定义 | | 菜单导航 | `src/components/Layout.tsx` | 菜单项和导航配置 | | API 客户端 | `src/services/api.ts` | Axios 包装,自动处理 Token | | 权限检查 | `src/hooks/usePagePermissions.ts` | 获取用户权限信息 | | 认证管理 | `src/hooks/useAuth.ts` | SSO 认证相关逻辑 | | 全局状态 | `src/atoms/auth.ts` | Jotai 状态原子定义 | --- ## 常用开发模式 ### 1. 获取列表并分页 ```typescript const [list, setList] = useState([]); const [page, setPage] = useState(1); const [size, setSize] = useState(10); const [total, setTotal] = useState(0); const fetchList = async () => { const res = await getXxxList({ page, size }); setList(res?.data ?? []); setTotal(res?.total ?? 0); }; useEffect(() => { fetchList(); }, [page, size]); // 在 Table 中使用
{ setPage(p); setSize(s); }, }} /> ``` ### 2. 打开编辑弹窗并回显数据 ```typescript const [editOpen, setEditOpen] = useState(false); const [editing, setEditing] = useState(null); const [form] = Form.useForm(); const openEdit = (record: T) => { setEditing(record); form.setFieldsValue({ field1: record.field1, field2: record.field2, }); setEditOpen(true); }; const submitEdit = async () => { const values = await form.validateFields(); await updateXxx(editing!.id, values); message.success('更新成功'); setEditOpen(false); fetchList(); }; ``` ### 3. 权限检查 ```typescript import { usePagePermissions } from '../hooks/usePagePermissions'; function MyComponent() { const { getAccessiblePages } = usePagePermissions(); const accessiblePages = getAccessiblePages(); if (!accessiblePages.includes('/system')) { return
无权访问
; } return
内容
; } ``` ### 4. 调用 API ```typescript import { apiClient } from '../services/api'; // GET 请求 const data = await apiClient.get('/admin/xxx'); // POST 请求 const result = await apiClient.post('/admin/xxx', { key: 'value' }); // PUT 请求 const updated = await apiClient.put('/admin/xxx/123', { key: 'new-value' }); // DELETE 请求 await apiClient.delete('/admin/xxx/123'); ``` --- ## Ant Design 常用组件 | 组件 | 用途 | 导入 | |------|------|------| | `Button` | 按钮 | `from 'antd'` | | `Table` | 数据表格 | `from 'antd'` | | `Form` | 表单 | `from 'antd'` | | `Input` | 文本输入 | `from 'antd'` | | `InputNumber` | 数字输入 | `from 'antd'` | | `Select` | 下拉选择 | `from 'antd'` | | `Modal` | 弹窗对话框 | `from 'antd'` | | `Message` | 消息提示 | `from 'antd'` | | `Tag` | 标签 | `from 'antd'` | | `Card` | 卡片容器 | `from 'antd'` | | `Space` | 间距布局 | `from 'antd'` | | `Popconfirm` | 确认弹窗 | `from 'antd'` | --- ## 开发工作流 1. **创建类型定义** → `src/types/xxx.ts` 2. **创建 API 服务** → `src/services/xxxApi.ts` 3. **创建页面组件** → `src/pages/Xxx.tsx` 4. **添加路由** → `src/App.tsx` 5. **添加菜单** → `src/components/Layout.tsx` 6. **测试** → `npm run dev` --- ## API 命名规范 - 列表: `GET /admin/xxx` - 详情: `GET /admin/xxx/:id` - 创建: `POST /admin/xxx` - 更新: `PUT /admin/xxx/:id` - 删除: `DELETE /admin/xxx/:id` - 状态: `PUT /admin/xxx/:id/status` --- ## 注意事项 1. **Token 自动管理**: API 请求会自动附带 Token,401 错误会自动刷新 Token 2. **权限检查**: 菜单和路由需要通过 `usePagePermissions` 检查用户权限 3. **类型安全**: 始终使用 TypeScript 定义接口,避免使用 `any` 4. **表单验证**: 使用 `Form.useForm()` 和 `validateFields()` 进行表单验证 5. **消息提示**: 使用 `message.success()` / `message.error()` 等方法提示用户 6. **异步处理**: 使用 `async/await` 简化异步逻辑 --- ## 参考文档 完整的开发指南请查看: `/Users/youziba/goalfyagent/goalfymax-admin-web/FRONTEND_GUIDE.md`