20260904173136
This commit is contained in:
1 parent
0928437c67
commit
4e167df0f0
40 files changed
+3471
-2314
No files matched your search
+78
-5
@@ -10,6 +10,8 @@
|
||||
|
||||
4. **修改范围最小化** — 只修改完成当前需求所必需的文件;发现无关的脏改动、临时文件或历史问题时,不擅自回滚或顺手重构。
|
||||
|
||||
5. **当前系统按内部 ERP 处理** — 默认使用场景是已登录的内部用户和受控网络。当前需求阶段不额外引入复杂的安全防护层、SQL 注入专项改造或前后端重复校验;除非用户明确提出,不因安全假设新增接口、中间层或配置系统。查询条件可以信任前端传入,后端重点完成查询、分页和数据格式转换。
|
||||
|
||||
## 前端代码风格
|
||||
|
||||
1. **少用语法糖** — 优先使用直白、易读的写法,避免为了炫技而使用语法糖,可读性优先。
|
||||
@@ -31,6 +33,39 @@
|
||||
|
||||
7. **命名反映职责** — 目录已经表达业务上下文时,文件名不再重复目录名;优先使用 `utils.js`、`constants.js` 等稳定名称,避免创建 `xxxHelper.js`、`xxxCommon.js` 等含义模糊的文件。
|
||||
|
||||
8. **为真实出现的第二个使用方而抽象** — 组件、组合式函数或页面级结构同理:只有出现第二个真实、形态接近的调用方(或确有确定性的同类页面即将落地)才抽取;不为"将来可能复用"预先抽一层,避免基于猜测设计接口。形似但数据与行为各异的页面(如模块树与菜单树),先各自平铺实现,等共性真正清晰后再合并。
|
||||
|
||||
### 命名约定
|
||||
|
||||
1. **按代码层次区分命名风格** — 前端 JavaScript 逻辑标识符使用 `camelCase`;后端返回字段、数据库字段和与表结构一一对应的业务数据使用 `snake_case`。不要为了追求全项目单一风格而把两类名称混在一起。
|
||||
|
||||
```js
|
||||
// 页面状态、局部变量和方法:camelCase
|
||||
const moduleTreeLoading = ref(false)
|
||||
const selectedModuleId = ref(null)
|
||||
async function loadModuleConfig() {}
|
||||
|
||||
// 后端业务数据和原始基线:snake_case
|
||||
const maindata = ref(null)
|
||||
const maindata_org = ref(null)
|
||||
const list_config = ref([])
|
||||
const list_config_org = ref([])
|
||||
```
|
||||
|
||||
2. **方法名统一使用 camelCase** — 采用职责明确的动词开头,例如 `loadModuleConfig()`、`handleSave()`、`buildSaveData()`、`clearModuleData()`。不使用 `loadMod()`、`dealData()`、`doIt()` 等含义不明确的缩写或泛化名称。
|
||||
|
||||
3. **禁止单词直接拼接** — 不使用 `maintableorg`、`subtablereceiveorg` 这类无法直观看出单词边界的名称。数据与基线必须使用 `_org` 分隔,例如 `maindata / maindata_org`、`fields / fields_org`。
|
||||
|
||||
4. **状态名称要能表达类型和职责** — 布尔值优先使用 `is`、`has`、`can`、`should` 前缀(如 `isLoading`、`hasChanges`、`canDelete`);ID 统一使用 `xxxId`;数组、集合使用复数名(如 `fields`、`unsavedModuleIds`)。避免使用 `data`、`info`、`flag`、`temp` 等无法说明职责的名称。
|
||||
|
||||
5. **避免含义不明的缩写** — 除领域内已经稳定且广泛使用的缩写(如 `id`、`api`、`i18n`)外,优先使用完整单词。例如使用 `containerData`,不使用 `zxData`;使用 `loadEdit()`,不使用 `loadMod()`。已有外部字段名或历史接口字段不因本规则擅自改名。
|
||||
|
||||
6. **API 层做命名边界转换** — API 封装函数的参数和前端局部对象使用 `camelCase`,请求体字段必须按后端契约保留原名(通常为 `snake_case`)。不得因为前端命名调整而修改接口字段、数据库字段、路由 meta、组件 props 或第三方库 API。
|
||||
|
||||
7. **Vue 公开接口统一模板命名** — 组件文件名和组件名使用 PascalCase;模板中的 props、事件和具名 `v-model` 使用 kebab-case,例如 `:module-data`、`@save-success`、`v-model:main-data`。组件内部变量和方法仍遵守前述 `camelCase` / `snake_case` 分层规则。
|
||||
|
||||
8. **新增代码遵守,旧代码按需迁移** — 不为了统一命名进行无需求的大范围重命名。修改旧代码时只在当前需求涉及的标识符上采用本规则,并同步更新全部引用和测试;除非用户明确要求,不保留仅用于兼容旧名称的转发别名。
|
||||
|
||||
### Store 设计
|
||||
|
||||
1. **统一使用 Setup Store** — 新增或改造 Pinia store 统一使用 `defineStore('name', () => {}, options)`,状态用 `ref`,派生状态用 `computed`,操作用普通函数。
|
||||
@@ -47,7 +82,23 @@
|
||||
|
||||
### 查询列表页面设计
|
||||
|
||||
(待补充)
|
||||
1. **查询条件由页面和模块配置共同决定** — 页面需要传入的业务固定条件(例如当前客户、当前单据 ID、费用类型)由业务代码组装;客户在列表或选择器中可以填写的条件,使用模块已有的查询配置(如 `s_field_query`)。
|
||||
|
||||
2. **不为每个业务动作编写特殊查询接口** — 优先使用现有通用列表查询接口,或新增一个通用的记录查询接口。接口接收模块、查询条件、分页和排序参数,不按“历史费用”“历史发货人”等场景分别新增接口。
|
||||
|
||||
3. **查询条件保持结构化** — 即使当前阶段信任前端传入的条件,也优先使用字段、操作符和值组成的对象,而不是在页面中散落拼接字符串。这样做是为了让条件容易阅读、复用和排查,不是为了增加安全限制。
|
||||
|
||||
```js
|
||||
{
|
||||
fieldId: 'mx_dw_id',
|
||||
operator: 'eq',
|
||||
value: currentData.b_customer_id,
|
||||
}
|
||||
```
|
||||
|
||||
4. **不提前做完整低代码查询配置** — 业务规则明确且只在一个页面使用时,直接写在页面代码中。只有同一规则被多个页面重复使用,并且确实需要由配置调整时,才抽取为公共配置。
|
||||
|
||||
5. **记录选择器只负责选择** — `FmsRecordPickerModal` 负责查询、筛选、分页和返回选中记录;字段回填由页面或已有映射函数处理;选择器不直接保存数据库。
|
||||
|
||||
### 详细表单页面设计
|
||||
|
||||
@@ -63,13 +114,13 @@
|
||||
|
||||
### 表单数据与变更检测
|
||||
|
||||
1. **保留 origin 基线** — 编辑页面加载完成后保留一份不可直接编辑的 `origin` 数据,页面修改只作用于 `draft` 数据。不要为每个字段单独维护一份旧值。
|
||||
1. **数据与基线成对命名 `xxx` / `xxx_org`** — 编辑页面加载完成后,每份数据保留"可编辑副本 `xxx`"与"原始基线 `xxx_org`"两个 ref:`xxx` 是页面修改作用的对象,`xxx_org` 是从数据库加载、不可直接编辑的基线(保存 diff、放弃修改还原用)。主对象用 `maindata / maindata_org`,关联数据用 `fields / fields_org`、`list_config / list_config_org` 等。**禁止用 `drafts` / `originals` 两个聚合对象,即使页面只有一个主对象也不例外。** 不要为每个字段单独维护一份旧值。
|
||||
|
||||
2. **保存使用 diff** — 保存前将 `origin` 与 `draft` 做差异对比,只提交新增、修改和删除的数据;没有变化时不调用保存接口。
|
||||
2. **保存使用 diff** — 保存前将当前值与 `_org` 基线做差异对比,只提交新增、修改和删除的数据;没有变化时不调用保存接口。
|
||||
|
||||
3. **origin 不提前覆盖** — 保存请求成功前不得把 `draft` 覆盖到 `origin`。保存成功后优先重新从服务端加载,拿到后端补充的主键、默认值和规范化数据,再更新 `origin`。
|
||||
3. **基线不提前覆盖** — 保存请求成功前不得把当前值覆盖到 `_org` 基线。保存成功后优先重新从服务端加载,拿到后端补充的主键、默认值和规范化数据,再更新 `_org`。
|
||||
|
||||
4. **新建数据单独处理** — 新建模式没有真实的 `origin`,可以使用 `null` 或空对象表示基线;临时 ID 只存在于 `draft`,提交成功后使用后端返回的真实 ID 更新或重新加载。
|
||||
4. **新建数据单独处理** — 新建模式没有真实基线,`_org` 可以用 `null` 或空数组表示;临时 ID 只存在于当前值,提交成功后用后端返回的真实 ID 更新或重新加载。
|
||||
|
||||
5. **多表单统一基线** — 主表、子表和配置表分别保留原始数据,但由同一个保存入口统一计算 diff 和提交,避免每个面板自行维护一套保存状态。
|
||||
|
||||
@@ -107,6 +158,28 @@ async function handleSave() {
|
||||
- `afterSave()` 必须在保存请求成功后执行;如果包含异步刷新,应使用 `await`。
|
||||
- 页面上的内联切换、批量操作等独立动作,可以使用 `saveToggle()`、`saveBatch()` 等专用名称,不套用主表单保存流程。
|
||||
|
||||
### 面板式配置页(左树右面板)设计
|
||||
|
||||
适用:左侧树切换对象、右侧按页签分多个面板编辑同一对象的多份数据(如模块管理的「基础/字段/列表/表单/查询/自动编码/多语言」)。核心分工是**编排层只持有数据,各面板通过 `v-model` 各管各的**:
|
||||
|
||||
1. **编排层不写中转回调** — 页面组件维护每份数据,各面板用 `defineModel` 直接读写属于自己的那份;不在父级写 `onModuleUpdate(section, data)` 这类"子面板 emit → 父级按 section 写回"的中转函数,也不在各面板各自重复加载或维护同一份状态。
|
||||
|
||||
2. **数据成对命名,不用两个聚合对象** — 每份数据一对 ref:`xxx` 为当前值(面板绑定),`xxx_org` 为该表从数据库加载的原始基线(保存 diff、放弃修改还原用),小写下划线命名。禁止用 `drafts` / `originals` 两个聚合对象包住全部子数据——**即使页面只有一个主对象,也一律 `maindata / maindata_org` 成对命名**。主表行(如 `maindata`)是单对象,其余是行数组(`fields`、`list_config`、`edit_config`、`query_config`、`autocode`、`i18n`)。
|
||||
|
||||
3. **加载就绪以主数据为判据** — 以主表行(如 `maindata`)是否为空决定配置区是否进入「加载中 / 暂无」占位,模板统一 `v-if="maindata"`。整批清空收敛到一个 `clearModuleData()`(或 `clearXxx()`)辅助函数,供切换对象、取消选择、加载失败复用。
|
||||
|
||||
4. **跨面板副作用由编排层 watch 承担** — 一个面板的改动要波及另一份数据时(删除分组后清理列表/编辑配置中的 `b_group_id` 引用;自动编码字段在表单中自动只读等),由编排层写**幂等** `watch` 联动;面板不直接改不属于自己的数据。
|
||||
|
||||
5. **表格编辑就地 patch + 换引用** — 表格组件对同一引用数组的 push 不敏感,行编辑采用"就地改行对象、提交时换新数组引用"触发渲染,不逐行深拷贝后再整表 emit。
|
||||
|
||||
6. **子表新增行用本地负数临时 id** — 新增行用 `createTempId()` 占位(`-1`、`-2`…),不逐个请求后端取号;保存时批量 `nextIdApi(N)` 一次换取 N 个真实雪花 id。主表(`s_module`)新建模块仍可单独取号。
|
||||
|
||||
7. **`_` / `v_` 前缀是内部派生字段** — 行内这类字段(参与勾选、受管标记等)不参与 diff、脏判断,也不随保存提交;提交前统一剥离。剥离会产生副本,会切断请求行与草稿行的引用共享,因此**必须在完成「临时 id → 真实主键 + 跨表外键重映射」之后**再剥离。
|
||||
|
||||
8. **`buildSaveData` 可为 async** — diff 出请求后,直接在函数内完成临时 id 换真实主键与跨表外键重映射再返回,无需再单独包一层 `prepareXxx()`;没有任何变化时返回 `null`。其余流程仍按「表单数据与变更检测」的 `handleSave → beforeSave → buildSaveData → 保存 → afterSave` 主流程。
|
||||
|
||||
9. **受管多语言不做实时响应** — 模块名/字段名/分组标题等自动生成的多语言行(受管键)只做两件事:加载模块后 reconcile 一次、保存前 reconcile 一次再 diff;不写 watch + 防抖去实时补齐。人工翻译不被 reconcile 覆盖。
|
||||
|
||||
### 布局与页签缓存
|
||||
|
||||
1. **缓存规则归布局目录** — keep-alive、页签名称、路由包装组件等规则属于布局层,相关函数放在 `src/layouts` 下,不放入全局工具目录。
|
||||
|
||||
Reference in new issue
Block a user