20260903221055

This commit is contained in:
oneao committed 2026-09-03 22:10:55 +08:00
1 parent 3d4e2c7413
commit 0928437c67
46 files changed
+2121 -1643

No files matched your search

+108 -4
View File
@@ -6,27 +6,131 @@
2. **后端接口优先通用** — 优先使用或设计通用接口,尽量不写特殊接口(为单一业务场景定制的接口)。如需编写特殊接口,必须提前询问用户并获得确认。
3. **先读现有代码再改** — 修改前先确认已有组件、store、接口和测试的职责边界,优先复用现有模式,不因局部需求引入新的架构分支。
4. **修改范围最小化** — 只修改完成当前需求所必需的文件;发现无关的脏改动、临时文件或历史问题时,不擅自回滚或顺手重构。
## 前端代码风格
1. **少用语法糖** — 优先使用直白、易读的写法,避免为了炫技而使用语法糖,可读性优先。
2. **避免无意义的封装** — 没有必要的抽取就不抽取,不为一层简单调用再包一层方法。例如 `emit('xxx')` 这类直接调用即可,无需再封装成自定义方法。
3. **按目录归属组织公共代码** — 只服务于某个业务目录的函数,放在该目录下的 `utils.js`;只服务于布局、页面或组件的函数,放在对应目录,不放入全局 `src/utils`。
4. **公共函数抽取条件** — 满足以下任一条件才抽取:
- 至少有两个独立调用点;
- 逻辑本身较复杂,内联会明显影响可读性;
- 需要保证多个调用点使用同一规则(例如缓存名称、字段 key 生成规则)。
只有一个调用点且逻辑简单的代码直接写在使用处;没有调用点的代码必须删除。
5. **公共工具文件数量控制** — 一个业务目录原则上只保留一个公共 `utils.js`。只有职责明显独立、体量较大或确实需要单独测试的特殊工具,才允许单独成文件。禁止为了一个函数创建一个文件。
6. **避免重复实现同一规则** — 即使函数很短,只要多个位置必须保持完全一致,就集中维护一个实现,不在各处复制字符串拼接、编码或判断逻辑。
7. **命名反映职责** — 目录已经表达业务上下文时,文件名不再重复目录名;优先使用 `utils.js`、`constants.js` 等稳定名称,避免创建 `xxxHelper.js`、`xxxCommon.js` 等含义模糊的文件。
### Store 设计
1. **统一使用 Setup Store** — 新增或改造 Pinia store 统一使用 `defineStore('name', () => {}, options)`,状态用 `ref`,派生状态用 `computed`,操作用普通函数。
2. **一个 store 一个领域** — store 只管理一个明确领域。`app.js` 只保存应用壳层状态(主题、侧栏、页签、路由刷新等);权限、会话、i18n、业务配置分别由各自 store 管理。
3. **避免重复状态源** — 同一状态只能有一个真实来源,禁止同时在两个 store 或 store 与工具模块中各维护一份。跨 store 使用时直接调用所属领域的 store,不通过兼容门面转发。
4. **持久化显式声明** — 只有确实需要跨刷新保留的字段才配置 `persist`,并明确 `pick` 范围;临时请求状态、页签状态和加载状态不持久化。
5. **异步职责收敛** — 请求、加载状态、错误状态和刷新方法放在对应领域 store 中;页面只负责触发加载并消费结果,不在多个页面复制同一套请求缓存逻辑。
6. **不为旧接口保留高兼容层** — 当调用方已可控且用户未要求兼容时,直接迁移调用方并删除旧文件、旧导出和重复状态,不保留只转发一层的兼容文件。
### 查询列表页面设计
(待补充)
### 详细表单页面设计
保存流程方法的命名与顺序参考旧系统风格(仅约定骨架,实现自行设计):
页面加载流程也统一使用以下命名,方便从入口快速定位新建和编辑逻辑:
1. `load()` — 页面加载入口,判断当前是新建还是编辑模式
2. `loadNew()` — 新建模式初始化:加载默认值、生成临时 ID、准备初始配置
3. `loadEdit()` — 编辑模式加载:按主键加载已有主表和子表数据
如果页面不区分新建和编辑,直接使用语义明确的 `loadXxx()`,例如 `loadModuleConfig()`、`loadMenuTree()`。不使用 `loadMod()` 这类含义不明确的缩写。
`initNew()` 只在“清空当前草稿后重新初始化”确实是独立流程时保留;否则直接合并到 `loadNew()`,避免同一套新建逻辑分散在多个方法中。
### 表单数据与变更检测
1. **保留 origin 基线** — 编辑页面加载完成后保留一份不可直接编辑的 `origin` 数据,页面修改只作用于 `draft` 数据。不要为每个字段单独维护一份旧值。
2. **保存使用 diff** — 保存前将 `origin` 与 `draft` 做差异对比,只提交新增、修改和删除的数据;没有变化时不调用保存接口。
3. **origin 不提前覆盖** — 保存请求成功前不得把 `draft` 覆盖到 `origin`。保存成功后优先重新从服务端加载,拿到后端补充的主键、默认值和规范化数据,再更新 `origin`。
4. **新建数据单独处理** — 新建模式没有真实的 `origin`,可以使用 `null` 或空对象表示基线;临时 ID 只存在于 `draft`,提交成功后使用后端返回的真实 ID 更新或重新加载。
5. **多表单统一基线** — 主表、子表和配置表分别保留原始数据,但由同一个保存入口统一计算 diff 和提交,避免每个面板自行维护一套保存状态。
标准表单页面统一采用以下保存流程和方法名称,便于快速定位代码。方法可以包含页面自身的业务逻辑,但不要随意改名或调换主流程顺序:
1. `handleSave()` — 保存入口
2. `beforeSave()` — 保存前校验与数据准备
3. `buildSaveData()` — 构建保存请求数据
4. `afterSave()` — 保存成功后的处理
2. `beforeSave()` — 保存前校验、字段同步和数据准备;返回 `false` 时终止保存
3. `buildSaveData()` — 基于当前草稿构建保存数据;可以返回单个请求、请求数组或 `null`
4. 调用通用保存接口
5. `afterSave()` — 保存成功后的重新加载、状态更新、草稿清理和页面刷新
推荐主流程保持如下结构:
```js
async function handleSave() {
saving.value = true
try {
if (!(await beforeSave())) return false
const saveData = buildSaveData()
if (!saveData) return true
await saveObjectApi(saveData)
await afterSave()
return true
} finally {
saving.value = false
}
}
```
说明:
- `handleSave()` 是页面或表单组件的统一保存入口,不按业务对象改成多个不同名称。
- `beforeSave()` 只做保存前工作,不直接调用保存接口。
- `buildSaveData()` 只负责组装请求,不修改页面流程状态。
- `afterSave()` 必须在保存请求成功后执行;如果包含异步刷新,应使用 `await`。
- 页面上的内联切换、批量操作等独立动作,可以使用 `saveToggle()`、`saveBatch()` 等专用名称,不套用主表单保存流程。
### 布局与页签缓存
1. **缓存规则归布局目录** — keep-alive、页签名称、路由包装组件等规则属于布局层,相关函数放在 `src/layouts` 下,不放入全局工具目录。
2. **区分组件 key 与组件 name** — `:key` 用于区分组件实例;`keep-alive :include` 按组件 name 匹配。需要按路由参数独立缓存时,必须为每个 `fullPath` 生成稳定且唯一的 name,并在记录页签和渲染包装组件时使用同一规则。
3. **不为了抽象而抽象** — 如果缓存逻辑只在一个组件中使用,直接放在该组件;只有页签和布局都需要同一缓存名称规则时,才抽成布局目录公共函数。
## 多语言(i18n)约定
1. **module 目录下的页面不做多语言翻译** — 模块管理、菜单管理、多语言类型等内部管理页面仅面向 IT 人员,界面文案固定使用中文,不接入 s_i18n 翻译体系、不提供多语言编辑入口。
2. 面向客户使用的业务页面才需要做多语言翻译(界面文案、s_i18n 键管理、多语言编辑入口)。
3. **区分界面语言与业务语言** — UI 当前语言、UI 翻译目录、`s_i18n_type` 业务语言清单统一由 `stores/i18n.js` 管理;页面不得自行维护第二份语言状态。
4. **多语言 key 规则就近维护** — 只服务于模块管理的 key 生成和同步逻辑放在 `module-management/utils.js`;不得放入全局 `src/utils`。单页面使用一次的校验、透视合并或字段转换逻辑直接写在页面中。
5. **默认语言必须按标记判断** — 使用 `b_default` / `default` 字段确定默认语言,不得依赖语言数组第一项;语言列表为空时不得静默伪造默认语言掩盖接口或配置错误。
### 变更验收
1. **先做引用清理** — 删除或移动文件后,用全局搜索确认没有旧路径、旧导出和旧方法名残留。
2. **按影响范围测试** — 至少运行受影响模块的单元测试;涉及公共 store、布局缓存或跨页面工具时,补充相关页面和状态测试。
3. **区分新问题与历史问题** — 构建、lint 或全量测试若被改动前已存在的缺失文件、旧测试或环境问题阻断,应在结果中明确记录,不为了通过检查修改无关代码。