hou il y a 1 semaine
Parent
commit
6646b0f07c
25 fichiers modifiés avec 1462 ajouts et 218 suppressions
  1. 77 0
      db/mysql/mes_gp12_items_category.sql
  2. 17 0
      db/mysql/mes_material_check_record_te_cai.sql
  3. 10 0
      db/mysql/mes_material_check_sample_tighten.sql
  4. 29 5
      src/main/java/com/jeesite/modules/mes/entity/MesGp12Items.java
  5. 10 0
      src/main/java/com/jeesite/modules/mes/entity/MesMaterialCheckRecord.java
  6. 138 3
      src/main/java/com/jeesite/modules/mes/entity/MesMaterialCheckRecordItems.java
  7. 137 3
      src/main/java/com/jeesite/modules/mes/service/MesMaterialCheckRecordService.java
  8. 7 0
      src/main/java/com/jeesite/modules/mes/service/MesProductRecordService.java
  9. 11 8
      src/main/java/com/jeesite/modules/mes/service/MesStationTimeValidateService.java
  10. 38 4
      src/main/java/com/jeesite/modules/mes/web/MesGp12ItemsController.java
  11. 551 33
      src/main/java/com/jeesite/modules/mes/web/MesMaterialCheckRecordController.java
  12. 1 1
      src/main/java/com/jeesite/modules/mes/web/MesMaterialLlRecordController.java
  13. 40 32
      src/main/java/com/jeesite/modules/mes/web/MesMaterialPrebindController.java
  14. 5 1
      src/main/java/com/jeesite/modules/mes/web/MesProductRecordController.java
  15. 24 2
      src/main/resources/views/modules/mes/mesGp12ItemsForm.html
  16. 13 5
      src/main/resources/views/modules/mes/mesGp12ItemsList.html
  17. 52 30
      src/main/resources/views/modules/mes/mesMaterialCheckRecordForm.html
  18. 20 29
      src/main/resources/views/modules/mes/mesMaterialCheckRecordForm2.html
  19. 8 13
      src/main/resources/views/modules/mes/mesMaterialCheckRecordFormEdit.html
  20. 112 0
      src/main/resources/views/modules/mes/mesMaterialCheckRecordFuCeForm.html
  21. 36 9
      src/main/resources/views/modules/mes/mesMaterialCheckRecordInfo.html
  22. 21 1
      src/main/resources/views/modules/mes/mesMaterialCheckRecordList.html
  23. 92 0
      src/main/resources/views/modules/mes/mesMaterialCheckRecordTeCaiForm.html
  24. 11 32
      src/main/resources/views/modules/mes/mesProductGp12Form.html
  25. 2 7
      src/main/resources/views/modules/mes/mesProductGp12List.html

+ 77 - 0
db/mysql/mes_gp12_items_category.sql

@@ -0,0 +1,77 @@
+-- mes_gp12_items 二级分类改造 + 外观判定标准初始数据
+-- MySQL 5.7 无 IF NOT EXISTS 时,若列已存在可忽略报错后继续
+ALTER TABLE mes_gp12_items ADD COLUMN category varchar(100) DEFAULT NULL COMMENT '一级分类' AFTER id;
+ALTER TABLE mes_gp12_items ADD COLUMN sort int(11) DEFAULT 0 COMMENT '排序号' AFTER description;
+ALTER TABLE mes_product_gp12 MODIFY COLUMN remark varchar(1000) DEFAULT NULL COMMENT '不良原因';
+
+-- 清空旧配置后导入(若需保留旧数据请注释下一行)
+DELETE FROM mes_gp12_items;
+
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0001', '尺寸', NULL, '孔径超差', 1, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0002', '尺寸', NULL, '尺寸超差', 2, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0003', '二维码/镭射/丝印/刻印/压铸', NULL, '标识漏', 3, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0004', '二维码/镭射/丝印/刻印/压铸', NULL, '标识脏污、磨损、不清晰、', 4, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0005', '二维码/镭射/丝印/刻印/压铸', NULL, '标识位置偏', 5, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0006', '二维码/镭射/丝印/刻印/压铸', NULL, '标识错', 6, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0007', '二维码/镭射/丝印/刻印/压铸', NULL, '扫码信息错误', 7, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0008', '外观', NULL, '型材变形', 8, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0009', '外观', NULL, '划伤/划痕/磕碰伤', 9, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0010', '外观', NULL, '涂胶残胶/漏涂/色差/不均匀/不固化', 10, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0011', '外观', NULL, '漏抛光/打磨', 11, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0012', '外观', NULL, '脏污/色差', 12, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0013', '外观', NULL, '铝屑', 13, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0014', '外观', NULL, '水管保护套漏装', 14, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0015', '外观', NULL, '水管保护套异物', 15, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0016', '外观', NULL, '水管磕碰伤', 16, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0017', '外观', NULL, '挡水墙打磨/变形', 17, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0018', '外观', NULL, 'PC片漏/偏位', 18, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0019', '焊接缺陷', NULL, '漏焊', 19, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0020', '焊接缺陷', NULL, '错焊', 20, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0021', '焊接缺陷', NULL, '焊渣', 21, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0022', '焊接缺陷', NULL, '焊烟', 22, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0023', '焊接缺陷', NULL, '毛刺', 23, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0024', '焊接缺陷', NULL, '焊缝开裂', 24, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0025', '焊接缺陷', NULL, '焊缝间隙', 25, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0026', '焊接缺陷', NULL, '焊缝气孔', 26, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0027', '焊接缺陷', NULL, '焊缝烧穿、咬边', 27, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0028', '焊接缺陷', NULL, '焊缝背透', 28, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0029', '焊接缺陷', NULL, '焊缝漏焊', 29, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0030', '焊接缺陷', NULL, '焊缝长度短', 30, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0031', '焊接缺陷', NULL, '焊缝弧坑', 31, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0032', '焊接缺陷', NULL, '焊缝偏位', 32, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0033', '铆接', NULL, '漏', 33, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0034', '铆接', NULL, '错(位置错、型号错)', 34, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0035', '铆接', NULL, '牙尾(未敲、未清理)', 35, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0036', '铆接', NULL, '铆钉突出', 36, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0037', '铆接', NULL, '扭矩不满足', 37, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0038', '铆接', NULL, '间隙大', 38, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0039', '液冷板', NULL, '漏底', 39, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0040', '液冷板', NULL, '水嘴残漆', 40, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0041', '液冷板', NULL, '凹坑', 41, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0042', '液冷板', NULL, '凸点(颗粒)', 42, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0043', '液冷板', NULL, '划伤划痕', 44, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0044', '液冷板', NULL, '脏污、胶渍', 45, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0045', '液冷板', NULL, '流挂、肥边', 46, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0046', '液冷板', NULL, '翻边、边缘披风、毛刺', 47, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0047', '液冷板', NULL, '产品绝缘面补漆(自喷漆或绝缘膏)', 48, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0048', '液冷板', NULL, '安装孔毛刺飞边', 49, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0049', '液冷板', NULL, '打码处遮蔽', 50, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0050', '液冷板', NULL, '气泡颗粒', 51, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0051', '液冷板', NULL, '缩孔', 52, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0052', '底护板', NULL, '色差', 53, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0053', '底护板', NULL, '磕伤', 54, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0054', '底护板', NULL, '压痕/擦伤', 55, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0055', '底护板', NULL, '划痕、划伤', 57, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0056', '底护板', NULL, 'PVC 缩孔', 58, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0057', '底护板', NULL, 'PVC 表面脏污', 59, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0058', '底护板', NULL, '凹坑/气孔', 60, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0059', '底护板', NULL, '水渍/水印', 61, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0060', '底护板', NULL, '开裂、起皮', 62, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0061', '底护板', NULL, '变形', 63, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0062', '底护板', NULL, 'PVC瘤', 64, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0063', '底护板', NULL, '安装孔外观', 65, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0064', '底护板', NULL, 'PVC喷涂涂层偏薄', 66, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0065', '底护板', NULL, '残胶、麻点', 67, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0066', '底护板', NULL, '孔到边缘处PVC未覆盖', 68, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0067', '底护板', NULL, '孔边缘PVC偏薄', 69, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');
+INSERT INTO mes_gp12_items (id, category, res_oprno, description, sort, create_by, create_date, update_by, update_date) VALUES ('gp12item0068', '其他', NULL, '其他', 70, 'system', '2026-08-12 20:20:00', 'system', '2026-08-12 20:20:00');

+ 17 - 0
db/mysql/mes_material_check_record_te_cai.sql

@@ -0,0 +1,17 @@
+-- 来料检查记录结果字典:增加「特采」
+-- 执行后请在【系统管理-系统设置-参数管理】或字典管理中刷新缓存,也可重启应用
+
+INSERT INTO js_sys_dict_data (
+  dict_code, parent_code, parent_codes, tree_sort, tree_sorts, tree_leaf, tree_level, tree_names,
+  dict_label, dict_value, dict_icon, dict_type, is_sys, description, css_style, css_class,
+  status, create_by, create_date, update_by, update_date, remarks, corp_code, corp_name
+)
+SELECT
+  '1990000000000000003', '0', '0,', 30, '0000000030,', '1', 0, '特采',
+  '特采', '3', NULL, 'mes_material_check_record_state', '1', '不合格特采放行', '', 'tag orange',
+  '0', 'system', NOW(), 'system', NOW(), '来料检查特采', '0', 'JeeSite'
+FROM (SELECT 1 AS tmp) t
+WHERE NOT EXISTS (
+  SELECT 1 FROM js_sys_dict_data
+  WHERE dict_type = 'mes_material_check_record_state' AND dict_value = '3' AND status = '0'
+);

+ 10 - 0
db/mysql/mes_material_check_sample_tighten.sql

@@ -0,0 +1,10 @@
+-- 来料检查加严:实测值扩展至8个,记录抽样数
+-- 规则:本周同物料累计3次不合格后加严为8个;加严期间连续3次合格后恢复5个,并清空本轮不合格计数
+
+ALTER TABLE mes_material_check_record_items
+  ADD COLUMN a6 varchar(255) NULL COMMENT '实测值6' AFTER a5,
+  ADD COLUMN a7 varchar(255) NULL COMMENT '实测值7' AFTER a6,
+  ADD COLUMN a8 varchar(255) NULL COMMENT '实测值8' AFTER a7;
+
+ALTER TABLE mes_material_check_record
+  ADD COLUMN sample_count int NULL DEFAULT 5 COMMENT '实测值抽样数(5正常/8加严)' AFTER state;

+ 29 - 5
src/main/java/com/jeesite/modules/mes/entity/MesGp12Items.java

@@ -5,28 +5,33 @@ import com.jeesite.common.mybatis.annotation.Column;
 import com.jeesite.common.mybatis.annotation.Table;
 import com.jeesite.common.mybatis.mapper.query.QueryType;
 
+import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.Size;
 
 /**
- * GP12工位问题项(不良原因)Entity
+ * GP12工位问题项(不良原因,一级/二级分类)Entity
  * @author mes
- * @version 2026-08-04
+ * @version 2026-08-12
  */
 @Table(name="mes_gp12_items", alias="a", label="GP12工位问题项信息", columns={
 		@Column(name="id", attrName="id", label="id", isPK=true),
+		@Column(name="category", attrName="category", label="一级分类", queryType=QueryType.LIKE),
 		@Column(name="res_oprno", attrName="resOprno", label="责任工位", queryType=QueryType.LIKE),
-		@Column(name="description", attrName="description", label="问题描述", queryType=QueryType.LIKE),
+		@Column(name="description", attrName="description", label="二级分类", queryType=QueryType.LIKE),
+		@Column(name="sort", attrName="sort", label="排序号", isUpdateForce=true),
 		@Column(name="create_by", attrName="createBy", label="创建人", isUpdate=false, isQuery=false),
 		@Column(name="create_date", attrName="createDate", label="创建时间", isUpdate=false, isQuery=false, isUpdateForce=true),
 		@Column(name="update_by", attrName="updateBy", label="更新人", isQuery=false),
 		@Column(name="update_date", attrName="updateDate", label="更新时间", isQuery=false, isUpdateForce=true),
-	}, orderBy="a.update_date DESC"
+	}, orderBy="a.sort ASC, a.update_date DESC"
 )
 public class MesGp12Items extends DataEntity<MesGp12Items> {
 
 	private static final long serialVersionUID = 1L;
+	private String category;
 	private String resOprno;
 	private String description;
+	private Integer sort;
 
 	public MesGp12Items() {
 		this(null);
@@ -36,6 +41,16 @@ public class MesGp12Items extends DataEntity<MesGp12Items> {
 		super(id);
 	}
 
+	@NotBlank(message="一级分类不能为空")
+	@Size(min=0, max=100, message="一级分类长度不能超过 100 个字符")
+	public String getCategory() {
+		return category;
+	}
+
+	public void setCategory(String category) {
+		this.category = category;
+	}
+
 	@Size(min=0, max=20, message="责任工位长度不能超过 20 个字符")
 	public String getResOprno() {
 		return resOprno;
@@ -45,7 +60,8 @@ public class MesGp12Items extends DataEntity<MesGp12Items> {
 		this.resOprno = resOprno;
 	}
 
-	@Size(min=0, max=255, message="问题描述长度不能超过 255 个字符")
+	@NotBlank(message="二级分类不能为空")
+	@Size(min=0, max=255, message="二级分类长度不能超过 255 个字符")
 	public String getDescription() {
 		return description;
 	}
@@ -53,4 +69,12 @@ public class MesGp12Items extends DataEntity<MesGp12Items> {
 	public void setDescription(String description) {
 		this.description = description;
 	}
+
+	public Integer getSort() {
+		return sort;
+	}
+
+	public void setSort(Integer sort) {
+		this.sort = sort;
+	}
 }

+ 10 - 0
src/main/java/com/jeesite/modules/mes/entity/MesMaterialCheckRecord.java

@@ -23,6 +23,7 @@ import java.util.Date;
 		@Column(name="fa_nums", attrName="faNums", label="发货数量", isUpdateForce=true),
 		@Column(name="nums", attrName="nums", label="实际数量", isUpdateForce=true),
 		@Column(name="state", attrName="state", label="状态"),
+		@Column(name="sample_count", attrName="sampleCount", label="实测值抽样数", isUpdateForce=true),
 		@Column(name="create_by", attrName="createBy", label="create_by", isUpdate=false, isQuery=false),
 		@Column(name="create_date", attrName="createDate", label="create_date", isUpdate=false, isUpdateForce=true),
 		@Column(name="update_by", attrName="updateBy", label="update_by", isQuery=false),
@@ -46,6 +47,7 @@ public class MesMaterialCheckRecord extends DataEntity<MesMaterialCheckRecord> {
 	private Long nums;		// 实际数量
 	private String materialTitle;
 	private String state;
+	private Integer sampleCount;	// 实测值抽样数(5正常/8加严)
 	private Date createDate;
 	private String sn;
 
@@ -107,6 +109,14 @@ public class MesMaterialCheckRecord extends DataEntity<MesMaterialCheckRecord> {
 		this.state = state;
 	}
 
+	public Integer getSampleCount() {
+		return sampleCount;
+	}
+
+	public void setSampleCount(Integer sampleCount) {
+		this.sampleCount = sampleCount;
+	}
+
 	public Date getCreateDate_gte() {
 		return sqlMap.getWhere().getValue("create_date", QueryType.GTE);
 	}

+ 138 - 3
src/main/java/com/jeesite/modules/mes/entity/MesMaterialCheckRecordItems.java

@@ -7,8 +7,10 @@ import com.jeesite.common.entity.DataEntity;
 import com.jeesite.common.mybatis.annotation.Column;
 import com.jeesite.common.mybatis.annotation.JoinTable;
 import com.jeesite.common.mybatis.annotation.Table;
-import com.jeesite.common.mybatis.mapper.query.QueryType;
+import com.jeesite.common.utils.excel.annotation.ExcelField;
+import com.jeesite.common.utils.excel.annotation.ExcelFields;
 
+import java.util.Date;
 import java.util.List;
 
 /**
@@ -25,6 +27,9 @@ import java.util.List;
 		@Column(name="a3", attrName="a3", label="实测值3"),
 		@Column(name="a4", attrName="a4", label="实测值4"),
 		@Column(name="a5", attrName="a5", label="实测值5"),
+		@Column(name="a6", attrName="a6", label="实测值6"),
+		@Column(name="a7", attrName="a7", label="实测值7"),
+		@Column(name="a8", attrName="a8", label="实测值8"),
 		@Column(name="result", attrName="result", label="结果"),
 	},
 		joinTable={
@@ -48,12 +53,54 @@ public class MesMaterialCheckRecordItems extends DataEntity<MesMaterialCheckReco
 	private String a3;		// 实测值3
 	private String a4;		// 实测值4
 	private String a5;		// 实测值5
+	private String a6;		// 实测值6
+	private String a7;		// 实测值7
+	private String a8;		// 实测值8
 	private String materialCheckTitle;
 	private String org;
 	private String orgName;
 	private String result;
 	private List img = ListUtils.newArrayList();
 	private List perfFiles = ListUtils.newArrayList();
+
+	/** 导出用:来料码 */
+	private String sn;
+	/** 导出用:物料名称 */
+	private String materialTitle;
+	/** 导出用:发货日期 */
+	private String faDate;
+	/** 导出用:报检数量 */
+	private Long nums;
+	/** 导出用:参与抽检数量 */
+	private Long faNums;
+	/** 导出用:整单结果 */
+	private String recordState;
+	/** 导出用:检查人 */
+	private String checkBy;
+	/** 导出用:检查日期 */
+	private Date checkDate;
+
+	@ExcelFields({
+			@ExcelField(title="来料码", attrName="sn", align= ExcelField.Align.CENTER, sort=10),
+			@ExcelField(title="物料", attrName="materialTitle", align= ExcelField.Align.CENTER, sort=20),
+			@ExcelField(title="发货日期", attrName="faDate", align= ExcelField.Align.CENTER, sort=30),
+			@ExcelField(title="报检数量", attrName="nums", align= ExcelField.Align.CENTER, sort=40),
+			@ExcelField(title="参与抽检数量", attrName="faNums", align= ExcelField.Align.CENTER, sort=50),
+			@ExcelField(title="整单结果", attrName="recordState", align= ExcelField.Align.CENTER, sort=60),
+			@ExcelField(title="检查人", attrName="checkBy", align= ExcelField.Align.CENTER, sort=70),
+			@ExcelField(title="检查日期", attrName="checkDate", align= ExcelField.Align.CENTER, sort=80, dataFormat="yyyy-MM-dd HH:mm:ss"),
+			@ExcelField(title="项目", attrName="orgName", align= ExcelField.Align.CENTER, sort=90),
+			@ExcelField(title="检查内容", attrName="materialCheckTitle", align= ExcelField.Align.CENTER, sort=100),
+			@ExcelField(title="实测值1", attrName="a1", align= ExcelField.Align.CENTER, sort=110),
+			@ExcelField(title="实测值2", attrName="a2", align= ExcelField.Align.CENTER, sort=120),
+			@ExcelField(title="实测值3", attrName="a3", align= ExcelField.Align.CENTER, sort=130),
+			@ExcelField(title="实测值4", attrName="a4", align= ExcelField.Align.CENTER, sort=140),
+			@ExcelField(title="实测值5", attrName="a5", align= ExcelField.Align.CENTER, sort=150),
+			@ExcelField(title="实测值6", attrName="a6", align= ExcelField.Align.CENTER, sort=160),
+			@ExcelField(title="实测值7", attrName="a7", align= ExcelField.Align.CENTER, sort=170),
+			@ExcelField(title="实测值8", attrName="a8", align= ExcelField.Align.CENTER, sort=180),
+			@ExcelField(title="项目结果", attrName="result", align= ExcelField.Align.CENTER, sort=190),
+	})
 	public MesMaterialCheckRecordItems() {
 		this(null);
 	}
@@ -107,7 +154,7 @@ public class MesMaterialCheckRecordItems extends DataEntity<MesMaterialCheckReco
 	public String getA4() {
 		return a4;
 	}
-
+	
 	public void setA4(String a4) {
 		this.a4 = a4;
 	}
@@ -120,6 +167,30 @@ public class MesMaterialCheckRecordItems extends DataEntity<MesMaterialCheckReco
 		this.a5 = a5;
 	}
 
+	public String getA6() {
+		return a6;
+	}
+
+	public void setA6(String a6) {
+		this.a6 = a6;
+	}
+
+	public String getA7() {
+		return a7;
+	}
+
+	public void setA7(String a7) {
+		this.a7 = a7;
+	}
+
+	public String getA8() {
+		return a8;
+	}
+
+	public void setA8(String a8) {
+		this.a8 = a8;
+	}
+
 	public String getMaterialCheckTitle() {
 		return materialCheckTitle;
 	}
@@ -167,4 +238,68 @@ public class MesMaterialCheckRecordItems extends DataEntity<MesMaterialCheckReco
 	public void setResult(String result) {
 		this.result = result;
 	}
-}
+
+	public String getSn() {
+		return sn;
+	}
+
+	public void setSn(String sn) {
+		this.sn = sn;
+	}
+
+	public String getMaterialTitle() {
+		return materialTitle;
+	}
+
+	public void setMaterialTitle(String materialTitle) {
+		this.materialTitle = materialTitle;
+	}
+
+	public String getFaDate() {
+		return faDate;
+	}
+
+	public void setFaDate(String faDate) {
+		this.faDate = faDate;
+	}
+
+	public Long getNums() {
+		return nums;
+	}
+
+	public void setNums(Long nums) {
+		this.nums = nums;
+	}
+
+	public Long getFaNums() {
+		return faNums;
+	}
+
+	public void setFaNums(Long faNums) {
+		this.faNums = faNums;
+	}
+
+	public String getRecordState() {
+		return recordState;
+	}
+
+	public void setRecordState(String recordState) {
+		this.recordState = recordState;
+	}
+
+	public String getCheckBy() {
+		return checkBy;
+	}
+
+	public void setCheckBy(String checkBy) {
+		this.checkBy = checkBy;
+	}
+
+	public Date getCheckDate() {
+		return checkDate;
+	}
+
+	public void setCheckDate(Date checkDate) {
+		this.checkDate = checkDate;
+	}
+}

+ 137 - 3
src/main/java/com/jeesite/modules/mes/service/MesMaterialCheckRecordService.java

@@ -1,14 +1,17 @@
 package com.jeesite.modules.mes.service;
 
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
 import java.util.List;
 
-import com.jeesite.modules.mes.dao.MesDeviceDao;
-import com.jeesite.modules.mes.entity.MesDevice;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import com.jeesite.common.entity.Page;
+import com.jeesite.common.lang.StringUtils;
 import com.jeesite.common.service.CrudService;
 import com.jeesite.modules.mes.entity.MesMaterialCheckRecord;
 import com.jeesite.modules.mes.dao.MesMaterialCheckRecordDao;
@@ -22,6 +25,15 @@ import com.jeesite.modules.file.utils.FileUploadUtils;
 @Service
 public class MesMaterialCheckRecordService extends CrudService<MesMaterialCheckRecordDao, MesMaterialCheckRecord> {
 
+	/** 正常抽样数 */
+	public static final int SAMPLE_COUNT_NORMAL = 5;
+	/** 加严抽样数 */
+	public static final int SAMPLE_COUNT_TIGHTENED = 8;
+	/** 触发加严的本周累计不合格次数 */
+	public static final int FAIL_TRIGGER_COUNT = 3;
+	/** 解除加严所需连续合格次数 */
+	public static final int PASS_RELEASE_COUNT = 3;
+
 	@Autowired
 	private MesMaterialCheckRecordDao mesMaterialCheckRecordDao;
 
@@ -90,5 +102,127 @@ public class MesMaterialCheckRecordService extends CrudService<MesMaterialCheckR
 	public void delete(MesMaterialCheckRecord mesMaterialCheckRecord) {
 		super.delete(mesMaterialCheckRecord);
 	}
+
+	/**
+	 * 计算指定物料当前应使用的实测值抽样数。
+	 * <p>
+	 * 规则(按自然周,周一至周日):
+	 * 1. 正常为5个;本周当前周期内累计不合格达到3次后进入加严(8个);
+	 * 2. 加严期间连续3次合格后恢复5个,并清空本轮不合格计数;
+	 * 3. 恢复后不受本周历史不合格影响,需重新累计3次不合格才会再次加严。
+	 * </p>
+	 *
+	 * @param materialId 物料主键
+	 * @return 5 或 8
+	 */
+	public int resolveSampleCount(String materialId) {
+		if (StringUtils.isBlank(materialId)) {
+			return SAMPLE_COUNT_NORMAL;
+		}
+		Date weekStart = getCurrentWeekStart();
+		Date weekEnd = getCurrentWeekEnd();
+
+		MesMaterialCheckRecord query = new MesMaterialCheckRecord();
+		query.setMaterialId(materialId);
+		query.setCreateDate_gte(weekStart);
+		query.setCreateDate_lte(weekEnd);
+		List<MesMaterialCheckRecord> weekRecords = findList(query);
+		if (weekRecords == null || weekRecords.isEmpty()) {
+			return SAMPLE_COUNT_NORMAL;
+		}
+
+		Collections.sort(weekRecords, new Comparator<MesMaterialCheckRecord>() {
+			@Override
+			public int compare(MesMaterialCheckRecord o1, MesMaterialCheckRecord o2) {
+				Date d1 = o1.getCreateDate();
+				Date d2 = o2.getCreateDate();
+				if (d1 == null && d2 == null) {
+					return 0;
+				}
+				if (d1 == null) {
+					return -1;
+				}
+				if (d2 == null) {
+					return 1;
+				}
+				int cmp = d1.compareTo(d2);
+				if (cmp != 0) {
+					return cmp;
+				}
+				String id1 = o1.getId();
+				String id2 = o2.getId();
+				if (id1 == null && id2 == null) {
+					return 0;
+				}
+				if (id1 == null) {
+					return -1;
+				}
+				if (id2 == null) {
+					return 1;
+				}
+				return id1.compareTo(id2);
+			}
+		});
+
+		boolean tightened = false;
+		int failInCycle = 0;
+		int consecutivePass = 0;
+		for (MesMaterialCheckRecord record : weekRecords) {
+			boolean pass = isPassState(record.getState());
+			if (!tightened) {
+				if (!pass) {
+					failInCycle++;
+					if (failInCycle >= FAIL_TRIGGER_COUNT) {
+						tightened = true;
+						consecutivePass = 0;
+					}
+				}
+			} else {
+				if (pass) {
+					consecutivePass++;
+					if (consecutivePass >= PASS_RELEASE_COUNT) {
+						// 恢复正常并清空本轮计数,避免本周历史不合格再次触发加严
+						tightened = false;
+						failInCycle = 0;
+						consecutivePass = 0;
+					}
+				} else {
+					consecutivePass = 0;
+				}
+			}
+		}
+		return tightened ? SAMPLE_COUNT_TIGHTENED : SAMPLE_COUNT_NORMAL;
+	}
+
+	/**
+	 * 合格判定:仅 state=1 视为合格;不合格(2)、特采(3)等均按不合格计入加严统计。
+	 */
+	private boolean isPassState(String state) {
+		return "1".equals(state);
+	}
+
+	/** 本周一 00:00:00 */
+	private Date getCurrentWeekStart() {
+		Calendar cal = Calendar.getInstance();
+		cal.setFirstDayOfWeek(Calendar.MONDAY);
+		cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
+		cal.set(Calendar.HOUR_OF_DAY, 0);
+		cal.set(Calendar.MINUTE, 0);
+		cal.set(Calendar.SECOND, 0);
+		cal.set(Calendar.MILLISECOND, 0);
+		return cal.getTime();
+	}
+
+	/** 本周日 23:59:59.999 */
+	private Date getCurrentWeekEnd() {
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(getCurrentWeekStart());
+		cal.add(Calendar.DAY_OF_MONTH, 6);
+		cal.set(Calendar.HOUR_OF_DAY, 23);
+		cal.set(Calendar.MINUTE, 59);
+		cal.set(Calendar.SECOND, 59);
+		cal.set(Calendar.MILLISECOND, 999);
+		return cal.getTime();
+	}
 	
-}
+}

+ 7 - 0
src/main/java/com/jeesite/modules/mes/service/MesProductRecordService.java

@@ -576,6 +576,13 @@ public class MesProductRecordService extends CrudService<MesProductRecordDao, Me
 			String lx = oldOprno.length() >= 5 ? oldOprno.substring(2, 5) : oldOprno;
 			throw new ServiceException(text(ErrorMsg.getErrorMsg(timeErr, lx)));
 		}
+		if (mesLineProcess1.getType().equals("3")) {
+			String dbjErr = mesStationTimeValidateService.validateDbjEntry(oldOprno, lineSn, sn, mesLineProcess1);
+			if (dbjErr != null) {
+				String lx = oldOprno.length() >= 5 ? oldOprno.substring(2, 5) : oldOprno;
+				throw new ServiceException(text(ErrorMsg.getErrorMsg(dbjErr, lx)));
+			}
+		}
 
 		String cateSn = "";
 		content = content.equals("OK")?"OK":"NG";

+ 11 - 8
src/main/java/com/jeesite/modules/mes/service/MesStationTimeValidateService.java

@@ -20,7 +20,7 @@ import java.util.List;
 @Service
 public class MesStationTimeValidateService {
 
-	private static final int COOLING_OP023_TO_OP030_MINUTES = 30;
+	private static final int COOLING_OP023_TO_OP031_MINUTES = 30;
 	private static final int CROSS_OP150_TO_OP160_MINUTES = 15;
 
 	@Autowired
@@ -31,25 +31,28 @@ public class MesStationTimeValidateService {
 	private MesProductRecordDao mesProductRecordDao;
 
 	/**
-	 * 单部件工位进站校验(精追码)
+	 * 单部件工位进站/提交校验(精追码)
+	 * - 全部单部件工位:本工位已有 OK 则防重复(工位配置「可重复工作」时放行)
+	 * - OP031(后边框)额外:OP023 已完成 + 冷却满 30 分钟
 	 * @return 两位错误码,null 表示通过
 	 */
-	public String validateDbjEntry(String oldOprno, String lineSn, String partSn) {
-		if (!matchOprno(oldOprno, "OP030")) {
-			return null;
-		}
+	public String validateDbjEntry(String oldOprno, String lineSn, String partSn, MesLineProcess mesLineProcess) {
 		if (StringUtils.isBlank(partSn)) {
 			return "NE";
 		}
-		if (mesProductDbjRecordService.findLatestOk(partSn, oldOprno, lineSn) != null) {
+		boolean allowRepeat = mesLineProcess != null && "1".equals(mesLineProcess.getRepeat());
+		if (!allowRepeat && mesProductDbjRecordService.findLatestOk(partSn, oldOprno, lineSn) != null) {
 			return "RP";
 		}
+		if (!matchOprno(oldOprno, "OP031")) {
+			return null;
+		}
 		MesProductDbjRecord op023Ok = mesProductDbjRecordService.findLatestOk(partSn, "OP023", lineSn);
 		if (op023Ok == null || op023Ok.getCreateDate() == null) {
 			return "QD";
 		}
 		long elapsedMs = System.currentTimeMillis() - op023Ok.getCreateDate().getTime();
-		if (elapsedMs < COOLING_OP023_TO_OP030_MINUTES * 60L * 1000L) {
+		if (elapsedMs < COOLING_OP023_TO_OP031_MINUTES * 60L * 1000L) {
 			return "GH";
 		}
 		return null;

+ 38 - 4
src/main/java/com/jeesite/modules/mes/web/MesGp12ItemsController.java

@@ -2,6 +2,7 @@ package com.jeesite.modules.mes.web;
 
 import com.jeesite.common.config.Global;
 import com.jeesite.common.entity.Page;
+import com.jeesite.common.lang.StringUtils;
 import com.jeesite.common.web.BaseController;
 import com.jeesite.modules.mes.entity.MesGp12Items;
 import com.jeesite.modules.mes.resp.CommonResp;
@@ -18,12 +19,15 @@ import org.springframework.web.bind.annotation.ResponseBody;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * GP12工位问题项(不良原因)Controller
  * @author mes
- * @version 2026-08-04
+ * @version 2026-08-12
  */
 @Controller
 @RequestMapping(value = "${adminPath}/mes/mesGp12Items")
@@ -76,16 +80,46 @@ public class MesGp12ItemsController extends BaseController {
 	}
 
 	/**
-	 * 客户端:查询不良原因列表(anon)
+	 * 客户端:查询不良原因二级分类(按一级分组)
+	 * data: [{category, items:[{id,description,resOprno,sort}]}]
 	 */
 	@PostMapping(value = "items")
 	@ResponseBody
 	public CommonResp items() {
-		CommonResp<List<MesGp12Items>> resp = new CommonResp<>();
+		CommonResp<List<Map<String, Object>>> resp = new CommonResp<>();
 		List<MesGp12Items> list = mesGp12ItemsService.findList(new MesGp12Items());
+		Map<String, List<Map<String, Object>>> grouped = new LinkedHashMap<>();
+		if (list != null) {
+			for (MesGp12Items item : list) {
+				String category = StringUtils.isNotBlank(item.getCategory())
+						? item.getCategory().trim() : "未分类";
+				String desc = item.getDescription();
+				if (StringUtils.isBlank(desc)) {
+					continue;
+				}
+				List<Map<String, Object>> children = grouped.get(category);
+				if (children == null) {
+					children = new ArrayList<>();
+					grouped.put(category, children);
+				}
+				Map<String, Object> child = new LinkedHashMap<>();
+				child.put("id", item.getId());
+				child.put("description", desc.trim());
+				child.put("resOprno", item.getResOprno());
+				child.put("sort", item.getSort());
+				children.add(child);
+			}
+		}
+		List<Map<String, Object>> data = new ArrayList<>();
+		for (Map.Entry<String, List<Map<String, Object>>> entry : grouped.entrySet()) {
+			Map<String, Object> group = new LinkedHashMap<>();
+			group.put("category", entry.getKey());
+			group.put("items", entry.getValue());
+			data.add(group);
+		}
 		resp.setResult(Global.TRUE);
 		resp.setMessage("查询成功");
-		resp.setData(list);
+		resp.setData(data);
 		return resp;
 	}
 }

+ 551 - 33
src/main/java/com/jeesite/modules/mes/web/MesMaterialCheckRecordController.java

@@ -25,6 +25,7 @@ import com.jeesite.modules.file.utils.FileUploadUtils;
 import com.jeesite.modules.sys.entity.DictData;
 import com.jeesite.modules.sys.entity.DictType;
 import com.jeesite.modules.sys.service.DictDataService;
+import com.jeesite.modules.sys.utils.DictUtils;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
@@ -33,9 +34,26 @@ import org.springframework.ui.Model;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
+import com.jeesite.common.codec.EncodeUtils;
 import com.jeesite.common.config.Global;
 import com.jeesite.common.entity.Page;
+import com.jeesite.common.lang.DateUtils;
+import com.jeesite.common.utils.excel.ExcelExport;
 import com.jeesite.common.web.BaseController;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.FillPatternType;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.RegionUtil;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 
 /**
  * 来料检查记录表Controller
@@ -108,6 +126,43 @@ public class MesMaterialCheckRecordController extends BaseController {
 	}
 
 	/**
+	 * 导出检测数据(勾选记录展开检查明细)
+	 */
+	@RequiresPermissions("mes:mesMaterialCheckRecord:view")
+	@RequestMapping(value = "exportData")
+	public void exportData(MesMaterialCheckRecord mesMaterialCheckRecord, String ids, HttpServletResponse response) {
+		List<MesMaterialCheckRecordItems> exportList = buildCheckDataExportList(mesMaterialCheckRecord, ids);
+		String fileName = "来料检查检测数据" + DateUtils.getDate("yyyyMMddHHmmss") + ".xlsx";
+		try (ExcelExport ee = new ExcelExport("来料检查检测数据", MesMaterialCheckRecordItems.class)) {
+			ee.setDataList(exportList).write(response, fileName);
+		}
+	}
+
+	/**
+	 * 详情页导出:基本信息只写一次,下方为检测明细
+	 */
+	@RequiresPermissions("mes:mesMaterialCheckRecord:view")
+	@RequestMapping(value = "exportInfoData")
+	public void exportInfoData(MesMaterialCheckRecord mesMaterialCheckRecord, HttpServletResponse response) throws Exception {
+		if (mesMaterialCheckRecord == null || StringUtils.isBlank(mesMaterialCheckRecord.getId())) {
+			return;
+		}
+		MesMaterialCheckRecord record = mesMaterialCheckRecordService.get(mesMaterialCheckRecord.getId());
+		if (record == null) {
+			return;
+		}
+		MesMaterialCheckRecordItems itemQuery = new MesMaterialCheckRecordItems();
+		itemQuery.setRecordId(record.getId());
+		List<MesMaterialCheckRecordItems> items = mesMaterialCheckRecordItemsService.findList(itemQuery);
+		for (MesMaterialCheckRecordItems item : items) {
+			fillOrgName(item);
+		}
+		String fileName = "来料检查检测数据_" + StringUtils.defaultString(record.getSn()) + "_"
+				+ DateUtils.getDate("yyyyMMddHHmmss") + ".xlsx";
+		writeDetailCheckExcel(record, items, response, fileName);
+	}
+
+	/**
 	 * 查看编辑表单
 	 */
 	@RequiresPermissions("mes:mesMaterialCheckRecord:view")
@@ -126,6 +181,7 @@ public class MesMaterialCheckRecordController extends BaseController {
 		mesMaterialCheckRecordItems.setRecordId(mesMaterialCheckRecord.getId());
 		List<MesMaterialCheckRecordItems> items = mesMaterialCheckRecordItemsService.findList(mesMaterialCheckRecordItems);
 		model.addAttribute("MesMaterialCheckRecordItems", items);
+		model.addAttribute("sampleCount", resolveRecordSampleCount(mesMaterialCheckRecord, items));
 
 		return "modules/mes/mesMaterialCheckRecordForm2";
 	}
@@ -152,6 +208,8 @@ public class MesMaterialCheckRecordController extends BaseController {
 		}
 
 		mesMaterialCheckRecord.setState(result);
+		int sampleCount = mesMaterialCheckRecordService.resolveSampleCount(mesMaterialCheckRecord.getMaterialId());
+		mesMaterialCheckRecord.setSampleCount(sampleCount);
 		mesMaterialCheckRecordService.save(mesMaterialCheckRecord);
 
 		// 更新报检编号相同的都更新
@@ -179,6 +237,9 @@ public class MesMaterialCheckRecordController extends BaseController {
 			mesMaterialCheckRecordItems.setA3(mecheck.getJSONObject(i).getString("a3"));
 			mesMaterialCheckRecordItems.setA4(mecheck.getJSONObject(i).getString("a4"));
 			mesMaterialCheckRecordItems.setA5(mecheck.getJSONObject(i).getString("a5"));
+			mesMaterialCheckRecordItems.setA6(mecheck.getJSONObject(i).getString("a6"));
+			mesMaterialCheckRecordItems.setA7(mecheck.getJSONObject(i).getString("a7"));
+			mesMaterialCheckRecordItems.setA8(mecheck.getJSONObject(i).getString("a8"));
 			mesMaterialCheckRecordItems.setResult(mecheck.getJSONObject(i).getString("result"));
 			mesMaterialCheckRecordItemsService.save(mesMaterialCheckRecordItems);
 			String org = mecheck.getJSONObject(i).getString("org");
@@ -203,6 +264,7 @@ public class MesMaterialCheckRecordController extends BaseController {
 			MesApproval approval = mesApprovalService.get(approvalId);
 			MesMaterialBlpRecord mesMaterialBlpRecord = new MesMaterialBlpRecord();
 			mesMaterialBlpRecord.setCheckRecordId(mesMaterialCheckRecord.getId());
+			mesMaterialBlpRecord.setState("1");
 			mesMaterialBlpRecordService.save(mesMaterialBlpRecord);
 			if(!approval.getNodes().isEmpty()){
 				String[] ids = approval.getNodes().split(",");
@@ -237,6 +299,16 @@ public class MesMaterialCheckRecordController extends BaseController {
 		}
 
 		model.addAttribute("mitList", mitList);
+		model.addAttribute("sampleCount", resolveRecordSampleCount(mesMaterialCheckRecord, mitList));
+
+		List teCaiFiles = loadBizFiles(mesMaterialCheckRecord.getId(), "mesMaterialCheckRecord_teCai_file", host);
+		if (!ListUtils.isEmpty(teCaiFiles)) {
+			model.addAttribute("teCaiFiles", teCaiFiles);
+		}
+		List fuCeFiles = loadBizFiles(mesMaterialCheckRecord.getId(), "mesMaterialCheckRecord_fuCe_file", host);
+		if (!ListUtils.isEmpty(fuCeFiles)) {
+			model.addAttribute("fuCeFiles", fuCeFiles);
+		}
 
 		return "modules/mes/mesMaterialCheckRecordInfo";
 	}
@@ -418,12 +490,16 @@ public class MesMaterialCheckRecordController extends BaseController {
 			nums = Long.valueOf(result[2]);  // 新规则下默认为0
 		}
 
+		int sampleCount = mesMaterialCheckRecordService.resolveSampleCount(ma.getId());
+
 		Map<String, Object> map = MapUtils.newHashMap();
 		map.put("sn", sn);
 		map.put("nums", nums);
 		map.put("result", result);
 		map.put("info", ma);
 		map.put("mitList", mitList);
+		map.put("sampleCount", sampleCount);
+		map.put("tightened", sampleCount >= MesMaterialCheckRecordService.SAMPLE_COUNT_TIGHTENED);
 		resp.setData(map);
 		resp.setResult(Global.TRUE);
 		return resp;
@@ -436,39 +512,10 @@ public class MesMaterialCheckRecordController extends BaseController {
 	@RequestMapping(value = "edit")
 	public String edit(MesMaterialCheckRecord mesMaterialCheckRecord, Model model,HttpServletRequest req) {
 		model.addAttribute("mesMaterialCheckRecord", mesMaterialCheckRecord);
-//		MesMaterialCheckRecordItems mci = new MesMaterialCheckRecordItems();
-//		mci.setRecordId(mesMaterialCheckRecord.getId());
-//		List<MesMaterialCheckRecordItems> mitList = mesMaterialCheckRecordItemsService.findList(mci);
-//		model.addAttribute("mitList", mitList);
-//		String host = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + "/js";
-//
-//		for (MesMaterialCheckRecordItems item:mitList){
-//			DictData dictData = new DictData();
-//			dictData.setDictType("mes_material_org");
-//			dictData.setDictValue(item.getOrg());
-//			List<DictData> dic = dictDataService.findList(dictData);
-//			if(!ListUtils.isEmpty(dic)){
-//				item.setOrgName(dic.get(0).getDictLabel());
-//			}
-//
-//			FileUpload fu = new FileUpload();
-//			fu.setBizKey(item.getCheckId());
-//			fu.setBizType("mesMaterialCheck_image");
-//			List<FileUpload> finfo = fileUploadService.findList(fu);
-//			List imgList = ListUtils.newArrayList();
-//			for (FileUpload fi:finfo){
-//				Map<String, Object> map1 = MapUtils.newHashMap();
-//				map1.put("id",fi.getFileEntity().getFileId());
-//				map1.put("url",host + fi.getFileUrl());
-//				map1.put("name",fi.getFileName());
-//				map1.put("size",fi.getFileEntity().getFileSize());
-//
-//				imgList.add(map1);
-//			}
-//			item.setImg(imgList);
-//		}
-//
-//		model.addAttribute("mitList", mitList);
+		MesMaterialCheckRecordItems sampleQuery = new MesMaterialCheckRecordItems();
+		sampleQuery.setRecordId(mesMaterialCheckRecord.getId());
+		List<MesMaterialCheckRecordItems> sampleItems = mesMaterialCheckRecordItemsService.findList(sampleQuery);
+		model.addAttribute("sampleCount", resolveRecordSampleCount(mesMaterialCheckRecord, sampleItems));
 		return "modules/mes/mesMaterialCheckRecordFormEdit";
 	}
 	@RequestMapping(value = "getSnInfo2")
@@ -546,6 +593,289 @@ public class MesMaterialCheckRecordController extends BaseController {
 		}
 	}
 
+	/**
+	 * 组装检测明细导出数据:优先按勾选 ids,其次单条 id
+	 */
+	private List<MesMaterialCheckRecordItems> buildCheckDataExportList(MesMaterialCheckRecord query, String ids) {
+		List<MesMaterialCheckRecord> records = ListUtils.newArrayList();
+		if (StringUtils.isNotBlank(ids)) {
+			String[] idArr = ids.split(",");
+			MesMaterialCheckRecord idQuery = new MesMaterialCheckRecord();
+			idQuery.getSqlMap().getWhere().and("id", QueryType.IN, idArr);
+			records = mesMaterialCheckRecordService.findList(idQuery);
+		} else if (query != null && StringUtils.isNotBlank(query.getId())) {
+			MesMaterialCheckRecord record = mesMaterialCheckRecordService.get(query.getId());
+			if (record != null) {
+				records.add(record);
+			}
+		}
+		List<MesMaterialCheckRecordItems> exportList = ListUtils.newArrayList();
+		if (ListUtils.isEmpty(records)) {
+			return exportList;
+		}
+		for (MesMaterialCheckRecord record : records) {
+			MesMaterialCheckRecordItems itemQuery = new MesMaterialCheckRecordItems();
+			itemQuery.setRecordId(record.getId());
+			List<MesMaterialCheckRecordItems> items = mesMaterialCheckRecordItemsService.findList(itemQuery);
+			if (ListUtils.isEmpty(items)) {
+				continue;
+			}
+			String recordStateLabel = DictUtils.getDictLabel("mes_material_check_record_state", record.getState(), "未知");
+			for (MesMaterialCheckRecordItems item : items) {
+				fillOrgName(item);
+				item.setSn(record.getSn());
+				item.setMaterialTitle(record.getMaterialTitle());
+				item.setFaDate(record.getFaDate());
+				item.setNums(record.getNums());
+				item.setFaNums(record.getFaNums());
+				item.setRecordState(recordStateLabel);
+				item.setCheckBy(record.getCreateBy());
+				item.setCheckDate(record.getCreateDate());
+				if ("1".equals(item.getResult())) {
+					item.setResult("合格");
+				} else {
+					item.setResult("不合格");
+				}
+				exportList.add(item);
+			}
+		}
+		return exportList;
+	}
+
+	/**
+	 * 详情页专用导出:上方基本信息(不重复),下方检测明细表
+	 */
+	private void writeDetailCheckExcel(MesMaterialCheckRecord record, List<MesMaterialCheckRecordItems> items,
+			HttpServletResponse response, String fileName) throws Exception {
+		try (Workbook wb = new XSSFWorkbook()) {
+			Sheet sheet = wb.createSheet("来料检查检测数据");
+			int sampleCount = resolveRecordSampleCount(record, items);
+			// 项目 + 检查内容 + 实测值N + 项目结果
+			final int colCount = 2 + sampleCount + 1;
+
+			Font titleFont = wb.createFont();
+			titleFont.setBold(true);
+			titleFont.setFontHeightInPoints((short) 16);
+			titleFont.setFontName("微软雅黑");
+
+			Font sectionFont = wb.createFont();
+			sectionFont.setBold(true);
+			sectionFont.setFontHeightInPoints((short) 12);
+			sectionFont.setFontName("微软雅黑");
+
+			Font boldFont = wb.createFont();
+			boldFont.setBold(true);
+			boldFont.setFontName("微软雅黑");
+			boldFont.setFontHeightInPoints((short) 10);
+
+			Font normalFont = wb.createFont();
+			normalFont.setFontName("微软雅黑");
+			normalFont.setFontHeightInPoints((short) 10);
+
+			short lightGray = IndexedColors.GREY_25_PERCENT.getIndex();
+			CellStyle titleStyle = createBorderedStyle(wb, titleFont, HorizontalAlignment.CENTER, true, lightGray);
+			CellStyle sectionStyle = createBorderedStyle(wb, sectionFont, HorizontalAlignment.LEFT, true, lightGray);
+			CellStyle labelStyle = createBorderedStyle(wb, boldFont, HorizontalAlignment.CENTER, true, lightGray);
+			CellStyle valueStyle = createBorderedStyle(wb, normalFont, HorizontalAlignment.LEFT, false, null);
+			CellStyle headerStyle = createBorderedStyle(wb, boldFont, HorizontalAlignment.CENTER, true, lightGray);
+			headerStyle.setWrapText(true);
+			CellStyle centerStyle = createBorderedStyle(wb, normalFont, HorizontalAlignment.CENTER, false, null);
+			centerStyle.setWrapText(true);
+			CellStyle leftWrapStyle = createBorderedStyle(wb, normalFont, HorizontalAlignment.LEFT, false, null);
+			leftWrapStyle.setWrapText(true);
+
+			int rowIdx = 0;
+			Row titleRow = sheet.createRow(rowIdx++);
+			titleRow.setHeightInPoints(30);
+			fillRowCells(titleRow, colCount, titleStyle);
+			titleRow.getCell(0).setCellValue("来料检查检测数据");
+			CellRangeAddress titleRegion = new CellRangeAddress(0, 0, 0, colCount - 1);
+			sheet.addMergedRegion(titleRegion);
+			applyRegionBorder(sheet, titleRegion);
+
+			rowIdx++;
+			Row baseSection = sheet.createRow(rowIdx++);
+			baseSection.setHeightInPoints(20);
+			fillRowCells(baseSection, colCount, sectionStyle);
+			baseSection.getCell(0).setCellValue("一、基本信息");
+			CellRangeAddress baseSectionRegion = new CellRangeAddress(baseSection.getRowNum(), baseSection.getRowNum(), 0, colCount - 1);
+			sheet.addMergedRegion(baseSectionRegion);
+			applyRegionBorder(sheet, baseSectionRegion);
+
+			String recordStateLabel = DictUtils.getDictLabel("mes_material_check_record_state", record.getState(), "未知");
+			String checkDate = record.getCreateDate() == null ? "" : DateUtils.formatDateTime(record.getCreateDate());
+			String[][] basePairs = new String[][]{
+					{"来料码", StringUtils.defaultString(record.getSn()), "物料", StringUtils.defaultString(record.getMaterialTitle())},
+					{"发货日期", StringUtils.defaultString(record.getFaDate()), "报检数量", record.getNums() == null ? "" : String.valueOf(record.getNums())},
+					{"参与抽检数量", record.getFaNums() == null ? "" : String.valueOf(record.getFaNums()), "整单结果", recordStateLabel},
+					{"检查人", StringUtils.defaultString(record.getCreateBy()), "检查日期", checkDate},
+					{"抽样数", String.valueOf(sampleCount), "", ""},
+			};
+			int mid = Math.max(3, colCount / 2);
+			for (String[] pair : basePairs) {
+				Row row = sheet.createRow(rowIdx++);
+				row.setHeightInPoints(20);
+				fillRowCells(row, colCount, valueStyle);
+				row.getCell(0).setCellValue(pair[0]);
+				row.getCell(0).setCellStyle(labelStyle);
+				row.getCell(1).setCellValue(pair[1]);
+				row.getCell(1).setCellStyle(valueStyle);
+				if (mid - 1 >= 2) {
+					CellRangeAddress leftValueRegion = new CellRangeAddress(row.getRowNum(), row.getRowNum(), 1, mid - 1);
+					sheet.addMergedRegion(leftValueRegion);
+					applyRegionBorder(sheet, leftValueRegion);
+				}
+				if (StringUtils.isNotBlank(pair[2])) {
+					row.getCell(mid).setCellValue(pair[2]);
+					row.getCell(mid).setCellStyle(labelStyle);
+					row.getCell(mid + 1).setCellValue(pair[3]);
+					row.getCell(mid + 1).setCellStyle(valueStyle);
+					if (colCount - 1 > mid + 1) {
+						CellRangeAddress rightValueRegion = new CellRangeAddress(row.getRowNum(), row.getRowNum(), mid + 1, colCount - 1);
+						sheet.addMergedRegion(rightValueRegion);
+						applyRegionBorder(sheet, rightValueRegion);
+					}
+				} else if (colCount - 1 > 1) {
+					CellRangeAddress leftOnlyRegion = new CellRangeAddress(row.getRowNum(), row.getRowNum(), 1, colCount - 1);
+					sheet.addMergedRegion(leftOnlyRegion);
+					applyRegionBorder(sheet, leftOnlyRegion);
+				}
+			}
+
+			rowIdx++;
+			Row detailSection = sheet.createRow(rowIdx++);
+			detailSection.setHeightInPoints(20);
+			fillRowCells(detailSection, colCount, sectionStyle);
+			detailSection.getCell(0).setCellValue("二、检测明细");
+			CellRangeAddress detailSectionRegion = new CellRangeAddress(detailSection.getRowNum(), detailSection.getRowNum(), 0, colCount - 1);
+			sheet.addMergedRegion(detailSectionRegion);
+			applyRegionBorder(sheet, detailSectionRegion);
+
+			String[] headers = new String[colCount];
+			headers[0] = "项目";
+			headers[1] = "检查内容";
+			for (int i = 1; i <= sampleCount; i++) {
+				headers[1 + i] = "实测值" + i;
+			}
+			headers[colCount - 1] = "项目结果";
+			Row headerRow = sheet.createRow(rowIdx++);
+			headerRow.setHeightInPoints(22);
+			for (int i = 0; i < headers.length; i++) {
+				Cell cell = headerRow.createCell(i);
+				cell.setCellValue(headers[i]);
+				cell.setCellStyle(headerStyle);
+			}
+
+			if (!ListUtils.isEmpty(items)) {
+				for (MesMaterialCheckRecordItems item : items) {
+					Row row = sheet.createRow(rowIdx++);
+					row.setHeightInPoints(20);
+					String resultLabel = "1".equals(item.getResult()) ? "合格" : "不合格";
+					String[] sampleValues = {
+							StringUtils.defaultString(item.getA1()),
+							StringUtils.defaultString(item.getA2()),
+							StringUtils.defaultString(item.getA3()),
+							StringUtils.defaultString(item.getA4()),
+							StringUtils.defaultString(item.getA5()),
+							StringUtils.defaultString(item.getA6()),
+							StringUtils.defaultString(item.getA7()),
+							StringUtils.defaultString(item.getA8())
+					};
+					String[] values = new String[colCount];
+					values[0] = StringUtils.defaultString(item.getOrgName());
+					values[1] = StringUtils.defaultString(item.getMaterialCheckTitle());
+					for (int i = 0; i < sampleCount; i++) {
+						values[2 + i] = sampleValues[i];
+					}
+					values[colCount - 1] = resultLabel;
+					for (int i = 0; i < values.length; i++) {
+						Cell cell = row.createCell(i);
+						cell.setCellValue(values[i]);
+						cell.setCellStyle(i == 1 ? leftWrapStyle : centerStyle);
+					}
+				}
+			} else {
+				Row emptyRow = sheet.createRow(rowIdx++);
+				emptyRow.setHeightInPoints(20);
+				fillRowCells(emptyRow, colCount, centerStyle);
+				emptyRow.getCell(0).setCellValue("暂无检测明细");
+				CellRangeAddress emptyRegion = new CellRangeAddress(emptyRow.getRowNum(), emptyRow.getRowNum(), 0, colCount - 1);
+				sheet.addMergedRegion(emptyRegion);
+				applyRegionBorder(sheet, emptyRegion);
+			}
+
+			sheet.setColumnWidth(0, 3500);
+			sheet.setColumnWidth(1, 12000);
+			for (int i = 0; i < sampleCount; i++) {
+				sheet.setColumnWidth(2 + i, 3200);
+			}
+			sheet.setColumnWidth(colCount - 1, 3500);
+			sheet.createFreezePane(0, headerRow.getRowNum() + 1);
+
+			response.reset();
+			response.setContentType("application/octet-stream; charset=utf-8");
+			response.addHeader("Content-Disposition", "attachment; filename*=utf-8'zh_cn'" + EncodeUtils.encodeUrl(fileName));
+			wb.write(response.getOutputStream());
+		}
+	}
+
+	/**
+	 * 解析历史记录的抽样数:优先用记录字段;旧数据则根据是否存在 a6~a8 推断。
+	 */
+	private int resolveRecordSampleCount(MesMaterialCheckRecord record, List<MesMaterialCheckRecordItems> items) {
+		if (record != null && record.getSampleCount() != null && record.getSampleCount() > 0) {
+			return record.getSampleCount();
+		}
+		if (!ListUtils.isEmpty(items)) {
+			for (MesMaterialCheckRecordItems item : items) {
+				if (StringUtils.isNotBlank(item.getA6())
+						|| StringUtils.isNotBlank(item.getA7())
+						|| StringUtils.isNotBlank(item.getA8())) {
+					return MesMaterialCheckRecordService.SAMPLE_COUNT_TIGHTENED;
+				}
+			}
+		}
+		return MesMaterialCheckRecordService.SAMPLE_COUNT_NORMAL;
+	}
+
+	private CellStyle createBorderedStyle(Workbook wb, Font font, HorizontalAlignment align, boolean fill, Short fillColor) {
+		CellStyle style = wb.createCellStyle();
+		style.setFont(font);
+		style.setAlignment(align);
+		style.setVerticalAlignment(VerticalAlignment.CENTER);
+		style.setBorderTop(BorderStyle.THIN);
+		style.setBorderBottom(BorderStyle.THIN);
+		style.setBorderLeft(BorderStyle.THIN);
+		style.setBorderRight(BorderStyle.THIN);
+		style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+		style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+		style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+		style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+		if (fill && fillColor != null) {
+			style.setFillForegroundColor(fillColor);
+			style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+		}
+		return style;
+	}
+
+	private void fillRowCells(Row row, int colCount, CellStyle style) {
+		for (int i = 0; i < colCount; i++) {
+			Cell cell = row.createCell(i);
+			cell.setCellStyle(style);
+		}
+	}
+
+	private void applyRegionBorder(Sheet sheet, CellRangeAddress region) {
+		RegionUtil.setBorderTop(BorderStyle.THIN, region, sheet);
+		RegionUtil.setBorderBottom(BorderStyle.THIN, region, sheet);
+		RegionUtil.setBorderLeft(BorderStyle.THIN, region, sheet);
+		RegionUtil.setBorderRight(BorderStyle.THIN, region, sheet);
+		RegionUtil.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex(), region, sheet);
+		RegionUtil.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex(), region, sheet);
+		RegionUtil.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex(), region, sheet);
+		RegionUtil.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex(), region, sheet);
+	}
+
 	private void loadCheckTemplateImages(MesMaterialCheckRecordItems item, String host) {
 		FileUpload fu = new FileUpload();
 		fu.setBizKey(item.getCheckId());
@@ -598,6 +928,9 @@ public class MesMaterialCheckRecordController extends BaseController {
 			items.setA3(mecheck.getJSONObject(i).getString("a3"));
 			items.setA4(mecheck.getJSONObject(i).getString("a4"));
 			items.setA5(mecheck.getJSONObject(i).getString("a5"));
+			items.setA6(mecheck.getJSONObject(i).getString("a6"));
+			items.setA7(mecheck.getJSONObject(i).getString("a7"));
+			items.setA8(mecheck.getJSONObject(i).getString("a8"));
 			items.setResult(mecheck.getJSONObject(i).getString("result"));
 			mesMaterialCheckRecordItemsService.save(items);
 			String org = mecheck.getJSONObject(i).getString("org");
@@ -609,4 +942,189 @@ public class MesMaterialCheckRecordController extends BaseController {
 		return renderResult(Global.TRUE, text("保存成功!"));
 	}
 
+	/**
+	 * 特采表单
+	 */
+	@RequiresPermissions("mes:mesMaterialCheckRecord:edit")
+	@RequestMapping(value = "teCai")
+	public String teCai(MesMaterialCheckRecord mesMaterialCheckRecord, Model model) {
+		model.addAttribute("mesMaterialCheckRecord", mesMaterialCheckRecord);
+		return "modules/mes/mesMaterialCheckRecordTeCaiForm";
+	}
+
+	/**
+	 * 特采保存:上传附件后标注为特采,并生成让步入库单
+	 */
+	@RequiresPermissions("mes:mesMaterialCheckRecord:edit")
+	@PostMapping(value = "teCaiSave")
+	@ResponseBody
+	@Transactional
+	public String teCaiSave(MesMaterialCheckRecord mesMaterialCheckRecord) {
+		if (mesMaterialCheckRecord == null || StringUtils.isBlank(mesMaterialCheckRecord.getId())) {
+			return renderResult(Global.FALSE, text("记录不存在"));
+		}
+		MesMaterialCheckRecord record = mesMaterialCheckRecordService.get(mesMaterialCheckRecord.getId());
+		if (record == null) {
+			return renderResult(Global.FALSE, text("记录不存在"));
+		}
+		if (!"2".equals(record.getState())) {
+			return renderResult(Global.FALSE, text("仅不合格记录可进行特采"));
+		}
+
+		MesMaterialRkRecord rkQuery = new MesMaterialRkRecord();
+		rkQuery.getSqlMap().getWhere().and("check_record_id", QueryType.EQ, record.getId());
+		List<MesMaterialRkRecord> existRk = mesMaterialRkRecordService.findList(rkQuery);
+		if (!ListUtils.isEmpty(existRk)) {
+			return renderResult(Global.FALSE, text("该记录已生成入库单,不能重复特采"));
+		}
+
+		// 先保存附件,再校验是否已上传
+		FileUploadUtils.saveFileUpload(mesMaterialCheckRecord, record.getId(), "mesMaterialCheckRecord_teCai_file");
+		FileUpload fu = new FileUpload();
+		fu.setBizKey(record.getId());
+		fu.setBizType("mesMaterialCheckRecord_teCai_file");
+		List<FileUpload> teCaiFiles = fileUploadService.findList(fu);
+		if (ListUtils.isEmpty(teCaiFiles)) {
+			return renderResult(Global.FALSE, text("请先上传特采文件"));
+		}
+
+		// 标注为特采
+		record.setState("3");
+		mesMaterialCheckRecordService.update(record);
+
+		// 生成让步入库单(与不良品审批通过逻辑一致)
+		MesMaterialRkRecord mesMaterialRkRecord = new MesMaterialRkRecord();
+		mesMaterialRkRecord.setCheckRecordId(record.getId());
+		mesMaterialRkRecord.setType("2");
+		mesMaterialRkRecord.setRemark("特采入库");
+		mesMaterialRkRecordService.save(mesMaterialRkRecord);
+
+		MesMaterialRkRecordItems mesMaterialRkRecordItems = new MesMaterialRkRecordItems();
+		mesMaterialRkRecordItems.setRecordId(mesMaterialRkRecord.getId());
+		mesMaterialRkRecordItems.setMaterialId(record.getMaterialId());
+		mesMaterialRkRecordItems.setNums(record.getNums());
+		mesMaterialRkRecordItemsService.save(mesMaterialRkRecordItems);
+
+		// 关闭关联不良品待审记录,避免重复审批入库
+		closePendingBlp(record.getId(), "特采自动关闭");
+
+		return renderResult(Global.TRUE, text("特采成功,已生成让步入库单!"));
+	}
+
+	/**
+	 * 复测表单
+	 */
+	@RequiresPermissions("mes:mesMaterialCheckRecord:edit")
+	@RequestMapping(value = "fuCe")
+	public String fuCe(MesMaterialCheckRecord mesMaterialCheckRecord, Model model) {
+		model.addAttribute("mesMaterialCheckRecord", mesMaterialCheckRecord);
+		return "modules/mes/mesMaterialCheckRecordFuCeForm";
+	}
+
+	/**
+	 * 复测保存:上传附件并选择合格/不合格
+	 */
+	@RequiresPermissions("mes:mesMaterialCheckRecord:edit")
+	@PostMapping(value = "fuCeSave")
+	@ResponseBody
+	@Transactional
+	public String fuCeSave(MesMaterialCheckRecord mesMaterialCheckRecord, HttpServletRequest request) {
+		if (mesMaterialCheckRecord == null || StringUtils.isBlank(mesMaterialCheckRecord.getId())) {
+			return renderResult(Global.FALSE, text("记录不存在"));
+		}
+		MesMaterialCheckRecord record = mesMaterialCheckRecordService.get(mesMaterialCheckRecord.getId());
+		if (record == null) {
+			return renderResult(Global.FALSE, text("记录不存在"));
+		}
+		if (!"2".equals(record.getState())) {
+			return renderResult(Global.FALSE, text("仅不合格记录可进行复测"));
+		}
+
+		String retestResult = request.getParameter("retestResult");
+		if (!"1".equals(retestResult) && !"2".equals(retestResult)) {
+			return renderResult(Global.FALSE, text("请选择复测结果"));
+		}
+
+		FileUploadUtils.saveFileUpload(mesMaterialCheckRecord, record.getId(), "mesMaterialCheckRecord_fuCe_file");
+		FileUpload fu = new FileUpload();
+		fu.setBizKey(record.getId());
+		fu.setBizType("mesMaterialCheckRecord_fuCe_file");
+		List<FileUpload> fuCeFiles = fileUploadService.findList(fu);
+		if (ListUtils.isEmpty(fuCeFiles)) {
+			return renderResult(Global.FALSE, text("请先上传复测文件"));
+		}
+
+		if ("1".equals(retestResult)) {
+			MesMaterialRkRecord rkQuery = new MesMaterialRkRecord();
+			rkQuery.getSqlMap().getWhere().and("check_record_id", QueryType.EQ, record.getId());
+			List<MesMaterialRkRecord> existRk = mesMaterialRkRecordService.findList(rkQuery);
+			if (!ListUtils.isEmpty(existRk)) {
+				return renderResult(Global.FALSE, text("该记录已生成入库单,不能重复提交合格"));
+			}
+
+			record.setState("1");
+			mesMaterialCheckRecordService.update(record);
+
+			MesMaterialRkRecord mesMaterialRkRecord = new MesMaterialRkRecord();
+			mesMaterialRkRecord.setCheckRecordId(record.getId());
+			mesMaterialRkRecord.setType("1");
+			mesMaterialRkRecord.setRemark("复测合格入库");
+			mesMaterialRkRecordService.save(mesMaterialRkRecord);
+
+			MesMaterialRkRecordItems mesMaterialRkRecordItems = new MesMaterialRkRecordItems();
+			mesMaterialRkRecordItems.setRecordId(mesMaterialRkRecord.getId());
+			mesMaterialRkRecordItems.setMaterialId(record.getMaterialId());
+			mesMaterialRkRecordItems.setNums(record.getNums());
+			mesMaterialRkRecordItemsService.save(mesMaterialRkRecordItems);
+
+			closePendingBlp(record.getId(), "复测合格自动关闭");
+			return renderResult(Global.TRUE, text("复测合格,已生成入库单!"));
+		}
+
+		// 复测仍不合格:仅保存附件,结果保持不合格
+		return renderResult(Global.TRUE, text("复测结果已提交(仍不合格)!"));
+	}
+
+	private List loadBizFiles(String bizKey, String bizType, String host) {
+		FileUpload fu = new FileUpload();
+		fu.setBizKey(bizKey);
+		fu.setBizType(bizType);
+		List<FileUpload> finfo = fileUploadService.findList(fu);
+		List fileList = ListUtils.newArrayList();
+		for (FileUpload fi : finfo) {
+			Map<String, Object> map1 = MapUtils.newHashMap();
+			map1.put("id", fi.getFileEntity().getFileId());
+			map1.put("url", host + fi.getFileUrl());
+			map1.put("name", fi.getFileName());
+			map1.put("size", fi.getFileEntity().getFileSize());
+			fileList.add(map1);
+		}
+		return fileList;
+	}
+
+	private void closePendingBlp(String checkRecordId, String remark) {
+		MesMaterialBlpRecord blpQuery = new MesMaterialBlpRecord();
+		blpQuery.getSqlMap().getWhere().and("check_record_id", QueryType.EQ, checkRecordId);
+		List<MesMaterialBlpRecord> blpList = mesMaterialBlpRecordService.findList(blpQuery);
+		if (ListUtils.isEmpty(blpList)) {
+			return;
+		}
+		for (MesMaterialBlpRecord blp : blpList) {
+			if (StringUtils.isBlank(blp.getState()) || "1".equals(blp.getState())) {
+				blp.setState("2");
+				mesMaterialBlpRecordService.update(blp);
+				List<MesApply> applyList = mesApplyService.getApplyData("2", blp.getId(), 0);
+				if (!ListUtils.isEmpty(applyList)) {
+					for (MesApply apply : applyList) {
+						if ("0".equals(apply.getState())) {
+							apply.setState("3");
+							apply.setRemark(remark);
+							mesApplyService.update(apply);
+						}
+					}
+				}
+			}
+		}
+	}
+
 }

+ 1 - 1
src/main/java/com/jeesite/modules/mes/web/MesMaterialLlRecordController.java

@@ -131,7 +131,7 @@ public class MesMaterialLlRecordController extends BaseController {
 			MesMaterialLlRecordItems mesMaterialLlRecordItems = new MesMaterialLlRecordItems();
 			mesMaterialLlRecordItems.setRecordId(mesMaterialLlRecord.getId());
 			mesMaterialLlRecordItems.setMaterialId(mecheck.getJSONObject(i).getString("id"));
-			Long s = Long.valueOf(mecheck.getJSONObject(i).getString("num"))*Long.valueOf(mecheck.getJSONObject(i).getString("nums"));
+			Long s = Long.valueOf(mecheck.getJSONObject(i).getString("num"));
 			mesMaterialLlRecordItems.setNums(s);
 			mesMaterialLlRecordItemsService.save(mesMaterialLlRecordItems);
 		}

+ 40 - 32
src/main/java/com/jeesite/modules/mes/web/MesMaterialPrebindController.java

@@ -1,17 +1,15 @@
 package com.jeesite.modules.mes.web;
 
-import java.util.ArrayList;
 import java.util.List;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import com.jeesite.common.collect.ListUtils;
 import com.jeesite.common.lang.ObjectUtils;
+import com.jeesite.common.lang.StringUtils;
 import com.jeesite.modules.mes.entity.*;
-import com.jeesite.modules.mes.resp.BindMaterialResp;
-import com.jeesite.modules.mes.resp.CommonResp;
 import com.jeesite.modules.mes.service.MesLineProcessMaterialService;
-import com.jeesite.modules.mes.service.MesMaterialKcLogService;
+import com.jeesite.modules.mes.service.MesMaterialService;
 import com.jeesite.modules.mes.util.CommonUitl;
 import com.jeesite.common.mybatis.mapper.query.QueryType;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
@@ -42,7 +40,7 @@ public class MesMaterialPrebindController extends BaseController {
 	private MesMaterialPrebindService mesMaterialPrebindService;
 
 	@Autowired
-	private MesMaterialKcLogService mesMaterialKcLogService;
+	private MesMaterialService mesMaterialService;
 
 	@Autowired
 	private MesLineProcessMaterialService mesLineProcessMaterialService;
@@ -102,33 +100,15 @@ public class MesMaterialPrebindController extends BaseController {
 	@ResponseBody
 	public String bind(MesMaterialPrebind mesMaterialPrebind,HttpServletRequest request) {
 		String craft = mesMaterialPrebind.getCraft();
-		//TODO::检查批次码是否符合要求
+		String batchSn = StringUtils.trim(mesMaterialPrebind.getBatchSn());
+		mesMaterialPrebind.setBatchSn(batchSn);
+
+		// 校验批次码前缀是否匹配物料 ERP 料号(mes.bind.material.check=1 开启)
 		String bindcheck = Global.getConfig("mes.bind.material.check");
-		if(bindcheck.equals("1")){
-			// 检查批次码是否是库中批次码
-			MesMaterialKcLog mesMaterialKcLog = new MesMaterialKcLog();
-			mesMaterialKcLog.setBatchSn(mesMaterialPrebind.getBatchSn());
-			MesMaterialKcLog mesMaterialKcLog2 = mesMaterialKcLogService.findInfo(mesMaterialKcLog);
-			if(ObjectUtils.isEmpty(mesMaterialKcLog2)){
-				return renderResult(Global.FALSE, text("批次码不正确!"));
-			}
-			// 检查物料是否是本工位需要的
-			MesLineProcessMaterial mesLineProcessMaterial = new MesLineProcessMaterial();
-			mesLineProcessMaterial.setOprno(CommonUitl.formatOprno(mesMaterialPrebind.getOprno()));
-			mesLineProcessMaterial.setLineSn(mesMaterialPrebind.getLineSn());
-			List<MesLineProcessMaterial> lists = mesLineProcessMaterialService.findList(mesLineProcessMaterial);
-			if(!ListUtils.isEmpty(lists)){
-				Boolean flag = false;
-				for(MesLineProcessMaterial mesLineProcessMaterial1:lists){
-					if(mesMaterialKcLog2.getErpSn().equals(mesLineProcessMaterial1.getErpSn())){
-						flag = true;
-						craft = mesLineProcessMaterial1.getCraft();
-						break;
-					}
-				}
-				if(!flag){
-					return renderResult(Global.FALSE, text("批次码不正确!"));
-				}
+		if ("1".equals(bindcheck)) {
+			String erpSn = resolveBindErpSn(mesMaterialPrebind);
+			if (StringUtils.isEmpty(batchSn) || StringUtils.isEmpty(erpSn) || !batchSn.startsWith(erpSn)) {
+				return renderResult(Global.FALSE, text("批次码格式不正确!"));
 			}
 		}
 
@@ -144,7 +124,7 @@ public class MesMaterialPrebindController extends BaseController {
 				mesMaterialPrebind.getMaterialId());
 
 		if(!ObjectUtils.isEmpty(mesMaterialPrebind2)){
-			mesMaterialPrebind2.setBatchSn(mesMaterialPrebind.getBatchSn());
+			mesMaterialPrebind2.setBatchSn(batchSn);
 			mesMaterialPrebind2.setLastTimes(lastTimes);
 			mesMaterialPrebindService.save(mesMaterialPrebind2);
 		}else{
@@ -157,6 +137,34 @@ public class MesMaterialPrebindController extends BaseController {
 	}
 
 	/**
+	 * 取绑定目标物料的 ERP 料号:优先 materialId,其次按工位+工艺号匹配工序物料。
+	 */
+	private String resolveBindErpSn(MesMaterialPrebind prebind) {
+		if (StringUtils.isNotEmpty(prebind.getMaterialId())) {
+			MesMaterial material = mesMaterialService.get(prebind.getMaterialId());
+			if (material != null && StringUtils.isNotEmpty(material.getErpSn())) {
+				return material.getErpSn().trim();
+			}
+		}
+		MesLineProcessMaterial query = new MesLineProcessMaterial();
+		query.setLineSn(prebind.getLineSn());
+		query.setOprno(CommonUitl.formatOprno(prebind.getOprno()));
+		List<MesLineProcessMaterial> lists = mesLineProcessMaterialService.findList(query);
+		if (ListUtils.isEmpty(lists)) {
+			return null;
+		}
+		String craft = prebind.getCraft();
+		if (StringUtils.isNotEmpty(craft)) {
+			for (MesLineProcessMaterial item : lists) {
+				if (craft.equals(item.getCraft()) && StringUtils.isNotEmpty(item.getErpSn())) {
+					return item.getErpSn().trim();
+				}
+			}
+		}
+		return null;
+	}
+
+	/**
 	 * 绑定批次时,剩余次数优先取工序物料配置的 nums(数量);未配置时回退全局参数。
 	 */
 	private Integer resolveBindLastTimes(String oprno, String lineSn, String craft, String materialId) {

+ 5 - 1
src/main/java/com/jeesite/modules/mes/web/MesProductRecordController.java

@@ -1163,7 +1163,7 @@ public class MesProductRecordController extends BaseController {
 				return ret;
 			}
 
-			String timeErr = mesStationTimeValidateService.validateDbjEntry(oldOprno, lineSn, sn);
+			String timeErr = mesStationTimeValidateService.validateDbjEntry(oldOprno, lineSn, sn, mesLineProcess1);
 			if (timeErr != null) {
 				return bs + timeErr;
 			}
@@ -1514,6 +1514,10 @@ public class MesProductRecordController extends BaseController {
 			return "RS" + timeErr;
 		}
 		if(mesLineProcess1.getType().equals("3")){ // 单部件
+			String dbjErr = mesStationTimeValidateService.validateDbjEntry(oprno, lineSn, sn, mesLineProcess1);
+			if (dbjErr != null) {
+				return "RS" + dbjErr;
+			}
 			List<BindMaterialResp> bmlists = mesLineProcessMaterialService.getBindMaterial(oprno, lineSn);
 			mesProductRecordService.updateRecordResult(sn,oprno,craft,"",content,userCode,params,lineSn,true,bmlists);
 		}else{

+ 24 - 2
src/main/resources/views/modules/mes/mesGp12ItemsForm.html

@@ -17,6 +17,28 @@
 					<div class="col-xs-12">
 						<div class="form-group">
 							<label class="control-label col-sm-2">
+								<span class="required">*</span> ${text('一级分类')}:</label>
+							<div class="col-sm-8">
+								<#form:input path="category" maxlength="100" class="form-control required" placeholder="如:尺寸、外观、焊接缺陷"/>
+							</div>
+						</div>
+					</div>
+				</div>
+				<div class="row">
+					<div class="col-xs-12">
+						<div class="form-group">
+							<label class="control-label col-sm-2">
+								<span class="required">*</span> ${text('二级分类')}:</label>
+							<div class="col-sm-8">
+								<#form:input path="description" maxlength="255" class="form-control required" placeholder="如:孔径超差、标识漏"/>
+							</div>
+						</div>
+					</div>
+				</div>
+				<div class="row">
+					<div class="col-xs-12">
+						<div class="form-group">
+							<label class="control-label col-sm-2">
 								${text('责任工位')}:</label>
 							<div class="col-sm-8">
 								<#form:input path="resOprno" maxlength="20" class="form-control"/>
@@ -28,9 +50,9 @@
 					<div class="col-xs-12">
 						<div class="form-group">
 							<label class="control-label col-sm-2">
-								<span class="required">*</span> ${text('问题描述')}:</label>
+								${text('排序号')}:</label>
 							<div class="col-sm-8">
-								<#form:input path="description" maxlength="255" class="form-control required"/>
+								<#form:input path="sort" maxlength="10" class="form-control digits"/>
 							</div>
 						</div>
 					</div>

+ 13 - 5
src/main/resources/views/modules/mes/mesGp12ItemsList.html

@@ -16,18 +16,24 @@
 			<#form:form id="searchForm" model="${mesGp12Items}" action="${ctx}/mes/mesGp12Items/listData" method="post" class="form-inline hide"
 					data-page-no="${parameter.pageNo}" data-page-size="${parameter.pageSize}" data-order-by="${parameter.orderBy}">
 				<div class="form-group">
-					<label class="control-label">${text('责任工位')}:</label>
+					<label class="control-label">${text('一级分类')}:</label>
 					<div class="control-inline">
-						<#form:input path="resOprno" maxlength="20" class="form-control width-120"/>
+						<#form:input path="category" maxlength="100" class="form-control width-160"/>
 					</div>
 				</div>
 				<div class="form-group">
-					<label class="control-label">${text('问题描述')}:</label>
+					<label class="control-label">${text('二级分类')}:</label>
 					<div class="control-inline">
 						<#form:input path="description" maxlength="255" class="form-control width-260"/>
 					</div>
 				</div>
 				<div class="form-group">
+					<label class="control-label">${text('责任工位')}:</label>
+					<div class="control-inline">
+						<#form:input path="resOprno" maxlength="20" class="form-control width-120"/>
+					</div>
+				</div>
+				<div class="form-group">
 					<button type="submit" class="btn btn-primary btn-sm">${text('查询')}</button>
 					<button type="reset" class="btn btn-default btn-sm">${text('重置')}</button>
 				</div>
@@ -42,8 +48,10 @@
 $('#dataGrid').dataGrid({
 	searchForm: $("#searchForm"),
 	columnModel: [
-		{header:'${text("责任工位")}', name:'resOprno', index:'a.res_oprno', width:150, align:"center"},
-		{header:'${text("问题描述")}', name:'description', index:'a.description', width:400, align:"center"},
+		{header:'${text("一级分类")}', name:'category', index:'a.category', width:180, align:"center"},
+		{header:'${text("二级分类")}', name:'description', index:'a.description', width:320, align:"left"},
+		{header:'${text("责任工位")}', name:'resOprno', index:'a.res_oprno', width:120, align:"center"},
+		{header:'${text("排序号")}', name:'sort', index:'a.sort', width:80, align:"center"},
 		{header:'${text("创建时间")}', name:'createDate', index:'a.create_date', width:160, align:"center"},
 		{header:'${text("操作")}', name:'actions', width:120, formatter: function(val, obj, row, act){
 			var actions = [];

+ 52 - 30
src/main/resources/views/modules/mes/mesMaterialCheckRecordForm.html

@@ -63,12 +63,21 @@
 					<div class="col-xs-12">
 						<div class="form-group">
 							<label class="control-label col-sm-2" title="">
-								<span class="required ">*</span> ${text('合格数量')}:<i class="fa icon-question hide"></i></label>
+								<span class="required ">*</span> ${text('参与抽检数量')}:<i class="fa icon-question hide"></i></label>
 							<div class="col-sm-10">
 								<input type="number" class="form-control"  name="faNums" v-model="faNums"  >
 							</div>
 						</div>
 					</div>
+					<div class="col-xs-12" v-if="tightened">
+						<div class="form-group">
+							<div class="col-sm-12">
+								<p class="text-center" style="color:#d9534f;font-size:16px;margin:0;">
+									本物料本周已触发加严,本次需填写 {{sampleCount}} 个实测值
+								</p>
+							</div>
+						</div>
+					</div>
 					<div class="col-xs-12">
 						<div class="form-group">
 <!--							<label class="control-label col-sm-2" title="">-->
@@ -79,11 +88,7 @@
 										<th>项目</th>
 										<th>检查内容</th>
 										<th>图片</th>
-										<th>实测值1</th>
-										<th>实测值2</th>
-										<th>实测值3</th>
-										<th>实测值4</th>
-										<th>实测值5</th>
+										<th v-for="n in sampleCount" :key="'h'+n">实测值{{n}}</th>
 										<th>结果</th>
 									</tr>
 									<tr v-for="(item,index) in options" :key="index">
@@ -95,16 +100,14 @@
 											</span>
 										</td>
 										<template v-if="isSizeItem(item)">
-											<td><input type="text" v-model="item.a1" class="form-control"></td>
-											<td><input type="text" v-model="item.a2" class="form-control"></td>
-											<td><input type="text" v-model="item.a3" class="form-control"></td>
-											<td><input type="text" v-model="item.a4" class="form-control"></td>
-											<td><input type="text" v-model="item.a5" class="form-control"></td>
+											<td v-for="n in sampleCount" :key="'v'+n">
+												<input type="text" v-model="item['a'+n]" class="form-control">
+											</td>
 										</template>
-										<td v-else-if="isPerfItem(item)" colspan="5">
+										<td v-else-if="isPerfItem(item)" :colspan="sampleCount">
 											<div :id="'perfUpload_'+item.id"></div>
 										</td>
-										<td v-else colspan="5" class="text-center text-muted" style="vertical-align: middle;">人工判定</td>
+										<td v-else :colspan="sampleCount" class="text-center text-muted" style="vertical-align: middle;">人工判定</td>
 										<td>
 											<div v-if="isSizeItem(item) && item.type == 1">
 												<label v-if="item.result == 1"><input type="radio" v-model="item.result" value="1">合格</label>
@@ -218,6 +221,8 @@ var vm = new Vue({
 		nums:0,
 		faNums:0,
 		state:"1",
+		sampleCount:5,
+		tightened:false,
 		options:[]
 	},
 	computed: {
@@ -271,11 +276,9 @@ var vm = new Vue({
 		formatData(){
 			for (let o in this.options){
 				if (!this.isSizeItem(this.options[o])) {
-					this.options[o].a1 = '';
-					this.options[o].a2 = '';
-					this.options[o].a3 = '';
-					this.options[o].a4 = '';
-					this.options[o].a5 = '';
+					for (let n = 1; n <= 8; n++) {
+						this.options[o]['a'+n] = '';
+					}
 					continue;
 				}
 				if(this.options[o]['type'] == "1"){ // 系统判定
@@ -285,18 +288,16 @@ var vm = new Vue({
 					this.options[o].maxValue = fmaxval;
 
 					let tret = "1";
-					let a1 = parseFloat(this.options[o].a1?this.options[o].a1:'');
-					let ret1 = this.checkResult(fminval,fmaxval,a1);
-					let a2 = parseFloat(this.options[o].a2?this.options[o].a2:'');
-					let ret2 = this.checkResult(fminval,fmaxval,a2);
-					let a3 = parseFloat(this.options[o].a3?this.options[o].a3:'');
-					let ret3 = this.checkResult(fminval,fmaxval,a3);
-					let a4 = parseFloat(this.options[o].a4?this.options[o].a4:'');
-					let ret4 = this.checkResult(fminval,fmaxval,a4);
-					let a5 = parseFloat(this.options[o].a5?this.options[o].a5:'');
-					let ret5 = this.checkResult(fminval,fmaxval,a5);
-					if(ret1 == "NG" || ret2 == "NG" || ret3 == "NG" || ret4 == "NG" || ret5 == "NG"){
-						tret = "0"
+					for (let n = 1; n <= this.sampleCount; n++) {
+						let raw = this.options[o]['a'+n];
+						if (raw === null || raw === undefined || String(raw).trim() === '') {
+							continue;
+						}
+						let aval = parseFloat(raw);
+						let ret = this.checkResult(fminval,fmaxval,aval);
+						if(ret == "NG"){
+							tret = "0";
+						}
 					}
 					this.options[o].result = tret;
 				}else{ //人工判定
@@ -392,6 +393,8 @@ var vm = new Vue({
 					that.faDate = "";
 					that.nums = 0;
 					that.faNums = 0;
+					that.sampleCount = 5;
+					that.tightened = false;
 					that.options = [];
 					that.formatData();
 				} else {
@@ -403,10 +406,17 @@ var vm = new Vue({
 					that.faDate = res.data.result[1];          // "2026-06-01"
 					that.faNums = res.data.result[2];          // 发货数量,现在为"0"
 					that.nums = res.data.nums;                 // 实际报检数量
+					that.sampleCount = res.data.sampleCount || 5;
+					that.tightened = !!res.data.tightened;
 					that.options = res.data.mitList;
 
 					for (let o in that.options){
 						that.options[o]['result'] = "";
+						for (let n = 1; n <= 8; n++) {
+							if (that.options[o]['a'+n] === undefined) {
+								that.$set(that.options[o], 'a'+n, '');
+							}
+						}
 					}
 					that.initPerfFileUploads();
 					console.log(res);
@@ -443,6 +453,8 @@ var vm = new Vue({
 								 that.faDate="";
 								 that.nums=0;
 								 that.faNums=0;
+								 that.sampleCount=5;
+								 that.tightened=false;
 								 that.options=[];
 								 that.formatData();
 							 }else {
@@ -453,7 +465,17 @@ var vm = new Vue({
 								 that.faDate = res.data.result[1];        // 直接用字符串
 								 that.faNums = res.data.result[2];        // 发货数量
 								 that.nums = res.data.nums;               // 实际数量
+								 that.sampleCount = res.data.sampleCount || 5;
+								 that.tightened = !!res.data.tightened;
 								 that.options=res.data.mitList;
+								 for (let o in that.options){
+									 that.options[o]['result'] = "";
+									 for (let n = 1; n <= 8; n++) {
+										 if (that.options[o]['a'+n] === undefined) {
+											 that.$set(that.options[o], 'a'+n, '');
+										 }
+									 }
+								 }
 								 console.log(res);
 								 console.log("--");
 								 that.formatData();

+ 20 - 29
src/main/resources/views/modules/mes/mesMaterialCheckRecordForm2.html

@@ -72,11 +72,7 @@
 										<th>项目</th>
 										<th>检查内容</th>
 										<th>图片</th>
-										<th>实测值1</th>
-										<th>实测值2</th>
-										<th>实测值3</th>
-										<th>实测值4</th>
-										<th>实测值5</th>
+										<th v-for="n in sampleCount" :key="'h'+n">实测值{{n}}</th>
 										<th>结果</th>
 									</tr>
 									<tr v-for="(item,index) in options" :key="index">
@@ -88,16 +84,14 @@
 											</span>
 										</td>
 										<template v-if="isSizeItem(item)">
-											<td><input type="text" v-model="item.a1" class="form-control"></td>
-											<td><input type="text" v-model="item.a2" class="form-control"></td>
-											<td><input type="text" v-model="item.a3" class="form-control"></td>
-											<td><input type="text" v-model="item.a4" class="form-control"></td>
-											<td><input type="text" v-model="item.a5" class="form-control"></td>
+											<td v-for="n in sampleCount" :key="'v'+n">
+												<input type="text" v-model="item['a'+n]" class="form-control">
+											</td>
 										</template>
-										<td v-else-if="isPerfItem(item)" colspan="5">
+										<td v-else-if="isPerfItem(item)" :colspan="sampleCount">
 											<div :id="'perfUpload_'+item.id"></div>
 										</td>
-										<td v-else colspan="5" class="text-center text-muted" style="vertical-align: middle;">人工判定</td>
+										<td v-else :colspan="sampleCount" class="text-center text-muted" style="vertical-align: middle;">人工判定</td>
 										<td>
 											<div v-if="isSizeItem(item) && item.type == 1">
 												<label v-if="item.result == 1"><input type="radio" v-model="item.result" value="1">合格</label>
@@ -182,6 +176,7 @@ var vm = new Vue({
 		nums:${mesMaterialCheckRecord.nums},
 		faNums:${mesMaterialCheckRecord.faNums},
 		state:"1",
+		sampleCount: ${sampleCount!5},
 		options:[]
 	},
 	computed: {
@@ -235,11 +230,9 @@ var vm = new Vue({
 		formatData(){
 			for (let o in this.options){
 				if (!this.isSizeItem(this.options[o])) {
-					this.options[o].a1 = '';
-					this.options[o].a2 = '';
-					this.options[o].a3 = '';
-					this.options[o].a4 = '';
-					this.options[o].a5 = '';
+					for (let n = 1; n <= 8; n++) {
+						this.options[o]['a'+n] = '';
+					}
 					continue;
 				}
 				if(this.options[o]['type'] == "1"){ // 系统判定
@@ -249,18 +242,16 @@ var vm = new Vue({
 					this.options[o].maxValue = fmaxval;
 
 					let tret = "1";
-					let a1 = parseFloat(this.options[o].a1?this.options[o].a1:'');
-					let ret1 = this.checkResult(fminval,fmaxval,a1);
-					let a2 = parseFloat(this.options[o].a2?this.options[o].a2:'');
-					let ret2 = this.checkResult(fminval,fmaxval,a2);
-					let a3 = parseFloat(this.options[o].a3?this.options[o].a3:'');
-					let ret3 = this.checkResult(fminval,fmaxval,a3);
-					let a4 = parseFloat(this.options[o].a4?this.options[o].a4:'');
-					let ret4 = this.checkResult(fminval,fmaxval,a4);
-					let a5 = parseFloat(this.options[o].a5?this.options[o].a5:'');
-					let ret5 = this.checkResult(fminval,fmaxval,a5);
-					if(ret1 == "NG" || ret2 == "NG" || ret3 == "NG" || ret4 == "NG" || ret5 == "NG"){
-						tret = "0"
+					for (let n = 1; n <= this.sampleCount; n++) {
+						let raw = this.options[o]['a'+n];
+						if (raw === null || raw === undefined || String(raw).trim() === '') {
+							continue;
+						}
+						let aval = parseFloat(raw);
+						let ret = this.checkResult(fminval,fmaxval,aval);
+						if(ret == "NG"){
+							tret = "0";
+						}
 					}
 					this.options[o].result = tret;
 				}else{ //人工判定

+ 8 - 13
src/main/resources/views/modules/mes/mesMaterialCheckRecordFormEdit.html

@@ -54,7 +54,7 @@
                 <div class="col-xs-6">
                     <div class="form-group">
                         <label class="control-label col-sm-4" title="">
-                            <span class="required hide">*</span> ${text('合格数量')}:<i class="fa icon-question hide"></i></label>
+                            <span class="required hide">*</span> ${text('参与抽检数量')}:<i class="fa icon-question hide"></i></label>
                         <div class="col-sm-8">
                             <#form:input path="faNums" readonly="true" maxlength="9" class="form-control digits"/>
                         </div>
@@ -100,11 +100,7 @@
                                     <th>项目</th>
                                     <th>检查内容</th>
                                     <th>图片</th>
-                                    <th>实测值1</th>
-                                    <th>实测值2</th>
-                                    <th>实测值3</th>
-                                    <th>实测值4</th>
-                                    <th>实测值5</th>
+                                    <th v-for="n in sampleCount" :key="'h'+n">实测值{{n}}</th>
                                     <th>结果</th>
                                 </tr>
                                 <tr v-for="item in options" :key="item.id">
@@ -116,19 +112,17 @@
 											</span>
                                     </td>
                                     <template v-if="isSizeItem(item)">
-                                        <td><input type="text" v-model="item.a1" class="form-control"></td>
-                                        <td><input type="text" v-model="item.a2" class="form-control"></td>
-                                        <td><input type="text" v-model="item.a3" class="form-control"></td>
-                                        <td><input type="text" v-model="item.a4" class="form-control"></td>
-                                        <td><input type="text" v-model="item.a5" class="form-control"></td>
+                                        <td v-for="n in sampleCount" :key="'v'+n">
+                                            <input type="text" v-model="item['a'+n]" class="form-control">
+                                        </td>
                                     </template>
-                                    <td v-else-if="isPerfItem(item)" colspan="5">
+                                    <td v-else-if="isPerfItem(item)" :colspan="sampleCount">
                                         <div v-if="item.perfFiles && item.perfFiles.length" style="margin-bottom: 8px;">
                                             <a v-for="(f, idx) in item.perfFiles" :key="idx" :href="f.url" target="_blank" style="margin-right: 10px;">{{f.name}}</a>
                                         </div>
                                         <div :id="'perfUpload_'+item.checkId"></div>
                                     </td>
-                                    <td v-else colspan="5" class="text-center text-muted" style="vertical-align: middle;">人工判定</td>
+                                    <td v-else :colspan="sampleCount" class="text-center text-muted" style="vertical-align: middle;">人工判定</td>
                                     <td>
                                             <label>
                                                 <input type="radio"  v-model="item.result" value="1">
@@ -209,6 +203,7 @@
             faNums: "${mesMaterialCheckRecord.faNums}",
             recordId:"${mesMaterialCheckRecord.id}",
             state:"1",
+            sampleCount: ${sampleCount!5},
             options: []
         },
         mounted(){

+ 112 - 0
src/main/resources/views/modules/mes/mesMaterialCheckRecordFuCeForm.html

@@ -0,0 +1,112 @@
+<% layout('/layouts/default.html', {title: '来料复测', libs: ['validate','fileupload']}){ %>
+<div class="main-content">
+	<div class="box box-main">
+		<div class="box-header with-border">
+			<div class="box-title">
+				<i class="fa fa-refresh"></i> ${text("复测登记")}
+			</div>
+			<div class="box-tools pull-right">
+				<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
+			</div>
+		</div>
+		<#form:form id="inputForm" model="${mesMaterialCheckRecord}" action="${ctx}/mes/mesMaterialCheckRecord/fuCeSave" method="post" class="form-horizontal">
+			<div class="box-body">
+				<div class="form-unit">${text('基本信息')}</div>
+				<#form:hidden path="id"/>
+				<table class="table table-bordered">
+					<tr>
+						<th class="mpc-header" style="width:140px;">来料码</th>
+						<td>${mesMaterialCheckRecord.sn}</td>
+					</tr>
+					<tr>
+						<th class="mpc-header">物料名称</th>
+						<td>${mesMaterialCheckRecord.materialTitle}</td>
+					</tr>
+					<tr>
+						<th class="mpc-header">报检数量</th>
+						<td>${mesMaterialCheckRecord.nums}</td>
+					</tr>
+					<tr>
+						<th class="mpc-header">原检查结果</th>
+						<td>${@DictUtils.getDictLabel("mes_material_check_record_state",mesMaterialCheckRecord.state,"未知")}</td>
+					</tr>
+					<tr>
+						<th class="mpc-header">检查日期</th>
+						<td>${@DateUtils.formatDateTime(mesMaterialCheckRecord.createDate)}</td>
+					</tr>
+				</table>
+				<div class="form-unit">${text('复测信息')}</div>
+				<div class="row">
+					<div class="col-xs-12">
+						<div class="form-group">
+							<label class="control-label col-sm-2">
+								<span class="required">*</span> ${text('复测结果')}:</label>
+							<div class="col-sm-10">
+								<label class="radio-inline"><input type="radio" name="retestResult" value="1" class="required"/> ${text('合格')}</label>
+								<label class="radio-inline"><input type="radio" name="retestResult" value="2" class="required"/> ${text('不合格')}</label>
+							</div>
+						</div>
+					</div>
+				</div>
+				<div class="row">
+					<div class="col-xs-12">
+						<div class="form-group">
+							<label class="control-label col-sm-2">
+								<span class="required">*</span> ${text('复测文件')}:</label>
+							<div class="col-sm-10">
+								<#form:fileupload id="uploadFuCeFile" bizKey="${mesMaterialCheckRecord.id}" bizType="mesMaterialCheckRecord_fuCe_file"
+									uploadType="all" class="required" readonly="false" preview="true"/>
+								<div class="mt10" style="color:#999;">${text('上传复测依据文件,并选择复测结果后提交。选择合格将准许入库和使用。')}</div>
+							</div>
+						</div>
+					</div>
+				</div>
+			</div>
+			<div class="box-footer">
+				<div class="row">
+					<div class="col-sm-offset-2 col-sm-10">
+						<% if (hasPermi('mes:mesMaterialCheckRecord:edit')){ %>
+							<button type="submit" class="btn btn-sm btn-primary" id="btnSubmit"><i class="fa fa-check"></i> ${text('提交复测')}</button>&nbsp;
+						<% } %>
+						<button type="button" class="btn btn-sm btn-default" id="btnCancel" onclick="js.closeCurrentTabPage()"><i class="fa fa-reply-all"></i> ${text('关 闭')}</button>
+					</div>
+				</div>
+			</div>
+		</#form:form>
+	</div>
+</div>
+<% } %>
+<script>
+$("#inputForm").validate({
+	submitHandler: function(form){
+		var retestResult = $('input[name="retestResult"]:checked').val();
+		if (!retestResult) {
+			js.showMessage('${text("请选择复测结果")}', null, 'warning');
+			return false;
+		}
+		var hasFile = false;
+		$('input[name="mesMaterialCheckRecord_fuCe_file"]').each(function () {
+			if ($(this).val()) {
+				hasFile = true;
+			}
+		});
+		if (!hasFile && $('#uploadFuCeFile').closest('.form-group').find('.file-item, .files .template-download').length === 0) {
+			js.showMessage('${text("请先上传复测文件")}', null, 'warning');
+			return false;
+		}
+		var tip = retestResult === '1'
+			? '${text("确认复测结果为合格并准许入库?")}'
+			: '${text("确认复测结果为不合格?")}';
+		js.confirm(tip, function(){
+			js.ajaxSubmitForm($(form), function(data){
+				js.showMessage(data.message);
+				if(data.result == Global.TRUE){
+					js.closeCurrentTabPage(function(contentWindow){
+						contentWindow.page();
+					});
+				}
+			}, "json");
+		});
+	}
+});
+</script>

+ 36 - 9
src/main/resources/views/modules/mes/mesMaterialCheckRecordInfo.html

@@ -6,6 +6,7 @@
 				<i class="fa icon-note"></i> ${text("详情")}
 			</div>
 			<div class="box-tools pull-right">
+				<a href="#" class="btn btn-default" id="btnExport"><i class="glyphicon glyphicon-export"></i> ${text('导出检测数据')}</a>
 				<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
 			</div>
 		</div>
@@ -29,13 +30,33 @@
 					<td>${mesMaterialCheckRecord.nums}</td>
 				</tr>
 				<tr>
-					<th class="mpc-header">合格数量</th>
+					<th class="mpc-header">参与抽检数量</th>
 					<td>${mesMaterialCheckRecord.faNums}</td>
 				</tr>
 				<tr>
 					<th class="mpc-header">结果</th>
 					<td>${@DictUtils.getDictLabel("mes_material_check_record_state",mesMaterialCheckRecord.state,"未知")}</td>
 				</tr>
+				<% if(isNotEmpty(teCaiFiles)){ %>
+				<tr>
+					<th class="mpc-header">特采附件</th>
+					<td>
+						<% for(var teCaiFile in teCaiFiles){ %>
+						<a href="${teCaiFile.url}" target="_blank">${teCaiFile.name}</a>&nbsp;
+						<% } %>
+					</td>
+				</tr>
+				<% } %>
+				<% if(isNotEmpty(fuCeFiles)){ %>
+				<tr>
+					<th class="mpc-header">复测附件</th>
+					<td>
+						<% for(var fuCeFile in fuCeFiles){ %>
+						<a href="${fuCeFile.url}" target="_blank">${fuCeFile.name}</a>&nbsp;
+						<% } %>
+					</td>
+				</tr>
+				<% } %>
 				<tr>
 					<th class="mpc-header">检查日期</th>
 					<td>${@DateUtils.formatDateTime(mesMaterialCheckRecord.createDate) }</td>
@@ -51,11 +72,9 @@
 								<th>项目</th>
 								<th>检查内容</th>
 								<th>图片</th>
-								<th>实测值1</th>
-								<th>实测值2</th>
-								<th>实测值3</th>
-								<th>实测值4</th>
-								<th>实测值5</th>
+								<% for(var n=1; n<=sampleCount; n++){ %>
+								<th>实测值${n}</th>
+								<% } %>
 								<th>结果</th>
 							</tr>
 							<% for(var item in mitList){ %>
@@ -75,14 +94,17 @@
 								<td>${item.a3!}</td>
 								<td>${item.a4!}</td>
 								<td>${item.a5!}</td>
+								<% if(sampleCount >= 6){ %><td>${item.a6!}</td><% } %>
+								<% if(sampleCount >= 7){ %><td>${item.a7!}</td><% } %>
+								<% if(sampleCount >= 8){ %><td>${item.a8!}</td><% } %>
 								<% } else if(item.orgName == '性能'){ %>
-								<td colspan="5">
+								<td colspan="${sampleCount}">
 									<% for(var perfFile in item.perfFiles){ %>
 									<a href="${perfFile.url}" target="_blank">${perfFile.name}</a>&nbsp;
 									<% } %>
 								</td>
 								<% } else { %>
-								<td colspan="5" class="text-center text-muted">人工判定</td>
+								<td colspan="${sampleCount}" class="text-center text-muted">人工判定</td>
 								<% } %>
 								<td>
 									<% if (item.result == '1'){ %>
@@ -108,4 +130,9 @@
 		</div>
 	</div>
 </div>
-<% } %>
+<% } %>
+<script>
+$('#btnExport').click(function(){
+	window.location.href = '${ctx}/mes/mesMaterialCheckRecord/exportInfoData?id=${mesMaterialCheckRecord.id}';
+});
+</script>

+ 21 - 1
src/main/resources/views/modules/mes/mesMaterialCheckRecordList.html

@@ -7,6 +7,7 @@
 			</div>
 			<div class="box-tools pull-right">
 				<a href="#" class="btn btn-default" id="btnSearch" title="${text('查询')}"><i class="fa fa-filter"></i> ${text('查询')}</a>
+				<a href="#" class="btn btn-default" id="btnExport"><i class="glyphicon glyphicon-export"></i> ${text('导出检测数据')}</a>
 				<% if(hasPermi('mes:mesMaterialCheckRecord:edit')){ %>
 					<a href="${ctx}/mes/mesMaterialCheckRecord/form" class="btn btn-default btnTool" title="${text('来料检查')}"><i class="fa fa-plus"></i> ${text('来料检查')}</a>
 				<% } %>
@@ -57,6 +58,18 @@
 </div>
 <% } %>
 <script>
+$('#btnExport').click(function(){
+	var ids = $('#dataGrid').dataGrid('getSelectRows');
+	if (!ids || ids.length === 0) {
+		js.showMessage('${text("请先勾选要导出的来料检查记录")}');
+		return;
+	}
+	js.ajaxSubmit({
+		url:'${ctx}/mes/mesMaterialCheckRecord/exportData',
+		data:{ids: ids.join(',')},
+		downloadFile:true
+	});
+});
 // 初始化DataGrid对象
 $('#dataGrid').dataGrid({
 	searchForm: $("#searchForm"),
@@ -71,7 +84,7 @@ $('#dataGrid').dataGrid({
 			}},
 		{header:'${text("检查人")}', name:'createBy', index:'a.create_by', width:120, align:"center"},
 		{header:'${text("检查日期")}', name:'createDate', index:'a.create_date', width:150, align:"center"},
-		{header:'${text("操作")}', name:'actions', width:120, formatter: function(val, obj, row, act){
+		{header:'${text("操作")}', name:'actions', width:220, formatter: function(val, obj, row, act){
 			var actions = [];
 			//<% if(hasPermi('mes:mesMaterialCheckRecord:edit')){ %>
 			// 	actions.push('<a href="${ctx}/mes/mesMaterialCheckRecord/form2?id='+row.id+'" class="btnList" title="${text("编辑来料检查记录表")}"><i class="fa fa-pencil"></i></a>&nbsp;');
@@ -79,9 +92,16 @@ $('#dataGrid').dataGrid({
 			//<% } %>
 				actions.push('<a href="${ctx}/mes/mesMaterialCheckRecord/info?id='+row.id+'" class="btnList" title="${text("详情")}"><i class="fa fa-fw fa-eye"></i></a>&nbsp;');
 				actions.push('<a href="${ctx}/mes/mesMaterialCheckRecord/edit?id='+row.id+'" class="btnList" title="${text("编辑")}"><i class="fa fa-pencil"></i></a>&nbsp;');
+				<% if(hasPermi('mes:mesMaterialCheckRecord:edit')){ %>
+				if(row.state == '2'){
+					actions.push('<a href="${ctx}/mes/mesMaterialCheckRecord/teCai?id='+row.id+'" class="btnList" title="${text("特采")}"><i class="fa fa-check-circle-o"></i> ${text("特采")}</a>&nbsp;');
+					actions.push('<a href="${ctx}/mes/mesMaterialCheckRecord/fuCe?id='+row.id+'" class="btnList" title="${text("复测")}"><i class="fa fa-refresh"></i> ${text("复测")}</a>&nbsp;');
+				}
+				<% } %>
 				return actions.join('');
 		}}
 	],
+	showCheckbox: true,
 	frozen: true,
 	// 加载成功后执行事件
 	ajaxSuccess: function(data){

+ 92 - 0
src/main/resources/views/modules/mes/mesMaterialCheckRecordTeCaiForm.html

@@ -0,0 +1,92 @@
+<% layout('/layouts/default.html', {title: '来料特采', libs: ['validate','fileupload']}){ %>
+<div class="main-content">
+	<div class="box box-main">
+		<div class="box-header with-border">
+			<div class="box-title">
+				<i class="fa fa-check-circle-o"></i> ${text("特采申请")}
+			</div>
+			<div class="box-tools pull-right">
+				<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
+			</div>
+		</div>
+		<#form:form id="inputForm" model="${mesMaterialCheckRecord}" action="${ctx}/mes/mesMaterialCheckRecord/teCaiSave" method="post" class="form-horizontal">
+			<div class="box-body">
+				<div class="form-unit">${text('基本信息')}</div>
+				<#form:hidden path="id"/>
+				<table class="table table-bordered">
+					<tr>
+						<th class="mpc-header" style="width:140px;">来料码</th>
+						<td>${mesMaterialCheckRecord.sn}</td>
+					</tr>
+					<tr>
+						<th class="mpc-header">物料名称</th>
+						<td>${mesMaterialCheckRecord.materialTitle}</td>
+					</tr>
+					<tr>
+						<th class="mpc-header">报检数量</th>
+						<td>${mesMaterialCheckRecord.nums}</td>
+					</tr>
+					<tr>
+						<th class="mpc-header">检查结果</th>
+						<td>${@DictUtils.getDictLabel("mes_material_check_record_state",mesMaterialCheckRecord.state,"未知")}</td>
+					</tr>
+					<tr>
+						<th class="mpc-header">检查日期</th>
+						<td>${@DateUtils.formatDateTime(mesMaterialCheckRecord.createDate)}</td>
+					</tr>
+				</table>
+				<div class="form-unit">${text('特采附件')}</div>
+				<div class="row">
+					<div class="col-xs-12">
+						<div class="form-group">
+							<label class="control-label col-sm-2">
+								<span class="required">*</span> ${text('特采文件')}:</label>
+							<div class="col-sm-10">
+								<#form:fileupload id="uploadTeCaiFile" bizKey="${mesMaterialCheckRecord.id}" bizType="mesMaterialCheckRecord_teCai_file"
+									uploadType="all" class="required" readonly="false" preview="true"/>
+								<div class="mt10" style="color:#999;">${text('上传特采审批/依据文件后,可将不合格结果标注为特采,准许入库和使用。')}</div>
+							</div>
+						</div>
+					</div>
+				</div>
+			</div>
+			<div class="box-footer">
+				<div class="row">
+					<div class="col-sm-offset-2 col-sm-10">
+						<% if (hasPermi('mes:mesMaterialCheckRecord:edit')){ %>
+							<button type="submit" class="btn btn-sm btn-primary" id="btnSubmit"><i class="fa fa-check"></i> ${text('确认特采')}</button>&nbsp;
+						<% } %>
+						<button type="button" class="btn btn-sm btn-default" id="btnCancel" onclick="js.closeCurrentTabPage()"><i class="fa fa-reply-all"></i> ${text('关 闭')}</button>
+					</div>
+				</div>
+			</div>
+		</#form:form>
+	</div>
+</div>
+<% } %>
+<script>
+$("#inputForm").validate({
+	submitHandler: function(form){
+		var hasFile = false;
+		$('input[name="mesMaterialCheckRecord_teCai_file"]').each(function () {
+			if ($(this).val()) {
+				hasFile = true;
+			}
+		});
+		if (!hasFile && $('#uploadTeCaiFile').closest('.form-group').find('.file-item, .files .template-download').length === 0) {
+			js.showMessage('${text("请先上传特采文件")}', null, 'warning');
+			return false;
+		}
+		js.confirm('${text("确认将该不合格记录标注为特采并准许入库?")}', function(){
+			js.ajaxSubmitForm($(form), function(data){
+				js.showMessage(data.message);
+				if(data.result == Global.TRUE){
+					js.closeCurrentTabPage(function(contentWindow){
+						contentWindow.page();
+					});
+				}
+			}, "json");
+		});
+	}
+});
+</script>

+ 11 - 32
src/main/resources/views/modules/mes/mesProductGp12Form.html

@@ -1,4 +1,4 @@
-<% layout('/layouts/default.html', {title: '工件拍照记录详情', libs: ['validate','dataGrid','fileupload']}){ %>
+<% layout('/layouts/default.html', {title: '不良原因记录详情', libs: ['validate']}){ %>
 
 <style>
 	table{
@@ -15,7 +15,7 @@
 	<div class="box box-main">
 		<div class="box-header with-border">
 			<div class="box-title">
-				<i class="fa icon-note"></i> 工件拍照记录详情
+				<i class="fa icon-note"></i> ${text('不良原因记录详情')}
 			</div>
 			<div class="box-tools pull-right">
 				<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
@@ -26,33 +26,29 @@
 			<div class="form-unit">${text('基本信息')}</div>
 			<table class="table table-bordered">
 				<tr>
-					<th class="mpc-header">工件码</th>
+					<th class="mpc-header">${text('工件码')}</th>
 					<td>${mesProductGp12.sn}</td>
 				</tr>
 				<tr>
-					<th class="mpc-header">工位号</th>
+					<th class="mpc-header">${text('工位号')}</th>
 					<td>${mesProductGp12.oprno}</td>
 				</tr>
 				<tr>
-					<th class="mpc-header">产线编号</th>
+					<th class="mpc-header">${text('产线编号')}</th>
 					<td>${mesProductGp12.lineSn}</td>
 				</tr>
 				<tr>
-					<th class="mpc-header">创建人</th>
-					<td>${mesProductGp12.createBy}</td>
+					<th class="mpc-header">${text('不良原因')}</th>
+					<td style="white-space: pre-wrap;">${mesProductGp12.remark}</td>
 				</tr>
 				<tr>
-					<th class="mpc-header">创建时间</th>
-					<td>${mesProductGp12.createDate,dateFormat='yyyy-MM-dd HH:mm'}</td>
+					<th class="mpc-header">${text('创建人')}</th>
+					<td>${mesProductGp12.createBy}</td>
 				</tr>
 				<tr>
-					<th class="mpc-header">图片</th>
-					<td>
-						<#form:fileupload id="uploadImage" bizKey="${mesProductGp12.id}" bizType="mesProductGp12_image"
-						uploadType="image" class="" readonly="true" preview="true"/>
-					</td>
+					<th class="mpc-header">${text('创建时间')}</th>
+					<td>${mesProductGp12.createDate,dateFormat='yyyy-MM-dd HH:mm'}</td>
 				</tr>
-
 			</table>
 		</div>
 		<div class="box-footer">
@@ -66,20 +62,3 @@
 </div>
 </div>
 <% } %>
-<script>
-
-</script>
-<script>
-	$("#inputForm").validate({
-		submitHandler: function(form){
-			js.ajaxSubmitForm($(form), function(data){
-				js.showMessage(data.message);
-				if(data.result == Global.TRUE){
-					js.closeCurrentTabPage(function(contentWindow){
-						contentWindow.page();
-					});
-				}
-			}, "json");
-		}
-	});
-</script>

+ 2 - 7
src/main/resources/views/modules/mes/mesProductGp12List.html

@@ -77,17 +77,12 @@ $('#dataGrid').dataGrid({
 		{header:'${text("工件码")}', name:'sn', index:'a.sn', width:300, align:"center", frozen:true},
 		{header:'${text("工位号")}', name:'oprno', index:'a.oprno', width:150, align:"center"},
 		{header:'${text("产线编号")}', name:'lineSn', index:'a.line_sn', width:150, align:"center"},
-		{header:'${text("备注")}', name:'remark', index:'a.remark', width:150, align:"center"},
+		{header:'${text("不良原因")}', name:'remark', index:'a.remark', width:280, align:"left"},
 		{header:'${text("创建人")}', name:'createBy', index:'a.create_by', width:150, align:"center"},
 		{header:'${text("创建时间")}', name:'createDate', index:'a.create_date', width:150, align:"center"},
 		{header:'${text("操作")}', name:'actions', width:120, formatter: function(val, obj, row, act){
 			var actions = [];
-			//<% if(hasPermi('mes:mesProductGp12:edit')){ %>
-			// 	actions.push('<a href="${ctx}/mes/mesProductGp12/form?id='+row.id+'" class="btnList" title="${text("编辑工件拍照记录")}"><i class="fa fa-pencil"></i></a>&nbsp;');
-			// 	actions.push('<a href="${ctx}/mes/mesProductGp12/delete?id='+row.id+'" class="btnList" title="${text("删除工件拍照记录")}" data-confirm="${text("确认要删除该工件拍照记录吗?")}"><i class="fa fa-trash-o"></i></a>&nbsp;');
-					actions.push('<a href="${ctx}/mes/mesProductGp12/form?id='+row.id+'" class="btnList" title="${text("详情")}"><i class="fa fa-fw fa-eye"></i></a>&nbsp;');
-
-				//<% } %>
+			actions.push('<a href="${ctx}/mes/mesProductGp12/form?id='+row.id+'" class="btnList" title="${text("不良原因详情")}"><i class="fa fa-fw fa-eye"></i></a>&nbsp;');
 			return actions.join('');
 		}}
 	],