8.4 KiB
8.4 KiB
字段权限与字段个性化设计
状态:设计定稿,未实现。创建于 2026-09-04。 关联文档:FMS新系统核心表结构设计.md(其中 s_power / s_role_power 部分)
1. 背景与目标
系统已有全局的字段显示配置(s_field_view / s_field_edit,在模块设计器中维护,对所有人生效)。本设计解决两个新需求:
- 字段权限:管理员(高权限角色)控制某个人/某些人能不能看到某些字段;
- 字段个性化:用户自己调整列表的字段顺序、列宽、显示/隐藏,且只影响自己、跨设备持久化。
范围约定:
- 个性化只作用于列表(查看态列表 + 可编辑列表两套配置),表单不参与顺序/布局个性化;
- 单公司系统(无多租户维度),后续如需多公司再扩展;
- 不考虑旧数据迁移难度。
2. 三层模型
第 1 层 默认配置(模板) s_field_view / s_field_edit 模块设计器维护,管理员设定
第 2 层 字段权限(谁能看) s_power(field型) + s_role_power 管理员按角色控制可见性
第 3 层 个人偏好(怎么摆) s_user_field_pref(新表) 用户自己拖拽,只影响自己
职责边界是本设计的核心原则:
- 权限决定"全集":deny 的字段对用户不存在(列表不渲染、后端不返回);
- 偏好只决定"排列":顺序、宽度、个人显隐,永远在权限允许的范围内生效,无法越过权限;
- 终端用户能控制的只有自己的布局,可见性的控制权始终在管理员手里。
两层独立演化:将来权限规则变化,偏好数据不需要清洗(偏好里残留无权限字段只是无效数据,不构成越权)。
3. 数据结构
3.1 新表:s_user_field_pref(个人偏好)
一人 × 一模块 × 一字段 × 一模式 一行。每字段一行(而非 JSON 快照),可直接复用现有通用 CRUD(DataService)。
CREATE TABLE s_user_field_pref (
b_id VARCHAR(32) PRIMARY KEY,
b_user_id VARCHAR(32) NOT NULL,
b_module_id VARCHAR(32) NOT NULL,
b_field_id VARCHAR(32) NOT NULL,
b_mode VARCHAR(10) NOT NULL, -- 'view' 查看列表 / 'edit' 可编辑列表
b_visible TINYINT DEFAULT 1, -- 个人显隐(1 显示,0 隐藏)
b_xh INT, -- 个人顺序号
b_width INT, -- 个人列宽
b_update_time DATETIME,
UNIQUE KEY uk_user_field (b_user_id, b_module_id, b_field_id, b_mode)
);
说明:
- 用户没有保存过偏好的字段不产生行,解析时落回默认配置(
s_field_view/s_field_edit); - 保存过布局 = 该用户该模块该 mode 存在偏好行,此后默认布局变更对该用户不再生效(保持用户自主摆放的稳定性),可通过"恢复默认"删除偏好行回到跟随默认;
- "恢复默认" = 删除该用户 + 该模块 + 该 mode 的全部行。
3.2 权限层:复用已有表 + 一张用户级覆盖表
s_power(field 型:b_module_id + b_field_id + b_operation[view/edit])与s_role_power(b_effectallow/deny)沿用现有表结构,见 FMS新系统核心表结构设计.md;- 新增用户级覆盖表,用于个别用户的特例(平时应为空表,只为"开小灶"使用,不值得建角色时用它):
CREATE TABLE s_user_power (
b_id VARCHAR(32) PRIMARY KEY,
b_user_id VARCHAR(32) NOT NULL,
b_module_id VARCHAR(32) NOT NULL,
b_field_id VARCHAR(32) NOT NULL,
b_operation VARCHAR(10) NOT NULL, -- 'view' / 'edit'
b_effect VARCHAR(10) NOT NULL, -- 'allow' / 'deny'
UNIQUE KEY uk_user_field_op (b_user_id, b_module_id, b_field_id, b_operation)
);
4. 解析算法
4.1 字段可见性(权限链)
可见性 = 用户级覆盖(s_user_power,有配置则生效,优先级最高)
↓ 该用户该字段无配置
角色合并(该用户所有角色的 s_role_power,deny 优先:任一角色 deny 即 deny)
↓ 无任何角色配置
默认可见(allow)
要点:
- 默认全部可见,黑名单式配置:只为敏感字段配 deny,不为普通字段配 allow,维护量最小;
- deny 优先(而非 allow 优先):敏感字段场景下更安全。
4.2 列表最终配置(合并链)
对查看列表和可编辑列表分别执行(mode = view / edit):
最终配置 = 默认配置(s_field_view / s_field_edit,含 b_canuse 过滤)
→ 剔除权限 deny 的字段
→ 用个人偏好覆盖 b_xh / b_width / b_visible
→ 偏好中没有的字段,按默认配置的顺序追加补全
有效显示 = 权限可见 && 字段可用(b_canuse=1) && 个人未隐藏。
补全规则保证:新增字段、默认配置新启用的字段,即使用户保存过布局也会按默认顺序出现在列表尾部,不会"消失"。
4.3 顺序合并细节
偏好只覆盖出现过的字段;用户保存布局后管理员再调默认顺序,对已保存用户无效(见 3.1)。
5. 边界场景约定
| 场景 | 行为 |
|---|---|
字段 b_canuse=0(下架) |
不进入候选集,即使偏好表里有该字段的行也不显示(偏好行成无效数据,暂不清理,量大后在字段下架入口顺带删除) |
| 新增字段 | 所有用户可见:无偏好则按默认顺序出现在尾部 |
| 权限从 allow 变 deny | 字段对用户消失;偏好数据保留,权限恢复后用户的自定义布局自动恢复 |
| 用户隐藏字段 vs 权限隐藏字段 | 是两回事:个人隐藏 = 有权看但暂时不显示,"栏位设置"弹窗可随时勾回;权限隐藏 = 对该用户不存在,弹窗中不展示无权限字段 |
| 必填字段被用户隐藏(可编辑列表) | 待定,见第 8 节 |
6. 交互设计
- 列表列头拖拽排序、拖拽调宽(能力已有:
FmsTable.vue); - 拖拽结束后防抖保存(约 500ms)upsert 到
s_user_field_pref; - 菜单保留"保存/恢复栏位设置",语义从全局改为个人:"恢复"即删除偏好行;
- "栏位设置"弹窗:排序 + 个人显隐 + 列宽统一管理;不展示无权限字段;
- 可选:管理员入口"保存为全局默认",仍写
s_field_view/s_field_edit。
7. 改动范围(实现清单)
| 位置 | 改动 |
|---|---|
| migration | 新增 s_user_field_pref、s_user_power 建表 |
| 后端 DataService | ① 字段权限过滤:deny 字段从查询结果中剔除(设计文档既定要求,目前后端零过滤)② 偏好表 CRUD 复用现有通用机制 |
前端 stores/permissions.js |
新增 canField(moduleId, fieldId, operation):用户级 → 角色合并(deny 优先)→ 默认 allow |
前端 FmsModuleListPage.vue |
loadData 中实现合并链(默认 → 权限过滤 → 偏好覆盖 → 补全);saveColumnSettings 保存目标从 s_field_view/s_field_edit 改为 s_user_field_pref |
| 权限管理 UI | 模块管理 ModuleFieldsPanel 旁新增"字段权限"页签:选角色 × 勾字段(view/edit);入口本身由现有模块/按钮权限(canPower)控制,实现"高权限的人才能配" |
| 用户级覆盖 UI | 可后置:先支持直接改 s_user_power 数据,界面后续补 |
关键实现纪律:合并偏好的代码必须在权限过滤之后执行,保证偏好永远无法越过权限。
8. 待定事项
必填字段被用户隐藏后的校验策略(可编辑列表场景):
| 方案 | 规则 | 适用 |
|---|---|---|
| A. 所见即所验 | 隐藏后跳过必填校验 | "必填"只是引导性要求,空值可接受;且权限隐藏与用户隐藏行为天然统一,无需特殊分支 |
| B. 必填不可藏 | 可编辑列表中必填字段置灰、禁止隐藏 | "必填"是业务硬规则,字段空值会损害下游使用 |
判断标准:该字段空着,会不会有人受害。会 → B;不会 → A。权限隐藏(管理员 deny)场景下无论选哪个方案,行为都与 A 一致(用户"没有这个字段",天然不校验)。
结论摘要:默认配置(全局模板)→ 字段权限(管理员按角色控制可见性,deny 优先,默认全可见,用户级覆盖兜底)→ 个人偏好(仅列表的顺序/列宽/显隐,权限范围内生效,未设置走默认)。