diff --git a/.gitignore b/.gitignore
index 0ec9630..14e595e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,6 +25,7 @@ selenium-debug.log
**/.idea/
.vscode/
**/.vscode/
+~$*.docx
*.suo
*.ntvs*
*.njsproj
diff --git a/MES_Manage_View_V20/MacroinfManage_MES.code-workspace b/MES_Manage_View_V20/MacroinfManage_MES.code-workspace
new file mode 100644
index 0000000..2a0ed79
--- /dev/null
+++ b/MES_Manage_View_V20/MacroinfManage_MES.code-workspace
@@ -0,0 +1,7 @@
+{
+ "folders": [
+ {
+ "path": ".."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/MES_Manage_View_V20/今后开发计划_2026-05-27.md b/MES_Manage_View_V20/今后开发计划_2026-05-27.md
new file mode 100644
index 0000000..8a5bdc5
--- /dev/null
+++ b/MES_Manage_View_V20/今后开发计划_2026-05-27.md
@@ -0,0 +1,77 @@
+# 今后开发计划与新增需求分析记录
+
+生成日期:2026-05-27
+项目:MES_Manage_View_V20
+
+## 一、排期口径
+
+- 按“最终调整”数字从小到大排序。
+- 从 2026-05-28 开始排期。
+- 按工作日递推,跳过周六、周日。
+- 跳过端午节、中秋节、国庆节法定放假日期。
+- 调休日按工作日处理。
+- 暂按单开发线串行排期。
+
+节假日口径:
+
+- 端午节:2026-06-19 至 2026-06-21。
+- 中秋节:2026-09-25 至 2026-09-27。
+- 国庆节:2026-10-01 至 2026-10-07。
+- 调休上班日:2026-09-20、2026-10-10。
+
+## 二、开发计划排期
+
+| 顺序 | 需求 | 预估 | 开始 | 结束 | 备注 |
+|---:|---|---:|---|---|---|
+| 1 | 及时开工报表 | 1天 | 5.28 | 5.29 | 增加正选/反选查询 |
+| 2 | 机加件排产-表单 | 4天 | 5.29 | 6.04 | 日期筛选优化 + 历史工时查询;SAP新增字段不算MES开发 |
+| 3 | 装配已排产需求 | 12天 | 6.04 | 6.23 | 大需求,建议分阶段交付 |
+| 4 | 生产日报-送检(机加/装配) | 3天 | 6.23 | 6.26 | 建议统一报表,部门做筛选维度 |
+| 5 | 及时齐套跟踪结果报表 | 5天 | 6.26 | 7.03 | 依赖SAP缺件明细实时接口 |
+| 6 | 生产计划跟踪 | 3天 | 7.03 | 7.08 | 工序状态、颜色、标准工时、订单状态 |
+| 7 | 外协任务检验 | 3天 | 7.08 | 7.13 | 增加指派对象、供应商、SAP采购收货单号 |
+| 8 | 异常管控 | 5天 | 7.13 | 7.20 | 报工页面直接报异常,多审批人可见一人处理 |
+| 9 | 机加班次产量表 | 3天 | 7.20 | 7.23 | 新增报表 |
+| 10 | 发货通知列表 | 1天 | 7.23 | 7.24 | SAP数据已提供,可新增查询报表 |
+| 11 | 质检任务早知道 | 1天 | 7.24 | 7.27 | 依赖“装配已排产需求”的装配完成时间 |
+| 12 | 生产工艺 | 2天 | 7.27 | 7.29 | 无生产工艺时自动带出标准工艺 |
+| 13 | 工时报表 + 备注类小改 | 2天 | 7.29 | 7.31 | 指派对象、机加领料备注、机加/装配退库备注、保存提示等合并处理 |
+| 14 | 生产订单关闭 | 4天 | 7.31 | 8.06 | 批量更新SAP订单状态,风险较高 |
+| 15 | 及时检验报表 | 4天 | 8.06 | 8.12 | 报工超过2天未检验 |
+| 16 | 工序流转卡 | 1天 | 8.12 | 8.13 | 增加打印原料编码 |
+| 17 | 一楼质检大屏幕 | 4天 | 8.13 | 8.19 | 及时率、关键工序 |
+| 18 | 机加现场大屏幕 | 3天 | 8.19 | 8.24 | 缺勤、关键工序 |
+| 19 | 装配现场大屏幕 | 4天 | 8.24 | 8.28 | 缺勤、及时送检 |
+| 20 | 编程任务清单 | 4天 | 8.28 | 9.03 | 按装配类似逻辑做编程报工 |
+| 21 | 设备档案管理 | 2天 | 9.03 | 9.07 | 需先确认字段归属 |
+| 22 | 设备数据采集 | 8天 | 9.07 | 9.17 | 采集接口、落库、状态汇总 |
+| 23 | 设备指标/OEE | 6天 | 9.17 | 9.24 | 依赖设备采集和标准工时口径 |
+| 24 | 工时查询-设备采集联动 | 2天 | 9.24 | 9.29 | 程序工时字段对接采集数据 |
+| 25 | 手续确认报表 | 3天 | 9.29 | 10.09 | 装配/电气齐套列表 + 手续确认 |
+| 26 | 产品合格率(工位) | 3天 | 10.09 | 10.13 | 需确认良品/总产量口径 |
+| 27 | 让步接收记录 | 2天 | 10.13 | 10.15 | 新增查询页面 |
+| 28 | 设计任务报工模块 | 6天 | 10.15 | 10.23 | MES自建设计任务、设计工时 |
+| 29 | 工时异常报表 | 2天 | 10.23 | 10.27 | 依赖标准工时和实际单件工时 |
+| 30 | 正在加工状态 | 1天 | 10.27 | 10.28 | 机加正在进行中状态 |
+
+## 三、暂不直接排期 / 需等前置条件
+
+| 需求 | 原因 |
+|---|---|
+| 及时拣配(机加)MES | 等SAP数据、条码规则、状态写回规则 |
+| 及时拣配(装配)MES | 等SAP数据、条码规则、状态写回规则 |
+| 及时送检报表 | 需确认发料齐套时间/SAP字段是否稳定;若SAP数据已到,可插入0.5天 |
+| 工艺内容修改 | 已开工任务怎么处理未定 |
+| 标牌打印操作 | 需求未确定 |
+| 首检、巡检 | 需求内容为空,无法估时 |
+| 外协采购订单收检结果 | 需求内容为空,无法估时 |
+| 不合格品单据打印 | 需求内容为空,无法估时 |
+
+## 四、排期结论
+
+按以上单线工作日计划,已明确可做事项预计到 2026-10-28 完成。
+
+实际执行建议先做 5.28 到 6.23 的前三项,因为它们优先级最高,且“装配已排产需求”会影响后面的质检早知道、日计划、及时送检等需求。
+
+## 五、新增需求分析与开发记录原文
+
diff --git a/MES_Manage_View_V20/今后开发计划与新增需求分析记录_2026-05-27.docx b/MES_Manage_View_V20/今后开发计划与新增需求分析记录_2026-05-27.docx
new file mode 100644
index 0000000..10399e1
Binary files /dev/null and b/MES_Manage_View_V20/今后开发计划与新增需求分析记录_2026-05-27.docx differ
diff --git a/MES_Manage_View_V20/元利项目详细用户使用说明书.md b/MES_Manage_View_V20/元利项目详细用户使用说明书.md
index eafa154..e293efd 100644
--- a/MES_Manage_View_V20/元利项目详细用户使用说明书.md
+++ b/MES_Manage_View_V20/元利项目详细用户使用说明书.md
@@ -1090,8 +1090,7 @@
9. 看板:生产、装配、质检、机加、综合大屏。
10. 新增需求页面:手续确认、及时检验、装配已排产需求、齐套跟踪等。
-## 二十一、逐页详细操作说明(按 src/views 页面)
-
+## 二十一、逐页详细操作说明
本章按源码页面逐页编写。每个页面都从实际 `v-model`、表格列、按钮、弹窗、页签、存储过程和 SAP/WMS 调用中提取操作线索,转换成用户可执行的步骤。
通用阅读方法:
diff --git a/MES_Manage_View_V20/技术开发详细方案_2026-05-27.docx b/MES_Manage_View_V20/技术开发详细方案_2026-05-27.docx
new file mode 100644
index 0000000..6eb6961
Binary files /dev/null and b/MES_Manage_View_V20/技术开发详细方案_2026-05-27.docx differ
diff --git a/MES_Manage_View_V20/技术开发详细方案_2026-05-27.md b/MES_Manage_View_V20/技术开发详细方案_2026-05-27.md
new file mode 100644
index 0000000..f0c69a3
--- /dev/null
+++ b/MES_Manage_View_V20/技术开发详细方案_2026-05-27.md
@@ -0,0 +1,3441 @@
+# MES 新增需求技术开发详细方案
+
+生成日期:2026-05-27
+依据文档:
+
+- `今后开发计划与新增需求分析记录_2026-05-27.docx`
+- `今后开发计划_2026-05-27.md`
+- `新增需求分析与开发记录_2026-05-23.md`
+- `新增需求界面原型设计_2026-05-25.md`
+- `隐性需求综合分析_2026-05-25.md`
+- `MES_功能列表_页面_存储过程映射.md`
+- `项目功能技术路线数据库分析报告_2026-05-22.md`
+- `script.sql`
+
+## 1. 项目技术现状
+
+### 1.1 前端技术栈
+
+主系统目录:`MES_Manage_View_V20`
+
+技术栈:
+
+- Vue 2.7.16
+- Vue Router 3.6.5
+- Vuex 3.6.2
+- Element UI 2.15.14
+- Webpack 5
+- Axios 0.21.4
+- xlsx / xlsx-style 用于导出
+- vue-print-nb 用于打印
+- pdfjs-dist 用于图纸预览
+- mqtt 用于设备或网关通信
+
+主业务页面位置:
+
+- `src/views/PlanManagement`:计划、排产、订单关闭。
+- `src/views/ProductionManagement`:生产中心、装配中心、工时、外协、异常、班次。
+- `src/views/QualityManagement`:质检中心、质检任务、质检明细、不合格品。
+- `src/views/ProcessManagement`:标准工艺、生产工艺、工序、图纸。
+- `src/views/DeviceManagement`:设备台账、设备实时数据、设备历史数据。
+- `src/views/SystemMaintenance`:工位、人员、权限等基础维护。
+- `src/views/AnomalousManagement/PunctualWork`:及时开工报表。
+
+### 1.2 后端调用方式
+
+系统现有模式不是 REST API,而是前端统一传存储过程名给 `.ashx` 网关执行。
+
+调用方式:
+
+```text
+Vue 页面
+ -> this.CreateData(type, procedureName, params, pageSize, pageCurrent)
+ -> this.ExecDatabase(data)
+ -> MESCommonBase.ashx
+ -> SQL Server 存储过程
+```
+
+常用 type:
+
+| type | 用途 | 说明 |
+|---|---|---|
+| `11` | 查询 | 返回列表或分页数据 |
+| `12` | 新增、修改、删除 | 返回执行结果 |
+| `7` | JSON 批量 | 用于批量排产等 |
+| `2001` | 导出 | 现有导出相关逻辑使用 |
+
+参数格式:
+
+```js
+const param = []
+param.push(['订单号', this.query.订单号])
+param.push(['合同号', this.query.合同号])
+const data = this.CreateData('11', '存储过程名', param, this.pageSize, this.pageCurrent)
+this.ExecDatabase(data).then(res => { ... })
+```
+
+### 1.3 核心数据库对象
+
+本轮开发必须优先复用以下核心表和视图。
+
+#### 生产计划主表
+
+表:`MES_接口_生产计划`
+
+关键字段:
+
+| 字段 | 类型 | 说明 |
+|---|---|---|
+| `订单编号` | int | 生产订单号,主键 |
+| `合同号` | nvarchar(50) | 合同维度 |
+| `订单状态` | nvarchar(50) | SAP/MES 订单状态 |
+| `物料编号` | nvarchar(500) | 主物料编码 |
+| `物料描述` | nvarchar(500) | 主物料名称 |
+| `计划数量` | decimal(16,2) | 订单计划数量 |
+| `计划开始` | datetime | 订单计划开始 |
+| `计划完成` | datetime | 订单计划完成 |
+| `同步时间` | datetime | SAP/MES 同步时间 |
+| `任务状态` | int | 订单任务状态 |
+| `齐套` | float | 齐套状态或齐套比例 |
+| `预计齐套时间` | datetime | 现有预计齐套时间 |
+| `齐套时间` | datetime | 现有齐套时间 |
+| `发料状态` | nvarchar(50) | 发料状态 |
+| `拣配状态` | nvarchar(50) | 拣配状态 |
+| `发货状态` | nvarchar(50) | 发货状态 |
+| `要求发货日期` | datetime | 发货要求日期 |
+| `入库数量` | decimal(19,6) | 入库数量 |
+| `确认状态` | nvarchar(50) | 现有确认状态 |
+
+#### 生产任务表
+
+表:`MES_接口_生产计划_生产任务`
+
+关键字段:
+
+| 字段 | 类型 | 说明 |
+|---|---|---|
+| `AID` | bigint | TaskAID,任务主键 |
+| `订单编号` | int | 生产订单号 |
+| `订单行号` | int | SAP/MES 行号 |
+| `订单拆分内码` | int | 拆分内码 |
+| `物料编码` | nvarchar(500) | 任务物料编码 |
+| `物料名称` | nvarchar(500) | 任务物料名称 |
+| `工序名称` | nvarchar(50) | 工序 |
+| `顺序编号` | int | 工序顺序 |
+| `总工时` | decimal(16,2) | 任务总工时,现有字段 |
+| `指派对象编号` | nvarchar(50) | 指派对象编号 |
+| `指派对象` | nvarchar(50) | 设备/工位/外协/装配等 |
+| `指派数量` | decimal(16,2) | 指派数量 |
+| `计划开始时间` | datetime | 任务计划开始 |
+| `计划完成时间` | datetime | 任务计划完成 |
+| `派工时间` | datetime | 现有派工时间 |
+| `任务状态` | int | 任务状态 |
+| `开工时间` | datetime | 开工时间 |
+| `完工时间` | datetime | 完工时间 |
+| `完成数量` | int | 完成数 |
+| `合格数量` | int | 合格数 |
+| `不合格数量` | int | 不合格数 |
+| `让步放行数量` | int | 让步放行数 |
+| `报废数量` | int | 报废数 |
+| `外协通知` | int | 外协通知状态 |
+| `外协备注` | nvarchar(500) | 外协备注 |
+| `外协分组` | nvarchar(50) | 外协分组 |
+
+#### 生产订单视图
+
+视图:`View_生产订单_MES`
+
+说明:目前多数计划、排产、生产任务查询均基于该视图。新增报表优先从该视图读取生产订单和任务基础信息。
+
+#### 生产操作记录
+
+表:`YL_加工中心_操作记录表`
+
+用途:
+
+- 现场开工、暂停、报工、辅助开始/结束、异常等操作记录。
+- 及时检验起算时间。
+- 历史工时查询。
+- 班次产量表。
+- 当前对应人员和当前对应内容。
+
+现有字段从视图和过程可见:
+
+| 字段 | 说明 |
+|---|---|
+| `TaskAID` | 关联生产任务 |
+| `操作类别` | 开始加工、报工、辅助结束、异常等 |
+| `操作人` | 人员 |
+| `工位编号` / `工位名称` | 工位/设备 |
+| `开始时间` / `结束时间` | 操作时间段 |
+| `工时` | 秒或分钟,现有过程按 `工时 > 0` 汇总 |
+| `操作数值` | 数量或工时数值 |
+| `操作内容` | 工作内容 |
+| `操作描述` / `异常说明` | 说明 |
+
+#### 质检记录
+
+表:`YL_质量检验_质检记录`
+
+用途:
+
+- 及时检验报表。
+- 生产日报-送检。
+- 让步接收记录。
+- 外协任务检验字段补充。
+- 产品合格率。
+
+关键字段:
+
+| 字段 | 说明 |
+|---|---|
+| `QT_AID` | 质检主键 |
+| `TaskAID` | 关联生产任务 |
+| `计划号` | 生产订单 |
+| `零件编码` / `零件名称` | 物料 |
+| `工序名称` | 工序 |
+| `质检类型` / `检测类型编号` | 首检、巡检、序检、收检、终检等 |
+| `检验数量` | 检验数 |
+| `合格数` | 合格数 |
+| `不合格数` | 不合格数 |
+| `检测人` | 检验人 |
+| `检测时间` | 检验时间 |
+| `检测说明` | 说明 |
+
+#### 设备采集相关表
+
+| 表 | 说明 |
+|---|---|
+| `CaChe_Var` | 设备变量当前缓存 |
+| `历史_设备数据_定时` | 设备历史采集点位数据 |
+| `历史_工位设备状态每分钟记录` | 工位状态每分钟快照 |
+| `YL_设备管理_设备台账` | 设备台账 |
+| `YL_设备管理_工时记录` | 设备循环时间采集记录 |
+
+## 2. 总体开发原则
+
+### 2.1 页面开发原则
+
+1. 保持现有 Element UI 风格:顶部筛选区、中间按钮区、下方表格、弹窗明细。
+2. 报表页面只做查询、导出、打印、钻取,不承担基础主数据维护。
+3. 待办类页面可以做状态确认,例如手续确认。
+4. 能扩展现有页面的,不新建孤立维护页面。
+5. 所有新增页面必须同步菜单、按钮权限、路由组件、操作日志。
+6. 所有新增表格都要支持分页,避免一次性查全量。
+7. 时间筛选全部统一为闭区间:开始日 `00:00:00` 到结束日 `23:59:59`。
+8. 导出字段必须与页面字段一致。
+
+### 2.2 数据库开发原则
+
+1. 优先复用现有表和视图。
+2. 新增字段优先加在业务主表或扩展表,不能只存在报表 SQL 中。
+3. 人工维护字段必须有维护人、维护时间、原值、新值或日志。
+4. SAP 来源字段必须有同步时间和同步状态。
+5. 设备采集数据必须先落原始数据,再做汇总,不直接覆盖业务结果。
+6. 报表公共口径优先做视图或公共存储过程,不允许每个报表各写一套算法。
+7. 看板类过程必须可传统计日期;实时看板默认 `GETDATE()`。
+8. 写操作过程必须返回 `result`、`message`,批量操作要返回每行结果。
+
+### 2.3 命名规范
+
+新增表建议命名:
+
+```text
+业务域_模块_对象
+例:生产管理_装配已排产需求_维护字段
+例:生产管理_设计任务
+例:生产管理_编程任务
+```
+
+新增存储过程建议命名:
+
+```text
+业务域_页面_动作
+例:生产管理_装配已排产需求_查询
+例:生产管理_装配已排产需求_维护字段保存
+例:质量管理_及时检验报表_查询
+```
+
+如复用原有 MES 前缀,也可以使用:
+
+```text
+MES_AssemblyPlannedDemand_Query
+MES_AssemblyPlannedDemand_SaveManualFields
+```
+
+本项目已有大量中文过程名,建议新增需求优先用中文业务名,便于现场维护。
+
+## 3. 通用新增基础表
+
+以下表建议作为多需求共用基础能力,避免每个需求重复建字段。
+
+### 3.1 SAP 调用日志表
+
+表名:`系统集成_SAP调用日志`
+
+用途:
+
+- 批量关闭 SAP 订单。
+- 外协采购收货单号追踪。
+- 发货通知、缺件明细、派工时间、合同交货期同步。
+- 后续 SAP 失败重试和对账。
+
+建议 SQL:
+
+```sql
+CREATE TABLE dbo.系统集成_SAP调用日志 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 业务类型 nvarchar(50) NOT NULL,
+ 业务主键 nvarchar(100) NULL,
+ 生产订单 int NULL,
+ TaskAID bigint NULL,
+ SAP对象类型 nvarchar(50) NULL,
+ SAP单据号 nvarchar(100) NULL,
+ 请求地址 nvarchar(500) NULL,
+ 请求方法 nvarchar(20) NULL,
+ 请求内容 nvarchar(max) NULL,
+ 返回内容 nvarchar(max) NULL,
+ 执行结果 nvarchar(20) NOT NULL,
+ 失败原因 nvarchar(max) NULL,
+ 请求人 nvarchar(50) NULL,
+ 请求时间 datetime NOT NULL DEFAULT GETDATE(),
+ 完成时间 datetime NULL,
+ 是否可重试 bit NOT NULL DEFAULT 0,
+ 重试次数 int NOT NULL DEFAULT 0
+);
+```
+
+索引:
+
+```sql
+CREATE INDEX IX_系统集成_SAP调用日志_生产订单 ON dbo.系统集成_SAP调用日志(生产订单);
+CREATE INDEX IX_系统集成_SAP调用日志_业务类型_时间 ON dbo.系统集成_SAP调用日志(业务类型, 请求时间);
+```
+
+### 3.2 业务字段维护日志表
+
+表名:`系统管理_业务字段维护日志`
+
+用途:
+
+- 装配完成时间人工修改。
+- 钣金预达、钣金到达、泵预达、泵到达维护。
+- 预计总工时维护。
+- 预计可执行时间维护。
+- 手续确认等人工确认字段。
+
+建议 SQL:
+
+```sql
+CREATE TABLE dbo.系统管理_业务字段维护日志 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 业务模块 nvarchar(100) NOT NULL,
+ 业务表名 nvarchar(100) NULL,
+ 业务主键 nvarchar(100) NOT NULL,
+ 字段名 nvarchar(100) NOT NULL,
+ 原值 nvarchar(max) NULL,
+ 新值 nvarchar(max) NULL,
+ 维护说明 nvarchar(500) NULL,
+ 维护人 nvarchar(50) NOT NULL,
+ 维护时间 datetime NOT NULL DEFAULT GETDATE()
+);
+```
+
+索引:
+
+```sql
+CREATE INDEX IX_系统管理_业务字段维护日志_业务 ON dbo.系统管理_业务字段维护日志(业务模块, 业务主键);
+```
+
+### 3.3 统一异常关联表
+
+现有已有异常管控页面和异常工时逻辑,但本轮要求所有报工页面选中任务直接报异常,且审批人多选。建议在不推翻现有异常管控的前提下增加关联表。
+
+表名:`生产管理_异常管控_任务异常`
+
+```sql
+CREATE TABLE dbo.生产管理_异常管控_任务异常 (
+ 异常AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 异常编号 nvarchar(50) NOT NULL,
+ 来源模块 nvarchar(50) NOT NULL,
+ 生产订单 int NULL,
+ TaskAID bigint NULL,
+ 工序名称 nvarchar(50) NULL,
+ 指派对象 nvarchar(50) NULL,
+ 异常类型 nvarchar(50) NULL,
+ 异常说明 nvarchar(1000) NULL,
+ 附件ID nvarchar(500) NULL,
+ 状态 nvarchar(20) NOT NULL DEFAULT N'待处理',
+ 发起人 nvarchar(50) NOT NULL,
+ 发起时间 datetime NOT NULL DEFAULT GETDATE(),
+ 实际处理人 nvarchar(50) NULL,
+ 处理时间 datetime NULL,
+ 处理意见 nvarchar(1000) NULL,
+ 是否关闭 bit NOT NULL DEFAULT 0
+);
+
+CREATE TABLE dbo.生产管理_异常管控_审批人 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 异常AID bigint NOT NULL,
+ 审批人编号 int NULL,
+ 审批人姓名 nvarchar(50) NOT NULL,
+ 是否已处理 bit NOT NULL DEFAULT 0,
+ 处理时间 datetime NULL
+);
+```
+
+索引:
+
+```sql
+CREATE INDEX IX_生产管理_异常管控_任务异常_TaskAID ON dbo.生产管理_异常管控_任务异常(TaskAID, 是否关闭);
+CREATE INDEX IX_生产管理_异常管控_审批人_人员 ON dbo.生产管理_异常管控_审批人(审批人姓名, 是否已处理);
+```
+
+## 4. 需求 1:及时开工报表
+
+### 4.1 现有页面
+
+页面:`src/views/AnomalousManagement/PunctualWork/index.vue`
+
+现状:
+
+- 当前按指派对象过滤偏向反选逻辑。
+- 需求要求增加正选页面或正选模式。
+- 真实目标不是重做及时开工算法,只改筛选方式。
+
+### 4.2 页面方案
+
+在查询区新增:
+
+| 控件 | 类型 | 默认值 | 说明 |
+|---|---|---|---|
+| 指派对象 | `el-select multiple` | 全部 | 复用现有设备/工位下拉 |
+| 查询模式 | `el-radio-group` 或 `el-select` | 排除 | 包含、排除 |
+
+页面展示:
+
+```text
+指派对象:[多选] 查询模式:[包含/排除] [查询] [重置] [导出]
+```
+
+交互规则:
+
+- 查询模式=包含:只查选中指派对象。
+- 查询模式=排除:排除选中指派对象。
+- 未选择指派对象:不增加指派对象条件。
+- 默认保留原逻辑,避免影响现场使用习惯。
+
+### 4.3 存储过程
+
+建议扩展现有及时开工过程,增加参数:
+
+```sql
+@指派对象 nvarchar(max) = N'',
+@指派对象查询模式 nvarchar(10) = N'排除'
+```
+
+过滤逻辑:
+
+```sql
+IF ISNULL(@指派对象, '') <> ''
+BEGIN
+ IF @指派对象查询模式 = N'包含'
+ SET @where = @where + N' AND 指派对象 IN (SELECT value FROM STRING_SPLIT(@指派对象, '',''))'
+ ELSE
+ SET @where = @where + N' AND 指派对象 NOT IN (SELECT value FROM STRING_SPLIT(@指派对象, '',''))'
+END
+```
+
+注意:
+
+- 现有很多过程使用字符串拼接动态 SQL,后续应尽量用 `sp_executesql` 参数化,降低注入风险。
+- 如果 `.ashx` 传参不支持 `nvarchar(max)`,可用 `nvarchar(4000)`。
+
+### 4.4 不需要新增表
+
+该需求只改页面和存储过程参数,不新增表。
+
+## 5. 需求 2:机加件排产-表单
+
+包含两件事:
+
+1. 已排产页面日期筛选优化。
+2. 新增历史工时查询,总工时列和明细弹窗。
+
+### 5.1 现有页面
+
+页面:`src/views/PlanManagement/SelfMakePlandone/index.vue`
+
+现有查询过程:`MES_OrderPlanned_Query`
+
+现有过程参数:
+
+```sql
+@计划开始1 nvarchar(50)='',
+@计划开始2 nvarchar(50)='',
+@计划完成1 nvarchar(50)='',
+@计划完成2 nvarchar(50)=''
+```
+
+### 5.2 页面日期筛选
+
+改造前:
+
+- 计划开始时间为时间段。
+- 计划完成时间为时间段。
+
+改造后:
+
+| 字段 | 控件 | 参数传递 |
+|---|---|---|
+| 计划开始日期 | `el-date-picker type="date"` | 传 `计划开始日期` |
+| 计划完成日期 | `el-date-picker type="date"` | 传 `计划完成日期` |
+
+前端传参方式:
+
+```js
+if (this.query.计划开始日期) {
+ param.push(['计划开始日期', this.query.计划开始日期])
+}
+if (this.query.计划完成日期) {
+ param.push(['计划完成日期', this.query.计划完成日期])
+}
+```
+
+兼容方案:
+
+- 为减少影响,可以继续传旧参数:
+ - `计划开始1 = yyyy-MM-dd 00:00:00`
+ - `计划开始2 = yyyy-MM-dd 23:59:59`
+ - `计划完成1 = yyyy-MM-dd 00:00:00`
+ - `计划完成2 = yyyy-MM-dd 23:59:59`
+- 存储过程也可新增单日期参数,旧参数保留。
+
+### 5.3 存储过程改造
+
+建议扩展 `MES_OrderPlanned_Query`:
+
+```sql
+ALTER PROCEDURE dbo.MES_OrderPlanned_Query
+ @订单号 int = 0,
+ ...
+ @计划开始日期 nvarchar(50) = '',
+ @计划完成日期 nvarchar(50) = '',
+ @指派对象查询模式 nvarchar(10) = N'包含'
+AS
+BEGIN
+ ...
+ IF (@计划开始日期 <> '')
+ SET @selectStrWhere = @selectStrWhere +
+ ' AND [计划开始时间] >= ''' + @计划开始日期 + ' 00:00:00''
+ AND [计划开始时间] < DATEADD(DAY, 1, ''' + @计划开始日期 + ''')'
+
+ IF (@计划完成日期 <> '')
+ SET @selectStrWhere = @selectStrWhere +
+ ' AND [计划完成时间] >= ''' + @计划完成日期 + ' 00:00:00''
+ AND [计划完成时间] < DATEADD(DAY, 1, ''' + @计划完成日期 + ''')'
+END
+```
+
+更稳妥写法是把日期转成 `date` 类型后参数化。
+
+### 5.4 总工时列
+
+表格新增列:
+
+| 列名 | 字段 | 交互 |
+|---|---|---|
+| 总工时 | `历史总工时` | 蓝色可点击,点击打开明细 |
+
+总工时口径:
+
+- 数据来源:`YL_加工中心_操作记录表`
+- 过滤:`TaskAID = 当前任务 TaskAID`,`工时 > 0`
+- 默认包含:报工、辅助结束、加班等正向工时。
+- 默认排除:异常负工时、未结束记录。
+- 单位:建议统一显示小时,保留 2 位小数。
+
+### 5.5 新增存储过程
+
+#### 5.5.1 总工时汇总
+
+过程名:`生产管理_机加已排产_历史工时汇总`
+
+参数:
+
+| 参数 | 类型 | 说明 |
+|---|---|---|
+| `@TaskAID` | bigint | 可选,单任务 |
+| `@订单号` | int | 可选,订单 |
+| `@开始日期` | date | 可选 |
+| `@结束日期` | date | 可选 |
+
+返回字段:
+
+| 字段 | 说明 |
+|---|---|
+| `TaskAID` | 任务 |
+| `订单编号` | 订单 |
+| `工序名称` | 工序 |
+| `总工时分钟` | 分钟 |
+| `总工时小时` | 小时 |
+
+SQL 示例:
+
+```sql
+CREATE PROCEDURE dbo.生产管理_机加已排产_历史工时汇总
+ @TaskAID bigint = NULL,
+ @订单号 int = NULL,
+ @开始日期 date = NULL,
+ @结束日期 date = NULL
+AS
+BEGIN
+ SET NOCOUNT ON;
+
+ SELECT
+ o.TaskAID,
+ MAX(t.订单编号) AS 订单编号,
+ MAX(o.工序名称) AS 工序名称,
+ SUM(ISNULL(o.工时, 0)) AS 总工时分钟,
+ CAST(SUM(ISNULL(o.工时, 0)) / 60.0 AS decimal(18,2)) AS 总工时小时
+ FROM dbo.YL_加工中心_操作记录表 o
+ LEFT JOIN dbo.MES_接口_生产计划_生产任务 t ON o.TaskAID = t.AID
+ WHERE ISNULL(o.工时, 0) > 0
+ AND (@TaskAID IS NULL OR o.TaskAID = @TaskAID)
+ AND (@订单号 IS NULL OR t.订单编号 = @订单号)
+ AND (@开始日期 IS NULL OR o.开始时间 >= @开始日期)
+ AND (@结束日期 IS NULL OR o.开始时间 < DATEADD(DAY, 1, @结束日期))
+ GROUP BY o.TaskAID;
+END
+```
+
+#### 5.5.2 历史工时明细
+
+过程名:`生产管理_机加已排产_历史工时明细`
+
+参数:
+
+| 参数 | 类型 | 说明 |
+|---|---|---|
+| `@TaskAID` | bigint | 必填 |
+
+返回字段:
+
+| 字段 | 说明 |
+|---|---|
+| `设备名称` | 工位名称 |
+| `日期` | 开始时间日期 |
+| `生产订单` | 订单编号 |
+| `物料编码` | 物料编码 |
+| `物料名称` | 物料名称 |
+| `工序名称` | 工序 |
+| `操作人` | 操作人 |
+| `开始时间` | 开始 |
+| `结束时间` | 结束 |
+| `工时分钟` | 分钟 |
+| `工时小时` | 小时 |
+| `设备日期小计小时` | 前端可展示分组合计 |
+
+SQL 示例:
+
+```sql
+CREATE PROCEDURE dbo.生产管理_机加已排产_历史工时明细
+ @TaskAID bigint
+AS
+BEGIN
+ SET NOCOUNT ON;
+
+ SELECT
+ o.工位名称 AS 设备名称,
+ CONVERT(date, o.开始时间) AS 日期,
+ t.订单编号 AS 生产订单,
+ t.物料编码,
+ t.物料名称,
+ o.工序名称,
+ o.操作人,
+ o.开始时间,
+ o.结束时间,
+ ISNULL(o.工时, 0) AS 工时分钟,
+ CAST(ISNULL(o.工时, 0) / 60.0 AS decimal(18,2)) AS 工时小时,
+ CAST(SUM(ISNULL(o.工时, 0)) OVER (PARTITION BY o.工位名称, CONVERT(date, o.开始时间)) / 60.0 AS decimal(18,2)) AS 设备日期小计小时
+ FROM dbo.YL_加工中心_操作记录表 o
+ LEFT JOIN dbo.MES_接口_生产计划_生产任务 t ON o.TaskAID = t.AID
+ WHERE o.TaskAID = @TaskAID
+ AND ISNULL(o.工时, 0) > 0
+ ORDER BY o.工位名称, CONVERT(date, o.开始时间), o.开始时间;
+END
+```
+
+### 5.6 前端弹窗
+
+弹窗名:历史工时明细
+
+结构:
+
+```text
+设备:CNC-01 小计:12.50小时
+ 日期:2026-05-28 小计:8.00小时
+ 任务明细表
+ 日期:2026-05-29 小计:4.50小时
+ 任务明细表
+```
+
+Element UI 实现建议:
+
+- 用 `el-dialog`。
+- 明细用 `el-table` 展示,按设备、日期排序。
+- 如需要折叠效果,使用前端 computed 分组。
+- 总工时列点击时调用 `生产管理_机加已排产_历史工时明细`。
+
+### 5.7 不建议新增表
+
+本需求只需要新增两个查询过程,不需要新增表。
+
+## 6. 需求 3:装配已排产需求
+
+### 6.1 定位
+
+这是本轮最大的独立子项目,不是简单报表。
+
+页面目标:
+
+- 基于装配已排产数据。
+- 整合 SAP 字段、MES 报工、工时、异常、齐套时间、装配完成时间、日计划。
+- 支持部分人工维护字段。
+
+建议页面路径:
+
+```text
+src/views/ProductionManagement/AssemblyPlannedDemand/index.vue
+```
+
+菜单建议:
+
+```text
+生产管理 -> 装配管理 -> 装配已排产需求
+```
+
+### 6.2 页面布局
+
+页面结构:
+
+```text
+查询区:
+系统分类[全部/系统/非系统] 合同号 生产订单 物料编码/名称
+发料齐套时间[起止] 装配完成时间[起止] 状态[全部]
+[查询] [重置] [导出] [日计划] [打印]
+
+主表:
+生产订单 合同号 物料编码 物料名称 系统/非系统 派工时间 合同交货期
+发料齐套时间 装配完成时间 当前对应人员 当前对应内容
+生产订单累计工时 合同工时汇总 合同进度参考 预计总工时 预计剩余工时
+钣金预达 钣金到达 泵 生产异常 历史工序说明 操作
+
+操作列:
+[维护] [工时明细] [异常明细] [历史工序] [日计划]
+```
+
+### 6.3 字段口径
+
+| 字段 | 来源 | 维护方式 | 说明 |
+|---|---|---|---|
+| 生产订单 | `MES_接口_生产计划.订单编号` | 只读 | 主键 |
+| 合同号 | `MES_接口_生产计划.合同号` | 只读 | 合同维度 |
+| 物料编码 | `MES_接口_生产计划.物料编号` | 只读 | 主物料 |
+| 物料名称 | `MES_接口_生产计划.物料描述` | 只读 | 主物料名称 |
+| 系统/非系统 | 规则计算 | 只读 | 高压冷却为系统,阀类非高压冷却为非系统 |
+| 派工时间 | SAP 或任务表 `派工时间` | 只读 | 以 SAP 提供为准;现任务表已有派工时间 |
+| 合同交货期 | SAP | 只读 | 需同步字段 |
+| 发料齐套时间 | SAP/MES 齐套字段 | 可必要维护 | 优先使用 `齐套时间` 或后续 SAP 字段 |
+| 装配完成时间 | 发料齐套时间 + 3 天 | 可维护 | 人工修改需记录日志 |
+| 钣金预达 | 人工维护 | 可维护 | 过程字段 |
+| 钣金到达 | 人工维护 | 可维护 | 过程字段 |
+| 泵 | 人工维护 | 可维护 | 建议拆成泵预达/泵到达或说明 |
+| 当前对应人员 | 操作记录 | 只读 | 开始未结束的人,多个合并 |
+| 当前对应内容 | 操作记录/新增字段 | 只读/维护 | 装配开始时输入的工作内容 |
+| 生产订单累计工时 | 工时汇总 | 只读 | 按生产订单分类汇总装配、质量、设计 |
+| 历史工序说明 | 报工结束说明 | 只读 | 多条合并或明细弹窗 |
+| 预计总工时 | 人工维护 | 可维护 | 订单级 |
+| 合同工时汇总 | 工时汇总 | 只读 | 合同下所有订单 |
+| 合同进度参考 | 计算 | 只读 | 累计工时 / 预计总工时 |
+| 预计剩余工时 | 计算 | 只读 | 预计总工时 - 累计工时 |
+| 生产异常 | 异常表 | 只读 | 未关闭异常数和明细 |
+
+### 6.4 建议新增字段
+
+优先不要直接无限扩展主表。建议建订单扩展表。
+
+表名:`生产管理_装配已排产需求_扩展`
+
+```sql
+CREATE TABLE dbo.生产管理_装配已排产需求_扩展 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 订单编号 int NOT NULL,
+ 发料齐套时间 datetime NULL,
+ 发料齐套时间来源 nvarchar(50) NULL,
+ 装配完成时间 datetime NULL,
+ 装配完成时间是否人工修改 bit NOT NULL DEFAULT 0,
+ SAP派工时间 datetime NULL,
+ 合同交货期 datetime NULL,
+ 钣金预达 datetime NULL,
+ 钣金到达 datetime NULL,
+ 泵预达 datetime NULL,
+ 泵到达 datetime NULL,
+ 泵说明 nvarchar(500) NULL,
+ 预计总工时 decimal(18,2) NULL,
+ 备注 nvarchar(1000) NULL,
+ 最后维护人 nvarchar(50) NULL,
+ 最后维护时间 datetime NULL,
+ 创建时间 datetime NOT NULL DEFAULT GETDATE()
+);
+
+CREATE UNIQUE INDEX UX_生产管理_装配已排产需求_扩展_订单编号
+ON dbo.生产管理_装配已排产需求_扩展(订单编号);
+```
+
+### 6.5 当前对应内容
+
+当前系统 `MES_人员工时记录_开始` 和装配中心已有人员工时逻辑。需求要求“装配开始时输入工作内容”。如果现有开始记录没有工作内容字段,应补字段到对应记录表。由于实际装配人员工时表名需从存储过程确认,建议先通过 `MES_人员工时记录_开始` 的落表对象确认。
+
+建议新增字段:
+
+```sql
+ALTER TABLE dbo.YL_加工中心_操作记录表 ADD 当前工作内容 nvarchar(1000) NULL;
+```
+
+如果装配人员工时独立表存在,应加在装配人员工时表,而不是操作记录表。
+
+前端改造:
+
+- `AssemblyCenter` 开始装配弹窗增加 `工作内容`。
+- 调用 `MES_人员工时记录_开始` 时增加参数 `@工作内容`。
+- 未结束记录的人员和工作内容在装配已排产需求页展示。
+
+### 6.6 工时汇总视图
+
+建议新增统一工时视图,供装配已排产需求、合同工时、工时报表复用。
+
+视图名:`View_统一工时明细`
+
+字段:
+
+| 字段 | 说明 |
+|---|---|
+| `来源模块` | 机加、装配、质量、设计、编程、设备 |
+| `工时类型` | 装配、质量、设计等 |
+| `生产订单` | 订单 |
+| `合同号` | 合同 |
+| `TaskAID` | 任务,可空 |
+| `工序名称` | 工序 |
+| `操作人` | 人员 |
+| `开始时间` | 开始 |
+| `结束时间` | 结束 |
+| `工时分钟` | 分钟 |
+| `工时小时` | 小时 |
+
+第一阶段可只接入:
+
+- 装配:`MES_人员工时记录_*` 对应表或 `YL_加工中心_操作记录表`
+- 质量:质检中心人员工时记录或操作记录
+- 设计:设计任务报工表,等设计模块完成后接入
+
+### 6.7 查询存储过程
+
+过程名:`生产管理_装配已排产需求_查询`
+
+参数:
+
+| 参数 | 类型 |
+|---|---|
+| `@系统分类` | nvarchar(20),全部/系统/非系统 |
+| `@合同号` | nvarchar(50) |
+| `@订单编号` | int |
+| `@物料` | nvarchar(100) |
+| `@发料齐套开始` | date |
+| `@发料齐套结束` | date |
+| `@装配完成开始` | date |
+| `@装配完成结束` | date |
+| `@状态` | nvarchar(50) |
+
+核心 SQL 口径:
+
+```sql
+SELECT
+ p.订单编号,
+ p.合同号,
+ p.物料编号,
+ p.物料描述,
+ CASE
+ WHEN p.物料描述 LIKE N'%高压冷却%' THEN N'系统'
+ ELSE N'非系统'
+ END AS 系统分类,
+ ISNULL(e.SAP派工时间, MAX(t.派工时间)) AS 派工时间,
+ e.合同交货期,
+ COALESCE(e.发料齐套时间, p.齐套时间, p.预计齐套时间) AS 发料齐套时间,
+ COALESCE(e.装配完成时间, DATEADD(DAY, 3, COALESCE(e.发料齐套时间, p.齐套时间, p.预计齐套时间))) AS 装配完成时间,
+ e.钣金预达,
+ e.钣金到达,
+ e.泵预达,
+ e.泵到达,
+ e.泵说明,
+ e.预计总工时,
+ wh.生产订单累计工时,
+ chw.合同工时汇总,
+ CASE WHEN ISNULL(e.预计总工时,0) = 0 THEN NULL
+ ELSE CAST(wh.生产订单累计工时 / e.预计总工时 AS decimal(18,4))
+ END AS 合同进度参考,
+ ISNULL(e.预计总工时,0) - ISNULL(wh.生产订单累计工时,0) AS 预计剩余工时,
+ cur.当前对应人员,
+ cur.当前对应内容,
+ abn.未关闭异常数
+FROM dbo.MES_接口_生产计划 p
+LEFT JOIN dbo.MES_接口_生产计划_生产任务 t ON p.订单编号 = t.订单编号
+LEFT JOIN dbo.生产管理_装配已排产需求_扩展 e ON p.订单编号 = e.订单编号
+LEFT JOIN (...) wh ON p.订单编号 = wh.生产订单
+LEFT JOIN (...) chw ON p.合同号 = chw.合同号
+LEFT JOIN (...) cur ON p.订单编号 = cur.生产订单
+LEFT JOIN (...) abn ON p.订单编号 = abn.生产订单
+WHERE p.自制件属性 LIKE N'%装配%'
+GROUP BY ...
+```
+
+### 6.8 保存维护字段过程
+
+过程名:`生产管理_装配已排产需求_维护字段保存`
+
+参数:
+
+- `@订单编号 int`
+- `@发料齐套时间 datetime = NULL`
+- `@装配完成时间 datetime = NULL`
+- `@钣金预达 datetime = NULL`
+- `@钣金到达 datetime = NULL`
+- `@泵预达 datetime = NULL`
+- `@泵到达 datetime = NULL`
+- `@泵说明 nvarchar(500) = NULL`
+- `@预计总工时 decimal(18,2) = NULL`
+- `@备注 nvarchar(1000) = NULL`
+- `@维护人 nvarchar(50)`
+
+逻辑:
+
+1. 如果扩展表不存在订单记录,插入。
+2. 如果存在,更新。
+3. 对每个字段写 `系统管理_业务字段维护日志`。
+4. 返回 `result=1`。
+
+### 6.9 工时明细过程
+
+过程名:`生产管理_装配已排产需求_工时明细`
+
+参数:
+
+- `@订单编号 int`
+- `@合同号 nvarchar(50)`
+- `@工时类型 nvarchar(50)`,全部/装配/质量/设计
+
+返回:
+
+- 来源模块
+- 工时类型
+- 生产订单
+- 合同号
+- TaskAID
+- 工序名称
+- 操作人
+- 开始时间
+- 结束时间
+- 工时小时
+- 工作内容
+- 结束说明
+
+### 6.10 异常明细过程
+
+过程名:`生产管理_装配已排产需求_异常明细`
+
+数据来源:
+
+- 优先:`生产管理_异常管控_任务异常`
+- 兼容:现有异常管控表或 `YL_加工中心_操作记录表` 中 `操作类别='异常'`
+
+返回:
+
+- 异常编号
+- 异常类型
+- 异常说明
+- 发起人
+- 发起时间
+- 状态
+- 处理人
+- 处理时间
+
+### 6.11 日计划
+
+页面按钮:`日计划`
+
+规则:
+
+- 按装配完成时间分组。
+- 未完成、未入库、未关闭的任务进入计划。
+- 支持导出、打印。
+
+过程名:`生产管理_装配已排产需求_日计划`
+
+参数:
+
+- `@计划日期 date`
+- `@系统分类 nvarchar(20)`
+
+返回字段:
+
+- 计划日期
+- 生产订单
+- 合同号
+- 物料编码
+- 物料名称
+- 装配完成时间
+- 当前对应人员
+- 当前对应内容
+- 异常状态
+- 备注
+
+打印:
+
+- 使用 `vue-print-nb`。
+- 打印区域为日计划表格。
+- 标题:`装配日计划 YYYY-MM-DD`。
+
+## 7. 需求 4:生产日报-送检
+
+### 7.1 页面定位
+
+建议新增页面:
+
+```text
+src/views/ReportManagement/InspectionDaily/index.vue
+```
+
+如果项目没有 `ReportManagement`,可放在:
+
+```text
+src/views/ProductionManagement/InspectionDaily/index.vue
+```
+
+菜单建议:
+
+```text
+报表中心 -> 生产日报 -> 生产日报-送检
+```
+
+页面采用两个页签:
+
+- 机加送检日报
+- 装配送检日报
+
+### 7.2 页面布局
+
+```text
+页签:[机加送检日报] [装配送检日报]
+
+查询区:
+日期[单日/日期范围] 部门[全部] 生产订单 物料 工序 状态
+[查询] [重置] [导出]
+
+汇总区:
+送检数量 已检数量 合格数量 不合格数量 未检数量 及时率
+
+表格:
+日期 部门 生产订单 物料编码 物料名称 工序名称 送检数量
+已检数量 合格数量 不合格数量 未检数量 送检时间 检验时间 状态
+```
+
+### 7.3 需要确认的数据
+
+| 数据 | 需要确认 |
+|---|---|
+| 送检记录来源 | 装配送检是否来自 `MES_ProductTask_SendInspection` 写入的记录,机加送检是否来自质检任务或操作记录 |
+| 部门归属 | 按操作人部门、工位部门、任务部门还是物料/订单类型 |
+| 送检数量 | 取操作记录 `操作数值`,还是质检记录 `检验数量` |
+| 已检数量 | 质检记录合计 |
+| 状态 | 未检、部分检验、已检、超期 |
+
+### 7.4 建议视图
+
+视图名:`View_生产日报_送检明细`
+
+字段:
+
+| 字段 | 来源 |
+|---|---|
+| 日期 | 送检时间日期 |
+| 送检类型 | 机加/装配 |
+| 部门 | 人员部门或工位部门 |
+| 生产订单 | 任务订单 |
+| TaskAID | 任务 |
+| 物料编码 | 任务物料 |
+| 物料名称 | 任务物料 |
+| 工序名称 | 任务工序 |
+| 指派对象 | 任务指派对象 |
+| 送检数量 | 送检记录 |
+| 已检数量 | 质检记录 |
+| 合格数量 | 质检记录 |
+| 不合格数量 | 质检记录 |
+| 未检数量 | 送检数量 - 已检数量 |
+| 送检时间 | 送检记录时间 |
+| 检验时间 | 最后检验时间 |
+| 状态 | 计算 |
+
+### 7.5 存储过程
+
+过程名:`生产日报_送检_查询`
+
+参数:
+
+| 参数 | 类型 |
+|---|---|
+| `@送检类型` | nvarchar(20),机加/装配 |
+| `@开始日期` | date |
+| `@结束日期` | date |
+| `@部门` | nvarchar(50) |
+| `@生产订单` | int |
+| `@物料` | nvarchar(100) |
+| `@状态` | nvarchar(20) |
+
+核心逻辑:
+
+```sql
+SELECT
+ 日期,
+ 部门,
+ 生产订单,
+ 物料编码,
+ 物料名称,
+ 工序名称,
+ SUM(送检数量) AS 送检数量,
+ SUM(已检数量) AS 已检数量,
+ SUM(合格数量) AS 合格数量,
+ SUM(不合格数量) AS 不合格数量,
+ SUM(未检数量) AS 未检数量,
+ MIN(送检时间) AS 最早送检时间,
+ MAX(检验时间) AS 最后检验时间,
+ CASE
+ WHEN SUM(已检数量) = 0 THEN N'未检'
+ WHEN SUM(未检数量) > 0 THEN N'部分检验'
+ ELSE N'已检'
+ END AS 状态
+FROM dbo.View_生产日报_送检明细
+WHERE 日期 >= @开始日期
+ AND 日期 < DATEADD(DAY, 1, @结束日期)
+ AND (@送检类型 = N'全部' OR 送检类型 = @送检类型)
+GROUP BY 日期, 部门, 生产订单, 物料编码, 物料名称, 工序名称
+ORDER BY 日期, 部门, 生产订单;
+```
+
+### 7.6 是否新增表
+
+第一阶段不新增表,使用视图 + 存储过程。
+
+如果客户要求日报定版,需要新增快照表:
+
+```sql
+CREATE TABLE dbo.生产日报_送检_日报快照 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 统计日期 date NOT NULL,
+ 送检类型 nvarchar(20) NOT NULL,
+ 部门 nvarchar(50) NULL,
+ 生产订单 int NULL,
+ TaskAID bigint NULL,
+ 物料编码 nvarchar(100) NULL,
+ 物料名称 nvarchar(500) NULL,
+ 工序名称 nvarchar(100) NULL,
+ 送检数量 decimal(18,2) NULL,
+ 已检数量 decimal(18,2) NULL,
+ 合格数量 decimal(18,2) NULL,
+ 不合格数量 decimal(18,2) NULL,
+ 未检数量 decimal(18,2) NULL,
+ 状态 nvarchar(20) NULL,
+ 生成时间 datetime NOT NULL DEFAULT GETDATE(),
+ 生成人 nvarchar(50) NULL
+);
+```
+
+## 8. 需求 5:及时齐套跟踪结果报表
+
+### 8.1 页面定位
+
+新增页面:
+
+```text
+src/views/ProductionManagement/KitTrackingResult/index.vue
+```
+
+菜单建议:
+
+```text
+报表中心 -> 齐套报表 -> 及时齐套跟踪结果报表
+```
+
+### 8.2 页面布局
+
+```text
+查询区:
+主生产订单 合同号 物料 齐套状态 预计可执行时间 SAP同步时间
+[查询] [重置] [导出]
+
+主表:
+主生产订单 合同号 物料编码 物料名称 计划数量 齐套状态
+缺件数 当前进度 预计可执行时间 SAP同步时间 操作
+
+展开明细:
+缺料生产订单 缺料物料编码 缺料物料名称 缺件数量 属性
+对应单据号 当前进度 全序展示 单条预计完成时间 预计可执行时间 操作
+```
+
+### 8.3 数据来源
+
+| 数据 | 来源 | 方式 |
+|---|---|---|
+| 主表订单 | `MES_接口_生产计划` / `View_生产订单_MES` | MES 查询 |
+| 齐套状态 | `齐套`、`齐套时间`、`发料状态`、`拣配状态` | MES 字段 |
+| 缺件明细 | SAP 实时接口 | 点击展开时查 |
+| 主子订单关系 | SAP 或 MES 人工维护 | 建议落 MES 表 |
+| 预计可执行时间 | 人工维护 | 报表内维护,写日志 |
+
+### 8.4 建议新增表
+
+#### 8.4.1 主子订单关系表
+
+表名:`生产管理_齐套跟踪_主子订单关系`
+
+```sql
+CREATE TABLE dbo.生产管理_齐套跟踪_主子订单关系 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 主生产订单 int NOT NULL,
+ 缺料生产订单 int NOT NULL,
+ 缺料物料编码 nvarchar(100) NULL,
+ 缺料物料名称 nvarchar(500) NULL,
+ 关系来源 nvarchar(50) NOT NULL DEFAULT N'人工维护',
+ 是否有效 bit NOT NULL DEFAULT 1,
+ 创建人 nvarchar(50) NULL,
+ 创建时间 datetime NOT NULL DEFAULT GETDATE(),
+ 最后维护人 nvarchar(50) NULL,
+ 最后维护时间 datetime NULL
+);
+
+CREATE INDEX IX_生产管理_齐套跟踪_主子订单关系_主单
+ON dbo.生产管理_齐套跟踪_主子订单关系(主生产订单, 是否有效);
+```
+
+#### 8.4.2 缺件预计信息表
+
+表名:`生产管理_齐套跟踪_缺件预计信息`
+
+```sql
+CREATE TABLE dbo.生产管理_齐套跟踪_缺件预计信息 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 主生产订单 int NOT NULL,
+ 缺料生产订单 int NULL,
+ 缺料物料编码 nvarchar(100) NOT NULL,
+ 对应单据号 nvarchar(100) NULL,
+ 预计可执行时间 datetime NULL,
+ 预计可执行说明 nvarchar(500) NULL,
+ 维护人 nvarchar(50) NULL,
+ 维护时间 datetime NOT NULL DEFAULT GETDATE()
+);
+
+CREATE INDEX IX_生产管理_齐套跟踪_缺件预计信息_主单
+ON dbo.生产管理_齐套跟踪_缺件预计信息(主生产订单);
+```
+
+#### 8.4.3 SAP 缺件缓存表
+
+如果 SAP 实时查询压力大,新增缓存表:
+
+```sql
+CREATE TABLE dbo.系统集成_SAP缺件明细缓存 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 主生产订单 int NOT NULL,
+ 查询时间 datetime NOT NULL DEFAULT GETDATE(),
+ 缺料生产订单 int NULL,
+ 物料编码 nvarchar(100) NULL,
+ 物料名称 nvarchar(500) NULL,
+ 缺件数量 decimal(18,2) NULL,
+ 属性 nvarchar(20) NULL,
+ 对应单据号 nvarchar(100) NULL,
+ 当前进度 nvarchar(500) NULL,
+ 全序展示 nvarchar(max) NULL,
+ 单条预计完成时间 datetime NULL,
+ 原始JSON nvarchar(max) NULL
+);
+```
+
+### 8.5 存储过程
+
+主表查询:
+
+```text
+生产管理_齐套跟踪结果_主表查询
+```
+
+参数:
+
+- `@主生产订单 int`
+- `@合同号 nvarchar(50)`
+- `@物料 nvarchar(100)`
+- `@齐套状态 nvarchar(50)`
+- `@预计开始 date`
+- `@预计结束 date`
+
+展开明细:
+
+```text
+生产管理_齐套跟踪结果_缺件明细查询
+```
+
+说明:
+
+- 第一优先:点击展开时前端调用 SAP 接口,再把结果展示并可缓存。
+- 如果后端 `.ashx` 只能执行 SQL,SAP 查询需要前端现有 SAP/B1 接口或后端新增封装接口。
+- 如果不能实时查 SAP,先读取 `系统集成_SAP缺件明细缓存`。
+
+预计可执行时间保存:
+
+```text
+生产管理_齐套跟踪结果_预计时间保存
+```
+
+参数:
+
+- 主生产订单
+- 缺料生产订单
+- 缺料物料编码
+- 对应单据号
+- 预计可执行时间
+- 预计可执行说明
+- 维护人
+
+同时写 `系统管理_业务字段维护日志`。
+
+## 9. 需求 6:生产计划跟踪
+
+### 9.1 现有页面
+
+页面:
+
+```text
+src/views/ProductionManagement/ProductionPlanTrack/index.vue
+```
+
+现有调用:
+
+```text
+生产管理_生产计划跟踪
+```
+
+页面现状已经有外协状态处理、工序子表、颜色等基础逻辑。
+
+### 9.2 页面改造
+
+新增/调整:
+
+| 内容 | 实现 |
+|---|---|
+| 当前序显示完整工艺路线 | 主表列改为路线组件 |
+| 工序文字按状态变色 | 前端根据状态 class |
+| 进度文案 | `打磨中`、`打磨毕待淬火` |
+| 标准工时 | 表格列 |
+| 订单状态 | 表格列 |
+
+工艺路线显示:
+
+```text
+打磨(张三) -> 淬火(外协) -> 精磨(李四)
+```
+
+颜色规则:
+
+| 状态 | 样式 |
+|---|---|
+| 正在加工 | 蓝色或绿色高亮 |
+| 已完成 | 灰色 |
+| 等待加工 | 默认 |
+| 异常 | 红色 |
+
+### 9.3 后端返回字段
+
+过程 `生产管理_生产计划跟踪` 需要返回每道工序:
+
+| 字段 | 说明 |
+|---|---|
+| 生产订单 |
+| 合同号 |
+| 订单状态 |
+| 物料编码 |
+| 物料名称 |
+| TaskAID |
+| 工序名称 |
+| 顺序编号 |
+| 指派对象 |
+| 任务状态 |
+| 加工状态 |
+| 开工时间 |
+| 完工时间 |
+| 标准工时 |
+| 外协结束时间 |
+
+### 9.4 SQL 逻辑
+
+标准工时来源:
+
+- 优先 `车间生产管理工艺_零件生产工艺_工艺库.标准工时`
+- 通过 `订单编号 + 工艺顺序/订单行号` 关联
+
+订单状态来源:
+
+- `MES_接口_生产计划.订单状态`
+- 如果 SAP 同步了单据状态,也可增加 `单据状态`
+
+过程示例逻辑:
+
+```sql
+SELECT
+ p.订单编号,
+ p.合同号,
+ p.订单状态,
+ t.AID AS TaskAID,
+ t.工序名称,
+ t.顺序编号,
+ t.指派对象,
+ t.任务状态,
+ t.加工状态,
+ t.开工时间,
+ t.完工时间,
+ ISNULL(g.标准工时, 0) AS 标准工时
+FROM dbo.MES_接口_生产计划 p
+JOIN dbo.MES_接口_生产计划_生产任务 t ON p.订单编号 = t.订单编号
+LEFT JOIN dbo.车间生产管理工艺_零件生产工艺_工艺库 g
+ ON t.订单编号 = g.订单编号
+ AND t.订单行号 = g.工艺顺序
+WHERE ...
+ORDER BY p.订单编号, t.顺序编号;
+```
+
+### 9.5 前端算法
+
+将后端明细按订单分组:
+
+```js
+const groups = {}
+rows.forEach(row => {
+ if (!groups[row.订单编号]) groups[row.订单编号] = []
+ groups[row.订单编号].push(row)
+})
+```
+
+每个工序状态:
+
+```js
+function getStepStatus(step) {
+ if (step.任务状态 === 4 || step.完工时间) return 'done'
+ if (step.加工状态 === 2 || step.开工时间) return 'doing'
+ return 'waiting'
+}
+```
+
+进度文案:
+
+```js
+function getProgressText(steps) {
+ const doing = steps.find(x => getStepStatus(x) === 'doing')
+ if (doing) return `${doing.工序名称}中`
+ const lastDoneIndex = findLastDoneIndex(steps)
+ if (lastDoneIndex >= 0 && steps[lastDoneIndex + 1]) {
+ return `${steps[lastDoneIndex].工序名称}毕待${steps[lastDoneIndex + 1].工序名称}`
+ }
+ if (lastDoneIndex === steps.length - 1) return '已完成'
+ return '待开工'
+}
+```
+
+## 10. 需求 7:外协任务检验
+
+### 10.1 现有页面
+
+相关页面:
+
+```text
+src/views/QualityManagement/CheckTask/processReceive.vue
+src/views/ProductionManagement/OutsourceMange/index.vue
+src/views/ProductionManagement/OutsourceSheet/index.vue
+```
+
+现有视图:
+
+- `View_质量检验_质检记录_外协收检`
+- `View_外协质检`
+- `View_外协报表视图`
+
+### 10.2 需求字段
+
+| 字段 | 来源 |
+|---|---|
+| 指派对象 | 任务表 `MES_接口_生产计划_生产任务.指派对象` |
+| 供应商 | SAP 采购/外协单据,或外协任务维护 |
+| SAP采购收货单号 | SAP 采购收货成功后返回 |
+
+### 10.3 建议新增字段
+
+优先加在质检记录扩展表,避免直接改大表影响历史。
+
+表名:`质量管理_外协收检_扩展`
+
+```sql
+CREATE TABLE dbo.质量管理_外协收检_扩展 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ QT_AID bigint NOT NULL,
+ TaskAID bigint NULL,
+ 指派对象 nvarchar(50) NULL,
+ 供应商编码 nvarchar(100) NULL,
+ 供应商名称 nvarchar(200) NULL,
+ SAP采购收货单号 nvarchar(100) NULL,
+ SAP采购收货行号 nvarchar(50) NULL,
+ SAP同步状态 nvarchar(20) NULL,
+ SAP返回信息 nvarchar(max) NULL,
+ 创建时间 datetime NOT NULL DEFAULT GETDATE(),
+ 最后维护时间 datetime NULL
+);
+
+CREATE UNIQUE INDEX UX_质量管理_外协收检_扩展_QT
+ON dbo.质量管理_外协收检_扩展(QT_AID);
+```
+
+如果客户明确要在质检主记录中查询导出,也可直接加字段到 `YL_质量检验_质检记录`:
+
+```sql
+ALTER TABLE dbo.YL_质量检验_质检记录 ADD
+ 指派对象 nvarchar(50) NULL,
+ 供应商编码 nvarchar(100) NULL,
+ 供应商名称 nvarchar(200) NULL,
+ SAP采购收货单号 nvarchar(100) NULL;
+```
+
+推荐扩展表,查询视图再拼接。
+
+### 10.4 页面改造
+
+序检收检页面:
+
+- 收检表格增加:指派对象、供应商、SAP采购收货单号。
+- 收检详情弹窗增加只读展示。
+- 外协收检成功后,保存 SAP 返回的采购收货单号。
+
+外协报表:
+
+- 增加供应商、SAP采购收货单号列。
+- 后续供应商合格率按供应商分组。
+
+### 10.5 存储过程
+
+新增或扩展:
+
+| 过程 | 说明 |
+|---|---|
+| `质量管理_外协收检_扩展保存` | 收检完成后保存供应商和 SAP 单号 |
+| `质量管理_外协收检_查询` | 查询外协收检扩展字段 |
+| `生产管理_外协任务报表_查询` | 扩展供应商、SAP采购收货单号、采购到货期说明 |
+
+## 11. 需求 8:异常管控
+
+### 11.1 现有页面
+
+异常页面:
+
+```text
+src/views/ProductionManagement/AbnormalControl/index.vue
+```
+
+需要加入口的页面:
+
+- `ProductionManagement/MachiningCenter/index.vue`
+- `ProductionManagement/AssemblyCenter/index.vue`
+- `QualityManagement/QualityCenter/index.vue`
+- `QualityManagement/CheckTask/processReceive.vue`
+- 后续设计任务报工页面
+- 后续编程任务清单页面
+
+### 11.2 页面交互
+
+在任务操作区增加按钮:
+
+```text
+[报异常]
+```
+
+点击前校验:
+
+- 必须选中任务。
+- 必须有 TaskAID 或生产订单。
+- 已关闭/已入库任务不允许新报异常,除非角色有权限。
+
+异常弹窗字段:
+
+| 字段 | 控件 |
+|---|---|
+| 生产订单 | 只读 |
+| TaskAID | 隐藏 |
+| 工序名称 | 只读 |
+| 指派对象 | 只读 |
+| 异常类型 | 下拉 |
+| 异常说明 | 多行文本 |
+| 附件 | 上传 |
+| 审批人 | 人员多选 |
+
+### 11.3 数据表
+
+使用第 3.3 节建议表:
+
+- `生产管理_异常管控_任务异常`
+- `生产管理_异常管控_审批人`
+
+### 11.4 存储过程
+
+#### 11.4.1 异常提交
+
+过程名:`生产管理_异常管控_任务异常提交`
+
+参数:
+
+- 来源模块
+- 生产订单
+- TaskAID
+- 工序名称
+- 指派对象
+- 异常类型
+- 异常说明
+- 附件ID
+- 审批人JSON
+- 发起人
+
+逻辑:
+
+1. 生成异常编号,例如 `YC202605280001`。
+2. 插入 `生产管理_异常管控_任务异常`。
+3. 解析审批人 JSON,插入 `生产管理_异常管控_审批人`。
+4. 返回异常编号。
+
+#### 11.4.2 待办查询
+
+过程名:`生产管理_异常管控_待办查询`
+
+参数:
+
+- `@审批人姓名`
+- `@状态`
+
+逻辑:
+
+- 审批人被选中即可看到任务。
+- 任一审批人处理后,异常进入下一状态或关闭。
+
+#### 11.4.3 异常处理
+
+过程名:`生产管理_异常管控_任务异常处理`
+
+参数:
+
+- 异常AID
+- 处理人
+- 处理意见
+- 处理结果
+
+逻辑:
+
+1. 更新异常主表实际处理人、处理时间、状态。
+2. 当前审批人记录标记已处理。
+3. 同一异常其他审批人不再待办。
+
+## 12. 需求 9:机加班次产量表
+
+### 12.1 页面定位
+
+新增页面:
+
+```text
+src/views/ProductionManagement/MachiningShiftOutput/index.vue
+```
+
+菜单:
+
+```text
+报表中心 -> 生产报表 -> 机加班次产量表
+```
+
+### 12.2 页面布局
+
+```text
+查询区:
+日期范围 班次 设备 操作人 生产订单 物料
+[查询] [重置] [导出]
+
+汇总卡:
+总数量 总工时 设备数 班次数
+
+分组表:
+设备 -> 日期 -> 班次 -> 任务明细
+```
+
+### 12.3 数据来源
+
+| 数据 | 来源 |
+|---|---|
+| 报工数量 | `YL_加工中心_操作记录表.操作数值` |
+| 工时 | `YL_加工中心_操作记录表.工时` |
+| 设备 | `工位名称` 或任务 `指派对象` |
+| 生产订单/物料 | `MES_接口_生产计划_生产任务` |
+| 班次 | `班次表` + 时间匹配 |
+
+现有函数:
+
+```text
+dbo.计算班次重叠秒数
+```
+
+可用于跨天班次工时计算。
+
+### 12.4 存储过程
+
+过程名:`生产报表_机加班次产量表_查询`
+
+参数:
+
+- 开始日期
+- 结束日期
+- 班次
+- 设备
+- 操作人
+- 生产订单
+- 物料
+
+返回:
+
+- 设备名称
+- 日期
+- 班次
+- 生产订单
+- 物料编码
+- 物料名称
+- 工序名称
+- 开始时间
+- 结束时间
+- 工时小时
+- 数量
+- 操作人
+- 设备日期班次小计数量
+- 设备日期班次小计工时
+
+SQL 逻辑:
+
+1. 取 `操作类别='报工'` 且 `操作数值 > 0` 的记录。
+2. 按 `开始时间` 匹配班次。
+3. 关联任务表补订单、物料、工序。
+4. 按设备、日期、班次排序。
+
+### 12.5 是否新增表
+
+第一阶段不新增表。
+
+如果班次规则不足,需要扩展 `班次表`:
+
+```sql
+ALTER TABLE dbo.班次表 ADD
+ 是否跨天 bit NULL,
+ 是否启用 bit NULL,
+ 班次日期归属 nvarchar(20) NULL; -- 开始日/结束日
+```
+
+## 13. 需求 10:发货通知列表
+
+### 13.1 页面定位
+
+新增页面:
+
+```text
+src/views/ProductionManagement/ShippingNoticeList/index.vue
+```
+
+菜单:
+
+```text
+报表中心 -> 交付报表 -> 发货通知列表
+```
+
+### 13.2 数据来源
+
+客户说明:SAP 已提供数据,大屏已显示。
+
+需要先查新分析:
+
+1. 大屏当前发货通知读取哪个过程。
+2. `装配中心任务看板_发货状态查询` 是否已经包含发货通知字段。
+3. `MES_接口_生产计划` 中 `发货状态`、`要求发货日期` 是否够用。
+4. 是否有 SAP 发货通知同步落地表。
+
+### 13.3 建议新增表
+
+若当前没有发货通知落地表,建议建:
+
+```sql
+CREATE TABLE dbo.系统集成_SAP发货通知 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 发货通知号 nvarchar(100) NOT NULL,
+ 合同号 nvarchar(50) NULL,
+ 客户编码 nvarchar(100) NULL,
+ 客户名称 nvarchar(200) NULL,
+ 生产订单 int NULL,
+ 物料编码 nvarchar(100) NULL,
+ 物料名称 nvarchar(500) NULL,
+ 数量 decimal(18,2) NULL,
+ 要求发货日期 datetime NULL,
+ 发货状态 nvarchar(50) NULL,
+ SAP同步时间 datetime NULL,
+ 原始JSON nvarchar(max) NULL,
+ 创建时间 datetime NOT NULL DEFAULT GETDATE()
+);
+
+CREATE INDEX IX_系统集成_SAP发货通知_合同号 ON dbo.系统集成_SAP发货通知(合同号);
+CREATE INDEX IX_系统集成_SAP发货通知_生产订单 ON dbo.系统集成_SAP发货通知(生产订单);
+```
+
+如果已有大屏同步表,则不建新表,直接复用。
+
+### 13.4 存储过程
+
+过程名:`报表_发货通知列表_查询`
+
+参数:
+
+- 发货通知号
+- 合同号
+- 客户
+- 状态
+- 要求发货开始
+- 要求发货结束
+
+返回:
+
+- 发货通知号
+- 合同号
+- 客户
+- 生产订单
+- 物料编码
+- 物料名称
+- 数量
+- 要求发货日期
+- 发货状态
+- SAP同步时间
+
+## 14. 需求 11:质检任务早知道
+
+### 14.1 依赖
+
+强依赖:
+
+- `生产管理_装配已排产需求_查询`
+- 装配完成时间字段可用
+
+### 14.2 页面定位
+
+新增页面:
+
+```text
+src/views/QualityManagement/QualityTaskPreview/index.vue
+```
+
+菜单:
+
+```text
+质量管理 -> 质量报表 -> 质检任务早知道
+```
+
+### 14.3 查询规则
+
+过滤:
+
+- 装配完成时间在查询范围内。
+- 排除已完成质检。
+- 排除订单关闭、取消、整单已入库。
+- 按装配完成时间分组。
+
+### 14.4 存储过程
+
+过程名:`质量管理_质检任务早知道_查询`
+
+参数:
+
+- 装配完成开始
+- 装配完成结束
+- 合同号
+- 生产订单
+- 物料
+
+返回:
+
+- 预计质检日期
+- 生产订单
+- 合同号
+- 物料编码
+- 物料名称
+- 装配完成时间
+- 当前对应人员
+- 当前对应内容
+- 累计工时
+- 异常状态
+- 质检状态
+
+SQL 逻辑:
+
+```sql
+-- 基于装配已排产需求结果,再排除已检/关闭/取消/入库
+```
+
+### 14.5 不新增表
+
+本需求不新增表,只新增查询过程和页面。
+
+## 15. 需求 12:生产工艺自动带出标准工艺
+
+### 15.1 现有页面
+
+相关页面:
+
+```text
+src/views/ProcessManagement/ProcessProduct/index.vue
+src/views/ProcessManagement/ProcessesDevelop/index.vue
+```
+
+### 15.2 页面交互
+
+点击编辑生产工艺:
+
+1. 查询是否已有生产工艺。
+2. 如果有,正常打开。
+3. 如果没有,提示:
+
+```text
+当前订单未找到生产工艺,是否按物料标准工艺带出草稿?
+[带出标准工艺] [取消]
+```
+
+4. 带出后只在页面显示,不直接落库。
+5. 用户点击保存后,写入生产工艺。
+
+### 15.3 存储过程
+
+新增:
+
+| 过程 | 用途 |
+|---|---|
+| `工艺管理_生产工艺_是否存在` | 按订单号查询是否已有生产工艺 |
+| `工艺管理_生产工艺_标准工艺草稿查询` | 按物料编码查标准工艺并映射成生产工艺字段 |
+
+保存仍复用现有生产工艺保存过程。
+
+### 15.4 字段映射
+
+需要查新分析:
+
+- 标准工艺表:`车间生产管理工艺_零件工序_工艺库`
+- 生产工艺表:`车间生产管理工艺_零件生产工艺_工艺库`
+- 两者字段差异。
+
+至少映射:
+
+| 标准工艺 | 生产工艺 |
+|---|---|
+| 物料编号 | 物料编号 |
+| 工序名称 | 工序名称 |
+| 工艺顺序 | 工艺顺序 |
+| 标准工时 | 标准工时 |
+| 工序说明 | 工序说明 |
+| 指派对象默认值 | 指派对象 |
+
+### 15.5 不建议新增表
+
+该需求只改页面和过程,不新增表。
+
+## 16. 需求 13:工时报表 + 备注类小改
+
+### 16.1 工时报表新增指派对象
+
+现有页面:
+
+```text
+src/views/ProductionManagement/Timesheet/index.vue
+```
+
+现有过程:
+
+```text
+工时统计_作业者工时_查询
+工时统计_作业者工时_查询_导出
+```
+
+改造:
+
+- 查询区增加指派对象筛选。
+- 表格增加指派对象列。
+- 导出增加指派对象列。
+
+SQL 来源:
+
+- `YL_加工中心_操作记录表.工位名称`
+- 或关联任务表取 `MES_接口_生产计划_生产任务.指派对象`
+
+推荐使用任务表指派对象,工位名称作为实际操作工位,两者都可展示:
+
+| 字段 | 说明 |
+|---|---|
+| 指派对象 | 任务被派给谁 |
+| 实际工位 | 操作记录发生在哪个工位 |
+
+### 16.2 机加领料备注
+
+需求:
+
+- 生产领料(机加)增加备注字段。
+
+需要先定位现有领料表。如果无独立表,可能通过 SAP 或操作记录保存。
+
+建议:
+
+```sql
+ALTER TABLE dbo.MES_接口_生产计划_生产任务 ADD 机加领料备注 nvarchar(500) NULL;
+```
+
+如领料已有专表,应加在领料单表。
+
+过程:
+
+- `生产管理_机加领料_备注保存`
+- `生产管理_机加领料_查询` 返回备注
+
+### 16.3 机加退库备注、装配退库备注
+
+建议如果退库已有业务表,在退库表增加:
+
+```sql
+备注 nvarchar(500) NULL,
+最后维护人 nvarchar(50) NULL,
+最后维护时间 datetime NULL
+```
+
+如果没有退库表,先不要加到任务表,建议建退库单扩展表:
+
+```sql
+CREATE TABLE dbo.生产管理_退库单_扩展 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 退库单号 nvarchar(100) NOT NULL,
+ 退库类型 nvarchar(20) NOT NULL, -- 机加/装配
+ 生产订单 int NULL,
+ TaskAID bigint NULL,
+ 备注 nvarchar(500) NULL,
+ 最后维护人 nvarchar(50) NULL,
+ 最后维护时间 datetime NULL
+);
+```
+
+### 16.4 外协任务报表采购到货期说明
+
+现有页面:
+
+```text
+src/views/ProductionManagement/OutsourceSheet/index.vue
+src/views/ProductionManagement/OutsourceMange/index.vue
+```
+
+建议字段:
+
+```sql
+ALTER TABLE dbo.MES_接口_生产计划_生产任务 ADD 采购到货期说明 nvarchar(500) NULL;
+```
+
+如果采购到货期说明来自 SAP,则不允许手工覆盖,改为扩展表:
+
+```sql
+CREATE TABLE dbo.生产管理_外协任务_采购扩展 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ TaskAID bigint NOT NULL,
+ 采购到货期说明 nvarchar(500) NULL,
+ SAP采购订单号 nvarchar(100) NULL,
+ SAP采购收货单号 nvarchar(100) NULL,
+ 供应商编码 nvarchar(100) NULL,
+ 供应商名称 nvarchar(200) NULL,
+ 最后维护人 nvarchar(50) NULL,
+ 最后维护时间 datetime NULL
+);
+```
+
+### 16.5 制定标准工艺保存提示
+
+页面:
+
+```text
+src/views/ProcessManagement/ProcessesDevelop/index.vue
+```
+
+改造:
+
+- 保存成功显示“标准工艺保存成功”。
+- 保存失败显示后端 message。
+- 如果字段缺失,提示具体字段。
+
+不涉及数据库。
+
+## 17. 需求 14:生产订单关闭批量更新 SAP 状态
+
+### 17.1 现有页面
+
+页面:
+
+```text
+src/views/PlanManagement/PlannOrderClose/index.vue
+```
+
+### 17.2 页面改造
+
+表格增加多选列:
+
+```html
+
+```
+
+新增按钮:
+
+```text
+[批量关闭 SAP 订单]
+```
+
+点击后:
+
+1. 校验已选择订单。
+2. 弹出二次确认:
+
+```text
+即将批量关闭 N 个 SAP 生产订单。该操作会写入 SAP,可能部分成功部分失败,是否继续?
+```
+
+3. 调用批量关闭逻辑。
+4. 展示结果弹窗。
+
+结果弹窗字段:
+
+- 生产订单
+- SAP 单据号
+- 执行结果
+- 失败原因
+- 是否可重试
+
+### 17.3 后端建议
+
+由于当前前端很多 SAP 操作是直接调用 SAP/B1 接口或通过现有工具实现,批量关闭建议封装为前端逐单执行 + MES 记录结果,或后端新增 API。若 `.ashx` 仅支持 SQL,不适合在 SQL 里直接调 SAP。
+
+建议流程:
+
+```text
+前端选中订单
+ -> 查询订单 SAP DocEntry
+ -> 逐单 PATCH SAP ProductionOrders
+ -> 每单结果写入 系统集成_SAP调用日志
+ -> 更新 MES 订单状态或 SAP关闭状态扩展字段
+```
+
+### 17.4 建议新增字段
+
+表:`MES_接口_生产计划`
+
+```sql
+ALTER TABLE dbo.MES_接口_生产计划 ADD
+ SAP关闭状态 nvarchar(20) NULL,
+ SAP关闭时间 datetime NULL,
+ SAP关闭失败原因 nvarchar(1000) NULL,
+ SAP关闭操作人 nvarchar(50) NULL;
+```
+
+如果不想改主表,新增扩展表:
+
+```sql
+CREATE TABLE dbo.生产管理_订单关闭_SAP状态 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 订单编号 int NOT NULL,
+ SAP单据号 nvarchar(100) NULL,
+ 关闭状态 nvarchar(20) NOT NULL,
+ 失败原因 nvarchar(1000) NULL,
+ 操作人 nvarchar(50) NULL,
+ 操作时间 datetime NOT NULL DEFAULT GETDATE(),
+ 是否可重试 bit NOT NULL DEFAULT 0
+);
+```
+
+### 17.5 存储过程
+
+| 过程 | 用途 |
+|---|---|
+| `生产管理_订单关闭_SAP结果保存` | 保存每单关闭结果 |
+| `生产管理_订单关闭_SAP结果查询` | 查询批量关闭结果 |
+| `生产管理_订单关闭_可关闭校验` | 批量操作前校验订单是否可关 |
+
+## 18. 需求 15:及时检验报表
+
+### 18.1 页面定位
+
+新增页面:
+
+```text
+src/views/QualityManagement/TimelyInspectionReport/index.vue
+```
+
+菜单:
+
+```text
+质量管理 -> 质量报表 -> 及时检验报表
+```
+
+### 18.2 查询口径
+
+进入报表条件:
+
+1. `YL_加工中心_操作记录表` 中存在报工记录。
+2. `操作类别 = '报工'`。
+3. `操作数值 > 0`。
+4. `结束时间 < DATEADD(DAY, -2, GETDATE())`。
+5. 对应质检记录不存在,或检验数量不足。
+
+### 18.3 页面字段
+
+| 字段 |
+|---|
+| 生产订单 |
+| TaskAID |
+| 物料编码 |
+| 物料名称 |
+| 工序名称 |
+| 指派对象 |
+| 报工数量 |
+| 已检数量 |
+| 未检数量 |
+| 报工结束时间 |
+| 超期天数 |
+| 操作人 |
+
+### 18.4 存储过程
+
+过程名:`质量管理_及时检验报表_查询`
+
+参数:
+
+- 生产订单
+- 物料
+- 工序
+- 指派对象
+- 报工结束开始
+- 报工结束结束
+- 超期天数,默认 2
+
+SQL 示例:
+
+```sql
+CREATE PROCEDURE dbo.质量管理_及时检验报表_查询
+ @生产订单 int = NULL,
+ @物料 nvarchar(100) = '',
+ @工序 nvarchar(100) = '',
+ @指派对象 nvarchar(100) = '',
+ @报工结束开始 date = NULL,
+ @报工结束结束 date = NULL,
+ @超期天数 int = 2
+AS
+BEGIN
+ SET NOCOUNT ON;
+
+ WITH rpt AS (
+ SELECT
+ o.TaskAID,
+ SUM(ISNULL(o.操作数值,0)) AS 报工数量,
+ MAX(o.结束时间) AS 报工结束时间,
+ MAX(o.操作人) AS 操作人
+ FROM dbo.YL_加工中心_操作记录表 o
+ WHERE o.操作类别 = N'报工'
+ AND ISNULL(o.操作数值,0) > 0
+ AND o.结束时间 < DATEADD(DAY, -@超期天数, GETDATE())
+ GROUP BY o.TaskAID
+ ),
+ qc AS (
+ SELECT
+ TaskAID,
+ SUM(ISNULL(检验数量,0)) AS 已检数量
+ FROM dbo.YL_质量检验_质检记录
+ GROUP BY TaskAID
+ )
+ SELECT
+ t.订单编号 AS 生产订单,
+ t.AID AS TaskAID,
+ t.物料编码,
+ t.物料名称,
+ t.工序名称,
+ t.指派对象,
+ rpt.报工数量,
+ ISNULL(qc.已检数量,0) AS 已检数量,
+ rpt.报工数量 - ISNULL(qc.已检数量,0) AS 未检数量,
+ rpt.报工结束时间,
+ DATEDIFF(DAY, rpt.报工结束时间, GETDATE()) AS 超期天数,
+ rpt.操作人
+ FROM rpt
+ JOIN dbo.MES_接口_生产计划_生产任务 t ON rpt.TaskAID = t.AID
+ LEFT JOIN qc ON rpt.TaskAID = qc.TaskAID
+ WHERE rpt.报工数量 > ISNULL(qc.已检数量,0)
+ AND (@生产订单 IS NULL OR t.订单编号 = @生产订单)
+ AND (@物料 = '' OR t.物料编码 LIKE '%' + @物料 + '%' OR t.物料名称 LIKE '%' + @物料 + '%')
+ AND (@工序 = '' OR t.工序名称 = @工序)
+ AND (@指派对象 = '' OR t.指派对象 = @指派对象)
+ AND (@报工结束开始 IS NULL OR rpt.报工结束时间 >= @报工结束开始)
+ AND (@报工结束结束 IS NULL OR rpt.报工结束时间 < DATEADD(DAY, 1, @报工结束结束))
+ ORDER BY 超期天数 DESC, rpt.报工结束时间;
+END
+```
+
+### 18.5 不新增表
+
+第一阶段不新增表。若要催办闭环,再新增催办记录表。
+
+## 19. 需求 16:工序流转卡增加原料编码
+
+### 19.1 需要查新分析
+
+先定位工序流转卡打印页面和模板:
+
+- 搜索页面中 `流转卡`、`print`、`打印`。
+- 确认当前流转卡数据来源过程。
+- 确认原料编码来自 BOM、SAP 工艺路线,还是物料字段。
+
+### 19.2 数据来源建议
+
+优先顺序:
+
+1. `BOM数据` 中父件对应子件,取原料类子件编码。
+2. SAP BOM/物料清单同步表。
+3. 生产任务行文本备注或行物料字段。
+
+### 19.3 存储过程
+
+扩展流转卡查询过程,增加字段:
+
+| 字段 | 说明 |
+|---|---|
+| 原料编码 | BOM 子件编码 |
+| 原料名称 | BOM 子件名称,可选 |
+| 原料规格 | 可选 |
+
+SQL 示例:
+
+```sql
+LEFT JOIN dbo.BOM数据 bom
+ ON bom.父件编码 = t.物料编码
+```
+
+### 19.4 页面/打印
+
+打印模板增加:
+
+```text
+原料编码:XXXXXXXX
+```
+
+如果一个成品对应多个原料编码:
+
+- 用逗号合并。
+- 或打印明细区。
+
+## 20. 需求 17-19:现场大屏
+
+包括:
+
+- 一楼质检大屏幕
+- 机加现场大屏幕
+- 装配现场大屏幕
+
+看板项目目录:
+
+| 大屏 | 项目目录 |
+|---|---|
+| 质检任务看板 | `质检任务看板` |
+| 机加现场大屏 | `机加设备生产运营看板`、`机加设备生产运营看板二` |
+| 装配现场大屏 | `装配任务看板` |
+
+### 20.1 通用大屏开发原则
+
+1. 大屏只展示,不作为字段维护入口。
+2. 关键工序、缺勤、送检状态必须先有业务数据来源。
+3. 所有大屏过程必须支持 `@统计日期`,默认当天。
+4. 大屏刷新频率建议 30 秒以上,除设备状态外不建议 5 秒刷新复杂 SQL。
+5. 指标必须能下钻明细。
+
+### 20.2 一楼质检大屏
+
+#### 20.2.1 新增指标
+
+| 指标 | 口径 |
+|---|---|
+| 质检及时率 | 及时检验任务数 / 应检任务数 |
+| 超 2 天未检任务 | 最后生产完成时间超过 2 天且未检足 |
+| 关键工序应检数 | 任务快照中 `是否关键工序=1` |
+| 关键工序未检数 | 应检但未检足 |
+| 关键工序已检数 | 已检足 |
+
+#### 20.2.2 需要新增字段
+
+关键工序不要放在看板维护,应加在工艺和任务快照。
+
+建议字段:
+
+```sql
+ALTER TABLE dbo.车间生产管理工艺_零件工序_工艺库 ADD 是否关键工序 bit NULL DEFAULT 0;
+ALTER TABLE dbo.车间生产管理工艺_零件生产工艺_工艺库 ADD 是否关键工序 bit NULL DEFAULT 0;
+ALTER TABLE dbo.MES_接口_生产计划_生产任务 ADD 是否关键工序 bit NULL DEFAULT 0;
+```
+
+任务下发时把生产工艺中的 `是否关键工序` 快照到任务表。
+
+#### 20.2.3 存储过程
+
+扩展或新增:
+
+| 过程 | 用途 |
+|---|---|
+| `质量任务看板_未完成统计` | 增加超期天数、关键工序统计 |
+| `质量任务看板_未完成明细查询` | 增加最后生产完成时间、是否关键工序 |
+| `质量任务看板_及时率统计` | 新增及时率 |
+
+### 20.3 机加现场大屏
+
+#### 20.3.1 新增指标
+
+| 指标 | 口径 |
+|---|---|
+| 缺勤人员 | 应到人员 - 实到人员 |
+| 关键工序任务汇总 | 已排产未完成且是否关键工序=1 |
+| 关键工序任务明细 | 订单、物料、工序、设备、指派对象、计划开始、完成数 |
+
+#### 20.3.2 缺勤数据
+
+不能从“没有报工”反推缺勤。
+
+需要数据:
+
+| 数据 | 来源 |
+|---|---|
+| 应到人员 | 班次人员绑定表、排班表、岗位工位绑定 |
+| 实到人员 | 考勤、上岗绑定、登录、报工记录 |
+
+建议新增排班表:
+
+```sql
+CREATE TABLE dbo.人员排班表 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 排班日期 date NOT NULL,
+ 班次编号 int NULL,
+ 班次名称 nvarchar(50) NULL,
+ 人员编号 int NOT NULL,
+ 人员姓名 nvarchar(50) NOT NULL,
+ 岗位 nvarchar(50) NULL,
+ 工位号 nvarchar(50) NULL,
+ 是否应到 bit NOT NULL DEFAULT 1,
+ 创建人 nvarchar(50) NULL,
+ 创建时间 datetime NOT NULL DEFAULT GETDATE()
+);
+
+CREATE INDEX IX_人员排班表_日期班次 ON dbo.人员排班表(排班日期, 班次名称);
+```
+
+如果已有 `班次人员绑定表` 能满足,则不新建,只扩展字段。
+
+#### 20.3.3 存储过程
+
+| 过程 | 用途 |
+|---|---|
+| `机加现场大屏_缺勤人员查询` | 返回岗位、工位、姓名 |
+| `机加现场大屏_关键工序统计` | 汇总关键工序任务 |
+| `机加现场大屏_关键工序明细` | 明细下钻 |
+
+### 20.4 装配现场大屏
+
+#### 20.4.1 新增指标
+
+| 指标 | 口径 |
+|---|---|
+| 缺勤人员 | 同机加 |
+| 及时送检任务 | 发料齐套后超过 2 天未送检 |
+| 排序 | 加急优先,其次及时送检超期,再按计划开始或齐套时间 |
+
+#### 20.4.2 存储过程
+
+| 过程 | 用途 |
+|---|---|
+| `装配现场大屏_缺勤人员查询` | 岗位、工位、姓名 |
+| `装配现场大屏_及时送检任务查询` | 发料齐套后 2 天未送检 |
+| `装配中心任务看板_装配任务查询` | 增加发料齐套时间、是否已送检、送检时间、超期天数 |
+
+#### 20.4.3 字段来源
+
+| 字段 | 来源 |
+|---|---|
+| 发料齐套时间 | `MES_接口_生产计划.齐套时间` 或装配扩展表 |
+| 是否已送检 | 送检记录/质检任务/质检记录 |
+| 送检时间 | 送检记录 |
+| 超期天数 | `DATEDIFF(DAY, 发料齐套时间, GETDATE())` |
+
+## 21. 需求 20:编程任务清单 / 编程报工
+
+### 21.1 页面定位
+
+新增页面:
+
+```text
+src/views/ProductionManagement/ProgrammingTask/index.vue
+```
+
+菜单:
+
+```text
+生产管理 -> 报工管理 -> 编程任务清单
+```
+
+### 21.2 页面布局
+
+```text
+查询区:
+设备 工位 程序员 状态 生产订单 物料 日期
+[查询] [重置] [新增编程任务] [导出]
+
+表格:
+任务编号 生产订单 物料编码 物料名称 工序 设备/工位 程序号
+编程人员 状态 开始时间 结束时间 工时 操作
+
+操作:
+[开始编程] [结束编程] [编辑程序号] [工时记录] [报异常]
+```
+
+### 21.3 新增表
+
+#### 21.3.1 编程任务表
+
+```sql
+CREATE TABLE dbo.生产管理_编程任务 (
+ 编程任务ID bigint IDENTITY(1,1) PRIMARY KEY,
+ 编程任务编号 nvarchar(50) NOT NULL,
+ 生产订单 int NULL,
+ TaskAID bigint NULL,
+ 物料编码 nvarchar(100) NULL,
+ 物料名称 nvarchar(500) NULL,
+ 工序名称 nvarchar(100) NULL,
+ 设备编号 nvarchar(50) NULL,
+ 设备名称 nvarchar(100) NULL,
+ 工位编号 nvarchar(50) NULL,
+ 工位名称 nvarchar(100) NULL,
+ 程序号 nvarchar(100) NULL,
+ 任务来源 nvarchar(50) NULL, -- 人工创建/订单生成/设备任务派生
+ 状态 nvarchar(20) NOT NULL DEFAULT N'未开始',
+ 任务说明 nvarchar(1000) NULL,
+ 创建人 nvarchar(50) NULL,
+ 创建时间 datetime NOT NULL DEFAULT GETDATE(),
+ 最后维护人 nvarchar(50) NULL,
+ 最后维护时间 datetime NULL
+);
+```
+
+#### 21.3.2 编程工时记录表
+
+```sql
+CREATE TABLE dbo.生产管理_编程任务_工时记录 (
+ 记录ID bigint IDENTITY(1,1) PRIMARY KEY,
+ 编程任务ID bigint NOT NULL,
+ 操作类别 nvarchar(20) NOT NULL, -- 开始/结束
+ 操作人编号 int NULL,
+ 操作人 nvarchar(50) NOT NULL,
+ 开始时间 datetime NULL,
+ 结束时间 datetime NULL,
+ 工时分钟 int NULL,
+ 工作内容 nvarchar(1000) NULL,
+ 结束说明 nvarchar(1000) NULL,
+ 创建时间 datetime NOT NULL DEFAULT GETDATE()
+);
+
+CREATE INDEX IX_生产管理_编程任务_工时记录_任务
+ON dbo.生产管理_编程任务_工时记录(编程任务ID);
+```
+
+### 21.4 存储过程
+
+| 过程 | 用途 |
+|---|---|
+| `生产管理_编程任务_查询` | 列表查询 |
+| `生产管理_编程任务_新增` | 新增任务 |
+| `生产管理_编程任务_开始` | 开始编程 |
+| `生产管理_编程任务_结束` | 结束编程并计算工时 |
+| `生产管理_编程任务_工时明细` | 工时明细 |
+| `生产管理_编程任务_程序号保存` | 维护程序号 |
+
+### 21.5 工时接入
+
+`View_统一工时明细` 增加:
+
+```text
+来源模块 = 编程
+工时类型 = 编程
+```
+
+这样后续合同工时、工时报表可选择是否包含编程工时。
+
+## 22. 需求 21-24:设备档案、设备数据采集、OEE、工时查询联动
+
+### 22.1 设备档案管理
+
+现有页面:
+
+```text
+src/views/DeviceManagement/DeviceInformation/index.vue
+```
+
+现有表:
+
+```text
+YL_设备管理_设备台账
+MES_资源主数据
+```
+
+建议新增字段:
+
+```sql
+ALTER TABLE dbo.YL_设备管理_设备台账 ADD
+ 是否主要设备 bit NULL DEFAULT 0,
+ 是否参与OEE bit NULL DEFAULT 0,
+ 设备标准工时 decimal(18,2) NULL,
+ 标准工时单位 nvarchar(20) NULL,
+ 采集设备编码 nvarchar(100) NULL,
+ 采集点位配置 nvarchar(max) NULL;
+```
+
+注意:
+
+- 设备标准工时不能替代工艺标准工时。
+- OEE 的标准单件工时优先来自工艺或设备+工序能力表。
+
+建议新增设备工序能力表:
+
+```sql
+CREATE TABLE dbo.设备管理_设备工序能力 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 设备编号 nvarchar(50) NOT NULL,
+ 设备名称 nvarchar(100) NULL,
+ 工序名称 nvarchar(100) NOT NULL,
+ 物料编码 nvarchar(100) NULL,
+ 标准单件工时 decimal(18,4) NULL,
+ 单位 nvarchar(20) NULL DEFAULT N'分钟',
+ 是否启用 bit NOT NULL DEFAULT 1,
+ 创建人 nvarchar(50) NULL,
+ 创建时间 datetime NOT NULL DEFAULT GETDATE(),
+ 最后维护人 nvarchar(50) NULL,
+ 最后维护时间 datetime NULL
+);
+```
+
+### 22.2 设备数据采集
+
+现有表:
+
+- `CaChe_Var`
+- `历史_设备数据_定时`
+- `历史_工位设备状态每分钟记录`
+- `YL_设备管理_工时记录`
+
+新增建议:
+
+#### 22.2.1 采集原始数据表
+
+如果现有 `历史_设备数据_定时` 字段足够,可不新建。否则新增:
+
+```sql
+CREATE TABLE dbo.设备采集_原始数据 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 采集时间 datetime NOT NULL,
+ 设备编号 nvarchar(50) NULL,
+ 设备名称 nvarchar(100) NULL,
+ 工位号 nvarchar(50) NULL,
+ 变量名称 nvarchar(100) NULL,
+ 变量值 nvarchar(200) NULL,
+ 原始值 nvarchar(200) NULL,
+ 单位 nvarchar(20) NULL,
+ TagID nvarchar(100) NULL,
+ DeviceCode nvarchar(100) NULL,
+ 在线状态 nvarchar(20) NULL,
+ 采集质量 nvarchar(20) NULL,
+ 创建时间 datetime NOT NULL DEFAULT GETDATE()
+);
+```
+
+#### 22.2.2 设备状态汇总表
+
+```sql
+CREATE TABLE dbo.设备采集_设备状态分钟汇总 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 统计时间 datetime NOT NULL,
+ 统计日期 date NOT NULL,
+ 班次名称 nvarchar(50) NULL,
+ 设备编号 nvarchar(50) NULL,
+ 设备名称 nvarchar(100) NULL,
+ 工位号 nvarchar(50) NULL,
+ 状态代码 int NULL,
+ 状态名称 nvarchar(50) NULL,
+ 持续秒数 int NOT NULL DEFAULT 60,
+ 是否有效加工 bit NULL,
+ 创建时间 datetime NOT NULL DEFAULT GETDATE()
+);
+```
+
+#### 22.2.3 设备任务绑定表
+
+设备采集必须绑定 MES 任务,否则无法算任务级程序工时。
+
+```sql
+CREATE TABLE dbo.设备采集_任务绑定 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 设备编号 nvarchar(50) NOT NULL,
+ 设备名称 nvarchar(100) NULL,
+ 工位号 nvarchar(50) NULL,
+ TaskAID bigint NOT NULL,
+ 生产订单 int NOT NULL,
+ 程序号 nvarchar(100) NULL,
+ 绑定开始时间 datetime NOT NULL,
+ 绑定结束时间 datetime NULL,
+ 绑定来源 nvarchar(50) NULL, -- 人工/条码/程序号/时间推断
+ 绑定人 nvarchar(50) NULL,
+ 创建时间 datetime NOT NULL DEFAULT GETDATE()
+);
+```
+
+### 22.3 OEE 指标
+
+建议新增指标明细表:
+
+```sql
+CREATE TABLE dbo.设备指标_班次明细 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 统计日期 date NOT NULL,
+ 班次名称 nvarchar(50) NULL,
+ 设备编号 nvarchar(50) NOT NULL,
+ 设备名称 nvarchar(100) NULL,
+ 工位号 nvarchar(50) NULL,
+ TaskAID bigint NULL,
+ 生产订单 int NULL,
+ 物料编码 nvarchar(100) NULL,
+ 物料名称 nvarchar(500) NULL,
+ 工序名称 nvarchar(100) NULL,
+ 计划产量 decimal(18,2) NULL,
+ 实际产量 decimal(18,2) NULL,
+ 良品数 decimal(18,2) NULL,
+ 不良品数 decimal(18,2) NULL,
+ 标准单件工时 decimal(18,4) NULL,
+ 实际单件工时 decimal(18,4) NULL,
+ 设备运行工时分钟 decimal(18,2) NULL,
+ 标准可用工时分钟 decimal(18,2) NULL,
+ 产能利用率 decimal(18,4) NULL,
+ 设备利用率 decimal(18,4) NULL,
+ 性能开动率 decimal(18,4) NULL,
+ 产品合格率 decimal(18,4) NULL,
+ OEE decimal(18,4) NULL,
+ 计算时间 datetime NOT NULL DEFAULT GETDATE(),
+ 计算说明 nvarchar(1000) NULL
+);
+```
+
+计算过程:
+
+| 过程 | 用途 |
+|---|---|
+| `设备指标_班次明细_生成` | 按日期班次生成指标 |
+| `设备指标_OEE_查询` | 页面/看板查询 |
+| `设备指标_OEE_明细` | 下钻查看三项组成 |
+
+### 22.4 工时查询联动
+
+现有页面:
+
+```text
+src/views/ProductionManagement/Workhours/index.vue
+src/views/ProductionManagement/Timesheet/index.vue
+src/views/ProductionManagement/WorkHoursCheck/index.vue
+```
+
+新增字段:
+
+- 程序工时
+- 设备运行工时
+- 实际单件工时
+- 标准单件工时
+- 设备采集状态
+- 采集质量
+
+建议过程:
+
+```text
+工时查询_设备采集工时_查询
+```
+
+按 TaskAID、设备、时间段关联 `设备采集_任务绑定` 和设备状态汇总。
+
+## 23. 需求 25:手续确认报表
+
+### 23.1 页面定位
+
+新增页面:
+
+```text
+src/views/ProductionManagement/FormalityConfirmReport/index.vue
+```
+
+菜单:
+
+```text
+报表中心 -> 装配报表 -> 手续确认报表
+```
+
+### 23.2 页面布局
+
+```text
+查询区:
+生产订单 合同号 物料 指派对象 发料齐套日期 手续确认状态
+[查询] [重置] [导出] [批量确认完成]
+
+表格:
+选择 生产订单 合同号 物料编码 物料名称 指派对象 发料齐套时间
+装配完成时间 当前状态 手续确认 确认人 确认时间 操作
+
+操作:
+[确认完成] [查看装配明细] [查看异常]
+```
+
+### 23.3 新增表
+
+表名:`生产管理_手续确认`
+
+```sql
+CREATE TABLE dbo.生产管理_手续确认 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 订单编号 int NOT NULL,
+ 确认状态 nvarchar(20) NOT NULL DEFAULT N'未完成',
+ 确认人 nvarchar(50) NULL,
+ 确认时间 datetime NULL,
+ 确认备注 nvarchar(500) NULL,
+ 创建时间 datetime NOT NULL DEFAULT GETDATE(),
+ 最后维护人 nvarchar(50) NULL,
+ 最后维护时间 datetime NULL
+);
+
+CREATE UNIQUE INDEX UX_生产管理_手续确认_订单编号
+ON dbo.生产管理_手续确认(订单编号);
+```
+
+### 23.4 消失条件
+
+报表只显示待确认数据:
+
+```sql
+WHERE ISNULL(fc.确认状态, N'未完成') <> N'已完成'
+ AND ISNULL(p.订单状态, '') NOT IN (N'取消', N'关闭')
+ AND ISNULL(p.单据状态, '') NOT IN (N'取消', N'关闭')
+ AND ISNULL(p.入库数量, 0) < ISNULL(p.计划数量, 0)
+```
+
+### 23.5 存储过程
+
+| 过程 | 用途 |
+|---|---|
+| `生产管理_手续确认报表_查询` | 待确认列表 |
+| `生产管理_手续确认报表_确认完成` | 单条确认 |
+| `生产管理_手续确认报表_批量确认完成` | 批量确认 |
+
+确认过程同时写 `系统管理_业务字段维护日志`。
+
+## 24. 需求 26:产品合格率(工位)
+
+### 24.1 页面定位
+
+新增或扩展质量报表页面:
+
+```text
+src/views/QualityManagement/StationPassRate/index.vue
+```
+
+### 24.2 口径
+
+公式:
+
+```text
+产品合格率 = 良品数 / 总产量
+```
+
+需要先确认:
+
+- 总产量取报工完成数量还是质检检验数量。
+- 良品数取质检合格数还是报工合格数。
+- 统计维度按工位、设备、指派对象还是实际操作工位。
+
+建议第一版:
+
+- 总产量:质检记录 `检验数量`
+- 良品数:质检记录 `合格数`
+- 工位:关联任务表 `指派对象`,同时展示质检记录来源工位。
+
+### 24.3 存储过程
+
+过程名:`质量管理_产品合格率_工位查询`
+
+参数:
+
+- 开始日期
+- 结束日期
+- 工位
+- 工序
+- 物料
+
+返回:
+
+- 工位
+- 检验数量
+- 合格数量
+- 不合格数量
+- 合格率
+- 明细数
+
+### 24.4 不新增表
+
+第一阶段不新增表。如果要日快照,写入 `质量管理_工位合格率_日快照`。
+
+## 25. 需求 27:让步接收记录
+
+### 25.1 页面定位
+
+新增页面:
+
+```text
+src/views/QualityManagement/ConcessionRecord/index.vue
+```
+
+菜单:
+
+```text
+质量管理 -> 质量报表 -> 让步接收记录
+```
+
+### 25.2 数据来源
+
+现有字段:
+
+- `MES_接口_生产计划_生产任务.让步放行数量`
+- `YL_质量检验_不合格品处理.申请让步放行`
+- `YL_质量检验_不合格品处理_处理节点.让步放行数量`
+
+需求说“新增让步记录查询页面”,建议从不合格品处理和质检记录综合查询。
+
+### 25.3 存储过程
+
+过程名:`质量管理_让步接收记录_查询`
+
+参数:
+
+- 生产订单
+- 物料
+- 检验人
+- 检验开始
+- 检验结束
+- 让步状态
+
+返回:
+
+- 生产订单
+- TaskAID
+- 物料编码
+- 物料名称
+- 工序名称
+- 检验数量
+- 不合格数量
+- 让步放行数量
+- 申请让步放行
+- 检验人
+- 检验时间
+- 处理意见
+
+### 25.4 不新增表
+
+页面只查询,不维护。
+
+## 26. 需求 28:设计任务报工模块 / 报工-技术-MES
+
+### 26.1 页面定位
+
+新增页面:
+
+```text
+src/views/ProductionManagement/DesignTaskWork/index.vue
+```
+
+菜单:
+
+```text
+生产管理 -> 报工管理 -> 设计任务报工
+```
+
+### 26.2 页面布局
+
+```text
+查询区:
+任务名称 设计人员 状态 日期
+[查询] [重置] [新增设计任务] [导出]
+
+表格:
+任务编号 任务名称 设计人员 状态 计划开始 计划完成
+开始时间 结束时间 工时 工作内容 结束说明 创建人 操作
+
+操作:
+[开始报工] [结束报工] [工时记录] [编辑] [取消]
+```
+
+### 26.3 新增表
+
+#### 26.3.1 设计任务表
+
+```sql
+CREATE TABLE dbo.生产管理_设计任务 (
+ 设计任务ID bigint IDENTITY(1,1) PRIMARY KEY,
+ 设计任务编号 nvarchar(50) NOT NULL,
+ 任务名称 nvarchar(200) NOT NULL,
+ 设计人员编号 int NULL,
+ 设计人员 nvarchar(50) NULL,
+ 状态 nvarchar(20) NOT NULL DEFAULT N'未开始',
+ 计划开始时间 datetime NULL,
+ 计划完成时间 datetime NULL,
+ 任务说明 nvarchar(1000) NULL,
+ 创建人 nvarchar(50) NULL,
+ 创建时间 datetime NOT NULL DEFAULT GETDATE(),
+ 最后维护人 nvarchar(50) NULL,
+ 最后维护时间 datetime NULL
+);
+```
+
+#### 26.3.2 设计工时记录表
+
+```sql
+CREATE TABLE dbo.生产管理_设计任务_工时记录 (
+ 记录ID bigint IDENTITY(1,1) PRIMARY KEY,
+ 设计任务ID bigint NOT NULL,
+ 操作人编号 int NULL,
+ 操作人 nvarchar(50) NOT NULL,
+ 开始时间 datetime NOT NULL,
+ 结束时间 datetime NULL,
+ 工时分钟 int NULL,
+ 工作内容 nvarchar(1000) NULL,
+ 结束说明 nvarchar(1000) NULL,
+ 创建时间 datetime NOT NULL DEFAULT GETDATE()
+);
+```
+
+### 26.4 存储过程
+
+| 过程 | 用途 |
+|---|---|
+| `生产管理_设计任务_查询` | 任务列表 |
+| `生产管理_设计任务_新增` | 新建任务 |
+| `生产管理_设计任务_编辑` | 编辑任务 |
+| `生产管理_设计任务_取消` | 取消 |
+| `生产管理_设计任务_开始` | 开始报工 |
+| `生产管理_设计任务_结束` | 结束报工 |
+| `生产管理_设计任务_工时明细` | 工时明细 |
+
+### 26.5 工时接入
+
+`View_统一工时明细` 增加:
+
+```text
+来源模块 = 设计
+工时类型 = 设计
+```
+
+注意:
+
+- 设计任务当前不上传 SAP。
+- 设计任务不产生质量、装配、机加工时。
+- 后续装配已排产需求的合同工时汇总可选择纳入设计工时。
+
+## 27. 需求 29:工时异常报表
+
+### 27.1 页面定位
+
+新增页面:
+
+```text
+src/views/ProductionManagement/WorkTimeAbnormalReport/index.vue
+```
+
+### 27.2 异常口径
+
+```text
+实际单件工时 > 标准单件工时
+```
+
+字段来源:
+
+| 字段 | 来源 |
+|---|---|
+| 实际单件工时 | 设备采集,或人工报工工时 / 完成数量 |
+| 标准单件工时 | 生产工艺或设备工序能力表 |
+| 设备/工位 | 任务指派对象、设备绑定 |
+| 程序号 | 编程任务或设备采集 |
+
+### 27.3 建议新增表
+
+如需要异常确认闭环:
+
+```sql
+CREATE TABLE dbo.生产管理_工时异常确认 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 统计日期 date NULL,
+ TaskAID bigint NULL,
+ 生产订单 int NULL,
+ 设备编号 nvarchar(50) NULL,
+ 设备名称 nvarchar(100) NULL,
+ 标准单件工时 decimal(18,4) NULL,
+ 实际单件工时 decimal(18,4) NULL,
+ 超出时长 decimal(18,4) NULL,
+ 超出比例 decimal(18,4) NULL,
+ 异常原因 nvarchar(500) NULL,
+ 责任部门 nvarchar(100) NULL,
+ 处理说明 nvarchar(1000) NULL,
+ 确认状态 nvarchar(20) NOT NULL DEFAULT N'未确认',
+ 确认人 nvarchar(50) NULL,
+ 确认时间 datetime NULL
+);
+```
+
+### 27.4 存储过程
+
+| 过程 | 用途 |
+|---|---|
+| `生产管理_工时异常报表_查询` | 查询异常 |
+| `生产管理_工时异常报表_明细` | 查看来源明细 |
+| `生产管理_工时异常报表_确认` | 异常确认 |
+
+### 27.5 页面字段
+
+- 设备/工位
+- 生产订单
+- 物料编码
+- 物料名称
+- 工序名称
+- 指派对象
+- 操作人
+- 程序号
+- 标准单件工时
+- 实际单件工时
+- 超出时长
+- 超出比例
+- 开始时间
+- 结束时间
+- 班次
+- 异常原因
+- 确认状态
+
+## 28. 需求 30:正在加工状态
+
+### 28.1 目标
+
+“正在加工状态:机加正在进行中”。
+
+### 28.2 数据来源
+
+优先复用:
+
+- `MES_ProductCenter_DoingTask`
+- `v_CurrentProductionStatus`
+- `YL_加工中心_操作记录表` 中开始未结束记录
+
+### 28.3 页面/看板展示
+
+可以在以下页面增加状态:
+
+- 加工中心任务列表
+- 生产计划跟踪
+- 机加现场大屏
+
+展示规则:
+
+| 条件 | 状态 |
+|---|---|
+| 有开始记录且未报工结束 | 正在加工 |
+| 任务状态已完成 | 已完成 |
+| 有暂停记录未恢复 | 暂停中 |
+| 无开始记录 | 未开工 |
+
+### 28.4 存储过程
+
+过程名:`生产管理_正在加工状态_查询`
+
+返回:
+
+- TaskAID
+- 生产订单
+- 工序名称
+- 指派对象
+- 操作人
+- 开始时间
+- 当前持续分钟
+- 状态
+
+## 29. 暂不排期项的技术前置
+
+### 29.1 及时拣配(机加/装配)
+
+必须先确认:
+
+1. 条码代表物料、箱号、批次、生产订单还是拣配单。
+2. SAP 库存和 MES 拣配状态谁是主口径。
+3. 扫码后是否写回 `拣配状态`、`齐套`、`发料状态`。
+4. 机加和装配是否共用拣配表。
+
+建议表:
+
+```sql
+CREATE TABLE dbo.生产管理_条码拣配记录 (
+ AID bigint IDENTITY(1,1) PRIMARY KEY,
+ 拣配类型 nvarchar(20) NOT NULL, -- 机加/装配
+ 条码 nvarchar(200) NOT NULL,
+ 生产订单 int NULL,
+ TaskAID bigint NULL,
+ 物料编码 nvarchar(100) NULL,
+ 物料名称 nvarchar(500) NULL,
+ 批次号 nvarchar(100) NULL,
+ 数量 decimal(18,2) NULL,
+ 拣配状态 nvarchar(20) NULL,
+ SAP校验状态 nvarchar(20) NULL,
+ 异常说明 nvarchar(500) NULL,
+ 拣配人 nvarchar(50) NULL,
+ 拣配时间 datetime NOT NULL DEFAULT GETDATE()
+);
+```
+
+### 29.2 标牌打印操作
+
+需求未定,不建表不开发。需确认:
+
+- 打印对象:物料、订单、设备、工位、产品?
+- 条码内容。
+- 模板样式。
+- 是否需要打印记录。
+
+### 29.3 首检、巡检
+
+现有质检中心已有首检、巡检相关逻辑。空需求暂不开发。若后续提出,应先确认是新增报表、页面优化还是流程改造。
+
+## 30. 菜单和权限
+
+所有新增页面都需要写入 `登录基础数据_二级菜单`,并按角色分配。
+
+建议新增菜单:
+
+| 页面 | 菜单 |
+|---|---|
+| 装配已排产需求 | 生产管理/装配管理 |
+| 生产日报-送检 | 报表中心/生产日报 |
+| 及时齐套跟踪结果报表 | 报表中心/齐套报表 |
+| 机加班次产量表 | 报表中心/生产报表 |
+| 发货通知列表 | 报表中心/交付报表 |
+| 质检任务早知道 | 质量管理/质量报表 |
+| 及时检验报表 | 质量管理/质量报表 |
+| 手续确认报表 | 报表中心/装配报表 |
+| 让步接收记录 | 质量管理/质量报表 |
+| 设计任务报工 | 生产管理/报工管理 |
+| 编程任务清单 | 生产管理/报工管理 |
+| 工时异常报表 | 报表中心/工时报表 |
+
+菜单字段要参考现有 `登录基础数据_二级菜单`:
+
+- `path`
+- `componet`
+- `name`
+- `title`
+- `icon`
+- `账号代码`
+
+## 31. 开发实施顺序
+
+### 31.1 第一阶段:高优先级低风险
+
+1. 及时开工报表正选/反选。
+2. 机加已排产日期筛选。
+3. 机加已排产历史工时弹窗。
+4. 生产工艺自动带出标准工艺。
+5. 工时报表指派对象。
+6. 小字段:备注、提示、流转卡原料编码。
+
+交付标准:
+
+- 页面可查询。
+- 数据字段正确。
+- 导出同步。
+- 不影响现有流程。
+
+### 31.2 第二阶段:装配已排产专项
+
+1. 建装配扩展表。
+2. 做主查询页面。
+3. 加系统/非系统筛选。
+4. 接派工时间、合同交货期、发料齐套时间。
+5. 做装配完成时间和人工维护。
+6. 做当前人员、当前内容、历史说明。
+7. 接工时汇总和明细。
+8. 接异常明细。
+9. 做日计划导出打印。
+10. 派生质检任务早知道。
+
+交付标准:
+
+- 主表字段完整。
+- 人工维护有日志。
+- 工时可钻取。
+- 日计划可导出/打印。
+
+### 31.3 第三阶段:质量和齐套报表
+
+1. 及时检验报表。
+2. 生产日报-送检。
+3. 及时齐套跟踪结果报表。
+4. 让步接收记录。
+5. 外协任务检验字段补充。
+6. 产品合格率。
+
+交付标准:
+
+- 每个报表有明确口径。
+- 可导出。
+- 可钻明细。
+- SAP 来源显示同步时间。
+
+### 31.4 第四阶段:流程增强
+
+1. 异常管控统一入口。
+2. 生产订单批量关闭 SAP。
+3. 手续确认报表。
+4. 编程任务报工。
+5. 设计任务报工。
+
+交付标准:
+
+- 写操作有确认。
+- 批量结果逐条返回。
+- 异常审批人多选可见,一人处理。
+- 工时进入统一工时视图。
+
+### 31.5 第五阶段:设备采集和大屏
+
+1. 设备档案字段。
+2. 设备采集原始数据和任务绑定。
+3. OEE 指标明细。
+4. 工时异常报表。
+5. 质检、机加、装配现场大屏。
+
+交付标准:
+
+- 设备采集有原始数据和汇总数据。
+- OEE 可下钻。
+- 大屏指标无占位数据。
+- 缺勤有排班/实到来源。
+
+## 32. 测试清单
+
+### 32.1 页面测试
+
+每个页面至少测试:
+
+- 查询为空。
+- 单条件查询。
+- 多条件组合查询。
+- 日期边界。
+- 分页。
+- 导出。
+- 弹窗打开关闭。
+- 保存成功。
+- 保存失败。
+- 无权限访问。
+
+### 32.2 数据测试
+
+重点验证:
+
+- 工时汇总和明细一致。
+- 日期筛选包含当天 00:00:00 到 23:59:59。
+- 已关闭/已入库/已完成任务是否正确排除。
+- SAP 字段为空时页面不报错。
+- 多人员当前任务合并显示正确。
+- 班次跨天统计正确。
+
+### 32.3 性能测试
+
+重点过程:
+
+- 装配已排产需求主查询。
+- 及时齐套跟踪主表。
+- 机加班次产量表。
+- 及时检验报表。
+- 大屏过程。
+
+要求:
+
+- 普通报表分页查询 3 秒内返回。
+- 大屏过程 2 秒内返回。
+- 导出可慢,但必须有 loading。
+
+### 32.4 回归测试
+
+必须回归:
+
+- 机加排产。
+- 装配排产。
+- 加工中心报工。
+- 装配中心报工。
+- 质检收检。
+- 外协收检。
+- 工时统计。
+- 生产计划跟踪。
+- 看板刷新。
+
+## 33. 风险和处理
+
+| 风险 | 处理 |
+|---|---|
+| SAP 字段未到 | 页面字段先预留,显示空;存储过程字段从扩展表读取 |
+| 发料齐套时间口径不稳 | 标记来源字段:SAP/MES/人工 |
+| 设计工时依赖设计任务模块 | 装配已排产先显示装配/质量,设计为 0,模块完成后接入 |
+| 设备采集不稳定 | OEE 和工时异常先用人工工时降级口径,并标记数据来源 |
+| 缺勤无排班来源 | 不做缺勤正式统计,只做排班来源确认或新增排班表 |
+| 动态 SQL 注入风险 | 新增过程尽量参数化 |
+| 历史数据为空 | 字段允许 NULL,页面显示 `--`,不强行补假数据 |
+
+## 34. 交付物
+
+每个需求完成时至少交付:
+
+1. 前端页面或页面改造。
+2. SQL 增量脚本。
+3. 新增/修改存储过程。
+4. 菜单和权限配置说明。
+5. 测试数据和测试结果。
+6. 字段口径说明。
+7. 如涉及 SAP,提供 SAP 调用日志或同步状态。
+
diff --git a/analyze_mes_v20_views.js b/analyze_mes_v20_views.js
new file mode 100644
index 0000000..d189402
--- /dev/null
+++ b/analyze_mes_v20_views.js
@@ -0,0 +1,731 @@
+const fs = require('fs');
+const path = require('path');
+
+const root = path.resolve('MES_Manage_View_V20');
+const viewsRoot = path.join(root, 'src', 'views');
+const outFile = path.join(root, 'MES_Manage_View_V20_src_views_逐页面详细分析.md');
+
+function existingSqlFiles() {
+ return fs.readdirSync(root)
+ .filter(x => x.toLowerCase().endsWith('.sql'))
+ .map(x => path.join(root, x));
+}
+
+function readText(file) {
+ const buf = fs.readFileSync(file);
+ if (buf.length >= 2 && buf[0] === 0xff && buf[1] === 0xfe) return buf.slice(2).toString('utf16le');
+ if (buf.length >= 2 && buf[0] === 0xfe && buf[1] === 0xff) {
+ const swapped = Buffer.alloc(buf.length - 2);
+ for (let i = 2; i + 1 < buf.length; i += 2) {
+ swapped[i - 2] = buf[i + 1];
+ swapped[i - 1] = buf[i];
+ }
+ return swapped.toString('utf16le');
+ }
+ if (buf.length >= 3 && buf[0] === 0xef && buf[1] === 0xbb && buf[2] === 0xbf) return buf.slice(3).toString('utf8');
+ return buf.toString('utf8');
+}
+
+function walk(dir, exts, result = []) {
+ if (!fs.existsSync(dir)) return result;
+ for (const item of fs.readdirSync(dir, { withFileTypes: true })) {
+ const p = path.join(dir, item.name);
+ if (item.isDirectory()) walk(p, exts, result);
+ else if (exts.includes(path.extname(item.name).toLowerCase())) result.push(p);
+ }
+ return result;
+}
+
+function rel(file) {
+ return path.relative(root, file).replace(/\\/g, '/');
+}
+
+function lineOf(text, index) {
+ return text.slice(0, index).split(/\r?\n/).length;
+}
+
+function uniq(arr) {
+ return [...new Set((arr || []).filter(Boolean))];
+}
+
+function clean(s) {
+ return (s || '')
+ .replace(/<[^>]*>/g, '')
+ .replace(/\{\{.*?\}\}/g, '')
+ .replace(/\s+/g, ' ')
+ .trim();
+}
+
+function escapeReg(s) {
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+}
+
+function isInComment(text, index) {
+ const lineStart = text.lastIndexOf('\n', index) + 1;
+ const beforeOnLine = text.slice(lineStart, index);
+ if (/^\s*\/\//.test(beforeOnLine)) return true;
+ const blockStart = text.lastIndexOf('/*', index);
+ const blockEnd = text.lastIndexOf('*/', index);
+ return blockStart > blockEnd;
+}
+
+function moduleNameFromPath(relPath) {
+ const parts = relPath.split('/');
+ if (parts[0] !== 'src' || parts[1] !== 'views') return '其他';
+ if (parts.length <= 3) return parts[2] || '根页面';
+ return `${parts[2]} / ${parts.slice(3, -1).join(' / ') || path.basename(relPath)}`;
+}
+
+function extractLabels(text) {
+ const labels = [];
+ const patterns = [
+ /]*>([\s\S]*?)<\/el-button>/g,
+ /\blabel="([^"]+)"/g,
+ /\bplaceholder="([^"]+)"/g,
+ /\btitle="([^"]+)"/g
+ ];
+ for (const re of patterns) {
+ let m;
+ while ((m = re.exec(text))) {
+ const value = clean(m[1]);
+ if (value && !/[{};]/.test(value) && value.length <= 48) labels.push(value);
+ }
+ }
+ return uniq(labels).slice(0, 30);
+}
+
+function extractFrontendFields(text) {
+ const vModels = [];
+ const tableColumns = [];
+ const pushedParams = [];
+
+ let m;
+ const vmRe = /\bv-model(?:\.[\w-]+)?="([^"]+)"/g;
+ while ((m = vmRe.exec(text))) vModels.push(clean(m[1]));
+
+ const colRe = /]*)>/g;
+ while ((m = colRe.exec(text))) {
+ const attrs = m[1];
+ const label = /label="([^"]+)"/.exec(attrs)?.[1] || '';
+ const prop = /prop="([^"]+)"/.exec(attrs)?.[1] || '';
+ if (label || prop) tableColumns.push({ label: clean(label), prop: clean(prop) });
+ }
+
+ const paramRe = /\.(?:push|unshift)\s*\(\s*\[\s*['"]([^'"]+)['"]\s*,\s*([^\]\n]+)\]/g;
+ while ((m = paramRe.exec(text))) pushedParams.push({ name: clean(m[1]), expr: clean(m[2]) });
+
+ return {
+ vModels: uniq(vModels).slice(0, 80),
+ tableColumns: uniq(tableColumns.map(x => `${x.label}|${x.prop}`))
+ .map(x => {
+ const [label, prop] = x.split('|');
+ return { label, prop };
+ })
+ .slice(0, 120),
+ pushedParams: uniq(pushedParams.map(x => `${x.name}|${x.expr}`))
+ .map(x => {
+ const [name, expr] = x.split('|');
+ return { name, expr };
+ })
+ .slice(0, 120)
+ };
+}
+
+function extractMethods(text) {
+ const methods = [];
+ const methodBlock = /methods\s*:\s*\{([\s\S]*?)\n\s*\}/m.exec(text);
+ const src = methodBlock ? methodBlock[1] : text;
+ const re = /^\s*([A-Za-z_$\u4e00-\u9fa5][\w$\u4e00-\u9fa5]*)\s*\([^)]*\)\s*\{/gm;
+ let m;
+ while ((m = re.exec(src))) methods.push(m[1]);
+ return uniq(methods).slice(0, 30);
+}
+
+function extractCreateData(text) {
+ const calls = [];
+ const re = /CreateData\s*\(\s*['"]([^'"]+)['"]\s*,\s*['"]([^'"]+)['"]/g;
+ let m;
+ while ((m = re.exec(text))) {
+ if (isInComment(text, m.index)) continue;
+ calls.push({ type: m[1], name: m[2], line: lineOf(text, m.index) });
+ }
+ return calls;
+}
+
+function extractSapCalls(text) {
+ const calls = [];
+ const re = /(getB1|postB1|patchB1|posttm)\s*\(\s*([`'"])([\s\S]*?)\2/g;
+ let m;
+ while ((m = re.exec(text))) {
+ if (isInComment(text, m.index)) continue;
+ const snippet = text.slice(m.index, m.index + 1200);
+ const keys = [];
+ const keyRe = /["']?([\u4e00-\u9fa5A-Za-z_][\u4e00-\u9fa5A-Za-z0-9_$]*)["']?\s*:/g;
+ let km;
+ while ((km = keyRe.exec(snippet)) && keys.length < 24) {
+ if (!['if', 'for', 'while', 'switch', 'catch', 'function'].includes(km[1])) keys.push(km[1]);
+ }
+ calls.push({ fn: m[1], url: clean(m[3]), line: lineOf(text, m.index), payloadKeys: uniq(keys) });
+ }
+ return calls;
+}
+
+function extractApiImports(text) {
+ const imports = [];
+ const re = /from\s+['"](@\/api\/[^'"]+)['"]/g;
+ let m;
+ while ((m = re.exec(text))) imports.push(m[1]);
+ return uniq(imports);
+}
+
+function stripSqlComments(sql) {
+ return sql.replace(/\/\*[\s\S]*?\*\//g, ' ').replace(/--.*$/gm, ' ');
+}
+
+function compactSqlLine(s) {
+ return clean(s)
+ .replace(/\s*,\s*/g, ', ')
+ .replace(/\s*=\s*/g, ' = ')
+ .replace(/\s+/g, ' ')
+ .slice(0, 240);
+}
+
+function extractSqlLines(body, regex, limit) {
+ return uniq(stripSqlComments(body)
+ .split(/\r?\n/)
+ .map(x => x.trim())
+ .filter(x => regex.test(x))
+ .map(compactSqlLine))
+ .slice(0, limit);
+}
+
+function extractSelectColumns(body, limit = 10) {
+ const noComments = stripSqlComments(body);
+ const m = /\bSELECT\b([\s\S]{0,2500}?)\bFROM\b/i.exec(noComments);
+ if (!m) return [];
+ return uniq(m[1].split(/\r?\n/)
+ .map(x => compactSqlLine(x.replace(/^\s*,/, '')))
+ .filter(x => x && !/^(TOP|DISTINCT)$/i.test(x)))
+ .slice(0, limit);
+}
+
+function objectRegex(name) {
+ return new RegExp(`(?:\\[dbo\\]\\.\\[${escapeReg(name)}\\]|dbo\\.\\[${escapeReg(name)}\\]|\\[${escapeReg(name)}\\]|\\b${escapeReg(name)}\\b)`, 'i');
+}
+
+function extractParams(body) {
+ const header = body.split(/\bAS\b/i)[0] || body.slice(0, 1000);
+ const params = [];
+ const re = /@([\u4e00-\u9fa5A-Za-z0-9_]+)\s+([A-Za-z0-9_\[\]\(\),]+)/g;
+ let m;
+ while ((m = re.exec(header))) params.push(`@${m[1]} ${m[2]}`);
+ return uniq(params).slice(0, 24);
+}
+
+function summarizeProc(name, body) {
+ const verbs = [];
+ if (/\bSELECT\b/i.test(body)) verbs.push('查询');
+ if (/\bINSERT\s+INTO\b/i.test(body)) verbs.push('新增');
+ if (/\bUPDATE\b/i.test(body)) verbs.push('更新');
+ if (/\bDELETE\s+FROM\b/i.test(body)) verbs.push('删除');
+ if (/\bOPENJSON\b|JSON_VALUE|JSON_QUERY/i.test(body)) verbs.push('解析 JSON 批量数据');
+ if (/ROW_NUMBER|OFFSET|@PageSize|@PageCurrent/i.test(body)) verbs.push('分页');
+ if (/SAP\.SBO_YL/i.test(body)) verbs.push('读取 SAP B1 数据库对象');
+ if (/报工|工时/.test(name + body)) verbs.push('处理报工/工时');
+ if (/质检|检验|不合格/.test(name + body)) verbs.push('处理质检数据');
+ if (/排产|派工|计划/.test(name + body)) verbs.push('处理计划/派工');
+ if (/外协/.test(name + body)) verbs.push('处理外协业务');
+ return uniq(verbs).join('、') || '执行数据库业务逻辑';
+}
+
+function extractSqlModel() {
+ const sql = existingSqlFiles().map(f => `\n-- FILE: ${path.basename(f)}\n${readText(f)}`).join('\n');
+ const objects = [];
+ const objectRe = /CREATE\s+(TABLE|VIEW)\s+(?:\[dbo\]\.)?\[([^\]]+)\]/gi;
+ let m;
+ while ((m = objectRe.exec(sql))) objects.push({ type: m[1].toUpperCase(), name: m[2] });
+
+ const markers = [];
+ const procRe = /(?:CREATE|ALTER)\s+(?:\s*PROCEDURE|\s*PROC)\s+(?:\[dbo\]\.)?\[([^\]]+)\]/gi;
+ while ((m = procRe.exec(sql))) markers.push({ index: m.index, name: m[1] });
+
+ const procByName = {};
+ for (let i = 0; i < markers.length; i++) {
+ const start = markers[i].index;
+ const nextGo = sql.slice(start).search(/\r?\nGO\r?\n/i);
+ const nextProc = i + 1 < markers.length ? markers[i + 1].index : sql.length;
+ const end = nextGo >= 0 ? Math.min(start + nextGo, nextProc) : nextProc;
+ const body = sql.slice(start, end);
+ const deps = [];
+ for (const obj of objects) {
+ if (objectRegex(obj.name).test(body)) deps.push(`${obj.type}:${obj.name}`);
+ }
+ const sapRefs = uniq(body.match(/SAP\.SBO_YL\.dbo\.[A-Za-z0-9_]+/g) || []);
+ for (const s of sapRefs) deps.push(`SAP:${s}`);
+ const writes = [];
+ for (const obj of objects) {
+ const n = escapeReg(obj.name);
+ const hit = new RegExp(`\\b(INSERT\\s+INTO|UPDATE|DELETE\\s+FROM|MERGE\\s+INTO)\\s+(?:\\[dbo\\]\\.\\[${n}\\]|dbo\\.\\[${n}\\]|\\[${n}\\]|${n})`, 'i').test(body);
+ if (hit) writes.push(obj.name);
+ }
+ const noComments = stripSqlComments(body);
+ procByName[markers[i].name] = {
+ name: markers[i].name,
+ body,
+ params: extractParams(body),
+ deps: uniq(deps),
+ writes: uniq(writes),
+ sapRefs,
+ summary: summarizeProc(markers[i].name, body),
+ features: {
+ selectColumns: extractSelectColumns(body),
+ filters: extractSqlLines(body, /\b(WHERE|AND|OR)\b/i, 10),
+ updates: extractSqlLines(body, /\b(UPDATE|SET|INSERT\s+INTO|DELETE\s+FROM|MERGE\s+INTO)\b/i, 12),
+ statusLines: extractSqlLines(body, /状态|排产|派工|加工|任务|质检|关闭|完成|已报工|DocEntry|SAP/i, 18),
+ grouping: extractSqlLines(body, /\b(GROUP\s+BY|HAVING|ORDER\s+BY|ROW_NUMBER|OFFSET|FETCH\s+NEXT)\b/i, 8),
+ tempTables: uniq(noComments.match(/#[\u4e00-\u9fa5A-Za-z0-9_]+/g) || []).slice(0, 10),
+ hasJson: /\bOPENJSON\b|JSON_VALUE|JSON_QUERY/i.test(noComments),
+ hasTransaction: /\bBEGIN\s+TRAN|COMMIT\s+TRAN|ROLLBACK\s+TRAN/i.test(noComments),
+ hasTryCatch: /\bTRY\b|\bCATCH\b/i.test(noComments),
+ hasPagination: /@PageCurrent|@PageSize|ROW_NUMBER|OFFSET\s+.*FETCH/i.test(noComments),
+ hasSap: /SAP\.SBO_YL/i.test(noComments)
+ }
+ };
+ }
+ return { objects, procByName };
+}
+
+function operationByType(type) {
+ return {
+ '1': '旧版查询',
+ '2': '旧版增删改',
+ '7': '旧版批量/JSON',
+ '11': '当前通用存储过程查询/执行',
+ '12': '当前通用存储过程写入/业务动作',
+ '2001': '导出/报表类过程'
+ }[type] || `自定义类型 ${type}`;
+}
+
+function procPurpose(name) {
+ const rules = [
+ [/查询|Get|Load|Query|Search|读取|查看/, '查询/读取数据'],
+ [/新增|增加|Add|Insert/, '新增业务数据'],
+ [/修改|编辑|Edit|Update|更新|保存|Save/, '更新/保存业务数据'],
+ [/删除|Delete/, '删除业务数据'],
+ [/Finish|确认|关闭|Close/, '确认/关闭/完成业务动作'],
+ [/派工|Planned|排产|计划/, '计划排产或派工'],
+ [/报工|工时|WorkTime|ProdReport/, '报工或工时处理'],
+ [/质检|检验|Check|Quality/, '质量检验处理'],
+ [/外协/, '外协业务处理'],
+ [/登录|人员|角色|菜单|权限/, '基础权限/人员数据处理'],
+ [/设备|工位|资源|Station|Resource/, '设备/工位/资源数据处理']
+ ];
+ for (const [re, purpose] of rules) if (re.test(name)) return purpose;
+ return '业务存储过程调用';
+}
+
+function businessDomainFromName(name) {
+ const rules = [
+ [/登录|人员|角色|菜单|权限|部门|工位管理/, '系统基础资料/权限'],
+ [/设备|报警|CaChe|StationStatus|工位设备/, '设备与实时数据'],
+ [/排产|派工|OrderPlanned|计划/, '计划排产'],
+ [/ProductCenter|ProductTask|加工中心|装配中心|报工|收料|开工|退库|发料|工时/, '生产执行/工时'],
+ [/外协/, '外协协同'],
+ [/图纸|工艺|Process|Drawing|Procedure|产品结构|标准/, '工艺与图纸'],
+ [/质检|检验|质量|不合格|Check/, '质量检验'],
+ [/销售/, '销售/订单状态'],
+ [/班次/, '班次与人员排班'],
+ [/加急/, '加急任务']
+ ];
+ for (const [re, domain] of rules) if (re.test(name)) return domain;
+ return '通用业务';
+}
+
+function pageBusinessNarrative(page) {
+ const p = page.relPath;
+ const labels = page.labels.slice(0, 10);
+ const procNames = uniq(page.procs.map(x => x.name)).slice(0, 6);
+ const common = [];
+ if (labels.length) common.push(`页面可见的操作/字段包括 ${labels.map(x => `“${x}”`).join('、')},这些是判断页面业务范围的主要线索。`);
+ if (procNames.length) common.push(`后端主要围绕 ${procNames.map(x => `\`${x}\``).join('、')}${page.procs.length > 6 ? ' 等过程' : ''} 取数或提交业务动作。`);
+ const rules = [
+ [/PunctualWork/, '该页面用于发现“计划应开工但现场未及时开工”的任务。用户按任务状态、工位、工序、异常原因等条件筛选,页面展示生产订单、销售订单、物料、工序、指派对象、分配数量、上序合格数、加工/收料状态和加急数量,服务于生产异常提醒和催办。'],
+ [/CraftMaintain/, '该页面维护产品结构树。用户可以新增、编辑、删除产品结构节点,查询现有产品结构,并为后续工艺、物料、BOM 或生产任务提供层级基础数据。'],
+ [/DeviceDataHistory/, '该页面查询设备历史采集数据。用户按工位/设备、参数名称、起止时间分页查询历史记录,用于追溯设备运行参数。'],
+ [/DeviceInformation/, '该页面维护设备台账和设备相关基础资料。用户可以查询设备信息,新增、修改、删除设备,维护维修记录、工时记录或点检配置。'],
+ [/DeviceRealData/, '该页面展示设备实时或近实时数据。用户按工位和数据类型查询当前采集值、统计值或状态,通常用于设备运行监控。'],
+ [/Devicedata/, '该页面面向设备采集数据管理,查询缓存变量和设备数据,支持按设备、参数、时间等维度查看实时或历史采集结果。'],
+ [/InstallMakePlanDo|InstallMakePlan/, '该页面处理装配件排产。用户查询待排产/已排产装配任务,选择工位和人员,修改计划开工/完成时间、优先级、说明,执行单条派工、二次派工、批量确认排产,并在确认时同步 SAP 生产订单状态或创建相关请求。'],
+ [/SelfMakePlandone|SelfMakePlando|SelfMakePlan/, '该页面处理自制件/机加件排产。用户查询生产订单工序任务,筛选订单、合同、物料、工序、计划时间、排产状态和指派对象,完成工位派工、人员二次派工、外协分组、计划说明维护和排产确认。'],
+ [/PlannOrderClose/, '该页面用于生产订单关闭。用户筛选 SAP 未关闭或 MES 待确认订单,执行 SAP 生产订单关闭,并调用 MES 过程记录或确认订单关闭状态。'],
+ [/PlanShell/, '该页面是计划外壳/计划跟踪类页面,按缺料、发货通知、完成状态等条件查询计划或装配相关任务,用于计划人员集中查看订单齐套、发货和关闭情况。'],
+ [/priorityinstall/, '该页面用于装配优先级调整。用户查询装配计划任务,修改计划开工/完成时间、派工对象或优先级,用于插单和重点订单排序。'],
+ [/PartDrawing/, '该页面维护零件图纸。用户按物料查询图纸、查看文件、验证名称、删除图纸并保留删除记录。'],
+ [/PartDrawlook/, '该页面偏只读查看零件图纸,通常给生产或质检人员按物料快速打开图纸。'],
+ [/ProcurementDrawlook/, '该页面提供采购场景的图纸查看。用户按采购物料查询图纸,查看或删除图纸记录,支持采购、外协、来料检验时查阅技术文件。'],
+ [/ProcessDrawing/, '该页面维护工艺图纸。用户按物料/工艺条件查询工艺图,验证名称,查看或删除工艺图纸。'],
+ [/ProcedureManagement/, '该页面维护工序/路线阶段主数据。新增或修改工序时会同步 SAP B1 的 RouteStages,保证 MES 工序与 SAP 工艺阶段一致。'],
+ [/ProcessesDevelop/, '该页面用于工艺开发和生产工艺路线维护。用户围绕物料、工序、顺序、工艺参数、图纸等信息维护标准或生产用工艺路线。'],
+ [/ProcessInquiry/, '该页面用于工艺路线查询。用户按物料、产品、工序等条件查看标准工艺或生产工艺,服务于排产、生产和质检核对。'],
+ [/ProcessProduct/, '该页面维护产品与工艺/图纸的关系,使产品、物料、工序、图纸等基础数据形成可查询的工艺资料。'],
+ [/ProductionOrder/, '该页面处理生产订单资料。用户查询 ERP/MES 生产订单,并通过过程把 SAP 生产订单同步成 MES 生产计划和工序任务。'],
+ [/MachiningCenter/, '该页面是机加生产执行主界面。操作员按工位查看可执行任务和进行中任务,完成收料、开始加工、暂停/恢复、报工、辅助工序、首检/巡检/序检、生产发料查询、退库、图纸查看等动作,并写入加工中心操作记录和报工记录。'],
+ [/AssemblyCenter/, '该页面是装配生产执行主界面。装配人员按工位查询装配任务,处理开工、结束/报工、送检、缺料查询、收检记录、图纸查看和退库等动作,并会读取 SAP 生产订单信息辅助判断。'],
+ [/AssembleTaskManage/, '该页面管理装配任务。用户查询装配生产任务、查看工时汇总,进行派工、任务说明维护和外协/生产通知。'],
+ [/ProductionTaskManage/, '该页面管理生产任务。用户按工位、订单、工序查询任务,查看工时汇总,执行派工、修改计划字段、维护任务说明和通知状态。'],
+ [/ProductionTasksSum/, '该页面做生产任务汇总。用户按订单、工序、工位、时间等维度查询生产任务和工时汇总,并可编辑计划说明或派工信息。'],
+ [/ProductionPlanTrack/, '该页面跟踪生产计划执行。用户查看计划任务的开工、完工、进度、延期或异常情况,用于计划员追踪订单。'],
+ [/AssemblePlanTrack/, '该页面跟踪装配计划执行,展示装配任务进度、计划完成情况和异常。'],
+ [/AssembleTasksSum/, '该页面汇总装配任务,按订单、工位、人员或状态统计装配执行情况。'],
+ [/AssembleStatus/, '该页面查看装配现场状态,重点展示进行中任务和当前工位状态。'],
+ [/DeviceStatus/, '该页面查看设备/工位生产状态,通常用于判断设备是否在加工、待机、报警或空闲。'],
+ [/OutsourceMange/, '该页面管理外协任务。用户查询可外协工序,按供应商/订单/工序分组,执行外协派工并同步 SAP 采购请求或相关单据,维护外协发料、收料、质检记录、打印次数和备注。'],
+ [/SaleOutsourceMange/, '该页面管理销售相关外协任务。业务与外协管理类似,但面向销售订单或销售外协场景,包含派工同步 SAP、外协发料、历史记录和质检记录查询。'],
+ [/OutsourceSheet/, '该页面输出外协报表。用户按订单、合同、产品、计划号、供应商、状态分页查询外协报表,也可导出,用于外协进度、发料、收料、质检、开票等跟踪。'],
+ [/PickingTask/, '该页面处理拣配任务。用户查询生产订单相关拣配/领料任务,并可能联动 SAP 生产订单或库存出入库状态。'],
+ [/PersonnelBinding/, '该页面维护工位与人员绑定关系。用于限制或提示哪些人员可在指定工位报工、开工或执行任务。'],
+ [/ShiftManagement/, '该页面维护班次和班次人员绑定。用户新增、修改、删除班次,给班次绑定人员,为工时计算和报工归属提供基础数据。'],
+ [/Timesheet/, '该页面统计工时。用户按项目、作业者、设备、任务等维度查询工时数据,并可导出报表。'],
+ [/WorkHoursCheck/, '该页面用于工时校验。质检/管理人员查询待校验报工记录,系统计算标准/实际工时,保存单条或批量校验结果;通过校验后可把工序资源或工时/出库类单据上传到 SAP。'],
+ [/WorkhoursEdit/, '该页面用于编辑已产生的工时/报工记录,修正异常工时或补录说明。'],
+ [/Workhours/, '该页面用于工时查询,按工序、人员、设备、时间等条件查看工序工时。'],
+ [/Urgent/, '该页面维护加急任务。用户查询加急订单明细,并更新完成、转至等字段,用于生产/质检优先级管理。'],
+ [/AbnormalControl/, '该页面管理生产异常。用户查询异常记录,推进异常处理、关闭或记录处理结果。'],
+ [/login/, '该页面是登录入口,负责账号密码登录、角色选择、Token/Cookie 写入、路由权限初始化和密码修改入口。'],
+ [/dashboard/, '该页面是系统首页或欢迎页,用于承载看板入口或基础统计展示。'],
+ [/404/, '该页面是 404 错误页,用于路由不存在时展示错误信息。'],
+ [/purchaseIncoming/, '该页面处理采购/来料检验。质检人员查询待检任务,查看图纸、字典和检验人员,录入合格数、不合格数、检测数据,调用过程更新来料检验结果。'],
+ [/processReceive/, '该页面处理序检收检。质检人员接收工序检验任务,查询加急和字典信息,读取 SAP 工序状态,新增序检收检记录。'],
+ [/processSpecial/, '该页面处理过程/特殊检验。用户按工序任务录入特殊检验结果和检测明细。'],
+ [/firstSpecial/, '该页面处理首检/特殊首件检验。用户按工序和工位选择任务,录入首检数据。'],
+ [/final/, '该页面处理终检任务。质检人员查询终检任务,录入终检结果,并预留 SAP 生产订单状态回写。'],
+ [/materialReturn/, '该页面处理原材料退库检验。用户查询退库检验任务,读取 SAP 生产订单辅助信息,录入并更新检验结果。'],
+ [/salesReturn/, '该页面处理销售退货检验。用户查询销售退货待检任务,录入检验结果并更新质检记录。'],
+ [/OtherInbound/, '该页面处理其他入库检验。用户查询其他入库待检任务,录入检验结果并更新质检记录。'],
+ [/QualityCenter/, '该页面是质量综合处理中心。用户集中查询质检任务、收检记录、终检/入库任务和质检明细,录入检测数据、不合格处理,并读取 SAP 生产订单信息辅助校验。'],
+ [/QualityDetails/, '该页面查询质检明细。用户按订单、物料、检验类型、时间等条件查询和导出检测明细。'],
+ [/QualityStandardQuery/, '该页面维护/查询检验标准。用户按物料、工序、检测项目维护理论值、上下限、量具和启用状态。'],
+ [/Unqualified/, '该页面处理不合格品。用户查询不合格记录,查看处置流程和节点,推进让步放行、返工、报废等处理。'],
+ [/saleorderstatus/, '该页面查询销售订单状态。用户按销售订单、合同、物料等条件查看销售订单在 SAP/MES 中的履约、生产、发货或关闭状态。'],
+ [/MenuManagement/, '该页面维护菜单和权限。管理员查询、增加、修改、删除菜单/角色权限配置,用于动态路由和按钮权限。'],
+ [/OperationLog/, '该页面查询操作日志或报工操作记录,用于审计用户操作和追踪异常。'],
+ [/OrgManagement/, '该页面维护组织/角色类基础数据,支持新增、修改、删除和查询。'],
+ [/PersonnelManagement/, '该页面维护人员账号。管理员维护人员、部门、角色、启用状态等登录和权限基础数据。'],
+ [/SystemRrolemaintenance/, '该页面维护系统角色。管理员查询、新增、修改、删除角色数据。'],
+ [/WorkstationManagement/, '该页面维护工位资源。管理员在 MES 侧新增、修改、删除工位,同时调用 SAP B1 Resources 新增或更新资源主数据。'],
+ [/layout|Sidebar|Navbar|TagsView|AppMain|ResizeHandler/, '该文件属于系统布局组件,负责菜单、导航、标签页、内容承载或窗口自适应,不承载具体业务流程。'],
+ [/login/, '该页面是登录入口,负责账号密码登录、角色选择、Token/Cookie 写入、路由权限初始化和密码修改入口。'],
+ [/dashboard/, '该页面是系统首页或欢迎页,用于承载看板入口或基础统计展示。'],
+ [/404/, '该页面是 404 错误页,用于路由不存在时展示错误信息。']
+ ];
+ const found = rules.find(([re]) => re.test(p));
+ return [found ? found[1] : '该页面为业务页面,具体功能由表单字段、按钮和存储过程调用共同决定。', ...common].join('\n\n');
+}
+
+function pageFlow(page) {
+ const p = page.relPath;
+ const flows = [
+ [/SelfMakePlandone|SelfMakePlando|SelfMakePlan/,
+ '自制/机加排产流程:\n\n```mermaid\nflowchart LR\n A[输入订单/合同/物料/工序/时间条件] --> B[调用 MES_OrderPlanned_Query 查询待排产任务]\n B --> C[选择任务行]\n C --> D{派工方式}\n D --> E[MES_OrderPlanned_SingleTaskPlanned 单任务派工]\n D --> F[计划排产_二次派工 绑定人员]\n D --> G[计划排产_外协分组]\n E --> H[MES_OrderPlanned_EditTaskPlanned/DescriptionEdit 修改计划字段]\n F --> H\n G --> H\n H --> I[MES_OrderPlanned_Finish 确认排产]\n I --> J[PATCH /ProductionOrders(...) 回写 SAP 生产订单状态]\n J --> K[页面刷新任务状态]\n```\n'],
+ [/InstallMakePlanDo|InstallMakePlan/,
+ '装配排产流程:\n\n```mermaid\nflowchart LR\n A[输入订单/合同/物料/工序/时间条件] --> B[MES_OrderPlanned_Queryzp 查询装配任务]\n B --> C[选择任务]\n C --> D[Prog_MES_LoadStations 选择工位]\n D --> E[系统管理_人员管理_所有有效人员查询 选择人员]\n E --> F[计划排产_派工 或 MES_OrderPlanned_SingleTaskPlanned]\n F --> G[计划排产_二次派工]\n G --> H[MES_OrderPlanned_Finish 确认排产]\n H --> I[PATCH /ProductionOrders(...) 或 POST SAP 请求]\n I --> J[刷新排产结果]\n```\n'],
+ [/MachiningCenter/,
+ '机加中心执行流程:\n\n```mermaid\nflowchart LR\n A[选择工位/人员/任务条件] --> B[MES_ProductCenter_GetTask 查询可执行任务]\n B --> C[选择任务]\n C --> D[收料 MES_ProductTask_MaterialRecieved]\n D --> E[开始加工 MES_ProductTask_WorkingStart]\n E --> F{生产过程}\n F --> G[暂停/恢复 MES_ProductTask_Rest]\n F --> H[首检/巡检/序检]\n F --> I[报工 MES_ProductTask_ProdReport]\n I --> J[写入操作记录/报工记录]\n J --> K[必要时读取 SAP ProductionOrders]\n K --> L[刷新任务和进行中状态]\n```\n'],
+ [/AssemblyCenter/,
+ '装配中心执行流程:\n\n```mermaid\nflowchart LR\n A[选择工位/人员] --> B[MES_ProductCenter_GetTask_zp 查询装配任务]\n B --> C[查看缺料/图纸/工序]\n C --> D[MES_人员工时记录_开始 或生产任务开始]\n D --> E[执行装配]\n E --> F[MES_ProductTask_SendInspection 送检/报工]\n F --> G[收检记录/质检数据]\n G --> H[读取 SAP ProductionOrders 辅助判断]\n H --> I[刷新装配任务]\n```\n'],
+ [/WorkHoursCheck/,
+ '工时校验流程:\n\n```mermaid\nflowchart LR\n A[选择人员/订单/工序/日期] --> B[工时校验_获取昨日之前操作记录列表]\n B --> C[选择待校验记录]\n C --> D[usp_ValidateWorkTime 或 usp_BatchValidateWorkTime 计算工时]\n D --> E{校验结果}\n E --> F[工时校验_保存校验记录]\n E --> G[工时校验_批量保存校验记录]\n F --> H[更新校验状态]\n G --> H\n H --> I[GET/PATCH /ProductionOrders 补工序资源]\n I --> J[POST /InventoryGenExits 上传 SAP]\n J --> K[标记已上传/失败留痕]\n```\n'],
+ [/purchaseIncoming|processReceive|processSpecial|firstSpecial|final|materialReturn|salesReturn|OtherInbound|QualityCenter/,
+ '质检业务流程:\n\n```mermaid\nflowchart LR\n A[选择检验类型/订单/物料/工序] --> B[查询待检任务]\n B --> C[查看图纸/字典/检验人员]\n C --> D[录入检测数量、合格数、不合格数和检测明细]\n D --> E{检验类型}\n E --> F[来料/入库/退货检验更新]\n E --> G[序检收检新增或更新]\n E --> H[终检/首检/特殊检验保存]\n F --> I[写入 YL_质量检验_质检记录及明细]\n G --> I\n H --> I\n I --> J[必要时查询/回写 SAP 生产订单或单据]\n```\n'],
+ [/OutsourceMange|SaleOutsourceMange|OutsourceSheet/,
+ '外协业务流程:\n\n```mermaid\nflowchart LR\n A[输入订单/合同/供应商/工序条件] --> B[查询外协任务或外协报表]\n B --> C[选择外协工序/供应商]\n C --> D[计划排产_派工 或外协分组]\n D --> E[POST SAP 采购/外协相关请求]\n E --> F[外协发料查询/保存]\n F --> G[外协收料/质检记录]\n G --> H[更新打印次数/备注/报表状态]\n```\n']
+ ];
+ const found = flows.find(([re]) => re.test(p));
+ if (found) return found[1];
+ if (!page.procs.length) return '该页面未发现后端存储过程调用,通常不形成完整业务流程。';
+ const procChain = uniq(page.procs.map(x => x.name)).slice(0, 8).map(x => `\`${x}\``).join(' -> ');
+ return `通用流程:用户输入查询/维护条件 -> 调用 ${procChain} -> 后端读写 SQL Server -> 页面刷新表格或弹窗结果。`;
+}
+
+function fieldMappingTable(page) {
+ const rows = [
+ '| 类型 | 前端字段/显示列 | 存储过程参数或表达式 | SQL返回字段/数据库字段线索 |',
+ '|---|---|---|---|'
+ ];
+ const mappings = [];
+ for (const p of page.fields.pushedParams || []) {
+ const relatedProc = page.procs.find(proc => proc.sqlInfo?.params?.some(x => x.includes(`@${p.name}`)));
+ const sqlField = relatedProc?.sqlInfo?.features?.selectColumns?.find(x => x.includes(p.name)) || '';
+ mappings.push(['参数', p.expr, `@${p.name}`, sqlField]);
+ }
+ for (const c of page.fields.tableColumns || []) {
+ if (!c.label && !c.prop) continue;
+ const sqlField = page.procs
+ .flatMap(proc => proc.sqlInfo?.features?.selectColumns || [])
+ .find(x => (c.prop && x.includes(c.prop)) || (c.label && x.includes(c.label))) || '';
+ mappings.push(['显示列', c.label || c.prop, '', sqlField || c.prop || c.label]);
+ }
+ for (const v of page.fields.vModels || []) {
+ const name = v.split('.').pop();
+ const param = page.fields.pushedParams.find(x => x.expr.includes(v) || x.expr.includes(name));
+ mappings.push(['输入字段', v, param ? `@${param.name}` : '', '']);
+ }
+ const uniqRows = uniq(mappings.map(x => x.join('|'))).slice(0, 40);
+ if (!uniqRows.length) return '未从页面模板中抽取到明确的 `v-model`、表格列或 `param.push` 字段映射。';
+ for (const item of uniqRows) {
+ const [type, front, param, sql] = item.split('|');
+ rows.push(`| ${type} | \`${front || '-'}\` | ${param ? `\`${param}\`` : '-'} | ${sql ? `\`${sql}\`` : '-'} |`);
+ }
+ return rows.join('\n');
+}
+
+function statusFlow(page) {
+ const lines = [];
+ for (const proc of page.procs) {
+ const info = proc.sqlInfo;
+ if (!info) continue;
+ const status = info.features.statusLines || [];
+ const useful = status.filter(x => /状态|排产|派工|加工|任务|质检|关闭|完成|已报工|DocEntry|SAP/i.test(x));
+ if (useful.length) lines.push(`- \`${proc.name}\`:${useful.slice(0, 8).map(x => `\`${x}\``).join(';')}`);
+ }
+ if (!lines.length) return '未从当前 SQL 静态解析到明确的状态字段变更。';
+ return lines.join('\n');
+}
+
+function sapInterfaceList(page) {
+ if (!page.sapCalls.length) return '页面无直接 SAP Service Layer 调用。';
+ const lines = ['| 行号 | 方法 | 接口 | 调用时机/影响判断 | 提交字段线索 |', '|---:|---|---|---|---|'];
+ for (const c of page.sapCalls) {
+ const timing = c.url.includes('ProductionOrders')
+ ? '读取或更新 SAP 生产订单。失败会导致 MES 与 SAP 订单状态/工序资源不同步。'
+ : c.url.includes('InventoryGenExits')
+ ? '上传库存发出/工时相关单据。失败会导致 SAP 未生成对应业务单据。'
+ : c.url.includes('Resources')
+ ? '新增或更新 SAP 资源主数据。失败会导致 MES 工位与 SAP 资源不一致。'
+ : c.url.includes('RouteStages')
+ ? '新增或更新 SAP 工序阶段。失败会导致 MES 工序与 SAP RouteStages 不一致。'
+ : '调用外部接口,失败会影响对应外部系统同步。';
+ lines.push(`| ${c.line} | \`${c.fn}\` | \`${c.url}\` | ${timing} | ${mdList(c.payloadKeys.slice(0, 12))} |`);
+ }
+ return lines.join('\n');
+}
+
+function dbCallChain(page) {
+ if (!page.procs.length) return '无数据库调用链。';
+ const lines = [];
+ for (const proc of page.procs) {
+ const deps = proc.sqlInfo ? depsMd(proc.sqlInfo.deps) : '过程体未解析';
+ const result = proc.purpose;
+ lines.push(`- \`${page.relPath}\` -> \`${proc.name}\` -> ${deps} -> ${result}`);
+ }
+ return lines.join('\n');
+}
+
+function mdList(items, empty = '无') {
+ if (!items || !items.length) return empty;
+ return items.map(x => `\`${x}\``).join('、');
+}
+
+function depsMd(deps) {
+ if (!deps || !deps.length) return '未在 SQL 中解析到明确依赖';
+ const tables = deps.filter(x => x.startsWith('TABLE:')).map(x => x.slice(6));
+ const views = deps.filter(x => x.startsWith('VIEW:')).map(x => x.slice(5));
+ const sap = deps.filter(x => x.startsWith('SAP:')).map(x => x.slice(4));
+ const parts = [];
+ if (tables.length) parts.push(`表:${mdList(tables)}`);
+ if (views.length) parts.push(`视图:${mdList(views)}`);
+ if (sap.length) parts.push(`SAP/链接库:${mdList(sap)}`);
+ return parts.join(';') || '未在 SQL 中解析到明确依赖';
+}
+
+function explainProc(proc) {
+ const info = proc.sqlInfo;
+ const lines = [];
+ lines.push(`\`${proc.name}\` 属于 ${businessDomainFromName(proc.name)},前端以 type=${proc.type}(${proc.op})调用,页面用途是 ${proc.purpose}。`);
+ if (!info) {
+ lines.push('当前工作区 SQL 中没有解析到该过程体,因此只能确认前端调用关系,不能准确说明内部读写表、过滤条件和状态变更。');
+ return lines.join(' ');
+ }
+ const f = info.features;
+ lines.push(`数据库处理摘要:${info.summary}。`);
+ lines.push(`读取/关联对象:${depsMd(info.deps)}。`);
+ if (info.writes.length) lines.push(`写入或更新对象:${mdList(info.writes)}。`);
+ if (info.params.length) lines.push(`入参/分页参数:${mdList(info.params)}。`);
+ if (f.selectColumns.length) lines.push(`主要返回字段线索:${mdList(f.selectColumns)}。`);
+ if (f.filters.length) lines.push(`关键过滤/匹配条件:${f.filters.map(x => `\`${x}\``).join(';')}。`);
+ if (f.updates.length) lines.push(`关键写入/状态处理语句线索:${f.updates.map(x => `\`${x}\``).join(';')}。`);
+ if (f.grouping.length) lines.push(`排序/分组/分页线索:${f.grouping.map(x => `\`${x}\``).join(';')}。`);
+ const flags = [];
+ if (f.hasPagination) flags.push('分页');
+ if (f.hasJson) flags.push('JSON 批量解析');
+ if (f.hasTransaction) flags.push('事务控制');
+ if (f.hasTryCatch) flags.push('异常捕获');
+ if (f.hasSap) flags.push('读取 SAP 数据库对象');
+ if (f.tempTables.length) flags.push(`临时表 ${mdList(f.tempTables)}`);
+ if (flags.length) lines.push(`实现特征:${flags.join('、')}。`);
+ return lines.join(' ');
+}
+
+function procTable(procs) {
+ if (!procs.length) return '未发现 `CreateData(...)` 存储过程调用。';
+ const rows = [
+ '| 行号 | type | 存储过程 | 前端用途 | SQL摘要 | 依赖对象 |',
+ '|---:|---|---|---|---|---|'
+ ];
+ for (const p of procs) {
+ const info = p.sqlInfo;
+ rows.push(`| ${p.line} | ${p.type}(${p.op}) | \`${p.name}\` | ${p.purpose} | ${info ? info.summary : '未找到过程体'} | ${info ? depsMd(info.deps) : '未知'} |`);
+ }
+ return rows.join('\n');
+}
+
+function sapTable(calls, procs) {
+ const procSap = [];
+ for (const p of procs) {
+ if (p.sqlInfo && p.sqlInfo.sapRefs.length) procSap.push(`${p.name}: ${p.sqlInfo.sapRefs.join(', ')}`);
+ }
+ if (!calls.length && !procSap.length) return '未发现页面直接调用 SAP B1 Service Layer;相关存储过程也未解析到 `SAP.SBO_YL` 链接库引用。';
+ const lines = [];
+ if (calls.length) {
+ lines.push('页面直接 SAP/接口调用:');
+ for (const c of calls) lines.push(`- 第 ${c.line} 行:\`${c.fn}("${c.url}")\``);
+ }
+ if (procSap.length) {
+ lines.push('存储过程内读取 SAP B1 数据库对象:');
+ for (const s of procSap) lines.push(`- ${s}`);
+ }
+ return lines.join('\n');
+}
+
+function techFlags(page) {
+ const flags = ['Vue 2 Options API', 'Element UI'];
+ if (page.procs.length) flags.push('`CreateData` + `ExecDatabase` 调用后端过程');
+ else flags.push('未发现页面级后端存储过程调用');
+ if (page.sapCalls.length) flags.push('SAP B1 Service Layer(`src/api/b1s.js`)');
+ if (page.imports.includes('@/api/tm') || page.sapCalls.some(x => x.fn === 'posttm')) flags.push('TM/OpenAPI 接口(`src/api/tm.js`)');
+ if (page.hasEcharts) flags.push('ECharts');
+ if (page.hasFullCalendar) flags.push('FullCalendar');
+ if (page.hasMqtt) flags.push('MQTT');
+ if (page.hasPdf) flags.push('PDF/图纸预览或导出');
+ if (page.hasExcel) flags.push('Excel/报表导出');
+ return flags;
+}
+
+function moduleSummary(pages) {
+ const groups = {};
+ for (const p of pages) {
+ const top = p.relPath.split('/')[2] || '其他';
+ groups[top] ||= [];
+ groups[top].push(p);
+ }
+ const lines = ['| 模块 | 页面数 | 主要能力 |', '|---|---:|---|'];
+ for (const [mod, ps] of Object.entries(groups)) {
+ const allProcs = uniq(ps.flatMap(p => p.procs.map(x => x.name)));
+ const allSap = ps.reduce((n, p) => n + p.sapCalls.length, 0);
+ const ability = [];
+ if (allProcs.length) ability.push(`过程 ${allProcs.length} 个`);
+ if (allSap) ability.push(`直接 SAP 调用 ${allSap} 处`);
+ if (ps.some(p => p.hasEcharts)) ability.push('图表');
+ if (ps.some(p => p.hasMqtt)) ability.push('设备实时通信');
+ lines.push(`| ${mod} | ${ps.length} | ${ability.join('、') || '布局/静态页面'} |`);
+ }
+ return lines.join('\n');
+}
+
+const sqlModel = extractSqlModel();
+const pages = walk(viewsRoot, ['.vue', '.js']).sort().map(file => {
+ const text = readText(file);
+ const relPath = rel(file);
+ const procs = extractCreateData(text).map(p => ({
+ ...p,
+ op: operationByType(p.type),
+ purpose: procPurpose(p.name),
+ sqlInfo: sqlModel.procByName[p.name]
+ }));
+ const page = {
+ file,
+ relPath,
+ module: moduleNameFromPath(relPath),
+ lines: text.split(/\r?\n/).length,
+ labels: extractLabels(text),
+ methods: extractMethods(text),
+ fields: extractFrontendFields(text),
+ procs,
+ sapCalls: extractSapCalls(text),
+ imports: extractApiImports(text),
+ hasMqtt: /mqtt|MQTT/.test(text),
+ hasEcharts: /echarts|ECharts|\$echarts/.test(text),
+ hasFullCalendar: /FullCalendar|@fullcalendar/.test(text),
+ hasPdf: /pdfjs|jspdf|html2canvas|pdf/i.test(text),
+ hasExcel: /xlsx|exportExcel|Excel|导出/.test(text)
+ };
+ page.functionDesc = pageBusinessNarrative(page);
+ return page;
+});
+
+const now = new Date().toISOString().slice(0, 10);
+let md = '';
+md += `# MES_Manage_View_V20 src/views 逐页面详细分析\n\n`;
+md += `生成日期:${now}\n\n`;
+md += `分析范围:\`MES_Manage_View_V20/src/views\` 下 ${pages.length} 个 .vue/.js 文件;辅助读取 \`src/api\`、\`config/index.js\`、\`static/config.js\` 和 SQL 脚本。\n\n`;
+md += `## 重要说明\n\n`;
+md += `- 页面功能不是只按文件名猜测,报告同时参考页面路径、按钮/字段文案、方法名、存储过程名和 SQL 过程体。\n`;
+md += `- 存储过程处理说明来自 SQL 静态解析:读写对象、参数、返回字段、过滤条件、更新语句、分组分页、JSON、事务、SAP 链接库等。\n`;
+md += `- 如果某个过程没有解析到过程体,报告会明确说明不能准确判断内部表/视图和处理逻辑。\n\n`;
+
+md += `## 技术路线总览\n\n`;
+md += `- 前端:Vue 2.7、Vue Router、Vuex、Element UI,页面以 Options API 为主。\n`;
+md += `- 数据访问:页面调用 \`this.CreateData(type, procedureName, params[, pageSize, pageCurrent])\` 组包,再调用 \`this.ExecDatabase(data)\` 访问后端。\n`;
+md += `- 后端:统一接口接收 type/name/param,执行 SQL Server 存储过程,返回 \`response.data\` 和分页信息。\n`;
+md += `- 数据库:\`YL_MESDB\`,核心对象包括生产计划/生产任务、加工中心操作记录、质检记录、设备数据、权限菜单等表和视图。\n`;
+md += `- SAP:\`src/api/b1s.js\` 通过 \`window.dt_Config.baseURLB1\` 访问 B1 Service Layer;部分过程也跨库读取 \`SAP.SBO_YL.dbo.*\`。\n\n`;
+
+md += `## 模块汇总\n\n${moduleSummary(pages)}\n\n`;
+
+const allSapCalls = [];
+for (const p of pages) {
+ for (const c of p.sapCalls) allSapCalls.push({ page: p.relPath, ...c });
+}
+if (allSapCalls.length) {
+ md += `## SAP 交互总清单\n\n`;
+ md += `| 页面 | 行号 | 方法 | 接口 | 提交字段线索 |\n|---|---:|---|---|---|\n`;
+ for (const c of allSapCalls) {
+ md += `| \`${c.page}\` | ${c.line} | \`${c.fn}\` | \`${c.url}\` | ${mdList(c.payloadKeys.slice(0, 10))} |\n`;
+ }
+ md += `\n`;
+}
+
+for (let i = 0; i < pages.length; i++) {
+ const p = pages[i];
+ md += `## ${i + 1}. ${p.relPath}\n\n`;
+ md += `**页面位置**:\`${p.relPath}\` \n`;
+ md += `**所属模块**:${p.module} \n`;
+ md += `**源码行数**:${p.lines}\n\n`;
+ md += `### 页面功能\n\n${p.functionDesc}\n\n`;
+ md += `### 页面流程图\n\n${pageFlow(p)}\n\n`;
+ md += `### 用户操作与页面线索\n\n`;
+ md += `可见按钮/字段线索:${mdList(p.labels.slice(0, 18))}\n\n`;
+ md += `主要方法线索:${mdList(p.methods.slice(0, 18))}\n\n`;
+ md += `### 字段映射表\n\n${fieldMappingTable(p)}\n\n`;
+ md += `### 技术路线\n\n${techFlags(p).map(x => `- ${x}`).join('\n')}\n\n`;
+ md += `### 调用的存储过程总览\n\n${procTable(p.procs)}\n\n`;
+ md += `### 与 SAP 的交互\n\n${sapTable(p.sapCalls, p.procs)}\n\n`;
+ md += `### SAP 接口级说明\n\n${sapInterfaceList(p)}\n\n`;
+ md += `### 状态流转说明\n\n${statusFlow(p)}\n\n`;
+ md += `### 数据库调用链\n\n${dbCallChain(p)}\n\n`;
+ md += `### 存储过程处理逻辑明细\n\n`;
+ if (!p.procs.length) {
+ md += `无页面级存储过程调用。\n\n`;
+ } else {
+ for (const proc of p.procs) md += `- ${explainProc(proc)}\n`;
+ md += `\n`;
+ }
+}
+
+md += `## SQL 对象总览\n\n`;
+md += `脚本中解析到表/视图 ${sqlModel.objects.length} 个、存储过程 ${Object.keys(sqlModel.procByName).length} 个。\n\n`;
+md += `### 表/视图清单\n\n`;
+for (const obj of sqlModel.objects) md += `- ${obj.type === 'TABLE' ? '表' : '视图'}:\`${obj.name}\`\n`;
+
+fs.writeFileSync(outFile, md, 'utf8');
+console.log(outFile);
diff --git a/tools/create_dev_plan_docx.ps1 b/tools/create_dev_plan_docx.ps1
new file mode 100644
index 0000000..e715de4
--- /dev/null
+++ b/tools/create_dev_plan_docx.ps1
@@ -0,0 +1,206 @@
+param(
+ [Parameter(Mandatory = $true)]
+ [string]$PlanMarkdown,
+
+ [Parameter(Mandatory = $true)]
+ [string]$SourceMarkdown,
+
+ [Parameter(Mandatory = $true)]
+ [string]$OutputDocx
+)
+
+$ErrorActionPreference = 'Stop'
+
+function Escape-XmlText {
+ param([AllowNull()][string]$Text)
+ if ($null -eq $Text) { return '' }
+ return [System.Security.SecurityElement]::Escape($Text)
+}
+
+function Add-Paragraph {
+ param(
+ [System.Collections.Generic.List[string]]$Parts,
+ [string]$Text,
+ [string]$Style = $null
+ )
+ $styleXml = ''
+ if ($Style) {
+ $styleXml = ""
+ }
+ $Parts.Add("$styleXml$(Escape-XmlText $Text)")
+}
+
+function Add-Table {
+ param(
+ [System.Collections.Generic.List[string]]$Parts,
+ [string[]]$Headers,
+ [object[]]$Rows
+ )
+ $tbl = New-Object System.Text.StringBuilder
+ [void]$tbl.Append('')
+ [void]$tbl.Append('')
+ [void]$tbl.Append('')
+ foreach ($h in $Headers) {
+ [void]$tbl.Append('')
+ [void]$tbl.Append((Escape-XmlText $h))
+ [void]$tbl.Append('')
+ }
+ [void]$tbl.Append('')
+ foreach ($row in $Rows) {
+ [void]$tbl.Append('')
+ foreach ($cell in $row) {
+ [void]$tbl.Append('')
+ [void]$tbl.Append((Escape-XmlText ([string]$cell)))
+ [void]$tbl.Append('')
+ }
+ [void]$tbl.Append('')
+ }
+ [void]$tbl.Append('')
+ $Parts.Add($tbl.ToString())
+}
+
+function Flush-TableBuffer {
+ param(
+ [System.Collections.Generic.List[string]]$Parts,
+ [System.Collections.Generic.List[string]]$Buffer
+ )
+ if ($Buffer.Count -lt 2) {
+ foreach ($line in $Buffer) {
+ Add-Paragraph -Parts $Parts -Text $line
+ }
+ $Buffer.Clear()
+ return
+ }
+ $header = $Buffer[0].Trim('|').Split('|') | ForEach-Object { $_.Trim() }
+ $rows = @()
+ for ($i = 2; $i -lt $Buffer.Count; $i++) {
+ $rows += ,($Buffer[$i].Trim('|').Split('|') | ForEach-Object { $_.Trim() })
+ }
+ Add-Table -Parts $Parts -Headers $header -Rows $rows
+ $Buffer.Clear()
+}
+
+function Add-MarkdownContent {
+ param(
+ [System.Collections.Generic.List[string]]$Parts,
+ [string]$MarkdownPath
+ )
+ $lines = [System.IO.File]::ReadAllLines($MarkdownPath, [System.Text.Encoding]::UTF8)
+ $tableBuffer = New-Object System.Collections.Generic.List[string]
+ $inCode = $false
+
+ foreach ($line in $lines) {
+ if ($line.TrimStart().StartsWith('```')) {
+ if ($tableBuffer.Count -gt 0) { Flush-TableBuffer -Parts $Parts -Buffer $tableBuffer }
+ $inCode = -not $inCode
+ Add-Paragraph -Parts $Parts -Text $line -Style 'Code'
+ continue
+ }
+ if ($inCode) {
+ Add-Paragraph -Parts $Parts -Text $line -Style 'Code'
+ continue
+ }
+ if ($line.TrimStart().StartsWith('|') -and $line.TrimEnd().EndsWith('|')) {
+ $tableBuffer.Add($line)
+ continue
+ }
+ if ($tableBuffer.Count -gt 0) { Flush-TableBuffer -Parts $Parts -Buffer $tableBuffer }
+
+ if ($line.StartsWith('### ')) {
+ Add-Paragraph -Parts $Parts -Text $line.Substring(4) -Style 'Heading3'
+ } elseif ($line.StartsWith('## ')) {
+ Add-Paragraph -Parts $Parts -Text $line.Substring(3) -Style 'Heading2'
+ } elseif ($line.StartsWith('# ')) {
+ Add-Paragraph -Parts $Parts -Text $line.Substring(2) -Style 'Heading1'
+ } elseif ($line.Trim().StartsWith('- ')) {
+ Add-Paragraph -Parts $Parts -Text ('· ' + $line.Trim().Substring(2))
+ } elseif ([string]::IsNullOrWhiteSpace($line)) {
+ Add-Paragraph -Parts $Parts -Text ''
+ } else {
+ Add-Paragraph -Parts $Parts -Text $line
+ }
+ }
+ if ($tableBuffer.Count -gt 0) { Flush-TableBuffer -Parts $Parts -Buffer $tableBuffer }
+}
+
+if (-not (Test-Path -LiteralPath $PlanMarkdown)) { throw "Plan markdown not found: $PlanMarkdown" }
+if (-not (Test-Path -LiteralPath $SourceMarkdown)) { throw "Source markdown not found: $SourceMarkdown" }
+
+$outputDir = Split-Path -Path $OutputDocx -Parent
+if (-not (Test-Path -LiteralPath $outputDir)) {
+ New-Item -ItemType Directory -Path $outputDir | Out-Null
+}
+
+$tempRoot = Join-Path $env:TEMP ("docx_" + [Guid]::NewGuid().ToString('N'))
+$wordDir = Join-Path $tempRoot 'word'
+$relsDir = Join-Path $tempRoot '_rels'
+$wordRelsDir = Join-Path $wordDir '_rels'
+New-Item -ItemType Directory -Path $wordDir, $relsDir, $wordRelsDir | Out-Null
+
+$parts = New-Object System.Collections.Generic.List[string]
+Add-MarkdownContent -Parts $parts -MarkdownPath $PlanMarkdown
+Add-MarkdownContent -Parts $parts -MarkdownPath $SourceMarkdown
+
+$documentXml = @"
+
+
+
+ $($parts -join "`n")
+
+
+
+
+
+
+"@
+
+$stylesXml = @'
+
+
+
+
+
+
+
+
+
+'@
+
+$contentTypesXml = @'
+
+
+
+
+
+
+
+'@
+
+$relsXml = @'
+
+
+
+
+'@
+
+$docRelsXml = @'
+
+
+
+
+'@
+
+[System.IO.File]::WriteAllText((Join-Path $tempRoot '[Content_Types].xml'), $contentTypesXml, [System.Text.Encoding]::UTF8)
+[System.IO.File]::WriteAllText((Join-Path $relsDir '.rels'), $relsXml, [System.Text.Encoding]::UTF8)
+[System.IO.File]::WriteAllText((Join-Path $wordDir 'document.xml'), $documentXml, [System.Text.Encoding]::UTF8)
+[System.IO.File]::WriteAllText((Join-Path $wordDir 'styles.xml'), $stylesXml, [System.Text.Encoding]::UTF8)
+[System.IO.File]::WriteAllText((Join-Path $wordRelsDir 'document.xml.rels'), $docRelsXml, [System.Text.Encoding]::UTF8)
+
+if (Test-Path -LiteralPath $OutputDocx) {
+ Remove-Item -LiteralPath $OutputDocx -Force
+}
+
+Add-Type -AssemblyName System.IO.Compression.FileSystem
+[System.IO.Compression.ZipFile]::CreateFromDirectory($tempRoot, $OutputDocx)
+Remove-Item -LiteralPath $tempRoot -Recurse -Force
+Write-Output $OutputDocx