wsj
3 days ago 71719ffa0eeb4a28a1b736f87499bcda2847a848
CLAUDE.md
@@ -41,7 +41,7 @@
│   ├── business/orders/
│   ├── customers/list/
│   └── error/NotFound.tsx
│   # 页面目录:index.tsx、columns.tsx、types.ts、server.ts、components/(按需)、同名 .scss(按需)
│   # 页面目录:index.tsx、columns.tsx、types.ts、service.ts、components/(按需)、同名 .scss(按需)
├── router/
│   ├── routeConfig.tsx  # 路由 + 菜单唯一数据源(含页面组件)
│   ├── menu.tsx         # 菜单过滤与 antd Menu 结构生成
@@ -152,8 +152,8 @@
- Drawer、Modal 等弹层组件,以及详情卡片、表单、局部业务块等可独立成文件的组件,放到 `src/pages/<业务域>/<页面>/components/` 下。
- 组件文件使用大驼峰命名,如 `UserFormDrawer.tsx`、`CustomerDetailDrawer.tsx`;组件私有的 `.scss` 与组件同目录,由组件自身引入。
- 组件只接收 `props`,接口调用仍放在页面目录的 `server.ts`,由页面或组件按需调用;不要跨页面直接引用其他页面目录下的组件。
- 单个页面文件(含 `index.tsx`)超过 300 行时必须拆分:按功能块抽取到 `components/`,`columns.tsx`、`types.ts`、`server.ts`、`utils.ts` 等继续单独维护。
- 组件只接收 `props`,接口调用仍放在页面目录的 `service.ts`,由页面或组件按需调用;不要跨页面直接引用其他页面目录下的组件。
- 单个页面文件(含 `index.tsx`)超过 300 行时必须拆分:按功能块抽取到 `components/`,`columns.tsx`、`types.ts`、`service.ts`、`utils.ts` 等继续单独维护。
- 大于 300 行的复杂组件同样按功能拆分,不要在同一个文件里堆叠多个组件。
```text
@@ -161,7 +161,7 @@
├── index.tsx                # 页面组件(尽量精简)
├── columns.tsx              # 表格列定义
├── types.ts
├── server.ts
├── service.ts
├── utils.ts                 # 按需
├── Users.scss
└── components/
@@ -230,12 +230,22 @@
## 六、新增页面流程
1. 在 `src/pages/<业务域>/<页面>/` 下创建 `index.tsx`(有表格时增加 `columns.tsx`,接口调用放在 `server.ts`,类型放 `types.ts`,有独立样式时增加同名 `.scss`)。
**新增页面统一使用 `/new-page` skill**(定义在 `.claude/skills/new-page/`),不要手工复制模板或另起方式搭脚手架。该 skill 会先选择表格页 / 非表格页分支并整体套用对应模板,再统一完成代码规范、路由注册、文档更新与类型检查。
调用示例:
```bash
/new-page 根据 <接口文档路径>,添加一个【页面名】页面,放到【分组名】目录下
```
> 仅修改现有页面(如调整某一列)等不涉及脚手架的场景,才直接改代码;即使手工创建页面,也须按以下流程执行。
1. 在 `src/pages/<业务域>/<页面>/` 下创建 `index.tsx`(有表格时增加 `columns.tsx`,独立组件放 `components/`,接口调用放在 `service.ts`,类型放 `types.ts`,有独立样式时增加同名 `.scss`)。
2. 在 `src/router/routeConfig.tsx` 中增加路由配置:
   - 使用完整路径(如 `/system/users`)。
   - 配置 `title`、`icon`、`element`。
   - 需要分组时使用 `children`。
3. 执行 `npm run typecheck` 验证。
3. 执行 `npm run typecheck` 验证(涉及样式或构建配置时再执行 `npm run build`)。
示例:
@@ -250,7 +260,10 @@
## 七、接口与状态约定
- **每个接口都必须为其请求(request)与响应(response)定义 `interface`**,并在 `server.ts` 中通过 `request<TReq, TRes>` 显式标注泛型;禁止使用 `any` 或省略类型。
以下四条为强制要求,新增与修改代码必须遵守:
1. **所有接口必须使用 `src/api/request.ts`**:统一通过其导出的 `request` 发起请求,不要在页面、组件或 `service.ts` 中直接使用 axios、`fetch` 或其他请求库。
2. **每个接口的 request 与 response 都必须定义 `interface`**:在 `service.ts` 中通过 `request<TReq, TRes>` 显式标注泛型,禁止使用 `any` 或省略类型。
  - 类型统一定义在页面目录的 `types.ts` 中(跨页面复用的放在 `src/api/` 对应业务域文件)。
  - 命名约定:`<业务>Request` / `<业务>Response`,例如 `TenantRoleListRequest`、`TenantRoleListResponse`。
  - 请求体 / 查询参数建模为 `XxxRequest`;响应建模为 `XxxResponse`,对应接口返回 `data` 的结构(若后端是 `Result<T>`,`Response` 指 `data` 部分)。
@@ -266,7 +279,7 @@
  status: RoleStatus;
}
// server.ts
   // service.ts
export const toggleRoleStatus = ({ id }: TenantRoleToggleStatusRequest) =>
  request<TenantRoleToggleStatusRequest, TenantRoleToggleStatusResponse>({
    url: `/tenant/roles/${id}/toggle-status`,
@@ -275,10 +288,7 @@
  });
```
- 页面级接口调用统一放在页面目录下的 `server.ts`,页面组件通过它调用接口,不要在组件内直接写请求实现。
- 跨页面复用的接口可放在 `src/api/` 下,按业务域拆分文件。
- 网络请求统一使用 `src/api/request.ts` 的 `request`(基于 axios),不要在页面或 `server.ts` 中直接使用 axios。
- **接口调用使用 `.then` 链式处理,不要使用 `await` + `try/catch`**:成功逻辑写在 `.then`,失败逻辑写在 `.catch`(错误已由 `request` 统一提示时可只保留空 `catch`),收尾逻辑写在 `.finally`。禁止写成 `try { const data = await xxx() } catch {}`。(存量代码在后续迭代中逐步迁移。)
3. **接口调用必须使用 `.then` 链式处理,禁止 `await` + `try/catch`**:成功逻辑写在 `.then`,失败逻辑写在 `.catch`(错误已由 `request` 统一提示时可只保留空 `catch`),收尾逻辑写在 `.finally`。
```ts
// ✅ 使用 .then 链式处理
@@ -304,6 +314,12 @@
}
```
4. **接口按作用域拆分文件**:
   - 页面接口放在页面目录下的 `service.ts` 中,页面与组件通过它调用,不要在组件内直接写请求实现。
   - 跨页面复用的公共接口按业务域拆分到 `src/api/` 下(如 `src/api/auth.ts`)。
补充说明:
- 开发环境通过 Vite 代理 `/proxy-api` 转发到 `VITE_API_PROXY_TARGET`(未配置时取 `VITE_API_BASE_URL`),因此浏览器端不会产生跨域请求;生产环境需后端配置 CORS,或使用同域反向代理。
  - 泛型:`request<TReq, TRes>(config, options)`,分别表示请求参数/请求体与响应 `data` 的类型。
  - 默认只返回响应 `data`;传 `parse: false` 时返回完整 `AxiosResponse`。