Przeglądaj źródła

参数维护上下限

hfy 1 miesiąc temu
rodzic
commit
d9366dca51
29 zmienionych plików z 1623 dodań i 62 usunięć
  1. 14 0
      src/main/java/com/jeesite/modules/mes/dao/MesProcessParamLimitDao.java
  2. 124 0
      src/main/java/com/jeesite/modules/mes/entity/MesProcessParamLimit.java
  3. 9 0
      src/main/java/com/jeesite/modules/mes/entity/MesProductArcWelding.java
  4. 10 1
      src/main/java/com/jeesite/modules/mes/entity/MesProductCmt.java
  5. 10 1
      src/main/java/com/jeesite/modules/mes/entity/MesProductProd.java
  6. 9 0
      src/main/java/com/jeesite/modules/mes/entity/MesProductQm.java
  7. 9 0
      src/main/java/com/jeesite/modules/mes/entity/MesWelderParamRecord.java
  8. 504 0
      src/main/java/com/jeesite/modules/mes/service/MesProcessParamLimitCheckService.java
  9. 59 0
      src/main/java/com/jeesite/modules/mes/service/MesProcessParamLimitService.java
  10. 12 1
      src/main/java/com/jeesite/modules/mes/service/MesProductGp12Service.java
  11. 1 3
      src/main/java/com/jeesite/modules/mes/web/MesBatchSnController.java
  12. 110 0
      src/main/java/com/jeesite/modules/mes/web/MesProcessParamLimitController.java
  13. 71 6
      src/main/java/com/jeesite/modules/mes/web/MesProductArcWeldingController.java
  14. 18 2
      src/main/java/com/jeesite/modules/mes/web/MesProductCmtController.java
  15. 6 1
      src/main/java/com/jeesite/modules/mes/web/MesProductProdController.java
  16. 5 0
      src/main/java/com/jeesite/modules/mes/web/MesProductQmController.java
  17. 7 1
      src/main/java/com/jeesite/modules/mes/web/MesWelderParamRecordController.java
  18. 6 1
      src/main/resources/static/common/common.css
  19. 202 0
      src/main/resources/views/modules/mes/mesProcessParamLimitForm.html
  20. 80 0
      src/main/resources/views/modules/mes/mesProcessParamLimitList.html
  21. 124 15
      src/main/resources/views/modules/mes/mesProductArcWeldingInfo.html
  22. 35 1
      src/main/resources/views/modules/mes/mesProductArcWeldingList.html
  23. 131 24
      src/main/resources/views/modules/mes/mesProductCmtInfo.html
  24. 11 2
      src/main/resources/views/modules/mes/mesProductCmtList.html
  25. 6 2
      src/main/resources/views/modules/mes/mesProductProdList.html
  26. 5 0
      src/main/resources/views/modules/mes/mesProductQmList.html
  27. 5 0
      src/main/resources/views/modules/mes/mesProductQmWhiteList.html
  28. 35 1
      src/main/resources/views/modules/mes/mesProductSpotWeldingList.html
  29. 5 0
      src/main/resources/views/modules/mes/mesWelderParamRecordList.html

+ 14 - 0
src/main/java/com/jeesite/modules/mes/dao/MesProcessParamLimitDao.java

@@ -0,0 +1,14 @@
+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.MesProcessParamLimit;
+
+/**
+ * 工艺参数上下限DAO接口
+ * @author mes
+ * @version 2026-09-07
+ */
+@MyBatisDao
+public interface MesProcessParamLimitDao extends CrudDao<MesProcessParamLimit> {
+}

+ 124 - 0
src/main/java/com/jeesite/modules/mes/entity/MesProcessParamLimit.java

@@ -0,0 +1,124 @@
+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 mes
+ * @version 2026-09-07
+ */
+@Table(name="mes_process_param_limit", alias="a", label="工艺参数上下限信息", columns={
+		@Column(name="id", attrName="id", label="id", isPK=true),
+		@Column(name="process_key", attrName="processKey", label="工艺编码", queryType=QueryType.LIKE),
+		@Column(name="process_name", attrName="processName", label="工艺名称", queryType=QueryType.LIKE),
+		@Column(name="param_code", attrName="paramCode", label="参数编码", queryType=QueryType.LIKE),
+		@Column(name="param_name", attrName="paramName", label="参数名称", queryType=QueryType.LIKE),
+		@Column(name="lower_limit", attrName="lowerLimit", label="下限", isUpdateForce=true),
+		@Column(name="upper_limit", attrName="upperLimit", label="上限", isUpdateForce=true),
+		@Column(name="unit", attrName="unit", label="单位", queryType=QueryType.LIKE),
+		@Column(name="remark", attrName="remark", 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),
+}, orderBy="a.update_date DESC, a.create_date DESC"
+)
+public class MesProcessParamLimit extends DataEntity<MesProcessParamLimit> {
+
+	private static final long serialVersionUID = 1L;
+
+	private String processKey;		// 工艺编码
+	private String processName;		// 工艺名称
+	private String paramCode;		// 参数编码
+	private String paramName;		// 参数名称
+	private String lowerLimit;		// 下限
+	private String upperLimit;		// 上限
+	private String unit;			// 单位
+	private String remark;			// 备注
+
+	public MesProcessParamLimit() {
+		this(null);
+	}
+
+	public MesProcessParamLimit(String id) {
+		super(id);
+	}
+
+	@Size(min=0, max=50, message="工艺编码长度不能超过 50 个字符")
+	public String getProcessKey() {
+		return processKey;
+	}
+
+	public void setProcessKey(String processKey) {
+		this.processKey = processKey;
+	}
+
+	@Size(min=0, max=100, message="工艺名称长度不能超过 100 个字符")
+	public String getProcessName() {
+		return processName;
+	}
+
+	public void setProcessName(String processName) {
+		this.processName = processName;
+	}
+
+	@Size(min=0, max=50, message="参数编码长度不能超过 50 个字符")
+	public String getParamCode() {
+		return paramCode;
+	}
+
+	public void setParamCode(String paramCode) {
+		this.paramCode = paramCode;
+	}
+
+	@Size(min=0, max=100, message="参数名称长度不能超过 100 个字符")
+	public String getParamName() {
+		return paramName;
+	}
+
+	public void setParamName(String paramName) {
+		this.paramName = paramName;
+	}
+
+	@Size(min=0, max=50, message="下限长度不能超过 50 个字符")
+	public String getLowerLimit() {
+		return lowerLimit;
+	}
+
+	public void setLowerLimit(String lowerLimit) {
+		this.lowerLimit = lowerLimit;
+	}
+
+	@Size(min=0, max=50, message="上限长度不能超过 50 个字符")
+	public String getUpperLimit() {
+		return upperLimit;
+	}
+
+	public void setUpperLimit(String upperLimit) {
+		this.upperLimit = upperLimit;
+	}
+
+	@Size(min=0, max=20, message="单位长度不能超过 20 个字符")
+	public String getUnit() {
+		return unit;
+	}
+
+	public void setUnit(String unit) {
+		this.unit = unit;
+	}
+
+	@Size(min=0, max=255, message="备注长度不能超过 255 个字符")
+	public String getRemark() {
+		return remark;
+	}
+
+	public void setRemark(String remark) {
+		this.remark = remark;
+	}
+}

+ 9 - 0
src/main/java/com/jeesite/modules/mes/entity/MesProductArcWelding.java

@@ -47,6 +47,7 @@ public class MesProductArcWelding extends DataEntity<MesProductArcWelding> {
 	private String content;		// 参数内容
 	private String oprnoTitle;	// 工位名称(关联工序表,支持模糊查询)
 	private String weldingCategory;	// 焊接类型:spot-点焊,arc-弧焊
+	private String limitFlag;	// 超限标记:1-超限 0-正常
 
 	@ExcelFields({
 		@ExcelField(title="id", attrName="id", align=Align.CENTER, sort=10),
@@ -132,5 +133,13 @@ public class MesProductArcWelding extends DataEntity<MesProductArcWelding> {
 			sqlMap.getWhere().and("b.title", QueryType.LIKE, "弧焊");
 		}
 	}
+
+	public String getLimitFlag() {
+		return limitFlag;
+	}
+
+	public void setLimitFlag(String limitFlag) {
+		this.limitFlag = limitFlag;
+	}
 	
 }

+ 10 - 1
src/main/java/com/jeesite/modules/mes/entity/MesProductCmt.java

@@ -32,6 +32,7 @@ public class MesProductCmt extends DataEntity<MesProductCmt> {
 	private String lineSn;		// 产线编号
 	private String content;		// 参数内容
 	private String oprnoTitle;	// 工位名称(非表字段,列表回填)
+	private String limitFlag;	// 超限标记:1-超限 0-正常
 
 	@ExcelFields({
 		@ExcelField(title="二维码", attrName="sn", align=Align.CENTER, sort=20),
@@ -91,5 +92,13 @@ public class MesProductCmt extends DataEntity<MesProductCmt> {
 	public void setOprnoTitle(String oprnoTitle) {
 		this.oprnoTitle = oprnoTitle;
 	}
+
+	public String getLimitFlag() {
+		return limitFlag;
+	}
+
+	public void setLimitFlag(String limitFlag) {
+		this.limitFlag = limitFlag;
+	}
 	
-}
+}

+ 10 - 1
src/main/java/com/jeesite/modules/mes/entity/MesProductProd.java

@@ -56,6 +56,7 @@ public class MesProductProd extends DataEntity<MesProductProd> {
 	private String serialNumber;
 	private Date dateTime;		// 拉铆时间
 	private String oldSn;
+	private String limitFlag;	// 超限标记:1-超限 0-正常
 
 	@ExcelFields({
 			@ExcelField(title="工件码", attrName="sn", align=Align.CENTER, sort=20),
@@ -205,6 +206,14 @@ public class MesProductProd extends DataEntity<MesProductProd> {
 		this.oldSn = oldSn;
 	}
 
+	public String getLimitFlag() {
+		return limitFlag;
+	}
+
+	public void setLimitFlag(String limitFlag) {
+		this.limitFlag = limitFlag;
+	}
+
 	public Date getDateTime_gte() {
 		return sqlMap.getWhere().getValue("date_time", QueryType.GTE);
 	}
@@ -220,4 +229,4 @@ public class MesProductProd extends DataEntity<MesProductProd> {
 	public void setDateTime_lte(Date DateTime) {
 		sqlMap.getWhere().and("date_time", QueryType.LTE, DateTime);
 	}
-}
+}

+ 9 - 0
src/main/java/com/jeesite/modules/mes/entity/MesProductQm.java

@@ -76,6 +76,7 @@ public class MesProductQm extends DataEntity<MesProductQm> {
 	private String title;
 	private String remark;
 	private String qmCategory;	// 气密类型:whitePart-白件气密,assembly-总成气密
+	private String limitFlag;	// 超限标记:1-超限 0-正常
 
 	@ExcelFields({
 		@ExcelField(title="序号", attrName="id", align=Align.CENTER, sort=10),
@@ -338,4 +339,12 @@ public class MesProductQm extends DataEntity<MesProductQm> {
 	public void setRemark(String remark) {
 		this.remark = remark;
 	}
+
+	public String getLimitFlag() {
+		return limitFlag;
+	}
+
+	public void setLimitFlag(String limitFlag) {
+		this.limitFlag = limitFlag;
+	}
 }

+ 9 - 0
src/main/java/com/jeesite/modules/mes/entity/MesWelderParamRecord.java

@@ -85,6 +85,7 @@ public class MesWelderParamRecord extends DataEntity<MesWelderParamRecord> {
 	private String weldPeakCurrent;		// 焊接峰值电流
 	private String temperPeakCurrent;	// 回火峰值电流
 	private Date recordTime;			// 采集时间
+	private String limitFlag;	// 超限标记:1-超限 0-正常
 
 	public MesWelderParamRecord() {
 		this(null);
@@ -338,4 +339,12 @@ public class MesWelderParamRecord extends DataEntity<MesWelderParamRecord> {
 		sqlMap.getWhere().and("record_time", QueryType.LTE, recordTime);
 	}
 
+	public String getLimitFlag() {
+		return limitFlag;
+	}
+
+	public void setLimitFlag(String limitFlag) {
+		this.limitFlag = limitFlag;
+	}
+
 }

+ 504 - 0
src/main/java/com/jeesite/modules/mes/service/MesProcessParamLimitCheckService.java

@@ -0,0 +1,504 @@
+package com.jeesite.modules.mes.service;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.jeesite.common.lang.StringUtils;
+import com.jeesite.modules.mes.entity.MesProcessParamLimit;
+import com.jeesite.modules.mes.entity.MesProductArcWelding;
+import com.jeesite.modules.mes.entity.MesProductCmt;
+import com.jeesite.modules.mes.entity.MesProductProd;
+import com.jeesite.modules.mes.entity.MesProductQm;
+import com.jeesite.modules.mes.entity.MesWelderParamRecord;
+import org.springframework.beans.BeanWrapperImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 工艺参数上下限校验Service
+ * @author mes
+ * @version 2026-09-07
+ */
+@Service
+public class MesProcessParamLimitCheckService {
+
+	private static final Map<String, String[]> ARC_PARAM_ALIASES = new HashMap<>();
+
+	static {
+		ARC_PARAM_ALIASES.put("current", new String[]{"current", "current1", "current2", "robot1Current", "robot2Current", "i1", "i2", "I1", "I2"});
+		ARC_PARAM_ALIASES.put("current1", new String[]{"current1", "robot1Current", "i1", "I1"});
+		ARC_PARAM_ALIASES.put("current2", new String[]{"current2", "robot2Current", "i2", "I2"});
+		ARC_PARAM_ALIASES.put("voltage", new String[]{"voltage", "voltage1", "voltage2", "robot1Voltage", "robot2Voltage", "v1", "v2", "V1", "V2"});
+		ARC_PARAM_ALIASES.put("voltage1", new String[]{"voltage1", "robot1Voltage", "v1", "V1"});
+		ARC_PARAM_ALIASES.put("voltage2", new String[]{"voltage2", "robot2Voltage", "v2", "V2"});
+		ARC_PARAM_ALIASES.put("speed", new String[]{"speed", "speed1", "speed2", "wireSpeed", "wireFeedSpeed", "sendWireSpeed", "robot1Speed", "robot2Speed", "feedSpeed"});
+		ARC_PARAM_ALIASES.put("speed1", new String[]{"speed1", "robot1Speed", "wireSpeed", "wireFeedSpeed", "sendWireSpeed"});
+		ARC_PARAM_ALIASES.put("speed2", new String[]{"speed2", "robot2Speed", "wireSpeed", "wireFeedSpeed", "sendWireSpeed"});
+	}
+
+	@Autowired
+	private MesProcessParamLimitService mesProcessParamLimitService;
+
+	public void markQm(List<MesProductQm> list, String qmCategory) {
+		Map<String, List<NormalizedLimit>> limitMap = loadLimitMap();
+		if (list == null || list.isEmpty()) {
+			return;
+		}
+		String processKey = normalizeProcessKey(qmCategory);
+		for (MesProductQm row : list) {
+			row.setLimitFlag(checkBeanRow(row, limitMap.get(processKey)) ? "1" : "0");
+		}
+	}
+
+	public void markProd(List<MesProductProd> list) {
+		Map<String, List<NormalizedLimit>> limitMap = loadLimitMap();
+		if (list == null || list.isEmpty()) {
+			return;
+		}
+		for (MesProductProd row : list) {
+			row.setLimitFlag(checkBeanRow(row, limitMap.get("prod")) ? "1" : "0");
+		}
+	}
+
+	public void markWelder(List<MesWelderParamRecord> list) {
+		Map<String, List<NormalizedLimit>> limitMap = loadLimitMap();
+		if (list == null || list.isEmpty()) {
+			return;
+		}
+		for (MesWelderParamRecord row : list) {
+			row.setLimitFlag(checkBeanRow(row, limitMap.get("welder")) ? "1" : "0");
+		}
+	}
+
+	public void markArc(List<MesProductArcWelding> list, String weldingCategory) {
+		markArc(list, weldingCategory, null);
+	}
+
+	public void markArc(List<MesProductArcWelding> list, String weldingCategory, String processName) {
+		Map<String, List<NormalizedLimit>> limitMap = loadLimitMap();
+		if (list == null || list.isEmpty()) {
+			return;
+		}
+		for (MesProductArcWelding row : list) {
+			String processKey = resolveArcProcessKey(row, weldingCategory, processName);
+			row.setLimitFlag(checkArcRow(row, limitMap.get(processKey)) ? "1" : "0");
+		}
+	}
+
+	public void markCmt(List<MesProductCmt> list) {
+		Map<String, List<NormalizedLimit>> limitMap = loadLimitMap();
+		if (list == null || list.isEmpty()) {
+			return;
+		}
+		List<NormalizedLimit> limits = limitMap.get("arc115");
+		for (MesProductCmt row : list) {
+			row.setLimitFlag(checkArcContent(row == null ? null : row.getContent(), limits) ? "1" : "0");
+		}
+	}
+
+	public List<ArcLimitRule> findArcLimitRules(MesProductArcWelding row, String weldingCategory) {
+		return findArcLimitRules(row, weldingCategory, null);
+	}
+
+	public List<ArcLimitRule> findArcLimitRules(MesProductArcWelding row, String weldingCategory, String processName) {
+		Map<String, List<NormalizedLimit>> limitMap = loadLimitMap();
+		List<NormalizedLimit> limits = limitMap.get(resolveArcProcessKey(row, weldingCategory, processName));
+		return toArcLimitRules(limits);
+	}
+
+	public List<ArcLimitRule> findCmtLimitRules() {
+		Map<String, List<NormalizedLimit>> limitMap = loadLimitMap();
+		return toArcLimitRules(limitMap.get("arc115"));
+	}
+
+	private List<ArcLimitRule> toArcLimitRules(List<NormalizedLimit> limits) {
+		List<ArcLimitRule> result = new ArrayList<>();
+		if (limits == null || limits.isEmpty()) {
+			return result;
+		}
+		for (NormalizedLimit limit : limits) {
+			result.add(new ArcLimitRule(limit.paramCode, limit.lowerLimit, limit.upperLimit));
+		}
+		return result;
+	}
+
+	public boolean checkArcRecord(MesProductArcWelding row, String weldingCategory) {
+		return checkArcRecord(row, weldingCategory, null);
+	}
+
+	public boolean checkArcRecord(MesProductArcWelding row, String weldingCategory, String processName) {
+		Map<String, List<NormalizedLimit>> limitMap = loadLimitMap();
+		return checkArcRow(row, limitMap.get(resolveArcProcessKey(row, weldingCategory, processName)));
+	}
+
+	public boolean checkCmtRecord(MesProductCmt row) {
+		Map<String, List<NormalizedLimit>> limitMap = loadLimitMap();
+		return checkArcContent(row == null ? null : row.getContent(), limitMap.get("arc115"));
+	}
+
+	private Map<String, List<NormalizedLimit>> loadLimitMap() {
+		Map<String, List<NormalizedLimit>> map = new HashMap<>();
+		for (MesProcessParamLimit limit : mesProcessParamLimitService.findEnabledList()) {
+			String processKey = normalizeProcessKey(StringUtils.defaultString(limit.getProcessKey(), limit.getProcessName()));
+			String paramCode = normalizeParamCode(StringUtils.defaultString(limit.getParamCode(), limit.getParamName()));
+			if (StringUtils.isBlank(processKey) || StringUtils.isBlank(paramCode)) {
+				continue;
+			}
+			NormalizedLimit normalized = new NormalizedLimit(processKey, paramCode, limit.getLowerLimit(), limit.getUpperLimit());
+			map.computeIfAbsent(processKey, k -> new ArrayList<>()).add(normalized);
+		}
+		return map;
+	}
+
+	private boolean checkBeanRow(Object row, List<NormalizedLimit> limits) {
+		if (row == null || limits == null || limits.isEmpty()) {
+			return false;
+		}
+		BeanWrapperImpl wrapper = new BeanWrapperImpl(row);
+		for (NormalizedLimit limit : limits) {
+			if (!wrapper.isReadableProperty(limit.paramCode)) {
+				continue;
+			}
+			Object value = wrapper.getPropertyValue(limit.paramCode);
+			if (isOutOfRange(value, limit)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	private boolean checkArcRow(MesProductArcWelding row, List<NormalizedLimit> limits) {
+		return checkArcContent(row == null ? null : row.getContent(), limits);
+	}
+
+	private boolean checkArcContent(String content, List<NormalizedLimit> limits) {
+		if (limits == null || limits.isEmpty() || StringUtils.isBlank(content)) {
+			return false;
+		}
+		for (JSONObject sample : parseArcSamples(content)) {
+			for (NormalizedLimit limit : limits) {
+				for (String value : readArcValues(sample, limit.paramCode)) {
+					if (isOutOfRange(value, limit)) {
+						return true;
+					}
+				}
+			}
+		}
+		return false;
+	}
+
+	private List<JSONObject> parseArcSamples(String content) {
+		List<JSONObject> result = new ArrayList<>();
+		try {
+			String text = StringUtils.trim(content);
+			if (StringUtils.isBlank(text)) {
+				return result;
+			}
+			if (text.startsWith("{")) {
+				JSONObject root = JSONObject.parseObject(text);
+				JSONArray samples = root == null ? null : root.getJSONArray("samples");
+				if (samples != null) {
+					for (int i = 0; i < samples.size(); i++) {
+						JSONObject sample = samples.getJSONObject(i);
+						if (sample != null) {
+							result.add(sample);
+						}
+					}
+				}
+				return result;
+			}
+			if (text.startsWith("[")) {
+				JSONArray arr = JSONArray.parseArray(text);
+				if (arr != null) {
+					for (int i = 0; i < arr.size(); i++) {
+						String record = arr.getString(i);
+						appendOldArcRecord(record, result);
+					}
+				}
+				return result;
+			}
+			appendOldArcRecord(text, result);
+			return result;
+		} catch (Exception ex) {
+			return result;
+		}
+	}
+
+	private void appendOldArcRecord(String record, List<JSONObject> result) {
+		if (StringUtils.isBlank(record)) {
+			return;
+		}
+		String valueText = StringUtils.trim(record);
+		if (valueText.startsWith("\"") && valueText.endsWith("\"") && valueText.length() > 1) {
+			valueText = StringUtils.trim(valueText.substring(1, valueText.length() - 1));
+		}
+		if (StringUtils.isBlank(valueText)) {
+			return;
+		}
+		String[] values = valueText.split("\\|");
+		if (values.length < 3) {
+			return;
+		}
+		JSONObject sample = new JSONObject();
+		for (int i = 0; i < values.length; i++) {
+			sample.put("v" + i, values[i]);
+		}
+		if (values.length >= 11) {
+			sample.put("robot1Voltage", values[0]);
+			sample.put("robot1Current", values[1]);
+			sample.put("robot1Speed", values[2]);
+			sample.put("robot2Voltage", values[5]);
+			sample.put("robot2Current", values[6]);
+			sample.put("robot2Speed", values[7]);
+			sample.put("time", values[values.length - 1]);
+		}
+		result.add(sample);
+	}
+
+	private List<String> readArcValues(JSONObject sample, String paramCode) {
+		List<String> values = new ArrayList<>();
+		String[] aliases = ARC_PARAM_ALIASES.get(paramCode);
+		if (aliases == null || aliases.length == 0) {
+			String value = sample.getString(paramCode);
+			if (StringUtils.isNotBlank(StringUtils.trim(value))) {
+				values.add(value.toString());
+			}
+			return values;
+		}
+		for (String alias : aliases) {
+			Object value = sample.get(alias);
+			if (value != null && StringUtils.isNotBlank(StringUtils.trim(value.toString()))) {
+				String text = value.toString();
+				if (!values.contains(text)) {
+					values.add(text);
+				}
+			}
+		}
+		return values;
+	}
+
+	private boolean isOutOfRange(Object value, NormalizedLimit limit) {
+		if (value == null || limit == null) {
+			return false;
+		}
+		return isOutOfRange(value.toString(), limit);
+	}
+
+	private boolean isOutOfRange(String value, NormalizedLimit limit) {
+		if (StringUtils.isBlank(value) || limit == null) {
+			return false;
+		}
+		BigDecimal actual;
+		try {
+			actual = new BigDecimal(normalizeNumberText(value));
+		} catch (Exception ex) {
+			return false;
+		}
+		BigDecimal lower = parseDecimal(limit.lowerLimit);
+		BigDecimal upper = parseDecimal(limit.upperLimit);
+		if (lower != null && upper != null && lower.compareTo(upper) > 0) {
+			BigDecimal tmp = lower;
+			lower = upper;
+			upper = tmp;
+		}
+		if (lower != null && actual.compareTo(lower) < 0) {
+			return true;
+		}
+		return upper != null && actual.compareTo(upper) > 0;
+	}
+
+	private BigDecimal parseDecimal(String value) {
+		if (StringUtils.isBlank(value)) {
+			return null;
+		}
+		try {
+			return new BigDecimal(normalizeNumberText(value));
+		} catch (Exception ex) {
+			return null;
+		}
+	}
+
+	private String normalizeNumberText(String value) {
+		if (value == null) {
+			return null;
+		}
+		String text = StringUtils.trim(value);
+		if (StringUtils.isBlank(text)) {
+			return text;
+		}
+		return text.replace('\u2212', '-')  // minus sign
+				.replace('\u2013', '-')    // en dash
+				.replace('\u2014', '-')    // em dash
+				.replace('\uFF0D', '-')    // full-width hyphen-minus
+				.replace('\uFE63', '-')    // small hyphen-minus
+				.replace('\uFF0B', '+')    // full-width plus
+				.replace(" ", "");
+	}
+
+	private String resolveArcProcessKey(MesProductArcWelding row, String weldingCategory) {
+		return resolveArcProcessKey(row, weldingCategory, null);
+	}
+
+	private String resolveArcProcessKey(MesProductArcWelding row, String weldingCategory, String processName) {
+		String category = normalizeProcessKey(processName);
+		if (!isArcProcessKey(category)) {
+			category = "";
+		}
+		if (StringUtils.isBlank(category)) {
+			category = normalizeProcessKey(weldingCategory);
+		}
+		if (StringUtils.isBlank(category) && row != null) {
+			category = normalizeProcessKey(row.getWeldingCategory());
+		}
+		if (StringUtils.isBlank(category)) {
+			return "arc";
+		}
+		if ("arc".equals(category)) {
+			return "arcHev";
+		}
+		if ("spot".equals(category)) {
+			return "spotHev";
+		}
+		return category;
+	}
+
+	private boolean isArcProcessKey(String processKey) {
+		return "arc115".equals(processKey) || "arcHev".equals(processKey)
+				|| "spot115".equals(processKey) || "spotHev".equals(processKey)
+				|| "arc".equals(processKey) || "spot".equals(processKey);
+	}
+
+	private String normalizeProcessKey(String value) {
+		if (StringUtils.isBlank(value)) {
+			return "";
+		}
+		String text = StringUtils.trim(value);
+		if ("whitePart".equalsIgnoreCase(text) || "白件气密".equals(text)) {
+			return "whitePart";
+		}
+		if ("assembly".equalsIgnoreCase(text) || "总成气密".equals(text)) {
+			return "assembly";
+		}
+		if ("prod".equalsIgnoreCase(text) || "拉铆".equals(text)) {
+			return "prod";
+		}
+		if ("welder".equalsIgnoreCase(text) || "凸焊".equals(text)) {
+			return "welder";
+		}
+		if ("arc115".equalsIgnoreCase(text) || "115弧焊".equals(text) || "115弧焊参数".equals(text)) {
+			return "arc115";
+		}
+		if ("arcHev".equalsIgnoreCase(text) || "HEV弧焊".equalsIgnoreCase(text) || "HEV弧焊参数".equals(text)) {
+			return "arcHev";
+		}
+		if ("spot115".equalsIgnoreCase(text) || "115点焊".equals(text) || "115点焊参数".equals(text)) {
+			return "spot115";
+		}
+		if ("spotHev".equalsIgnoreCase(text) || "HEV点焊".equalsIgnoreCase(text) || "HEV点焊参数".equals(text)) {
+			return "spotHev";
+		}
+		if ("spot".equalsIgnoreCase(text)) {
+			return "spot";
+		}
+		if ("arc".equalsIgnoreCase(text)) {
+			return "arc";
+		}
+		return text;
+	}
+
+	private String normalizeParamCode(String value) {
+		if (StringUtils.isBlank(value)) {
+			return "";
+		}
+		String text = StringUtils.trim(value);
+		if ("current1".equalsIgnoreCase(text) || "电流1".equals(text)) {
+			return "current1";
+		}
+		if ("current2".equalsIgnoreCase(text) || "电流2".equals(text)) {
+			return "current2";
+		}
+		if ("voltage1".equalsIgnoreCase(text) || "电压1".equals(text)) {
+			return "voltage1";
+		}
+		if ("voltage2".equalsIgnoreCase(text) || "电压2".equals(text)) {
+			return "voltage2";
+		}
+		if ("speed1".equalsIgnoreCase(text) || "送丝速度1".equals(text)) {
+			return "speed1";
+		}
+		if ("speed2".equalsIgnoreCase(text) || "送丝速度2".equals(text)) {
+			return "speed2";
+		}
+		if ("testPressure".equalsIgnoreCase(text) || "测试压".equals(text)) {
+			return "testPressure";
+		}
+		if ("leakVal".equalsIgnoreCase(text) || "泄漏值".equals(text)) {
+			return "leakVal";
+		}
+		if ("fout".equalsIgnoreCase(text) || "拉力".equals(text)) {
+			return "fout";
+		}
+		if ("sout".equalsIgnoreCase(text) || "行程".equals(text)) {
+			return "sout";
+		}
+		if ("current".equalsIgnoreCase(text) || "电流".equals(text)) {
+			return "current";
+		}
+		if ("voltage".equalsIgnoreCase(text) || "电压".equals(text)) {
+			return "voltage";
+		}
+		if ("speed".equalsIgnoreCase(text) || "送丝速度".equals(text)) {
+			return "speed";
+		}
+		if ("capVoltage".equalsIgnoreCase(text) || "电容电压".equals(text)) {
+			return "capVoltage";
+		}
+		if ("weldPeakCurrent".equalsIgnoreCase(text) || "焊接峰值电流".equals(text)) {
+			return "weldPeakCurrent";
+		}
+		return text;
+	}
+
+	private static class NormalizedLimit {
+		private final String processKey;
+		private final String paramCode;
+		private final String lowerLimit;
+		private final String upperLimit;
+
+		private NormalizedLimit(String processKey, String paramCode, String lowerLimit, String upperLimit) {
+			this.processKey = processKey;
+			this.paramCode = paramCode;
+			this.lowerLimit = lowerLimit;
+			this.upperLimit = upperLimit;
+		}
+	}
+
+	public static class ArcLimitRule {
+		private final String paramCode;
+		private final String lowerLimit;
+		private final String upperLimit;
+
+		public ArcLimitRule(String paramCode, String lowerLimit, String upperLimit) {
+			this.paramCode = paramCode;
+			this.lowerLimit = lowerLimit;
+			this.upperLimit = upperLimit;
+		}
+
+		public String getParamCode() {
+			return paramCode;
+		}
+
+		public String getLowerLimit() {
+			return lowerLimit;
+		}
+
+		public String getUpperLimit() {
+			return upperLimit;
+		}
+	}
+}

+ 59 - 0
src/main/java/com/jeesite/modules/mes/service/MesProcessParamLimitService.java

@@ -0,0 +1,59 @@
+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.dao.MesProcessParamLimitDao;
+import com.jeesite.modules.mes.entity.MesProcessParamLimit;
+
+/**
+ * 工艺参数上下限Service
+ * @author mes
+ * @version 2026-09-07
+ */
+@Service
+public class MesProcessParamLimitService extends CrudService<MesProcessParamLimitDao, MesProcessParamLimit> {
+
+	@Override
+	public MesProcessParamLimit get(MesProcessParamLimit mesProcessParamLimit) {
+		return super.get(mesProcessParamLimit);
+	}
+
+	@Override
+	public Page<MesProcessParamLimit> findPage(MesProcessParamLimit mesProcessParamLimit) {
+		return super.findPage(mesProcessParamLimit);
+	}
+
+	@Override
+	public List<MesProcessParamLimit> findList(MesProcessParamLimit mesProcessParamLimit) {
+		return super.findList(mesProcessParamLimit);
+	}
+
+	public List<MesProcessParamLimit> findEnabledList() {
+		MesProcessParamLimit query = new MesProcessParamLimit();
+		query.setStatus(MesProcessParamLimit.STATUS_NORMAL);
+		return findList(query);
+	}
+
+	@Override
+	@Transactional
+	public void save(MesProcessParamLimit mesProcessParamLimit) {
+		super.save(mesProcessParamLimit);
+	}
+
+	@Override
+	@Transactional
+	public void updateStatus(MesProcessParamLimit mesProcessParamLimit) {
+		super.updateStatus(mesProcessParamLimit);
+	}
+
+	@Override
+	@Transactional
+	public void delete(MesProcessParamLimit mesProcessParamLimit) {
+		super.delete(mesProcessParamLimit);
+	}
+}

+ 12 - 1
src/main/java/com/jeesite/modules/mes/service/MesProductGp12Service.java

@@ -7,6 +7,7 @@ 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.MesProductGp12;
 import com.jeesite.modules.mes.dao.MesProductGp12Dao;
@@ -44,6 +45,16 @@ public class MesProductGp12Service extends CrudService<MesProductGp12Dao, MesPro
 		return mesProductGp12Dao.findInfo(mesProductGp12);
 	}
 
+	public MesProductGp12 findLatestBySn(String sn) {
+		if (StringUtils.isBlank(sn)) {
+			return null;
+		}
+		MesProductGp12 query = new MesProductGp12();
+		query.setSn(sn);
+		query.getSqlMap().getOrder().setOrderBy("a.create_date DESC, a.id DESC");
+		return mesProductGp12Dao.findInfo(query);
+	}
+
 	/**
 	 * 查询分页数据
 	 * @param mesProductGp12 查询条件
@@ -144,4 +155,4 @@ public class MesProductGp12Service extends CrudService<MesProductGp12Dao, MesPro
 		super.delete(mesProductGp12);
 	}
 	
-}
+}

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

@@ -237,9 +237,7 @@ public class MesBatchSnController extends BaseController {
 		if (StringUtils.isNotBlank(mesProduct1.getBatchSn())) {
 			return renderResult(Global.FALSE, text("该工件已绑定批次码"));
 		}
-		MesProductGp12 mesProductGp12 = new MesProductGp12();
-		mesProductGp12.setSn(sn);
-		MesProductGp12 latestGp12 = mesProductGp12Service.findInfo(mesProductGp12);
+		MesProductGp12 latestGp12 = mesProductGp12Service.findLatestBySn(sn);
 		if (ObjectUtils.isEmpty(latestGp12)) {
 			return renderResult(Global.FALSE, text("请先进行GP12检查"));
 		}

+ 110 - 0
src/main/java/com/jeesite/modules/mes/web/MesProcessParamLimitController.java

@@ -0,0 +1,110 @@
+package com.jeesite.modules.mes.web;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+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.MesProcessParamLimit;
+import com.jeesite.modules.mes.service.MesProcessParamLimitService;
+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;
+
+/**
+ * 工艺参数上下限Controller
+ * @author mes
+ * @version 2026-09-07
+ */
+@Controller
+@RequestMapping(value = "${adminPath}/mes/mesProcessParamLimit")
+public class MesProcessParamLimitController extends BaseController {
+
+	@Autowired
+	private MesProcessParamLimitService mesProcessParamLimitService;
+
+	@ModelAttribute
+	public MesProcessParamLimit get(String id, boolean isNewRecord) {
+		return mesProcessParamLimitService.get(id, isNewRecord);
+	}
+
+	@RequiresPermissions("mes:mesProcessParamLimit:view")
+	@RequestMapping(value = {"list", ""})
+	public String list(MesProcessParamLimit mesProcessParamLimit, Model model) {
+		model.addAttribute("mesProcessParamLimit", mesProcessParamLimit);
+		return "modules/mes/mesProcessParamLimitList";
+	}
+
+	@RequiresPermissions("mes:mesProcessParamLimit:view")
+	@RequestMapping(value = "listData")
+	@ResponseBody
+	public Page<MesProcessParamLimit> listData(MesProcessParamLimit mesProcessParamLimit, HttpServletRequest request,
+			HttpServletResponse response) {
+		mesProcessParamLimit.setPage(new Page<>(request, response));
+		return mesProcessParamLimitService.findPage(mesProcessParamLimit);
+	}
+
+	@RequiresPermissions("mes:mesProcessParamLimit:view")
+	@RequestMapping(value = "form")
+	public String form(MesProcessParamLimit mesProcessParamLimit, Model model) {
+		model.addAttribute("mesProcessParamLimit", mesProcessParamLimit);
+		return "modules/mes/mesProcessParamLimitForm";
+	}
+
+	@RequiresPermissions("mes:mesProcessParamLimit:edit")
+	@PostMapping(value = "save")
+	@ResponseBody
+	public String save(@Validated MesProcessParamLimit mesProcessParamLimit) {
+		trimFields(mesProcessParamLimit);
+		if (StringUtils.isBlank(mesProcessParamLimit.getStatus())) {
+			mesProcessParamLimit.setStatus(MesProcessParamLimit.STATUS_NORMAL);
+		}
+		mesProcessParamLimitService.save(mesProcessParamLimit);
+		return renderResult(Global.TRUE, text("保存参数上下限成功!"));
+	}
+
+	@RequiresPermissions("mes:mesProcessParamLimit:edit")
+	@RequestMapping(value = "disable")
+	@ResponseBody
+	public String disable(MesProcessParamLimit mesProcessParamLimit) {
+		mesProcessParamLimit.setStatus(MesProcessParamLimit.STATUS_DISABLE);
+		mesProcessParamLimitService.updateStatus(mesProcessParamLimit);
+		return renderResult(Global.TRUE, text("停用参数上下限成功"));
+	}
+
+	@RequiresPermissions("mes:mesProcessParamLimit:edit")
+	@RequestMapping(value = "enable")
+	@ResponseBody
+	public String enable(MesProcessParamLimit mesProcessParamLimit) {
+		mesProcessParamLimit.setStatus(MesProcessParamLimit.STATUS_NORMAL);
+		mesProcessParamLimitService.updateStatus(mesProcessParamLimit);
+		return renderResult(Global.TRUE, text("启用参数上下限成功"));
+	}
+
+	@RequiresPermissions("mes:mesProcessParamLimit:edit")
+	@RequestMapping(value = "delete")
+	@ResponseBody
+	public String delete(MesProcessParamLimit mesProcessParamLimit) {
+		mesProcessParamLimitService.delete(mesProcessParamLimit);
+		return renderResult(Global.TRUE, text("删除参数上下限成功!"));
+	}
+
+	private void trimFields(MesProcessParamLimit mesProcessParamLimit) {
+		mesProcessParamLimit.setProcessKey(StringUtils.trim(mesProcessParamLimit.getProcessKey()));
+		mesProcessParamLimit.setProcessName(StringUtils.trim(mesProcessParamLimit.getProcessName()));
+		mesProcessParamLimit.setParamCode(StringUtils.trim(mesProcessParamLimit.getParamCode()));
+		mesProcessParamLimit.setParamName(StringUtils.trim(mesProcessParamLimit.getParamName()));
+		mesProcessParamLimit.setLowerLimit(StringUtils.trim(mesProcessParamLimit.getLowerLimit()));
+		mesProcessParamLimit.setUpperLimit(StringUtils.trim(mesProcessParamLimit.getUpperLimit()));
+		mesProcessParamLimit.setUnit(StringUtils.trim(mesProcessParamLimit.getUnit()));
+		mesProcessParamLimit.setRemark(StringUtils.trim(mesProcessParamLimit.getRemark()));
+	}
+}

+ 71 - 6
src/main/java/com/jeesite/modules/mes/web/MesProductArcWeldingController.java

@@ -33,6 +33,7 @@ import org.springframework.util.ObjectUtils;
 import org.springframework.web.multipart.MultipartFile;
 import com.jeesite.common.web.BaseController;
 import com.jeesite.modules.mes.entity.MesProductArcWelding;
+import com.jeesite.modules.mes.service.MesProcessParamLimitCheckService;
 import com.jeesite.modules.mes.service.MesLineProcessService;
 import com.jeesite.modules.mes.service.MesProductArcWeldingService;
 
@@ -50,6 +51,9 @@ public class MesProductArcWeldingController extends BaseController {
 
 	@Autowired
 	private MesLineProcessService mesLineProcessService;
+
+	@Autowired
+	private MesProcessParamLimitCheckService mesProcessParamLimitCheckService;
 	
 	/**
 	 * 获取数据
@@ -64,9 +68,11 @@ public class MesProductArcWeldingController extends BaseController {
 		if (StringUtils.isNotBlank(id) && StringUtils.isBlank(entity.getContent())) {
 			MesProductArcWelding latest = mesProductArcWeldingService.findLatestById(id);
 			if (latest != null && StringUtils.isNotBlank(latest.getContent())) {
+				applyLimitFlag(latest);
 				return latest;
 			}
 		}
+		applyLimitFlag(entity);
 		return entity;
 	}
 	
@@ -75,8 +81,8 @@ public class MesProductArcWeldingController extends BaseController {
 	 */
 	@RequiresPermissions("mes:mesProductArcWelding:view")
 	@RequestMapping(value = {"list", ""})
-	public String list(MesProductArcWelding mesProductArcWelding, Model model) {
-		return listSpot(mesProductArcWelding, model);
+	public String list(MesProductArcWelding mesProductArcWelding, String processName, Model model) {
+		return listSpot(mesProductArcWelding, processName, model);
 	}
 
 	/**
@@ -84,9 +90,10 @@ public class MesProductArcWeldingController extends BaseController {
 	 */
 	@RequiresPermissions("mes:mesProductArcWelding:view")
 	@RequestMapping(value = "listSpot")
-	public String listSpot(MesProductArcWelding mesProductArcWelding, Model model) {
+	public String listSpot(MesProductArcWelding mesProductArcWelding, String processName, Model model) {
 		applyDefaultCreateDate(mesProductArcWelding);
 		mesProductArcWelding.setWeldingCategory("spot");
+		model.addAttribute("processName", defaultWeldingProcessName("spot", processName));
 		model.addAttribute("mesProductArcWelding", mesProductArcWelding);
 		return "modules/mes/mesProductSpotWeldingList";
 	}
@@ -96,9 +103,10 @@ public class MesProductArcWeldingController extends BaseController {
 	 */
 	@RequiresPermissions("mes:mesProductArcWelding:view")
 	@RequestMapping(value = "listArc")
-	public String listArc(MesProductArcWelding mesProductArcWelding, Model model) {
+	public String listArc(MesProductArcWelding mesProductArcWelding, String processName, Model model) {
 		applyDefaultCreateDate(mesProductArcWelding);
 		mesProductArcWelding.setWeldingCategory("arc");
+		model.addAttribute("processName", defaultWeldingProcessName("arc", processName));
 		model.addAttribute("mesProductArcWelding", mesProductArcWelding);
 		return "modules/mes/mesProductArcWeldingList";
 	}
@@ -120,11 +128,12 @@ public class MesProductArcWeldingController extends BaseController {
 	@RequiresPermissions("mes:mesProductArcWelding:view")
 	@RequestMapping(value = "listData")
 	@ResponseBody
-	public Page<MesProductArcWelding> listData(MesProductArcWelding mesProductArcWelding, HttpServletRequest request, HttpServletResponse response) {
+	public Page<MesProductArcWelding> listData(MesProductArcWelding mesProductArcWelding, String processName, HttpServletRequest request, HttpServletResponse response) {
 		mesProductArcWelding.setPage(new Page<>(request, response));
 		Page<MesProductArcWelding> page = mesProductArcWeldingService.findPage(mesProductArcWelding);
 		fillOprnoTitle(page.getList());
 		fillCreateByFromProductRecord(page.getList());
+		mesProcessParamLimitCheckService.markArc(page.getList(), mesProductArcWelding.getWeldingCategory(), processName);
 		return page;
 	}
 
@@ -269,7 +278,12 @@ public class MesProductArcWeldingController extends BaseController {
 	 */
 	@RequiresPermissions("mes:mesProductArcWelding:view")
 	@GetMapping("info")
-	public String info(MesProductArcWelding mesProductArcWelding, Model model) {
+	public String info(MesProductArcWelding mesProductArcWelding, String processName, Model model) {
+		String category = resolveWeldingCategory(mesProductArcWelding);
+		String processContext = defaultWeldingProcessName(category, processName);
+		applyLimitFlag(mesProductArcWelding, processContext);
+		model.addAttribute("arcLimitRules", mesProcessParamLimitCheckService.findArcLimitRules(mesProductArcWelding, category, processContext));
+		model.addAttribute("processName", processContext);
 		model.addAttribute("mesProductArcWelding", mesProductArcWelding);
 		return "modules/mes/mesProductArcWeldingInfo";
 	}
@@ -310,5 +324,56 @@ public class MesProductArcWeldingController extends BaseController {
 		return renderResult(Global.TRUE, text("操作成功!"));
 	}
 
+	private void applyLimitFlag(MesProductArcWelding mesProductArcWelding) {
+		applyLimitFlag(mesProductArcWelding, null);
+	}
+
+	private void applyLimitFlag(MesProductArcWelding mesProductArcWelding, String processName) {
+		if (mesProductArcWelding == null) {
+			return;
+		}
+		String category = resolveWeldingCategory(mesProductArcWelding);
+		mesProductArcWelding.setLimitFlag(mesProcessParamLimitCheckService.checkArcRecord(mesProductArcWelding, category, processName) ? "1" : "0");
+	}
+
+	private String resolveWeldingCategory(MesProductArcWelding mesProductArcWelding) {
+		if (mesProductArcWelding == null) {
+			return "";
+		}
+		String category = StringUtils.trim(mesProductArcWelding.getWeldingCategory());
+		if (StringUtils.isNotBlank(category)) {
+			return category;
+		}
+		String title = StringUtils.trim(mesProductArcWelding.getOprnoTitle());
+		if (StringUtils.contains(title, "点焊")) {
+			return "spot";
+		}
+		if (StringUtils.contains(title, "弧焊")) {
+			return "arc";
+		}
+		String content = StringUtils.trim(mesProductArcWelding.getContent());
+		if (StringUtils.contains(content, "\"weldingType\":\"spot\"")) {
+			return "spot";
+		}
+		if (StringUtils.contains(content, "\"weldingType\":\"arc\"")) {
+			return "arc";
+		}
+		return "";
+	}
+
+	private String defaultWeldingProcessName(String weldingCategory, String processName) {
+		String text = StringUtils.trim(processName);
+		if (StringUtils.isNotBlank(text)) {
+			return text;
+		}
+		if ("spot".equals(weldingCategory)) {
+			return "HEV点焊";
+		}
+		if ("arc".equals(weldingCategory)) {
+			return "HEV弧焊";
+		}
+		return "";
+	}
+
 	
 }

+ 18 - 2
src/main/java/com/jeesite/modules/mes/web/MesProductCmtController.java

@@ -32,6 +32,7 @@ import com.jeesite.common.utils.excel.annotation.ExcelField.Type;
 import org.springframework.web.multipart.MultipartFile;
 import com.jeesite.common.web.BaseController;
 import com.jeesite.modules.mes.entity.MesProductCmt;
+import com.jeesite.modules.mes.service.MesProcessParamLimitCheckService;
 import com.jeesite.modules.mes.service.MesProductCmtService;
 
 /**
@@ -48,13 +49,18 @@ public class MesProductCmtController extends BaseController {
 
 	@Autowired
 	private MesLineProcessService mesLineProcessService;
+
+	@Autowired
+	private MesProcessParamLimitCheckService mesProcessParamLimitCheckService;
 	
 	/**
 	 * 获取数据
 	 */
 	@ModelAttribute
 	public MesProductCmt get(String id, boolean isNewRecord) {
-		return mesProductCmtService.get(id, isNewRecord);
+		MesProductCmt mesProductCmt = mesProductCmtService.get(id, isNewRecord);
+		applyLimitFlag(mesProductCmt);
+		return mesProductCmt;
 	}
 	
 	/**
@@ -80,6 +86,7 @@ public class MesProductCmtController extends BaseController {
 		mesProductCmt.setPage(new Page<>(request, response));
 		Page<MesProductCmt> page = mesProductCmtService.findPage(mesProductCmt);
 		fillOprnoTitle(page.getList());
+		mesProcessParamLimitCheckService.markCmt(page.getList());
 		return page;
 	}
 
@@ -226,8 +233,17 @@ public class MesProductCmtController extends BaseController {
 	@RequiresPermissions("mes:mesProductCmt:view")
 	@GetMapping("info")
 	public String info(MesProductCmt mesProductCmt, Model model) {
+		applyLimitFlag(mesProductCmt);
+		model.addAttribute("arcLimitRules", mesProcessParamLimitCheckService.findCmtLimitRules());
 		model.addAttribute("mesProductCmt", mesProductCmt);
 		return "modules/mes/mesProductCmtInfo";
 	}
+
+	private void applyLimitFlag(MesProductCmt mesProductCmt) {
+		if (mesProductCmt == null) {
+			return;
+		}
+		mesProductCmt.setLimitFlag(mesProcessParamLimitCheckService.checkCmtRecord(mesProductCmt) ? "1" : "0");
+	}
 	
-}
+}

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

@@ -31,6 +31,7 @@ import com.jeesite.common.utils.excel.annotation.ExcelField.Type;
 import org.springframework.web.multipart.MultipartFile;
 import com.jeesite.common.web.BaseController;
 import com.jeesite.modules.mes.entity.MesProductProd;
+import com.jeesite.modules.mes.service.MesProcessParamLimitCheckService;
 import com.jeesite.modules.mes.service.MesProductProdService;
 
 /**
@@ -44,6 +45,9 @@ public class MesProductProdController extends BaseController {
 
 	@Autowired
 	private MesProductProdService mesProductProdService;
+
+	@Autowired
+	private MesProcessParamLimitCheckService mesProcessParamLimitCheckService;
 	
 	/**
 	 * 获取数据
@@ -75,6 +79,7 @@ public class MesProductProdController extends BaseController {
 	public Page<MesProductProd> listData(MesProductProd mesProductProd, HttpServletRequest request, HttpServletResponse response) {
 		mesProductProd.setPage(new Page<>(request, response));
 		Page<MesProductProd> page = mesProductProdService.findPage(mesProductProd);
+		mesProcessParamLimitCheckService.markProd(page.getList());
 		return page;
 	}
 
@@ -188,4 +193,4 @@ public class MesProductProdController extends BaseController {
 		return renderResult(Global.TRUE, text("操作成功!"));
 	}
 
-}
+}

+ 5 - 0
src/main/java/com/jeesite/modules/mes/web/MesProductQmController.java

@@ -34,6 +34,7 @@ import com.jeesite.common.utils.excel.annotation.ExcelField.Type;
 import org.springframework.web.multipart.MultipartFile;
 import com.jeesite.common.web.BaseController;
 import com.jeesite.modules.mes.entity.MesProductQm;
+import com.jeesite.modules.mes.service.MesProcessParamLimitCheckService;
 import com.jeesite.modules.mes.service.MesProductQmService;
 
 /**
@@ -48,6 +49,9 @@ public class MesProductQmController extends BaseController {
 	@Autowired
 	private MesProductQmService mesProductQmService;
 
+	@Autowired
+	private MesProcessParamLimitCheckService mesProcessParamLimitCheckService;
+
 	/**
 	 * 获取数据
 	 */
@@ -103,6 +107,7 @@ public class MesProductQmController extends BaseController {
 				}
 			}
 		}
+		mesProcessParamLimitCheckService.markQm(page.getList(), mesProductQm.getQmCategory());
 		Utf8DisplayUtils.repairGraph(page);
 		return page;
 	}

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

@@ -25,6 +25,7 @@ import com.jeesite.common.web.BaseController;
 import com.jeesite.modules.mes.entity.MesWelderParamRecord;
 import com.jeesite.modules.mes.req.MesSearchDateReq;
 import com.jeesite.modules.mes.service.MesLogService;
+import com.jeesite.modules.mes.service.MesProcessParamLimitCheckService;
 import com.jeesite.modules.mes.service.MesWelderParamRecordService;
 import com.jeesite.modules.mes.util.CommonUitl;
 
@@ -43,6 +44,9 @@ public class MesWelderParamRecordController extends BaseController {
 	@Autowired
 	private MesLogService mesLogService;
 
+	@Autowired
+	private MesProcessParamLimitCheckService mesProcessParamLimitCheckService;
+
 	@ModelAttribute
 	public MesWelderParamRecord get(String id, boolean isNewRecord) {
 		return mesWelderParamRecordService.get(id, isNewRecord);
@@ -68,7 +72,9 @@ public class MesWelderParamRecordController extends BaseController {
 	public Page<MesWelderParamRecord> listData(MesWelderParamRecord mesWelderParamRecord,
 			HttpServletRequest request, HttpServletResponse response) {
 		mesWelderParamRecord.setPage(new Page<>(request, response));
-		return mesWelderParamRecordService.findPage(mesWelderParamRecord);
+		Page<MesWelderParamRecord> page = mesWelderParamRecordService.findPage(mesWelderParamRecord);
+		mesProcessParamLimitCheckService.markWelder(page.getList());
+		return page;
 	}
 
 	/**

+ 6 - 1
src/main/resources/static/common/common.css

@@ -15,6 +15,11 @@
     white-space: pre-wrap!important;
 }
 
+.ui-jqgrid tr.limit-exceed td,
+.limit-exceed td{
+    background-color: #fff3cd !important;
+}
+
 .width-sn{
 
-}
+}

+ 202 - 0
src/main/resources/views/modules/mes/mesProcessParamLimitForm.html

@@ -0,0 +1,202 @@
+<% 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(mesProcessParamLimit.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="${mesProcessParamLimit}" action="${ctx}/mes/mesProcessParamLimit/save" method="post" class="form-horizontal">
+			<div class="box-body">
+				<div class="form-unit">${text('基本信息')}</div>
+				<#form:hidden path="id"/>
+				<#form:hidden path="processKey"/>
+				<#form:hidden path="paramCode"/>
+				<div class="row">
+					<div class="col-xs-6">
+						<div class="form-group">
+							<label class="control-label col-sm-4">${text('工艺名称')}:</label>
+							<div class="col-sm-8">
+								<select id="processNameSelect" name="processName" class="form-control"></select>
+							</div>
+						</div>
+					</div>
+					<div class="col-xs-6">
+						<div class="form-group">
+							<label class="control-label col-sm-4">${text('参数名称')}:</label>
+							<div class="col-sm-8">
+								<select id="paramNameSelect" name="paramName" class="form-control"></select>
+							</div>
+						</div>
+					</div>
+					<div class="col-xs-6">
+						<div class="form-group">
+							<label class="control-label col-sm-4">${text('下限')}:</label>
+							<div class="col-sm-8">
+								<#form:input path="lowerLimit" maxlength="50" class="form-control"/>
+							</div>
+						</div>
+					</div>
+					<div class="col-xs-6">
+						<div class="form-group">
+							<label class="control-label col-sm-4">${text('上限')}:</label>
+							<div class="col-sm-8">
+								<#form:input path="upperLimit" maxlength="50" 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:mesProcessParamLimit: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>
+const processLimitOptions = [
+	{key:'whitePart', name:'白件气密', params:[
+		{code:'testPressure', name:'测试压'},
+		{code:'leakVal', name:'泄漏值'}
+	]},
+	{key:'assembly', name:'总成气密', params:[
+		{code:'testPressure', name:'测试压'},
+		{code:'leakVal', name:'泄漏值'}
+	]},
+	{key:'prod', name:'拉铆', params:[
+		{code:'fout', name:'拉力'},
+		{code:'sout', name:'行程'}
+	]},
+	{key:'welder', name:'凸焊', params:[
+		{code:'capVoltage', name:'电容电压'},
+		{code:'weldPeakCurrent', name:'焊接峰值电流'}
+	]},
+	{key:'arc115', name:'115弧焊', params:[
+		{code:'voltage1', name:'电压1'},
+		{code:'current1', name:'电流1'},
+		{code:'speed1', name:'送丝速度1'},
+		{code:'voltage2', name:'电压2'},
+		{code:'current2', name:'电流2'},
+		{code:'speed2', name:'送丝速度2'}
+	]},
+	{key:'arcHev', name:'HEV弧焊', params:[
+		{code:'voltage1', name:'电压1'},
+		{code:'current1', name:'电流1'},
+		{code:'voltage2', name:'电压2'},
+		{code:'current2', name:'电流2'}
+	]},
+	{key:'spotHev', name:'HEV点焊', params:[
+		{code:'current1', name:'电流1'},
+		{code:'current2', name:'电流2'}
+	]}/*,
+	{key:'spot115', name:'115点焊', params:[
+		{code:'current1', name:'电流1'},
+		{code:'current2', name:'电流2'}
+	]}*/
+];
+
+const findProcess = (key, name) => processLimitOptions.find(item => item.key === key || item.name === name);
+const findParam = (processKey, code, name) => {
+	const process = findProcess(processKey);
+	const params = process ? process.params : [];
+	return params.find(item => item.code === code || item.name === name);
+};
+
+function fillProcessOptions() {
+	const processSelect = $('#processNameSelect');
+	processSelect.empty().append('<option value="">请选择工艺名称</option>');
+	processLimitOptions.forEach(item => {
+		processSelect.append('<option value="'+item.name+'" data-code="'+item.key+'">'+item.name+'</option>');
+	});
+}
+
+function fillParamOptions(processKey, selectedParamName) {
+	const paramSelect = $('#paramNameSelect');
+	const process = findProcess(processKey);
+	paramSelect.empty().append('<option value="">请选择参数名称</option>');
+	if (!process) {
+		return;
+	}
+	process.params.forEach(item => {
+		paramSelect.append('<option value="'+item.name+'" data-code="'+item.code+'">'+item.name+'</option>');
+	});
+	if (selectedParamName) {
+		paramSelect.val(selectedParamName);
+	}
+}
+
+function syncProcessLimitFields() {
+	const processSelect = $('#processNameSelect');
+	const paramSelect = $('#paramNameSelect');
+	const processName = processSelect.val() || '';
+	const processItem = processLimitOptions.find(item => item.name === processName);
+	$('#processKey').val(processItem ? processItem.key : '');
+
+	const paramName = paramSelect.val() || '';
+	const paramItem = processItem ? processItem.params.find(item => item.name === paramName) : null;
+	$('#paramCode').val(paramItem ? paramItem.code : '');
+}
+
+function initProcessLimitSelects() {
+	fillProcessOptions();
+	const currentProcessName = '${mesProcessParamLimit.processName!}';
+	const currentProcessKey = '${mesProcessParamLimit.processKey!}';
+	const currentParamName = '${mesProcessParamLimit.paramName!}';
+	const currentParamCode = '${mesProcessParamLimit.paramCode!}';
+	const processItem = findProcess(currentProcessKey, currentProcessName);
+	const processName = processItem ? processItem.name : currentProcessName;
+	$('#processNameSelect').val(processName);
+	fillParamOptions(processItem ? processItem.key : currentProcessKey, currentParamName);
+	if (currentParamName && currentParamCode && $('#paramNameSelect option').filter(function(){
+		return $(this).val() === currentParamName;
+	}).length === 0) {
+		$('#paramNameSelect').append('<option value="'+currentParamName+'" data-code="'+currentParamCode+'">'+currentParamName+'</option>');
+		$('#paramNameSelect').val(currentParamName);
+	}
+	if (!currentParamName && currentParamCode && processItem) {
+		const paramItem = findParam(processItem.key, currentParamCode, '');
+		if (paramItem) {
+			$('#paramNameSelect').val(paramItem.name);
+		}
+	}
+	syncProcessLimitFields();
+}
+
+$('#processNameSelect').change(function(){
+	const processName = $(this).val() || '';
+	const processItem = processLimitOptions.find(item => item.name === processName);
+	fillParamOptions(processItem ? processItem.key : '', '');
+	syncProcessLimitFields();
+});
+
+$('#paramNameSelect').change(function(){
+	syncProcessLimitFields();
+});
+
+$("#inputForm").validate({
+	submitHandler: function(form){
+		syncProcessLimitFields();
+		js.ajaxSubmitForm($(form), function(data){
+			js.showMessage(data.message);
+			if(data.result == Global.TRUE){
+				js.closeCurrentTabPage(function(contentWindow){
+					contentWindow.page();
+				});
+			}
+		}, "json");
+	}
+});
+
+initProcessLimitSelects();
+</script>

+ 80 - 0
src/main/resources/views/modules/mes/mesProcessParamLimitList.html

@@ -0,0 +1,80 @@
+<% layout('/layouts/default.html', {title: '工艺参数上下限管理', libs: ['dataGrid']}){ %>
+<style>
+	.ui-jqgrid tr.limit-exceed td {
+		background-color: #fff3cd !important;
+	}
+</style>
+<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:mesProcessParamLimit:edit')){ %>
+					<a href="${ctx}/mes/mesProcessParamLimit/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="${mesProcessParamLimit}" action="${ctx}/mes/mesProcessParamLimit/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="processName" maxlength="100" class="form-control width-120"/>
+					</div>
+				</div>
+				<div class="form-group">
+					<label class="control-label">${text('参数名称')}:</label>
+					<div class="control-inline">
+						<#form:input path="paramName" maxlength="100" 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:'processName', index:'a.process_name', width:140, align:"center"},
+		{header:'${text("参数名称")}', name:'paramName', index:'a.param_name', width:140, align:"center"},
+		{header:'${text("下限")}', name:'lowerLimit', index:'a.lower_limit', width:120, align:"center"},
+		{header:'${text("上限")}', name:'upperLimit', index:'a.upper_limit', width:120, align:"center"},
+		{header:'${text("状态")}', name:'status', index:'a.status', width:80, align:"center", formatter: function(val){
+			return js.getDictLabel(${@DictUtils.getDictListJson('sys_search_status')}, val, '${text("未知")}', true);
+		}},
+		{header:'${text("创建时间")}', name:'createDate', index:'a.create_date', width:160, align:"center"},
+		{header:'${text("操作")}', name:'actions', width:120, formatter: function(val, obj, row){
+			var actions = [];
+			actions.push('<a href="${ctx}/mes/mesProcessParamLimit/form?id='+row.id+'" class="btnList" title="${text("编辑")}"><i class="fa fa-pencil"></i></a>&nbsp;');
+			if (row.status == Global.STATUS_NORMAL){
+				actions.push('<a href="${ctx}/mes/mesProcessParamLimit/disable?id='+row.id+'" class="btnList" title="${text("停用")}" data-confirm="${text("确认要停用该参数上下限吗?")}"><i class="glyphicon glyphicon-ban-circle"></i></a>&nbsp;');
+			} else if (row.status == Global.STATUS_DISABLE){
+				actions.push('<a href="${ctx}/mes/mesProcessParamLimit/enable?id='+row.id+'" class="btnList" title="${text("启用")}" data-confirm="${text("确认要启用该参数上下限吗?")}"><i class="glyphicon glyphicon-ok-circle"></i></a>&nbsp;');
+			}
+			actions.push('<a href="${ctx}/mes/mesProcessParamLimit/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>

+ 124 - 15
src/main/resources/views/modules/mes/mesProductArcWeldingInfo.html

@@ -37,10 +37,10 @@
                         </tr>
                     </thead>
                     <tbody>
-                        <tr v-for="(item,index) in myData" :key="index">
+                        <tr v-for="(item,index) in myData" :key="index" :class="{'limit-exceed': item.limitFlag === '1'}">
                             <template v-for="n in welderCount">
-                                <td v-if="showVoltageChart">{{ item['V' + n] }}</td>
-                                <td>{{ item['I' + n] }}</td>
+                                <td v-if="showVoltageChart" :class="{'limit-cell-exceed': item.limitCellMap && item.limitCellMap['V' + n]}">{{ item['V' + n] }}</td>
+                                <td :class="{'limit-cell-exceed': item.limitCellMap && item.limitCellMap['I' + n]}">{{ item['I' + n] }}</td>
                                 <td v-if="!isNewJson">{{ item['code' + n] }}</td>
                                 <td v-if="!isNewJson">{{ item['JOB' + n] }}</td>
                             </template>
@@ -66,6 +66,15 @@
     </div>
 </div>
 <% } %>
+<style>
+    .limit-exceed td {
+        background-color: #fff3cd !important;
+    }
+    .limit-cell-exceed {
+        color: #856404 !important;
+        font-weight: 600;
+    }
+</style>
 <script>
     $("#inputForm").validate({
         submitHandler: function (form) {
@@ -90,9 +99,9 @@
             for (let i = 0; i < welderCount; i++) {
                 const baseIndex = i * 5;
                 if (baseIndex + 4 >= values.length) break;
-                obj['V' + (i + 1)] = parseFloat(values[baseIndex]) || 0;
-                obj['I' + (i + 1)] = parseFloat(values[baseIndex + 1]) || 0;
-                obj['speed' + (i + 1)] = parseFloat(values[baseIndex + 2]) || 0;
+                obj['V' + (i + 1)] = parseNumber(values[baseIndex]);
+                obj['I' + (i + 1)] = parseNumber(values[baseIndex + 1]);
+                obj['speed' + (i + 1)] = parseNumber(values[baseIndex + 2]);
                 obj['code' + (i + 1)] = parseInt(values[baseIndex + 3], 10) || 0;
                 obj['JOB' + (i + 1)] = parseInt(values[baseIndex + 4], 10) || 0;
             }
@@ -131,6 +140,22 @@
         return text;
     };
 
+    const normalizeNumberText = (value) => {
+        if (value === null || value === undefined) return '';
+        return String(value)
+            .trim()
+            .replace(/[\u2212\u2013\u2014\uFF0D\uFE63]/g, '-')
+            .replace(/\uFF0B/g, '+')
+            .replace(/\s+/g, '');
+    };
+
+    const parseNumber = (value) => {
+        const text = normalizeNumberText(value);
+        if (!text) return 0;
+        const result = Number(text);
+        return Number.isFinite(result) ? result : 0;
+    };
+
     const parseJsonContent = (content) => {
         const text = normalizeContent(content);
         if (!text.startsWith('{')) return null;
@@ -142,6 +167,74 @@
         }
     };
 
+    const buildLimitRuleMap = (rules) => {
+        const map = {};
+        (rules || []).forEach(rule => {
+            if (rule && rule.paramCode) {
+                map[rule.paramCode] = rule;
+            }
+        });
+        return map;
+    };
+
+    const getArcFieldsByParam = (paramCode) => {
+        if (paramCode === 'voltage1') return ['V1'];
+        if (paramCode === 'voltage2') return ['V2'];
+        if (paramCode === 'current1') return ['I1'];
+        if (paramCode === 'current2') return ['I2'];
+        if (paramCode === 'speed1') return ['speed1'];
+        if (paramCode === 'speed2') return ['speed2'];
+        if (paramCode === 'voltage') return ['V1', 'V2'];
+        if (paramCode === 'current') return ['I1', 'I2'];
+        if (paramCode === 'speed') return ['speed1', 'speed2'];
+        return [];
+    };
+
+    const parseLimitNumber = (value) => {
+        const text = normalizeNumberText(value);
+        if (!text) return null;
+        const result = Number(text);
+        return Number.isFinite(result) ? result : null;
+    };
+
+    const isOutOfRange = (value, rule) => {
+        if (!rule) return false;
+        const actual = parseLimitNumber(value);
+        if (actual === null) return false;
+        let lower = parseLimitNumber(rule.lowerLimit);
+        let upper = parseLimitNumber(rule.upperLimit);
+        if (lower !== null && upper !== null && lower > upper) {
+            const tmp = lower;
+            lower = upper;
+            upper = tmp;
+        }
+        if (lower !== null && actual < lower) {
+            return true;
+        }
+        return upper !== null && actual > upper;
+    };
+
+    const decorateArcRows = (rows, rules) => {
+        const ruleMap = buildLimitRuleMap(rules);
+        return (rows || []).map(row => {
+            const limitCellMap = {};
+            let limitFlag = '0';
+            Object.keys(ruleMap).forEach(paramCode => {
+                const rule = ruleMap[paramCode];
+                getArcFieldsByParam(paramCode).forEach(field => {
+                    if (isOutOfRange(row[field], rule)) {
+                        limitCellMap[field] = true;
+                        limitFlag = '1';
+                    }
+                });
+            });
+            return Object.assign({}, row, {
+                limitFlag: limitFlag,
+                limitCellMap: limitCellMap
+            });
+        });
+    };
+
     const getWelderCount = (content) => {
         if (!content || content === '' || content === '[]') return 2;
         try {
@@ -150,8 +243,8 @@
             if (json) {
                 const first = json.samples && json.samples.length > 0 ? json.samples[0] : {};
                 let count = 0;
-                if (first.robot1Current !== undefined || first.robot1Voltage !== undefined || first.robot1Pressure !== undefined) count++;
-                if (first.robot2Current !== undefined || first.robot2Voltage !== undefined || first.robot2Pressure !== undefined) count++;
+                if (first.robot1Current !== undefined || first.robot1Voltage !== undefined || first.robot1Speed !== undefined || first.robot1Pressure !== undefined) count++;
+                if (first.robot2Current !== undefined || first.robot2Voltage !== undefined || first.robot2Speed !== undefined || first.robot2Pressure !== undefined) count++;
                 return count || 2;
             }
             let strArr;
@@ -179,10 +272,12 @@
         const samples = json.samples || [];
         return samples.map(sample => {
             const obj = {};
-            obj.V1 = parseFloat(sample.robot1Voltage || 0) || 0;
-            obj.I1 = parseFloat(sample.robot1Current || 0) || 0;
-            obj.V2 = parseFloat(sample.robot2Voltage || 0) || 0;
-            obj.I2 = parseFloat(sample.robot2Current || 0) || 0;
+            obj.V1 = parseNumber(sample.robot1Voltage);
+            obj.I1 = parseNumber(sample.robot1Current);
+            obj.speed1 = parseNumber(sample.robot1Speed);
+            obj.V2 = parseNumber(sample.robot2Voltage);
+            obj.I2 = parseNumber(sample.robot2Current);
+            obj.speed2 = parseNumber(sample.robot2Speed);
             obj.code1 = 0;
             obj.JOB1 = '';
             obj.code2 = 0;
@@ -220,13 +315,15 @@
         el: '#app',
         data: {
             content: ${toJson(mesProductArcWelding.content!'')},
+            limitFlag: ${toJson(mesProductArcWelding.limitFlag!'0')},
+            limitRules: ${toJson(arcLimitRules![])},
             welderCount: 2,
             vChart: null,
             iChart: null
         },
         computed: {
             myData() {
-                return parseContent(this.content);
+                return decorateArcRows(parseContent(this.content), this.limitRules);
             },
             contentJson() {
                 return parseJsonContent(this.content);
@@ -278,7 +375,13 @@
                     legendData.push(this.firstMetricName + i);
                     const arr = [];
                     for (let j = 0; j < this.myData.length; j++) {
-                        arr.push(this.myData[j]['V' + i]);
+                        const row = this.myData[j];
+                        const value = row['V' + i];
+                        arr.push(row.limitFlag === '1' || (row.limitCellMap && row.limitCellMap['V' + i]) ? {
+                            value: value,
+                            itemStyle: { color: '#d39e00' },
+                            symbolSize: 10
+                        } : value);
                     }
                     series.push({
                         name: this.firstMetricName + i,
@@ -307,7 +410,13 @@
                     legendData.push(this.secondMetricName + i);
                     const arr = [];
                     for (let j = 0; j < this.myData.length; j++) {
-                        arr.push(this.myData[j]['I' + i]);
+                        const row = this.myData[j];
+                        const value = row['I' + i];
+                        arr.push(row.limitFlag === '1' || (row.limitCellMap && row.limitCellMap['I' + i]) ? {
+                            value: value,
+                            itemStyle: { color: '#d39e00' },
+                            symbolSize: 10
+                        } : value);
                     }
                     series.push({
                         name: this.secondMetricName + i,

+ 35 - 1
src/main/resources/views/modules/mes/mesProductArcWeldingList.html

@@ -1,4 +1,9 @@
 <% layout('/layouts/default.html', {title: '弧焊参数管理', libs: ['dataGrid']}){ %>
+<style>
+	.ui-jqgrid tr.limit-exceed td {
+		background-color: #fff3cd !important;
+	}
+</style>
 <div class="main-content">
 	<div class="box box-main">
 		<div class="box-header">
@@ -15,6 +20,7 @@
 			<#form:form id="searchForm" model="${mesProductArcWelding}" action="${ctx}/mes/mesProductArcWelding/listData" method="post" class="form-inline hide"
 					data-page-no="${parameter.pageNo}" data-page-size="${parameter.pageSize}" data-order-by="${parameter.orderBy}">
 				<input type="hidden" name="weldingCategory" value="arc"/>
+				<input type="hidden" name="processName" id="processName" value="${processName!}"/>
 				<div class="form-group">
 					<label class="control-label">${text('产线编号')}:</label>
 					<div class="control-inline">
@@ -61,6 +67,29 @@
 </div>
 <% } %>
 <script>
+function getCurrentMenuTitle() {
+	const $menuRoot = (window.top && window.top.$ && window.top !== window) ? window.top.$ : $;
+	let title = '';
+	$menuRoot('#leftMenu li.active a, #topMenu li.active a').each(function(){
+		const text = $.trim($(this).attr('data-title') || $(this).children('span').first().text() || $(this).text() || '');
+		if (text) {
+			title = text;
+		}
+	});
+	return title;
+}
+
+function initProcessName() {
+	const serverProcessName = ${toJson(processName!'')};
+	const queryProcessName = new URLSearchParams(window.location.search).get('processName') || '';
+	const menuTitle = getCurrentMenuTitle();
+	const hashTitle = decodeURIComponent((window.location.hash || '').split('#').pop() || '').trim();
+	const knownNames = ['arc115', 'arcHev', '115弧焊', 'HEV弧焊', '115弧焊参数', 'HEV弧焊参数'];
+	const resolved = (knownNames.indexOf(menuTitle) >= 0 ? menuTitle : '') || serverProcessName || queryProcessName || (knownNames.indexOf(hashTitle) >= 0 ? hashTitle : '');
+	$('#processName').val(resolved);
+}
+initProcessName();
+
 $('#dataGrid').dataGrid({
 	searchForm: $("#searchForm"),
 	columnModel: [
@@ -72,11 +101,16 @@ $('#dataGrid').dataGrid({
 		{header:'${text("创建时间")}', name:'createDate', index:'a.create_date', width:160, align:"center"},
 		{header:'${text("操作")}', name:'actions', width:100, align:"center", formatter: function(val, obj, row, act){
 			var actions = [];
-			actions.push('<a href="${ctx}/mes/mesProductArcWelding/info?id='+row.id+'&createDate='+encodeURIComponent(row.createDate||'')+'" class="btnList" title="${text("详情")}"><i class="fa fa-fw fa-eye"></i></a>&nbsp;');
+			actions.push('<a href="${ctx}/mes/mesProductArcWelding/info?id='+row.id+'&createDate='+encodeURIComponent(row.createDate||'')+'&processName='+encodeURIComponent($("#processName").val()||'')+'" class="btnList" title="${text("详情")}"><i class="fa fa-fw fa-eye"></i></a>&nbsp;');
 			return actions.join('');
 		}}
 	],
 	ajaxSuccess: function(data){
+		$.each((data && data.list) || [], function(k, v){
+			if (v && v.limitFlag == '1'){
+				$('#' + $.jgrid.jqID(v.id)).addClass('limit-exceed');
+			}
+		});
 	}
 });
 </script>

+ 131 - 24
src/main/resources/views/modules/mes/mesProductCmtInfo.html

@@ -41,15 +41,15 @@
                         </tr>
                     </thead>
                     <tbody>
-                        <tr v-for="(item,index) in myData" :key="index">
-                            <td>{{ item.V1 }}</td>
-                            <td>{{ item.I1 }}</td>
-                            <td>{{ item.speed1 }}</td>
+                        <tr v-for="(item,index) in myData" :key="index" :class="{'limit-exceed': item.limitFlag === '1'}">
+                            <td :class="{'limit-cell-exceed': item.limitCellMap && item.limitCellMap.V1}">{{ item.V1 }}</td>
+                            <td :class="{'limit-cell-exceed': item.limitCellMap && item.limitCellMap.I1}">{{ item.I1 }}</td>
+                            <td :class="{'limit-cell-exceed': item.limitCellMap && item.limitCellMap.speed1}">{{ item.speed1 }}</td>
                             <td>{{ item.code1 }}</td>
                             <td>{{ item.JOB1 }}</td>
-                            <td>{{ item.V2 }}</td>
-                            <td>{{ item.I2 }}</td>
-                            <td>{{ item.speed2 }}</td>
+                            <td :class="{'limit-cell-exceed': item.limitCellMap && item.limitCellMap.V2}">{{ item.V2 }}</td>
+                            <td :class="{'limit-cell-exceed': item.limitCellMap && item.limitCellMap.I2}">{{ item.I2 }}</td>
+                            <td :class="{'limit-cell-exceed': item.limitCellMap && item.limitCellMap.speed2}">{{ item.speed2 }}</td>
                             <td>{{ item.code2 }}</td>
                             <td>{{ item.JOB2 }}</td>
                             <td> {{ item.createDate }}</td>
@@ -74,6 +74,15 @@
     </div>
 </div>
 <% } %>
+<style>
+    .limit-exceed td {
+        background-color: #fff3cd !important;
+    }
+    .limit-cell-exceed {
+        color: #856404 !important;
+        font-weight: 600;
+    }
+</style>
 <script>
     $("#inputForm").validate({
         submitHandler: function (form) {
@@ -91,18 +100,55 @@
 <script src="${ctxStatic}/echarts/4.2/echarts.min.js"></script>
 <script src="${ctxStatic}/common/elementui2.15.5/vue-2.6.14.js"></script>
 <script>
+    const normalizeNumberText = (value) => {
+        if (value === null || value === undefined) return '';
+        return String(value)
+            .trim()
+            .replace(/[\u2212\u2013\u2014\uFF0D\uFE63]/g, '-')
+            .replace(/\uFF0B/g, '+')
+            .replace(/\s+/g, '');
+    };
+
+    const parseNumber = (value) => {
+        const text = normalizeNumberText(value);
+        if (!text) return 0;
+        const result = Number(text);
+        return Number.isFinite(result) ? result : 0;
+    };
+
+    const parseLimitNumber = (value) => {
+        const text = normalizeNumberText(value);
+        if (!text) return null;
+        const result = Number(text);
+        return Number.isFinite(result) ? result : null;
+    };
+
+    const parseContent = (content) => {
+        if (!content || content === '[]') return [];
+        const text = String(content).trim();
+        try {
+            if (text.startsWith('[') || text.startsWith('"')) {
+                const parsed = JSON.parse(text);
+                return Array.isArray(parsed) ? parsed : [];
+            }
+        } catch (e) {
+            console.error('解析content出错:', e, text);
+        }
+        return text.slice(2, -2).split('","');
+    };
+
     const convertToArrayOfObjects = (arr) => {
         return arr.map(str => {
             const values = str.split('|');
             return {
-                V1: parseFloat(values[0]),         // 电压1
-                I1: parseFloat(values[1]),         // 电流1
-                speed1: parseFloat(values[2]),    // 送丝速度1
+                V1: parseNumber(values[0]),       // 电压1
+                I1: parseNumber(values[1]),       // 电流1
+                speed1: parseNumber(values[2]),   // 送丝速度1
                 code1: parseInt(values[3], 10),   // 错误码1
                 JOB1: parseInt(values[4], 10),    // JOB号1
-                V2: parseFloat(values[5]),         // JOB号1
-                I2: parseFloat(values[6]),         // 电压2
-                speed2: parseFloat(values[7]),    // 电流2
+                V2: parseNumber(values[5]),       // 电压2
+                I2: parseNumber(values[6]),       // 电流2
+                speed2: parseNumber(values[7]),   // 送丝速度2
                 code2: parseInt(values[8], 10),   // 错误码2
                 JOB2: parseInt(values[9], 10),    // JOB号2
                 createDate: values[10]             // 记录时间
@@ -110,21 +156,82 @@
         });
     };
 
+    const buildLimitRuleMap = (rules) => {
+        const map = {};
+        (rules || []).forEach(rule => {
+            if (rule && rule.paramCode) {
+                map[rule.paramCode] = rule;
+            }
+        });
+        return map;
+    };
+
+    const getArcFieldsByParam = (paramCode) => {
+        if (paramCode === 'voltage1') return ['V1'];
+        if (paramCode === 'voltage2') return ['V2'];
+        if (paramCode === 'current1') return ['I1'];
+        if (paramCode === 'current2') return ['I2'];
+        if (paramCode === 'speed1') return ['speed1'];
+        if (paramCode === 'speed2') return ['speed2'];
+        if (paramCode === 'voltage') return ['V1', 'V2'];
+        if (paramCode === 'current') return ['I1', 'I2'];
+        if (paramCode === 'speed') return ['speed1', 'speed2'];
+        return [];
+    };
+
+    const isOutOfRange = (value, rule) => {
+        if (!rule) return false;
+        const actual = parseLimitNumber(value);
+        if (actual === null) return false;
+        let lower = parseLimitNumber(rule.lowerLimit);
+        let upper = parseLimitNumber(rule.upperLimit);
+        if (lower !== null && upper !== null && lower > upper) {
+            const tmp = lower;
+            lower = upper;
+            upper = tmp;
+        }
+        if (lower !== null && actual < lower) {
+            return true;
+        }
+        return upper !== null && actual > upper;
+    };
+
+    const decorateArcRows = (rows, rules) => {
+        const ruleMap = buildLimitRuleMap(rules);
+        return (rows || []).map(row => {
+            const limitCellMap = {};
+            let limitFlag = '0';
+            Object.keys(ruleMap).forEach(paramCode => {
+                const rule = ruleMap[paramCode];
+                getArcFieldsByParam(paramCode).forEach(field => {
+                    if (isOutOfRange(row[field], rule)) {
+                        limitCellMap[field] = true;
+                        limitFlag = '1';
+                    }
+                });
+            });
+            return Object.assign({}, row, {
+                limitFlag: limitFlag,
+                limitCellMap: limitCellMap
+            });
+        });
+    };
+
     new Vue({
         el: '#app',
         data: {
-            content: '${mesProductCmt.content}',
+            content: ${toJson(mesProductCmt.content!'')},
+            limitRules: ${toJson(arcLimitRules![])},
             vChart: null,
             iChart: null,
             speedChart: null
         },
         computed: {
             myData() {
-                const strArr = this.content.slice(2, -2).split('","')
-                return convertToArrayOfObjects(strArr)
+                return decorateArcRows(convertToArrayOfObjects(parseContent(this.content)), this.limitRules)
             },
             V1Arr() {
-                arr = []
+                const arr = []
                 for (let i = 0; i < this.myData.length; i++) {
                     const {V1} = this.myData[i]
                     arr.push(V1)
@@ -132,7 +239,7 @@
                 return arr
             },
             V2Arr() {
-                arr = []
+                const arr = []
                 for (let i = 0; i < this.myData.length; i++) {
                     const {V2} = this.myData[i]
                     arr.push(V2)
@@ -140,7 +247,7 @@
                 return arr
             },
             I1Arr() {
-                arr = []
+                const arr = []
                 for (let i = 0; i < this.myData.length; i++) {
                     const {I1} = this.myData[i]
                     arr.push(I1)
@@ -148,7 +255,7 @@
                 return arr
             },
             I2Arr() {
-                arr = []
+                const arr = []
                 for (let i = 0; i < this.myData.length; i++) {
                     const {I2} = this.myData[i]
                     arr.push(I2)
@@ -156,7 +263,7 @@
                 return arr
             },
             timeArr() {
-                arr = []
+                const arr = []
                 for (let i = 0; i < this.myData.length; i++) {
                     const {createDate} = this.myData[i]
                     arr.push(createDate.slice(14))
@@ -164,7 +271,7 @@
                 return arr
             },
             speed1Arr() {
-                arr = []
+                const arr = []
                 for (let i = 0; i < this.myData.length; i++) {
                     const {speed1} = this.myData[i]
                     arr.push(speed1)
@@ -172,7 +279,7 @@
                 return arr
             },
             speed2Arr() {
-                arr = []
+                const arr = []
                 for (let i = 0; i < this.myData.length; i++) {
                     const {speed2} = this.myData[i]
                     arr.push(speed2)
@@ -347,4 +454,4 @@
             console.log(this.myData)
         }
     })
-</script>
+</script>

+ 11 - 2
src/main/resources/views/modules/mes/mesProductCmtList.html

@@ -1,4 +1,9 @@
 <% layout('/layouts/default.html', {title: '弧焊过程参数管理', libs: ['dataGrid']}){ %>
+<style>
+	.ui-jqgrid tr.limit-exceed td {
+		background-color: #fff3cd !important;
+	}
+</style>
 <div class="main-content">
 	<div class="box box-main">
 		<div class="box-header">
@@ -82,7 +87,11 @@ $('#dataGrid').dataGrid({
 	],
 	// 加载成功后执行事件
 	ajaxSuccess: function(data){
-		
+		$.each((data && data.list) || [], function(k, v){
+			if (v && v.limitFlag == '1'){
+				$('#' + $.jgrid.jqID(v.id)).addClass('limit-exceed');
+			}
+		});
 	}
 });
 </script><script>
@@ -138,4 +147,4 @@ $('#btnImport').click(function(){
 		</div>
 	</div>
 </form>
-//--></script>
+//--></script>

+ 6 - 2
src/main/resources/views/modules/mes/mesProductProdList.html

@@ -111,7 +111,11 @@ $('#dataGrid').dataGrid({
 	frozenCols: true,
 	// 加载成功后执行事件
 	ajaxSuccess: function(data){
-		
+		$.each((data && data.list) || [], function(k, v){
+			if (v && v.limitFlag == '1'){
+				$('#' + $.jgrid.jqID(v.id)).addClass('limit-exceed');
+			}
+		});
 	}
 });
 </script><script>
@@ -167,4 +171,4 @@ $('#btnImport').click(function(){
 		</div>
 	</div>
 </form>
-//--></script>
+//--></script>

+ 5 - 0
src/main/resources/views/modules/mes/mesProductQmList.html

@@ -84,6 +84,11 @@ $('#dataGrid').dataGrid({
 	],
 	frozenCols: true,
 	ajaxSuccess: function(data){
+		$.each((data && data.list) || [], function(k, v){
+			if (v && v.limitFlag == '1'){
+				$('#' + $.jgrid.jqID(v.id)).addClass('limit-exceed');
+			}
+		});
 	}
 });
 </script>

+ 5 - 0
src/main/resources/views/modules/mes/mesProductQmWhiteList.html

@@ -84,6 +84,11 @@ $('#dataGrid').dataGrid({
 	],
 	frozenCols: true,
 	ajaxSuccess: function(data){
+		$.each((data && data.list) || [], function(k, v){
+			if (v && v.limitFlag == '1'){
+				$('#' + $.jgrid.jqID(v.id)).addClass('limit-exceed');
+			}
+		});
 	}
 });
 </script>

+ 35 - 1
src/main/resources/views/modules/mes/mesProductSpotWeldingList.html

@@ -1,4 +1,9 @@
 <% layout('/layouts/default.html', {title: '点焊参数管理', libs: ['dataGrid']}){ %>
+<style>
+	.ui-jqgrid tr.limit-exceed td {
+		background-color: #fff3cd !important;
+	}
+</style>
 <div class="main-content">
 	<div class="box box-main">
 		<div class="box-header">
@@ -15,6 +20,7 @@
 			<#form:form id="searchForm" model="${mesProductArcWelding}" action="${ctx}/mes/mesProductArcWelding/listData" method="post" class="form-inline hide"
 					data-page-no="${parameter.pageNo}" data-page-size="${parameter.pageSize}" data-order-by="${parameter.orderBy}">
 				<input type="hidden" name="weldingCategory" value="spot"/>
+				<input type="hidden" name="processName" id="processName" value="${processName!}"/>
 				<div class="form-group">
 					<label class="control-label">${text('产线编号')}:</label>
 					<div class="control-inline">
@@ -61,6 +67,29 @@
 </div>
 <% } %>
 <script>
+function getCurrentMenuTitle() {
+	const $menuRoot = (window.top && window.top.$ && window.top !== window) ? window.top.$ : $;
+	let title = '';
+	$menuRoot('#leftMenu li.active a, #topMenu li.active a').each(function(){
+		const text = $.trim($(this).attr('data-title') || $(this).children('span').first().text() || $(this).text() || '');
+		if (text) {
+			title = text;
+		}
+	});
+	return title;
+}
+
+function initProcessName() {
+	const serverProcessName = ${toJson(processName!'')};
+	const queryProcessName = new URLSearchParams(window.location.search).get('processName') || '';
+	const menuTitle = getCurrentMenuTitle();
+	const hashTitle = decodeURIComponent((window.location.hash || '').split('#').pop() || '').trim();
+	const knownNames = ['spot115', 'spotHev', '115点焊', 'HEV点焊', '115点焊参数', 'HEV点焊参数'];
+	const resolved = (knownNames.indexOf(menuTitle) >= 0 ? menuTitle : '') || serverProcessName || queryProcessName || (knownNames.indexOf(hashTitle) >= 0 ? hashTitle : '');
+	$('#processName').val(resolved);
+}
+initProcessName();
+
 $('#dataGrid').dataGrid({
 	searchForm: $("#searchForm"),
 	columnModel: [
@@ -72,11 +101,16 @@ $('#dataGrid').dataGrid({
 		{header:'${text("创建时间")}', name:'createDate', index:'a.create_date', width:160, align:"center"},
 		{header:'${text("操作")}', name:'actions', width:100, align:"center", formatter: function(val, obj, row, act){
 			var actions = [];
-			actions.push('<a href="${ctx}/mes/mesProductArcWelding/info?id='+row.id+'&createDate='+encodeURIComponent(row.createDate||'')+'" class="btnList" title="${text("详情")}"><i class="fa fa-fw fa-eye"></i></a>&nbsp;');
+			actions.push('<a href="${ctx}/mes/mesProductArcWelding/info?id='+row.id+'&createDate='+encodeURIComponent(row.createDate||'')+'&processName='+encodeURIComponent($("#processName").val()||'')+'" class="btnList" title="${text("详情")}"><i class="fa fa-fw fa-eye"></i></a>&nbsp;');
 			return actions.join('');
 		}}
 	],
 	ajaxSuccess: function(data){
+		$.each((data && data.list) || [], function(k, v){
+			if (v && v.limitFlag == '1'){
+				$('#' + $.jgrid.jqID(v.id)).addClass('limit-exceed');
+			}
+		});
 	}
 });
 </script>

+ 5 - 0
src/main/resources/views/modules/mes/mesWelderParamRecordList.html

@@ -91,6 +91,11 @@ $('#dataGrid').dataGrid({
 		}}
 	],
 	ajaxSuccess: function(data){
+		$.each((data && data.list) || [], function(k, v){
+			if (v && v.limitFlag == '1'){
+				$('#' + $.jgrid.jqID(v.id)).addClass('limit-exceed');
+			}
+		});
 	}
 });
 </script>