feat: 新增IM即时通讯(浮窗)、工作流、打印模块及工作台增强
- IM: 新增浮窗聊天(ImFloatWindow)、管理页(monitor/config/service/message)、SSE推送 - 工作流: 新增待办/我的流程页面及后端服务 - 打印: 新增打印模板、出库单打印(PrintPage)、模板种子脚本 - 工作台: 增强快捷入口与工作台数据 - 修复: TagsView页签关闭、CrudPage通用表格增强 - 移除导航菜单中的即时通讯入口,改为右下角浮窗
This commit is contained in:
+90
-24
@@ -11,9 +11,10 @@
|
||||
|
||||
| 序号 | 任务 | 状态 | 说明 |
|
||||
|---|---|---|---|
|
||||
| 1 | 通用打印接入主前端 | 🔄 **进行中(方案已定,代码未写)** | 三步:后端取行数据接口 → openprint 外部打印模式 → web 打印按钮 |
|
||||
| 2 | 简易工作流(采购/领料审批闭环) | ⏳ 待做 | 4 张表已有,缺服务+API+前端审批 UI |
|
||||
| 1 | 通用打印接入主前端 | ✅ **v1 完成(三等分原料出库单可打印)** | 后端取数 + openprint `/print` 外部页 + web 打印按钮 + 三等分模板入库;联调收尾:已造测试数据(庄口 id=3 / 出库单 id=2),打印数据端点返回完整 |
|
||||
| 2 | 简易工作流(采购/领料审批闭环) | ✅ **v1 完成(原料出库审批闭环)** | WorkflowService + API(7 端点)+ 待办中心/我发起的页面 + 原料出库单「审批」按钮;流程定义/菜单已种子入库 |
|
||||
| 3 | 专业业务逻辑(自动定级/能耗分摊/成本盈亏/断料预警) | ⏳ 待做 | 依赖任务 1/2 部分能力 |
|
||||
| 4 | 轻量 IM(内部互发 + 系统通知) | ✅ **v1 完成** | ImService + API(6 端点)+ IM 聊天页 + 顶栏消息铃铛 + 演示通知种子 |
|
||||
|
||||
已完成的里程碑:
|
||||
- ✅ 动态 CRUD 体系(后端 EntityCatalog + CrudService + DataController,前端 CrudPage + table-map 配置驱动)
|
||||
@@ -59,22 +60,31 @@
|
||||
### 3.1 目标
|
||||
表单通用打印:主前端任意 CRUD 列表 → 选中一行 → 选打印模板 → 打开 openprint 用**该行真实数据**打印(针式/A4 均支持,设计器已具备)。
|
||||
|
||||
### 3.2 已锁定的三步方案(代码未写,按序实施)
|
||||
### 3.2 已实施的三步方案(2026-08-15 落地 ✅,v1 三等分原料出库单)
|
||||
|
||||
**Step A — 后端:取行数据接口**
|
||||
- 位置:`PrintApiController.cs` 新增(或新 controller),路由建议 `GET api/print/data/{table}/{id}`。
|
||||
- 实现:基于 `EntityCatalog` 动态取实体 Type → FreeSql 按 `id` 查询单行 → 输出结构匹配绑定 Path:`{ "<TableId>": { "<camelCaseProp>": value, ... } }`(如 `{ "rawMaterial_Instock": { "orderNo": "...", ... } }` 或 PascalCase 表名,需与 PrintService.BuildFields 生成的 Path 前缀一致,建议直接复用 BuildFields 的 TableId)。
|
||||
- 返回裸 JSON(同 PrintApiController 风格,走 openprint http-client 的 json() 直接拿对象)。
|
||||
**Step A — 后端:取行数据接口(已完成)**
|
||||
- 位置:`PrintApiController.cs`,路由 `GET api/print/data/{table}/{id}`。
|
||||
- 实现:`EntityCatalog` 动态取实体 Type → 反射 `CrudService<T>.GetByIdAsync`(复用 DataController 模式,`ActivatorUtilities.CreateInstance`)→ 输出 `{ "<TableId>": { camelCase 字段 } }` 裸 JSON(信封键=表名,与 BuildFields 的 Path 前缀一致)。
|
||||
- 增强:① 关联字段补全 `RefMap`(`*Id` → `{Xxx}Code`/`{Xxx}Name`,如 ZhuangkouCode、OutOrgName);② 枚举文本注入 `EnumMaps`(如 OutType → OutTypeText 领料/退货/报损)。
|
||||
- 注意:openprint 侧会为行对象自动补 PascalCase 别名(`withPascalAliases`),因此模板绑定 `RawMaterial_OutStock.BillNo` 或 `billNo` 均可取到值。
|
||||
|
||||
**Step B — openprint:外部打印模式**
|
||||
- 解析 URL 参数:`?print=1&template=<templateId>&table=<tableId>&row=<rowId>&token=<token>`。
|
||||
- 启动流程(main.ts 或 App.vue):检测 `print=1` 时 → 用 URL 的 token(或 localStorage)替换 HttpClient 的 token → 加载模板(designer store `loadTemplate`)→ 调 `api/print/data/{table}/{row}` 拉真实数据 → 注入到 dataSource store 供 `doPrint` 使用(如新增 `externalData` 状态,`PrintDialog.doPrint()` 优先用它替代 `buildPreviewData`)→ 自动打开打印弹窗。
|
||||
- 注意:`designer.ts` 有 `loadTemplate({id,name,data})`/`buildTemplate()`/`setRepository()`/`restoreLastTemplate()`;`dataSource.ts` 的 `activeFields` 决定字段列表;只读模式下应隐藏设计工具栏(TopToolbar 需加 `readonly`/`external` 模式判断)。
|
||||
- token:主前端 web 存于 localStorage `f9mes_token`,openprint 与 web 同源部署时可直接读;跨端口时用 URL 传 token 更稳(注意 URL 长度与泄露,内网 MES 可接受)。
|
||||
**Step B — openprint:外部打印模式(已完成)**
|
||||
- 新增 `openprint/src/print/PrintPage.vue`(独立轻量打印页,不进入设计器),`main.ts` 检测 `?print=1` 时改挂载该页。
|
||||
- URL 参数:`?print=1&template=<模板id>&table=<表名>&row=<行id>&token=<JWT>`;可选 `api`(API 根地址,缺省 `VITE_OPENPRINT_API_BASE`);可选 `data`(base64url 直传数据,绕过数据请求)。
|
||||
- 流程:解析参数 → `createHttpRepository({baseUrl, token})` → 拉数据(`fetch /api/print/data/{table}/{row}` 带 Bearer,或解码 `data`)→ `withPascalAliases` 归一化 → `createHeadless({repository}).buildRequest(tplId, data)` → `headless.print()`(浏览器打印对话框,可用三等分纸型)。
|
||||
- 页面状态机:加载中/打印中/完成/失败 +「重新打印」「关闭」按钮;缺参/HTTP 错误均给出中文提示。
|
||||
|
||||
**Step C — web:CrudPage 打印按钮**
|
||||
- `CrudPage.vue` 工具栏加「打印」按钮(选中一行时可用)→ 弹窗列出该表可用模板(调 `api/print/templates` 按 `tableId` 过滤)→ 确认后 `window.open` openprint 打印 URL:`http://localhost:5227/?print=1&template={id}&table={tableName}&row={rowId}&token={token}`。
|
||||
- 需要后端提供"按表过滤模板"的能力(`PrintApiController.templates` 若支持 tableId 查询则直接用,否则加参数)。
|
||||
**Step C — web:CrudPage 打印按钮(已完成)**
|
||||
- 新增 `web/src/config/print.js`:`OPENPRINT_URL`(env `VITE_OPENPRINT_URL`,默认 `http://localhost:5227`)、`PRINT_API_BASE`(默认 `http://localhost:5136`)、`buildPrintUrl()`。
|
||||
- `web/src/config/table-map.js`:`PRINT_MAP`(component → `{templateName, title}`)+ `resolvePrint()`;当前配置 `RawMaterial/outstock` → 模板名「三等分原料出库单」。
|
||||
- `web/src/router/index.js`:路由 meta 注入 `printTemplate: resolvePrint(component)`。
|
||||
- `CrudPage.vue`:操作列按 `printTemplate` 条件渲染「打印」按钮 → `GET /print/templates` 按模板名找到 id → `window.open(buildPrintUrl({template, table, row, token}))`。操作列宽度 `opWidth` 随按钮动态计算。
|
||||
- 模板匹配用**名称**而非硬编码 id(数据库重建也不失效);多模板扩展时改 `PRINT_MAP` 即可。
|
||||
|
||||
**三等分模板(已完成)**
|
||||
- `server/scripts/print-template-outstock.json`:210×99mm(A4 三等分)竖版 portrait、边距 8mm;标题/单号/庄口/领料车间/出库重量(kg)/出库日期/出库类型/领用人+仓管员签章区。
|
||||
- `server/scripts/seed-print-template.mjs`:登录→查重→POST `/api/print/templates` 创建(node 执行,避免 PowerShell 中文编码坑)。
|
||||
- 已入库:模板 id=2(Name=三等分原料出库单,PaperSize=三等分)。表 `RawMaterial_OutStock` 当前无数据,录入后可验证完整打印。
|
||||
|
||||
### 3.3 关键文件清单
|
||||
- 后端:`server/src/F9MES.Api/Controllers/PrintApiController.cs`、`server/src/F9MES.Application/Print/PrintService.cs`、`server/src/F9MES.Api/Common/EntityCatalog.cs`、`server/src/F9MES.Api/Controllers/DataController.cs`
|
||||
@@ -83,13 +93,35 @@
|
||||
|
||||
---
|
||||
|
||||
## 四、任务 2:简易工作流(待做)
|
||||
## 四、任务 2:简易工作流(v1 完成 ✅,原料出库审批闭环)
|
||||
|
||||
- 现状:4 张表(Common_Workflow/Node/Instance/Task)已有,`WorkBenchController` 已统计 `Common_WorkflowTask`(Status==0 && UserId==当前用户)为待办。
|
||||
- 要做:
|
||||
1. 后端:工作流服务(流程定义 CRUD、节点配置、发起实例、提交/审批/驳回/撤回流转)、API(建议 `api/workflow/...`)。
|
||||
2. 前端:审批中心页面(我的待办、我发起的、流程配置);在采购单、领料申请等表单接审批。
|
||||
3. 关联:任务 1 打印对审批单据同样适用。
|
||||
### 4.1 已实施(2026-08-15 落地)
|
||||
- **后端**:
|
||||
- `Common_WorkflowInstance` 实体新增 `BizId`(关联业务记录 ID,跳转业务详情用)。
|
||||
- 新增 `server/src/F9MES.Application/Workflow/WorkflowService.cs` + `WorkflowDtos.cs`、`server/src/F9MES.Api/Controllers/WorkflowController.cs`(DI 注册于 Program.cs)。
|
||||
- API(`api/workflow/*`,均需 JWT):
|
||||
- `GET definitions?bizType=` 已发布流程定义(含节点)
|
||||
- `POST start` 发起流程(创建实例 + 首个审批任务,审批人取节点 ApproverJson 数组首个用户,缺省当前操作人)
|
||||
- `GET todos?page=&size=` 我的待办
|
||||
- `POST approve` 同意(推进下一审批节点,无则实例通过 Status=1)
|
||||
- `POST reject` 驳回(实例终止 Status=2)
|
||||
- `GET instances?page=&size=&status=` 我发起的实例
|
||||
- `GET instance/{id}` 实例详情(含任务轨迹)
|
||||
- **前端**:
|
||||
- `web/src/api/workflow.js`(7 个方法封装)。
|
||||
- `web/src/views/workflow/todo.vue` 待办中心(同意/驳回带意见、详情抽屉轨迹时间线);`my.vue` 我发起的(状态筛选、详情轨迹)。
|
||||
- `table-map.js`:`SPECIAL_PAGES` 加 `WorkFlow/todo`、`WorkFlow/my`;新增 `WORKFLOW_MAP`(component → bizType,当前 `RawMaterial/outstock` → `RawMaterial_OutStock`)+ `resolveWorkflow()`。
|
||||
- `router/index.js`:静态路由 `/workflow/todo`、`/workflow/my`;动态路由 meta 注入 `workflow: resolveWorkflow(component)`。
|
||||
- `CrudPage.vue`:操作列按 `workflowCfg` 条件渲染「审批」按钮 → 弹窗选流程定义 → 发起(opWidth 动态加宽)。
|
||||
- **种子**:`server/scripts/seed-workflow.mjs` 创建「原料出库审批」流程(Code=RM_OUT_APPROVE,节点:发起→车间主管审批[ApproverJson=["1"]]→结束)+ 侧边栏菜单(工作流目录→待办中心/我发起的)。
|
||||
- **验证**:编译通过(0 错误);端到端 API 闭环通过——发起(instanceId 正常回填)→ 待办(任务归属当前用户)→ 同意(实例 Status=1)→ 驳回(Status=2);菜单树已含待办中心/我发起的;web 全部模块编译 200。
|
||||
|
||||
### 4.2 关键坑
|
||||
- **FreeSql 自增回填**:`Insert(entity).ExecuteAffrowsAsync()` **不回填主键**(返回 0),必须用 `ExecuteIdentityAsync()` 并手动赋值(CrudService.AddAsync 已是此写法)。
|
||||
|
||||
### 4.3 扩展方式
|
||||
- 新表单接审批:`WORKFLOW_MAP` 加一行 + 后端种子加流程定义(复用 `seed-workflow.mjs` 模式);审批人可在节点 `ApproverJson` 配置用户ID数组。
|
||||
- v1 简化:单审批人串行流转;同一节点多审批人(会签/或签)、条件分支、撤回、流程配置 UI 留待后续。
|
||||
|
||||
---
|
||||
|
||||
@@ -111,6 +143,8 @@
|
||||
4. **openprint 是独立应用**:改动它用 `npm run dev`(端口 5227)独立调试;主前端 web 是 vite dev(5173)。
|
||||
5. 数据库连接(项目说明):MySQL `116.198.221.105` 库 `f9web` 用户 `f9web`;阿里云 OSS(bbit-f9-web);天气用 Open-Meteo。
|
||||
6. 项目要求:类/文件命名带模块前缀、各模块类放各自文件夹、公共类放 Common;界面 MES 风格、高可配置、表单必须支持打印、表单带简易工作流。
|
||||
7. **PowerShell 中文编码坑**:PS 5.1 按 GBK 读 UTF-8 无 BOM 文件(脚本/文件名乱码)、`Invoke-RestMethod` 发中文 body 易 400(GBK 发送 vs UTF-8 解析)。对策:① 涉及中文的脚本用 **node(.mjs)** 写(原生 UTF-8);② 文件/命令行尽量用 ASCII 名。
|
||||
8. **启动服务**:后端中文路径下 `Start-Process dotnet` 传中文路径会损坏 → 用 `cmd /c "cd /d <路径> && dotnet run ..."` 或先取 8.3 短路径;API 冷启动要等 `SyncStructure` 全表同步(114 张远程表,约 1-2 分钟)后才监听端口。
|
||||
|
||||
---
|
||||
|
||||
@@ -125,11 +159,43 @@
|
||||
### 7.2 openprint 全链路调研(已完成,结论见第三节)
|
||||
已通读:PrintApiController / PrintService / EntityCatalog / DataController / http-repo / http-datasource / http-client / backend.ts / main.ts / App.vue / TopToolbar / PrintDialog / designer store / dataSource store / preview-data / expression / data-binder / CrudPage / table-map / request.js / user store / 两端 vite 配置。结论已沉淀到第三节。
|
||||
|
||||
### 7.3 通用打印接入 v1(已完成 ✅,三等分原料出库单)
|
||||
- 后端:`GET api/print/data/{table}/{id}`(关联名称 + 枚举文本补全,裸 JSON 信封),CORS 增加 `localhost:5227`。
|
||||
- openprint:`/print` 外部打印页(PrintPage.vue + main.ts 挂载逻辑),headless 渲染 + 浏览器打印。
|
||||
- web:打印按钮(CrudPage + PRINT_MAP + router meta + print.js 配置)。
|
||||
- 模板:210×99mm 三等分原料出库单已入库(id=2);种子脚本 `server/scripts/seed-print-template.mjs`(node)。
|
||||
- 验证:API/模板/数据端点 404 行为/两端模块编译均通过。
|
||||
- **联调收尾**:已造测试数据——庄口 id=3(YLZ20260815-001 四川凉山-2026春茧)、出库单 id=2(CK-20260815-001,125.5kg 领料/已出库);`GET api/print/data/RawMaterial_OutStock/2` 返回完整(含 ZhuangkouName、OutTypeText=领料);打印页 URL 已验证(选三等分纸型即可打印)。脚本 `server/scripts/seed-test-outstock.mjs`。
|
||||
|
||||
### 7.4 简易工作流 v1(已完成 ✅,原料出库审批闭环)
|
||||
- 后端:WorkflowService + WorkflowController(7 端点,`api/workflow/*`),实体加 BizId;DI 注册。
|
||||
- 前端:workflow.js / todo.vue / my.vue / WORKFLOW_MAP / 静态路由 / CrudPage「审批」按钮。
|
||||
- 种子:`server/scripts/seed-workflow.mjs`(原料出库审批流程 + 工作流菜单)。
|
||||
- 验证:编译 0 错误;API 闭环(发起→待办→同意 Status=1;驳回 Status=2);菜单树含待办中心/我发起的;web 模块编译 200。
|
||||
- 坑:FreeSql `ExecuteAffrowsAsync` 不回填自增主键,须用 `ExecuteIdentityAsync`。
|
||||
|
||||
### 7.5 轻量 IM v1(已完成 ✅,内部互发 + 系统通知)
|
||||
- **数据**:复用 `Common_Message` 表,扩展 `SenderId`/`SenderName`(发送人,0=系统),`MsgType` 增加 `4=单聊消息`(原 0=系统 1=业务 2=审批 3=预警)。
|
||||
- **后端**:`server/src/F9MES.Application/Im/ImService.cs` + `ImDtos.cs`、`server/src/F9MES.Api/Controllers/ImController.cs`(DI 注册于 Program.cs)。
|
||||
- API(`api/im/*`,均需 JWT):`GET sessions` 会话列表(按对端聚合最后消息+未读数)、`GET messages?peerId=&page=&size=` 聊天记录(双方)、`POST send` 发送单聊({peerId, content})、`POST read` 标记已读({peerId})、`GET unread-count` 未读总数(顶栏角标)、`POST notify` 系统/业务通知群发({userIds?, msgType, title, content},空 userIds=全部启用用户)。
|
||||
- **前端**:
|
||||
- `web/src/api/im.js`(6 方法)。
|
||||
- `web/src/views/im/index.vue` 聊天中心(左侧会话列表:头像/最后消息/时间/未读角标,右上 + 发起新会话选用户;右侧消息气泡+Enter 发送;5s 轮询)。
|
||||
- `layout/index.vue` 顶栏新增消息铃铛(未读角标,30s 轮询,点击跳 `/im`)。
|
||||
- `table-map.js`:`SPECIAL_PAGES` 加 `Im/index`;`router/index.js` 加静态路由 `/im`。
|
||||
- **种子**:`server/scripts/seed-im.mjs` 创建「协作」目录 →「即时通讯」菜单 + 3 条演示通知(工单变更/工艺单变更/审批通知)。
|
||||
- **验证**:编译 0 错误;API 闭环通过——管理员↔车间主管李工(测试用户 id=2)互发、未读计数、已读清零、会话聚合、聊天记录含 senderName;菜单树含「即时通讯/待办中心/我发起的」;web 模块编译 200;前端 lint 0 错误。
|
||||
- **坑(重要)**:`InitDataService.InitMenusAsync` 有开发期修复——**只要存在 `MenuType==1 && ParentId==0`(顶层页面节点)就物理删除全部菜单重建**。种子脚本新增菜单**必须用目录节点(MenuType=0, ParentId=0)+ 页面子节点(MenuType=1, ParentId=目录id)**结构;否则会连带删掉工作流等全部自定义菜单(本次已踩坑并修复)。
|
||||
|
||||
---
|
||||
|
||||
## 八、下一步行动(新对话从这里开始)
|
||||
|
||||
1. 先读本文件 + `功能清单` + `项目说明`(若工作区未加载)。
|
||||
2. 按 **3.2 的 Step A → B → C** 依次实施任务 1(打印接入),每步完成后用 `dotnet build -o build_tmp` / `npm run build` 验证,openprint 与 web 可分别 `npm run dev` 联调。
|
||||
3. 完成后再推进任务 2(简易工作流)。
|
||||
4. 任何大改动前更新本文件「最后更新」日期与任务状态。
|
||||
2. **人工验证**:
|
||||
- IM:登录 web → 顶栏消息铃铛(未读角标)→ 侧边栏「协作 → 即时通讯」聊天(管理员 ↔ 车间主管李工 13900000001/123456);演示通知已发到管理员账号。
|
||||
- 工作流:待办中心/我发起的列表 + 原料出库单「审批」按钮(测试数据出库单 id=2,流程 RM_OUT_APPROVE)。
|
||||
3. **多表单扩展**:a) 打印:`PRINT_MAP` 加 component → `{templateName}` + 设计模板;b) 审批:`WORKFLOW_MAP` 加 component → bizType + 种子脚本加流程定义;c) 通知:业务代码调 `POST api/im/notify`(工单/工艺单变更处已留接口)。
|
||||
4. 后续推进任务 3(专业业务逻辑:断料预警 → 自动定级 → 能耗分摊 → 成本盈亏)。
|
||||
5. 任何大改动前更新本文件「最后更新」日期与任务状态。
|
||||
6. **新增菜单铁律**:种子脚本建菜单必须「目录节点(MenuType=0)+ 页面子节点(MenuType=1)」结构,禁止顶层页面节点(MenuType=1, ParentId=0),否则 `InitDataService` 孤儿修复会物理删除全部菜单重建。
|
||||
|
||||
Reference in New Issue
Block a user