- 新增 buildFileProxyUrl 函数构建永久代理路径,避免富文本图片链接过期 - 重构 uploadFile 函数,统一将后端返回的数值型 ID 转换为字符串 - 在业务富文本编辑器中使用永久代理路径替换临时签名 URL - 完善 API 适配层 ID 规范,确保所有 ID 字段统一转换为字符串类型 - 移除废弃的编辑器相关路由和组件 - 更新构建代理配置以支持富文本图片直连访问 - 删除冗余的类型定义和依赖包
28 KiB
28 KiB
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、themesrc/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:表单校验与表单实例 hooksrc/constants/status-tag.ts:业务对象状态颜色(ElTag type)集中配置src/styles/scss/element-plus.scss:当前项目表格、弹层、按钮、表单密度与公共壳样式标准packages/*:项目内本地共享库docs/:当前工作上下文的一部分,做架构级、权限级、页面规范级改动前优先查阅
生成文件
除非有非常明确的理由并且同步维护生成流程,否则不要手工修改生成文件。
src/router/elegant/imports.tssrc/router/elegant/routes.tssrc/router/elegant/transform.tssrc/typings/elegant-router.d.tssrc/typings/components.d.tsdocs/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"。
简单示例:
<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" />
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 typecheckpnpm 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/中现有说明,避免与当前文档约定冲突。