feat():learning后台管理前端页面初始化

This commit is contained in:
yuj
2025-12-04 17:51:24 +08:00
commit 83a614bd75
97 changed files with 23324 additions and 0 deletions

557
API_PATTERNS.md Normal file
View File

@@ -0,0 +1,557 @@
# API 调用模式和通用实现指南
## 核心 API 架构
### ApiClient 核心类
**位置**`src/services/api.ts`
```typescript
class ApiClient {
private instance: any;
private isRefreshing = false;
private failedQueue: Array<{
resolve: (value: any) => void;
reject: (reason: any) => void;
}> = [];
// 特性:
// 1. 自动附加 Authorization 头
// 2. 401 错误时自动刷新 token
// 3. Token 刷新期间的请求入队
// 4. 自动处理 token 过期和重新登录
}
export const apiClient = new ApiClient();
```
---
## API 服务层设计模式
### 模式 1列表 API用户等级配置
**文件**`src/services/userLevelConfigApi.ts`
```typescript
import { apiClient } from './api';
import type { UserLevelConfig, UserLevelConfigListRequest, UserLevelConfigListResponse } from '../types/userLevelConfig';
// 模式:响应数据提取
// - apiClient 返回完整响应对象
// - 最后一层 API 函数负责提取数据或返回完整响应
export const getUserLevelConfigList = async (
params: UserLevelConfigListRequest
): Promise<UserLevelConfigListResponse> => {
const response = await apiClient.get('/admin/user-level-configs', { params });
return response.data; // 返回响应数据部分
};
export const getAllUserLevelConfigs = async (): Promise<UserLevelConfig[]> => {
const response = await apiClient.get('/admin/user-level-configs/all');
return response.data;
};
```
### 模式 2列表 API系统配置
**文件**`src/services/systemConfigApi.ts`
```typescript
import { apiClient } from './api';
import type { SystemConfig, SystemConfigListRequest, SystemConfigListResponse } from '../types/systemConfig';
// 注意:不同的 API 可能返回不同的数据结构
// 系统配置返回完整响应对象(包含 code, message, data
export const getSystemConfigList = async (
params: SystemConfigListRequest
): Promise<SystemConfigListResponse> => {
const response = await apiClient.get('/admin/system-configs', { params });
return response; // 返回完整响应对象
};
```
### 模式 3CRUD 操作
```typescript
// 创建
export const createUserLevelConfig = async (
data: UserLevelConfigCreateRequest
): Promise<UserLevelConfig> => {
const response = await apiClient.post('/admin/user-level-configs', data);
return response.data;
};
// 读取
export const getUserLevelConfigById = async (id: number): Promise<UserLevelConfig> => {
const response = await apiClient.get(`/admin/user-level-configs/${id}`);
return response.data;
};
// 更新
export const updateUserLevelConfig = async (
id: number,
data: UserLevelConfigUpdateRequest
): Promise<UserLevelConfig> => {
const response = await apiClient.put(`/admin/user-level-configs/${id}`, data);
return response.data;
};
// 删除
export const deleteUserLevelConfig = async (id: number): Promise<void> => {
await apiClient.delete(`/admin/user-level-configs/${id}`);
};
// 特殊操作:状态更新
export const updateUserLevelConfigStatus = async (
id: number,
data: UserLevelConfigStatusRequest
): Promise<void> => {
await apiClient.put(`/admin/user-level-configs/${id}/status`, data);
};
```
---
## 类型定义模式
### 完整类型定义示例
**文件**`src/types/userLevelConfig.ts`
```typescript
// 1. 主体数据类型
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;
}
// 2. 请求类型
export interface UserLevelConfigListRequest {
level_name?: string;
status?: 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;
}
// 3. 响应类型
export interface UserLevelConfigListResponse {
data: UserLevelConfig[];
total: number;
page: number;
size: number;
}
```
---
## 页面组件实现模式
### CRUD 页面的标准结构
**文件**`src/pages/UserLevelConfigs.tsx`
```typescript
import React, { useEffect, useState } from 'react';
import { Table, Button, Modal, Form, Input, message } from 'antd';
import type { UserLevelConfig } from '../types/userLevelConfig';
import { getUserLevelConfigList, createUserLevelConfig, updateUserLevelConfig, deleteUserLevelConfig } from '../services/userLevelConfigApi';
export default function UserLevelConfigs() {
// ==================== 状态管理 ====================
const [loading, setLoading] = useState(false);
const [list, setList] = useState<UserLevelConfig[]>([]);
const [total, setTotal] = useState(0);
const [page, setPage] = useState(1);
const [size, setSize] = useState(10);
// 编辑弹窗状态
const [editOpen, setEditOpen] = useState(false);
const [editing, setEditing] = useState<UserLevelConfig | null>(null);
const [form] = Form.useForm();
// 创建弹窗状态
const [createOpen, setCreateOpen] = useState(false);
const [createForm] = Form.useForm();
// ==================== 数据加载 ====================
const fetchList = async () => {
setLoading(true);
try {
const res = await getUserLevelConfigList({ page, size });
// 处理响应数据:兼容数组和对象两种格式
if (Array.isArray(res)) {
setList(res);
setTotal(res.length);
} else {
setList(res?.data ?? []);
setTotal(res?.total ?? 0);
}
} catch (e) {
console.error('获取列表失败:', e);
message.error('获取列表失败');
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchList();
}, [page, size]);
// ==================== 创建操作 ====================
const openCreate = () => {
createForm.resetFields();
setCreateOpen(true);
};
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 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 handleDelete = async (config: UserLevelConfig) => {
try {
await deleteUserLevelConfig(config.id);
message.success('删除成功');
fetchList();
} catch (error) {
message.error('删除失败');
}
};
// ==================== 渲染 ====================
const columns = [
// 列定义...
];
return (
<div>
{/* 主表格 */}
<Table dataSource={list} columns={columns} loading={loading} />
{/* 创建弹窗 */}
<Modal title="新建" open={createOpen} onOk={submitCreate}>
<Form form={createForm} layout="vertical">
{/* 表单字段 */}
</Form>
</Modal>
{/* 编辑弹窗 */}
<Modal title="编辑" open={editOpen} onOk={submitEdit}>
<Form form={form} layout="vertical">
{/* 表单字段 */}
</Form>
</Modal>
</div>
);
}
```
---
## 权限和路由集成
### 添加新页面的完整检查清单
#### 1. 创建类型定义
- [ ] `src/types/xxxConfig.ts` - 定义数据模型和请求/响应类型
#### 2. 创建 API 服务
- [ ] `src/services/xxxConfigApi.ts` - 实现 CRUD API 方法
#### 3. 创建页面组件
- [ ] `src/pages/XxxConfigs.tsx` - 实现 CRUD UI
#### 4. 注册路由
- [ ] `src/App.tsx` - 添加路由定义
```typescript
<Route path="/system/xxx-configs" element={<XxxConfigs />} />
```
#### 5. 添加菜单项
- [ ] `src/components/DynamicMenu.tsx` - 添加菜单项定义
```typescript
{
key: 'system-xxx-configs',
label: 'Xxx 配置',
icon: <SettingOutlined />,
path: '/xxx-configs',
}
```
#### 6. 更新 Layout 子导航
- [ ] `src/components/Layout.tsx` - 添加子导航按钮(如果需要)
```typescript
<button
className={activeSubTab === 'xxx-configs' ? 'active' : ''}
onClick={() => navigate('/system/xxx-configs')}
>
Xxx 配置
</button>
```
---
## 错误处理最佳实践
### API 错误处理
```typescript
// 页面中的错误处理
const fetchList = async () => {
try {
const res = await getUserLevelConfigList({ page, size });
setList(res?.data ?? []);
} catch (error: any) {
// 1. 优先使用 API 返回的错误信息
const errorMsg = error?.response?.data?.message;
// 2. 其次使用通用错误信息
message.error(errorMsg || '获取列表失败');
// 3. 记录详细错误用于调试
console.error('获取列表失败:', error);
}
};
// 创建/更新时的错误处理
const submitCreate = async () => {
try {
const values = await createForm.validateFields();
await createUserLevelConfig(values);
message.success('创建成功');
} catch (error: any) {
// 区分表单验证错误和 API 错误
if (error?.response?.data?.message) {
message.error(error.response.data.message);
} else {
message.error('创建失败');
}
}
};
```
---
## 通用 CRUD 表格列定义
### 标准列定义模式
```typescript
const columns = [
// 基本字段列
{
title: '名称',
dataIndex: 'level_name',
key: 'level_name',
},
{
title: '代码',
dataIndex: 'level_code',
key: 'level_code',
},
// 数值列(带格式化)
{
title: '数值',
dataIndex: 'value',
key: 'value',
render: (value: number) => (value === 0 ? '不限' : value),
},
// 状态列(带标签)
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: (value: number) =>
value === 1 ? <Tag color="green">启用</Tag> : <Tag color="red">禁用</Tag>,
},
// 操作列(最后)
{
title: '操作',
key: 'action',
render: (_: any, record: T) => (
<Space>
<Button type="link" icon={<EditOutlined />} onClick={() => openEdit(record)}>
编辑
</Button>
<Button type="link" icon={<DeleteOutlined />} danger onClick={() => handleDelete(record)}>
删除
</Button>
</Space>
),
},
];
```
---
## 表单字段最佳实践
### 创建表单示例
```typescript
<Form form={createForm} layout="vertical">
{/* 必填字段 */}
<Form.Item
name="level_name"
label="等级名称"
rules={[{ required: true, message: '请输入等级名称' }]}
>
<Input placeholder="请输入等级名称" />
</Form.Item>
{/* 代码字段(创建时必填,编辑时只读) */}
<Form.Item
name="level_code"
label="等级代码"
rules={[{ required: true, message: '请输入等级代码' }]}
>
<Input placeholder="请输入等级代码" />
</Form.Item>
{/* 数值字段 */}
<Form.Item
name="project_limit"
label="项目数限制0表示不限"
rules={[{ required: true, message: '请输入项目数限制' }]}
initialValue={0}
>
<InputNumber min={0} style={{ width: '100%' }} />
</Form.Item>
{/* 可选文本字段 */}
<Form.Item name="description" label="描述">
<Input.TextArea rows={3} placeholder="请输入描述" />
</Form.Item>
{/* 排序字段 */}
<Form.Item name="sort_order" label="排序顺序" initialValue={0}>
<InputNumber min={0} style={{ width: '100%' }} />
</Form.Item>
</Form>
```
---
## 分页实现
### 标准分页逻辑
```typescript
// 状态
const [page, setPage] = useState(1);
const [size, setSize] = useState(10);
const [total, setTotal] = useState(0);
// 依赖项变化时重新加载
useEffect(() => {
fetchList();
}, [page, size]);
// 表格分页配置
<Table
dataSource={list}
columns={columns}
pagination={{
current: page,
pageSize: size,
total: total,
onChange: (p, s) => {
setPage(p);
setSize(s);
},
}}
/>
```
---
## 总结
### API 调用流程
```
用户操作
页面组件(使用 API 函数)
API 服务函数(处理参数和响应)
ApiClient自动处理 token 和拦截器)
AxiosHTTP 请求)
后端 API
```
### 关键要点
1. **分层设计**:每一层职责清晰
2. **类型安全**:完整的 TypeScript 类型支持
3. **错误处理**:统一的错误处理机制
4. **权限集成**:无缝的权限管理
5. **状态管理**:简化的本地状态管理
6. **自动化**:自动 Token 管理和刷新