# AGENTS.md

本文件为后续编码代理提供 `cn-rdms-web` 的稳定仓库上下文。
在修改代码前请先阅读。

## 适用范围

本说明适用于以 `C:\code\gitea\rdms\cn-rdms-web` 为根目录的整个仓库。

描述仓库现状时，以当前代码、当前配置、当前文档中可直接验证的事实为准；除非用户明确要求，不引入历史实现、过渡方案或猜测来解释当前行为。

默认回答保持精简，优先给结论、改动点、验证方式和必要风险；如果用户只要求分析、审阅或方案，就停留在分析层，不主动扩展到实现层。

分析、解释、方案类回答优先用业务和逻辑语言把结构、差异与结论说清楚，不要大段贴源码、罗列 `file:line` 或把实现细节当解释；只有用户明确要求看代码、或某行确实是讨论焦点的关键佐证时，才贴最小必要的代码片段。

## 交互与执行原则

- 进入实施阶段前，先说明目标、涉及模块、预计改动点和验证方式。
- 先定义验证方式，再执行修改和校验；如果没有实际运行命令，需要明确说明只做了静态检查。
- 只在当前任务需要的最小范围内改动，避免把无关重构混入同一次修改。

## 项目概览

- 应用类型：RDMS 系统的 Vue 3 后台前端
- 包管理器：`pnpm`
- 运行时与工具链：Vite 7、TypeScript、Pinia、Element Plus、UnoCSS
- 工作区包位于 `packages/*`，通过 `@sa/*` 引用
- Node 版本要求：`>=20.19.0`
- pnpm 版本要求：`>=8.7.0`

## 项目骨架主线

当前项目不是边写边拼的页面集合，而是已经形成闭环的后台前端骨架。后续改动优先顺着这几条主线做，而不是平行再起一套：

- 路由来源统一：页面文件与自定义路由作为源头，经 `elegant-router` 生成路由产物，再由 `build/plugins/router.ts` 集中补齐 `meta`
- 权限入口统一：常量路由和权限路由明确分流，`route store` 负责初始化、菜单生成、缓存路由和面包屑
- 请求入口统一：所有业务请求默认走 `src/service/request/index.ts`
- 页面套路统一：列表页通常拆为搜索区、表格区、操作弹层/抽屉和 `modules/*` 子组件
- 衍生资产统一：页面资源白名单从路由结构生成，不手工维护第二份页面清单

## 环境与构建说明

- Vite 路径别名：`@` -> `src`
- Vite 路径别名：`~` -> 仓库根目录
- 开发服务器默认端口：`9527`
- 预览服务器默认端口：`9725`
- 环境文件包括 `.env`、`.env.dev`、`.env.prod`

## 关键目录与文件

- `src/views`：业务页面
- `src/components`：共享组件
- `src/layouts`：应用壳、头部、侧栏、菜单、标签页、主题抽屉
- `src/store/modules`：Pinia 模块，包含 app、auth、route、tab、theme
- `src/service/api`：接口封装与请求参数拼装
- `src/service/request`：统一请求实例、鉴权头、加密、错误处理、token 刷新
- `src/router/routes`：自定义路由定义
- `src/router/elegant`：自动生成的路由产物
- `src/theme/settings.ts`：默认主题与布局设置
- `build/plugins/router.ts`：elegant-router 配置与路由元信息生成逻辑
- `src/hooks/common/table.ts`：列表页表格 hook 主入口
- `src/hooks/common/form.ts`：表单校验与表单实例 hook
- `src/constants/status-tag.ts`：业务对象状态颜色（ElTag type）集中配置
- `src/styles/scss/element-plus.scss`：当前项目表格、弹层、按钮、表单密度与公共壳样式标准
- `packages/*`：项目内本地共享库
- `docs/`：当前工作上下文的一部分，做架构级、权限级、页面规范级改动前优先查阅

## 生成文件

除非有非常明确的理由并且同步维护生成流程，否则不要手工修改生成文件。

- `src/router/elegant/imports.ts`
- `src/router/elegant/routes.ts`
- `src/router/elegant/transform.ts`
- `src/typings/elegant-router.d.ts`
- `src/typings/components.d.ts`
- `docs/frontend-page-resource-manifest.json`

如果路由生成产物过期或不一致，执行 `pnpm gen-route`。
如果页面资源清单需要同步，执行 `pnpm gen:page-resource-manifest`。

## 路由与导航开发口径

- 新增业务页面时，优先通过页面文件与 `build/plugins/router.ts` 补齐路由，不要手工在多个位置重复注册同一页面。
- 路由 `meta` 的中心落点是 `build/plugins/router.ts`；新增业务页的 `icon`、`order`、`roles`、`keepAlive` 优先在那里集中维护。
- 当前代码链路仍保留 `i18nKey` 兼容字段，但它是兼容保留项，不是新增业务页面必须补齐的默认要求。
- `meta.constant = true` 的路由属于常量路由；其余默认属于权限路由。
- 常量路由维护入口优先是 `build/plugins/router.ts` 和 `src/router/routes/custom-routes.ts`，不要把常量路由散落到业务页面逻辑里。
- 菜单图标约定属于路由契约的一部分：`meta.icon` 表示 Iconify 图标，`meta.localIcon` 表示本地 SVG 图标；不要混用字段语义。

### 对象上下文业务域入口页口径

- `product`、`project` 这类对象上下文业务域的入口页，按 `docs/rdms/rdms-object-context-navigation-implementation-notes.md` 口径，本来就是“先进入业务域入口页，再选择对象建立上下文”；不要把“入口页是可点击菜单”误判成配置错误。
- 对象上下文业务域的“入口态”页面，例如 `product_list -> /product/list -> view.product_list`，可以作为左侧一级入口菜单实际命中的页面；这不等于已经进入对象上下文态。
- 不要为了修复“点击入口页后只剩内容页、布局壳消失”的问题，直接要求把对象域入口菜单从“菜单”改成“目录”。先检查当前是不是动态权限路由模式，以及后端 `get-user-routes` 返回是否缺少业务域根路由。
- 在 `VITE_AUTH_ROUTE_MODE=dynamic` 下，如果后端只返回对象域入口叶子页，而没有返回本地静态骨架中的业务域根路由，例如缺少 `product -> layout.base`、只返回 `product_list -> view.product_list`，前端必须在动态路由归一化阶段补回本地业务域骨架，而不是让入口页直接裸挂成顶层 `view.*` 路由。
- 对象上下文业务域的稳定来源仍应是本地路由骨架：业务域根路由负责 `layout.base`，入口页负责对象列表或对象选择，真正的对象功能页继续挂在该业务域下。动态路由兼容逻辑只能做“补骨架”和“对齐入口”，不要反过来推翻这层结构。
- 后续新增新的对象上下文业务域时，至少同步检查这几处是否闭环：本地静态路由骨架、`src/constants/object-context.ts` 中的 `domainKey / entryRouteKey / entryRoutePath / fallbackDefaultRouteKey`、动态路由归一化逻辑、对象上下文 store 与头部菜单切换逻辑。

## 分层职责约束

### `src/views`

- 页面层负责页面编排、交互状态、表单行为和对 store/service 的组合调用。
- 不要在页面组件里散落 URL 拼接、token 注入、统一错误提示或权限路由推导逻辑。

### `src/components`

- 共享组件负责可复用 UI 或局部业务部件。
- 不要把只服务于单个页面的复杂流程长期堆在公共组件目录中。

### `src/service/api`

- API 层负责接口封装、请求参数归一化、查询字符串拼装和返回类型对齐。
- 不要在 `views`、`store` 或 `components` 中重复手写同一接口地址和参数序列化逻辑。

### `src/service/request`

- 请求层负责统一请求实例、鉴权头、接口加密、成功码判定、token 刷新和通用错误处理。
- 除非任务明确需要，不要平行引入新的 `axios`/`fetch` 调用链绕开现有封装。

### `src/store/modules`

- Store 负责跨页面共享状态，例如认证、路由、标签页、主题、布局和全局 UI 状态。
- 临时性的页面局部状态优先留在页面组件或 composable 中，不要无边界堆进全局 store。

### `src/router` 与 `build/plugins/router.ts`

- 路由、菜单、权限标识、首页配置和路由元信息优先沿用当前 elegant-router 与 route store 链路。
- 不要只在页面里临时写条件分支来替代正式的路由、菜单或权限配置。

### `src/layouts` 与 `src/theme`

- 布局壳和主题设置是全局行为源头，相关改动要同时检查布局组件、theme store 和默认设置。
- 不要在业务页面里复制一套平行的布局状态或主题状态。

## 业务页面开发风格

- 页面组件保持“编排层薄”。页面文件主要负责搜索参数、表格 hook、列定义、弹层开关、接口调用编排，不把大量表单细节和重复交互直接堆在页面根组件里。
- 列表页优先拆出同目录下的 `modules/*` 子组件，例如搜索组件、操作弹层、详情抽屉、资源面板等。
- 系统管理下现有 `user`、`role`、`menu`、`dict` 页面可以作为参考实现，新增同类页面优先沿用它们的拆分方式。
- 新增或触达列表页搜索组件时，必须按 `docs/table-search-fields-usage.md` 使用 `src/components/custom/table-search-fields.vue` 的 `fields` 声明式配置，不得手写 `ElRow / ElCol / ElFormItem` 搜索区骨架；只有字段存在复杂联动、自定义插槽或 `TableSearchFields` 明确无法承载时，才允许退回 `src/components/custom/table-search-panel.vue`，并需要在实施说明中写明原因。搜索模块本身应尽量只接收 `model` 和必要选项，只向外发出 `reset` / `search`，不直接承载列表请求逻辑。
- 列表能力优先复用 `src/hooks/common/table.ts` 中的 `useUIPaginatedTable`、`useTableOperate`、`defaultTransform`。
- 表单能力优先复用 `src/hooks/common/form.ts` 中的 `useForm`、`useFormRules`。
- 当前项目的真实业务口径是“内网中文优先”。新增业务页不必为了形式强行补全国际化键；但如果是在已有大量 `$t(...)` 的页面或模块内继续开发，优先保持该局部代码风格一致，不要半页中文直写、半页国际化混用。

## 表格、搜索区与操作列约束

- 搜索区按钮组必须固定在第一行最后一个位置；存在折叠项时，按钮顺序保持为“展开/收起 -> 重置 -> 查询”。这是 `TableSearchFields` 的布局契约，不允许因为查询条件不足、展开/收起或响应式样式把按钮提前到中间位置或挤到后续行。
- 不要在每个页面重新拼一套搜索区骨架；常规查询条件必须使用 `TableSearchFields`，通过 `columns` 控制每行格子数和折叠阈值。`columns` 表示首行总格数，其中最后 1 格永远留给按钮区；字段不足 `columns - 1` 时由公共组件补空占位，字段超过时剩余字段进入展开区。类似项目管理入口页这类 4 个查询条件的场景，必须使用 `:columns="4"`，形成“3 个条件 + 按钮区”的首行布局。
- 表格操作列优先复用 `src/components/custom/business-table-action-cell.tsx`。
- 操作数 `<= 2` 时默认直出；操作数 `> 2` 时优先收敛为 `1 个直出主按钮 + 1 个更多按钮`。
- 新增列表页如果使用 `ElCard` 承载需要撑满剩余高度的 `ElTable height="100%"`，`body-class` 优先使用公共类 `business-table-card-body`，该类由 `src/styles/scss/element-plus.scss` 统一维护；不要再为每个页面新增 `xxx-table-card-body` 私有样式。历史页面已有私有类时不强制专项回改，当前任务触达相关页面再按公共类收敛。
- 表格、按钮、弹层、表单的尺寸和间距标准优先由 `src/styles/scss/element-plus.scss` 和公共组件承接，不在业务页面散落写新的局部尺寸作为事实标准。

## 表单与弹层约束

- 新增、编辑能力优先沿用 `ElDialog / ElDrawer / ElForm / ElScrollbar / #footer` 这一套标准组合，不额外创造新的弹层交互模型。
- 轻中量表单优先复用 `src/components/custom/business-form-dialog.vue`；字段较多、需要保留列表上下文或承载重型控件时，再考虑 `src/components/custom/business-form-drawer.vue`。
- 表单分组优先复用 `src/components/custom/business-form-section.vue`。
- `dialog` 宽度优先按纯表单字段数分三档：`<= 6` 个字段用 `sm`，默认单列，目标宽度 `520px`；`7 ~ 14` 个字段用 `md`，默认双列，目标宽度 `720px`；`> 14` 个字段用 `lg`，仍以双列为主，目标宽度 `960px`。宽度只做响应式收缩，实际宽度不超过 `calc(100vw - 32px)`；不因为单个 `textarea` 自动升档，也不做列数响应式折叠。
- 常规 CRUD 表单优先使用 `label-position="top"`、`ElRow + ElCol` 双列布局、`gutter=16`；普通字段优先 `span=12`，长文本或重量级字段优先 `span=24`。如果整体字段数 `<= 6`，默认按单列表单理解。
- 当纯表单 `dialog` 因字段数 `<= 6` 归入 `sm` 时，不能只改 `preset`；字段布局也要同步落到单列，常规 `ElCol` 应使用 `span=24`，除非该弹框已经被明确判定为复合内容特例。
- 左右分栏、表单 + 表格、表单 + 树、关系编辑器、时间线、大段说明区这类复合内容 `dialog`，不强行按字段数归类；可按内容复杂度单独评估使用 `md`、`lg` 或更宽值，但只有在无法合理归入“纯表单三档”时才允许特例。
- 禁止用页面级宽范围样式直接覆盖整页 `.business-form-dialog` 来统一放大弹框；如确实需要特殊宽度，只能精确作用于目标弹框，且不能误伤同页面其他 `dialog`。
- 底部按钮顺序固定为“取消 -> 确认”，并保持右对齐。
- 单选组和开关类字段优先复用仓库既有样式钩子，例如 `business-form-radio-group`、`business-form-switch-field`。
- 权限控制按钮默认采用“无权限不渲染”口径，不要把纯权限不足的入口做成禁用态再展示给用户；只有业务状态暂时不可操作、但仍需让用户感知入口存在时，才允许保留禁用态。

## 接口、路由与权限约束

- 默认沿用 `src/service/request/index.ts` 中现有请求链路，不要另造一套鉴权、加密、错误处理或 token 刷新机制。
- 接口前缀、服务常量优先复用现有常量定义，例如 `src/constants/service.ts`。
- 后端契约变化时，至少同步检查 `src/service/api/*`、`src/typings/api/*`、相关页面调用和说明文档是否一致。
- 涉及路由、菜单、权限的改动时，同时检查 `build/plugins/router.ts`、`src/router/routes/*`、`src/store/modules/route/*` 和相关文档。
- 对于可再生的路由产物，优先修改源配置并执行 `pnpm gen-route`，不要把手工修补生成文件当成常规方案。

## 防重复提交（两层联防）

用户快速双击、键盘连按 Enter、`ElMessageBox.confirm` 的"确定"按钮内置无 loading 等场景，都可能让同一写操作发出多次。仓库采用两层防御，新增写操作功能时按顺序检查：

### 第一层：业务按钮的 loading 锁（视觉防御）

- 新增、编辑入口优先使用 `src/components/custom/business-form-dialog.vue` 或 `src/components/custom/business-form-drawer.vue`，它们在 `submit` 流程内 await 接口期间会自动将"确认"按钮置为 `loading` + `disabled`。
- 不要裸手写 `<ElButton @click="submit">` 直接调接口；若必须使用裸 `ElButton`，需要自行绑定 `:loading` 并在 await 接口期间锁住按钮。
- 删除二次确认使用 `ElMessageBox.confirm` 时，其内部"确定"按钮没有 loading 能力，必须依赖第二层兜底，不要尝试改造 confirm 的内部按钮。

### 第二层：请求层全局去重（逻辑兜底）

- 入口：`src/service/request/dedupe.ts` 提供 `withDedupe`，已在 `src/service/request/index.ts` 包住统一的 `request` 实例；`demoRequest` 未启用。
- 指纹：`method + 完整 URL + 排序后的 params + 稳定序列化的 body`；body 内对象按 key 排序，数组保序。
- 行为：写操作（`POST` / `PUT` / `DELETE` / `PATCH`）在第一次请求 pending 期内，若再次发起指纹相同的请求，自动复用第一次的 Promise，不发出第二次实际请求；调用方两次拿到完全相同的返回对象。
- 跳过条件（即不去重，按原逻辑发出）：`GET` / `HEAD` / `OPTIONS`，请求体为 `FormData` 或 `Blob`（上传场景），调用方显式传 `{ dedupe: false }`。
- 业务调用方零感知：新增接口默认即享受兜底，不需要在 `src/service/api/*` 或页面层做任何改动。
- 极少数业务确实允许短时间内并发提交完全相同的写请求时，在调用处显式传 `request({ ..., dedupe: false })` 单接口关闭。
- 兜底超时 30 秒：极端情况下若某次 Promise 未 settle，pending 条目过期后下一次相同请求视为新请求，避免内存泄漏。

### 设计责任划分

- 视觉层负责"按下立刻锁住按钮"的用户感知；逻辑层负责"即使锁失败也只发一次"的实际接口保护。
- 不要因为有第二层兜底就省略第一层 loading 锁：用户没有视觉反馈会再次点击；也不要试图在业务页面再造一套请求去重逻辑。

## 运行时字典使用口径

- 运行时字典统一由 `src/store/modules/dict/index.ts` 管理，登录后通过 `/system/dict-data/frontend-cache` 初始化；不要在业务页面重复直调字典接口。
- 字典编码常量优先收敛在 `src/constants/dict.ts`，不要在页面里散落硬编码 `dictType`。
- 不要猜测字典编码。新增某个业务字段对应的字典前，先从“后端接口文档、后端字段契约、系统字典管理页”确认真实 `dictType`，再写入 `src/constants/dict.ts`。
- `src/constants/dict.ts` 中每个导出的字典常量，尽量补中文注释，至少说明两件事：对应哪个业务字段、这个编码是从哪里确认出来的。
- 如果后端实际 `dictType` 带有历史命名痕迹，例如当前对象方向仍叫 `rdms_product_direction`，前端常量名优先按真实业务语义命名，不要继续把历史误导扩散到页面代码里。
- 表单下拉优先使用 `src/components/custom/dict-select.vue`。
- 普通文案回显优先使用 `src/components/custom/dict-text.vue`。
- 需要标签态回显时优先使用 `src/components/custom/dict-tag.vue`，标签颜色仍由业务页面自己决定。
- 在 `script setup`、TSX 列格式化、复杂判断里，优先使用 `src/hooks/business/dict.ts` 提供的 `useDict(dictCode)`，常用能力包括 `dictOptions`、`getItem`、`getLabel`、`getLabels`、`hasValue`。
- `DictSelect` 默认只展示启用项；确实需要包含禁用项时，显式传 `:only-enabled="false"`。

简单示例：

```vue
<DictSelect v-model="form.directionCode" :dict-code="RDMS_OBJECT_DIRECTION_DICT_CODE" />
<DictText :dict-code="RDMS_OBJECT_DIRECTION_DICT_CODE" :value="row.directionCode" />
<DictTag :dict-code="SYSTEM_USER_COMPANY_DICT_CODE" :value="row.companyCode" type="info" />
```

```ts
const { getLabel, getLabels } = useDict(RDMS_OBJECT_DIRECTION_DICT_CODE);

const directionLabel = getLabel(row.directionCode);
const directionLabels = getLabels(row.directionCodes, { separator: '，' });
```

确认字典编码的典型方式：

- 后端接口文档直接写明字段使用哪个字典，例如产品 `directionCode -> rdms_product_direction`；如果该编码只是历史命名，前端常量名仍按通用业务语义命名。
- 当前系统已有页面或接口已经稳定使用某个字典，例如用户所属公司 `company -> system_user_company`。
- 如果以上两种都没有，就先让后端或业务明确 `dictType`，不要前端自己命名。

## 业务对象状态颜色集中口径

- 各业务域（产品、项目、需求、任务、执行、工单等）的 `statusCode -> ElTag type` 集中维护在 `src/constants/status-tag.ts`，不要在各业务页面或模块内散落硬编码同一份映射。
- 通用入口是 `getStatusTagType(domain, statusCode)`，未匹配的 `statusCode` 默认回退到 `'info'`。
- 业务模块按域写薄包装暴露给页面调用，例如 `getExecutionStatusTagType(code)` 内部调用 `getStatusTagType('projectExecution', code)`，避免页面直接耦合到 domain 字符串。
- 新增对象域时同步两处：`StatusDomain` 增加枚举值；`statusTagTypeRegistry` 添加对应 `statusCode -> StatusTagType` 映射。
- 后端契约：未来若状态字典开始返回颜色字段，调用方应优先使用后端值，缺失时再回退到 `getStatusTagType` 的前端兜底映射，不要直接绕开集中配置另写一份。

## 页面资源与菜单目录约束

- 页面组件键、页面资源、菜单目录是三层不同概念，不要把它们当成同一个值。
- `component` 决定“渲染哪个页面组件”；菜单目录决定“挂在哪个业务目录下”和最终 URL；页面资源主要用于从白名单中选择并回填组件信息。
- 不要因为组件键是 `view.system_dict`，就推导它只能挂在 `/system/dict`；同一个页面组件允许挂在新的业务目录下复用。
- 页面资源白名单中的标准路径是参考路径，不应反向覆盖当前菜单树已经确定的最终 URL。
- 涉及菜单编辑器或页面资源选择逻辑时，优先保证“组件可解析、资源合法、最终 URL 由菜单树决定”，不要强绑页面资源标准路径和父级目录前缀。

## 代码约定

- 优先使用现有别名导入（`@/...`、`~/...`），避免过长的相对路径。
- 保持与 TypeScript 严格模式兼容。
- 后端返回的主键 ID、用户 ID、对象 ID、雪花 ID、Long ID 等，一律优先按 `string` 在前端接收和传递，不要默认写成 `number`。
- 这是强约束，不是建议项。原因包括：JavaScript `number` 无法稳定承载长整型精度、接口序列化后可能出现精度丢失、运行时还容易出现 `number/string` 键不一致，最终导致回显、筛选、映射、路由参数、对象上下文等逻辑异常。
- 这条约束要落实到所有层：`typings`、API 返回类型、页面表单 `model`、组件 `props` / `emits`、`ElSelect` 的 `value`、路由参数、查询参数、`Map` 键、筛选条件、store 状态，一律优先使用 `string` / `string[]`。
- 明确禁止把 ID 当成普通数值处理。禁止写法包括但不限于：`Number(id)`、`+id`、`parseInt(id)`、`parseFloat(id)`、`Math.floor(id)`，以及任何“为了比较、传参、回填、提交而把 ID 转成 number”的做法。
- 比较、映射、筛选 ID 时，默认按字符串语义处理，例如 `id === targetId`、`Map<string, T>`、`Set<string>`，不要混用 `number/string` 双口径。
- 如果后端当前接口暂时还返回数值型 ID，前端也必须尽可能在 `typings`、API 适配层或进入业务层前转成 `string`，不要把 ID 按 `number` 扩散到页面、store 和组件里。
- 但要注意：如果后端把超出 JS 安全整数范围的 Long 直接作为 JSON 数字返回，前端在业务层再 `String(number)` 只能得到“已经丢精度后的错误字符串”。这种情况必须明确记为接口契约风险，不要误判为“前端已安全处理”。
- 因此，新增或改造接口时，最稳妥的契约仍然是：后端长整型 ID 直接按字符串返回；前端全链路按字符串接收和传递。若后端暂未改，前端侧也不得新增 `number` 口径 ID 用法。
- API 适配层兜底（实操约束）：所有从后端接收的数值型 ID 字段（不论后端实际返回 `string`、`number` 或两者混合），都必须在 `src/service/api/*` 的 normalize 或 map 函数中显式调用 `String(rawId)` 归一一次；前端业务层（`views`、`store`、组件、`Map` 键、路由参数）只接收 `string` 形态，永远不需要自己 `String()`。这条与后端是否做了 Long → String 全局序列化无关——后端做了是双保险，没做且字段取值始终在 JS 安全整数内（例如 `infra_file_config.id` 永远是两位数）也是合理选择，前端 normalize 已经把口径收死，业务层无感。但这条不开按字段取值范围豁免的口子：前端 normalize 是无差别的，任何 ID 都要 `String()`，不要按某个字段当前取值大小决定要不要走 normalize，避免后续逐步污染仓库的 ID 纪律。
- 对仓库中的历史代码，原则是“不再新增 number 口径 ID，当前任务触达相关链路时优先顺手矫正”；不要继续复制历史写法。
- 遵循仓库现有的 Vue SFC 风格：`script setup`、类型化 store、职责单一的小型 composable/helper。
- 修改界面时优先延续 `src/layouts` 和 `src/theme` 中已有的 UI 模式，不要平行引入另一套设计体系。
- 注释保持克制，只在代码本身不够直观时补充必要说明。

## 注释与编码

- 新增或修改代码时，关键分支、关键约束和非直观实现可以补充简洁中文注释。
- 不要为了省事删除原有有效注释，也不要添加没有信息量的注释。
- 写入中文内容时保持 UTF-8 编码，并自行确认显示正常；不要用改成英文来规避编码问题。

## 校验建议

对有实际影响的代码改动，优先执行：

- 对前端页面、交互、样式类任务，除非用户明确要求新增测试、运行测试，或当前任务本身就是修测试/补测试，否则默认不补前端测试，也不主动跑前端测试命令。
- 上述前端任务默认只做静态校验；最小校验口径是 `pnpm typecheck`。如果需要更严格的静态检查，再补 `pnpm lint`。
- `pnpm typecheck`
- `pnpm lint`

如果改动涉及路由，额外执行：

- `pnpm gen-route`

如果改动影响页面资源清单、菜单资源选择或页面白名单，额外执行：

- `pnpm gen:page-resource-manifest`

静态校验时，至少自查以下几点：

- 调用链是否闭环，改动是否落在正确的分层位置
- 路由、菜单、权限标识、主题状态或资源注册是否前后一致
- 改动范围是否控制在当前任务所需的最小集合内
- 文档、类型定义、接口封装或生成产物是否需要同步更新

## 提交与脚本约束

- `pre-commit` 会执行 `pnpm typecheck && pnpm lint && git diff --exit-code`，因此“代码能跑”不等于“可以提交”。
- `pnpm lint` 实际会执行 `eslint . --fix`；提交失败后要检查是否有被自动修复但尚未重新暂存的文件。
- 提交规范说明以 `docs/前端提交规范与示例.md` 为准；最稳妥的提交方式是执行 `pnpm commit:zh`，按交互选择 `type`、`scope` 和 `description`。
- `commit-msg` 钩子会校验 Conventional Commits；推荐使用 `pnpm commit:zh` 生成提交信息。
- 如果手动提交，执行 `git commit -m "type(scope): 描述"`，并确保 `type`、`scope`、描述写法与 `docs/前端提交规范与示例.md` 保持一致。
- 提交信息基础格式遵循 `type(scope): 描述`。
- 写 Node ESM 脚本时，避免沿用 `__filename`、`__dirname` 这类下划线悬挂命名。
- 能并发的批量异步任务优先 `Promise.all(...)`，不要默认在循环体里直接 `await`。
- 手写 `new Promise(...)` 时优先使用 block 写法，不要把 executor 写成隐式返回值的单表达式箭头函数。
- 一个函数如果开始同时承担“判断 + 转换 + 组装 + 递归”，优先拆 helper，避免把复杂度堆到单个函数里。

## 代理工作说明

- 除非用户明确要求，否则不要主动执行任何 git 操作，包括但不限于 `git status`、`git diff`、`git add`、`git commit`、`git restore`、`git reset`、`git checkout`。
- 如果任务需要识别用户已有改动，优先通过当前文件内容和直接读取文件来判断；只有用户明确要求查看 git 状态时，才执行对应 git 命令。
- 在工作树不干净时，不要回退与当前任务无关的变更。
- 修改布局或主题行为时，同时检查 `src/layouts/*` 和 `src/store/modules/theme/*`，因为相关逻辑分散在界面层和状态层。
- 修改路由或菜单时，同时检查 `build/plugins/router.ts` 和 `src/router/routes/*`。
- 做架构级、权限级或页面规范级修改前，优先查阅 `docs/` 中现有说明，避免与当前文档约定冲突。
