# CRM Admin 项目开发指南(CLAUDE.md) > Vite 6 + React 18 + TypeScript + Ant Design 5 + SCSS 的后台管理平台。 > 源码位于 `src/`,使用 BrowserRouter 的 SPA 架构,包含多级路由菜单、登录 / 退出、路由守卫与按钮级权限。 ## 一、常用命令 ```bash npm install # 安装依赖 npm run dev # 启动开发服务器(http://localhost:5173) npm run typecheck # TypeScript 类型检查 npm run build # 类型检查 + 生产构建 npm run preview # 预览生产构建 ``` 改动代码后至少执行 `npm run typecheck`;涉及构建配置或样式时再执行 `npm run build`。 ## 二、技术栈 - **构建**:Vite 6 - **框架**:React 18 + TypeScript(strict) - **路由**:react-router-dom 6 - **UI**:Ant Design 5 + `@ant-design/icons` - **状态**:Zustand 5(登录态持久化到 localStorage) - **样式**:SCSS + Ant Design 主题 token + CSS 变量 - **日期**:Day.js(已配置中文 locale) ## 三、目录结构 ```text src/ ├── api/ # 接口层;当前只有模拟登录 auth.ts ├── components/ # 通用组件:RouteGuard / AuthButton / Permission ├── constants/ # 权限码与中文说明 ├── hooks/ # usePermission ├── layouts/ # BasicLayout:侧边栏、头部、内容区、面包屑 ├── pages/ # 页面,按业务域分目录 │ ├── login/ │ ├── dashboard/ │ ├── system/ │ │ ├── users/ │ │ ├── roles/ │ │ └── permission/{menu,button}/ │ ├── business/{orders,customers}/ │ └── error/{Forbidden,NotFound}.tsx ├── router/ │ ├── routeConfig.tsx # 路由 + 菜单唯一数据源(含权限码、页面组件) │ ├── menu.tsx # 菜单过滤与 antd Menu 结构生成 │ └── index.tsx # 配置转 React Router 嵌套路由 ├── store/ # Zustand 登录态 ├── styles/ # 全局 SCSS、通用页面样式 ├── types/ # 全局类型 └── main.tsx # ConfigProvider + AntdApp + BrowserRouter ``` ## 四、核心架构 ### 1. 路由与菜单 `src/router/routeConfig.tsx` 是**唯一数据源**。新增页面只需在这里配置 `path`、`title`、`icon`、`permission`、`element`,侧边栏、面包屑、路由守卫会自动读取。 - 支持 2-3 级嵌套,例如:`系统管理 → 权限中心 → 菜单权限`。 - `src/router/index.tsx` 把菜单配置转换成 React Router 的嵌套路由。 - 分组路由(无 `element`)通过 `` 渲染子页面,默认跳转到第一个有权限的子页面。 - 菜单项按 `permission` 过滤;分组下所有子项都无权限时整组隐藏。 - 未匹配路由显示 404,普通页面组件位于 `BasicLayout` 内。 ### 2. 登录态 - `src/store/auth.ts`:Zustand store,持久化 `token` 与 `user`。 - 登录 / 退出:`src/api/auth.ts` 提供模拟登录。 - 演示账号: - `admin / admin123`:权限为 `['*']`,拥有全部权限。 - `user / user123`:仅有工作台、用户、订单、客户的查看权限。 - 未登录访问受保护页面会跳转 `/login`,登录成功后返回原地址。 ### 3. 权限体系 - 权限码统一维护在 `src/constants/permissions.ts`。 - 路由级权限:页面配置中的 `permission` + `src/components/RouteGuard.tsx`。 - 未登录 → `/login` - 无权限 → `/403` - 菜单级权限:`buildMenuItems` 按权限码过滤。 - 按钮 / UI 级权限: - ``:无权限默认隐藏,`fallback="disabled"` 时禁用。 - ``:包裹任意 UI。 - `usePermission()`:页面内自定义判断。 - `*` 表示全部权限。 - **前端权限只用于界面控制,接入真实后端时必须在服务端再次鉴权。** ### 4. 布局与样式 - 统一使用 `BasicLayout`,页面只负责内容区域。 - 全局 CSS 变量:`--app-primary`、`--app-bg`、`--app-border`、`--app-text`、`--app-text-secondary` 等。 - 通用页面类:`.page-header`、`.page-toolbar`、`.filter-card`、`.table-card`、`.text-muted`。 - 页面级样式与页面同目录,使用 BEM 风格类名(如 `.user-cell__name`)。 - Ant Design 主题在 `src/main.tsx` 的 `ConfigProvider` 中统一配置。 ## 五、代码风格(必遵守) ### 1. 统一使用箭头函数 组件、工具函数、Hook、事件处理函数都使用箭头函数,不新增 `function` 声明。 ```tsx // ✅ 组件 const UserList = () => { const handleSubmit = async () => { // ... }; return ; }; export default UserList; // ✅ 工具 / Hook export const usePermission = () => { // ... }; export const formatAmount = (value: number) => `¥${value.toLocaleString()}`; // ❌ 不要这样写 export default function UserList() {} export function formatAmount(value: number) {} ``` ### 2. 优先使用现成组件 写功能前先找现成能力,不要重复造轮子: 1. **优先使用 Ant Design 组件**:Table、Form、Modal、Select、DatePicker、Card、Tabs、Drawer 等。 2. **优先使用项目已有封装**: - 按钮权限:`src/components/AuthButton.tsx` - 任意 UI 权限:`src/components/Permission.tsx` - 路由守卫:`src/components/RouteGuard.tsx` - 布局:`src/layouts/BasicLayout.tsx` - 全局提示 / 确认框:使用 `` 提供的 `AntdApp.useApp()` 中的 `message`、`modal`,**不要**直接使用 `message.success()` / `Modal.confirm()` 静态方法。 3. **不要手写**弹窗、Toast、下拉菜单、分页、表格、表单校验等基础交互。 4. 不引入新的 UI 组件库;确需新组件时,先检查 `src/components/` 是否已有可复用实现,再考虑新增。 5. 使用 Ant Design 5 的 API,不使用已废弃属性。例如 Card 使用 `variant="borderless"`,不要使用 `bordered={false}`。 ### 3. Table columns 必须单独提取 所有表格的 `columns` 不得内联在 `
` 的 JSX 中,也不写在页面组件内部。统一提取到页面目录单独维护。 推荐结构: ```text src/pages/system/users/ ├── index.tsx # 页面组件 ├── columns.tsx # 表格列定义 └── Users.scss # 页面样式 ``` ```tsx // columns.tsx import type { TableProps } from 'antd'; export const userColumns: TableProps['columns'] = [ { title: '用户名', dataIndex: 'username', }, // ... ]; // 需要事件回调时,导出工厂函数 export const createUserColumns = (options: { onEdit: (record: UserRecord) => void; onDelete: (record: UserRecord) => void; }): TableProps['columns'] => [ // ... { title: '操作', key: 'action', render: (_value, record) => ( options.onEdit(record)}> 编辑 ), }, ]; ``` ```tsx // index.tsx const columns = useMemo(() => createUserColumns({ onEdit, onDelete }), []); rowKey="key" columns={columns} dataSource={dataSource} />; ``` 约定: - columns 只负责展示,数据与业务逻辑留在页面组件。 - 列类型使用 `TableProps['columns']`,不要使用 `any`。 - 权限控制可以直接用 `AuthButton` / `Permission`(它们自己读取 store),无需把权限状态传进 columns。 - 若列配置依赖页面状态或回调,使用 `createColumns(options)` 工厂 + `useMemo`,不要每次渲染重新创建。 > 说明:存量页面中部分 columns 仍定义在页面组件内部、部分函数使用 `function` 声明。新增和修改代码一律遵循上述规范,存量代码在后续迭代中逐步迁移。 ## 六、新增页面流程 1. 在 `src/pages/<业务域>/<页面>/` 下创建 `index.tsx`(有表格时增加 `columns.tsx`,有独立样式时增加同名 `.scss`)。 2. 如需权限控制,在 `src/constants/permissions.ts` 新增权限码与中文说明。 3. 在 `src/router/routeConfig.tsx` 中增加路由配置: - 使用完整路径(如 `/system/users`)。 - 配置 `title`、`icon`、`permission`、`element`。 - 需要分组时使用 `children`。 4. 在模拟账号(`src/api/auth.ts`)或真实后端中给角色分配权限码。 5. 页面内需要权限按钮时使用 `AuthButton`,路由级权限由 `RouteGuard` 自动处理。 6. 执行 `npm run typecheck` 验证。 示例: ```tsx { path: '/business/invoices', title: '发票管理', icon: , permission: PERMISSION.INVOICE_VIEW, element: , } ``` ## 七、接口与状态约定 - 所有请求统一放在 `src/api/` 下,按业务域拆分文件;不要在页面内直接写请求实现。 - 当前 `src/api/auth.ts` 是前端模拟接口。接入真实后端时替换为实际请求,并保留登录态写入 store 的流程。 - 全局登录态只放在 `src/store/auth.ts`;页面局部状态使用 `useState` / `useMemo`。 - 不要在前端持久化敏感信息;真实项目的 Token 建议使用 HttpOnly Cookie,并由后端处理过期与刷新。 ## 八、样式与可访问性 - 使用 SCSS,不在 JSX 中堆叠大量内联样式;动态颜色等少量样式可使用 `style`。 - 颜色、圆角优先使用已有的 CSS 变量或 Ant Design token。 - 交互元素保持可读的 hover / focus 状态;图标按钮补充 `aria-label`。 - 页面需兼容窄屏:表格使用 `scroll={{ x: ... }}`,左右布局使用栅格或 flex 换行。 ## 九、部署注意 - 使用 BrowserRouter,生产环境需要配置 SPA fallback:所有前端路由回退到 `index.html`。 - 构建产物在 `dist/`。 - 当前主包较大(Ant Design 全量依赖),后续可通过路由懒加载 / 手动分包优化。