Преглед на файлове

IPQC以及qms文档更新

liuwb преди 4 месеца
родител
ревизия
07d17bc35d
променени са 23 файла, в които са добавени 1066 реда и са изтрити 1196 реда
  1. 29 0
      docs/qms/FQC-提示词要点.md
  2. 144 0
      docs/qms/IPQC-提示词要点.md
  3. 175 0
      docs/qms/IQC-提示词要点.md
  4. 0 186
      docs/qms/IQC开发流程与注意事项.md
  5. 0 151
      docs/qms/IQC来料检验任务流程开发说明.md
  6. 0 186
      docs/qms/IQC来料检验申请-后端设计.md
  7. 0 111
      docs/qms/IQC来料质量检验.md
  8. 0 87
      docs/qms/QMS_INTEGRATION_GUIDE.md
  9. 0 240
      docs/qms/codex-dev-playbook.md
  10. 7 0
      yudao-module-qms/src/main/java/cn/iocoder/yudao/module/qms/controller/admin/ipqc/IpqcTaskController.java
  11. 60 6
      yudao-module-qms/src/main/java/cn/iocoder/yudao/module/qms/controller/admin/ipqc/vo/IpqcInspectRecordDetailRespVO.java
  12. 19 4
      yudao-module-qms/src/main/java/cn/iocoder/yudao/module/qms/controller/admin/ipqc/vo/IpqcInspectRecordMainRespVO.java
  13. 87 18
      yudao-module-qms/src/main/java/cn/iocoder/yudao/module/qms/controller/admin/ipqc/vo/IpqcInspectRecordSaveReqVO.java
  14. 3 0
      yudao-module-qms/src/main/java/cn/iocoder/yudao/module/qms/controller/admin/ipqc/vo/IpqcTaskProcessInfoVO.java
  15. 3 0
      yudao-module-qms/src/main/java/cn/iocoder/yudao/module/qms/dal/mysql/ipqc/IpqcTaskMapper.java
  16. 2 0
      yudao-module-qms/src/main/java/cn/iocoder/yudao/module/qms/service/ipqc/IpqcTaskService.java
  17. 14 0
      yudao-module-qms/src/main/java/cn/iocoder/yudao/module/qms/service/ipqc/IpqcTaskServiceImpl.java
  18. 112 19
      yudao-module-qms/src/main/resources/mapper/ipqc/IpqcTaskMapper.xml
  19. 31 4
      yudao-ui/yudao-ui-admin-vue3/src/api/qms/ipqc/task/index.ts
  20. 57 25
      yudao-ui/yudao-ui-admin-vue3/src/store/modules/qms/ipqcInspectRecord.ts
  21. 6 1
      yudao-ui/yudao-ui-admin-vue3/src/views/bpm/processInstance/detail/index.vue
  22. 84 0
      yudao-ui/yudao-ui-admin-vue3/src/views/qms/ipqc/task/IpqcTaskSummary.vue
  23. 233 158
      yudao-ui/yudao-ui-admin-vue3/src/views/qms/ipqc/task/ProcessDetailForm.vue

+ 29 - 0
docs/qms/FQC-提示词要点.md

@@ -0,0 +1,29 @@
+# FQC 成品检验(现行配置与待补项)
+
+> 说明:当前代码侧仅保留前端模块配置,后端接口与表结构待补齐。
+
+## 1. 现行入口与路由(前端)
+- 模块:FQC(`qmsModules.ts`)
+- 申请列表:`/qms/fqc/apply/list`
+- 任务列表:`/qms/fqc/task/list`
+- 收藏/帮助:`/qms/fqc/favorites`、`/qms/fqc/help`
+
+## 2. 现行页面配置(前端)
+- 列表字段以 `qmsModules.ts` 中的 `fqc.applicationList / fqc.taskList` 为准
+- 字段中文标签、筛选项、按钮权限沿用统一 QMS 规则
+
+## 3. 待补项(与代码对齐后更新)
+- 后端接口:
+  - 申请分页、任务分页、启动流程、记录保存等
+- 数据表:
+  - 申请主子表、检验单主子表
+- 流程 Key 与审批详情展示规则
+
+## 4. 列表查询 SQL
+- 申请列表:待定
+- 任务列表:待定
+
+## 5. 扩展开发注意事项(通用)
+- 菜单路由地址不允许以 `/` 开头;组件地址使用前端视图路径,不加 `.vue`。
+- 流程表单若使用“业务表单”,需提供前端组件路径供 `BpmProcessInstanceDetail` 注册渲染。
+- BPM 表结构需完整(`bpm_*`、`ACT_*`),否则会出现 501 提示“表结构未导入”。 

+ 144 - 0
docs/qms/IPQC-提示词要点.md

@@ -0,0 +1,144 @@
+# IPQC 过程检验(现行配置与问题修正)
+
+> 目标:与当前系统代码保持一致;记录已确认的字段映射、页面行为与问题修正方案。
+
+## 1. 现行入口与路由(前端)
+- 模块:IPQC(`qmsModules.ts`)
+- 任务列表:`/qms/ipqc/task/list`
+- 过程检验编辑页(流程内):`/s6/ipqc/task/edit/:taskId`(自定义业务表单)
+- 流程详情页:`BpmProcessInstanceDetail`
+
+## 2. 现行接口(后端)
+- 任务分页:`GET /admin-api/qms/ipqc-task/page`
+- 任务详情:`GET /admin-api/qms/ipqc-task/get?id=...`
+- 发起流程:`POST /admin-api/qms/ipqc-task/start`
+- 检验记录主表:`GET /admin-api/qms/ipqc-task/record/main?taskId=...`
+- 检验记录明细:`GET /admin-api/qms/ipqc-task/record/detail-list?taskId=...`
+- 保存检验记录:`POST /admin-api/qms/ipqc-task/record/save`
+
+## 3. 现行数据来源与表结构
+### 3.1 任务列表来源
+- 主要来源:`WorkOrdMaster`、`ItemMaster`、`mes_morder`
+- 状态来源:`qms_gcjyd.status`(检验状态)
+  - 无关联数据:默认 **待检验**
+  - 有记录:
+    - `检验中` → processing
+    - `检验完成` → completed
+    - 其它 → pending
+
+### 3.2 记录主表
+- 表:`qms_gcjyd`
+- 关键字段:`bdbh、bbh、sxrq、djbh、cplx、scph、wlbm、wlmc、gxbm、gxmc、sczyry、lydjbh、jyrq、jyr、jysl、jyhgsl、jybhgsl、clfs、lysl、phsl、pd、fj、jgbh、jgbb、bz、status`
+
+### 3.3 记录子表
+- 表:`qms_gcjydzb`
+- 关键字段:`jyxm、jybz、sx、xx、jypc、j1~j20、pd、bz`
+
+## 4. 流程与审批详情(现行行为)
+- 流程 Key:`qms_ipqc_task`
+- 流程发起时写入 variables:`taskId / applicationId / workOrder / materialCode / batch`
+- 审批详情页:**不显示业务表单**,仅显示“任务列表字段汇总”
+  - 展示字段(中文):任务编号、单据编号、生产订单、状态、工序/工步、线体/工位/设备、物料编码、物料名称、批次、负责人、优先级、开始/完成时间、备注
+
+## 5. 过程检验编辑页(业务表单)
+- 风格与 IQC 对齐(只读三行、只读默认项、附件上传、明细样本列等)
+- 核心逻辑:
+  - 检验数量来源任务列表(只读)
+  - 合格/不合格互算
+  - 处理方式下拉(退货/挑选/让步接收)
+  - 样本1~20,样本量动态列
+
+## 6. 问题修正方案(保留)
+- 404/侧边菜单点击无效:菜单路由与前端路由需一致,禁止以 `/` 开头时使用内部路由补正
+- BPM 缺表报错:确认 bpm_* 与 ACT_* 表存在于 `dopdemo`
+- 审批详情重复页签:仅保留 1 个“流程详情”页签
+
+## 7. 列表查询 SQL(与现行代码一致)
+### 7.1 任务列表(IpqcTaskMapper.selectTaskPage)
+```sql
+SELECT
+  fr.recid AS id,
+  fr.Drawing AS applicationId,
+  fr.WorkOrd AS workOrder,
+  fr.Typed AS process,
+  fr.WoTyped AS step,
+  fr.Project AS line,
+  NULL AS station,
+  NULL AS equipment,
+  fr.ItemNum AS materialCode,
+  im.Descr AS materialName,
+  fr.LotSerial AS batch,
+  fr.lbrvar AS responsibleName,
+  CASE
+    WHEN mo.MaterialSituation IN ('high', 'H', 'A', '1') THEN 'high'
+    WHEN mo.MaterialSituation IN ('medium', 'M', 'B', '2') THEN 'medium'
+    WHEN mo.MaterialSituation IN ('low', 'L', 'C', '3') THEN 'low'
+    ELSE 'medium'
+  END AS priority,
+  fr.OrdDate AS startTime,
+  fr.DueDate AS completeTime,
+  fr.Remark AS remark,
+  CASE d.status_rank
+    WHEN 3 THEN 'processing'
+    WHEN 2 THEN 'completed'
+    ELSE 'pending'
+  END AS status
+FROM
+(
+  SELECT
+    `Domain`,
+    WorkOrd,
+    ItemNum,
+    LotSerial,
+    SUM(IFNULL(QtyOrded, 0))     AS sum_QtyOrded,
+    SUM(IFNULL(QtyCompleted, 0)) AS sum_QtyCompleted,
+    COUNT(*)                     AS merge_cnt,
+    MIN(recid)                   AS first_recid
+  FROM WorkOrdMaster
+  WHERE IFNULL(Status, '') <> ''
+    AND Status <> 'c'
+  GROUP BY `Domain`, WorkOrd, ItemNum, LotSerial
+) AS g
+INNER JOIN WorkOrdMaster fr
+  ON fr.recid = g.first_recid
+LEFT JOIN ItemMaster im
+  ON im.ItemNum = fr.ItemNum
+ AND im.`Domain` = fr.`Domain`
+LEFT JOIN mes_morder mo
+  ON mo.morder_no = fr.WorkOrd
+ AND mo.factory_id = fr.`Domain`
+LEFT JOIN (
+  SELECT
+    lydjbh,
+    scph,
+    MAX(
+      CASE
+        WHEN `status` = '检验中' THEN 3
+        WHEN `status` = '检验完成' THEN 2
+        ELSE 1
+      END
+    ) AS status_rank
+  FROM qms_gcjyd
+  GROUP BY lydjbh, scph
+) d
+  ON d.scph = fr.LotSerial
+ AND d.lydjbh = fr.WorkOrd
+/* where: factoryId / status */
+ORDER BY fr.OrdDate DESC, fr.recid DESC
+```
+
+### 7.2 其它列表
+待定(目前 IPQC 仅有任务列表)
+
+## 8. 扩展开发注意事项(保留)
+### 7.1 菜单与路由约束
+- 菜单管理中路由地址不允许以 `/` 开头(如 `qms/ipqc/task/list`)。
+- 组件地址为前端视图路径(如 `qms/TaskList`),不加 `.vue`。
+
+### 8.2 流程表单类型(业务表单)
+- 表单类型选择“业务表单”时,表单查看地址应指向前端组件路径(示例:`/qms/ipqc/task/ProcessDetailForm.vue`)。
+- 审批详情页对 IPQC 流程改为**任务汇总展示**,不渲染业务表单。
+
+### 8.3 状态映射与展示
+- 工单状态与检验状态无关:检验状态以 `qms_gcjyd.status` 为准。
+- 前端状态统一中文显示:待检验 / 检验中 / 已完成。

+ 175 - 0
docs/qms/IQC-提示词要点.md

@@ -0,0 +1,175 @@
+# IQC 来料检验(现行配置与问题修正)
+
+> 目标:与当前系统代码保持一致;记录已确认的字段映射、页面行为与问题修正方案。
+
+## 1. 现行入口与路由(前端)
+- 模块:IQC(`qmsModules.ts`)
+- 任务列表:`/qms/iqc/task/list`
+- 申请列表:`/qms/iqc/apply/list`
+- 申请编辑/查看:`/qms/iqc/apply/edit/:id`、`/qms/iqc/apply/view/:id`
+- 任务流程详情:通过 `BpmProcessInstanceDetail` 打开(流程实例详情页)
+
+## 2. 现行数据表(后端)
+- 申请主表:`qms_qcp_inspecapplyn`
+- 申请子表:`qms_qcp_insappnentry`
+- 检验单主表:`qms_qcp_inspbill`
+- 检验单子表:`qms_qcp_inspbilllist`
+
+## 3. 已确认字段映射(补充 1)
+### 3.1 检验单主表(qms_qcp_inspbill)
+- `bdbh` 表单编号
+- `bb` 表单版本
+- `sxrq` 生效日期
+- `jgbh` 检规编号
+- `jgbb` 检规版本
+- `jyr` 检验员
+- `jysl` 检验数量
+- `bhgsl` 不合格数量
+- `fj` 附件(上传链接)
+- **说明**:主表没有 `bz` 字段(备注),不要写入。
+
+### 3.2 检验单子表(qms_qcp_inspbilllist)
+- `jyxm` 检验项目
+- `bz` 检验标准
+- `sx` 上限
+- `xx` 下限
+- `pd` 判断
+- `j1` 样本1
+- `j2` 样本2
+- `j3` 样本3
+
+## 4. 审批详情(流程详情页)
+### 4.1 展示来源
+- **申请信息**统一从“任务详情接口”返回,并中文展示:
+  - 任务编号、单据编号、申请人、申请时间、批次、数量、单位、供应商编码、任务状态等
+- **字段命名规范化**:
+  - 展示字段规范为 **物料编码/物料名称**
+  - 不再使用“来源单号/来源类型”混用含义
+- **状态中文**:待检验 / 检验中 / 检验完成
+
+### 4.2 业务修正要点
+- 流程详情中只显示**任务信息 + 打开编辑页入口**
+- “返回/保存成功后回到流程详情页”
+- 多个“流程详情”标签重复问题:统一只保留一个详情页签
+
+## 5. 来料检验单编辑页(现行交互要求)
+### 5.1 顶部只读三行(灰色)
+- 表单编号:`RS/IV-7.4-32`
+- 表单版本:`1.2`
+- 表单生效日期:`2022.04.01`
+
+### 5.2 主表字段与默认值
+- 检规编号:默认 `QC-H644-1-05`(只读)
+- 检规版本:默认 `A.8`(只读)
+- 检验员:为空时赋当前账号昵称(只读)
+- 检验数量:来自任务列表数量(只读)
+
+### 5.3 数量与计算规则
+- 合格数量 / 不合格数量 **互算**(留样/破坏不计入)
+- 处理方式:下拉(退货 / 挑选 / 让步接收)
+- 留样数量、破坏数量:普通输入框
+- **所有数字输入框:整数校验**
+
+### 5.4 附件
+- 上传控件,不限格式;存储为 `fj` 链接
+
+### 5.5 主表判断
+- 判断:下拉(合格 / 不合格)
+
+## 6. 检验明细(子表)
+- 检验项目、检验标准:只读多行文本框
+- 判断:下拉(合格 / 不合格)
+- 样本量:手填整数
+- 样本列:支持样本1~样本20
+- 样本列动态规则:
+  - 记录“已填写最大列数”
+  - 后续输入更小值不收缩,更大值则新增列
+- 操作列移动到最左侧
+- 上限/下限/样本量/样本1~20:整数校验
+
+## 7. 申请信息字段规则
+- 申请人:前端显示昵称;数据库存 **用户ID**
+- 组织/部门:前端显示 **部门名称**;数据库存 **部门ID**;与申请人一致设置只读
+
+## 8. 物料数改名
+- 申请列表表头:**物料数 → 物料种类**
+
+## 9. 列表查询 SQL(与现行代码一致)
+### 9.1 申请列表(IqcApplyMapper.selectApplyPage)
+```sql
+SELECT *
+FROM (
+  SELECT
+    a.id AS applyId,
+    a.FBILLNO AS id,
+    a.FAPPLYUSER AS applicantId,
+    u.username AS applicantName,
+    a.FAPPLYTIME AS applyTime,
+    a.FBILLTYPE AS businessType,
+    a.FORGID AS department,
+    a.FCOMMENT AS remark,
+    COUNT(b.id) AS materialCount,
+    CASE
+      WHEN SUM(CASE WHEN b.FINSPECTSTATUS = '检验中' THEN 1 ELSE 0 END) > 0 THEN '检验中'
+      WHEN COUNT(b.id) > 0
+        AND SUM(CASE WHEN b.FINSPECTSTATUS = '检验完成' THEN 1 ELSE 0 END) = COUNT(b.id) THEN '检验完成'
+      ELSE '待检验'
+    END AS status
+  FROM qms_qcp_inspecapplyn a
+  LEFT JOIN system_users u ON u.id = a.FAPPLYUSER
+  LEFT JOIN qms_qcp_insappnentry b ON b.glid = a.id
+  /* where: keyword / applicantIds / applyTime */
+  GROUP BY a.id, a.FBILLNO, a.FAPPLYUSER, u.username, a.FAPPLYTIME, a.FBILLTYPE, a.FORGID, a.FCOMMENT
+) t
+/* where: statusList */
+ORDER BY t.applyTime DESC
+```
+
+### 9.2 任务列表(IqcTaskMapper.selectTaskPage)
+```sql
+SELECT
+  b.id AS id,
+  a.FBILLNO AS applicationId,
+  a.FAPPLYTIME AS applyTime,
+  a.FAPPLYUSER AS applicantId,
+  u.username AS applicantName,
+  b.FSUPPLIER AS supplierCode,
+  b.process_instance_id AS processInstanceId,
+  b.FSRCORDERNUM AS materialCode,
+  b.FSRCORDERTYPE AS materialName,
+  b.FLOTNUMBER AS batch,
+  b.FAPPLYQTY AS quantity,
+  b.FUNIT AS unit,
+  CASE
+    WHEN COALESCE(b.FINSPECTSTATUS, a.FBILLSTATUS) IN ('processing', '检验中') THEN 'processing'
+    WHEN COALESCE(b.FINSPECTSTATUS, a.FBILLSTATUS) IN ('completed', '检验完成') THEN 'completed'
+    ELSE 'pending'
+  END AS status
+FROM qms_qcp_insappnentry b
+LEFT JOIN qms_qcp_inspecapplyn a ON a.id = b.glid
+LEFT JOIN system_users u ON u.id = a.FAPPLYUSER
+/* where: status */
+ORDER BY a.FAPPLYTIME DESC, b.id DESC
+```
+
+### 9.3 检验单列表
+待定(当前代码未提供专用 list SQL) 
+
+## 10. 问题修正方案(保留)
+- 404/路由问题:菜单地址不允许以 `/` 开头时,使用内部路由配置补正
+- 申请时间字段:统一使用 `FAPPLYTIME` 映射
+- 状态中文:前端直接中文展示,避免混用枚举
+- “备注”字段冲突:主表无 `bz`,子表 `bz` 为“检验标准”
+
+## 11. 扩展开发注意事项(保留)
+### 10.1 菜单与路由约束
+- 菜单管理中**路由地址不能以 `/` 开头**,需保持与前端路由一致(例如 `qms/iqc/apply/list`)。
+- 菜单“组件地址”使用前端视图路径(如 `qms/ApplicationList`),不要加 `.vue`。
+
+### 10.2 流程表单类型(业务表单)
+- 流程模型“表单类型”选择**业务表单**时,表单查看地址对应前端组件路径(示例:`/qms/iqc/task/ProcessDetailForm.vue`)。
+- 实际渲染由 `BpmProcessInstanceDetail` 通过 `formCustomViewPath` 动态注册组件完成。
+
+### 10.3 BPM 表结构校验
+- 数据库需要同时具备 `bpm_*` 与 `ACT_*` 表(`ACT_GE_PROPERTY` 可用于快速验证引擎表是否可用)。
+- 出现 `bpm_process_instance` 不存在或 501 提示时,优先确认当前连接的库是否为 `dopdemo`。

+ 0 - 186
docs/qms/IQC开发流程与注意事项.md

@@ -1,186 +0,0 @@
-# IQC 开发流程与注意事项(AI 友好版)
-
-> 目标:让 AI/新人能够快速定位 IQC 功能的页面、接口、数据库字段映射和流程联动。
-
----
-
-## 1. 模块范围
-- **IQC 来料检验申请**:创建/编辑/查看申请单(主表 + 子表)。
-- **IQC 来料检验任务**:基于申请子表生成任务列表,触发流程。
-- **IQC 检验单编辑**:在流程中编辑检验明细(新页面)。
-- **流程引擎**:Flowable(流程实例、任务、历史)。
-
----
-
-## 2. 前端页面与路由
-
-### 2.1 申请相关
-- 列表:`/qms/iqc/apply/list`
-  - 页面:`yudao-ui/yudao-ui-admin-vue3/src/views/qms/ApplicationList.vue`
-- 表单:`/qms/iqc/apply/new | /edit/:id | /view/:id`
-  - 页面:`yudao-ui/yudao-ui-admin-vue3/src/views/qms/ApplicationForm.vue`
-
-### 2.2 任务与流程相关
-- 任务列表:`/qms/iqc/task/list`
-  - 页面:`yudao-ui/yudao-ui-admin-vue3/src/views/qms/TaskList.vue`
-- 流程详情(审批详情页):
-  - 页面:`yudao-ui/yudao-ui-admin-vue3/src/views/qms/iqc/task/ProcessDetailForm.vue`
-  - 当前只展示 **申请信息 + 备注**,并提供“打开编辑页面”按钮。
-
-### 2.3 检验单编辑页(新页面)
-- `/qms/iqc/task/edit/:taskId`
-- `/s5/iqc/iqc/task/edit/:taskId`
-- 页面:`yudao-ui/yudao-ui-admin-vue3/src/views/qms/iqc/task/InspectBillEdit.vue`
-
----
-
-## 3. 关键接口清单
-
-### 3.1 申请(IQC Apply)
-- `GET /qms/iqc-apply/page` 申请列表
-- `GET /qms/iqc-apply/get?id=...` 申请详情
-- `POST /qms/iqc-apply/create` 新建
-- `PUT /qms/iqc-apply/update` 更新
-- `DELETE /qms/iqc-apply/delete?id=...` 删除
-
-### 3.2 任务(IQC Task)
-- `GET /qms/iqc-task/page` 任务列表
-- `GET /qms/iqc-task/get?id=...` 任务详情
-- `POST /qms/iqc-task/start` 开始检验(创建流程实例)
-
-### 3.3 检验单编辑(Inspect Bill)
-- `GET /qms/iqc-task/inspect-bill/main?taskId=...` 主表信息
-- `GET /qms/iqc-task/detail-list?taskId=...` 明细列表
-- `POST /qms/iqc-task/inspect-bill/save` 保存主表+明细
-
----
-
-## 4. 数据表与字段映射
-
-### 4.1 申请主表(qms_qcp_inspecapplyn)
-- `id`:主键
-- `FBILLNO`:申请单号(前端 `id`)
-- `FAPPLYUSER`:申请人 ID
-- `FAPPLYTIME`:申请时间
-- `FBILLTYPE`:业务类型
-- `FCOMMENT`:**总体备注**(申请列表显示)
-
-### 4.2 申请子表 / 任务来源表(qms_qcp_insappnentry)
-- `glid`:关联主表 `id`
-- `FSRCORDERNUM`:物料编码(前端 `materialCode`)
-- `FSRCORDERTYPE`:物料名称(前端 `materialName`)
-- `FLOTNUMBER`:批次(前端 `batch`)
-- `FAPPLYQTY`:数量(前端 `quantity`)
-- `FUNIT`:单位(前端 `unit`)
-- `FINSPECTSTATUS`:检验状态(待检验/检验中/检验完成)
-- `FSUPPLIER`:**供应商编码**(前端 `supplierCode`)
-- `process_instance_id`:流程实例
-- `flowstate`:流程状态
-
-### 4.3 检验单主表(qms_qcp_inspbill)
-- `hid`:关联任务 ID(`qms_qcp_insappnentry.id`)
-- `FCOMMENT`:临时存 JSON:`{"A":"...","B":"...","C":"...","D":"..."}`
-
-### 4.4 检验单明细表(qms_qcp_inspbilllist)
-- `billid`:主表 ID
-- `jyxm/bz/sx/xx`:A/B/C/D 的临时字段映射
-
----
-
-## 5. 字段映射表(前端 ↔ DB)
-
-### 5.1 申请明细(ApplicationForm.detail)
-| 前端字段 | 表字段 | 说明 |
-|---|---|---|
-| materialCode | FSRCORDERNUM | 物料编码 |
-| materialName | FSRCORDERTYPE | 物料名称 |
-| specification | (暂无) | 当前未映射 |
-| batch | FLOTNUMBER | 批次 |
-| inspectStatus | FINSPECTSTATUS | 检验状态 |
-| quantity | FAPPLYQTY | 数量 |
-| unit | FUNIT | 单位 |
-| supplierCode | FSUPPLIER | 供应商编码 |
-
-### 5.2 申请列表(ApplicationList)
-- `remark` ← `qms_qcp_inspecapplyn.FCOMMENT`(总体备注)
-- `status`:由子表汇总计算(见 6.1)
-
-### 5.3 任务列表(TaskList)
-- `supplierCode` ← `qms_qcp_insappnentry.FSUPPLIER`
-- `applicationId` ← `qms_qcp_inspecapplyn.FBILLNO`
-- `status`:由 `FINSPECTSTATUS/FBILLSTATUS` 映射
-
----
-
-## 6. 流程联动(BPM)
-
-### 6.1 流程 Key
-- `PROCESS_KEY = qms_iqc_task`
-- 定义在:`yudao-module-qms/src/main/java/cn/iocoder/yudao/module/qms/service/iqc/IqcTaskServiceImpl.java`
-
-### 6.2 启动流程
-- 入口:`POST /qms/iqc-task/start`
-- 行为:
-  - 创建流程实例
-  - 回写 `process_instance_id`
-  - 更新 `flowstate = 审批中`
-  - 更新 `FINSPECTSTATUS = 检验中`
-
-### 6.3 流程结束回写
-- 通过:`flowstate = 审批完成`,`FINSPECTSTATUS = 检验完成`
-- 取消:`flowstate = 已取消`,`FINSPECTSTATUS = 待检验`
-- 驳回:`flowstate = 已驳回`,`FINSPECTSTATUS = 检验中`
-
----
-
-## 7. 前端配置关键点(AI 容易踩坑)
-
-1) **申请列表 vs 任务列表**
-- 申请列表显示 **总体备注**(`FCOMMENT`)
-- 任务列表显示 **供应商编码**(`FSUPPLIER`)
-
-2) **状态角标**
-- `ApplicationForm.vue` 已移除状态角标(不显示“待检验/检验中/检验完成”)。
-
-3) **检验明细编辑入口**
-- 审批详情页(流程详情)不再内嵌明细表。
-- 通过“打开编辑页面”进入 `InspectBillEdit.vue`。
-
-4) **通过前强制保存**
-- `ProcessInstanceOperationButton.vue` 对 `qms_iqc_task` 做了保存拦截:
-  - “通过”前会调用检验单保存接口
-  - 保存失败会阻止通过
-
----
-
-## 8. 常见问题与排查
-
-- 列表字段不显示:检查 **前端字段 key**、**后端 VO 字段名**、**Mapper SQL 字段别名** 是否一致。
-- 明细更新不生效:当前是“先删后插”,ID 会重新生成;确认 `update` 逻辑是否走到。
-- 流程未发起:`process_instance_id` 为空时会提示“请先开始检验”。
-- 申请时间格式:前端使用时间戳(`value-format: 'x'`),后端用 `LocalDateTime` 接收。
-
----
-
-## 9. 关键代码位置索引
-
-- 前端配置:`yudao-ui/yudao-ui-admin-vue3/src/config/qmsModules.ts`
-- 申请表单:`yudao-ui/yudao-ui-admin-vue3/src/views/qms/ApplicationForm.vue`
-- 任务列表:`yudao-ui/yudao-ui-admin-vue3/src/views/qms/TaskList.vue`
-- 审批详情:`yudao-ui/yudao-ui-admin-vue3/src/views/qms/iqc/task/ProcessDetailForm.vue`
-- 检验单编辑:`yudao-ui/yudao-ui-admin-vue3/src/views/qms/iqc/task/InspectBillEdit.vue`
-
-- 申请 Mapper:`yudao-module-qms/src/main/resources/mapper/iqc/IqcApplyMapper.xml`
-- 任务 Mapper:`yudao-module-qms/src/main/resources/mapper/iqc/IqcTaskMapper.xml`
-- 任务 Service:`yudao-module-qms/src/main/java/cn/iocoder/yudao/module/qms/service/iqc/IqcTaskServiceImpl.java`
-
----
-
-## 10. 建议的开发顺序(可复用)
-1. 先确定 DB 字段映射(主/子表)
-2. 后端 VO + Mapper SQL + Service
-3. 前端 API 类型对齐
-4. 列表字段 key 与后端字段别名一致
-5. 流程 start/回写字段校验
-6. 审批详情 + 编辑页联调
-

+ 0 - 151
docs/qms/IQC来料检验任务流程开发说明.md

@@ -1,151 +0,0 @@
-通过 Listener 监听流程回调并更新 `flowstate`。  
-业务状态 `FINSPECTSTATUS` 可在流程节点触发或流程结束时统一更新。
-
-#### 2.2.1 流程提交后:更新为“审批中”
-```sql
-update qms_qcp_insappnentry
-set flowstate = '审批中'
-where id = '{<TaskEntryId>}';
-```
-
-#### 2.2.2 流程结束后:更新为“审批完成”
-```sql
-update qms_qcp_insappnentry
-set flowstate = '审批完成',
-    FINSPECTSTATUS = '检验完成'
-where id = '{<TaskEntryId>}';
-```
-
-#### 2.2.3 流程驳回/取消(示例)
-```sql
-update qms_qcp_insappnentry
-set flowstate = '已驳回'
-where id = '{<TaskEntryId>}';
-```
-
----
-
-## 3. 后端业务逻辑开发(参照 oa_leave)
-
-### 3.1 参考类
-请假流程后端参考:
-- `BpmOALeaveController`
-- `BpmOALeaveServiceImpl`
-
-IQC 任务流程需实现类似结构的 Controller + Service,并按“业务表 + 流程”方式接入。
-
-### 3.2 流程模型编号(PROCESS_KEY)
-请假:`oa_leave`  
-IQC 任务(示例):`iqc_task`
-
-> 该 Key 必须与流程模型实际配置一致。
-
-### 3.3 创建流程实例
-使用 `BpmProcessInstanceApi` 创建流程实例:
-- `#createProcessInstance(...)`
-
-发起 IQC 任务流程时建议完成:
-- 创建流程实例
-- 回写 `qms_qcp_insappnentry.process_instance_id`
-- 更新 `flowstate = '审批中'`
-
----
-
-## 4. 前端业务逻辑开发(参照 oa_leave)
-
-### 4.1 参考页面
-请假模块前端参考:
-- 发起:`leave/create.vue`
-- 详情:`leave/detail.vue`
-- 列表:`leave/index.vue`
-
-### 4.2 IQC 任务页面对齐建议
-可沿用现有 QMS 任务页面逻辑:
-- 列表:`TaskList.vue`(已存在)
-- 表单:`TaskForm.vue`(已存在)
-
-接入流程后建议实现:
-- 列表 “开始检验 / 继续检验” → 发起流程(create)
-- 详情查看 → 进入流程详情(detail)
-
----
-
-## 5. 路由配置(参照 remaining.ts 的请假流程配置)
-
-为 IQC 任务流程新增隐藏路由(create/detail),用于:
-- 从任务列表发起流程
-- 在流程流转/审批中查看业务详情
-
-示例(路径可按实际模块调整):
-```ts
-{
-  path: '/qms',
-  component: Layout,
-  name: 'qms',
-  meta: { hidden: true },
-  children: [
-    {
-      path: 'iqc/task/create',
-      component: () => import('@/views/qms/TaskForm.vue'),
-      name: 'IqcTaskCreate',
-      meta: {
-        noCache: true,
-        hidden: true,
-        canTo: true,
-        title: '发起 IQC 任务',
-        activeMenu: '/qms/iqc/task/list'
-      }
-    },
-    {
-      path: 'iqc/task/detail/:id',
-      component: () => import('@/views/qms/TaskForm.vue'),
-      name: 'IqcTaskDetail',
-      meta: {
-        noCache: true,
-        hidden: true,
-        canTo: true,
-        title: '查看 IQC 任务',
-        activeMenu: '/qms/iqc/task/list'
-      }
-    }
-  ]
-}
-```
-
----
-
-## 6. 流程结束回调监听与业务状态更新(必须)
-
-### 6.1 监听器机制
-审批结束时(通过 / 不通过 / 取消),后端必须监听最终结果并更新业务表状态。
-
-请假流程实现参考:
-- `BpmOALeaveStatusListener`
-(继承 `BpmProcessInstanceStatusEventListener`)
-
-### 6.2 IQC 任务监听器要求
-需新增监听器(示例命名:`BpmIqcTaskStatusListener`),继承:
-- `BpmProcessInstanceStatusEventListener`
-
-监听流程结束结果并更新:
-- `qms_qcp_insappnentry.flowstate`
-- `qms_qcp_insappnentry.FINSPECTSTATUS`(按业务规则设置为“检验完成”或“待检验”)
-
----
-
-## 7. 交付清单(Checklist)
-
-### 7.1 后端
-- [ ] 定义 `PROCESS_KEY = "iqc_task"`(以实际模型为准)
-- [ ] Controller(参照 `BpmOALeaveController`)
-- [ ] ServiceImpl(参照 `BpmOALeaveServiceImpl`)
-- [ ] 调用 `BpmProcessInstanceApi#createProcessInstance(...)` 创建流程实例
-- [ ] 回写 `process_instance_id`
-- [ ] 提交后/结束后 `flowstate` 更新逻辑(建议走 Listener)
-- [ ] 新增流程结束监听器(继承 `BpmProcessInstanceStatusEventListener`)
-
-### 7.2 前端
-- [ ] 任务列表 “开始检验/继续检验” 触发流程发起
-- [ ] create 页面(复用 `TaskForm.vue` 或新建)
-- [ ] detail 页面(复用 `TaskForm.vue` 或新建)
-- [ ] `router/modules/remaining.ts` 增加 IQC 任务 create/detail 路由

+ 0 - 186
docs/qms/IQC来料检验申请-后端设计.md

@@ -1,186 +0,0 @@
-IQC 来料检验申请列表 - 后端设计(MVP)
-
-目标
-- 聚焦“来料检验申请”列表接口,满足前端列表展示/筛选/分页
-- 单据编号使用 FBILLNO,申请人用 system_users.username,状态返回中文
-
-1) VO 定义(建议先定 VO)
-
-IqcApplyPageReqVO
-- keyword: String(模糊匹配 FBILLNO/FCOMMENT)
-- statusList: List<String>(值:待检验/检验中/检验完成)
-- applicantIds: List<Long>
-- beginTime: LocalDateTime(申请时间起)
-- endTime: LocalDateTime(申请时间止)
-- pageNo: Integer
-- pageSize: Integer
-
-IqcApplyPageRespVO
-- id: String(FBILLNO)
-- applicantId: Long(FAPPLYUSER)
-- applicantName: String(system_users.username)
-- applyTime: LocalDateTime(FAPPLYTIME)
-- status: String(中文:待检验/检验中/检验完成)
-- materialCount: Integer(子表行数)
-- remark: String(FCOMMENT)
-
-2) DO(最小字段集)
-
-qms_qcp_inspecapplyn(主表)
-- id: Long
-- FBILLNO: String
-- FAPPLYUSER: Long
-- FAPPLYTIME: LocalDateTime
-- FCOMMENT: String
-- FCREATETIME: LocalDateTime(排序可用)
-
-qms_qcp_insappnentry(子表)
-- id: Long
-- glid: Long(关联主表 id)
-- FINSPECTSTATUS: String(中文:待检验/检验中/检验完成)
-
-system_users(用户表)
-- id: Long
-- username: String
-
-3) Mapper SQL 草案(分页列表)
-
-要点
-- 状态由子表汇总:
-  - 任一行“检验中” => 检验中
-  - 全部“检验完成” => 检验完成
-  - 其他/无子表 => 待检验
-- 申请人通过 system_users.id 关联 system_users.username
-
-示意 SQL(MyBatis 可按此改写)
-SELECT
-  a.FBILLNO AS id,
-  a.FAPPLYUSER AS applicantId,
-  u.username AS applicantName,
-  a.FAPPLYTIME AS applyTime,
-  a.FCOMMENT AS remark,
-  COUNT(b.id) AS materialCount,
-  CASE
-    WHEN SUM(CASE WHEN b.FINSPECTSTATUS = '检验中' THEN 1 ELSE 0 END) > 0 THEN '检验中'
-    WHEN COUNT(b.id) > 0
-      AND SUM(CASE WHEN b.FINSPECTSTATUS = '检验完成' THEN 1 ELSE 0 END) = COUNT(b.id) THEN '检验完成'
-    ELSE '待检验'
-  END AS status
-FROM qms_qcp_inspecapplyn a
-LEFT JOIN system_users u ON u.id = a.FAPPLYUSER
-LEFT JOIN qms_qcp_insappnentry b ON b.glid = a.id
-WHERE 1 = 1
-  -- keyword:FBILLNO/FCOMMENT 模糊匹配
-  -- applicantIds:a.FAPPLYUSER IN (...)
-  -- 时间:a.FAPPLYTIME BETWEEN beginTime AND endTime
-GROUP BY a.FBILLNO, a.FAPPLYUSER, u.username, a.FAPPLYTIME, a.FCOMMENT
--- 若要按状态筛选,可在外层包一层并对 status 过滤
-ORDER BY a.FCREATETIME DESC
-
-4) Mapper XML 示例(分页列表)
-
-说明
-- 先在子查询中计算 status,再在外层按 status 过滤,避免 HAVING 复杂度
-- SQL 仅示意,按实际分页插件(MyBatis-Plus / PageHelper)调整
-
-示例(XML 片段)
-```xml
-<select id="selectIqcApplyPage" resultType="cn.iocoder.yudao.module.qms.controller.admin.iqc.vo.IqcApplyPageRespVO">
-  SELECT *
-  FROM (
-    SELECT
-      a.FBILLNO AS id,
-      a.FAPPLYUSER AS applicantId,
-      u.username AS applicantName,
-      a.FAPPLYTIME AS applyTime,
-      a.FCOMMENT AS remark,
-      COUNT(b.id) AS materialCount,
-      CASE
-        WHEN SUM(CASE WHEN b.FINSPECTSTATUS = '检验中' THEN 1 ELSE 0 END) > 0 THEN '检验中'
-        WHEN COUNT(b.id) > 0
-          AND SUM(CASE WHEN b.FINSPECTSTATUS = '检验完成' THEN 1 ELSE 0 END) = COUNT(b.id) THEN '检验完成'
-        ELSE '待检验'
-      END AS status
-    FROM qms_qcp_inspecapplyn a
-    LEFT JOIN system_users u ON u.id = a.FAPPLYUSER
-    LEFT JOIN qms_qcp_insappnentry b ON b.glid = a.id
-    <where>
-      <if test="keyword != null and keyword != ''">
-        AND (a.FBILLNO LIKE CONCAT('%', #{keyword}, '%')
-             OR a.FCOMMENT LIKE CONCAT('%', #{keyword}, '%'))
-      </if>
-      <if test="applicantIds != null and applicantIds.size() > 0">
-        AND a.FAPPLYUSER IN
-        <foreach collection="applicantIds" item="id" open="(" separator="," close=")">
-          #{id}
-        </foreach>
-      </if>
-      <if test="beginTime != null and endTime != null">
-        AND a.FAPPLYTIME BETWEEN #{beginTime} AND #{endTime}
-      </if>
-    </where>
-    GROUP BY a.FBILLNO, a.FAPPLYUSER, u.username, a.FAPPLYTIME, a.FCOMMENT
-  ) t
-  <where>
-    <if test="statusList != null and statusList.size() > 0">
-      AND t.status IN
-      <foreach collection="statusList" item="status" open="(" separator="," close=")">
-        #{status}
-      </foreach>
-    </if>
-  </where>
-  ORDER BY t.applyTime DESC
-</select>
-```
-
-5) Service / Controller 设计(建议)
-
-Controller
-- GET /admin-api/qms/iqc-apply/page
-  - 入参:IqcApplyPageReqVO
-  - 出参:PageResult<IqcApplyPageRespVO>
-
-Service
-- IqcApplyService#getIqcApplyPage(IqcApplyPageReqVO reqVO)
-  - 负责参数校验、调用 Mapper、封装分页结果
-
-6) 接口契约(示例)
-
-请求示例
-```http
-GET /admin-api/qms/iqc-apply/page?keyword=IQC&statusList=待检验&applicantIds=1&pageNo=1&pageSize=20
-```
-
-响应示例
-```json
-{
-  "code": 0,
-  "data": {
-    "list": [
-      {
-        "id": "IQC20240115001",
-        "applicantId": 1,
-        "applicantName": "zhangsan",
-        "applyTime": "2024-01-15 09:30:00",
-        "status": "待检验",
-        "materialCount": 5,
-        "remark": "紧急物料,请优先处理"
-      }
-    ],
-    "total": 1
-  }
-}
-```
-
-7) 前端对接清单(最小改动)
-
-- 列表接口地址:/admin-api/qms/iqc-apply/page
-- 字段映射:
-  - id -> 单据编号(FBILLNO)
-  - applicantName -> 申请人
-  - applyTime -> 申请时间
-  - status -> 状态(中文)
-  - materialCount -> 物料数
-  - remark -> 备注
-- 状态显示:已在 StatusBadge 与筛选项里支持中文
-

Файловите разлики са ограничени, защото са твърде много
+ 0 - 111
docs/qms/IQC来料质量检验.md


+ 0 - 87
docs/qms/QMS_INTEGRATION_GUIDE.md

@@ -1,87 +0,0 @@
-# 质量管理系统 (QMS) 整合指南
-
-## 菜单结构
-
-```
-质量管理系统 (6100) - /qms
-├── IQC 来料检验 (6110)
-│   ├── 来料检验申请 (6111) - /qms/iqc/apply/list
-│   └── 来料检验任务 (6112) - /qms/iqc/task/list
-├── IPQC 过程检验 (6120)
-│   ├── 过程检验申请 (6121) - /qms/ipqc/apply/list
-│   └── 过程检验任务 (6122) - /qms/ipqc/task/list
-├── FQC 成品检验 (6130)
-│   ├── 成品检验申请 (6131) - /qms/fqc/apply/list
-│   └── 成品检验任务 (6132) - /qms/fqc/task/list
-└── SPEC 检规 (6140)
-    ├── 检规索引 (6141) - /qms/spec/index
-    └── 导入检规 (6142) - /qms/spec/import
-```
-
-## 目录结构
-
-```
-src/
-├── views/qms/                    # 质量管理视图
-│   ├── ApplicationList.vue       # 申请列表(IQC/IPQC/FQC 共用)
-│   ├── ApplicationForm.vue       # 申请表单
-│   ├── TaskList.vue              # 任务列表
-│   ├── TaskForm.vue              # 任务表单
-│   ├── Favorites.vue             # 我的关注
-│   ├── Help.vue                  # 帮助
-│   └── spec/                     # 检规管理
-│       ├── SpecIndex.vue         # 检规索引
-│       ├── SpecDetail.vue        # 检规详情
-│       ├── SpecVersion.vue       # 版本对比
-│       └── SpecImport.vue        # 导入检规
-├── components/Iqc/               # 质量管理组件
-│   ├── StatusBadge.vue           # 状态徽标
-│   ├── SearchFilter.vue          # 搜索筛选
-│   ├── PermissionButton.vue      # 权限按钮
-│   └── EmptyState.vue            # 空状态
-├── store/modules/iqc/            # 状态管理
-│   ├── user.ts                   # 用户权限
-│   ├── application.ts            # 申请管理
-│   └── task.ts                   # 任务管理
-└── config/iqcModules.ts          # 模块配置
-```
-
-## 路由配置
-
-路由基础路径: `/qms`
-
-| 路由路径 | 名称 | 组件 |
-|---------|------|------|
-| /qms/iqc/apply/list | IqcApplicationList | ApplicationList.vue |
-| /qms/iqc/task/list | IqcTaskList | TaskList.vue |
-| /qms/ipqc/apply/list | IpqcApplicationList | ApplicationList.vue |
-| /qms/ipqc/task/list | IpqcTaskList | TaskList.vue |
-| /qms/fqc/apply/list | FqcApplicationList | ApplicationList.vue |
-| /qms/fqc/task/list | FqcTaskList | TaskList.vue |
-| /qms/spec/index | SpecIndex | spec/SpecIndex.vue |
-| /qms/spec/import | SpecImport | spec/SpecImport.vue |
-
-## 数据库菜单
-
-执行 `sql/mysql/custom_modules_menu.sql` 中的质量管理系统部分。
-
-## 权限标识
-
-- `qms:iqc:apply:list` - IQC 来料检验申请
-- `qms:iqc:task:list` - IQC 来料检验任务
-- `qms:ipqc:apply:list` - IPQC 过程检验申请
-- `qms:ipqc:task:list` - IPQC 过程检验任务
-- `qms:fqc:apply:list` - FQC 成品检验申请
-- `qms:fqc:task:list` - FQC 成品检验任务
-- `qms:spec:index` - 检规索引
-- `qms:spec:import` - 导入检规
-
-## 组件复用
-
-IQC/IPQC/FQC 三种检验类型共用同一套组件,通过 `route.meta.module` 区分模块类型:
-- `iqc` - 来料检验
-- `ipqc` - 过程检验
-- `fqc` - 成品检验
-- `spec` - 检规管理
-
-配置文件 `iqcModules.ts` 定义了各模块的差异化配置。

+ 0 - 240
docs/qms/codex-dev-playbook.md

@@ -1,240 +0,0 @@
-# Codex 提示词(WSL + VS Code + 芋道/Yudao)前后端开发与自校验规范
-
-> 用途:把本文件放入项目(建议 `docs/codex-dev-playbook.md` 或根目录 `CODEX_DEV_PROMPT.md`),用于驱动 Codex **自行开发 + 自行校验**。  
-> 目标:在当前系统(WSL2 + VS Code Remote-WSL + 多模块 Maven + pnpm 前端)下,快速、稳定地完成“需求→代码→联调→验收”。
-
----
-
-## 0. 你的角色与输出要求(给 Codex 的总指令)
-
-你是项目的“全栈工程助手”,需要在不遗漏工程化细节的前提下完成开发任务。每次改动必须满足:
-
-- **最小可跑通**:后端接口可启动可调用;前端页面可访问可返回列表/详情。
-- **工程一致性**:遵循芋道项目分层与命名规范;模块边界清晰。
-- **自校验**:每次交付都包含 *如何验证*(命令/URL/返回示例/截图点位)。
-- **不走捷径**:不要用临时 hardcode 绕开权限/租户/路由,除非明确写在“临时方案”并给出回滚方式。
-
-输出格式固定为:
-
-1) 你要做什么(任务拆解清单)  
-2) 你改了哪些文件(文件路径列表 + 关键 diff)  
-3) 你如何验证(命令 + 预期结果)  
-4) 风险与回滚(潜在影响 + 如何撤销)
-
----
-
-## 1. 环境与工具链硬约束(避免踩坑)
-
-### 1.1 WSL 下运行后端:禁止“单文件运行”
-- **禁止**使用 VS Code Code Runner 的 *Run Code*(它只会 `javac` 单文件,会导致 `org.springframework.boot` 等依赖找不到)。
-- **必须**使用 Maven/IDEA/Java 扩展的 *Run Java* 或 Maven 命令启动。
-
-### 1.2 多模块 Maven:在哪个目录执行很关键
-- 聚合根目录不一定声明 `spring-boot` 插件,根目录直接 `mvn spring-boot:run` 可能失败。
-- 推荐命令(任选其一):
-  - `cd yudao-server && mvn -DskipTests spring-boot:run`
-  - 或:`mvn -pl yudao-server -am -DskipTests org.springframework.boot:spring-boot-maven-plugin:run`
-
-### 1.3 新增模块后:必须 install 一次
-- 新增 `yudao-module-xxx` 或改依赖后:
-  - `mvn -pl yudao-module-xxx -am -DskipTests install`
-- 否则 VS Code / Maven 会提示本地仓库缺 jar(`~/.m2` 不存在)。
-
-### 1.4 前端 baseURL:必须指向后端,不要请求打到 5173
-- `.env.local` 必须配置(示例):
-  - `VITE_BASE_URL='http://127.0.0.1:48080'`
-  - `VITE_API_URL=/admin-api`
-- 如验证码接口 404(打到 5173),优先检查 baseURL 与代理。
-
----
-
-## 2. 项目开发总流程(强制遵循的“从 0 到 1”)
-
-当你接到一个业务模块(如 IQC 申请列表)需求时,按以下顺序执行:
-
-### 2.1 先定“接口契约”(VO),再定“数据映射”(DO)
-1) 明确前端需要的字段与含义(含显示名/枚举中文/时间格式)。  
-2) **先创建 VO**:`*RespVO` / `*PageReqVO` 等。  
-3) 之后再考虑 DO / entity / 表字段映射(如果需要)。
-
-### 2.2 模块边界:优先拆独立模块
-- 建议将业务放入独立模块,例如:
-  - `yudao-module-qms`(质量相关)
-- 目的:隔离依赖、便于发布、避免污染 `system` 等核心模块。
-
-### 2.3 后端开发顺序(固定)
-1) Controller:定义路由与入参(分页、筛选)。  
-2) Service:定义业务接口与实现(先跑通空分页也行)。  
-3) Mapper + XML:落地 SQL(再逐步补齐 join/聚合)。  
-4) 权限/租户:对齐项目现有注解与过滤器,不要私自绕过。  
-5) Swagger/Knife4j:确保接口可在文档里调通。
-
-### 2.4 前端开发顺序(固定)
-1) API 封装:`src/api/...` 定义请求与返回类型。  
-2) 页面与路由:保证 list/detail/edit 等页面可访问。  
-3) 菜单/权限:如走动态路由,确保后端菜单配置可生成路由。  
-4) 联调:Network 检查 URL 是否正确(不是 5173)。
-
----
-
-## 3. 动态路由 / 菜单(最常见 404 的根因与规则)
-
-### 3.1 菜单 path 规则(硬规则)
-- **子菜单 path 不要以 “/” 开头**,并且必须相对父菜单。
-- 否则最终路由拼接会错,导致“返回列表 404”。
-
-### 3.2 动态路由缓存
-- 前端通常缓存 `roleRouters`(或类似字段)。
-- **菜单/路由改动后必须清缓存**(localStorage/sessionStorage)并刷新,否则仍旧 404。
-
-### 3.3 “返回列表 404”的诊断 checklist
-当你发现“新增/编辑/查看后返回列表 404”:
-1) 打开控制台打印当前注册 routes,确认是否存在 `/xxx/list`。  
-2) 若只有 new/edit/view,而没有 list,则:  
-   - 要么 list 页面没注册路由  
-   - 要么动态路由菜单没下发 list  
-3) 修复后清缓存再验证。
-
----
-
-## 4. IQC 申请列表的字段口径与 SQL 聚合规范(可复用模板)
-
-### 4.1 列表主键与显示
-- 返回给前端的 `id` 用 `FBILLNO`(或按需求指定的业务单号),而非数据库自增 id(除非前端明确要求)。
-
-### 4.2 申请人字段
-- `FAPPLYUSER` 存用户 id,列表显示需要 join `system_users.id -> system_users.username`。
-
-### 4.3 状态聚合(示例规则)
-- 主表状态由子表汇总得出(示例优先级):
-  1) 若任一子表为“检验中” → 主表“检验中”
-  2) 否则若所有子表“检验完成” → 主表“检验完成”
-  3) 否则 → 主表“待检验”
-- 需要同时返回:子表总数、完成数、检验中数(便于前端展示与筛选)。
-
-> 注意:状态中文化建议由后端返回中文(避免前端二次映射出错),除非项目规范要求前端映射。
-
----
-
-## 5. 交付时必须包含的自校验(不写=未完成)
-
-### 5.1 后端自检命令(必须给出)
-- 编译:
-  - `mvn -pl yudao-server -am -DskipTests package`
-- 新模块依赖安装(如新增模块):
-  - `mvn -pl yudao-module-qms -am -DskipTests install`
-- 启动:
-  - `cd yudao-server && mvn -DskipTests spring-boot:run`
-
-### 5.2 接口自检(必须给出)
-- 以 Swagger/Knife4j 或 curl 方式,提供:
-  - URL
-  - Header(token、tenant-id)
-  - 请求参数示例
-  - 返回 JSON 示例(至少包含关键字段)
-
-### 5.3 前端自检(必须给出)
-- 启动:
-  - `pnpm i`
-  - `pnpm dev`
-- 检查点:
-  1) Network 请求 baseURL 是否指向后端端口
-  2) 列表页能否加载
-  3) 新增/编辑/查看返回列表是否 404
-  4) 清缓存后动态路由是否生效
-
----
-
-## 6. Codex 开发提示词模板(复制即用)
-
-> 你可以直接把下面这一段当作每次给 Codex 的 Prompt 开头。
-
-### 6.1 通用开发 Prompt
-- 任务:在当前项目(WSL + VS Code Remote-WSL + 多模块 Maven + pnpm 前端)下,实现【填业务功能】。
-- 约束:
-  1) 禁止单文件运行 Spring Boot;必须通过 Maven 启动与编译校验。
-  2) 若新增模块或依赖,必须执行 `mvn -pl <module> -am -DskipTests install`。
-  3) 前端 baseURL 必须指向 `http://127.0.0.1:48080`(或项目实际端口)。
-  4) 动态路由:子菜单 path 不能以 `/` 开头;改菜单后清缓存验证。
-- 输出要求(必须逐项给出):
-  1) 任务拆解清单
-  2) 修改文件路径列表 + 关键 diff
-  3) 自校验命令与预期结果(后端/前端/接口)
-  4) 风险与回滚方案
-
-### 6.2 IQC 申请列表 Prompt(示例)
-- 任务:实现 IQC 来料检验“申请列表”接口与前端页面联调,要求:
-  - 列表主键返回 `FBILLNO`(作为 `id`)。
-  - `FAPPLYUSER` 关联 `system_users` 显示申请人 username。
-  - 状态从子表聚合得到,并返回中文:待检验/检验中/检验完成。
-  - 返回分页字段符合芋道分页规范。
-- 交付:接口可在 Swagger 调通,前端列表可加载且“返回列表”不 404。
-
----
-
-## 7. 快速故障定位(遇到问题先按这里排)
-
-### 7.1 后端启动类报错
-- `package org.springframework.boot does not exist`:
-  - 你用错了运行方式(单文件 javac),改用 Maven 启动。
-- `No plugin found for prefix 'spring-boot'`:
-  - 你在聚合根目录运行了,cd 到 `yudao-server` 或用插件全坐标。
-- “缺 jar / m2 不存在”:
-  - 新增模块未 install:`mvn -pl <module> -am -DskipTests install`
-
-### 7.2 前端接口 404
-- 请求打到 5173:
-  - baseURL 未配置或 env 没生效,检查 `.env.local`,重启 dev。
-
-### 7.3 页面返回列表 404
-- routes 没有 list:
-  - 路由未注册或动态菜单未下发 list。
-- 改菜单没生效:
-  - 清 `roleRouters` 等缓存,硬刷新。
-
----
-
-## 8. 你必须遵循的“完成定义”(Definition of Done)
-
-只有同时满足以下条件才算完成:
-
----
-
-## 9. 今日问题记录(IPQC 任务 / 2026-01-22~23)
-
-### 9.1 后端 500:多租户自动拼接 tenant_id
-- 现象:`/admin-api/qms/ipqc-task/page` 报 500,日志里出现 `Unknown column 'tenant_id'`。
-- 根因:多租户拦截器自动追加 `tenant_id`,但 `WorkOrdMaster / ItemMaster / mes_morder / qms_gcjyd` 无该字段。
-- 处理:在 `yudao-server/src/main/resources/application.yaml` 的 `yudao.tenant.ignore-tables` 增加这四张表,避免注入。
-
-### 9.2 后端 404:接口未注册
-- 现象:请求命中 `127.0.0.1:48080` 仍返回 `{"code":404,"msg":"请求地址不存在:admin-api/qms/ipqc-task/page"}`。
-- 根因:本地运行的 `yudao-module-qms` jar 不是最新,`IpqcTaskController` 未被打包进运行时。
-- 处理:
-  1) 先执行 `mvn -pl yudao-module-qms -am -DskipTests install`  
-  2) 再重启 `yudao-server`
-  3) 可用 `jar tf ~/.m2/repository/.../yudao-module-qms-*.jar | rg IpqcTaskController` 验证
-
-### 9.3 后端启动失败:端口占用
-- 现象:启动时报 `Port 48080 was already in use`。
-- 处理:停止旧进程(`ss -ltnp | rg :48080` 找 PID,再 `kill <pid>`),再启动。
-
-### 9.4 VS Code JMX 刷新失败提示
-- 现象:`Failed to refresh live data from process service:jmx...`
-- 结论:仅 VS Code Spring Boot Dashboard 监控失败,不影响接口功能,可忽略或重连监控。
-
-### 9.5 验证方法(最小闭环)
-- 后端直连验证:  
-  `curl "http://127.0.0.1:48080/admin-api/qms/ipqc-task/page?pageNo=1&pageSize=20&status=pending"`
-- 预期返回:  
-  未登录返回 `401`;登录后应返回 `code=0` 且有分页结构。
-
-- [ ] 后端 `mvn -pl yudao-server -am -DskipTests package` 通过  
-- [ ] 后端启动后接口可访问(Swagger/curl)  
-- [ ] 前端启动后列表页可打开、可加载数据  
-- [ ] 新增/编辑/查看后返回列表不 404  
-- [ ] 文档包含:改动文件列表 + 验证步骤 + 回滚方式  
-
----
-
-> 维护建议:每做完一个模块,把“字段口径/状态规则/路由菜单坑点”沉淀在本文件的对应章节,逐步形成团队的工程手册。

+ 7 - 0
yudao-module-qms/src/main/java/cn/iocoder/yudao/module/qms/controller/admin/ipqc/IpqcTaskController.java

@@ -41,6 +41,13 @@ public class IpqcTaskController {
         return success(ipqcTaskService.getIpqcTaskPage(pageReqVO));
     }
 
+    @GetMapping("/get")
+    @Operation(summary = "获取过程检验任务")
+    @PreAuthorize("@ss.hasPermission('qms:ipqc:task:list')")
+    public CommonResult<IpqcTaskRespVO> getIpqcTask(@RequestParam("id") Long id) {
+        return success(ipqcTaskService.getIpqcTask(id));
+    }
+
     @PostMapping("/start")
     @Operation(summary = "发起过程检验任务流程")
     @PreAuthorize("@ss.hasPermission('qms:ipqc:task:list')")

+ 60 - 6
yudao-module-qms/src/main/java/cn/iocoder/yudao/module/qms/controller/admin/ipqc/vo/IpqcInspectRecordDetailRespVO.java

@@ -21,6 +21,15 @@ public class IpqcInspectRecordDetailRespVO {
     @Schema(description = "检验标准")
     private String jybz;
 
+    @Schema(description = "上限")
+    private String sx;
+
+    @Schema(description = "下限")
+    private String xx;
+
+    @Schema(description = "样本量")
+    private BigDecimal jypc;
+
     @Schema(description = "样本1")
     private String j1;
 
@@ -30,14 +39,59 @@ public class IpqcInspectRecordDetailRespVO {
     @Schema(description = "样本3")
     private String j3;
 
-    @Schema(description = "判定")
-    private Long pd;
+    @Schema(description = "样本4")
+    private String j4;
+
+    @Schema(description = "样本5")
+    private String j5;
+
+    @Schema(description = "样本6")
+    private String j6;
+
+    @Schema(description = "样本7")
+    private String j7;
+
+    @Schema(description = "样本8")
+    private String j8;
+
+    @Schema(description = "样本9")
+    private String j9;
+
+    @Schema(description = "样本10")
+    private String j10;
 
-    @Schema(description = "检验数量")
-    private BigDecimal jysl;
+    @Schema(description = "样本11")
+    private String j11;
 
-    @Schema(description = "不合格数量")
-    private BigDecimal bhgsl;
+    @Schema(description = "样本12")
+    private String j12;
+
+    @Schema(description = "样本13")
+    private String j13;
+
+    @Schema(description = "样本14")
+    private String j14;
+
+    @Schema(description = "样本15")
+    private String j15;
+
+    @Schema(description = "样本16")
+    private String j16;
+
+    @Schema(description = "样本17")
+    private String j17;
+
+    @Schema(description = "样本18")
+    private String j18;
+
+    @Schema(description = "样本19")
+    private String j19;
+
+    @Schema(description = "样本20")
+    private String j20;
+
+    @Schema(description = "判定")
+    private Long pd;
 
     @Schema(description = "备注")
     private String bz;

+ 19 - 4
yudao-module-qms/src/main/java/cn/iocoder/yudao/module/qms/controller/admin/ipqc/vo/IpqcInspectRecordMainRespVO.java

@@ -54,11 +54,26 @@ public class IpqcInspectRecordMainRespVO {
     @Schema(description = "检验人")
     private String jyr;
 
-    @Schema(description = "最大样本量")
-    private BigDecimal ybl;
+    @Schema(description = "检验数量")
+    private BigDecimal jysl;
 
-    @Schema(description = "结果判定")
-    private Long jgpd;
+    @Schema(description = "合格数量")
+    private BigDecimal jyhgsl;
+
+    @Schema(description = "不合格数量")
+    private BigDecimal jybhgsl;
+
+    @Schema(description = "处理方式")
+    private String clfs;
+
+    @Schema(description = "留样数量")
+    private BigDecimal lysl;
+
+    @Schema(description = "破坏数量")
+    private BigDecimal phsl;
+
+    @Schema(description = "判定")
+    private Long pd;
 
     @Schema(description = "附件")
     private String fj;

+ 87 - 18
yudao-module-qms/src/main/java/cn/iocoder/yudao/module/qms/controller/admin/ipqc/vo/IpqcInspectRecordSaveReqVO.java

@@ -60,11 +60,26 @@ public class IpqcInspectRecordSaveReqVO {
     @Schema(description = "检验人")
     private String jyr;
 
-    @Schema(description = "最大样本量")
-    private BigDecimal ybl;
+    @Schema(description = "检验数量")
+    private BigDecimal jysl;
 
-    @Schema(description = "结果判定")
-    private Long jgpd;
+    @Schema(description = "合格数量")
+    private BigDecimal jyhgsl;
+
+    @Schema(description = "不合格数量")
+    private BigDecimal jybhgsl;
+
+    @Schema(description = "处理方式")
+    private String clfs;
+
+    @Schema(description = "留样数量")
+    private BigDecimal lysl;
+
+    @Schema(description = "破坏数量")
+    private BigDecimal phsl;
+
+    @Schema(description = "判定")
+    private Long pd;
 
     @Schema(description = "附件")
     private String fj;
@@ -94,26 +109,80 @@ public class IpqcInspectRecordSaveReqVO {
         @Schema(description = "检验项目")
         private String jyxm;
 
-        @Schema(description = "检验标准")
-        private String jybz;
+    @Schema(description = "检验标准")
+    private String jybz;
+
+    @Schema(description = "上限")
+    private String sx;
+
+    @Schema(description = "下限")
+    private String xx;
+
+    @Schema(description = "样本量")
+    private BigDecimal jypc;
+
+    @Schema(description = "样本1")
+    private String j1;
+
+    @Schema(description = "样本2")
+    private String j2;
+
+    @Schema(description = "样本3")
+    private String j3;
+
+    @Schema(description = "样本4")
+    private String j4;
+
+    @Schema(description = "样本5")
+    private String j5;
+
+    @Schema(description = "样本6")
+    private String j6;
+
+    @Schema(description = "样本7")
+    private String j7;
+
+    @Schema(description = "样本8")
+    private String j8;
+
+    @Schema(description = "样本9")
+    private String j9;
+
+    @Schema(description = "样本10")
+    private String j10;
+
+    @Schema(description = "样本11")
+    private String j11;
+
+    @Schema(description = "样本12")
+    private String j12;
+
+    @Schema(description = "样本13")
+    private String j13;
+
+    @Schema(description = "样本14")
+    private String j14;
+
+    @Schema(description = "样本15")
+    private String j15;
 
-        @Schema(description = "样本1")
-        private String j1;
+    @Schema(description = "样本16")
+    private String j16;
 
-        @Schema(description = "样本2")
-        private String j2;
+    @Schema(description = "样本17")
+    private String j17;
 
-        @Schema(description = "样本3")
-        private String j3;
+    @Schema(description = "样本18")
+    private String j18;
 
-        @Schema(description = "判定")
-        private Long pd;
+    @Schema(description = "样本19")
+    private String j19;
 
-        @Schema(description = "检验数量")
-        private BigDecimal jysl;
+    @Schema(description = "样本20")
+    private String j20;
 
-        @Schema(description = "不合格数量")
-        private BigDecimal bhgsl;
+    @Schema(description = "判定")
+    private Long pd;
 
         @Schema(description = "备注")
         private String bz;

+ 3 - 0
yudao-module-qms/src/main/java/cn/iocoder/yudao/module/qms/controller/admin/ipqc/vo/IpqcTaskProcessInfoVO.java

@@ -21,4 +21,7 @@ public class IpqcTaskProcessInfoVO {
 
     @Schema(description = "批次")
     private String batch;
+
+    @Schema(description = "检验数量")
+    private java.math.BigDecimal inspectQty;
 }

+ 3 - 0
yudao-module-qms/src/main/java/cn/iocoder/yudao/module/qms/dal/mysql/ipqc/IpqcTaskMapper.java

@@ -22,6 +22,9 @@ public interface IpqcTaskMapper {
     @TenantIgnore
     Page<IpqcTaskRespVO> selectTaskPage(Page<IpqcTaskRespVO> page, @Param("req") IpqcTaskPageReqVO req);
 
+    @TenantIgnore
+    IpqcTaskRespVO selectTaskById(@Param("id") Long id);
+
     @TenantIgnore
     IpqcTaskProcessInfoVO selectTaskProcessInfo(@Param("id") Long id);
 

+ 2 - 0
yudao-module-qms/src/main/java/cn/iocoder/yudao/module/qms/service/ipqc/IpqcTaskService.java

@@ -13,6 +13,8 @@ public interface IpqcTaskService {
 
     PageResult<IpqcTaskRespVO> getIpqcTaskPage(IpqcTaskPageReqVO pageReqVO);
 
+    IpqcTaskRespVO getIpqcTask(Long taskId);
+
     String startIpqcTaskProcess(Long taskId);
 
     IpqcInspectRecordMainRespVO getInspectRecordMain(Long taskId);

+ 14 - 0
yudao-module-qms/src/main/java/cn/iocoder/yudao/module/qms/service/ipqc/IpqcTaskServiceImpl.java

@@ -39,6 +39,14 @@ public class IpqcTaskServiceImpl implements IpqcTaskService {
         return ipqcTaskMapper.selectTaskPage(pageReqVO);
     }
 
+    @Override
+    public IpqcTaskRespVO getIpqcTask(Long taskId) {
+        if (taskId == null) {
+            return null;
+        }
+        return ipqcTaskMapper.selectTaskById(taskId);
+    }
+
     @Override
     public String startIpqcTaskProcess(Long taskId) {
         if (taskId == null) {
@@ -81,6 +89,9 @@ public class IpqcTaskServiceImpl implements IpqcTaskService {
             main.setLydjbh(workOrder);
             main.setScph(batch);
             main.setWlbm(taskInfo.getMaterialCode());
+            main.setJysl(taskInfo.getInspectQty());
+        } else if (main.getJysl() == null) {
+            main.setJysl(taskInfo.getInspectQty());
         }
         return main;
     }
@@ -117,6 +128,9 @@ public class IpqcTaskServiceImpl implements IpqcTaskService {
         reqVO.setLydjbh(workOrder);
         reqVO.setScph(batch);
         reqVO.setWlbm(materialCode);
+        if (reqVO.getJysl() == null) {
+            reqVO.setJysl(taskInfo.getInspectQty());
+        }
         IpqcInspectRecordMainRespVO existing = ipqcTaskMapper.selectInspectRecordMain(workOrder, batch);
 
         Long recordId = reqVO.getId();

+ 112 - 19
yudao-module-qms/src/main/resources/mapper/ipqc/IpqcTaskMapper.xml

@@ -25,7 +25,6 @@
             fr.OrdDate AS startTime,
             fr.DueDate AS completeTime,
             fr.Remark AS remark,
-            bpi.process_instance_id AS processInstanceId,
             CASE d.status_rank
                 WHEN 3 THEN 'processing'
                 WHEN 2 THEN 'completed'
@@ -71,13 +70,6 @@
         ) d
             ON d.scph = fr.LotSerial
            AND d.lydjbh = fr.WorkOrd
-        LEFT JOIN (
-            SELECT business_key, MAX(id) AS process_instance_id
-            FROM bpm_process_instance
-            WHERE process_definition_key = 'qms_ipqc_task'
-            GROUP BY business_key
-        ) bpi
-            ON bpi.business_key = CAST(fr.recid AS CHAR)
         <where>
             <if test="req.factoryId != null and req.factoryId != ''">
                 AND fr.`Domain` = #{req.factoryId}
@@ -95,13 +87,86 @@
         ORDER BY fr.OrdDate DESC, fr.recid DESC
     </select>
 
+    <select id="selectTaskById" resultType="cn.iocoder.yudao.module.qms.controller.admin.ipqc.vo.IpqcTaskRespVO">
+        SELECT
+            fr.recid AS id,
+            fr.Drawing AS applicationId,
+            fr.WorkOrd AS workOrder,
+            fr.Typed AS process,
+            fr.WoTyped AS step,
+            fr.Project AS line,
+            NULL AS station,
+            NULL AS equipment,
+            fr.ItemNum AS materialCode,
+            im.Descr AS materialName,
+            fr.LotSerial AS batch,
+            fr.lbrvar AS responsibleName,
+            CASE
+                WHEN mo.MaterialSituation IN ('high', 'H', 'A', '1') THEN 'high'
+                WHEN mo.MaterialSituation IN ('medium', 'M', 'B', '2') THEN 'medium'
+                WHEN mo.MaterialSituation IN ('low', 'L', 'C', '3') THEN 'low'
+                ELSE 'medium'
+            END AS priority,
+            fr.OrdDate AS startTime,
+            fr.DueDate AS completeTime,
+            fr.Remark AS remark,
+            CASE d.status_rank
+                WHEN 3 THEN 'processing'
+                WHEN 2 THEN 'completed'
+                ELSE 'pending'
+            END AS status
+        FROM
+        (
+            SELECT
+                `Domain`,
+                WorkOrd,
+                ItemNum,
+                LotSerial,
+                SUM(IFNULL(QtyOrded, 0))     AS sum_QtyOrded,
+                SUM(IFNULL(QtyCompleted, 0)) AS sum_QtyCompleted,
+                COUNT(*)                     AS merge_cnt,
+                MIN(recid)                   AS first_recid
+            FROM WorkOrdMaster
+            WHERE IFNULL(Status, '') &lt;&gt; ''
+              AND Status &lt;&gt; 'c'
+            GROUP BY `Domain`, WorkOrd, ItemNum, LotSerial
+        ) AS g
+        INNER JOIN WorkOrdMaster fr
+            ON fr.recid = g.first_recid
+        LEFT JOIN ItemMaster im
+            ON im.ItemNum = fr.ItemNum
+           AND im.`Domain` = fr.`Domain`
+        LEFT JOIN mes_morder mo
+            ON mo.morder_no = fr.WorkOrd
+           AND mo.factory_id = fr.`Domain`
+        LEFT JOIN (
+            SELECT
+                lydjbh,
+                scph,
+                MAX(
+                    CASE
+                        WHEN `status` = '检验中' THEN 3
+                        WHEN `status` = '检验完成' THEN 2
+                        ELSE 1
+                    END
+                ) AS status_rank
+            FROM qms_gcjyd
+            GROUP BY lydjbh, scph
+        ) d
+            ON d.scph = fr.LotSerial
+           AND d.lydjbh = fr.WorkOrd
+        WHERE fr.recid = #{id}
+        LIMIT 1
+    </select>
+
     <select id="selectTaskProcessInfo" resultType="cn.iocoder.yudao.module.qms.controller.admin.ipqc.vo.IpqcTaskProcessInfoVO">
         SELECT
             a.recid AS id,
             a.Drawing AS applicationId,
             a.WorkOrd AS workOrder,
             a.ItemNum AS materialCode,
-            a.LotSerial AS batch
+            a.LotSerial AS batch,
+            a.QtyOrded AS inspectQty
         FROM WorkOrdMaster a
         WHERE a.recid = #{id}
     </select>
@@ -123,8 +188,13 @@
             lydjbh,
             jyrq,
             jyr,
-            ybl,
-            jgpd,
+            jysl,
+            jyhgsl,
+            jybhgsl,
+            clfs,
+            lysl,
+            phsl,
+            pd,
             fj,
             jgbh,
             jgbb,
@@ -143,12 +213,30 @@
             glid,
             jyxm,
             jybz,
+            sx,
+            xx,
+            jypc,
             j1,
             j2,
             j3,
+            j4,
+            j5,
+            j6,
+            j7,
+            j8,
+            j9,
+            j10,
+            j11,
+            j12,
+            j13,
+            j14,
+            j15,
+            j16,
+            j17,
+            j18,
+            j19,
+            j20,
             pd,
-            jysl,
-            bhgsl,
             bz
         FROM qms_gcjydzb
         WHERE glid = #{recordId}
@@ -157,9 +245,9 @@
 
     <insert id="insertInspectRecordMain">
         INSERT INTO qms_gcjyd
-        (id, bdbh, bbh, sxrq, djbh, cplx, scph, wlbm, wlmc, gxbm, gxmc, sczyry, lydjbh, jyrq, jyr, ybl, jgpd, fj, jgbh, jgbb, bz, status)
+        (id, bdbh, bbh, sxrq, djbh, cplx, scph, wlbm, wlmc, gxbm, gxmc, sczyry, lydjbh, jyrq, jyr, jysl, jyhgsl, jybhgsl, clfs, lysl, phsl, pd, fj, jgbh, jgbb, bz, status)
         VALUES
-        (#{id}, #{req.bdbh}, #{req.bbh}, #{req.sxrq}, #{djbh}, #{req.cplx}, #{batch}, #{req.wlbm}, #{req.wlmc}, #{req.gxbm}, #{req.gxmc}, #{req.sczyry}, #{workOrder}, #{req.jyrq}, #{req.jyr}, #{req.ybl}, #{req.jgpd}, #{req.fj}, #{req.jgbh}, #{req.jgbb}, #{req.bz}, #{status})
+        (#{id}, #{req.bdbh}, #{req.bbh}, #{req.sxrq}, #{djbh}, #{req.cplx}, #{batch}, #{req.wlbm}, #{req.wlmc}, #{req.gxbm}, #{req.gxmc}, #{req.sczyry}, #{workOrder}, #{req.jyrq}, #{req.jyr}, #{req.jysl}, #{req.jyhgsl}, #{req.jybhgsl}, #{req.clfs}, #{req.lysl}, #{req.phsl}, #{req.pd}, #{req.fj}, #{req.jgbh}, #{req.jgbb}, #{req.bz}, #{status})
     </insert>
 
     <update id="updateInspectRecordMain">
@@ -179,8 +267,13 @@
             lydjbh = #{workOrder},
             jyrq = #{req.jyrq},
             jyr = #{req.jyr},
-            ybl = #{req.ybl},
-            jgpd = #{req.jgpd},
+            jysl = #{req.jysl},
+            jyhgsl = #{req.jyhgsl},
+            jybhgsl = #{req.jybhgsl},
+            clfs = #{req.clfs},
+            lysl = #{req.lysl},
+            phsl = #{req.phsl},
+            pd = #{req.pd},
             fj = #{req.fj},
             jgbh = #{req.jgbh},
             jgbb = #{req.jgbb},
@@ -196,10 +289,10 @@
 
     <insert id="insertInspectRecordDetailBatch">
         INSERT INTO qms_gcjydzb
-        (id, glid, jyxm, jybz, j1, j2, j3, pd, jysl, bhgsl, bz)
+        (id, glid, jyxm, jybz, sx, xx, jypc, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15, j16, j17, j18, j19, j20, pd, bz)
         VALUES
         <foreach collection="list" item="item" separator=",">
-            (#{item.id}, #{recordId}, #{item.jyxm}, #{item.jybz}, #{item.j1}, #{item.j2}, #{item.j3}, #{item.pd}, #{item.jysl}, #{item.bhgsl}, #{item.bz})
+            (#{item.id}, #{recordId}, #{item.jyxm}, #{item.jybz}, #{item.sx}, #{item.xx}, #{item.jypc}, #{item.j1}, #{item.j2}, #{item.j3}, #{item.j4}, #{item.j5}, #{item.j6}, #{item.j7}, #{item.j8}, #{item.j9}, #{item.j10}, #{item.j11}, #{item.j12}, #{item.j13}, #{item.j14}, #{item.j15}, #{item.j16}, #{item.j17}, #{item.j18}, #{item.j19}, #{item.j20}, #{item.pd}, #{item.bz})
         </foreach>
     </insert>
 

+ 31 - 4
yudao-ui/yudao-ui-admin-vue3/src/api/qms/ipqc/task/index.ts

@@ -42,8 +42,13 @@ export interface IpqcInspectRecordMainRespVO {
   lydjbh?: string
   jyrq?: string
   jyr?: string
-  ybl?: number | string
-  jgpd?: number | string
+  jysl?: number | string
+  jyhgsl?: number | string
+  jybhgsl?: number | string
+  clfs?: string
+  lysl?: number | string
+  phsl?: number | string
+  pd?: number | string
   fj?: string | string[]
   jgbh?: string
   jgbb?: string
@@ -56,12 +61,30 @@ export interface IpqcInspectRecordDetailRespVO {
   glid?: number | string
   jyxm?: string
   jybz?: string
+  sx?: string
+  xx?: string
+  jypc?: number | string
   j1?: string
   j2?: string
   j3?: string
+  j4?: string
+  j5?: string
+  j6?: string
+  j7?: string
+  j8?: string
+  j9?: string
+  j10?: string
+  j11?: string
+  j12?: string
+  j13?: string
+  j14?: string
+  j15?: string
+  j16?: string
+  j17?: string
+  j18?: string
+  j19?: string
+  j20?: string
   pd?: number | string
-  jysl?: number | string
-  bhgsl?: number | string
   bz?: string
 }
 
@@ -74,6 +97,10 @@ export const getIpqcTaskPage = (params: IpqcTaskPageReqVO) => {
   return request.get<PageResult<IpqcTaskRespVO[]>>({ url: '/qms/ipqc-task/page', params })
 }
 
+export const getIpqcTask = (id: number | string) => {
+  return request.get<IpqcTaskRespVO>({ url: '/qms/ipqc-task/get', params: { id } })
+}
+
 export const startIpqcTaskProcess = (id: number | string) => {
   return request.post<string>({ url: '/qms/ipqc-task/start', data: { id } })
 }

+ 57 - 25
yudao-ui/yudao-ui-admin-vue3/src/store/modules/qms/ipqcInspectRecord.ts

@@ -11,9 +11,9 @@ import {
 type InspectRecordMain = IpqcInspectRecordMainRespVO
 
 const createEmptyMain = (): InspectRecordMain => ({
-  bdbh: '',
-  bbh: '',
-  sxrq: '',
+  bdbh: 'RS/IV-7.4-32',
+  bbh: '1.2',
+  sxrq: '2022.04.01',
   djbh: '',
   cplx: '',
   scph: '',
@@ -25,26 +25,35 @@ const createEmptyMain = (): InspectRecordMain => ({
   lydjbh: '',
   jyrq: '',
   jyr: '',
-  ybl: undefined,
-  jgpd: undefined,
+  jysl: undefined,
+  jyhgsl: undefined,
+  jybhgsl: undefined,
+  clfs: '',
+  lysl: undefined,
+  phsl: undefined,
+  pd: undefined,
   fj: '',
-  jgbh: '',
-  jgbb: '',
+  jgbh: 'QC-H644-1-05',
+  jgbb: 'A.8',
   bz: '',
   status: ''
 })
 
-const createEmptyRow = (): IpqcInspectRecordDetailRespVO => ({
-  jyxm: '',
-  jybz: '',
-  j1: '',
-  j2: '',
-  j3: '',
-  pd: undefined,
-  jysl: undefined,
-  bhgsl: undefined,
-  bz: ''
-})
+const createEmptyRow = (): IpqcInspectRecordDetailRespVO => {
+  const row: IpqcInspectRecordDetailRespVO = {
+    jyxm: '',
+    jybz: '',
+    sx: '',
+    xx: '',
+    jypc: undefined,
+    pd: undefined,
+    bz: ''
+  }
+  for (let i = 1; i <= 20; i += 1) {
+    ;(row as any)[`j${i}`] = ''
+  }
+  return row
+}
 
 const unwrapResult = <T>(result: any): T => (result && result.data !== undefined ? result.data : result)
 
@@ -115,8 +124,13 @@ export const useIpqcInspectRecordStore = defineStore('ipqcInspectRecord', {
         lydjbh: draft.main.lydjbh,
         jyrq: draft.main.jyrq,
         jyr: draft.main.jyr,
-        ybl: draft.main.ybl,
-        jgpd: draft.main.jgpd,
+        jysl: draft.main.jysl,
+        jyhgsl: draft.main.jyhgsl,
+        jybhgsl: draft.main.jybhgsl,
+        clfs: draft.main.clfs,
+        lysl: draft.main.lysl,
+        phsl: draft.main.phsl,
+        pd: draft.main.pd,
         fj: Array.isArray(draft.main.fj) ? draft.main.fj.join(',') : draft.main.fj,
         jgbh: draft.main.jgbh,
         jgbb: draft.main.jgbb,
@@ -126,12 +140,30 @@ export const useIpqcInspectRecordStore = defineStore('ipqcInspectRecord', {
           id: item.id,
           jyxm: item.jyxm,
           jybz: item.jybz,
-          j1: item.j1,
-          j2: item.j2,
-          j3: item.j3,
+          sx: item.sx,
+          xx: item.xx,
+          jypc: item.jypc,
+          j1: (item as any).j1,
+          j2: (item as any).j2,
+          j3: (item as any).j3,
+          j4: (item as any).j4,
+          j5: (item as any).j5,
+          j6: (item as any).j6,
+          j7: (item as any).j7,
+          j8: (item as any).j8,
+          j9: (item as any).j9,
+          j10: (item as any).j10,
+          j11: (item as any).j11,
+          j12: (item as any).j12,
+          j13: (item as any).j13,
+          j14: (item as any).j14,
+          j15: (item as any).j15,
+          j16: (item as any).j16,
+          j17: (item as any).j17,
+          j18: (item as any).j18,
+          j19: (item as any).j19,
+          j20: (item as any).j20,
           pd: item.pd,
-          jysl: item.jysl,
-          bhgsl: item.bhgsl,
           bz: item.bz
         }))
       }

+ 6 - 1
yudao-ui/yudao-ui-admin-vue3/src/views/bpm/processInstance/detail/index.vue

@@ -62,7 +62,8 @@
                       </el-col>
                       <!-- 情况二:业务表单 -->
                       <div v-if="processDefinition?.formType === BpmModelFormType.CUSTOM">
-                        <BusinessFormComponent :id="processInstance.businessKey" />
+                        <IpqcTaskSummary v-if="showIpqcTaskSummary" :task-id="processInstance.businessKey" />
+                        <BusinessFormComponent v-else :id="processInstance.businessKey" />
                       </div>
                     </div>
                   </el-col>
@@ -153,6 +154,7 @@ import approveSvg from '@/assets/svgs/bpm/approve.svg'
 import rejectSvg from '@/assets/svgs/bpm/reject.svg'
 import cancelSvg from '@/assets/svgs/bpm/cancel.svg'
 import PrintDialog from './PrintDialog.vue'
+import IpqcTaskSummary from '@/views/qms/ipqc/task/IpqcTaskSummary.vue'
 
 defineOptions({ name: 'BpmProcessInstanceDetail' })
 const props = defineProps<{
@@ -182,6 +184,9 @@ const detailForm = ref({
 }) // 流程实例的表单详情
 
 const writableFields: Array<string> = [] // 表单可以编辑的字段
+const showIpqcTaskSummary = computed(() =>
+  processDefinition.value?.formCustomViewPath?.includes('/qms/ipqc/task/ProcessDetailForm.vue')
+)
 
 /** 获得详情 */
 const getDetail = () => {

+ 84 - 0
yudao-ui/yudao-ui-admin-vue3/src/views/qms/ipqc/task/IpqcTaskSummary.vue

@@ -0,0 +1,84 @@
+<template>
+  <div class="ipqc-task-summary" v-loading="loading">
+    <el-descriptions v-if="task" :column="2" border>
+      <el-descriptions-item label="任务编号">{{ task.id ?? '-' }}</el-descriptions-item>
+      <el-descriptions-item label="单据编号">{{ task.applicationId ?? '-' }}</el-descriptions-item>
+      <el-descriptions-item label="生产订单">{{ task.workOrder ?? '-' }}</el-descriptions-item>
+      <el-descriptions-item label="状态">{{ statusLabel(task.status) }}</el-descriptions-item>
+      <el-descriptions-item label="工序/工步">{{ formatProcess(task) }}</el-descriptions-item>
+      <el-descriptions-item label="线体/工位/设备">{{ formatLine(task) }}</el-descriptions-item>
+      <el-descriptions-item label="物料编码">{{ task.materialCode ?? '-' }}</el-descriptions-item>
+      <el-descriptions-item label="物料名称">{{ task.materialName ?? '-' }}</el-descriptions-item>
+      <el-descriptions-item label="批次">{{ task.batch ?? '-' }}</el-descriptions-item>
+      <el-descriptions-item label="负责人">{{ task.responsibleName ?? '-' }}</el-descriptions-item>
+      <el-descriptions-item label="优先级">{{ priorityLabel(task.priority) }}</el-descriptions-item>
+      <el-descriptions-item label="开始时间">{{ formatDateTime(task.startTime) }}</el-descriptions-item>
+      <el-descriptions-item label="完成时间">{{ formatDateTime(task.completeTime) }}</el-descriptions-item>
+      <el-descriptions-item label="备注" :span="2">{{ task.remark ?? '-' }}</el-descriptions-item>
+    </el-descriptions>
+    <el-empty v-else description="暂无任务信息" />
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, watch } from 'vue'
+import { getIpqcTask, type IpqcTaskRespVO } from '@/api/qms/ipqc/task'
+
+const props = defineProps<{
+  taskId: number | string | undefined
+}>()
+
+const loading = ref(false)
+const task = ref<IpqcTaskRespVO | null>(null)
+
+const statusLabel = (value?: string) => {
+  if (value === 'processing') return '检验中'
+  if (value === 'completed') return '已完成'
+  return '待检验'
+}
+
+const priorityLabel = (value?: string) => {
+  if (value === 'high') return '高'
+  if (value === 'low') return '低'
+  return '中'
+}
+
+const formatProcess = (row: IpqcTaskRespVO) => `${row.process || '-'} / ${row.step || '-'}`
+const formatLine = (row: IpqcTaskRespVO) => `${row.line || '-'} / ${row.station || '-'} / ${row.equipment || '-'}`
+
+const formatDateTime = (value?: string | number) => {
+  if (!value) return '-'
+  const date = typeof value === 'number' || /^\d+$/.test(String(value))
+    ? new Date(Number(value))
+    : new Date(String(value))
+  if (Number.isNaN(date.getTime())) return String(value)
+  const yyyy = String(date.getFullYear())
+  const MM = String(date.getMonth() + 1).padStart(2, '0')
+  const dd = String(date.getDate()).padStart(2, '0')
+  const HH = String(date.getHours()).padStart(2, '0')
+  const mm = String(date.getMinutes()).padStart(2, '0')
+  const ss = String(date.getSeconds()).padStart(2, '0')
+  return `${yyyy}-${MM}-${dd} ${HH}:${mm}:${ss}`
+}
+
+const load = async () => {
+  if (!props.taskId) {
+    task.value = null
+    return
+  }
+  loading.value = true
+  try {
+    task.value = await getIpqcTask(props.taskId)
+  } finally {
+    loading.value = false
+  }
+}
+
+watch(() => props.taskId, () => { load() }, { immediate: true })
+</script>
+
+<style scoped>
+.ipqc-task-summary {
+  width: 100%;
+}
+</style>

+ 233 - 158
yudao-ui/yudao-ui-admin-vue3/src/views/qms/ipqc/task/ProcessDetailForm.vue

@@ -24,31 +24,15 @@
       <div class="form-body">
         <el-form label-width="110px">
           <div class="form-section">
-            <div class="section-title">基本信息</div>
+            <div class="section-title">
+              基本信息
+              <div class="form-meta">
+                <div>表单编号:{{ mainForm.bdbh || '-' }}</div>
+                <div>表单版本:{{ mainForm.bbh || '-' }}</div>
+                <div>表单生效日期:{{ mainForm.sxrq || '-' }}</div>
+              </div>
+            </div>
             <el-row :gutter="20">
-              <el-col :span="8">
-                <el-form-item label="表单编号">
-                  <el-input v-model="mainForm.bdbh" placeholder="请输入表单编号" />
-                </el-form-item>
-              </el-col>
-              <el-col :span="8">
-                <el-form-item label="版本号">
-                  <el-input v-model="mainForm.bbh" placeholder="请输入版本号" />
-                </el-form-item>
-              </el-col>
-              <el-col :span="8">
-                <el-form-item label="生效日期">
-                  <el-date-picker
-                    v-model="mainForm.sxrq"
-                    type="date"
-                    placeholder="请选择日期"
-                    format="YYYY-MM-DD"
-                    value-format="YYYY-MM-DD"
-                    style="width: 100%"
-                  />
-                </el-form-item>
-              </el-col>
-
               <el-col :span="8">
                 <el-form-item label="单据编号">
                   <el-input v-model="mainForm.djbh" placeholder="自动生成" />
@@ -110,37 +94,65 @@
                 </el-form-item>
               </el-col>
               <el-col :span="8">
-                <el-form-item label="检验">
-                  <el-input v-model="mainForm.jyr" placeholder="请输入检验人" />
+                <el-form-item label="检验">
+                  <el-input v-model="mainForm.jyr" readonly />
                 </el-form-item>
               </el-col>
 
               <el-col :span="8">
-                <el-form-item label="最大样本量">
-                  <el-input-number v-model="mainForm.ybl" :min="0" controls-position="right" style="width: 100%" />
+                <el-form-item label="检验数量">
+                  <el-input v-model="mainForm.jysl" readonly />
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item label="合格数量">
+                  <el-input v-model="mainForm.jyhgsl" @input="handleMainIntInput('jyhgsl')" placeholder="请输入合格数量" />
                 </el-form-item>
               </el-col>
               <el-col :span="8">
-                <el-form-item label="结果判定">
-                  <el-select v-model="mainForm.jgpd" placeholder="请选择结果判定" style="width: 100%">
-                    <el-option v-for="option in resultOptions" :key="option.value" :label="option.label" :value="option.value" />
+                <el-form-item label="不合格数量">
+                  <el-input v-model="mainForm.jybhgsl" @input="handleMainIntInput('jybhgsl')" placeholder="请输入不合格数量" />
+                </el-form-item>
+              </el-col>
+
+              <el-col :span="8">
+                <el-form-item label="处理方式">
+                  <el-select v-model="mainForm.clfs" placeholder="请选择处理方式" style="width: 100%">
+                    <el-option v-for="option in clfsOptions" :key="option.value" :label="option.label" :value="option.value" />
                   </el-select>
                 </el-form-item>
               </el-col>
+              <el-col :span="8">
+                <el-form-item label="留样数量">
+                  <el-input v-model="mainForm.lysl" @input="handleMainIntInput('lysl')" placeholder="请输入留样数量" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item label="破坏数量">
+                  <el-input v-model="mainForm.phsl" @input="handleMainIntInput('phsl')" placeholder="请输入破坏数量" />
+                </el-form-item>
+              </el-col>
 
+              <el-col :span="8">
+                <el-form-item label="判断">
+                  <el-select v-model="mainForm.pd" placeholder="请选择判断" style="width: 100%">
+                    <el-option v-for="option in judgeOptions" :key="option.value" :label="option.label" :value="option.value" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
               <el-col :span="8">
                 <el-form-item label="附件">
-                  <UploadFile v-model="mainForm.fj" :limit="1" :file-type="['pdf','doc','docx','xls','xlsx','png','jpg']" :is-show-tip="false" />
+                  <UploadFile v-model="mainForm.fj" :limit="5" :is-show-tip="false" />
                 </el-form-item>
               </el-col>
               <el-col :span="8">
                 <el-form-item label="检规编号">
-                  <el-input v-model="mainForm.jgbh" placeholder="请输入检规编号" />
+                  <el-input v-model="mainForm.jgbh" readonly />
                 </el-form-item>
               </el-col>
               <el-col :span="8">
                 <el-form-item label="检规版本">
-                  <el-input v-model="mainForm.jgbb" placeholder="请输入检规版本" />
+                  <el-input v-model="mainForm.jgbb" readonly />
                 </el-form-item>
               </el-col>
 
@@ -159,60 +171,45 @@
                 <el-button type="primary" size="small" @click="handleAddRow">新增行</el-button>
               </div>
             </div>
-            <el-table :data="detailRows" border>
-              <el-table-column label="检验项目" width="200">
-                <template #default="{ row }">
-                  <el-input v-model="row.jyxm" placeholder="请输入检验项目" />
-                </template>
-              </el-table-column>
-              <el-table-column label="检验标准" width="220">
-                <template #default="{ row }">
-                  <el-input v-model="row.jybz" placeholder="请输入检验标准" />
-                </template>
-              </el-table-column>
-              <el-table-column label="样本1" width="140">
-                <template #default="{ row }">
-                  <el-input v-model="row.j1" placeholder="请输入样本1" />
-                </template>
-              </el-table-column>
-              <el-table-column label="样本2" width="140">
-                <template #default="{ row }">
-                  <el-input v-model="row.j2" placeholder="请输入样本2" />
-                </template>
-              </el-table-column>
-              <el-table-column label="样本3" width="140">
-                <template #default="{ row }">
-                  <el-input v-model="row.j3" placeholder="请输入样本3" />
-                </template>
-              </el-table-column>
-              <el-table-column label="判定" width="120">
-                <template #default="{ row }">
-                  <el-select v-model="row.pd" placeholder="选择" style="width: 100%">
-                    <el-option v-for="option in resultOptions" :key="option.value" :label="option.label" :value="option.value" />
-                  </el-select>
-                </template>
-              </el-table-column>
-              <el-table-column label="检验数量" width="140">
-                <template #default="{ row }">
-                  <el-input-number v-model="row.jysl" :min="0" controls-position="right" style="width: 100%" />
-                </template>
-              </el-table-column>
-              <el-table-column label="不合格数" width="140">
-                <template #default="{ row }">
-                  <el-input-number v-model="row.bhgsl" :min="0" controls-position="right" style="width: 100%" />
-                </template>
-              </el-table-column>
-              <el-table-column label="备注" min-width="200">
-                <template #default="{ row }">
-                  <el-input v-model="row.bz" placeholder="请输入备注" />
-                </template>
-              </el-table-column>
-              <el-table-column label="操作" width="90">
-                <template #default="{ $index }">
-                  <el-button link type="danger" size="small" @click="handleDeleteRow($index)">删除</el-button>
-                </template>
-              </el-table-column>
-            </el-table>
+          <el-table :data="detailRows" border>
+            <el-table-column label="操作" width="90">
+              <template #default="{ $index }">
+                <el-button link type="danger" size="small" @click="handleDeleteRow($index)">删除</el-button>
+              </template>
+            </el-table-column>
+            <el-table-column label="检验项目" min-width="200">
+              <template #default="{ row }">
+                <el-input v-model="row.jyxm" type="textarea" :autosize="{ minRows: 1, maxRows: 4 }" readonly />
+              </template>
+            </el-table-column>
+            <el-table-column label="检验标准" min-width="240">
+              <template #default="{ row }">
+                <el-input v-model="row.jybz" type="textarea" :autosize="{ minRows: 1, maxRows: 4 }" readonly />
+              </template>
+            </el-table-column>
+            <el-table-column label="判断" width="120">
+              <template #default="{ row }">
+                <el-select v-model="row.pd" placeholder="选择" style="width: 100%">
+                  <el-option v-for="option in judgeOptions" :key="option.value" :label="option.label" :value="option.value" />
+                </el-select>
+              </template>
+            </el-table-column>
+            <el-table-column label="样本量" width="120">
+              <template #default="{ row }">
+                <el-input v-model="row.jypc" @input="handleRowIntInput(row, 'jypc')" placeholder="数量" />
+              </template>
+            </el-table-column>
+            <el-table-column
+              v-for="index in sampleColumnCount"
+              :key="`sample-${index}`"
+              :label="`样本${index}`"
+              width="120"
+            >
+              <template #default="{ row }">
+                <el-input v-model="row[`j${index}`]" @input="handleRowIntInput(row, `j${index}`)" placeholder="数值" />
+              </template>
+            </el-table-column>
+          </el-table>
           </div>
         </el-form>
       </div>
@@ -221,31 +218,15 @@
     <div v-else class="form-body">
       <el-form label-width="110px">
         <div class="form-section">
-          <div class="section-title">基本信息</div>
+          <div class="section-title">
+            基本信息
+            <div class="form-meta">
+              <div>表单编号:{{ mainForm.bdbh || '-' }}</div>
+              <div>表单版本:{{ mainForm.bbh || '-' }}</div>
+              <div>表单生效日期:{{ mainForm.sxrq || '-' }}</div>
+            </div>
+          </div>
           <el-row :gutter="20">
-            <el-col :span="8">
-              <el-form-item label="表单编号">
-                <el-input v-model="mainForm.bdbh" placeholder="请输入表单编号" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="8">
-              <el-form-item label="版本号">
-                <el-input v-model="mainForm.bbh" placeholder="请输入版本号" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="8">
-              <el-form-item label="生效日期">
-                <el-date-picker
-                  v-model="mainForm.sxrq"
-                  type="date"
-                  placeholder="请选择日期"
-                  format="YYYY-MM-DD"
-                  value-format="YYYY-MM-DD"
-                  style="width: 100%"
-                />
-              </el-form-item>
-            </el-col>
-
             <el-col :span="8">
               <el-form-item label="单据编号">
                 <el-input v-model="mainForm.djbh" placeholder="自动生成" />
@@ -307,37 +288,65 @@
               </el-form-item>
             </el-col>
             <el-col :span="8">
-              <el-form-item label="检验">
-                <el-input v-model="mainForm.jyr" placeholder="请输入检验人" />
+              <el-form-item label="检验">
+                <el-input v-model="mainForm.jyr" readonly />
               </el-form-item>
             </el-col>
 
             <el-col :span="8">
-              <el-form-item label="最大样本量">
-                <el-input-number v-model="mainForm.ybl" :min="0" controls-position="right" style="width: 100%" />
+              <el-form-item label="检验数量">
+                <el-input v-model="mainForm.jysl" readonly />
               </el-form-item>
             </el-col>
             <el-col :span="8">
-              <el-form-item label="结果判定">
-                <el-select v-model="mainForm.jgpd" placeholder="请选择结果判定" style="width: 100%">
-                  <el-option v-for="option in resultOptions" :key="option.value" :label="option.label" :value="option.value" />
+              <el-form-item label="合格数量">
+                <el-input v-model="mainForm.jyhgsl" @input="handleMainIntInput('jyhgsl')" placeholder="请输入合格数量" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="不合格数量">
+                <el-input v-model="mainForm.jybhgsl" @input="handleMainIntInput('jybhgsl')" placeholder="请输入不合格数量" />
+              </el-form-item>
+            </el-col>
+
+            <el-col :span="8">
+              <el-form-item label="处理方式">
+                <el-select v-model="mainForm.clfs" placeholder="请选择处理方式" style="width: 100%">
+                  <el-option v-for="option in clfsOptions" :key="option.value" :label="option.label" :value="option.value" />
                 </el-select>
               </el-form-item>
             </el-col>
+            <el-col :span="8">
+              <el-form-item label="留样数量">
+                <el-input v-model="mainForm.lysl" @input="handleMainIntInput('lysl')" placeholder="请输入留样数量" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="破坏数量">
+                <el-input v-model="mainForm.phsl" @input="handleMainIntInput('phsl')" placeholder="请输入破坏数量" />
+              </el-form-item>
+            </el-col>
 
+            <el-col :span="8">
+              <el-form-item label="判断">
+                <el-select v-model="mainForm.pd" placeholder="请选择判断" style="width: 100%">
+                  <el-option v-for="option in judgeOptions" :key="option.value" :label="option.label" :value="option.value" />
+                </el-select>
+              </el-form-item>
+            </el-col>
             <el-col :span="8">
               <el-form-item label="附件">
-                <UploadFile v-model="mainForm.fj" :limit="1" :file-type="['pdf','doc','docx','xls','xlsx','png','jpg']" :is-show-tip="false" />
+                <UploadFile v-model="mainForm.fj" :limit="5" :is-show-tip="false" />
               </el-form-item>
             </el-col>
             <el-col :span="8">
               <el-form-item label="检规编号">
-                <el-input v-model="mainForm.jgbh" placeholder="请输入检规编号" />
+                <el-input v-model="mainForm.jgbh" readonly />
               </el-form-item>
             </el-col>
             <el-col :span="8">
               <el-form-item label="检规版本">
-                <el-input v-model="mainForm.jgbb" placeholder="请输入检规版本" />
+                <el-input v-model="mainForm.jgbb" readonly />
               </el-form-item>
             </el-col>
 
@@ -357,56 +366,41 @@
             </div>
           </div>
           <el-table :data="detailRows" border>
-            <el-table-column label="检验项目" width="200">
-              <template #default="{ row }">
-                <el-input v-model="row.jyxm" placeholder="请输入检验项目" />
-              </template>
-            </el-table-column>
-            <el-table-column label="检验标准" width="220">
-              <template #default="{ row }">
-                <el-input v-model="row.jybz" placeholder="请输入检验标准" />
-              </template>
-            </el-table-column>
-            <el-table-column label="样本1" width="140">
-              <template #default="{ row }">
-                <el-input v-model="row.j1" placeholder="请输入样本1" />
+            <el-table-column label="操作" width="90">
+              <template #default="{ $index }">
+                <el-button link type="danger" size="small" @click="handleDeleteRow($index)">删除</el-button>
               </template>
             </el-table-column>
-            <el-table-column label="样本2" width="140">
+            <el-table-column label="检验项目" min-width="200">
               <template #default="{ row }">
-                <el-input v-model="row.j2" placeholder="请输入样本2" />
+                <el-input v-model="row.jyxm" type="textarea" :autosize="{ minRows: 1, maxRows: 4 }" readonly />
               </template>
             </el-table-column>
-            <el-table-column label="样本3" width="140">
+            <el-table-column label="检验标准" min-width="240">
               <template #default="{ row }">
-                <el-input v-model="row.j3" placeholder="请输入样本3" />
+                <el-input v-model="row.jybz" type="textarea" :autosize="{ minRows: 1, maxRows: 4 }" readonly />
               </template>
             </el-table-column>
-            <el-table-column label="判" width="120">
+            <el-table-column label="判" width="120">
               <template #default="{ row }">
                 <el-select v-model="row.pd" placeholder="选择" style="width: 100%">
-                  <el-option v-for="option in resultOptions" :key="option.value" :label="option.label" :value="option.value" />
+                  <el-option v-for="option in judgeOptions" :key="option.value" :label="option.label" :value="option.value" />
                 </el-select>
               </template>
             </el-table-column>
-            <el-table-column label="检验数量" width="140">
+            <el-table-column label="样本量" width="120">
               <template #default="{ row }">
-                <el-input-number v-model="row.jysl" :min="0" controls-position="right" style="width: 100%" />
+                <el-input v-model="row.jypc" @input="handleRowIntInput(row, 'jypc')" placeholder="数量" />
               </template>
             </el-table-column>
-            <el-table-column label="不合格数" width="140">
+            <el-table-column
+              v-for="index in sampleColumnCount"
+              :key="`sample-${index}`"
+              :label="`样本${index}`"
+              width="120"
+            >
               <template #default="{ row }">
-                <el-input-number v-model="row.bhgsl" :min="0" controls-position="right" style="width: 100%" />
-              </template>
-            </el-table-column>
-            <el-table-column label="备注" min-width="200">
-              <template #default="{ row }">
-                <el-input v-model="row.bz" placeholder="请输入备注" />
-              </template>
-            </el-table-column>
-            <el-table-column label="操作" width="90">
-              <template #default="{ $index }">
-                <el-button link type="danger" size="small" @click="handleDeleteRow($index)">删除</el-button>
+                <el-input v-model="row[`j${index}`]" @input="handleRowIntInput(row, `j${index}`)" placeholder="数值" />
               </template>
             </el-table-column>
           </el-table>
@@ -423,11 +417,13 @@ import { computed, ref, watch } from 'vue'
 import { useRoute, useRouter } from 'vue-router'
 import { useIpqcInspectRecordStore } from '@/store/modules/qms/ipqcInspectRecord'
 import { UploadFile } from '@/components/UploadFile'
+import { useUserStore } from '@/store/modules/user'
 
 const props = defineProps<{ id?: string }>()
 const route = useRoute()
 const router = useRouter()
 const recordStore = useIpqcInspectRecordStore()
+const userStore = useUserStore()
 
 const loading = ref(false)
 const saving = ref(false)
@@ -450,6 +446,14 @@ const fetchData = async () => {
   loading.value = true
   try {
     await recordStore.load(taskId.value)
+    if (!mainForm.value.jyr) {
+      mainForm.value.jyr = userStore.getUser?.nickname || ''
+    }
+    detailRows.value.forEach((row: any) => {
+      const sampleFromInput = toInt(row.jypc)
+      const sampleFromValues = getRowSampleMax(row)
+      row.__maxSampleCount = Math.min(20, Math.max(sampleFromInput, sampleFromValues))
+    })
   } finally {
     loading.value = false
   }
@@ -465,6 +469,60 @@ const handleDeleteRow = (index: number) => {
   recordStore.deleteDetailRow(taskId.value, index)
 }
 
+const toInt = (value: unknown) => {
+  if (value === null || value === undefined || value === '') return 0
+  const num = Number(String(value).replace(/[^\d]/g, ''))
+  return Number.isNaN(num) ? 0 : num
+}
+
+const normalizeInt = (value: unknown) => {
+  if (value === null || value === undefined) return ''
+  const text = String(value).replace(/[^\d]/g, '')
+  return text
+}
+
+const handleMainIntInput = (field: string) => {
+  ;(mainForm.value as any)[field] = normalizeInt((mainForm.value as any)[field])
+  if (field === 'jyhgsl') {
+    const total = toInt(mainForm.value.jysl)
+    const qualified = toInt(mainForm.value.jyhgsl)
+    mainForm.value.jybhgsl = String(Math.max(total - qualified, 0)) as any
+  }
+  if (field === 'jybhgsl') {
+    const total = toInt(mainForm.value.jysl)
+    const unqualified = toInt(mainForm.value.jybhgsl)
+    mainForm.value.jyhgsl = String(Math.max(total - unqualified, 0)) as any
+  }
+}
+
+const getRowSampleMax = (row: any) => {
+  let max = 0
+  for (let i = 1; i <= 20; i += 1) {
+    if (row[`j${i}`] !== undefined && row[`j${i}`] !== '') {
+      max = Math.max(max, i)
+    }
+  }
+  return max
+}
+
+const handleRowIntInput = (row: any, field: string) => {
+  row[field] = normalizeInt(row[field])
+  if (field === 'jypc') {
+    const input = Math.min(20, toInt(row.jypc))
+    const currentMax = row.__maxSampleCount || 0
+    row.__maxSampleCount = Math.max(currentMax, input)
+  }
+}
+
+const sampleColumnCount = computed(() => {
+  let max = 0
+  detailRows.value.forEach((row: any) => {
+    const rowMax = row.__maxSampleCount || toInt(row.jypc) || 0
+    if (rowMax > max) max = rowMax
+  })
+  return Math.min(20, Math.max(1, max))
+})
+
 const handleSave = async () => {
   if (!taskId.value) return
   saving.value = true
@@ -487,10 +545,15 @@ const handleOpenEditor = () => {
   router.push(`${basePath}/${taskId.value}`)
 }
 
-const resultOptions = [
+const judgeOptions = [
   { label: '合格', value: 1 },
-  { label: '不合格', value: 0 },
-  { label: '待判定', value: 2 }
+  { label: '不合格', value: 0 }
+]
+
+const clfsOptions = [
+  { label: '退货', value: '0' },
+  { label: '挑选', value: '1' },
+  { label: '让步接收', value: '2' }
 ]
 
 watch(taskId, () => { fetchData() }, { immediate: true })
@@ -518,6 +581,18 @@ watch(taskId, () => { fetchData() }, { immediate: true })
 .form-card {
   margin-bottom: 16px;
 }
+.section-title {
+  display: flex;
+  align-items: flex-start;
+  justify-content: space-between;
+  font-weight: 600;
+}
+.form-meta {
+  text-align: right;
+  font-size: 12px;
+  color: var(--el-text-color-secondary);
+  line-height: 1.6;
+}
 .card-header {
   display: flex;
   align-items: center;

Някои файлове не бяха показани, защото твърде много файлове са промени