Przeglądaj źródła

it化要求优化

sunxianglong 1 dzień temu
rodzic
commit
c1027cdc80
25 zmienionych plików z 1589 dodań i 134 usunięć
  1. 25 0
      db/mes_process_qty_config.sql
  2. 3 0
      db/mes_product_screen_week_plan.sql
  3. 9 0
      db/mes_wuliao_cz_tare_split.sql
  4. 15 0
      src/main/java/com/jeesite/modules/mes/dao/MesMaterialJycheckDao.java
  5. 12 0
      src/main/java/com/jeesite/modules/mes/dao/MesProcessQtyConfigDao.java
  6. 5 0
      src/main/java/com/jeesite/modules/mes/dao/MesProductRecordDao.java
  7. 163 0
      src/main/java/com/jeesite/modules/mes/entity/MesMaterialJycheck.java
  8. 72 0
      src/main/java/com/jeesite/modules/mes/entity/MesProcessQtyConfig.java
  9. 81 0
      src/main/java/com/jeesite/modules/mes/service/MesMaterialJycheckService.java
  10. 101 0
      src/main/java/com/jeesite/modules/mes/service/MesProcessQtyConfigService.java
  11. 11 0
      src/main/java/com/jeesite/modules/mes/service/MesProductRecordService.java
  12. 22 0
      src/main/java/com/jeesite/modules/mes/web/MesMaterialCheckRecordController.java
  13. 106 0
      src/main/java/com/jeesite/modules/mes/web/MesMaterialJycheckController.java
  14. 78 0
      src/main/java/com/jeesite/modules/mes/web/MesProcessQtyConfigController.java
  15. 26 10
      src/main/java/com/jeesite/modules/mes/web/MesProductRecordController.java
  16. 2 0
      src/main/java/com/jeesite/modules/utils/ErrorMsg.java
  17. 15 0
      src/main/resources/mappings/modules/mes/MesMaterialJycheckDao.xml
  18. 8 0
      src/main/resources/mappings/modules/mes/MesProductRecordDao.xml
  19. 224 123
      src/main/resources/views/modules/mes/mesMaterialCheckRecordForm.html
  20. 151 0
      src/main/resources/views/modules/mes/mesMaterialJycheckForm.html
  21. 83 0
      src/main/resources/views/modules/mes/mesMaterialJycheckList.html
  22. 2 1
      src/main/resources/views/modules/mes/mesMaterialList.html
  23. 82 0
      src/main/resources/views/modules/mes/mesProcessQtyConfigForm.html
  24. 66 0
      src/main/resources/views/modules/mes/mesProcessQtyConfigList.html
  25. 227 0
      src/test/java/com/jeesite/test/FrameworkQmQualityCheckTest.java

+ 25 - 0
db/mes_process_qty_config.sql

@@ -0,0 +1,25 @@
+-- 工位加工件数配置(质量校验:启用时按 OK+NG 件数对加工件数求余,余数为 0 则不通过)
+CREATE TABLE IF NOT EXISTS `mes_process_qty_config` (
+  `id` varchar(64) NOT NULL COMMENT '主键',
+  `oprno` varchar(20) DEFAULT NULL COMMENT '工位号',
+  `process_qty` int DEFAULT NULL COMMENT '加工件数',
+  `enable_flag` char(1) DEFAULT '1' COMMENT '是否启用:1是 0否',
+  `remarks` varchar(500) DEFAULT NULL COMMENT '备注',
+  `status` char(1) DEFAULT '0' COMMENT '状态(0正常 1删除 2停用)',
+  `create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
+  `create_date` datetime DEFAULT NULL COMMENT '创建时间',
+  `update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
+  `update_date` datetime DEFAULT NULL COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  KEY `idx_process_qty_oprno` (`oprno`),
+  KEY `idx_process_qty_enable` (`enable_flag`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='工位加工件数配置';
+
+-- 示例:
+-- INSERT INTO mes_process_qty_config(id, oprno, process_qty, enable_flag, status, create_date, update_date)
+-- VALUES (REPLACE(UUID(),'-',''), 'OP360', 10, '1', '0', NOW(), NOW());
+
+-- 菜单:
+-- 名称:工位加工件数配置
+-- 链接:/mes/mesProcessQtyConfig/list
+-- 权限:mes:mesProcessQtyConfig:view 、 mes:mesProcessQtyConfig:edit

+ 3 - 0
db/mes_product_screen_week_plan.sql

@@ -0,0 +1,3 @@
+-- 大屏展示配置:本周计划
+ALTER TABLE `mes_product_screen`
+  ADD COLUMN `week_plan` varchar(255) DEFAULT NULL COMMENT '本周计划' AFTER `plan_output`;

+ 9 - 0
db/mes_wuliao_cz_tare_split.sql

@@ -0,0 +1,9 @@
+-- 称重表去皮字段拆分:原铝锭去皮 / 回炉料去皮
+-- 执行前请备份 mes_wuliao_cz
+
+ALTER TABLE mes_wuliao_cz ADD COLUMN y_qp_zl VARCHAR(64) NULL COMMENT '原铝锭去皮' AFTER h_zl;
+ALTER TABLE mes_wuliao_cz ADD COLUMN h_qp_zl VARCHAR(64) NULL COMMENT '回炉料去皮' AFTER y_qp_zl;
+
+-- 若存在旧字段 qp_zl,可将历史数据迁移到回炉料去皮(按需执行)
+-- UPDATE mes_wuliao_cz SET h_qp_zl = qp_zl WHERE (h_qp_zl IS NULL OR h_qp_zl = '') AND qp_zl IS NOT NULL;
+-- ALTER TABLE mes_wuliao_cz DROP COLUMN qp_zl;

+ 15 - 0
src/main/java/com/jeesite/modules/mes/dao/MesMaterialJycheckDao.java

@@ -0,0 +1,15 @@
+package com.jeesite.modules.mes.dao;
+
+import com.jeesite.common.dao.CrudDao;
+import com.jeesite.common.mybatis.annotation.MyBatisDao;
+import com.jeesite.modules.mes.entity.MesMaterialJycheck;
+
+/**
+ * 来料检查内容表DAO接口
+ * @author sxl
+ * @version 2026-11-01
+ */
+@MyBatisDao
+public interface MesMaterialJycheckDao extends CrudDao<MesMaterialJycheck> {
+	
+}

+ 12 - 0
src/main/java/com/jeesite/modules/mes/dao/MesProcessQtyConfigDao.java

@@ -0,0 +1,12 @@
+package com.jeesite.modules.mes.dao;
+
+import com.jeesite.common.dao.CrudDao;
+import com.jeesite.common.mybatis.annotation.MyBatisDao;
+import com.jeesite.modules.mes.entity.MesProcessQtyConfig;
+
+/**
+ * 工位加工件数配置DAO接口
+ */
+@MyBatisDao
+public interface MesProcessQtyConfigDao extends CrudDao<MesProcessQtyConfig> {
+}

+ 5 - 0
src/main/java/com/jeesite/modules/mes/dao/MesProductRecordDao.java

@@ -65,4 +65,9 @@ public interface MesProductRecordDao extends CrudDao<MesProductRecord> {
             @Param("startTime") Date startTime,
             @Param("endTime") Date endTime);
 
+    /**
+     * 统计指定工位前缀全部历史 craft=100000 且 content 为 OK 或 NG 的条数(不限产线、不限时间)
+     */
+    Long countOkNgByOprnoPrefix(@Param("oprnoPrefix") String oprnoPrefix);
+
 }

+ 163 - 0
src/main/java/com/jeesite/modules/mes/entity/MesMaterialJycheck.java

@@ -0,0 +1,163 @@
+package com.jeesite.modules.mes.entity;
+
+import javax.validation.constraints.Size;
+
+import com.jeesite.common.entity.DataEntity;
+import com.jeesite.common.mybatis.annotation.Column;
+import com.jeesite.common.mybatis.annotation.Table;
+import com.jeesite.common.mybatis.mapper.query.QueryType;
+
+/**
+ * 来料检查内容表Entity
+ * @author sxl
+ * @version 2026-11-01
+ */
+@Table(name="mes_material_jycheck", alias="a", label="来料检查内容表信息", columns={
+		@Column(name="id", attrName="id", label="id", isPK=true),
+		@Column(name="org", attrName="org", label="项目"),
+		@Column(name="content", attrName="content", label="检查内容"),
+		@Column(name="status", attrName="status", label="状态", isUpdate=false),
+		@Column(name="create_by", attrName="createBy", label="create_by", isUpdate=false, isQuery=false),
+		@Column(name="create_date", attrName="createDate", label="create_date", isUpdate=false, isQuery=false, isUpdateForce=true),
+		@Column(name="update_by", attrName="updateBy", label="update_by", isQuery=false),
+		@Column(name="update_date", attrName="updateDate", label="update_date", isQuery=false, isUpdateForce=true),
+		@Column(name="material_id", attrName="materialId", label="物料id"),
+		@Column(name="type", attrName="type", label="1=系统 2=人工"),
+		@Column(name="min_value", attrName="minValue", label="min_value", isUpdateForce=true),
+		@Column(name="max_value", attrName="maxValue", label="max_value", isUpdateForce=true),
+		@Column(name="conclusion_content", attrName="conclusionContent", label="结论内容"),
+		@Column(name="check_tool", attrName="checkTool", label="检查工具"),
+		@Column(name="check_frequency", attrName="checkFrequency", label="检查频率"),
+		@Column(name="serial_no", attrName="serialNo", label="编号"),
+		@Column(name="measure_type", attrName="measureType", label="测量类型", comment="测量类型(1=输入框,2=下拉框)"),
+	}, orderBy="a.update_date DESC"
+)
+public class MesMaterialJycheck extends DataEntity<MesMaterialJycheck> {
+	
+	private static final long serialVersionUID = 1L;
+	private String org;		// 项目
+	private String content;		// 检查内容
+	private String materialId;		// 物料id
+	private String type;		// 1=系统 2=人工
+	private Double minValue;		// min_value
+	private Double maxValue;		// max_value
+	private String conclusionContent;		// 结论内容
+	private String checkTool;		// 检查工具
+	private String checkFrequency;		// 检查频率
+	private String serialNo;		// 编号
+	private String measureType;		// 测量类型(1=输入框,2=下拉框)
+	private String orgName;		// 项目名称(非表字段)
+
+	public MesMaterialJycheck() {
+		this(null);
+	}
+	
+	public MesMaterialJycheck(String id){
+		super(id);
+	}
+	
+	@Size(min=0, max=5, message="项目长度不能超过 5 个字符")
+	public String getOrg() {
+		return org;
+	}
+
+	public void setOrg(String org) {
+		this.org = org;
+	}
+	
+	public String getContent() {
+		return content;
+	}
+
+	public void setContent(String content) {
+		this.content = content;
+	}
+	
+	@Size(min=0, max=64, message="物料id长度不能超过 64 个字符")
+	public String getMaterialId() {
+		return materialId;
+	}
+
+	public void setMaterialId(String materialId) {
+		this.materialId = materialId;
+	}
+	
+	@Size(min=0, max=1, message="1=系统 2=人工长度不能超过 1 个字符")
+	public String getType() {
+		return type;
+	}
+
+	public void setType(String type) {
+		this.type = type;
+	}
+	
+	public Double getMinValue() {
+		return minValue;
+	}
+
+	public void setMinValue(Double minValue) {
+		this.minValue = minValue;
+	}
+	
+	public Double getMaxValue() {
+		return maxValue;
+	}
+
+	public void setMaxValue(Double maxValue) {
+		this.maxValue = maxValue;
+	}
+	
+	@Size(min=0, max=500, message="结论内容长度不能超过 500 个字符")
+	public String getConclusionContent() {
+		return conclusionContent;
+	}
+
+	public void setConclusionContent(String conclusionContent) {
+		this.conclusionContent = conclusionContent;
+	}
+	
+	@Size(min=0, max=255, message="检查工具长度不能超过 255 个字符")
+	public String getCheckTool() {
+		return checkTool;
+	}
+
+	public void setCheckTool(String checkTool) {
+		this.checkTool = checkTool;
+	}
+	
+	@Size(min=0, max=255, message="检查频率长度不能超过 255 个字符")
+	public String getCheckFrequency() {
+		return checkFrequency;
+	}
+
+	public void setCheckFrequency(String checkFrequency) {
+		this.checkFrequency = checkFrequency;
+	}
+	
+	@Size(min=0, max=64, message="编号长度不能超过 64 个字符")
+	public String getSerialNo() {
+		return serialNo;
+	}
+
+	public void setSerialNo(String serialNo) {
+		this.serialNo = serialNo;
+	}
+	
+	@Size(min=0, max=1, message="测量类型长度不能超过 1 个字符")
+	public String getMeasureType() {
+		return measureType;
+	}
+
+	public void setMeasureType(String measureType) {
+		this.measureType = measureType;
+	}
+
+	public String getOrgName() {
+		return orgName;
+	}
+
+	public void setOrgName(String orgName) {
+		this.orgName = orgName;
+	}
+
+}

+ 72 - 0
src/main/java/com/jeesite/modules/mes/entity/MesProcessQtyConfig.java

@@ -0,0 +1,72 @@
+package com.jeesite.modules.mes.entity;
+
+import javax.validation.constraints.Size;
+
+import com.jeesite.common.entity.DataEntity;
+import com.jeesite.common.mybatis.annotation.Column;
+import com.jeesite.common.mybatis.annotation.Table;
+import com.jeesite.common.mybatis.mapper.query.QueryType;
+
+/**
+ * 工位加工件数配置Entity
+ * <p>
+ * 启用后:当前班次该工位 OK+NG 件数对加工件数求余,余数为 0 时质量校验不通过。
+ */
+@Table(name="mes_process_qty_config", alias="a", label="工位加工件数配置", columns={
+		@Column(name="id", attrName="id", label="id", isPK=true),
+		@Column(name="oprno", attrName="oprno", label="工位号", queryType=QueryType.LIKE),
+		@Column(name="process_qty", attrName="processQty", label="加工件数"),
+		@Column(name="enable_flag", attrName="enableFlag", label="是否启用"),
+		@Column(name="remarks", attrName="remarks", label="备注", queryType=QueryType.LIKE),
+		@Column(name="status", attrName="status", label="状态", isUpdate=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.oprno ASC"
+)
+public class MesProcessQtyConfig extends DataEntity<MesProcessQtyConfig> {
+
+	private static final long serialVersionUID = 1L;
+
+	public static final String ENABLE_YES = "1";
+	public static final String ENABLE_NO = "0";
+
+	private String oprno;
+	private Integer processQty;
+	private String enableFlag;
+
+	public MesProcessQtyConfig() {
+		this(null);
+	}
+
+	public MesProcessQtyConfig(String id) {
+		super(id);
+	}
+
+	@Size(min=0, max=20, message="工位号长度不能超过 20 个字符")
+	public String getOprno() {
+		return oprno;
+	}
+
+	public void setOprno(String oprno) {
+		this.oprno = oprno;
+	}
+
+	public Integer getProcessQty() {
+		return processQty;
+	}
+
+	public void setProcessQty(Integer processQty) {
+		this.processQty = processQty;
+	}
+
+	@Size(min=0, max=1, message="是否启用长度不能超过 1 个字符")
+	public String getEnableFlag() {
+		return enableFlag;
+	}
+
+	public void setEnableFlag(String enableFlag) {
+		this.enableFlag = enableFlag;
+	}
+}

+ 81 - 0
src/main/java/com/jeesite/modules/mes/service/MesMaterialJycheckService.java

@@ -0,0 +1,81 @@
+package com.jeesite.modules.mes.service;
+
+import java.util.List;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.jeesite.common.entity.Page;
+import com.jeesite.common.service.CrudService;
+import com.jeesite.modules.mes.entity.MesMaterialJycheck;
+import com.jeesite.modules.mes.dao.MesMaterialJycheckDao;
+
+/**
+ * 来料检查内容表Service
+ * @author sxl
+ * @version 2026-11-01
+ */
+@Service
+public class MesMaterialJycheckService extends CrudService<MesMaterialJycheckDao, MesMaterialJycheck> {
+	
+	/**
+	 * 获取单条数据
+	 * @param mesMaterialJycheck
+	 * @return
+	 */
+	@Override
+	public MesMaterialJycheck get(MesMaterialJycheck mesMaterialJycheck) {
+		return super.get(mesMaterialJycheck);
+	}
+	
+	/**
+	 * 查询分页数据
+	 * @param mesMaterialJycheck 查询条件
+	 * @param mesMaterialJycheck.page 分页对象
+	 * @return
+	 */
+	@Override
+	public Page<MesMaterialJycheck> findPage(MesMaterialJycheck mesMaterialJycheck) {
+		return super.findPage(mesMaterialJycheck);
+	}
+	
+	/**
+	 * 查询列表数据
+	 * @param mesMaterialJycheck
+	 * @return
+	 */
+	@Override
+	public List<MesMaterialJycheck> findList(MesMaterialJycheck mesMaterialJycheck) {
+		return super.findList(mesMaterialJycheck);
+	}
+	
+	/**
+	 * 保存数据(插入或更新)
+	 * @param mesMaterialJycheck
+	 */
+	@Override
+	@Transactional
+	public void save(MesMaterialJycheck mesMaterialJycheck) {
+		super.save(mesMaterialJycheck);
+	}
+	
+	/**
+	 * 更新状态
+	 * @param mesMaterialJycheck
+	 */
+	@Override
+	@Transactional
+	public void updateStatus(MesMaterialJycheck mesMaterialJycheck) {
+		super.updateStatus(mesMaterialJycheck);
+	}
+	
+	/**
+	 * 删除数据
+	 * @param mesMaterialJycheck
+	 */
+	@Override
+	@Transactional
+	public void delete(MesMaterialJycheck mesMaterialJycheck) {
+		super.delete(mesMaterialJycheck);
+	}
+	
+}

+ 101 - 0
src/main/java/com/jeesite/modules/mes/service/MesProcessQtyConfigService.java

@@ -0,0 +1,101 @@
+package com.jeesite.modules.mes.service;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+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.mybatis.mapper.query.QueryType;
+import com.jeesite.common.service.CrudService;
+import com.jeesite.modules.mes.dao.MesProcessQtyConfigDao;
+import com.jeesite.modules.mes.entity.MesProcessQtyConfig;
+import com.jeesite.modules.mes.util.CommonUitl;
+
+/**
+ * 工位加工件数配置Service
+ */
+@Service
+public class MesProcessQtyConfigService extends CrudService<MesProcessQtyConfigDao, MesProcessQtyConfig> {
+
+	@Autowired
+	private MesProductRecordService mesProductRecordService;
+
+	@Override
+	public MesProcessQtyConfig get(MesProcessQtyConfig mesProcessQtyConfig) {
+		return super.get(mesProcessQtyConfig);
+	}
+
+	@Override
+	public Page<MesProcessQtyConfig> findPage(MesProcessQtyConfig mesProcessQtyConfig) {
+		return super.findPage(mesProcessQtyConfig);
+	}
+
+	@Override
+	public List<MesProcessQtyConfig> findList(MesProcessQtyConfig mesProcessQtyConfig) {
+		return super.findList(mesProcessQtyConfig);
+	}
+
+	@Override
+	@Transactional
+	public void save(MesProcessQtyConfig mesProcessQtyConfig) {
+		if (StringUtils.isBlank(mesProcessQtyConfig.getEnableFlag())) {
+			mesProcessQtyConfig.setEnableFlag(MesProcessQtyConfig.ENABLE_YES);
+		}
+		if (StringUtils.isNotBlank(mesProcessQtyConfig.getOprno())) {
+			mesProcessQtyConfig.setOprno(CommonUitl.formatOprno(mesProcessQtyConfig.getOprno().trim()));
+		}
+		super.save(mesProcessQtyConfig);
+	}
+
+	@Override
+	@Transactional
+	public void delete(MesProcessQtyConfig mesProcessQtyConfig) {
+		super.delete(mesProcessQtyConfig);
+	}
+
+	public MesProcessQtyConfig findEnabledByOprno(String oprno) {
+		if (StringUtils.isBlank(oprno)) {
+			return null;
+		}
+		Set<String> oprnos = new HashSet<>();
+		oprnos.add(oprno.trim());
+		String formatted = CommonUitl.formatOprno(oprno.trim());
+		if (StringUtils.isNotBlank(formatted)) {
+			oprnos.add(formatted);
+		}
+		MesProcessQtyConfig query = new MesProcessQtyConfig();
+		query.setEnableFlag(MesProcessQtyConfig.ENABLE_YES);
+		query.setStatus(MesProcessQtyConfig.STATUS_NORMAL);
+		query.getSqlMap().getWhere().and("oprno", QueryType.IN, oprnos.toArray());
+		List<MesProcessQtyConfig> list = findList(query);
+		if (list == null || list.isEmpty()) {
+			logger.info("加工件数配置未命中 oprno={} candidates={}", oprno, oprnos);
+			return null;
+		}
+		return list.get(0);
+	}
+
+	/**
+	 * 质量校验:配置启用时,按该工位全部历史 OK+NG 件数对加工件数求余;
+	 * 余数为 0 则不通过。统计全部数据(不限班次、不限产线)。
+	 */
+	public boolean checkProcessQtyPass(String oprno, String lineSn) {
+		MesProcessQtyConfig config = findEnabledByOprno(oprno);
+		if (config == null || config.getProcessQty() == null || config.getProcessQty() <= 0) {
+			return true;
+		}
+
+		int processQty = config.getProcessQty();
+		String fOprno = CommonUitl.formatOprno(oprno);
+		int total = mesProductRecordService.countOkNgByOprnoPrefix(fOprno);
+		boolean pass = total % processQty != 0;
+		logger.info("加工件数校验(全部数据) oprno={} okNgTotal={} processQty={} pass={}",
+				fOprno, total, processQty, pass);
+		return pass;
+	}
+}

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

@@ -1654,6 +1654,17 @@ public class MesProductRecordService extends CrudService<MesProductRecordDao, Me
 	}
 
 	/**
+	 * 指定工位前缀全部历史 OK+NG 件数(craft=100000,不限产线、不限时间)
+	 */
+	public int countOkNgByOprnoPrefix(String oprnoPrefix) {
+		if (StringUtils.isBlank(oprnoPrefix)) {
+			return 0;
+		}
+		Long c = mesProductRecordDao.countOkNgByOprnoPrefix(CommonUitl.formatOprno(oprnoPrefix.trim()));
+		return c == null ? 0 : c.intValue();
+	}
+
+	/**
 	 * 本周产量:周一 00:00 至下周一 00:00,按大屏类型取对应下线工位产能合计。
 	 * MP铸造 OP100 / JJ机加 OP160 / ZP装配·ZS总屏 OP400;产线默认 XT。
 	 */

+ 22 - 0
src/main/java/com/jeesite/modules/mes/web/MesMaterialCheckRecordController.java

@@ -71,6 +71,9 @@ public class MesMaterialCheckRecordController extends BaseController {
 	private MesMaterialCheckService mesMaterialCheckService;
 
 	@Autowired
+	private MesMaterialJycheckService mesMaterialJycheckService;
+
+	@Autowired
 	private MesMaterialReportService mesMaterialReportService;
 
 	/**
@@ -351,6 +354,25 @@ public class MesMaterialCheckRecordController extends BaseController {
 		}
 		map.put("inspectLevel", inspectLevel);
 
+		// 加严检验不合格时,返回该物料的加严检查项
+		List<MesMaterialJycheck> jyMitList = new ArrayList<>();
+		if ("不合格".equals(inspectLevel)) {
+			MesMaterialJycheck jyQuery = new MesMaterialJycheck();
+			jyQuery.setMaterialId(ma.getId());
+			jyQuery.setStatus(MesMaterialJycheck.STATUS_NORMAL);
+			jyMitList = mesMaterialJycheckService.findList(jyQuery);
+			for (MesMaterialJycheck item : jyMitList) {
+				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());
+				}
+			}
+		}
+		map.put("jyMitList", jyMitList);
+
 		resp.setData(map);
 		resp.setResult(Global.TRUE);
 		return resp;

+ 106 - 0
src/main/java/com/jeesite/modules/mes/web/MesMaterialJycheckController.java

@@ -0,0 +1,106 @@
+package com.jeesite.modules.mes.web;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.jeesite.modules.mes.entity.MesMaterial;
+import com.jeesite.modules.mes.service.MesMaterialService;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.jeesite.common.config.Global;
+import com.jeesite.common.entity.Page;
+import com.jeesite.common.web.BaseController;
+import com.jeesite.modules.mes.entity.MesMaterialJycheck;
+import com.jeesite.modules.mes.service.MesMaterialJycheckService;
+
+/**
+ * 来料加严检查内容表Controller
+ * @author sxl
+ * @version 2026-11-01
+ */
+@Controller
+@RequestMapping(value = "${adminPath}/mes/mesMaterialJycheck")
+public class MesMaterialJycheckController extends BaseController {
+
+	@Autowired
+	private MesMaterialJycheckService mesMaterialJycheckService;
+	@Autowired
+	private MesMaterialService mesMaterialService;
+
+	/**
+	 * 获取数据
+	 */
+	@ModelAttribute
+	public MesMaterialJycheck get(String id, boolean isNewRecord) {
+		return mesMaterialJycheckService.get(id, isNewRecord);
+	}
+
+	/**
+	 * 查询列表
+	 */
+	@RequiresPermissions("mes:mesMaterialJycheck:view")
+	@RequestMapping(value = {"list", ""})
+	public String list(MesMaterialJycheck mesMaterialJycheck, Model model) {
+		model.addAttribute("mesMaterialJycheck", mesMaterialJycheck);
+		return "modules/mes/mesMaterialJycheckList";
+	}
+
+	/**
+	 * 查询列表数据
+	 */
+	@RequiresPermissions("mes:mesMaterialJycheck:view")
+	@RequestMapping(value = "listData")
+	@ResponseBody
+	public Page<MesMaterialJycheck> listData(MesMaterialJycheck mesMaterialJycheck, HttpServletRequest request, HttpServletResponse response) {
+		mesMaterialJycheck.setPage(new Page<>(request, response));
+		Page<MesMaterialJycheck> page = mesMaterialJycheckService.findPage(mesMaterialJycheck);
+		return page;
+	}
+
+	/**
+	 * 查看编辑表单
+	 */
+	@RequiresPermissions("mes:mesMaterialJycheck:view")
+	@RequestMapping(value = "form")
+	public String form(MesMaterialJycheck mesMaterialJycheck, Model model) {
+		model.addAttribute("mesMaterialJycheck", mesMaterialJycheck);
+
+		MesMaterial mesMaterial = new MesMaterial();
+		mesMaterial.setId(mesMaterialJycheck.getMaterialId());
+		MesMaterial mml = mesMaterialService.get(mesMaterial);
+		model.addAttribute("mml", mml);
+
+		return "modules/mes/mesMaterialJycheckForm";
+	}
+
+	/**
+	 * 保存数据
+	 */
+	@RequiresPermissions("mes:mesMaterialJycheck:edit")
+	@PostMapping(value = "save")
+	@ResponseBody
+	public String save(@Validated MesMaterialJycheck mesMaterialJycheck) {
+		mesMaterialJycheckService.save(mesMaterialJycheck);
+		return renderResult(Global.TRUE, text("保存加严检查内容成功!"));
+	}
+
+	/**
+	 * 删除数据
+	 */
+	@RequiresPermissions("mes:mesMaterialJycheck:edit")
+	@RequestMapping(value = "delete")
+	@ResponseBody
+	public String delete(MesMaterialJycheck mesMaterialJycheck) {
+		mesMaterialJycheckService.delete(mesMaterialJycheck);
+		return renderResult(Global.TRUE, text("删除加严检查内容成功!"));
+	}
+
+}

+ 78 - 0
src/main/java/com/jeesite/modules/mes/web/MesProcessQtyConfigController.java

@@ -0,0 +1,78 @@
+package com.jeesite.modules.mes.web;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import com.jeesite.common.config.Global;
+import com.jeesite.common.entity.Page;
+import com.jeesite.common.web.BaseController;
+import com.jeesite.modules.mes.entity.MesProcessQtyConfig;
+import com.jeesite.modules.mes.service.MesProcessQtyConfigService;
+
+/**
+ * 工位加工件数配置Controller
+ */
+@Controller
+@RequestMapping(value = "${adminPath}/mes/mesProcessQtyConfig")
+public class MesProcessQtyConfigController extends BaseController {
+
+	@Autowired
+	private MesProcessQtyConfigService mesProcessQtyConfigService;
+
+	@ModelAttribute
+	public MesProcessQtyConfig get(String id, boolean isNewRecord) {
+		return mesProcessQtyConfigService.get(id, isNewRecord);
+	}
+
+	@RequiresPermissions("mes:mesProcessQtyConfig:view")
+	@RequestMapping(value = {"list", ""})
+	public String list(MesProcessQtyConfig mesProcessQtyConfig, Model model) {
+		model.addAttribute("mesProcessQtyConfig", mesProcessQtyConfig);
+		return "modules/mes/mesProcessQtyConfigList";
+	}
+
+	@RequiresPermissions("mes:mesProcessQtyConfig:view")
+	@RequestMapping(value = "listData")
+	@ResponseBody
+	public Page<MesProcessQtyConfig> listData(MesProcessQtyConfig mesProcessQtyConfig,
+			HttpServletRequest request, HttpServletResponse response) {
+		mesProcessQtyConfig.setPage(new Page<>(request, response));
+		return mesProcessQtyConfigService.findPage(mesProcessQtyConfig);
+	}
+
+	@RequiresPermissions("mes:mesProcessQtyConfig:view")
+	@RequestMapping(value = "form")
+	public String form(MesProcessQtyConfig mesProcessQtyConfig, Model model) {
+		if (mesProcessQtyConfig.getIsNewRecord() && mesProcessQtyConfig.getEnableFlag() == null) {
+			mesProcessQtyConfig.setEnableFlag(MesProcessQtyConfig.ENABLE_YES);
+		}
+		model.addAttribute("mesProcessQtyConfig", mesProcessQtyConfig);
+		return "modules/mes/mesProcessQtyConfigForm";
+	}
+
+	@RequiresPermissions("mes:mesProcessQtyConfig:edit")
+	@PostMapping(value = "save")
+	@ResponseBody
+	public String save(@Validated MesProcessQtyConfig mesProcessQtyConfig) {
+		mesProcessQtyConfigService.save(mesProcessQtyConfig);
+		return renderResult(Global.TRUE, text("保存工位加工件数配置成功!"));
+	}
+
+	@RequiresPermissions("mes:mesProcessQtyConfig:edit")
+	@RequestMapping(value = "delete")
+	@ResponseBody
+	public String delete(MesProcessQtyConfig mesProcessQtyConfig) {
+		mesProcessQtyConfigService.delete(mesProcessQtyConfig);
+		return renderResult(Global.TRUE, text("删除工位加工件数配置成功!"));
+	}
+}

+ 26 - 10
src/main/java/com/jeesite/modules/mes/web/MesProductRecordController.java

@@ -223,6 +223,8 @@ public class MesProductRecordController extends BaseController {
 	@Autowired
 	private MesProductSampleRecordService mesProductSampleRecordService;
 	@Autowired
+	private MesProcessQtyConfigService mesProcessQtyConfigService;
+	@Autowired
 	private MesOrderSnService mesOrderSnService;
 	@Autowired
 	private MesMaterialLlService mesMaterialLlService;
@@ -1685,8 +1687,7 @@ public class MesProductRecordController extends BaseController {
 			}
 
 			if(mesLineProcess1.getQuality().equals("0") && mesLineProcess1.getRepeat().equals("1")){ // 不防呆且可以重复工作
-				ret = bs+"UD";
-				return ret;
+				return applyProcessQtyIfUd(bs, oldOprno, lineSn);
 			}else{
 				// 1.检查开班点检
 				if(!mesProcessCheckRecordService.checkRecord(oldOprno,lineSn)){
@@ -1725,7 +1726,7 @@ public class MesProductRecordController extends BaseController {
 					}
 				}
 			}
-			return bs+"UD";
+			return applyProcessQtyIfUd(bs, oldOprno, lineSn);
 		}else{
 			checkTempalte(sn,oprno,lineSn); // 检查模板
 		}
@@ -1797,8 +1798,7 @@ public class MesProductRecordController extends BaseController {
 		oprno = CommonUitl.formatOprno(oprno);
 		if(mesLineProcess1.getQuality().equals("0") && mesLineProcess1.getRepeat().equals("1")){ // 不防呆且可以重复工作
 			logger.info("1111111111111111");
-			ret = bs+"UD";
-			return ret;
+			return applyProcessQtyIfUd(bs, oprno, lineSn);
 		}
 
 
@@ -1936,14 +1936,20 @@ public class MesProductRecordController extends BaseController {
 		ret = bs+lxres;
 
 		if(mesLineProcess1.getQuality().equals("0") && !ret.equals(bs+"OK") && !ret.equals(bs+"NG")){ // 不防呆
-			ret = bs+"UD";
-			return ret;
+			return applyProcessQtyIfUd(bs, oprno, lineSn);
 		}
 
 		if(mesLineProcess1.getRepeat().equals("1") && (ret.equals(bs+"OK") || ret.equals(bs+"NG"))){ // 可重复工作
 			ret = bs+"UD";
 		}
 
+		// 可加工时再做加工件数模数校验
+		if (ret != null && ret.length() >= 5 && "UD".equals(ret.substring(ret.length() - 2))) {
+			if (!mesProcessQtyConfigService.checkProcessQtyPass(oprno, lineSn)) {
+				return bs + "MJ";
+			}
+		}
+
 //		if (oprno.equals("OP370")){
 //			//判断是否要进行4h时间管控
 //			String isOpen = Global.getConfig("mes.Hwtime.isOpen");
@@ -1991,6 +1997,16 @@ public class MesProductRecordController extends BaseController {
 		return ret;
 	}
 
+	/**
+	 * 本可返回 UD 时,若工位加工件数配置启用且当前班次 OK+NG 对加工件数求余为 0,则改为 MJ。
+	 */
+	private String applyProcessQtyIfUd(String bs, String oprno, String lineSn) {
+		if (!mesProcessQtyConfigService.checkProcessQtyPass(oprno, lineSn)) {
+			return bs + "MJ";
+		}
+		return bs + "UD";
+	}
+
 	//上传质量结果
 	//msg=aaaabbbbbABWMQDWGWOP040 GY100000ID151245P00000106200123062900001      RSOKDA2023-08-18ZT14:32:33YHjinjushijinjushi1111
 	//msg=aaaabbbbbABWMQDWGWOP080 GY100000LX  ID151245P00000106200123062900001      RSOKDA2023-08-18ZT14:32:33YHjinjushijinjushi111105Y11111111N22222222Y33333333Y44444444Y55555555
@@ -3017,9 +3033,9 @@ public String check(@RequestBody MesElectricTorque mesElectricTorque,HttpServlet
 		}
 
 		try{
-			// OP110提交OK时,同步将OP110~OP180工位结果都提交为OK
+			// OP110提交OK时,同步将OP110~OP160工位结果都提交为OK
 			if("OP110".equals(CommonUitl.formatOprno(oprno)) && "OK".equals(result)){
-				String[] relatedOprnos = new String[]{"OP110", "OP120", "OP130", "OP140", "OP150", "OP160", "OP170", "OP180"};
+				String[] relatedOprnos = new String[]{"OP110", "OP120", "OP130", "OP140", "OP150", "OP160"};
 				for(String relatedOprno : relatedOprnos){
 					MesLineProcess mlp = "OP110".equals(relatedOprno)
 							? mesLineProcess1
@@ -3027,7 +3043,7 @@ public String check(@RequestBody MesElectricTorque mesElectricTorque,HttpServlet
 					if(ObjectUtils.isEmpty(mlp)){
 						return renderResult(Global.FALSE, text(relatedOprno + "子工位未配置!"));
 					}
-					// OP120-OP180提交前检查模板,缺失则补建
+					// OP120-OP160提交前检查模板,缺失则补建
 					if(!"OP110".equals(relatedOprno)){
 						checkTempalte(sn, relatedOprno, lineSn);
 					}

+ 2 - 0
src/main/java/com/jeesite/modules/utils/ErrorMsg.java

@@ -56,6 +56,8 @@ public class ErrorMsg {
                 lmsg = "请先上传照片";
             } else if (processMsgRet.equalsIgnoreCase("SJ")) {
                 lmsg = "OKNG样件未进行开班点检";
+            } else if (processMsgRet.equalsIgnoreCase("MJ")) {
+                lmsg = "已达加工件数模数,质量校验不通过";
             }
         }catch (Exception e){ }
         return lmsg;

+ 15 - 0
src/main/resources/mappings/modules/mes/MesMaterialJycheckDao.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.jeesite.modules.mes.dao.MesMaterialJycheckDao">
+	
+	<!-- 查询数据
+	<select id="findList" resultType="MesMaterialJycheck">
+		SELECT ${sqlMap.column.toSql()}
+		FROM ${sqlMap.table.toSql()}
+		<where>
+			${sqlMap.where.toSql()}
+		</where>
+		ORDER BY ${sqlMap.order.toSql()}
+	</select> -->
+	
+</mapper>

+ 8 - 0
src/main/resources/mappings/modules/mes/MesProductRecordDao.xml

@@ -96,4 +96,12 @@
           AND UPPER(TRIM(IFNULL(a.content, ''))) = 'NG'
           AND a.oprno LIKE CONCAT(#{oprnoPrefix}, '%')
     </select>
+
+    <select id="countOkNgByOprnoPrefix" resultType="java.lang.Long">
+        SELECT COUNT(1)
+        FROM mes_product_record a
+        WHERE a.craft = '100000'
+          AND UPPER(TRIM(IFNULL(a.content, ''))) IN ('OK', 'NG')
+          AND a.oprno LIKE CONCAT(#{oprnoPrefix}, '%')
+    </select>
 </mapper>

+ 224 - 123
src/main/resources/views/modules/mes/mesMaterialCheckRecordForm.html

@@ -81,8 +81,6 @@
 					</div>
 					<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>-->
 							<div class="col-sm-12">
 								<table class="table table-bordered" style="table-layout: fixed;white-space: normal;">
 									<tr>
@@ -98,58 +96,53 @@
 										<th>实测值5</th>
 										<th>结果</th>
 									</tr>
-									<tr v-for="(item,index) in options" :key="index">
-									<td>{{item.orgName}}</td>
-									<td style="word-wrap: break-word;overflow-wrap: break-word;">{{item.content}}</td>
-									<td style="word-wrap: break-word;overflow-wrap: break-word;">
-										<textarea v-model="item.conclusionContent" class="form-control" rows="2"></textarea>
-									</td>
-									<td>{{item.checkTool}}</td>
-									<td>{{item.checkFrequency}}</td>
-									<template v-if="item.measureType == '2'">
-										<td>
-											<select v-model="item.a1" class="form-control" @change="autoResult(item)">
-
-												<option value="合格">合格</option>
-												<option value="不合格">不合格</option>
-											</select>
-										</td>
-										<td>
-											<select v-model="item.a2" class="form-control" @change="autoResult(item)">
-
-												<option value="合格">合格</option>
-												<option value="不合格">不合格</option>
-											</select>
-										</td>
-										<td>
-											<select v-model="item.a3" class="form-control" @change="autoResult(item)">
-
-												<option value="合格">合格</option>
-												<option value="不合格">不合格</option>
-											</select>
-										</td>
-										<td>
-											<select v-model="item.a4" class="form-control" @change="autoResult(item)">
-
-												<option value="合格">合格</option>
-												<option value="不合格">不合格</option>
-											</select>
+									<tr v-for="(item,index) in options" :key="'n-'+index">
+										<td>{{item.orgName}}</td>
+										<td style="word-wrap: break-word;overflow-wrap: break-word;">{{item.content}}</td>
+										<td style="word-wrap: break-word;overflow-wrap: break-word;">
+											<textarea v-model="item.conclusionContent" class="form-control" rows="2"></textarea>
 										</td>
-										<td>
-											<select v-model="item.a5" class="form-control" @change="autoResult(item)">
-
-												<option value="合格">合格</option>
-												<option value="不合格">不合格</option>
-											</select>
-										</td>
-									</template>
-									<template v-else>
-									<td><input type="text" v-model="item.a1" class="form-control" @input="formatData()"></td>
-									<td><input type="text" v-model="item.a2" class="form-control" @input="formatData()"></td>
-									<td><input type="text" v-model="item.a3" class="form-control" @input="formatData()"></td>
-									<td><input type="text" v-model="item.a4" class="form-control" @input="formatData()"></td>
-									<td><input type="text" v-model="item.a5" class="form-control" @input="formatData()"></td>
-								</template>
+										<td>{{item.checkTool}}</td>
+										<td>{{item.checkFrequency}}</td>
+										<template v-if="item.measureType == '2'">
+											<td>
+												<select v-model="item.a1" class="form-control" @change="autoResult(item)">
+													<option value="合格">合格</option>
+													<option value="不合格">不合格</option>
+												</select>
+											</td>
+											<td>
+												<select v-model="item.a2" class="form-control" @change="autoResult(item)">
+													<option value="合格">合格</option>
+													<option value="不合格">不合格</option>
+												</select>
+											</td>
+											<td>
+												<select v-model="item.a3" class="form-control" @change="autoResult(item)">
+													<option value="合格">合格</option>
+													<option value="不合格">不合格</option>
+												</select>
+											</td>
+											<td>
+												<select v-model="item.a4" class="form-control" @change="autoResult(item)">
+													<option value="合格">合格</option>
+													<option value="不合格">不合格</option>
+												</select>
+											</td>
+											<td>
+												<select v-model="item.a5" class="form-control" @change="autoResult(item)">
+													<option value="合格">合格</option>
+													<option value="不合格">不合格</option>
+												</select>
+											</td>
+										</template>
+										<template v-else>
+											<td><input type="text" v-model="item.a1" class="form-control" @input="formatData()"></td>
+											<td><input type="text" v-model="item.a2" class="form-control" @input="formatData()"></td>
+											<td><input type="text" v-model="item.a3" class="form-control" @input="formatData()"></td>
+											<td><input type="text" v-model="item.a4" class="form-control" @input="formatData()"></td>
+											<td><input type="text" v-model="item.a5" class="form-control" @input="formatData()"></td>
+										</template>
 										<td>
 											<div v-if="item.type == 1">
 												<label v-if="item.result == 1"><input type="radio" v-model="item.result" value="1">合格</label>
@@ -157,11 +150,11 @@
 											</div>
 											<div v-if="item.type == 2">
 												<label>
-													<input type="radio" :name="item.id" v-model="item.result" value="1" @click="formatData2(item,1)">
+													<input type="radio" :name="'n-'+item.id" v-model="item.result" value="1" @click="formatData2(item,1,'options')">
 													合格
 												</label>
 												<label>
-													<input type="radio" :name="item.id" v-model="item.result" value="0" @click="formatData2(item,0)">
+													<input type="radio" :name="'n-'+item.id" v-model="item.result" value="0" @click="formatData2(item,0,'options')">
 													不合格
 												</label>
 											</div>
@@ -171,6 +164,94 @@
 							</div>
 						</div>
 					</div>
+					<div v-if="inspectLevel === '不合格'">
+						<div class="form-unit">${text('加严检查项')}</div>
+						<div class="col-xs-12">
+							<div class="form-group">
+								<div class="col-sm-12">
+									<table class="table table-bordered" style="table-layout: fixed;white-space: normal;">
+										<tr>
+											<th>项目</th>
+											<th>检查内容</th>
+											<th>结论内容</th>
+											<th>检查工具</th>
+											<th>检查频率</th>
+											<th>实测值1</th>
+											<th>实测值2</th>
+											<th>实测值3</th>
+											<th>实测值4</th>
+											<th>实测值5</th>
+											<th>结果</th>
+										</tr>
+										<tr v-for="(item,index) in jyOptions" :key="'j-'+index">
+											<td>{{item.orgName}}</td>
+											<td style="word-wrap: break-word;overflow-wrap: break-word;">{{item.content}}</td>
+											<td style="word-wrap: break-word;overflow-wrap: break-word;">
+												<textarea v-model="item.conclusionContent" class="form-control" rows="2"></textarea>
+											</td>
+											<td>{{item.checkTool}}</td>
+											<td>{{item.checkFrequency}}</td>
+											<template v-if="item.measureType == '2'">
+												<td>
+													<select v-model="item.a1" class="form-control" @change="autoResult(item)">
+														<option value="合格">合格</option>
+														<option value="不合格">不合格</option>
+													</select>
+												</td>
+												<td>
+													<select v-model="item.a2" class="form-control" @change="autoResult(item)">
+														<option value="合格">合格</option>
+														<option value="不合格">不合格</option>
+													</select>
+												</td>
+												<td>
+													<select v-model="item.a3" class="form-control" @change="autoResult(item)">
+														<option value="合格">合格</option>
+														<option value="不合格">不合格</option>
+													</select>
+												</td>
+												<td>
+													<select v-model="item.a4" class="form-control" @change="autoResult(item)">
+														<option value="合格">合格</option>
+														<option value="不合格">不合格</option>
+													</select>
+												</td>
+												<td>
+													<select v-model="item.a5" class="form-control" @change="autoResult(item)">
+														<option value="合格">合格</option>
+														<option value="不合格">不合格</option>
+													</select>
+												</td>
+											</template>
+											<template v-else>
+												<td><input type="text" v-model="item.a1" class="form-control" @input="formatData()"></td>
+												<td><input type="text" v-model="item.a2" class="form-control" @input="formatData()"></td>
+												<td><input type="text" v-model="item.a3" class="form-control" @input="formatData()"></td>
+												<td><input type="text" v-model="item.a4" class="form-control" @input="formatData()"></td>
+												<td><input type="text" v-model="item.a5" class="form-control" @input="formatData()"></td>
+											</template>
+											<td>
+												<div v-if="item.type == 1">
+													<label v-if="item.result == 1"><input type="radio" v-model="item.result" value="1">合格</label>
+													<label v-if="item.result == 0"><input type="radio" v-model="item.result" value="0">不合格</label>
+												</div>
+												<div v-if="item.type == 2">
+													<label>
+														<input type="radio" :name="'j-'+item.id" v-model="item.result" value="1" @click="formatData2(item,1,'jyOptions')">
+														合格
+													</label>
+													<label>
+														<input type="radio" :name="'j-'+item.id" v-model="item.result" value="0" @click="formatData2(item,0,'jyOptions')">
+														不合格
+													</label>
+												</div>
+											</td>
+										</tr>
+									</table>
+								</div>
+							</div>
+						</div>
+					</div>
 <!--					<div class="col-xs-12">-->
 <!--						<div class="form-group">-->
 <!--							<label class="control-label col-sm-2" title="">-->
@@ -244,17 +325,22 @@ var vm = new Vue({
 		bhgQty:"",
 		inspectLevel:"",
 		state:"1",
-		options:[]
+		options:[],
+		jyOptions:[]
 	},
 	computed: {
-		// 计算属性的 getter
 		resoptions: function () {
-			//console.log(newval);
 			return JSON.parse(JSON.stringify(this.options));
+		},
+		resJyOptions: function () {
+			return JSON.parse(JSON.stringify(this.jyOptions));
 		}
 	},
 	watch: {
-		resoptions: function (newval, oldval) {
+		resoptions: function () {
+			this.formatData();
+		},
+		resJyOptions: function () {
 			this.formatData();
 		},
 		sn:function (newval, oldval){
@@ -274,76 +360,75 @@ var vm = new Vue({
 			var hasMax = max !== null && max !== undefined && max !== '' && !isNaN(Number(max));
 			return hasMin || hasMax;
 		},
-		formatData(){
-			for (let o in this.options){
-				let item = this.options[o];
-				// 检查5个实测值是否全部为空
-				let v1 = item.a1 ? String(item.a1).trim() : '';
-				let v2 = item.a2 ? String(item.a2).trim() : '';
-				let v3 = item.a3 ? String(item.a3).trim() : '';
-				let v4 = item.a4 ? String(item.a4).trim() : '';
-				let v5 = item.a5 ? String(item.a5).trim() : '';
-				let allEmpty = (v1 === '' && v2 === '' && v3 === '' && v4 === '' && v5 === '');
+		calcItemResult(item){
+			let v1 = item.a1 ? String(item.a1).trim() : '';
+			let v2 = item.a2 ? String(item.a2).trim() : '';
+			let v3 = item.a3 ? String(item.a3).trim() : '';
+			let v4 = item.a4 ? String(item.a4).trim() : '';
+			let v5 = item.a5 ? String(item.a5).trim() : '';
+			let allEmpty = (v1 === '' && v2 === '' && v3 === '' && v4 === '' && v5 === '');
 
-				if(item['type'] == "1"){ // 系统判定
-					let fminval = parseFloat(item.minValue ? item.minValue : '');
-					item.minValue = fminval;
-					let fmaxval = parseFloat(item.maxValue ? item.maxValue : '');
-					item.maxValue = fmaxval;
+			if(item['type'] == "1"){ // 系统判定
+				let fminval = parseFloat(item.minValue ? item.minValue : '');
+				item.minValue = fminval;
+				let fmaxval = parseFloat(item.maxValue ? item.maxValue : '');
+				item.maxValue = fmaxval;
 
-					// 5个实测值未输入或未选择时,默认不合格
-					if(allEmpty){
-						item.result = "0";
-						continue;
-					}
-					// 下拉框类型:全部为"合格"才合格
-					if(item.measureType == '2'){
-						let allOk = (v1 === '合格' && v2 === '合格' && v3 === '合格' && v4 === '合格' && v5 === '合格');
-						item.result = allOk ? "1" : "0";
-						continue;
-					}
-					// 输入框类型无测量范围但有输入值,默认合格
-					if (!this.hasMeasureRange(item)) {
-						item.result = "1";
-						continue;
-					}
-					// 输入框类型有测量范围,逐个判断
-					let tret = "1";
-					let a1 = parseFloat(item.a1 ? item.a1 : '');
-					let ret1 = this.checkResult(fminval, fmaxval, a1);
-					let a2 = parseFloat(item.a2 ? item.a2 : '');
-					let ret2 = this.checkResult(fminval, fmaxval, a2);
-					let a3 = parseFloat(item.a3 ? item.a3 : '');
-					let ret3 = this.checkResult(fminval, fmaxval, a3);
-					let a4 = parseFloat(item.a4 ? item.a4 : '');
-					let ret4 = this.checkResult(fminval, fmaxval, a4);
-					let a5 = parseFloat(item.a5 ? item.a5 : '');
-					let ret5 = this.checkResult(fminval, fmaxval, a5);
-					if(ret1 == "NG" || ret2 == "NG" || ret3 == "NG" || ret4 == "NG" || ret5 == "NG"){
-						tret = "0"
-					}
-					item.result = tret;
-				}else{ // 人工判定
-					// 5个实测值未输入或未选择且未手动选择结果时,默认不合格
-					if(allEmpty && (item.result === '' || item.result === undefined || item.result === null)){
-						item.result = "0";
-					}
+				if(allEmpty){
+					item.result = "0";
+					return;
+				}
+				if(item.measureType == '2'){
+					let allOk = (v1 === '合格' && v2 === '合格' && v3 === '合格' && v4 === '合格' && v5 === '合格');
+					item.result = allOk ? "1" : "0";
+					return;
+				}
+				if (!this.hasMeasureRange(item)) {
+					item.result = "1";
+					return;
+				}
+				let tret = "1";
+				let a1 = parseFloat(item.a1 ? item.a1 : '');
+				let ret1 = this.checkResult(fminval, fmaxval, a1);
+				let a2 = parseFloat(item.a2 ? item.a2 : '');
+				let ret2 = this.checkResult(fminval, fmaxval, a2);
+				let a3 = parseFloat(item.a3 ? item.a3 : '');
+				let ret3 = this.checkResult(fminval, fmaxval, a3);
+				let a4 = parseFloat(item.a4 ? item.a4 : '');
+				let ret4 = this.checkResult(fminval, fmaxval, a4);
+				let a5 = parseFloat(item.a5 ? item.a5 : '');
+				let ret5 = this.checkResult(fminval, fmaxval, a5);
+				if(ret1 == "NG" || ret2 == "NG" || ret3 == "NG" || ret4 == "NG" || ret5 == "NG"){
+					tret = "0"
+				}
+				item.result = tret;
+			}else{ // 人工判定
+				if(allEmpty && (item.result === '' || item.result === undefined || item.result === null)){
+					item.result = "0";
 				}
 			}
-
-			let ops = JSON.parse(JSON.stringify(this.options));
-
-			console.log('ret',JSON.stringify(ops));
-			$('#checkList').val(JSON.stringify(ops));
-
 		},
-		formatData2(obj,val){
+		formatData(){
 			for (let o in this.options){
-				if(obj.id == this.options[o].id){
-					this.options[o].result = val;
+				this.calcItemResult(this.options[o]);
+			}
+			for (let o in this.jyOptions){
+				this.calcItemResult(this.jyOptions[o]);
+			}
+
+			let ops = JSON.parse(JSON.stringify(this.options || []));
+			let jyOps = JSON.parse(JSON.stringify(this.jyOptions || []));
+			let allOps = ops.concat(jyOps);
+			console.log('ret',JSON.stringify(allOps));
+			$('#checkList').val(JSON.stringify(allOps));
+		},
+		formatData2(obj,val,listName){
+			var list = this[listName || 'options'] || [];
+			for (let o in list){
+				if(obj.id == list[o].id){
+					list[o].result = val;
 				}
 			}
-			console.log('ret11',val,JSON.stringify(this.options));
 			this.formatData();
 		},
 		// 测量类型为下拉框时,根据5个实测值自动判定结果
@@ -360,6 +445,7 @@ var vm = new Vue({
 				}
 			}
 			item.result = allOk ? '1' : '0';
+			this.formatData();
 		},
 		checkResult(fminval,fmaxval,fcontent){
 			let result = "NG";
@@ -412,6 +498,7 @@ var vm = new Vue({
 					that.bhgQty="";
 					that.inspectLevel="";
 					that.options=[];
+					that.jyOptions=[];
 					that.formatData();
 				}else {
 					var snFields = that.parseSnFields(that.sn);
@@ -424,11 +511,15 @@ var vm = new Vue({
 					that.hgQty=that.faNums;
 					that.bhgQty=0;
 					that.inspectLevel=res.data.inspectLevel || "";
-					that.options=res.data.mitList;
+					that.options=res.data.mitList || [];
+					that.jyOptions=(that.inspectLevel === '不合格' ? (res.data.jyMitList || []) : []);
 
 					for (let o in that.options){
 						that.options[o]['result'] = "";
 					}
+					for (let o in that.jyOptions){
+						that.jyOptions[o]['result'] = "";
+					}
 					that.formatData();
 				}
 			})
@@ -464,7 +555,9 @@ var vm = new Vue({
 								 that.faNums=0;
 								 that.hgQty="";
 								 that.bhgQty="";
+								 that.inspectLevel="";
 								 that.options=[];
+								 that.jyOptions=[];
 								 that.formatData();
 							 }else {
 								 var snFields = that.parseSnFields(value);
@@ -476,7 +569,15 @@ var vm = new Vue({
 								 that.faNums=res.data.faNums || snFields.faNums;
 								 that.hgQty=that.faNums;
 								 that.bhgQty=0;
-								 that.options=res.data.mitList;
+								 that.inspectLevel=res.data.inspectLevel || "";
+								 that.options=res.data.mitList || [];
+								 that.jyOptions=(that.inspectLevel === '不合格' ? (res.data.jyMitList || []) : []);
+								 for (let o in that.options){
+									 that.options[o]['result'] = "";
+								 }
+								 for (let o in that.jyOptions){
+									 that.jyOptions[o]['result'] = "";
+								 }
 								 that.formatData();
 							 }
 						})

+ 151 - 0
src/main/resources/views/modules/mes/mesMaterialJycheckForm.html

@@ -0,0 +1,151 @@
+<% layout('/layouts/default.html', {title: '加严检查内容管理', libs: ['validate']}){ %>
+<div class="main-content">
+	<div class="box box-main">
+		<div class="box-header with-border">
+			<div class="box-title">
+				<i class="fa icon-note"></i> ${text(mesMaterialJycheck.isNewRecord ? '新增加严检查内容' : '编辑加严检查内容')}
+			</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="${mesMaterialJycheck}" action="${ctx}/mes/mesMaterialJycheck/save" method="post" class="form-horizontal">
+			<div class="box-body">
+				<div class="form-unit">${text('基本信息')}</div>
+				<#form:hidden path="id"/>
+				<#form:hidden path="materialId"/>
+				<div class="row">
+				<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>
+						<div class="col-sm-8">
+							${mml.title}
+						</div>
+					</div>
+				</div>
+				<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>
+						<div class="col-sm-8">
+							<#form:input path="serialNo" maxlength="64" class="form-control"/>
+						</div>
+					</div>
+				</div>
+			</div>
+				<div class="row">
+					<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>
+							<div class="col-sm-8">
+								<#form:select path="org" dictType="mes_material_org" blankOption="true" class="form-control" />
+							</div>
+						</div>
+					</div>
+				</div>
+				<div class="row">
+					<div class="col-xs-12">
+						<div class="form-group">
+							<label class="control-label col-sm-2" title="">
+								<span class="required hide">*</span> ${text('检查内容')}:<i class="fa icon-question hide"></i></label>
+							<div class="col-sm-10">
+								<#form:textarea path="content" rows="4" class="form-control"/>
+							</div>
+						</div>
+					</div>
+				</div>
+				<div class="row">
+				<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>
+						<div class="col-sm-8">
+							<#form:input path="checkTool" maxlength="255" class="form-control"/>
+						</div>
+					</div>
+				</div>
+				<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>
+						<div class="col-sm-8">
+							<#form:input path="checkFrequency" maxlength="255" class="form-control"/>
+						</div>
+					</div>
+				</div>
+			</div>
+			<div class="row">
+				<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>
+						<div class="col-sm-8">
+							<#form:select path="measureType" dictType="mes_measure_type" blankOption="true" class="form-control" />
+						</div>
+					</div>
+				</div>
+			</div>
+				<div class="row">
+					<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>
+							<div class="col-sm-8">
+								<#form:select path="type" dictType="mes_material_check_type" blankOption="true" class="form-control" />
+							</div>
+						</div>
+					</div>
+				</div>
+				<div class="row">
+					<div class="col-xs-6" id="min">
+						<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>
+							<div class="col-sm-8">
+								<#form:input path="minValue" maxlength="253" class="form-control"/>
+							</div>
+						</div>
+					</div>
+				</div>
+				<div class="row">
+					<div class="col-xs-6" id="max">
+						<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>
+							<div class="col-sm-8">
+								<#form:input path="maxValue" maxlength="253" class="form-control"/>
+							</div>
+						</div>
+					</div>
+				</div>
+			</div>
+			<div class="box-footer">
+				<div class="row">
+					<div class="col-sm-offset-2 col-sm-10">
+						<% if (hasPermi('mes:mesMaterialJycheck: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){
+		js.ajaxSubmitForm($(form), function(data){
+			js.showMessage(data.message);
+			if(data.result == Global.TRUE){
+				js.closeCurrentTabPage(function(contentWindow){
+					contentWindow.page();
+				});
+			}
+		}, "json");
+    }
+});
+</script>

+ 83 - 0
src/main/resources/views/modules/mes/mesMaterialJycheckList.html

@@ -0,0 +1,83 @@
+<% layout('/layouts/default.html', {title: '加严检查内容管理', libs: ['dataGrid']}){ %>
+<div class="main-content">
+	<div class="box box-main">
+		<div class="box-header">
+			<div class="box-title">
+				<i class="fa icon-notebook"></i> ${text('加严检查内容管理')}
+			</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>
+				<% if(hasPermi('mes:mesMaterialJycheck:edit')){ %>
+					<a href="${ctx}/mes/mesMaterialJycheck/form?materialId=${mesMaterialJycheck.materialId}" class="btn btn-default btnTool" title="${text('新增')}"><i class="fa fa-plus"></i> ${text('新增')}</a>
+				<% } %>
+				<a href="#" class="btn btn-default" id="btnSetting" title="${text('设置')}"><i class="fa fa-navicon"></i></a>
+			</div>
+		</div>
+		<div class="box-body">
+			<#form:form id="searchForm" model="${mesMaterialJycheck}" action="${ctx}/mes/mesMaterialJycheck/listData" method="post" class="form-inline hide"
+					data-page-no="${parameter.pageNo}" data-page-size="${parameter.pageSize}" data-order-by="${parameter.orderBy}">
+				<#form:hidden path="materialId"/>
+
+				<div class="form-group">
+					<label class="control-label">${text('项目')}:</label>
+					<div class="control-inline width-120">
+						<#form:select path="org" dictType="mes_material_org" blankOption="true" class="form-control"/>
+					</div>
+				</div>
+				<div class="form-group">
+					<label class="control-label">${text('检查内容')}:</label>
+					<div class="control-inline">
+						<#form:input path="content" class="form-control width-120"/>
+					</div>
+				</div>
+				<div class="form-group">
+					<label class="control-label">${text('状态')}:</label>
+					<div class="control-inline width-120">
+						<#form:select path="status" dictType="sys_search_status" blankOption="true" class="form-control isQuick"/>
+					</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>
+			</#form:form>
+			<table id="dataGrid"></table>
+			<div id="dataGridPage"></div>
+		</div>
+	</div>
+</div>
+<% } %>
+<script>
+$('#dataGrid').dataGrid({
+	searchForm: $("#searchForm"),
+	columnModel: [
+		{header:'${text("编号")}', name:'serialNo', index:'a.serial_no', width:300, align:"center"},
+		{header:'${text("项目")}', name:'org', index:'a.org', width:120, align:"center", frozen:true, formatter: function(val, obj, row, act){
+				return js.getDictLabel(${@DictUtils.getDictListJson('mes_material_org')}, val, '${text("未知")}', true);
+			}},
+		{header:'${text("检查内容")}', name:'content', index:'a.content', width:300, align:"center"},
+		{header:'${text("检查工具")}', name:'checkTool', index:'a.check_tool', width:150, align:"center"},
+		{header:'${text("检查频率")}', name:'checkFrequency', index:'a.check_frequency', width:150, align:"center"},
+		{header:'${text("判定类型")}', name:'type', index:'a.type', width:120, align:"center", formatter: function(val, obj, row, act){
+				return js.getDictLabel(${@DictUtils.getDictListJson('mes_material_check_type')}, val, '${text("未知")}', true);
+			}},
+		{header:'${text("最小值")}', name:'minValue', index:'a.min_value', width:120, align:"center"},
+		{header:'${text("最大值")}', name:'maxValue', index:'a.max_value', width:120, align:"center"},
+		{header:'${text("状态")}', name:'status', index:'a.status', width:120, align:"center", formatter: function(val, obj, row, act){
+			return js.getDictLabel(${@DictUtils.getDictListJson('sys_search_status')}, val, '${text("未知")}', true);
+		}},
+		{header:'${text("更新时间")}', name:'updateDate', index:'a.update_date', width:150, align:"center"},
+		{header:'${text("操作")}', name:'actions', width:120, formatter: function(val, obj, row, act){
+			var actions = [];
+			//<% if(hasPermi('mes:mesMaterialJycheck:edit')){ %>
+				actions.push('<a href="${ctx}/mes/mesMaterialJycheck/form?id='+row.id+'" class="btnList" title="${text("编辑")}"><i class="fa fa-pencil"></i></a>&nbsp;');
+				actions.push('<a href="${ctx}/mes/mesMaterialJycheck/delete?id='+row.id+'" class="btnList" title="${text("删除")}" data-confirm="${text("确认要删除该加严检查内容吗?")}"><i class="fa fa-trash-o"></i></a>&nbsp;');
+			//<% } %>
+			return actions.join('');
+		}}
+	],
+	ajaxSuccess: function(data){
+
+	}
+});
+</script>

+ 2 - 1
src/main/resources/views/modules/mes/mesMaterialList.html

@@ -105,7 +105,7 @@ $('#dataGrid').dataGrid({
 			return js.getDictLabel(${@DictUtils.getDictListJson('sys_search_status')}, val, '${text("未知")}', true);
 		}},
 		{header:'${text("更新日期")}', name:'updateDate', index:'a.update_date', width:150, align:"center"},
-		{header:'${text("操作")}', name:'actions', width:150, formatter: function(val, obj, row, act){
+		{header:'${text("操作")}', name:'actions', width:220, formatter: function(val, obj, row, act){
 			var actions = [];
 			//<% if(hasPermi('mes:mesMaterial:edit')){ %>
 				actions.push('<a href="${ctx}/mes/mesMaterial/form?id='+row.id+'" class="btnList" title="${text("编辑物料表")}"><i class="fa fa-pencil"></i></a>&nbsp;');
@@ -116,6 +116,7 @@ $('#dataGrid').dataGrid({
 				}
 				actions.push('<a href="${ctx}/mes/mesMaterial/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/mesMaterialCheck/list?materialId='+row.id+'" class="btnList" title="${text("检查项")}" >检查项</a>&nbsp;');
+				actions.push('<a href="${ctx}/mes/mesMaterialJycheck/list?materialId='+row.id+'" class="btnList" title="${text("加严检查项")}" >加严检查项</a>&nbsp;');
 
 				//<% } %>
 			return actions.join('');

+ 82 - 0
src/main/resources/views/modules/mes/mesProcessQtyConfigForm.html

@@ -0,0 +1,82 @@
+<% layout('/layouts/default.html', {title: '工位加工件数配置', libs: ['validate']}){ %>
+<div class="main-content">
+	<div class="box box-main">
+		<div class="box-header with-border">
+			<div class="box-title">
+				<i class="fa icon-note"></i> ${text(mesProcessQtyConfig.isNewRecord ? '新增工位加工件数配置' : '编辑工位加工件数配置')}
+			</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="${mesProcessQtyConfig}" action="${ctx}/mes/mesProcessQtyConfig/save" method="post" class="form-horizontal">
+			<div class="box-body">
+				<div class="form-unit">${text('基本信息')}</div>
+				<#form:hidden path="id"/>
+				<div class="row">
+					<div class="col-xs-6">
+						<div class="form-group">
+							<label class="control-label col-sm-4" title="">
+								<span class="required">*</span> ${text('工位号')}:<i class="fa icon-question hide"></i></label>
+							<div class="col-sm-8">
+								<#form:input path="oprno" maxlength="20" class="form-control required" placeholder="如 OP360"/>
+							</div>
+						</div>
+					</div>
+					<div class="col-xs-6">
+						<div class="form-group">
+							<label class="control-label col-sm-4" title="">
+								<span class="required">*</span> ${text('加工件数')}:<i class="fa icon-question hide"></i></label>
+							<div class="col-sm-8">
+								<#form:input path="processQty" maxlength="10" class="form-control required digits" placeholder="当前班次OK+NG对该数求余为0时校验不通过"/>
+							</div>
+						</div>
+					</div>
+					<div class="col-xs-6">
+						<div class="form-group">
+							<label class="control-label col-sm-4" title="">
+								<span class="required">*</span> ${text('是否启用')}:<i class="fa icon-question hide"></i></label>
+							<div class="col-sm-8">
+								<#form:radio path="enableFlag" dictType="sys_yes_no" class="form-control required"/>
+							</div>
+						</div>
+					</div>
+					<div class="col-xs-12">
+						<div class="form-group">
+							<label class="control-label col-sm-2" title="">
+								<span class="required hide">*</span> ${text('备注')}:<i class="fa icon-question hide"></i></label>
+							<div class="col-sm-8">
+								<#form:textarea path="remarks" rows="3" maxlength="500" class="form-control"/>
+							</div>
+						</div>
+					</div>
+				</div>
+			</div>
+			<div class="box-footer">
+				<div class="row">
+					<div class="col-sm-offset-2 col-sm-10">
+						<% if (hasPermi('mes:mesProcessQtyConfig: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){
+		js.ajaxSubmitForm($(form), function(data){
+			js.showMessage(data.message);
+			if(data.result == Global.TRUE){
+				js.closeCurrentTabPage(function(contentWindow){
+					contentWindow.page();
+				});
+			}
+		}, "json");
+    }
+});
+</script>

+ 66 - 0
src/main/resources/views/modules/mes/mesProcessQtyConfigList.html

@@ -0,0 +1,66 @@
+<% layout('/layouts/default.html', {title: '工位加工件数配置', libs: ['dataGrid']}){ %>
+<div class="main-content">
+	<div class="box box-main">
+		<div class="box-header">
+			<div class="box-title">
+				<i class="fa icon-notebook"></i> ${text('工位加工件数配置')}
+			</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>
+				<% if(hasPermi('mes:mesProcessQtyConfig:edit')){ %>
+					<a href="${ctx}/mes/mesProcessQtyConfig/form" class="btn btn-default btnTool" title="${text('新增工位加工件数配置')}"><i class="fa fa-plus"></i> ${text('新增')}</a>
+				<% } %>
+				<a href="#" class="btn btn-default" id="btnSetting" title="${text('设置')}"><i class="fa fa-navicon"></i></a>
+			</div>
+		</div>
+		<div class="box-body">
+			<#form:form id="searchForm" model="${mesProcessQtyConfig}" action="${ctx}/mes/mesProcessQtyConfig/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>
+					<div class="control-inline">
+						<#form:input path="oprno" maxlength="20" class="form-control width-120"/>
+					</div>
+				</div>
+				<div class="form-group">
+					<label class="control-label">${text('是否启用')}:</label>
+					<div class="control-inline width-90">
+						<#form:select path="enableFlag" dictType="sys_yes_no" blankOption="true" class="form-control"/>
+					</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>
+			</#form:form>
+			<table id="dataGrid"></table>
+			<div id="dataGridPage"></div>
+		</div>
+	</div>
+</div>
+<% } %>
+<script>
+$('#dataGrid').dataGrid({
+	searchForm: $("#searchForm"),
+	columnModel: [
+		{header:'${text("工位号")}', name:'oprno', index:'a.oprno', width:120, align:"center", frozen:true, formatter: function(val, obj, row, act){
+			return '<a href="${ctx}/mes/mesProcessQtyConfig/form?id='+row.id+'" class="btnList" data-title="${text("编辑工位加工件数配置")}">'+(val||row.id)+'</a>';
+		}},
+		{header:'${text("加工件数")}', name:'processQty', index:'a.process_qty', width:120, align:"center"},
+		{header:'${text("是否启用")}', name:'enableFlag', index:'a.enable_flag', width:90, align:"center", formatter: function(val){
+			return val === '1' ? '${text("是")}' : '${text("否")}';
+		}},
+		{header:'${text("备注")}', name:'remarks', index:'a.remarks', width:200, align:"left"},
+		{header:'${text("操作")}', name:'actions', width:120, formatter: function(val, obj, row, act){
+			var actions = [];
+			//<% if(hasPermi('mes:mesProcessQtyConfig:edit')){ %>
+				actions.push('<a href="${ctx}/mes/mesProcessQtyConfig/form?id='+row.id+'" class="btnList" title="${text("编辑")}"><i class="fa fa-pencil"></i></a>&nbsp;');
+				actions.push('<a href="${ctx}/mes/mesProcessQtyConfig/delete?id='+row.id+'" class="btnList" title="${text("删除")}" data-confirm="${text("确认要删除该配置吗?")}"><i class="fa fa-trash-o"></i></a>&nbsp;');
+			//<% } %>
+			return actions.join('');
+		}}
+	],
+	ajaxSuccess: function(data){
+	}
+});
+</script>

+ 227 - 0
src/test/java/com/jeesite/test/FrameworkQmQualityCheckTest.java

@@ -0,0 +1,227 @@
+package com.jeesite.test;
+
+import com.jeesite.common.config.Global;
+import com.jeesite.common.lang.ObjectUtils;
+import com.jeesite.common.lang.StringUtils;
+import com.jeesite.common.tests.BaseSpringContextTests;
+import com.jeesite.modules.Application;
+import com.jeesite.modules.mes.entity.MesLineProcess;
+import com.jeesite.modules.mes.entity.MesProductSample;
+import com.jeesite.modules.mes.entity.MesProductSampleRecord;
+import com.jeesite.modules.mes.resp.MesShiftDataResp;
+import com.jeesite.modules.mes.service.MesLineProcessService;
+import com.jeesite.modules.mes.service.MesProductSampleRecordService;
+import com.jeesite.modules.mes.service.MesProductSampleService;
+import com.jeesite.modules.mes.service.MesShiftService;
+import com.jeesite.modules.mes.util.CommonUitl;
+import com.jeesite.modules.mes.web.MesProductRecordController;
+import com.jeesite.modules.utils.ErrorMsg;
+import org.junit.Assume;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.test.context.ActiveProfiles;
+
+import java.util.List;
+
+/**
+ * 框架气密(OP360 / FrameworkQmScheduling)质量校验联调测试。
+ * <p>
+ * 与定时任务一致:调用 {@link MesProductRecordController#checkQualityCommon},
+ * 返回码第 4~5 位为 {@code UD} 视为可进站。
+ * <p>
+ * 使用方式:填写下方 TEST_SN 后,IDE 中右键运行对应 {@code @Test},或运行 {@code main}。
+ */
+@ActiveProfiles("test")
+@SpringBootTest(classes = Application.class)
+public class FrameworkQmQualityCheckTest extends BaseSpringContextTests {
+
+	// ===================== 自行填写 =====================
+
+	/** 待测工件码(必填;普通件 / 样件码均可) */
+	private static final String TEST_SN = "0050010200173E5240017069091B40";
+
+	/** 产线编号;留空则读 mes.framework.qm.lineSn,再默认 XT */
+	private static final String TEST_LINE_SN = "";
+
+	/** 工位号;留空则读 mes.framework.qm.oprno,再默认 OP360 */
+	private static final String TEST_OPRNO = "";
+
+	// ===================== 自行填写结束 =====================
+
+	private static final String DEFAULT_OPRNO = "OP360";
+	private static final String DEFAULT_LINE_SN = "XT";
+
+	@Autowired
+	private MesProductRecordController mesProductRecordController;
+	@Autowired
+	private MesLineProcessService mesLineProcessService;
+	@Autowired
+	private MesProductSampleService mesProductSampleService;
+	@Autowired
+	private MesProductSampleRecordService mesProductSampleRecordService;
+	@Autowired
+	private MesShiftService mesShiftService;
+
+	/**
+	 * 与 FrameworkQmScheduling#doQualityCheck 相同调用链。
+	 */
+	@Test
+	public void testFrameworkQmQualityCheck() {
+		Assume.assumeTrue("请先填写 TEST_SN", StringUtils.isNotBlank(TEST_SN));
+		runCheck(resolveOprno(), TEST_SN.trim(), resolveLineSn());
+	}
+
+	/**
+	 * 打印防呆/班次/样件配置诊断,便于排查「配了样件仍放行」。
+	 */
+	@Test
+	public void testFrameworkQmSampleDiagnose() {
+		Assume.assumeTrue("请先填写 TEST_SN", StringUtils.isNotBlank(TEST_SN));
+		printDiagnose(resolveOprno(), TEST_SN.trim(), resolveLineSn());
+		runCheck(resolveOprno(), TEST_SN.trim(), resolveLineSn());
+	}
+
+	public static void main(String[] args) {
+		if (StringUtils.isBlank(TEST_SN)) {
+			System.out.println("请先填写 TEST_SN");
+			return;
+		}
+		System.out.println("========== FrameworkQmScheduling 质量校验联调 ==========");
+		ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);
+		try {
+			FrameworkQmQualityCheckTest bean = new FrameworkQmQualityCheckTest();
+			bean.mesProductRecordController = ctx.getBean(MesProductRecordController.class);
+			bean.mesLineProcessService = ctx.getBean(MesLineProcessService.class);
+			bean.mesProductSampleService = ctx.getBean(MesProductSampleService.class);
+			bean.mesProductSampleRecordService = ctx.getBean(MesProductSampleRecordService.class);
+			bean.mesShiftService = ctx.getBean(MesShiftService.class);
+
+			String oprno = resolveOprnoStatic();
+			String lineSn = resolveLineSnStatic();
+			String sn = TEST_SN.trim();
+			bean.printDiagnose(oprno, sn, lineSn);
+			bean.runCheck(oprno, sn, lineSn);
+		} finally {
+			ctx.close();
+		}
+	}
+
+	private void runCheck(String oprno, String sn, String lineSn) {
+		String oldOprno = oprno.length() == 5 ? oprno + "A" : oprno;
+		System.out.println("--- checkQualityCommon ---");
+		System.out.println("oprno=" + oprno + " oldOprno=" + oldOprno
+				+ " sn=" + sn + " lineSn=" + lineSn);
+
+		String ret = mesProductRecordController.checkQualityCommon(oldOprno, sn, lineSn, "");
+		System.out.println("raw ret=" + ret);
+
+		org.junit.Assert.assertNotNull(ret);
+		org.junit.Assert.assertTrue("返回值长度不足: " + ret, ret.length() >= 5);
+
+		String code = ret.substring(3, 5);
+		boolean pass = "UD".equals(code);
+		String lx = ret.length() >= 3 ? ret.substring(0, 3).trim() : "";
+		String msg = "UD".equals(code) ? "工件可以加工(可进站)" : ErrorMsg.getErrorMsg(code, lx);
+
+		System.out.println("code=" + code + " pass=" + pass + " msg=" + msg);
+		System.out.println("(FrameworkQmScheduling 判定:pass == \"UD\".equals(ret.substring(3,5)))");
+	}
+
+	private void printDiagnose(String oprno, String sn, String lineSn) {
+		String oldOprno = oprno.length() == 5 ? oprno + "A" : oprno;
+		String fOprno = CommonUitl.formatOprno(oldOprno);
+
+		System.out.println("--- 诊断(样件/防呆)---");
+		MesLineProcess process = mesLineProcessService.findInfoByOprnoLineSn(oldOprno, lineSn);
+		if (ObjectUtils.isEmpty(process)) {
+			System.out.println("工位工艺不存在: oldOprno=" + oldOprno + " lineSn=" + lineSn);
+		} else {
+			System.out.println("工位工艺 type=" + process.getType()
+					+ " quality(防呆)=" + process.getQuality()
+					+ " repeat=" + process.getRepeat()
+					+ " stop=" + process.getStop());
+			if (!"1".equals(process.getQuality())) {
+				System.out.println("!! quality!=1,样件检查不会执行");
+			}
+			if ("0".equals(process.getQuality()) && "1".equals(process.getRepeat())) {
+				System.out.println("!! quality=0 且 repeat=1,会直接返回 UD(跳过样件)");
+			}
+		}
+
+		MesShiftDataResp shift = mesShiftService.getCurShift();
+		if (ObjectUtils.isEmpty(shift)) {
+			System.out.println("当前班次=无  !! checkRecord 无班次时直接 true(不拦样件)");
+		} else {
+			System.out.println("当前班次 start=" + shift.getStart() + " end=" + shift.getEnd());
+		}
+
+		MesProductSample query = new MesProductSample();
+		query.setOprno(fOprno);
+		query.setLineSn(lineSn);
+		query.setStatus("0");
+		List<MesProductSample> samples = mesProductSampleService.findList(query);
+		System.out.println("启用样件数量(oprno=" + fOprno + ", lineSn=" + lineSn + ")="
+				+ (samples == null ? 0 : samples.size()));
+		if (samples == null || samples.isEmpty()) {
+			System.out.println("!! 未查到样件配置,checkRecord 会直接 true");
+		} else {
+			for (MesProductSample s : samples) {
+				System.out.println("  样件 sn=" + s.getSn() + " type=" + s.getType()
+						+ " (1=OK样件,2=NG样件)");
+				MesProductSampleRecord recQ = new MesProductSampleRecord();
+				recQ.setSn(s.getSn());
+				recQ.setOprno(fOprno);
+				recQ.setLineSn(lineSn);
+				recQ.setType(s.getType());
+				recQ.setResult("1".equals(s.getType()) ? "OK" : "NG");
+				if (!ObjectUtils.isEmpty(shift)
+						&& StringUtils.isNotBlank(shift.getStart())
+						&& StringUtils.isNotBlank(shift.getEnd())) {
+					try {
+						java.text.DateFormat df = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm");
+						recQ.setCreateDate_gte(df.parse(shift.getStart()));
+						recQ.setCreateDate_lte(df.parse(shift.getEnd()));
+					} catch (Exception e) {
+						e.printStackTrace();
+					}
+				}
+				MesProductSampleRecord hit = mesProductSampleRecordService.findInfo(recQ);
+				System.out.println("    本班次合格样件记录=" + (ObjectUtils.isEmpty(hit) ? "无" : "有 id=" + hit.getId()));
+			}
+		}
+
+		boolean isSample = mesProductSampleService.checkSampleSn(fOprno, lineSn, sn);
+		boolean sampleOk = mesProductSampleService.checkRecord(fOprno, lineSn, sn);
+		System.out.println("当前 SN 是否样件=" + isSample);
+		System.out.println("开班样件点检通过 checkRecord=" + sampleOk
+				+ (sampleOk ? "" : "(应返回 SJ)"));
+		System.out.println();
+	}
+
+	private String resolveOprno() {
+		return resolveOprnoStatic();
+	}
+
+	private String resolveLineSn() {
+		return resolveLineSnStatic();
+	}
+
+	private static String resolveOprnoStatic() {
+		if (StringUtils.isNotBlank(TEST_OPRNO)) {
+			return TEST_OPRNO.trim();
+		}
+		String cfg = Global.getConfig("mes.framework.qm.oprno");
+		return StringUtils.isBlank(cfg) ? DEFAULT_OPRNO : cfg.trim();
+	}
+
+	private static String resolveLineSnStatic() {
+		if (StringUtils.isNotBlank(TEST_LINE_SN)) {
+			return TEST_LINE_SN.trim();
+		}
+		String cfg = Global.getConfig("mes.framework.qm.lineSn");
+		return StringUtils.isBlank(cfg) ? DEFAULT_LINE_SN : cfg.trim();
+	}
+}