diff --git a/code/fms/FMS新系统核心表结构设计.md b/code/fms/FMS新系统核心表结构设计.md index 23b0985f..9bc30352 100644 --- a/code/fms/FMS新系统核心表结构设计.md +++ b/code/fms/FMS新系统核心表结构设计.md @@ -9,7 +9,7 @@ 1. **沿用模块主体**:`s_module` 继续作为旧系统的数据配置主体,分类节点和数据节点在同一棵树中维护,不再增加 `s_entity`。 2. **模块与字段分离**:`data/query` 模块声明查询源、保存源和主键字段;字段挂模块保存物理列名、字段类型和数据库列约束,列表、表单、查询行为由字段配置子表承载。 -3. **菜单与模块分离**:`s_menu` 是纯导航树,只引用数据模块或固定页面;本方案只恢复旧系统的模块数据主体,不把菜单、路由和角色授权重新塞回 `s_module`。 +3. **菜单与模块分离**:`s_menu` 是纯导航树,页面菜单引用数据模块,外链菜单保存外链地址;本方案只恢复旧系统的模块数据主体,不把菜单、路由和角色授权重新塞回 `s_module`。 4. **字段定义与视图配置分责**:字段定义、列表、表单、查询是四类不同职责;本方案保留 `s_field` 作为字段定义表,并将列表、表单、查询行为分别放入 `s_field_view`、`s_field_edit`、`s_field_query`,避免一张宽表充满互不相关的空字段。 5. **配置入口与存储职责分离**:管理员可从模块树或菜单树定位数据模块,在一个配置工作台内完成数据源、字段、列表、表单、查询、编码和权限配置;底层仍按模块、菜单和权限分别存储。 @@ -35,7 +35,7 @@ | 5 | `s_field_edit` | 字段表单配置(显示、排序、分组、必填、只读、禁用) | | 6 | `s_field_query` | 字段查询配置(关联字段、操作符、默认值) | | 7 | `s_relation` | 模块关系(引用/主从/多对多,驱动外键校验与级联) | -| 8 | `s_menu` | 菜单/页面导航(data 菜单直接绑定数据模块) | +| 8 | `s_menu` | 菜单/页面导航(页面菜单绑定数据模块) | | 9 | `s_autocode` | 自动编码规则 | | 10 | `s_role` | 角色 | | 11 | `s_user_role` | 用户-角色 | @@ -360,7 +360,7 @@ create index ix_s_relation_target on s_relation (b_target_module_id); ### 3.8 s_menu 菜单/页面 -菜单是独立导航树,不保存字段、列表或表单配置。`data` 菜单通过 `b_module_id` 绑定一个 `data/query` 模块,并直接使用该模块的字段、列表、表单和查询配置。 +菜单是独立导航树,不保存字段、列表或表单配置。`page` 菜单通过 `b_module_id` 绑定一个 `data/query` 模块,并直接使用该模块的字段、列表、表单和查询配置。 ```sql create table s_menu ( @@ -369,16 +369,14 @@ create table s_menu ( b_code varchar(50) not null, -- 菜单编码 b_name nvarchar(100) not null, -- 菜单名称 b_i18n varchar(150) null, -- 显示名多语言 key - b_menu_type varchar(20) not null, -- directory/page/data/external - b_route varchar(200) null, -- 固定页面路由或外链 - b_module_id bigint null, -- data 页面绑定的数据/查询模块 - b_power_code varchar(50) null, -- 页面访问权限编码(对应 s_power.b_code) + b_menu_type varchar(20) not null, -- directory/page/external + b_route varchar(200) null, -- 页面路由或外链地址 + b_module_id bigint null, -- page 页面绑定的数据/查询模块 b_icon nvarchar(max) null, -- 图标:lucide:Name 引用 lucide 图标;其他字符串视为 SVG 标记 b_xh int not null default 0, -- 排序号 b_canuse int not null default 1, -- 是否启用(停用后不显示且不可访问) constraint pk_s_menu primary key (b_id), constraint ux_s_menu_code unique (b_code), - constraint ck_s_menu_parent check (b_parent_id is null or b_parent_id <> b_id), -- constraint fk_s_menu_parent foreign key (b_parent_id) references s_menu (b_id), -- constraint fk_s_menu_module foreign key (b_module_id) references s_module (b_id) ); @@ -387,9 +385,9 @@ create index ix_s_menu_parent on s_menu (b_parent_id, b_xh, b_canuse); create index ix_s_menu_module on s_menu (b_module_id); ``` -菜单类型约束由元数据校验器执行:`directory` 不填业务路由;`page` 必须填 `b_route`;`data` 必须填 `b_module_id`;`external` 必须填 `b_route` 和 `b_power_code`。目录仅在存在至少一个可访问子页面时显示。 +菜单类型约束由业务层执行:`directory` 不填业务路由和模块;`page` 必须填 `b_route` 和 `b_module_id`;`external` 必须填 `b_route` 且不绑定模块。已保存菜单不允许修改菜单类型。菜单访问权限由 `b_code` 派生,不再单独配置访问权限编码。目录仅在存在至少一个可访问子页面时显示。 -配置工作台按以下顺序呈现:页面基本信息 → 绑定模块 → 字段与分组 → 列表 → 表单 → 查询 → 关联模块 → 自动编码 → 权限。保存时由服务层校验菜单类型、模块类型和模块关系。 +菜单配置表单按以下顺序呈现:菜单名称 → 菜单编码 → 菜单类型 → 上级菜单 → 路由/外链地址 → 绑定模块 → 图标 → 排序号。菜单类型、路由和模块关系由菜单管理的业务层校验。 没有独立菜单的子模块、费用明细、选择器和公共字典,通过主模块的 `s_relation` 挂在所属数据模块下;它们不需要重复创建菜单,也可以在同一个配置工作台中维护。 @@ -470,7 +468,7 @@ create table s_power ( b_name nvarchar(100) not null, -- 权限名称 b_i18n varchar(150) null, -- 显示名多语言 key b_power_type varchar(20) not null, -- menu/module/action/field - b_resource varchar(128) null, -- 菜单 code/固定页面或扩展资源 code + b_resource varchar(128) null, -- 菜单 code 或扩展资源 code b_module_id bigint null, -- module/action/field 所属模块 b_field_id bigint null, -- field 权限对应字段 b_operation varchar(30) not null, -- access/read/create/update/delete/export/execute/view/edit @@ -557,7 +555,7 @@ create index ix_s_role_scope_role on s_role_scope (b_role_id, b_module_id, b_ope - 数据范围在操作权限通过后追加。`creator` 匹配记录创建人,`owner/department/department-tree` 通过 `b_scope_field_id` 与当前登录上下文匹配;`custom` 使用结构化 JSON 条件并只允许引用当前模块字段。 - 用户拥有多个角色时,同一操作的数据范围取并集;同一角色内多个范围记录也取并集,任一角色/范围为 `all` 即不再追加范围条件。没有有效范围记录时,即使拥有操作权限也默认无数据,避免误放开全表。 -菜单显示继承页面访问权限:用户拥有 `s_menu.b_power_code` 对应权限时菜单才显示;前端只控制显示,后端运行时必须再次校验同一个权限编码。 +菜单显示继承页面访问权限:用户拥有由 `s_menu.b_code` 派生的菜单访问权限时菜单才显示;前端只控制显示,后端运行时必须再次校验同一个权限编码。 ### 3.15 s_i18n 多语言 @@ -733,7 +731,7 @@ create index ix_s_log_audit_field_customer on s_log_audit_field (b_module_code, | `s_menu` | `b_module_id` | 绑定的数据/查询模块 `s_module.b_id` | | `s_field_group` | `b_parent_id` | 父分组必须与当前分组属于同一数据模块 | | `s_field_query` | `b_relation_id` | 关联关系必须从查询模块可达条件字段所属模块 | -| `s_menu` | `b_power_code` | `s_power.b_code` | +| `s_menu` | `b_code` | 菜单访问权限资源编码(由菜单编码派生) | | `s_power` | `b_resource` / `b_operation` | 权限类型、资源和操作必须匹配;field 权限必须引用对应模块字段 | | `s_role_scope` | `b_scope_field_id` / `b_scope_value` | 数据范围字段和范围值必须与模块及范围类型匹配 | | `s_relation` | 关系类型与字段组合 | 多对多必须有中间模块;两侧字段必须属于声明的模块 | diff --git a/code/fms/fms-api/config/migrations/100_initialize_fms_new.sql b/code/fms/fms-api/config/migrations/100_initialize_fms_new.sql index 64a650c0..48166242 100644 --- a/code/fms/fms-api/config/migrations/100_initialize_fms_new.sql +++ b/code/fms/fms-api/config/migrations/100_initialize_fms_new.sql @@ -256,14 +256,11 @@ BEGIN b_menu_type varchar(20) NOT NULL, b_route varchar(200) NULL, b_module_id bigint NULL, - b_power_code varchar(50) NULL, b_icon nvarchar(max) NULL, -- 图标:lucide:Name 引用 lucide,否则为 SVG 标记 b_xh int NOT NULL CONSTRAINT DF_s_menu_xh DEFAULT (0), b_canuse int NOT NULL CONSTRAINT DF_s_menu_canuse DEFAULT (1), CONSTRAINT PK_s_menu PRIMARY KEY (b_id), - CONSTRAINT UQ_s_menu_code UNIQUE (b_code), - CONSTRAINT CK_s_menu_parent CHECK (b_parent_id IS NULL OR b_parent_id <> b_id), - CONSTRAINT CK_s_menu_type CHECK (b_menu_type IN ('directory','page','data','external')) + CONSTRAINT UQ_s_menu_code UNIQUE (b_code) ); CREATE INDEX IX_s_menu_parent ON dbo.s_menu (b_parent_id, b_xh, b_canuse); CREATE INDEX IX_s_menu_module ON dbo.s_menu (b_module_id); diff --git a/code/fms/fms-api/config/migrations/104_drop_menu_power_code.sql b/code/fms/fms-api/config/migrations/104_drop_menu_power_code.sql new file mode 100644 index 00000000..8af5fbff --- /dev/null +++ b/code/fms/fms-api/config/migrations/104_drop_menu_power_code.sql @@ -0,0 +1,16 @@ +/* + 菜单访问权限统一由 s_menu.b_code 派生,不再保存独立的 b_power_code。 + 可重复执行:仅在旧列存在时删除。 +*/ +SET NOCOUNT ON; +SET XACT_ABORT ON; + +IF DB_NAME() <> N'FMS' + THROW 53600, N'This migration can only run against the FMS database.', 1; + +IF COL_LENGTH(N'dbo.s_menu', N'b_power_code') IS NOT NULL +BEGIN + ALTER TABLE dbo.s_menu DROP COLUMN b_power_code; +END; + +PRINT N'Migration 104 applied: s_menu.b_power_code removed.'; diff --git a/code/fms/fms-api/config/migrations/105_normalize_menu_types.sql b/code/fms/fms-api/config/migrations/105_normalize_menu_types.sql new file mode 100644 index 00000000..c19c4040 --- /dev/null +++ b/code/fms/fms-api/config/migrations/105_normalize_menu_types.sql @@ -0,0 +1,39 @@ +/* + 菜单类型由业务层校验,不在数据库中使用 CHECK 约束限制。 + 现有 data 菜单统一迁移为 page:页面现在同时填写路由和绑定模块。 + 可重复执行:仅处理仍存在的旧数据或约束。 +*/ +SET NOCOUNT ON; +SET XACT_ABORT ON; + +IF DB_NAME() <> N'FMS' + THROW 53600, N'This migration can only run against the FMS database.', 1; + +IF OBJECT_ID(N'dbo.s_menu', N'U') IS NOT NULL +BEGIN + UPDATE dbo.s_menu + SET b_menu_type = 'page' + WHERE b_menu_type = 'data'; + + IF EXISTS ( + SELECT 1 + FROM sys.check_constraints + WHERE parent_object_id = OBJECT_ID(N'dbo.s_menu') + AND name = N'CK_s_menu_type' + ) + BEGIN + ALTER TABLE dbo.s_menu DROP CONSTRAINT CK_s_menu_type; + END; + + IF EXISTS ( + SELECT 1 + FROM sys.check_constraints + WHERE parent_object_id = OBJECT_ID(N'dbo.s_menu') + AND name = N'CK_s_menu_parent' + ) + BEGIN + ALTER TABLE dbo.s_menu DROP CONSTRAINT CK_s_menu_parent; + END; +END; + +PRINT N'Migration 105 applied: s_menu uses directory/page/external and business-layer validation.'; diff --git a/code/fms/fms-vue/docs/module-management-code-refactor-plan.md b/code/fms/fms-vue/docs/module-management-code-refactor-plan.md deleted file mode 100644 index 46186e21..00000000 --- a/code/fms/fms-vue/docs/module-management-code-refactor-plan.md +++ /dev/null @@ -1,303 +0,0 @@ -# `/system/module-management` 代码重构计划(还债专项) - -> 本文是**纯代码重构**计划:不改任何行为、UI 文案、API 调用和保存协议。 -> 与 `docs/module-management-refactor-plan.md`(功能改造历史记录)无关,请勿混淆。 -> 执行前请先通读本文第 1、2 节和第 8 节(禁改清单)。 - -## 1. 背景与问题 - -模块管理功能已基本完成,但代码存在三类结构性债务: - -1. **巨型文件**:`ModuleFormEditPanel.vue` 3709 行、`ModuleTableEditPanel.vue` 2962 行、 - `index.vue` 1522 行,全模块约 1.2 万行。 -2. **三处复制的"同源"逻辑**:分组树推导在面板与结构树组件间整段复制(注释自述 - "与面板 bandMeta 同源");两个编辑面板共享约 20 个同名函数。靠人肉同步,改一处漏 - 一处就是 bug。 -3. **index.vue 职责过重**:树管理、草稿加载、保存编排、删除级联、i18n 协调、确认弹窗 - 全部平铺在一个 `