diff --git a/code/fms/FMS新系统核心表结构设计.md b/code/fms/FMS新系统核心表结构设计.md
index d1f7fd49..b3c6df8c 100644
--- a/code/fms/FMS新系统核心表结构设计.md
+++ b/code/fms/FMS新系统核心表结构设计.md
@@ -2,7 +2,7 @@
## 1. 说明
-本文档给出 FMS 新系统核心平台表的 SQL Server 建表语句,范围包括元数据中心(模块、实体、字段、关系、页面菜单、视图、表单、多语言)、Schema Compiler、权限中心、技术日志、登录日志与业务审计。
+本文档给出 FMS 新系统核心平台表的 SQL Server 建表语句,范围包括元数据中心(模块、实体、字段、字段分组、关系、页面菜单、多语言)、Schema Compiler、权限中心、技术日志、登录日志与业务审计。
设计原则:
@@ -83,7 +83,7 @@ create index ix_s_module_dependency_depends_on
### 2.3 实体
-实体是数据和字段的所有者,映射一张真实物理表。页面、列表、表单都只是实体的展示方式。
+实体是数据和字段的所有者,同时声明"查询数据源"和"保存数据源"。查询源可以是真实表,也可以是带关联/聚合的 SQL 视图(视图只读,用于列表展示);保存目标必须是真实表。
```sql
create table dbo.s_module_entity (
@@ -92,7 +92,9 @@ create table dbo.s_module_entity (
b_module_key nvarchar(128) not null, -- 所属模块 key
b_name_key nvarchar(256) not null, -- 实体显示名多语言 key
b_description nvarchar(1000) null, -- 实体描述
- b_physical_table sysname not null, -- 真实物理表名(如 ocean_booking)
+ b_view_table sysname not null, -- 查询数据源:真实表或 SQL 视图(如 ocean_booking / v_booking_with_fee)
+ b_save_table sysname null, -- 保存目标:真实表(查询源为视图时必填,只读实体可为空)
+ b_key_field sysname null, -- 保存对象主键字段(如 b_id)
b_delete_policy nvarchar(32) not null -- 删除策略(默认 lifecycle)
constraint df_s_module_entity_delete_policy default N'lifecycle',
b_origin nvarchar(32) not null -- 来源:platform / tenant
@@ -109,6 +111,17 @@ create table dbo.s_module_entity (
create index ix_s_module_entity_module_key
on dbo.s_module_entity (b_module_key, b_enabled);
+
+create index ix_s_module_entity_save_table
+ on dbo.s_module_entity (b_save_table);
+```
+
+实体的"查/存"配置方式:
+
+```text
+简单实体: b_view_table = b_save_table = 同一张真实表
+复杂列表: b_view_table = 带关联/聚合的 SQL 视图(只读), b_save_table = 真实表
+纯查询实体: b_view_table = 视图或表, b_save_table = 空(只读,不可保存)
```
删除策略取值(`b_delete_policy`):
@@ -153,6 +166,7 @@ create table dbo.s_module_field (
constraint df_s_module_field_readonly default 0,
b_sort_no int not null -- 列表/表单默认排序
constraint df_s_module_field_sort_no default 0,
+ b_group_key nvarchar(128) null, -- 所属分组 key(逻辑外键,空为未分组)
b_width int null, -- 列表列宽(px)
b_component_type nvarchar(64) null, -- 固定表单组件类型(input/select/date)
b_auditable tinyint not null -- 是否记录字段级审计
@@ -253,8 +267,6 @@ create table dbo.s_module_menu (
b_menu_type nvarchar(32) not null, -- 菜单类型:directory/page/data_category/data/external
b_route nvarchar(256) null, -- 固定页面或外链路由
b_entity_key nvarchar(192) null, -- data 类型绑定的实体 key
- b_view_key nvarchar(192) null, -- 绑定的列表视图 key
- b_form_key nvarchar(192) null, -- 绑定的表单 key
b_permission_key nvarchar(192) null, -- 页面访问权限 key
b_icon_key nvarchar(128) null, -- 菜单图标 key
b_sort_no int not null -- 菜单排序号
@@ -284,153 +296,40 @@ create index ix_s_module_menu_entity_key
on dbo.s_module_menu (b_entity_key, b_enabled);
```
-约束由元数据校验器执行:`directory` 节点不能填写业务表或外链路由;`page` 节点必须填写 `b_route`,并绑定 `b_view_key`/`b_form_key`;`data` 节点必须填写 `b_entity_key` 和 `b_view_key`(列表)、`b_form_key`(表单);`external` 节点必须填写 `b_route` 和 `b_permission_key`。固定页面组件负责布局,表中只保存实体与视图/表单引用和导航配置。目录仅在至少存在一个可访问子页面时显示。
+约束由元数据校验器执行:`directory` 节点不能填写业务表或外链路由;`page` 节点必须填写 `b_route`;`data` 节点必须填写 `b_entity_key`(查询走实体 `b_view_table`,保存走实体 `b_save_table`);`external` 节点必须填写 `b_route` 和 `b_permission_key`。固定页面组件负责布局,表中只保存实体引用和导航配置。目录仅在至少存在一个可访问子页面时显示。
-### 2.7 视图(列表)
+### 2.7 字段分组
-视图定义"一个实体的一种列表展示方式",一个实体可以有多个视图。列表字段配置在 `s_module_view_field`。
+字段分组是实体级独立定义,用于表单/编辑页面按分组展示字段。分组定义集中在 `s_module_field_group`,字段通过 `s_module_field.b_group_key` 引用分组,避免在每个字段上重复维护分组标题与排序。
```sql
-create table dbo.s_module_view (
+create table dbo.s_module_field_group (
b_id bigint identity(1, 1) not null, -- 内部自增主键
- b_view_key nvarchar(192) not null, -- 视图稳定业务标识(如 ocean.booking.default-list)
b_entity_key nvarchar(192) not null, -- 所属实体 key
- b_name_key nvarchar(256) not null, -- 视图显示名多语言 key
- b_queryable tinyint not null -- 是否提供查询条件区
- constraint df_s_module_view_queryable default 1,
- b_exportable tinyint not null -- 是否允许导出
- constraint df_s_module_view_exportable default 1,
- b_page_size int null, -- 默认分页大小
- b_config_text nvarchar(max) null, -- 视图扩展配置
- b_enabled tinyint not null -- 是否启用
- constraint df_s_module_view_enabled default 1,
+ b_group_key nvarchar(128) not null, -- 分组稳定标识(如 basic/transport/finance)
+ b_name_key nvarchar(256) not null, -- 分组标题多语言 key
+ b_sort_no int not null -- 分组排序号(0,1,2...)
+ constraint df_s_module_field_group_sort_no default 0,
+ b_enabled tinyint not null -- 是否启用(停用则整组隐藏)
+ constraint df_s_module_field_group_enabled default 1,
+ b_config_text nvarchar(max) null, -- 分组扩展配置
b_created_at datetime2(3) not null -- 创建时间(UTC)
- constraint df_s_module_view_created_at default sysutcdatetime(),
+ constraint df_s_module_field_group_created_at default sysutcdatetime(),
b_created_by nvarchar(128) not null, -- 创建人
- constraint pk_s_module_view primary key (b_id),
- constraint ux_s_module_view_key unique (b_view_key)
-);
-
-create index ix_s_module_view_entity_key
- on dbo.s_module_view (b_entity_key, b_enabled);
-
-create table dbo.s_module_view_field (
- b_id bigint identity(1, 1) not null, -- 内部自增主键
- b_view_key nvarchar(192) not null, -- 所属视图 key
- b_field_key nvarchar(128) not null, -- 引用的字段 key
- b_sort_no int not null -- 列排序号
- constraint df_s_module_view_field_sort_no default 0,
- b_width int null, -- 列宽(px)
- b_visible tinyint not null -- 是否显示
- constraint df_s_module_view_field_visible default 1,
- b_frozen tinyint not null -- 是否固定列
- constraint df_s_module_view_field_frozen default 0,
- b_align nvarchar(16) null, -- 对齐方式(left/center/right)
- b_format_text nvarchar(256) null, -- 显示格式(如日期、千分位)
- b_queryable tinyint not null -- 是否作为查询条件
- constraint df_s_module_view_field_queryable default 0,
- b_query_operator nvarchar(32) null, -- 查询运算符(eq/contains/gte/...)
- b_created_at datetime2(3) not null -- 创建时间(UTC)
- constraint df_s_module_view_field_created_at default sysutcdatetime(),
- b_created_by nvarchar(128) not null, -- 创建人
- constraint pk_s_module_view_field primary key (b_id),
- constraint ux_s_module_view_field_key unique (
- b_view_key,
- b_field_key
+ constraint pk_s_module_field_group primary key (b_id),
+ constraint ux_s_module_field_group_key unique (
+ b_entity_key,
+ b_group_key
)
);
-create index ix_s_module_view_field_view_key
- on dbo.s_module_view_field (b_view_key, b_sort_no);
+create index ix_s_module_field_group_entity_key
+ on dbo.s_module_field_group (b_entity_key, b_enabled, b_sort_no);
```
-### 2.8 表单
+表单渲染时按 `b_sort_no` 排序分组,组内字段按 `s_module_field.b_sort_no` 排序;`s_module_field.b_group_key` 为空的字段显示在"未分组"区。新增/调整/停用分组只改 `s_module_field_group`,不需要逐个修改字段。
-表单定义"一个实体的一种编辑展示方式",一个实体可以有多个表单(默认编辑表单、详情表单)。子表区域通过 `s_module_form_relation` 引用关系并绑定子表视图/表单。
-
-```sql
-create table dbo.s_module_form (
- b_id bigint identity(1, 1) not null, -- 内部自增主键
- b_form_key nvarchar(192) not null, -- 表单稳定业务标识(如 ocean.booking.default-form)
- b_entity_key nvarchar(192) not null, -- 所属实体 key
- b_name_key nvarchar(256) not null, -- 表单显示名多语言 key
- b_form_type nvarchar(32) not null -- 表单类型(默认 default)
- constraint df_s_module_form_form_type default N'default',
- b_config_text nvarchar(max) null, -- 表单扩展配置(控件特有属性与布局 JSON)
- b_enabled tinyint not null -- 是否启用
- constraint df_s_module_form_enabled default 1,
- b_created_at datetime2(3) not null -- 创建时间(UTC)
- constraint df_s_module_form_created_at default sysutcdatetime(),
- b_created_by nvarchar(128) not null, -- 创建人
- constraint pk_s_module_form primary key (b_id),
- constraint ux_s_module_form_key unique (b_form_key)
-);
-
-create index ix_s_module_form_entity_key
- on dbo.s_module_form (b_entity_key, b_enabled);
-
-create table dbo.s_module_form_field (
- b_id bigint identity(1, 1) not null, -- 内部自增主键
- b_form_key nvarchar(192) not null, -- 所属表单 key
- b_field_key nvarchar(128) not null, -- 引用的字段 key
- b_group_key nvarchar(128) null, -- 所属分组 key(逻辑分组)
- b_sort_no int not null -- 表单内排序号
- constraint df_s_module_form_field_sort_no default 0,
- b_required tinyint not null -- 是否必填(覆盖字段默认)
- constraint df_s_module_form_field_required default 0,
- b_readonly tinyint not null -- 是否只读(覆盖字段默认)
- constraint df_s_module_form_field_readonly default 0,
- b_visible tinyint not null -- 是否显示
- constraint df_s_module_form_field_visible default 1,
- b_span int null, -- 布局占宽(栅格)
- b_component_type nvarchar(64) null, -- 控件类型(覆盖字段默认)
- b_config_text nvarchar(max) null, -- 控件扩展配置
- b_created_at datetime2(3) not null -- 创建时间(UTC)
- constraint df_s_module_form_field_created_at default sysutcdatetime(),
- b_created_by nvarchar(128) not null, -- 创建人
- constraint pk_s_module_form_field primary key (b_id),
- constraint ux_s_module_form_field_key unique (
- b_form_key,
- b_field_key
- )
-);
-
-create index ix_s_module_form_field_form_key
- on dbo.s_module_form_field (b_form_key, b_sort_no);
-
-create table dbo.s_module_form_relation (
- b_id bigint identity(1, 1) not null, -- 内部自增主键
- b_form_key nvarchar(192) not null, -- 所属表单 key
- b_relation_key nvarchar(192) not null, -- 引用的主子关系 key
- b_child_view_key nvarchar(192) null, -- 子表列表配置 key
- b_child_form_key nvarchar(192) null, -- 子表编辑配置 key(可选)
- b_display_mode nvarchar(16) not null -- 子表显示模式(默认 table)
- constraint df_s_module_form_relation_display_mode default N'table',
- b_editable tinyint not null -- 是否可编辑
- constraint df_s_module_form_relation_editable default 1,
- b_allow_add tinyint not null -- 是否允许新增行
- constraint df_s_module_form_relation_allow_add default 1,
- b_allow_delete tinyint not null -- 是否允许删除行
- constraint df_s_module_form_relation_allow_delete default 1,
- b_allow_sort tinyint not null -- 是否允许排序行
- constraint df_s_module_form_relation_allow_sort default 1,
- b_sort_no int not null -- 子表区域排序号
- constraint df_s_module_form_relation_sort_no default 0,
- b_created_at datetime2(3) not null -- 创建时间(UTC)
- constraint df_s_module_form_relation_created_at default sysutcdatetime(),
- b_created_by nvarchar(128) not null, -- 创建人
- constraint pk_s_module_form_relation primary key (b_id),
- constraint ux_s_module_form_relation_key unique (
- b_form_key,
- b_relation_key
- )
-);
-
-create index ix_s_module_form_relation_form_key
- on dbo.s_module_form_relation (b_form_key, b_sort_no);
-```
-
-### 2.9 多语言
+### 2.8 多语言
多语言是元数据运行时的一部分,统一解析模块、菜单、实体、字段、动作和主数据的显示名称。
@@ -461,7 +360,7 @@ create index ix_s_module_i18n_resource_key
## 3. Schema Compiler
-元数据变更通过服务层校验后立即生效。Schema Compiler 根据 `s_module_entity` 的物理表配置和 `s_module_field` 的物理字段配置生成所需的 `CREATE TABLE`、`ALTER TABLE`、索引和约束语句,并由应用服务直接执行;执行成功后提交元数据变更,失败则回滚本次操作并返回错误。
+元数据变更通过服务层校验后立即生效。Schema Compiler 根据 `s_module_entity` 的保存数据源(`b_save_table`)配置和 `s_module_field` 的物理字段配置生成所需的 `CREATE TABLE`、`ALTER TABLE`、索引和约束语句,并由应用服务直接执行;执行成功后提交元数据变更,失败则回滚本次操作并返回错误。查询数据源(`b_view_table`)为 SQL 视图时,由运维或售后在数据库中维护视图定义,Schema Compiler 不生成视图 DDL。
执行机制:
@@ -700,24 +599,14 @@ create index ix_s_log_audit_field_audit_event_id
| `s_module_dependency` | `b_depends_on_module_key` | `s_module.b_module_key` |
| `s_module_entity` | `b_module_key` | `s_module.b_module_key` |
| `s_module_field` | `b_entity_key` | `s_module_entity.b_entity_key` |
+| `s_module_field` | `b_group_key` | 实体范围内的 `s_module_field_group.b_group_key` |
+| `s_module_field_group` | `b_entity_key` | `s_module_entity.b_entity_key` |
| `s_module_relation` | `b_entity_key` | `s_module_entity.b_entity_key` |
| `s_module_relation` | `b_target_entity_key` | `s_module_entity.b_entity_key` |
| `s_module_menu` | `b_module_key` | `s_module.b_module_key` |
| `s_module_menu` | `b_parent_menu_key` | `s_module_menu.b_menu_key` |
| `s_module_menu` | `b_entity_key` | `s_module_entity.b_entity_key` |
-| `s_module_menu` | `b_view_key` | `s_module_view.b_view_key` |
-| `s_module_menu` | `b_form_key` | `s_module_form.b_form_key` |
| `s_module_menu` | `b_permission_key` | `s_auth_permission.b_permission_key` |
-| `s_module_view` | `b_entity_key` | `s_module_entity.b_entity_key` |
-| `s_module_view_field` | `b_view_key` | `s_module_view.b_view_key` |
-| `s_module_view_field` | `b_field_key` | 实体范围内的 `s_module_field.b_field_key` |
-| `s_module_form` | `b_entity_key` | `s_module_entity.b_entity_key` |
-| `s_module_form_field` | `b_form_key` | `s_module_form.b_form_key` |
-| `s_module_form_field` | `b_field_key` | 实体范围内的 `s_module_field.b_field_key` |
-| `s_module_form_relation` | `b_form_key` | `s_module_form.b_form_key` |
-| `s_module_form_relation` | `b_relation_key` | `s_module_relation.b_relation_key` |
-| `s_module_form_relation` | `b_child_view_key` | `s_module_view.b_view_key` |
-| `s_module_form_relation` | `b_child_form_key` | `s_module_form.b_form_key` |
| `s_auth_user_role` | `b_role_key` | `s_auth_role.b_role_key` |
| `s_auth_role_permission` | `b_permission_key` | `s_auth_permission.b_permission_key` |
| `s_log_audit_field` | `b_event_id` | `s_log_audit.b_id` |
@@ -730,7 +619,7 @@ create index ix_s_log_audit_field_audit_event_id
稳定 key 是否存在
-> 目标资源是否启用
-> 跨模块引用是否合法(依赖图中可达)
--> 字段、实体类型和视图/表单配置是否匹配
+-> 字段、实体类型和分组配置是否匹配
-> 删除或停用前是否仍被其他资源引用
```
@@ -739,8 +628,7 @@ create index ix_s_log_audit_field_audit_event_id
建议按以下顺序创建和启用这些表:
```text
-元数据基础表(模块/依赖/实体/字段/关系)
--> 视图与表单表
+元数据基础表(模块/依赖/实体/字段/字段分组/关系)
-> 页面菜单入口表
-> 多语言表
-> 权限表
diff --git a/code/fms/fms-demo/index.html b/code/fms/fms-demo/index.html
index ad44af3d..8687d962 100644
--- a/code/fms/fms-demo/index.html
+++ b/code/fms/fms-demo/index.html
@@ -196,7 +196,7 @@ const MODULES = [
desc: '海运出口全流程:订舱、海运单、集装箱、提单、船期。典型的"一模块多实体多菜单"形态。',
deps: ['platform', 'master-data'],
entities: [
- { key: 'ocean.booking', table: 'ocean_booking', policy: 'lifecycle', desc: '订舱主单' },
+ { key: 'ocean.booking', table: 'ocean_booking', view: 'v_booking_with_fee', policy: 'lifecycle', desc: '订舱主单' },
{ key: 'ocean.container', table: 'ocean_container', policy: 'cascadeOwned', desc: '箱信息(订舱的子表)' },
{ key: 'ocean.shipment', table: 'ocean_shipment', policy: 'lifecycle', desc: '海运单' },
{ key: 'ocean.bill_of_lading', table: 'ocean_bill_of_lading', policy: 'lifecycle', desc: '提单' },
@@ -204,7 +204,7 @@ const MODULES = [
],
menus: [
{ key: 'ocean.booking.cat', name: '订舱管理', type: 'data_category', children: [
- { key: 'ocean.booking.list', name: '订舱列表', type: 'data', entity: 'ocean.booking', view: 'ocean.booking.default-list', form: 'ocean.booking.default-form', route: '/ocean/bookings', permission: 'ocean.booking.view' },
+ { key: 'ocean.booking.list', name: '订舱列表', type: 'data', entity: 'ocean.booking', route: '/ocean/bookings', permission: 'ocean.booking.view' },
]},
{ key: 'ocean.shipment.cat', name: '海运单', type: 'data_category', children: [
{ key: 'ocean.shipment.list', name: '海运单列表', type: 'data', entity: 'ocean.shipment', route: '/ocean/shipments', permission: 'ocean.shipment.view' },
@@ -274,16 +274,16 @@ const MODULES = [
const ENTITY_FIELDS = {
'ocean.booking': [
- { key: 'booking_no', label: '订舱号', type: 'varchar(50)', write: 'direct', req: 1, qry: 1, list: 1, edit: 1 },
- { key: 'customer_id', label: '客户', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1 },
- { key: 'carrier_id', label: '船公司', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1 },
- { key: 'etd', label: '预计离港日', type: 'date', write: 'direct', req: 0, qry: 1, list: 1, edit: 1 },
- { key: 'load_port', label: '起运港', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1 },
- { key: 'discharge_port', label: '目的港', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1 },
- { key: 'freight_payment',label: '运费支付', type: 'varchar(32)', write: 'direct', req: 0, qry: 1, list: 1, edit: 1 },
- { key: 'gross_weight', label: '毛重', type: 'decimal(18,3)', write: 'direct', req: 0, qry: 0, list: 1, edit: 1 },
- { key: 'volume', label: '体积', type: 'decimal(18,3)', write: 'direct', req: 0, qry: 0, list: 1, edit: 1 },
- { key: 'container_count',label: '箱量', type: 'int', write: 'computed', req: 0, qry: 0, list: 1, edit: 0 },
+ { key: 'booking_no', label: '订舱号', type: 'varchar(50)', write: 'direct', req: 1, qry: 1, list: 1, edit: 1, group: 'basic' },
+ { key: 'customer_id', label: '客户', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1, group: 'basic' },
+ { key: 'carrier_id', label: '船公司', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1, group: 'basic' },
+ { key: 'etd', label: '预计离港日', type: 'date', write: 'direct', req: 0, qry: 1, list: 1, edit: 1, group: 'transport' },
+ { key: 'load_port', label: '起运港', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1, group: 'transport' },
+ { key: 'discharge_port', label: '目的港', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1, group: 'transport' },
+ { key: 'freight_payment',label: '运费支付', type: 'varchar(32)', write: 'direct', req: 0, qry: 1, list: 1, edit: 1, group: 'transport' },
+ { key: 'gross_weight', label: '毛重', type: 'decimal(18,3)', write: 'direct', req: 0, qry: 0, list: 1, edit: 1, group: 'cargo' },
+ { key: 'volume', label: '体积', type: 'decimal(18,3)', write: 'direct', req: 0, qry: 0, list: 1, edit: 1, group: 'cargo' },
+ { key: 'container_count',label: '箱量', type: 'int', write: 'computed', req: 0, qry: 0, list: 1, edit: 0, group: 'cargo' },
{ key: 'status', label: '状态', type: 'varchar(32)', write: 'actionOnly', req: 0, qry: 1, list: 1, edit: 0 },
{ key: 'remark', label: '备注', type: 'nvarchar(1000)', write: 'direct', req: 0, qry: 0, list: 0, edit: 1 },
{ key: 'created_by', label: '创建人', type: 'nvarchar(128)', write: 'system', req: 0, qry: 0, list: 0, edit: 0 },
@@ -297,15 +297,27 @@ const ENTITY_FIELDS = {
{ key: 'vgm', label: 'VGM', type: 'decimal(18,3)', write: 'direct', req: 0, qry: 0, list: 1, edit: 1 },
],
'finance.invoice': [
- { key: 'invoice_no', label: '发票号', type: 'varchar(50)', write: 'direct', req: 1, qry: 1, list: 1, edit: 0 },
- { key: 'direction', label: '方向', type: 'varchar(16)', write: 'createOnly', req: 1, qry: 1, list: 1, edit: 0 },
- { key: 'customer_id', label: '往来单位', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1 },
- { key: 'amount', label: '含税金额', type: 'decimal(18,2)', write: 'computed', req: 0, qry: 0, list: 1, edit: 0 },
- { key: 'currency', label: '币种', type: 'varchar(8)', write: 'direct', req: 1, qry: 1, list: 1, edit: 1 },
+ { key: 'invoice_no', label: '发票号', type: 'varchar(50)', write: 'direct', req: 1, qry: 1, list: 1, edit: 0, group: 'basic' },
+ { key: 'direction', label: '方向', type: 'varchar(16)', write: 'createOnly', req: 1, qry: 1, list: 1, edit: 0, group: 'basic' },
+ { key: 'customer_id', label: '往来单位', type: 'bigint', write: 'direct', req: 1, qry: 1, list: 1, edit: 1, group: 'basic' },
+ { key: 'amount', label: '含税金额', type: 'decimal(18,2)', write: 'computed', req: 0, qry: 0, list: 1, edit: 0, group: 'amount' },
+ { key: 'currency', label: '币种', type: 'varchar(8)', write: 'direct', req: 1, qry: 1, list: 1, edit: 1, group: 'amount' },
{ key: 'status', label: '状态', type: 'varchar(32)', write: 'actionOnly', req: 0, qry: 1, list: 1, edit: 0 },
],
};
+const ENTITY_GROUPS = {
+ 'ocean.booking': [
+ { key: 'basic', name: '基本信息', sort: 0 },
+ { key: 'transport', name: '运输信息', sort: 1 },
+ { key: 'cargo', name: '货物信息', sort: 2 },
+ ],
+ 'finance.invoice': [
+ { key: 'basic', name: '基本信息', sort: 0 },
+ { key: 'amount', name: '金额信息', sort: 1 },
+ ],
+};
+
const ENTITY_RELATIONS = {
'ocean.booking': [
{ key: 'ocean.booking.containers', type: 'one-to-many', target: 'ocean.container', ownership: 'composition', delete: 'cascade', save: 'with-parent' },
@@ -314,19 +326,6 @@ const ENTITY_RELATIONS = {
],
};
-const ENTITY_VIEWS = {
- 'ocean.booking': [
- { key: 'ocean.booking.default-list', queryable: 1, exportable: 1, pageSize: 50 },
- { key: 'ocean.booking.query-simple', queryable: 1, exportable: 1, pageSize: 20 },
- ],
-};
-const ENTITY_FORMS = {
- 'ocean.booking': [
- { key: 'ocean.booking.default-form', type: 'default', relations: ['ocean.booking.containers'] },
- { key: 'ocean.booking.readonly-form', type: 'readonly', relations: [] },
- ],
-};
-
/* ============ 状态与工具 ============ */
const state = { selectedModule: null, selectedMenu: null, selectedEntity: null, openModules: new Set(['ocean']) };
@@ -340,7 +339,11 @@ function findMenuByKey(k) {
function menuOwner(k) {
return MODULES.find(m => m.menus.some(mm => mm.key === k || (mm.children && mm.children.some(c => c.key === k))));
}
-function entityTable(ek) {
+function entityViewTable(ek) {
+ for (const m of MODULES) { const e = m.entities.find(x => x.key === ek); if (e) return e.view || e.table; }
+ return '—';
+}
+function entitySaveTable(ek) {
for (const m of MODULES) { const e = m.entities.find(x => x.key === ek); if (e) return e.table; }
return '—';
}
@@ -473,14 +476,14 @@ function renderModule() {
包含实体(s_module_entity)点击实体查看字段元数据
- | 实体 key | 物理表 | 删除策略 | 描述 | 字段数 |
+ | 实体 key | 查询源 view_table | 保存目标 save_table | 删除策略 | 字段数 |
${m.entities.map(e => `
| ${e.key} |
+ ${e.view || e.table}${e.view ? '(视图)' : ''} |
${e.table} |
${e.policy} |
- ${e.desc} |
${ENTITY_FIELDS[e.key] ? ENTITY_FIELDS[e.key].length : 0} |
`).join('')}
@@ -517,10 +520,12 @@ function renderMenu() {
const t = MENU_TYPES[menu.type];
const owner = menuOwner(menu.key);
const entity = menu.entity ? {
- key: menu.entity, table: entityTable(menu.entity),
+ key: menu.entity,
+ viewTable: entityViewTable(menu.entity),
+ saveTable: entitySaveTable(menu.entity),
fields: ENTITY_FIELDS[menu.entity] ? ENTITY_FIELDS[menu.entity].length : 0,
- views: ENTITY_VIEWS[menu.entity] ? ENTITY_VIEWS[menu.entity].length : 0,
- forms: ENTITY_FORMS[menu.entity] ? ENTITY_FORMS[menu.entity].length : 0,
+ groups: ENTITY_GROUPS[menu.entity] ? ENTITY_GROUPS[menu.entity].length : 0,
+ rels: ENTITY_RELATIONS[menu.entity] ? ENTITY_RELATIONS[menu.entity].length : 0,
} : null;
return `
@@ -542,13 +547,12 @@ function renderMenu() {
${entity ? `
-
绑定的实体 / 视图 / 表单 data 类型
+
绑定的实体 查询走 view_table,保存走 save_table
-
-
字段 / 视图 / 表单
${entity.fields} / ${entity.views} / ${entity.forms}
-
视图 key
${menu.view || '—'}
-
表单 key
${menu.form || '—'}
+
查询源 view_table
${entity.viewTable}${entity.viewTable !== entity.saveTable ? '(视图)' : ''}
+
保存目标 save_table
${entity.saveTable}
+
字段 / 分组 / 关系
${entity.fields} / ${entity.groups} / ${entity.rels}
` : ''}
@@ -569,26 +573,50 @@ function renderEntity() {
const ek = ent.key;
const fields = ENTITY_FIELDS[ek] || [];
const rels = ENTITY_RELATIONS[ek] || [];
- const views = ENTITY_VIEWS[ek] || [];
- const forms = ENTITY_FORMS[ek] || [];
+ const groups = ENTITY_GROUPS[ek] || [];
+ const viewTable = ent.view || ent.table;
+ const isView = !!ent.view;
+ const groupName = gk => (groups.find(g => g.key === gk) || {}).name || '未分组';
return `
-
${ent.desc} ${ent.policy}
+
${ent.desc} ${ent.policy}${isView ? ' 视图查询' : ''}
-
entity_key: ${ek} · 物理表: ${ent.table} · module: ${mod.key}
+
entity_key: ${ek} · 查询源: ${viewTable} · 保存目标: ${ent.table} · module: ${mod.key}
-
字段元数据(s_module_field)写入模式 / 必填 / 查询 / 列表 / 编辑
+
查询 / 保存数据源 s_module_entity
+
+
查询源 view_table
${viewTable}${isView ? '(SQL 视图,带关联/聚合)' : ''}
+
保存目标 save_table
${ent.table}
+
+
+
+
+
+
字段分组(s_module_field_group)分组独立定义,字段引用 group_key
+ ${groups.length ? `
+ | 分组 key | 标题 name_key | 排序 |
+ ${groups.map(g => `
+ | ${g.key} |
+ ${g.name} |
+ ${g.sort} |
+
`).join('')}
+
` : '
无分组(字段直接平铺)
'}
+
+
+
+
字段元数据(s_module_field)写入模式 / 分组 / 必填 / 查询 / 列表 / 编辑
- | 字段 key | 标签 | 数据类型 | 写入模式 | 必填 | 查询 | 列表 | 编辑 |
+ | 字段 key | 标签 | 数据类型 | 写入模式 | 分组 | 必填 | 查询 | 列表 | 编辑 |
${fields.map(f => `
| ${f.key} |
${f.label} |
${f.type} |
${f.write} |
+ ${f.group ? groupName(f.group) : '—'} |
${f.req ? '必填' : '—'} |
${f.qry ? '✓' : ''} |
${f.list ? '✓' : ''} |
@@ -615,15 +643,6 @@ function renderEntity() {
`).join('')}
` : '
无
'}
-
-
-
视图与表单 一个实体可有多视图/多表单
-
-
视图 s_module_view
${views.map(v => v.key).join('、') || '—'}
-
表单 s_module_form
${forms.map(v => v.key).join('、') || '—'}
-
- ${forms.some(f => f.relations.length) ? `
主子表区域(s_module_form_relation):${forms.find(f => f.relations.length).relations.join('、')} 以 table 模式内嵌于表单
` : ''}
-
`;
}