diff --git a/code/fms/.codebuddy/plans/module-management-full-implementation_944cf345.md b/code/fms/.codebuddy/plans/module-management-full-implementation_944cf345.md new file mode 100644 index 00000000..86530651 --- /dev/null +++ b/code/fms/.codebuddy/plans/module-management-full-implementation_944cf345.md @@ -0,0 +1,183 @@ +--- +name: module-management-full-implementation +overview: 基于新数据库结构.md和开发规范.md完整实现模块管理页面。左侧模块树+右侧7个子Tab,子Tab操作按钮靠左。不创建独立的service文件,数据加载/保存逻辑直接写在index.vue中。 +todos: + - id: create-main-page + content: 重写 index.vue 主页面:左侧 a-tree 模块树 + 右侧 a-tabs 布局,编写全部数据加载/保存逻辑(loadDataApi、saveObjectApi、diff、typeFromSqlType) + status: completed + - id: create-basic-panel + content: 使用 [skill:antdv-next] 创建 ModuleBasicPanel.vue 基本信息表单,根据模块类型控制字段可见性,支持新增和编辑 + status: completed + dependencies: + - create-main-page + - id: create-fields-panel + content: 创建 ModuleFieldsPanel.vue 字段定义面板,FmsTable 展示字段列表,实现「同步字段」功能,操作按钮靠左 + status: completed + dependencies: + - create-main-page + - id: create-list-edit-query-panels + content: 创建 ModuleListConfigPanel.vue、ModuleEditConfigPanel.vue、ModuleQueryConfigPanel.vue 三个配置面板,FmsTable 展示数据,操作按钮靠左 + status: completed + dependencies: + - create-main-page + - id: create-group-manager + content: 创建 FieldGroupManager.vue 共用分组管理弹窗,a-modal + a-tree 管理分组树增删改 + status: completed + dependencies: + - create-list-edit-query-panels + - id: create-powers-i18n-panels + content: 创建 ModulePowersPanel.vue 权限配置面板和 ModuleI18nPanel.vue 多语言面板,FmsTable 管理数据,操作按钮靠左 + status: completed + dependencies: + - create-main-page +--- + +## 用户需求 + +根据新数据库结构.md和开发规范.md,完整实现模块管理页面。当前页面是空占位,需要构建左侧模块树+右侧子Tab布局。 + +## 核心功能 + +### 主页面布局 + +- 左侧:使用 `a-tree` 展示模块树(directory/page/data 三级),选中节点加载详情 +- 右侧:使用 `a-tabs` 展示子Tab,根据选中模块的 `b_module_type` 动态显示对应Tab + +### 模块类型与Tab对应 + +| 模块类型 | 可见Tab | +| --- | --- | +| directory | 基本信息、权限配置、多语言 | +| page | 基本信息、权限配置、多语言 | +| data | 基本信息、字段定义、列表配置、编辑配置、查询配置、权限配置、多语言 | + + +### 各子Tab功能 + +1. **基本信息**:`a-form` 编辑 `b_module` 核心字段(b_code、b_name、b_module_type、b_parent_id、b_route、b_viewtable、b_savetable、b_keyfield、b_orderfield、b_canmenu、b_canuse、b_xh),根据模块类型控制字段可见性。新增模块先调 `nextIdApi` +2. **字段定义**:`FmsTable` 展示 `b_module_field`,不可手工增删行。提供「同步字段」按钮调用 `describeApi` 获取 `b_viewtable` 列结构并与已有字段合并(保留已有配置、追加新列、标记已删列为不可用)。可编辑 `b_name`、`b_type`、`b_default_value`、`b_options`、`b_canuse` +3. **列表配置**:`FmsTable` 展示 `b_module_field_list`,支持新增/删除行。可选字段来自 `b_module_field`(b_canuse=1)。`FieldGroupManager` 弹窗管理 `b_module_field_group` 分组树 +4. **编辑配置**:`FmsTable` 展示 `b_module_field_edit`,支持新增/删除行。同样通过 `FieldGroupManager` 管理分组 +5. **查询配置**:`FmsTable` 展示 `b_module_field_query`(b_component、b_operator 下拉选择),支持新增/删除行 +6. **权限配置**:`FmsTable` 展示 `b_module_power`(b_code、b_name),支持新增/删除行 +7. **多语言**:`FmsTable` 展示 `b_i18n`(b_key、b_locale、b_value),支持新增/删除行 + +### 操作按钮排列 + +所有子Tab面板内,新增、删除、保存等操作按钮**靠左对齐**(`justify-content: flex-start`) + +### 数据架构(不创建独立 service 文件) + +- `index.vue` 承担全部数据职责:加载模块树、加载完整配置、维护草稿、对比diff、调用 `saveObjectApi` +- 子组件只通过 `props` 接收数据,通过 `emit('update', newRows)` 上报变更 +- `index.vue` 保存流程:`diffRows(draft, original)` → `allocateTemporaryIds` → `saveObjectApi` + +## 技术方案 + +### 实现策略 + +**数据流集中管理**:`index.vue` 作为数据中枢,直接调用 `loadDataApi`、`saveObjectApi`、`nextIdApi`、`describeApi` 等通用接口。所有子组件是无状态的展示/编辑组件,仅通过 props/emit 与父组件通信。 + +### 数据流 + +``` +index.vue 选中模块 + → loadDataApi 加载 b_module + 各子表(Promise.all) + → cloneData 生成 originals(原始快照)和 drafts(编辑草稿) + → 各子组件接收 drafts 对应部分,用户编辑后 emit('update', newRows) + → index.vue 更新对应 draft + → 用户点击「保存」 + → 遍历各子表 diffRows(draft, original) + → allocateTemporaryIds 分配临时ID + → 构建 saveObjectApi 请求数组 + → 提交保存 → 重新加载刷新 originals +``` + +### 关键设计决策 + +1. **无独立 service 文件**:所有 `loadDataApi`/`saveObjectApi` 调用直接写在 `index.vue` 的 ` - - - - diff --git a/code/fms/fms-vue/src/views/i18n-management/index.vue b/code/fms/fms-vue/src/views/i18n-management/index.vue deleted file mode 100644 index 8521c13a..00000000 --- a/code/fms/fms-vue/src/views/i18n-management/index.vue +++ /dev/null @@ -1,193 +0,0 @@ - - - - - diff --git a/code/fms/fms-vue/src/views/module-management/components/FieldGroupManager.vue b/code/fms/fms-vue/src/views/module-management/components/FieldGroupManager.vue new file mode 100644 index 00000000..646d5b65 --- /dev/null +++ b/code/fms/fms-vue/src/views/module-management/components/FieldGroupManager.vue @@ -0,0 +1,256 @@ + + + + + diff --git a/code/fms/fms-vue/src/views/module-management/components/MetadataTablePanel.vue b/code/fms/fms-vue/src/views/module-management/components/MetadataTablePanel.vue deleted file mode 100644 index 740ce983..00000000 --- a/code/fms/fms-vue/src/views/module-management/components/MetadataTablePanel.vue +++ /dev/null @@ -1,115 +0,0 @@ - - - - - diff --git a/code/fms/fms-vue/src/views/module-management/components/ModuleBasicPanel.vue b/code/fms/fms-vue/src/views/module-management/components/ModuleBasicPanel.vue index 09233fc5..ebc5124b 100644 --- a/code/fms/fms-vue/src/views/module-management/components/ModuleBasicPanel.vue +++ b/code/fms/fms-vue/src/views/module-management/components/ModuleBasicPanel.vue @@ -1,65 +1,174 @@ diff --git a/code/fms/fms-vue/src/views/module-management/components/ModuleEditConfigPanel.vue b/code/fms/fms-vue/src/views/module-management/components/ModuleEditConfigPanel.vue new file mode 100644 index 00000000..060182ed --- /dev/null +++ b/code/fms/fms-vue/src/views/module-management/components/ModuleEditConfigPanel.vue @@ -0,0 +1,163 @@ + + + + + diff --git a/code/fms/fms-vue/src/views/module-management/components/ModuleEditPanel.vue b/code/fms/fms-vue/src/views/module-management/components/ModuleEditPanel.vue deleted file mode 100644 index 39663bdd..00000000 --- a/code/fms/fms-vue/src/views/module-management/components/ModuleEditPanel.vue +++ /dev/null @@ -1,131 +0,0 @@ - - - - - diff --git a/code/fms/fms-vue/src/views/module-management/components/ModuleFieldsPanel.vue b/code/fms/fms-vue/src/views/module-management/components/ModuleFieldsPanel.vue index dc9f4cb1..09b7d93c 100644 --- a/code/fms/fms-vue/src/views/module-management/components/ModuleFieldsPanel.vue +++ b/code/fms/fms-vue/src/views/module-management/components/ModuleFieldsPanel.vue @@ -1,52 +1,116 @@ + + diff --git a/code/fms/fms-vue/src/views/module-management/components/ModuleI18nPanel.vue b/code/fms/fms-vue/src/views/module-management/components/ModuleI18nPanel.vue new file mode 100644 index 00000000..ed785b7e --- /dev/null +++ b/code/fms/fms-vue/src/views/module-management/components/ModuleI18nPanel.vue @@ -0,0 +1,140 @@ + + + + + diff --git a/code/fms/fms-vue/src/views/module-management/components/ModuleListConfigPanel.vue b/code/fms/fms-vue/src/views/module-management/components/ModuleListConfigPanel.vue new file mode 100644 index 00000000..02bef05d --- /dev/null +++ b/code/fms/fms-vue/src/views/module-management/components/ModuleListConfigPanel.vue @@ -0,0 +1,168 @@ + + + + + diff --git a/code/fms/fms-vue/src/views/module-management/components/ModuleListPanel.vue b/code/fms/fms-vue/src/views/module-management/components/ModuleListPanel.vue deleted file mode 100644 index 8e864e9f..00000000 --- a/code/fms/fms-vue/src/views/module-management/components/ModuleListPanel.vue +++ /dev/null @@ -1,142 +0,0 @@ - - - diff --git a/code/fms/fms-vue/src/views/module-management/components/ModulePowersPanel.vue b/code/fms/fms-vue/src/views/module-management/components/ModulePowersPanel.vue index 75a92e32..cf98c8f1 100644 --- a/code/fms/fms-vue/src/views/module-management/components/ModulePowersPanel.vue +++ b/code/fms/fms-vue/src/views/module-management/components/ModulePowersPanel.vue @@ -1,34 +1,112 @@ + + diff --git a/code/fms/fms-vue/src/views/module-management/components/ModuleQueryConfigPanel.vue b/code/fms/fms-vue/src/views/module-management/components/ModuleQueryConfigPanel.vue new file mode 100644 index 00000000..36830518 --- /dev/null +++ b/code/fms/fms-vue/src/views/module-management/components/ModuleQueryConfigPanel.vue @@ -0,0 +1,165 @@ + + + + + diff --git a/code/fms/fms-vue/src/views/module-management/components/ModuleQueryPanel.vue b/code/fms/fms-vue/src/views/module-management/components/ModuleQueryPanel.vue deleted file mode 100644 index 00b75c8b..00000000 --- a/code/fms/fms-vue/src/views/module-management/components/ModuleQueryPanel.vue +++ /dev/null @@ -1,65 +0,0 @@ - - - diff --git a/code/fms/fms-vue/src/views/module-management/components/ModuleTranslationsPanel.vue b/code/fms/fms-vue/src/views/module-management/components/ModuleTranslationsPanel.vue deleted file mode 100644 index 8f09a72d..00000000 --- a/code/fms/fms-vue/src/views/module-management/components/ModuleTranslationsPanel.vue +++ /dev/null @@ -1,44 +0,0 @@ - - - diff --git a/code/fms/fms-vue/src/views/module-management/components/ModuleTreePanel.vue b/code/fms/fms-vue/src/views/module-management/components/ModuleTreePanel.vue deleted file mode 100644 index 415d6cdf..00000000 --- a/code/fms/fms-vue/src/views/module-management/components/ModuleTreePanel.vue +++ /dev/null @@ -1,164 +0,0 @@ - - - - - diff --git a/code/fms/fms-vue/src/views/module-management/components/cellRenderers.js b/code/fms/fms-vue/src/views/module-management/components/cellRenderers.js new file mode 100644 index 00000000..bfd6f31a --- /dev/null +++ b/code/fms/fms-vue/src/views/module-management/components/cellRenderers.js @@ -0,0 +1,64 @@ +import { h } from "vue"; +import { Button, Popconfirm, Select } from "antdv-next"; + +// 单元格渲染器统一采用 stk-table-vue 的 customCell 写法: +// customCell 会被当作函数式组件调用(props 含 row/col/rowIndex/cellValue), +// 必须返回 h(...) 生成的 VNode,返回普通对象会渲染成 [object Object]。 + +// 删除操作单元格 +export function createDeleteCell(onDelete) { + return (props) => + h( + Popconfirm, + { + title: "确定删除该记录?", + okText: "删除", + okButtonProps: { danger: true }, + onConfirm: () => onDelete(props.row), + }, + { + default: () => + h( + Button, + { type: "link", size: "small", danger: true, onClick: (e) => e.stopPropagation() }, + "删除", + ), + }, + ); +} + +// 是否(0/1)切换单元格 +export function createToggleCell(field, onChange) { + return (props) => { + const checked = props.row[field] === 1; + return h( + "span", + { + style: { cursor: "pointer" }, + onClick: (e) => { + e.stopPropagation(); + onChange(props.row, field); + }, + }, + checked ? "是" : "否", + ); + }; +} + +// 下拉选择单元格。getOptions 返回最新选项(保持响应式)。 +export function createSelectCell({ value, getOptions, onChange, placeholder, allowClear }) { + return (props) => + h(Select, { + value: + props.row[value] != null && props.row[value] !== "" + ? String(props.row[value]) + : undefined, + size: "small", + style: { width: "100%" }, + options: getOptions(), + allowClear: allowClear !== false, + placeholder: placeholder || "请选择", + "onUpdate:value": (v) => onChange(props.row, v), + onClick: (e) => e.stopPropagation(), + }); +} diff --git a/code/fms/fms-vue/src/views/module-management/index.vue b/code/fms/fms-vue/src/views/module-management/index.vue index cda2cba5..d9384b48 100644 --- a/code/fms/fms-vue/src/views/module-management/index.vue +++ b/code/fms/fms-vue/src/views/module-management/index.vue @@ -1,679 +1,705 @@ diff --git a/code/fms/开发规范.md b/code/fms/开发规范.md index 7fdd8a70..80306be1 100644 --- a/code/fms/开发规范.md +++ b/code/fms/开发规范.md @@ -2,6 +2,7 @@ ## 一、构建与测试 +- **不要过多封装** - 完成功能后不主动执行构建和测试。 - 只有用户明确要求时,才执行构建、测试或启动服务。 - 交付时说明本次未执行构建和测试。 @@ -27,6 +28,7 @@ - 不使用 CSS 重复实现组件已有的尺寸、状态、交互和响应式能力。 - 新增 CSS 应限制在当前组件作用域内,避免影响其他页面。 - 不需要兼容移动端,按桌面端实现即可。 +- 表格尽量使用 `fms-table` 组件(项目封装的表格组件),不重复从零实现表格能力。 ## 四、变更范围 diff --git a/code/fms/新数据库结构.md b/code/fms/新数据库结构.md index c02601ca..c10f857d 100644 --- a/code/fms/新数据库结构.md +++ b/code/fms/新数据库结构.md @@ -6,6 +6,7 @@ - 字段来源于数据模块配置的查询视图。系统不创建业务字段,只同步视图字段,并维护其展示、编辑、查询和权限配置。 - 页面和业务逻辑不完全元数据化。海运等复杂业务页面由 Vue 页面显式组合多个数据模块,元数据只提供 Table、Form 和 Query 配置。 - 字段基础定义、列表、编辑、查询四个职责分离;一个字段可以只参与其中任一场景。 +- 编辑规则与列表布局解耦:`b_module_field_edit` 是通用编辑配置,FmsForm 和 FmsTable 可按自身规则消费;`b_module_field_list` 只描述列表展示,不保存编辑状态。 - 模块权限控制访问,操作权限控制按钮动作;操作权限必须依赖模块访问权限。 - 表名和字段名沿用旧系统的 `b_` 风格。全部 `b_id` 使用 `bigint` 雪花 ID,返回前端时序列化为字符串。 - 布尔字段统一使用 `int`,`1` 表示是、`0` 表示否;`b_xh` 数值越小越靠前。 @@ -17,16 +18,16 @@ b_module |-- parent_id --> b_module |-- 1:N --------> b_module_field - |-- 1:N --------> b_module_field_edit_group + |-- 1:N --------> b_module_field_group -- parent_id --> b_module_field_group |-- 1:N --------> b_module_power ---- 1:N ----> b_user_power `-- 1:N --------> b_user_module b_module_field - |-- 0:N --------> b_module_field_list -- parent_id --> b_module_field_list - |-- 0:1 --------> b_module_field_edit + |-- 0:N --------> b_module_field_list -- group_id --> b_module_field_group + |-- 0:1 --------> b_module_field_edit -- group_id --> b_module_field_group `-- 0:1 --------> b_module_field_query -b_module / b_module_field / b_module_field_list / b_module_field_edit_group / b_module_power +b_module / b_module_field / b_module_field_list / b_module_field_group / b_module_power `-- b_i18n --> b_i18n.b_key ``` @@ -58,15 +59,15 @@ b_module / b_module_field / b_module_field_list / b_module_field_edit_group / b_ - `b_parent_id` 外键引用 `b_module.b_id`;保存服务校验父节点不形成循环。 - `b_code` 唯一,格式为 `^[a-z][a-z0-9_]{1,49}$`;`b_route` 非空时唯一。 - `directory` 不能配置 `b_route`、`b_viewtable` 或 `b_savetable`。 -- `page` 必须配置 `b_route`,用于路由、菜单和页面编排;其业务数据由页面组合的数据模块提供。 -- `data` 不能显示菜单;应配置 `b_viewtable`,保存场景再按需配置 `b_savetable` 与 `b_keyfield`。 +- `page` 必须配置 `b_route`;其业务数据由页面组合的数据模块提供。 +- `data` 不能显示菜单且不能配置 `b_route`;必须配置 `b_viewtable`,保存场景再按需配置 `b_savetable` 与 `b_keyfield`。 - `b_canmenu`、`b_canuse` 只能为 `0` 或 `1`。建议建立 `(b_parent_id, b_xh, b_id)` 索引。 ## 字段来源与同步 ### `dbo.b_module_field` -保存字段的基础定义,不保存列宽、表单位置、是否显示或查询位置等场景属性。字段来自数据模块的 `b_viewtable`,并以 `(b_module_id, b_field)` 作为稳定标识。 +保存字段的基础定义。字段来自数据模块的 `b_viewtable`,并以 `(b_module_id, b_field)` 作为稳定标识;列表、编辑和查询场景属性均由独立配置表维护。 | 字段 | 类型 | 说明 | | --- | --- | --- | @@ -75,19 +76,18 @@ b_module / b_module_field / b_module_field_list / b_module_field_edit_group / b_ | `b_field` | `varchar(50)` | 视图字段名 | | `b_name` | `nvarchar(100)` | 默认字段名称,由管理员维护 | | `b_i18n` | `varchar(150)` | 多语言键 | -| `b_field_type` | `varchar(30)` | `string`、`text`、`number`、`date`、`datetime`、`boolean`、`select_id` 或 `select_input` | -| `b_value` | `nvarchar(500)` | 新增时默认值 | -| `b_dict_code` | `varchar(100)` | 字典编码;非字典字段为空 | +| `b_type` | `varchar(30)` | 字段控件类型:`input`、`textarea`、`number`、`money`、`date`、`datetime`、`switch` 或 `select` | +| `b_default_value` | `nvarchar(500)` | 新增记录时的默认值 | +| `b_options` | `nvarchar(max)` | 类型专属 JSON 配置,例如选择器来源、日期格式或金额精度 | | `b_canuse` | `int` | 是否启用 | -| `b_remark` | `nvarchar(500)` | 字段说明 | -| `b_inputdatetime` | `datetime` | 首次同步创建时间 | 约束: - `b_module_id` 外键引用 `b_module.b_id`,`(b_module_id, b_field)` 唯一。 -- `b_canuse` 只能为 `0` 或 `1`。建议建立 `(b_module_id, b_canuse, b_id)` 索引。 +- `b_canuse` 只能为 `0` 或 `1`;`b_options` 非空时通过 `ISJSON` 校验。建议建立 `(b_module_id, b_canuse, b_id)` 索引。 - 字段没有 list、edit 或 query 配置记录时,不参与对应场景。 -- `select_id` 保存选项 ID;`select_input` 支持从选项中选择或直接输入文本。字典型选择器使用 `b_dict_code`;视图型选择器的来源配置保存在 `b_module_field_edit.b_editor_options`。 +- `b_type` 是展示和编辑的唯一类型来源:Table 按它渲染默认显示器,Form 或可编辑 Table 按它选择控件。 +- `select` 是否允许自由输入、选项来源、值字段和显示字段均由 `b_options` 控制。 ### 视图同步规则 @@ -95,90 +95,82 @@ b_module / b_module_field / b_module_field_list / b_module_field_edit_group / b_ 1. 后端校验视图名并读取列名、SQL 类型和可空性等结构信息。 2. 按列名查找同模块的 `b_module_field`;新列创建字段基础记录。 -3. 同步更新 `b_field`、`b_field_type` 和 `b_canuse`。SQL 类型映射为 `string`、`text`、`number`、`date`、`datetime`、`boolean` 等基础类型;`select_id`、`select_input` 由管理员在同步后按业务语义调整。 +3. 新字段按 SQL 类型初始化 `b_type`,例如字符串映射为 `input`、长文本映射为 `textarea`、数值映射为 `number`、日期映射为 `date` 或 `datetime`、位值映射为 `switch`。已有字段不覆盖管理员维护的 `b_type` 与 `b_options`;`select`、`money` 等业务类型由管理员按语义调整。 4. 本次视图中不存在的已有字段保留其记录和全部场景配置,但将 `b_canuse` 置为 `0`。 -5. 不覆盖人工维护的 `b_name`、`b_i18n`、`b_value`、`b_dict_code` 和 `b_remark`,不物理删除字段或场景配置。 +5. 不覆盖人工维护的 `b_name`、`b_i18n`、`b_default_value` 与 `b_options`,不物理删除字段或场景配置。 ## 列表配置 ### `dbo.b_module_field_list` -控制 Table 列、显示方式和多级表头。分组节点与字段节点通过 `b_parent_id` 组成任意层级的表头树;只读列表和可编辑 Table 共用本表。 - -| 字段 | 类型 | 说明 | -| --- | --- | --- | -| `b_id` | `bigint` | 雪花 ID 主键 | -| `b_module_id` | `bigint` | 所属模块 ID | -| `b_parent_id` | `bigint` | 上级表头分组 ID;顶层为空 | -| `b_kind` | `varchar(20)` | `group` 或 `field` | -| `b_field_id` | `bigint` | 字段节点关联 `b_module_field.b_id`;分组节点为空 | -| `b_title` | `nvarchar(100)` | 分组标题或字段标题覆盖值;字段为空时使用默认字段名称 | -| `b_i18n` | `varchar(150)` | 分组多语言键或字段标题多语言键覆盖值 | -| `b_visible` | `int` | 是否显示,默认 `1` | -| `b_xh` | `int` | 同一父节点下的显示顺序,默认 `0` | -| `b_width` | `int` | 字段列宽;分组节点为空 | -| `b_sortable` | `int` | 是否允许排序,默认 `0` | -| `b_display_type` | `varchar(30)` | 显示器类型,例如 `text`、`money`、`date`、`datetime`、`tag`;前端按类型使用统一显示规则 | -| `b_canedit` | `int` | 是否允许表格单元格编辑,默认 `0` | - -约束: - -- `b_module_id` 外键引用 `b_module.b_id`,`b_parent_id` 外键引用本表 `b_id`,`b_field_id` 外键引用 `b_module_field.b_id`。 -- `group` 节点必须没有 `b_field_id`、`b_width`,且 `b_canedit = 0`;`field` 节点必须有 `b_field_id`。 -- 同一字段在同一模块的列树中最多出现一次,使用非空 `b_field_id` 的过滤唯一索引约束。 -- `b_parent_id` 必须指向同模块的 `group` 节点,且不能形成循环,由保存服务校验。 -- `b_visible`、`b_sortable`、`b_canedit` 只能为 `0` 或 `1`;`b_canedit = 1` 时必须存在对应编辑规则。 -- `b_display_type` 决定显示器及其格式化规则,例如 `money`、`date`、`datetime` 均由前端按统一规则渲染。建议建立 `(b_module_id, b_parent_id, b_xh, b_id)` 索引。 -- 不保存 `level`、`colspan`、`rowspan`;前端一次加载全部节点后,按 `b_parent_id`、`b_xh` 组装嵌套 `children` 并计算表头。隐藏分组的后代不显示;没有可见字段后代的分组不渲染。 - -示例: - -```text -费用(group) -|-- 人工费(field) -`-- 海运费(field) -``` - -## 编辑配置 - -### `dbo.b_module_field_edit` - -控制 Form 和可编辑 Table 的共享编辑规则,以及字段在 Form 中的布局。表单分组仅是视觉分组,不表示页签、动态页面区域或主子表关系;分组标题和多语言由独立分组表维护。 +控制列表字段列。多级表头由 `b_module_field_group` 的 `list` 分组树表达;本表不保存编辑状态。 | 字段 | 类型 | 说明 | | --- | --- | --- | | `b_id` | `bigint` | 雪花 ID 主键 | | `b_module_id` | `bigint` | 所属模块 ID | | `b_field_id` | `bigint` | 关联 `b_module_field.b_id` | -| `b_editor` | `varchar(30)` | 例如 `input`、`textarea`、`select`、`date`、`switch` | -| `b_edit_format` | `varchar(100)` | 编辑值格式 | -| `b_editor_options` | `nvarchar(max)` | 编辑器专属 JSON 配置 | -| `b_required` | `int` | 是否必填,默认 `0` | -| `b_readonly` | `int` | 是否只读,默认 `0` | -| `b_disabled` | `int` | 是否禁用,默认 `0` | -| `b_placeholder` | `nvarchar(200)` | 输入提示 | -| `b_canform` | `int` | 是否进入 Form,默认 `1` | -| `b_edit_group` | `varchar(100)` | 关联 `b_module_field_edit_group.b_code`;不分组时为空 | -| `b_edit_xh` | `int` | 编辑区域中的全局顺序,默认 `0` | -| `b_edit_span` | `int` | Form 栅格宽度,默认 `24`,范围 `1` 至 `24` | +| `b_group_id` | `bigint` | 所属分组 ID;为空时为顶层列 | +| `b_title` | `nvarchar(100)` | 字段标题覆盖值;为空时使用默认字段名称 | +| `b_i18n` | `varchar(150)` | 字段标题多语言键覆盖值 | +| `b_visible` | `int` | 是否显示,默认 `1` | +| `b_xh` | `int` | 同一分组内的显示顺序,默认 `0` | +| `b_width` | `int` | 字段列宽 | +| `b_sortable` | `int` | 是否允许排序,默认 `0` | 约束: -- `b_module_id` 外键引用 `b_module.b_id`,`b_field_id` 外键引用 `b_module_field.b_id`;`(b_module_id, b_field_id)` 唯一,保存服务校验字段属于同一模块。 -- `b_required`、`b_readonly`、`b_disabled`、`b_canform` 只能为 `0` 或 `1`;`b_editor_options` 非空时通过 `ISJSON` 校验。 -- 非空 `b_edit_group` 必须引用同模块的启用分组;保存服务校验模块一致性。 -- 已分组字段按所属分组的 `b_xh` 排列,组内按 `b_edit_xh` 排列;未分组字段作为末尾默认区域,按 `b_edit_xh` 排列。建议建立 `(b_module_id, b_edit_xh, b_id)` 索引。 -- `b_canform = 0` 时,编辑规则仍可由可编辑 Table 使用。 +- `b_module_id` 外键引用 `b_module.b_id`,`b_field_id` 外键引用 `b_module_field.b_id`,`b_group_id` 引用同模块的 `b_module_field_group`。 +- `b_field_id` 必填;同一字段在同一模块的列表中最多出现一次,使用 `(b_module_id, b_field_id)` 唯一约束。 +- `b_visible`、`b_sortable` 只能为 `0` 或 `1`。 +- 字段列的显示器由关联字段的 `b_type` 与 `b_options` 决定,例如 `money`、`date`、`datetime` 均由前端按统一规则渲染。建议建立 `(b_module_id, b_group_id, b_xh, b_id)` 索引。 +- 不保存 `level`、`colspan`、`rowspan`;前端加载 Table 分组树和字段列后,按分组的 `b_parent_id`、`b_xh` 组装嵌套表头。隐藏分组的后代不显示;没有可见字段后代的分组不渲染。 -### `dbo.b_module_field_edit_group` +示例: -保存 Form 的视觉分组标题和多语言配置。一个模块可以有多个分组,字段通过 `b_edit_group` 引用其稳定编码。 +```text +费用(`b_module_field_group`) +|-- 人工费(`b_module_field_list.b_group_id` 指向费用) +`-- 海运费(`b_module_field_list.b_group_id` 指向费用) +``` + +## 编辑配置 + +### `dbo.b_module_field_edit` + +控制字段的通用编辑属性与编辑分组。控件类型、显示规则和类型专属参数统一由 `b_module_field.b_type`、`b_options` 决定;FmsForm 与 FmsTable 自行决定何时使用本配置。 | 字段 | 类型 | 说明 | | --- | --- | --- | | `b_id` | `bigint` | 雪花 ID 主键 | | `b_module_id` | `bigint` | 所属模块 ID | -| `b_code` | `varchar(100)` | 分组稳定编码,例如 `basic`、`booking` | +| `b_field_id` | `bigint` | 关联 `b_module_field.b_id` | +| `b_required` | `int` | 是否必填,默认 `0` | +| `b_readonly` | `int` | 是否只读,默认 `0` | +| `b_disabled` | `int` | 是否禁用,默认 `0` | +| `b_group_id` | `bigint` | 所属编辑分组 ID;不分组时为空 | +| `b_xh` | `int` | 编辑场景中的显示顺序,默认 `0` | + +约束: + +- `b_module_id` 外键引用 `b_module.b_id`,`b_field_id` 外键引用 `b_module_field.b_id`;`(b_module_id, b_field_id)` 唯一,保存服务校验字段属于同一模块。 +- `b_required`、`b_readonly`、`b_disabled` 只能为 `0` 或 `1`。 +- `b_group_id` 必须关联同模块的 `b_module_field_group`;分组为空时字段进入默认编辑区域。 +- 已分组字段按分组的 `b_xh` 排列,组内按 `b_xh` 排列;默认区域字段按 `b_xh` 排列。 + +## 布局分组 + +### `dbo.b_module_field_group` + +统一保存列表多级表头和编辑分组。分组只负责布局,不定义编辑能力;列表或编辑用途由引用它的配置表决定。 + +被 `b_module_field_list` 引用时,分组组成同一份 FmsTable 的多级表头;被 `b_module_field_edit` 引用时,分组组成同一份编辑界面中的业务区块,例如提单、订舱、报关、备注,不代表拆分为多个 Form。 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| `b_id` | `bigint` | 雪花 ID 主键 | +| `b_module_id` | `bigint` | 所属模块 ID | +| `b_parent_id` | `bigint` | 上级分组 ID;顶层为空 | | `b_title` | `nvarchar(100)` | 默认分组标题 | | `b_i18n` | `varchar(150)` | 分组标题多语言键 | | `b_xh` | `int` | 分组显示顺序,默认 `0` | @@ -186,11 +178,13 @@ b_module / b_module_field / b_module_field_list / b_module_field_edit_group / b_ 约束: -- `b_module_id` 外键引用 `b_module.b_id`,`(b_module_id, b_code)` 唯一。 +- `b_module_id` 外键引用 `b_module.b_id`;`b_id` 是分组的稳定关联键。 +- `b_parent_id` 必须引用同模块的分组且不能形成循环。 +- 多级分组可用于 FmsTable 表头;FmsForm 与 FmsTable 如何消费编辑分组由各自实现决定。 - `b_canuse` 只能为 `0` 或 `1`。建议建立 `(b_module_id, b_canuse, b_xh, b_id)` 索引。 -- 无字段引用的分组不渲染;分组停用时,其关联字段不在 Form 中显示。 +- 无字段引用的分组不渲染;分组停用时,其关联字段或列不显示。 -视图型选择器的 `b_editor_options` 示例: +视图型选择器的 `b_options` 示例: ```json { @@ -202,7 +196,7 @@ b_module / b_module_field / b_module_field_list / b_module_field_edit_group / b_ } ``` -`select_id` 使用 `allowInput: false` 并保存 `valueField` 的值;`select_input` 可使用 `allowInput: true` 并保存选择值或用户输入文本。视图名、值字段和标签字段均须由后端校验。 +`select` 使用 `allowInput: false` 时保存 `valueField` 的值;设为 `true` 时可保存选择值或用户输入文本。视图名、值字段和标签字段均须由后端校验。 ## 查询配置 @@ -299,13 +293,13 @@ b_module / b_module_field / b_module_field_list / b_module_field_edit_group / b_ ```text 海运页面 m_sea(page:路由、菜单、权限、页面编排) -|-- 海运主单 d_sea(data:Table / Form / Query) -|-- 费用 d_charge(data:可编辑 Table) -|-- 订舱 d_booking(data:独立表和主键时) -`-- 箱明细 d_container(data:可编辑 Table) +|-- 海运主单 d_sea(data) +|-- 费用 d_charge(data) +|-- 订舱 d_booking(data,独立表和主键时) +`-- 箱明细 d_container(data) ``` -若订舱字段实际保存于海运主单表,不创建 `d_booking`,而在 `d_sea` 的 Form 中用 `b_module_field_edit_group` 和 `b_edit_group` 表达视觉分组。主子表关联、加载时机、保存顺序和页面布局均由业务 Vue 页面明确实现。 +若订舱字段实际保存于海运主单表,不创建 `d_booking`,而在 `d_sea` 的编辑配置中用 `b_group_id` 和 `b_xh` 定义编辑分组与顺序,分组标题来自 `b_module_field_group`。主子表关联、加载时机、保存顺序和页面布局均由业务 Vue 页面明确实现。 ## 现有字段迁移 @@ -313,19 +307,19 @@ b_module / b_module_field / b_module_field_list / b_module_field_edit_group / b_ | 当前字段 | 目标位置 | | --- | --- | -| `b_type` | `b_module_field.b_field_type` | +| `b_type` | `b_module_field.b_type` | | `b_canlist` | `b_module_field_list.b_visible` | | `b_width` | `b_module_field_list.b_width` | -| `b_xh` | 初始化 `b_module_field_list.b_xh`、`b_module_field_edit.b_edit_xh` 与 `b_module_field_query.b_xh` | -| `b_canform` | `b_module_field_edit.b_canform` | +| `b_xh` | 初始化 `b_module_field_list.b_xh`、`b_module_field_edit.b_xh` 与 `b_module_field_query.b_xh` | +| `b_canform` | 为原 Form 字段创建 `b_module_field_edit` 记录 | | `b_must` | `b_module_field_edit.b_required` | | `b_readonly` | `b_module_field_edit.b_readonly` | | `b_canquery` | 创建 `b_module_field_query` 记录 | -| `b_value` | 保留在 `b_module_field.b_value` | +| `b_value` | 迁移为 `b_module_field.b_default_value` | 迁移步骤: -1. 新建 list、edit、edit_group、query 表和必要索引,新增 `b_module.b_module_type` 与 `b_module_field.b_field_type` 的兼容列。 -2. 为原有字段创建顶层 `field` 列配置;根据原 `b_canform`、`b_canquery` 创建编辑和查询配置;初始 `b_canedit = 0`。 +1. 新建 list、edit、group、query 表和必要索引,新增 `b_module.b_module_type` 与 `b_module_field.b_type` 的兼容列。 +2. 为原有字段创建列表配置;根据原 `b_canform`、`b_canquery` 初始化编辑和查询配置。 3. 前端和后端改为读取新场景表,查询不再读取 list 配置;模块类型切换为读取 `b_module_type`。 4. 完成视图同步服务并验证新结构的读写稳定后,删除旧混合列和兼容字段。