质量模块: - 11 张工艺执行书表:封面、试产、选剥茧(含混茧明细)、试缫与大样、煮茧(含分车间配温、副煮车间)、自动缫(含大样)、后缫 - 10 张样茧试缫表:原料小样/综合样主表 + 选茧、解舒、茧幅、切剖、清洁洁净、茧丝百回纤度 - 服务与接口、主控指针回写(列表 √ 标记)、封面编号自增、报表数据包 - 前端 8 张报表打印版式、7 类工艺单据与 6 类调查录入弹窗、字段驱动表单/表格组件 - 菜单种子新增「质量」目录,样茧子表增加 YjssType 判别列,避免原料小样与综合样自增 Id 重号串数据 报表中心: - 报表模板管理、锐浪 grhtml5 加载与预览、全屏设计面板、数据源 Provider(落丝记录、职工花名册) 其他:同步工作进度 1-11 文档、开发数据库快照、.gitignore 忽略运行日志目录
11 KiB
工作进度-3:部门管理补齐管理/职责类型绑定 + 角色授权「勾了不生效」根治
时间:2026-10-20(承接工作进度-2) 环境:后端 .NET 8 + FreeSql + MediatR(Windows),前端 Vue3 + Element Plus + Vite 范围:①部门管理页支持绑定
ManageType/DutyType;②定位并修复角色管理「分配权限保存后不生效、该角色登录看不到菜单」
一、本轮目标
- 部门管理页面支持给部门绑定
Department_ManageType_Enum与Department_DutyType_Enum - 修复「角色管理 → 分配权限 → 勾选菜单 → 确认保存 → 重新打开仍是未勾选、该角色账号也加载不到菜单」
- 补齐工作进度-2 遗留第 1 条:部门增删改后刷新部门缓存
- 顺手修掉部门编辑会把「禁用」状态改回「启用」的连带问题
二、部门管理(ManageType / DutyType 绑定)
2.1 定位
后端原本就支持两个字段:
| 层 | 文件 | 说明 |
|---|---|---|
| 实体 | Domain/Entities/SYS/Departments/Department.cs |
manage_type / duty_type 私有 set |
| 命令 | CQRS/SYS/Departments/Commands/Department_CreateCommand.cs / _UpdateCommand.cs |
均带两个枚举参数 |
| 读模型 | Dtos/SYS/Departments/DepartmentReadDto_View.cs |
已返回 ManageType / DutyType |
问题全在前端 web/src/views/sys/department/index.vue:
| 项 | 原状态 |
|---|---|
| 表单 | 只有「类型」= ManageType;DutyType 藏在 form 里固定为 5,没有任何控件 |
| 列表 | 只有管理类型列,看不到职责类型 |
| 上级部门 | 平铺下拉,看不出层级;clearable 还会把值置空,与 long ParentId 不匹配 |
| 缓存 | 保存后不刷新 utils/departments.js 的部门缓存 |
注:菜单里生效的是
/sys/department→sys/department/index;/system/department→system/department/index是死代码(它调用的/api/department已被下线,菜单也被 seeder 清掉)。
2.2 改动
web/src/views/sys/department/index.vue(重写)
- 表单新增「职责类型」下拉;「管理类型」改用共享枚举,删除本地硬编码的
manageTypeMap - 列表改树形表格(
row-key+tree-props),新增「管理类型」「职责类型」两个标签列,新增「添加子部门」 - 上级部门改
el-tree-select,编辑时排除自身及子孙(防成环) - 保存 / 删除 / 启停后调用
clearDepCache(),报表部门控件立即生效 parentId按字符串回传(部门 Id 是雪花 Id,Number()会丢精度)
web/src/utils/departments.js
DUTY补上缺失的后缫整理: 115- 新增
enumOptions(map)(枚举 → 下拉选项,按数值升序)与enumLabel(map, value)(值 → 中文名)
Infrastructure/FreeSqls/Repositories/SYS/Departments/DepartmentWriteRepository.cs
UpdateAsync增加.IgnoreColumns(x => new { x.IsActive, x.IsDeleted, x.CreatedAt, x.CreatedBy })- 原因:
Department_UpdateCommand用new Department(...)重建对象,构造函数里IsActive = true,SetSource会把「禁用」的部门一编辑就改回「启用」;启用状态本应由独立的/active命令维护
2.3 实测
POST /api/sys/department {manageType:15(车间), dutyType:25(前缫)} → 创建成功
PUT /api/sys/department/{id} {dutyType:115(后缫整理)} → 修改成功
读回 => manageType=15 dutyType=115 isActive=False ✅
运行时(无头 Edge,/sys/department):
TABLE_ROWS=38
ROW0=123 / 安泰茧丝绸 / 部门 / 其他 / 0 / 启用 / 添加子部门 编辑 禁用 删除
FORM_ITEMS=编码|部门名称|上级部门|管理类型|职责类型|排序|描述
SELECT_1(管理类型)=公司,部门,车间,科室,岗位,虚拟
SELECT_2(职责类型)=其他,仓储,选剥茧,煮茧,前缫,后缫,设备办,副产品,质检,丝绵,人力资源,丝业中心,前缫管理,后缫检验,后缫整理
三、角色授权「勾了不生效」根治(本轮重点)
3.1 现象
角色管理 → 分配权限 → 勾选菜单 → 确认保存(提示"权限保存成功")
→ 再次点分配权限:之前勾的菜单仍是未勾选
→ 用该角色的账号登录:菜单也加载不出来
3.2 根因:雪花 Id 被前端 Number() 转成 double 后精度丢失
真实菜单 id : 134905585693888512
Number() 后 : 134905585693888510 ← 少 2
Number.isSafeInteger(...) = false
web/src/views/sys/role/index.vue 的 savePermType() 里写的是 resourceId: Number(key)。后端本来已经用 LongToStringJsonConverter 把 long 序列化成字符串(避免 JS 精度问题),前端又把它转回数字,等于白折腾。
| 环节 | 表现 |
|---|---|
| 保存 | sys_rbac_permissions.resource_id 写成 ...510,菜单表里不存在这个 id |
| 重新打开 | setCheckedKeys(['...510']) 在树里找不到节点 → 勾不上 → 显示未勾选 |
| 登录 | GetUserMenusAsync 拿 ...510 查菜单表 → 查不到 → 菜单不出现 |
| 再点确认 | 回显是空的 → 删除逻辑把这些权限彻底删掉 |
只有 id 很小的老数据(11/12/13…)不暴露此问题。第一轮用「普通用户」角色验证时,恰好勾的都是小 id 菜单,所以误判成"功能正常"——这是本次排查的教训。
3.3 修复(4 处)
| # | 文件 | 改动 |
|---|---|---|
| 1 | web/src/views/sys/role/index.vue |
resourceId: Number(key) → resourceId: key(字符串原样回传)。菜单 / 部门 / 报表三类授权共用此函数,一并修好 |
| 2 | Application/CQRS/SYS/RBAC/Roles/Commands/RolePermission_AssignCommand.cs |
新建菜单/操作类权限单元时,用菜单自己声明的 Permission(如 base:worktype:list)作为 Code;原来只有消息订阅会补 Code,其余会退化成 menu:{id}:visble,导致 v-permission 永远匹配不上。为此注入 IRepository<Menu> |
| 3 | Infrastructure/Data/DbSeeder.cs(FixMenuPermissions) |
增加自愈:启动时删除 ResourceId 不对应任何菜单的失效权限单元及其 role_permission 关联,修复历史脏数据 |
| 4 | web/src/views/sys/department/index.vue |
parentId 同样改为字符串回传(同类隐患) |
后端
LongToStringJsonConverter的Read已兼容字符串,所以前端传字符串可以直接绑定long(实测parentId:"1"、resourceId:"134905585693888512"均正常)。
3.4 验证
修复前(前缫主任,勾「工种管理」):
API_AFTER(18)=...,42,134905585693888510 ← 尾数错
REOPEN_CHECKED=17 MISSING_ON_REOPEN=工种管理
修复后:
TICKED=工种管理 now=18
API_AFTER(18)=11,12,17,13,14,18,15,16,19,20,21,22,23,24,30,31,42,134905585693888512
REOPEN_CHECKED=18
PERSISTED=YES(工种管理) ✅
用该角色真实账号(余华 / 13547542219)登录:
LOGIN code=200 roles=1000
MENU_TREE:
- 考勤管理 [/attendance] → 考勤打卡 / 我的考勤 / 请假申请 / 考勤记录 / 请假审批
- 基础数据 [/base] → 工艺庄口 / 生丝规格 / 生丝定级指标 / 机组车台 / 工种管理 ← 新加的已生效
- 生产-前缫 [/prod/qianxian] → 落丝记录
- 消息中心 [/notification] → 我的消息 / 消息订阅
PERMS 含 base:worktype:list
重启后端时的自愈日志侧证:前缫主任 菜单权限中「不在菜单树内」的资源 Id 由 1 条降为 0;后缫检验员 / 后缫主任 的菜单权限数由 10 → 8(清掉 2 条无效单元)。
四、验证手段(沿用工作进度-2)
无 Node/npm 全局环境,前端用项目内置 node:
:: .run-logs\start-web.bat 内部就是这两步
set PATH=C:\Users\32453\.workbuddy\binaries\node\versions\22.22.2-6;%PATH%
cd /d d:\f9webaitest\f9-web-ai-test\web
node node_modules\vite\bin\vite.js
后端:dotnet run --project backend\F9WebAiTest.Api\F9WebAiTest.Api.csproj
SPA 运行时验证(无头 Edge):
# 1) 诊断页放 web/public/__diag_xxx.html:login 写 localStorage → history.replaceState 改路由
# → 等 el-table 出现 → 模拟点击 → 把断言写进 <div class="L">,用完即删
msedge --headless=new --user-data-dir=$prof --virtual-time-budget=150000 --disable-gpu --dump-dom "http://localhost:5173/__diag_xxx.html"
要点:
--virtual-time-budget会让setTimeout与fetch都按虚拟时钟推进,可放心写await sleep()- 想直接渲染某路由,在模块脚本之前插一段
history.replaceState({}, '', '/sys/role')(本项目是createWebHistory,不是 hash 路由) - 诊断页里的内联
<script>必须语法正确——本轮曾因多一个catch导致整段不执行、表现为"页面没反应",排查成本很高 - 统计勾选状态要用
:scope > .el-tree-node__content .el-checkbox,否则父节点会把子节点的is-checked也算进来
五、当前状态
| 项 | 状态 |
|---|---|
| 部门新增/编辑绑定管理类型、职责类型 | 正常,列表以标签展示 ✅ |
| 部门树形列表 / 添加子部门 / 上级树选择(排除自身子孙) | 正常 ✅ |
| 部门编辑不再覆盖启用状态 | 已修 ✅ |
| 部门缓存(报表部门控件)随部门增删改刷新 | 已接入 clearDepCache() ✅(工作进度-2 遗留 1 关闭) |
| 角色授权:勾选 → 保存 → 回显 → 该角色登录加载菜单 | 全链路打通(含雪花 Id 菜单)✅ |
六、遗留与后续建议
- 同类
Number()隐患(新数据 id 变大后会踩同一个坑,尚未动)web/src/views/base/chejianbanci/index.vue(depId)web/src/views/base/jizuchetai/index.vue(departmentId)web/src/views/base/employee/index.vue(depId/worktypeId,含depIds.map(Number))web/src/views/prod/lsjl/index.vue(depOptions的id: Number(o.key))
- 超级管理员
RolePermission重复:sys_rbac_role_permissions中 admin 有 481 条、只对应 49 个不同资源(历史 seeding 重复插入)。不影响鉴权(admin 直接放行),但角色列表「菜单权限」数字虚高,可去重。 - 部门权限无消费方:授权会写
department:{id}:visble,但前后端没有任何地方按此做数据/可见性过滤,勾选当前无实际效果——要么接上数据范围,要么先从 UI 移除。 - 「操作」权限维度未启用:
Resource_Type_Enum.操作 = 2与Permission_Type_Enum.Enable = 2已定义但未使用;保存时permissionType硬编码为 1(Visble)。按钮类节点混在「菜单权限」树里,无法单独授予「可操作」。 - 新菜单的权限单元:现由
FixMenuPermissions在启动时补建,AssignRolePermission兜底时也会按菜单的Permission补Code;建议后续把「建 Permission」直接并入菜单 CRUD,避免依赖启动时机。 - 死代码:
web/src/views/system/{department,role,menu,employee}与路由表里的system/*/index已无菜单指向(/system%菜单被 seeder 删除,对应后端接口也已下线),可整批删掉。