diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/common/MesXslFormulaSpecEditLogDiffUtil.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/common/MesXslFormulaSpecEditLogDiffUtil.java index 0283a566..43cb79df 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/common/MesXslFormulaSpecEditLogDiffUtil.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/common/MesXslFormulaSpecEditLogDiffUtil.java @@ -16,6 +16,10 @@ import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.jeecg.modules.xslmes.entity.MesXslFormulaSpec; import org.jeecg.modules.xslmes.entity.MesXslFormulaSpecLine; +import org.jeecg.modules.xslmes.entity.MesXslMixingSpecDownStep; +import org.jeecg.modules.xslmes.entity.MesXslMixingSpecMaterial; +import org.jeecg.modules.xslmes.entity.MesXslMixingSpecStep; +import org.jeecg.modules.xslmes.entity.MesXslMixingSpecTcu; import org.jeecg.modules.xslmes.vo.MesXslFormulaSpecEditChangeItemVO; import org.jeecg.modules.xslmes.vo.MesXslMixingSpecPage; @@ -82,6 +86,35 @@ public final class MesXslFormulaSpecEditLogDiffUtil { return toJson(snapshot); } + //update-begin---author:cursor ---date:20260612 for:【XSLMES-20260612-A03】混炼示方历史快照反序列化----------- + public static MesXslMixingSpecPage parseMixingSnapshotJson(String snapshotJson) { + if (StringUtils.isBlank(snapshotJson)) { + return null; + } + JSONObject root = parseObject(snapshotJson); + if (root == null) { + return null; + } + JSONObject mainObj = root.getJSONObject(SECTION_MAIN); + if (mainObj == null) { + return null; + } + MesXslMixingSpecPage page = mainObj.toJavaObject(MesXslMixingSpecPage.class); + page.setMaterialList(parseSnapshotList(root.getJSONArray(SECTION_MATERIAL), MesXslMixingSpecMaterial.class)); + page.setStepList(parseSnapshotList(root.getJSONArray(SECTION_STEP), MesXslMixingSpecStep.class)); + page.setDownStepList(parseSnapshotList(root.getJSONArray(SECTION_DOWN_STEP), MesXslMixingSpecDownStep.class)); + page.setTcuList(parseSnapshotList(root.getJSONArray(SECTION_TCU), MesXslMixingSpecTcu.class)); + return page; + } + + private static List parseSnapshotList(JSONArray array, Class clazz) { + if (array == null || array.isEmpty()) { + return new ArrayList<>(); + } + return array.toJavaList(clazz); + } + //update-end---author:cursor ---date:20260612 for:【XSLMES-20260612-A03】混炼示方历史快照反序列化----------- + public static List compare( String specType, String beforeSnapshot, String afterSnapshot) { List items = new ArrayList<>(); diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslMixingSpecController.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslMixingSpecController.java index f35e9ef5..8af66b6c 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslMixingSpecController.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslMixingSpecController.java @@ -20,6 +20,7 @@ import org.jeecg.common.util.oConvertUtils; import org.jeecg.modules.xslmes.entity.MesXslMixingSpec; import org.jeecg.modules.xslmes.service.IMesXslMixingSpecService; import org.jeecg.modules.xslmes.vo.MesXslMixingSpecPage; +import org.jeecg.modules.xslmes.vo.MesXslMixingSpecSmallWeighRangeVO; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -175,6 +176,17 @@ public class MesXslMixingSpecController extends JeecgController updateSmallWeighRange(@RequestBody MesXslMixingSpecSmallWeighRangeVO vo) { + mesXslMixingSpecService.updateSmallWeighRange(vo); + return Result.OK("保存成功"); + } + //update-end---author:cursor ---date:20260612 for:【XSLMES-20260612-A02】混炼示方小料称重范围设置----------- + @RequiresPermissions("xslmes:mes_xsl_mixing_spec:exportXls") @RequestMapping(value = "/exportXls") public ModelAndView exportXls(HttpServletRequest request, MesXslMixingSpec model) { diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslMixingSpecHistoryController.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslMixingSpecHistoryController.java new file mode 100644 index 00000000..a550ab57 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslMixingSpecHistoryController.java @@ -0,0 +1,49 @@ +package org.jeecg.modules.xslmes.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.jeecg.common.api.vo.Result; +import org.jeecg.modules.xslmes.entity.MesXslMixingSpecHistory; +import org.jeecg.modules.xslmes.service.IMesXslMixingSpecHistoryService; +import org.jeecg.modules.xslmes.vo.MesXslMixingSpecPage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * 混炼示方历史记录 + */ +@Tag(name = "MES混炼示方历史记录") +@RestController +@RequestMapping("/xslmes/mesXslMixingSpecHistory") +@Slf4j +public class MesXslMixingSpecHistoryController { + + @Autowired + private IMesXslMixingSpecHistoryService mesXslMixingSpecHistoryService; + + @Operation(summary = "混炼示方历史记录-按示方ID查询") + @GetMapping(value = "/listByMixingSpecId") + public Result> listByMixingSpecId( + @RequestParam(name = "mixingSpecId", required = true) String mixingSpecId) { + if (StringUtils.isBlank(mixingSpecId)) { + return Result.error("mixingSpecId不能为空"); + } + return Result.OK(mesXslMixingSpecHistoryService.listByMixingSpecId(mixingSpecId)); + } + + @Operation(summary = "混炼示方历史记录-查询快照详情") + @GetMapping(value = "/queryById") + public Result queryById(@RequestParam(name = "id", required = true) String id) { + MesXslMixingSpecPage page = mesXslMixingSpecHistoryService.queryPageByHistoryId(id); + if (page == null) { + return Result.error("未找到对应历史记录"); + } + return Result.OK(page); + } +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslMixingSpec.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslMixingSpec.java index 84e47134..50d0f7bb 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslMixingSpec.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslMixingSpec.java @@ -89,6 +89,20 @@ public class MesXslMixingSpec implements Serializable { @Schema(description = "自动小料打印设定") private String autoSmallPrintSetting; + //update-begin---author:cursor ---date:20260612 for:【XSLMES-20260612-A02】混炼示方小料称重上下限容差----------- + @Schema(description = "人工小料计量下限容差(KG)") + private BigDecimal manualSmallWeighLowerTol; + + @Schema(description = "人工小料计量上限容差(KG)") + private BigDecimal manualSmallWeighUpperTol; + + @Schema(description = "自动小料计量下限容差(KG)") + private BigDecimal autoSmallWeighLowerTol; + + @Schema(description = "自动小料计量上限容差(KG)") + private BigDecimal autoSmallWeighUpperTol; + //update-end---author:cursor ---date:20260612 for:【XSLMES-20260612-A02】混炼示方小料称重上下限容差----------- + @Schema(description = "设定车数") private Integer setTrainCount; diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslMixingSpecHistory.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslMixingSpecHistory.java new file mode 100644 index 00000000..a7898551 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/entity/MesXslMixingSpecHistory.java @@ -0,0 +1,71 @@ +package org.jeecg.modules.xslmes.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import java.io.Serializable; +import java.util.Date; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.jeecg.common.aspect.annotation.Dict; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.springframework.format.annotation.DateTimeFormat; + +/** + * 混炼示方历史记录 + */ +@Data +@TableName("mes_xsl_mixing_spec_history") +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Schema(description = "混炼示方历史记录") +public class MesXslMixingSpecHistory implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.ASSIGN_ID) + @Schema(description = "主键") + private String id; + + @Schema(description = "混炼示方主表ID") + private String mixingSpecId; + + @Excel(name = "规格名", width = 20) + @Schema(description = "规格名") + private String specName; + + @Excel(name = "发行编号", width = 16) + @Schema(description = "发行编号") + private String issueNumber; + + @Excel(name = "版本号", width = 10) + @Schema(description = "版本号") + private String versionNo; + + @Excel(name = "操作类型", width = 10, dicCode = "xslmes_formula_spec_edit_log_action") + @Dict(dicCode = "xslmes_formula_spec_edit_log_action") + @Schema(description = "操作类型 create=新增 update=修改") + private String actionType; + + @Schema(description = "完整快照JSON") + private String snapshotJson; + + @Excel(name = "操作时间", width = 20, format = "yyyy-MM-dd HH:mm:ss") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Schema(description = "操作时间") + private Date operateTime; + + @Schema(description = "操作人账号") + private String operateBy; + + @Excel(name = "操作人", width = 14) + @Schema(description = "操作人姓名") + private String operateByName; + + @Schema(description = "租户ID") + private Integer tenantId; +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mapper/MesXslMixingSpecHistoryMapper.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mapper/MesXslMixingSpecHistoryMapper.java new file mode 100644 index 00000000..3396a05f --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mapper/MesXslMixingSpecHistoryMapper.java @@ -0,0 +1,9 @@ +package org.jeecg.modules.xslmes.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.jeecg.modules.xslmes.entity.MesXslMixingSpecHistory; + +/** + * 混炼示方历史记录 + */ +public interface MesXslMixingSpecHistoryMapper extends BaseMapper {} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslMixingSpecHistoryService.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslMixingSpecHistoryService.java new file mode 100644 index 00000000..d357ee7c --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslMixingSpecHistoryService.java @@ -0,0 +1,30 @@ +package org.jeecg.modules.xslmes.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import java.util.List; +import org.jeecg.modules.xslmes.entity.MesXslMixingSpecHistory; +import org.jeecg.modules.xslmes.vo.MesXslMixingSpecPage; + +/** + * 混炼示方历史记录 + */ +public interface IMesXslMixingSpecHistoryService extends IService { + + String ACTION_CREATE = "create"; + String ACTION_UPDATE = "update"; + + /** + * 保存一条历史快照 + */ + void recordHistory(MesXslMixingSpecPage page, String actionType); + + /** + * 按示方主表ID查询历史列表(版本倒序) + */ + List listByMixingSpecId(String mixingSpecId); + + /** + * 解析历史快照为混炼示方页面对象 + */ + MesXslMixingSpecPage queryPageByHistoryId(String historyId); +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslMixingSpecService.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslMixingSpecService.java index c7e4baed..39ef993c 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslMixingSpecService.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslMixingSpecService.java @@ -12,6 +12,7 @@ import org.jeecg.modules.xslmes.entity.MesXslMixingSpecStep; import org.jeecg.modules.xslmes.entity.MesXslMixingSpecTcu; import org.jeecg.modules.xslmes.entity.MesXslMixerPsCompile; import org.jeecg.modules.xslmes.vo.MesXslMixingSpecPage; +import org.jeecg.modules.xslmes.vo.MesXslMixingSpecSmallWeighRangeVO; public interface IMesXslMixingSpecService extends IService { void saveMain( @@ -57,4 +58,9 @@ public interface IMesXslMixingSpecService extends IService { void revertFromMixerPsWorkflow(MesXslMixerPsCompile ps, String mixerPsTargetStatus); //update-end---author:GHT ---date:2026-05-29 for:【QH-MES审批流设计】密炼PS拒绝/撤回联动回退混炼示方----------- //update-end---author:cursor ---date:20260526 for:【XSLMES-20260526-A61】混炼示方密炼PS审批联动同步审批人----------- + + //update-begin---author:cursor ---date:20260612 for:【XSLMES-20260612-A02】混炼示方小料称重范围设置----------- + /** 更新人工/自动小料称重上下限容差 */ + void updateSmallWeighRange(MesXslMixingSpecSmallWeighRangeVO vo); + //update-end---author:cursor ---date:20260612 for:【XSLMES-20260612-A02】混炼示方小料称重范围设置----------- } diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingSpecHistoryServiceImpl.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingSpecHistoryServiceImpl.java new file mode 100644 index 00000000..34e60530 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingSpecHistoryServiceImpl.java @@ -0,0 +1,93 @@ +package org.jeecg.modules.xslmes.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import java.util.Date; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.SecurityUtils; +import org.jeecg.common.system.vo.LoginUser; +import org.jeecg.modules.xslmes.common.MesXslFormulaSpecEditLogDiffUtil; +import org.jeecg.modules.xslmes.entity.MesXslMixingSpecHistory; +import org.jeecg.modules.xslmes.mapper.MesXslMixingSpecHistoryMapper; +import org.jeecg.modules.xslmes.service.IMesXslMixingSpecHistoryService; +import org.jeecg.modules.xslmes.vo.MesXslMixingSpecPage; +import org.springframework.stereotype.Service; + +/** + * 混炼示方历史记录 + */ +@Service +@Slf4j +public class MesXslMixingSpecHistoryServiceImpl + extends ServiceImpl + implements IMesXslMixingSpecHistoryService { + + @Override + public void recordHistory(MesXslMixingSpecPage page, String actionType) { + if (page == null || StringUtils.isBlank(page.getId())) { + return; + } + long existCount = count(new LambdaQueryWrapper() + .eq(MesXslMixingSpecHistory::getMixingSpecId, page.getId())); + String versionNo = "v1." + existCount; + + LoginUser loginUser = resolveLoginUser(); + MesXslMixingSpecHistory row = new MesXslMixingSpecHistory(); + row.setMixingSpecId(page.getId()); + row.setSpecName(page.getSpecName()); + row.setIssueNumber(page.getIssueNumber()); + row.setVersionNo(versionNo); + row.setActionType(StringUtils.defaultIfBlank(actionType, ACTION_UPDATE)); + row.setSnapshotJson(MesXslFormulaSpecEditLogDiffUtil.buildMixingSnapshotJson(page)); + row.setOperateTime(new Date()); + if (loginUser != null) { + row.setOperateBy(loginUser.getUsername()); + row.setOperateByName(StringUtils.defaultIfBlank(loginUser.getRealname(), loginUser.getUsername())); + } else { + row.setOperateByName("未知"); + } + row.setTenantId(page.getTenantId()); + save(row); + } + + @Override + public List listByMixingSpecId(String mixingSpecId) { + if (StringUtils.isBlank(mixingSpecId)) { + return List.of(); + } + return list(new LambdaQueryWrapper() + .eq(MesXslMixingSpecHistory::getMixingSpecId, mixingSpecId) + .orderByDesc(MesXslMixingSpecHistory::getOperateTime) + .orderByDesc(MesXslMixingSpecHistory::getVersionNo)); + } + + @Override + public MesXslMixingSpecPage queryPageByHistoryId(String historyId) { + if (StringUtils.isBlank(historyId)) { + return null; + } + MesXslMixingSpecHistory row = getById(historyId); + if (row == null) { + return null; + } + MesXslMixingSpecPage page = MesXslFormulaSpecEditLogDiffUtil.parseMixingSnapshotJson(row.getSnapshotJson()); + if (page != null) { + page.setId(row.getMixingSpecId()); + } + return page; + } + + private static LoginUser resolveLoginUser() { + try { + Object principal = SecurityUtils.getSubject().getPrincipal(); + if (principal instanceof LoginUser loginUser) { + return loginUser; + } + } catch (Exception ex) { + log.debug("解析登录用户失败: {}", ex.getMessage()); + } + return null; + } +} diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingSpecServiceImpl.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingSpecServiceImpl.java index 06c4543d..bae0102a 100644 --- a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingSpecServiceImpl.java +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingSpecServiceImpl.java @@ -21,6 +21,7 @@ import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.jeecg.common.constant.CommonConstant; +import org.jeecg.common.exception.JeecgBootException; import org.jeecg.common.util.oConvertUtils; import org.jeecg.modules.mes.material.entity.MesMaterial; import org.jeecg.modules.mes.material.service.IMesMaterialService; @@ -38,8 +39,10 @@ import org.jeecg.modules.xslmes.mapper.MesXslMixingSpecMaterialMapper; import org.jeecg.modules.xslmes.mapper.MesXslMixingSpecStepMapper; import org.jeecg.modules.xslmes.mapper.MesXslMixingSpecTcuMapper; import org.jeecg.modules.xslmes.service.IMesXslFormulaSpecEditLogService; +import org.jeecg.modules.xslmes.service.IMesXslMixingSpecHistoryService; import org.jeecg.modules.xslmes.service.IMesXslMixingSpecService; import org.jeecg.modules.xslmes.vo.MesXslMixingSpecPage; +import org.jeecg.modules.xslmes.vo.MesXslMixingSpecSmallWeighRangeVO; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -75,6 +78,9 @@ public class MesXslMixingSpecServiceImpl extends ServiceImpl wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(MesXslMixingSpec::getId, vo.getId()) + .set(MesXslMixingSpec::getManualSmallWeighLowerTol, manualLower) + .set(MesXslMixingSpec::getManualSmallWeighUpperTol, manualUpper) + .set(MesXslMixingSpec::getAutoSmallWeighLowerTol, autoLower) + .set(MesXslMixingSpec::getAutoSmallWeighUpperTol, autoUpper) + .set(MesXslMixingSpec::getUpdateTime, new Date()); + update(wrapper); + } + + private BigDecimal normalizeSmallWeighTol(BigDecimal value) { + if (value == null) { + return DEFAULT_SMALL_WEIGH_TOL; + } + if (value.compareTo(BigDecimal.ZERO) < 0) { + throw new JeecgBootException("小料称重容差不能为负数"); + } + return value.setScale(6, java.math.RoundingMode.HALF_UP); + } + //update-end---author:cursor ---date:20260612 for:【XSLMES-20260612-A02】混炼示方小料称重范围设置----------- } diff --git a/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/vo/MesXslMixingSpecSmallWeighRangeVO.java b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/vo/MesXslMixingSpecSmallWeighRangeVO.java new file mode 100644 index 00000000..3954d906 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/vo/MesXslMixingSpecSmallWeighRangeVO.java @@ -0,0 +1,28 @@ +package org.jeecg.modules.xslmes.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.math.BigDecimal; +import lombok.Data; + +/** + * 混炼示方小料称重范围设置 + */ +@Data +@Schema(description = "混炼示方小料称重范围") +public class MesXslMixingSpecSmallWeighRangeVO { + + @Schema(description = "混炼示方主表ID") + private String id; + + @Schema(description = "人工小料计量下限容差(KG)") + private BigDecimal manualSmallWeighLowerTol; + + @Schema(description = "人工小料计量上限容差(KG)") + private BigDecimal manualSmallWeighUpperTol; + + @Schema(description = "自动小料计量下限容差(KG)") + private BigDecimal autoSmallWeighLowerTol; + + @Schema(description = "自动小料计量上限容差(KG)") + private BigDecimal autoSmallWeighUpperTol; +} diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_148__mes_xsl_mixing_spec_small_weigh_tol.sql b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_148__mes_xsl_mixing_spec_small_weigh_tol.sql new file mode 100644 index 00000000..9c481907 --- /dev/null +++ b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_148__mes_xsl_mixing_spec_small_weigh_tol.sql @@ -0,0 +1,11 @@ +-- 混炼示方:人工/自动小料称重上下限容差 +-- author: cursor date: 2026-06-12 for:【XSLMES-20260612-A02】混炼示方小料称重范围 +SET @db = DATABASE(); + +SET @sql = IF((SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='mes_xsl_mixing_spec' AND COLUMN_NAME='manual_small_weigh_lower_tol')=0, + 'ALTER TABLE `mes_xsl_mixing_spec` + ADD COLUMN `manual_small_weigh_lower_tol` decimal(18,6) DEFAULT 0.010000 COMMENT ''人工小料计量下限容差(KG)'' AFTER `auto_small_print_setting`, + ADD COLUMN `manual_small_weigh_upper_tol` decimal(18,6) DEFAULT 0.010000 COMMENT ''人工小料计量上限容差(KG)'' AFTER `manual_small_weigh_lower_tol`, + ADD COLUMN `auto_small_weigh_lower_tol` decimal(18,6) DEFAULT 0.010000 COMMENT ''自动小料计量下限容差(KG)'' AFTER `manual_small_weigh_upper_tol`, + ADD COLUMN `auto_small_weigh_upper_tol` decimal(18,6) DEFAULT 0.010000 COMMENT ''自动小料计量上限容差(KG)'' AFTER `auto_small_weigh_lower_tol`','SELECT 1'); +PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s; diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_149__mes_xsl_mixing_spec_history.sql b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_149__mes_xsl_mixing_spec_history.sql new file mode 100644 index 00000000..00a2de24 --- /dev/null +++ b/jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_149__mes_xsl_mixing_spec_history.sql @@ -0,0 +1,21 @@ +-- 混炼示方历史记录表(完整快照备份) +SET NAMES utf8mb4; + +CREATE TABLE IF NOT EXISTS `mes_xsl_mixing_spec_history` ( + `id` varchar(32) NOT NULL COMMENT '主键', + `mixing_spec_id` varchar(32) NOT NULL COMMENT '混炼示方主表ID', + `spec_name` varchar(200) DEFAULT NULL COMMENT '规格名', + `issue_number` varchar(64) DEFAULT NULL COMMENT '发行编号', + `version_no` varchar(20) NOT NULL COMMENT '版本号,如v1.0', + `action_type` varchar(20) NOT NULL COMMENT '操作类型 create=新增 update=修改', + `snapshot_json` longtext COMMENT '完整快照JSON', + `operate_time` datetime DEFAULT NULL COMMENT '操作时间', + `operate_by` varchar(64) DEFAULT NULL COMMENT '操作人账号', + `operate_by_name` varchar(100) DEFAULT NULL COMMENT '操作人姓名', + `tenant_id` int DEFAULT NULL COMMENT '租户ID', + PRIMARY KEY (`id`), + KEY `idx_msh_mixing_spec_id` (`mixing_spec_id`), + KEY `idx_msh_version_no` (`version_no`), + KEY `idx_msh_operate_time` (`operate_time`), + KEY `idx_msh_tenant` (`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES混炼示方历史记录'; diff --git a/jeecgboot-vue3/src/views/xslmes/approval/integration/traceRecordHelper.ts b/jeecgboot-vue3/src/views/xslmes/approval/integration/traceRecordHelper.ts index 22c4f8dc..e68ee974 100644 --- a/jeecgboot-vue3/src/views/xslmes/approval/integration/traceRecordHelper.ts +++ b/jeecgboot-vue3/src/views/xslmes/approval/integration/traceRecordHelper.ts @@ -80,3 +80,23 @@ export async function loadRecordWithTrace( } return record; } + +/** 业务页签章区展示(起草取主表,校对/审核/批准取痕迹表) */ +export interface BizSignDisplay { + draftBy: string; + proofreadBy: string; + auditBy: string; + approveBy: string; +} + +export function buildBizSignDisplay( + row: Recordable = {}, + resolveDraftBy: (record: Recordable) => string, +): BizSignDisplay { + return { + draftBy: resolveDraftBy(row) || '', + proofreadBy: row.traceProofreadBy || '', + auditBy: row.traceAuditBy || '', + approveBy: row.traceApproveBy || '', + }; +} diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.api.ts b/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.api.ts index 7a49e28f..04262f0c 100644 --- a/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.api.ts +++ b/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.api.ts @@ -12,6 +12,7 @@ enum Api { queryById = '/xslmes/mesXslMixingSpec/queryById', queryIssueNumberOptions = '/xslmes/mesXslMixingSpec/queryIssueNumberOptions', queryPurposeOptions = '/xslmes/mesXslMixingSpec/queryPurposeOptions', + updateSmallWeighRange = '/xslmes/mesXslMixingSpec/updateSmallWeighRange', } export const getExportUrl = Api.exportXls; @@ -24,6 +25,7 @@ export const saveOrUpdate = (params, isUpdate) => //update-end---author:cursor ---date:20260525 for:【XSLMES-20260525-A45】混炼示方主子表保存延长超时避免误报失败----------- export const queryIssueNumberOptions = (params) => defHttp.get({ url: Api.queryIssueNumberOptions, params }); export const queryPurposeOptions = (params) => defHttp.get({ url: Api.queryPurposeOptions, params }); +export const updateSmallWeighRange = (params) => defHttp.post({ url: Api.updateSmallWeighRange, params }); export const deleteOne = (params, handleSuccess) => defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => handleSuccess()); diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.data.ts b/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.data.ts index 629cb928..1b156e96 100644 --- a/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.data.ts +++ b/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.data.ts @@ -69,7 +69,15 @@ export const mainSchema: FormSchema[] = [ { label: '用途', field: 'purpose', component: 'Input', required: true, colProps: { span: 8 } }, { label: '机台', field: 'machineName', component: 'Input', colProps: { span: 8 } }, { label: '制作日期', field: 'makeDate', component: 'DatePicker', colProps: { span: 8 }, componentProps: { valueFormat: 'YYYY-MM-DD' } }, - { label: '发行编号', field: 'issueNumber', component: 'Input', required: true, colProps: { span: 8 } }, + { + label: '发行编号', + field: 'issueNumber', + component: 'Input', + required: true, + colProps: { span: 8 }, + componentProps: { placeholder: '请点击选择密炼PS' }, + rules: [{ required: true, message: '请选择发行编号' }], + }, //update-end---author:cursor ---date:20260522 for:【XSLMES-20260522-A33】混炼示方基本信息字段优化----------- { label: '换算系数', field: 'convertFactor', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 2, style: { width: '100%' } } }, { label: '填充体积', field: 'fillVolume', component: 'InputNumber', colProps: { span: 8 }, componentProps: { precision: 6, style: { width: '100%' } } }, @@ -1387,7 +1395,7 @@ export function resolveMixingSpecFormulaStatus(record: Recordable = {}): string return '编制中'; } -/** 混炼示方是否允许编辑/删除(与配合示方一致:仅编制状态可改) */ +/** 混炼示方是否允许删除(仅编制状态可删;编辑不受状态限制) */ export function isMixingSpecEditable(record: Recordable = {}): boolean { //update-begin---author:cursor ---date:20260608 for:【XSLMES-20260608-A01】混炼示方编辑权限按状态字段判断----------- return !record?.status || record.status === 'compile'; @@ -1471,3 +1479,180 @@ export function cloneMixingSpecPageForReferenceAdd(source: Recordable = {}): Rec }; } //update-end---author:cursor ---date:20260526 for:【XSLMES-20260526-A59】规格点击选择混炼示方及参照新增----------- + +//update-begin---author:cursor ---date:20260612 for:【XSLMES-20260612-A01】混炼示方列表查看人工/自动小料明细----------- +/** 小料计量默认容差(KG),与旧系统施工表一致 */ +export const MIXING_SMALL_MATERIAL_WEIGH_TOLERANCE = 0.01; + +/** 小料称重容差 */ +export interface MixingSmallWeighTolerance { + lower: number; + upper: number; +} + +/** 解析示方主表小料称重容差(未配置时默认 0.01) */ +export function resolveMixingSmallWeighTolerance( + main: Recordable | null | undefined, + type: MixingSmallMaterialViewType, +): MixingSmallWeighTolerance { + const lowerKey = type === 'manual' ? 'manualSmallWeighLowerTol' : 'autoSmallWeighLowerTol'; + const upperKey = type === 'manual' ? 'manualSmallWeighUpperTol' : 'autoSmallWeighUpperTol'; + const lower = toMixingMaterialNumber(main?.[lowerKey]); + const upper = toMixingMaterialNumber(main?.[upperKey]); + return { + lower: lower != null && lower >= 0 ? lower : MIXING_SMALL_MATERIAL_WEIGH_TOLERANCE, + upper: upper != null && upper >= 0 ? upper : MIXING_SMALL_MATERIAL_WEIGH_TOLERANCE, + }; +} + +/** 构建小料称重范围表单默认值 */ +export function buildMixingSmallWeighRangeForm(main: Recordable = {}): Recordable { + const manual = resolveMixingSmallWeighTolerance(main, 'manual'); + const auto = resolveMixingSmallWeighTolerance(main, 'auto'); + return { + id: main.id || '', + specName: main.specName || '', + manualSmallWeighLowerTol: manual.lower, + manualSmallWeighUpperTol: manual.upper, + autoSmallWeighLowerTol: auto.lower, + autoSmallWeighUpperTol: auto.upper, + }; +} + +/** 小料查看类型 */ +export type MixingSmallMaterialViewType = 'manual' | 'auto'; + +export interface MixingSmallMaterialWeighRow { + materialCode: string; + standardWeight: number | null; + lowerLimit: number | null; + upperLimit: number | null; + accumWeight: number | null; +} + +export interface MixingSmallMaterialAutoGroup { + groupKey: string; + groupLabel: string; + rows: MixingSmallMaterialWeighRow[]; +} + +/** 判断明细行是否属于人工小料(按种类/小类) */ +export function isMixingManualSmallMaterial(row: Recordable): boolean { + if (!isMixingMaterialDataRow(row)) { + return false; + } + const kind = String(row.materialKind || '').trim(); + const minor = String(row.materialMinor || '').trim(); + if (kind.includes('人工') || minor.includes('人工')) { + return true; + } + return false; +} + +/** 判断明细行是否属于自动小料(按种类/小类) */ +export function isMixingAutoSmallMaterial(row: Recordable): boolean { + if (!isMixingMaterialDataRow(row)) { + return false; + } + const kind = String(row.materialKind || '').trim(); + const minor = String(row.materialMinor || '').trim(); + if (kind.includes('自动') || minor.includes('自动')) { + return true; + } + return false; +} + +/** 解析自动小料分组标签:顺序未填默认 自动1-1 */ +export function resolveMixingAutoSmallGroupLabel(seqNo: unknown): string { + const seq = toMixingMaterialNumber(seqNo); + const suffix = seq != null && seq > 0 ? Math.round(seq) : 1; + return `自动1-${suffix}`; +} + +/** 按顺序字段分组自动小料明细 */ +export function groupMixingAutoSmallMaterials( + rows: Recordable[] = [], + main?: Recordable, +): MixingSmallMaterialAutoGroup[] { + const filtered = (rows || []).filter(isMixingAutoSmallMaterial); + if (!filtered.length) { + return []; + } + const groupMap = new Map(); + const groupOrder: string[] = []; + for (const row of filtered) { + const label = resolveMixingAutoSmallGroupLabel(row.seqNo); + if (!groupMap.has(label)) { + groupMap.set(label, []); + groupOrder.push(label); + } + groupMap.get(label)!.push(row); + } + groupOrder.sort((a, b) => resolveAutoGroupSortNo(a) - resolveAutoGroupSortNo(b)); + return groupOrder.map((label) => ({ + groupKey: label, + groupLabel: label, + rows: buildMixingSmallMaterialWeighRows( + groupMap.get(label) || [], + resolveMixingSmallWeighTolerance(main, 'auto'), + ), + })); +} + +function resolveAutoGroupSortNo(groupLabel: string): number { + const matched = groupLabel.match(/-(\d+)$/); + if (!matched) { + return 1; + } + const num = Number(matched[1]); + return Number.isFinite(num) ? num : 1; +} + +/** 构建人工小料计量行 */ +export function buildMixingManualSmallMaterialRows( + rows: Recordable[] = [], + main?: Recordable, +): MixingSmallMaterialWeighRow[] { + return buildMixingSmallMaterialWeighRows( + (rows || []).filter(isMixingManualSmallMaterial), + resolveMixingSmallWeighTolerance(main, 'manual'), + ); +} + +/** 将明细行转为药品计量展示行(含累计标准重量) */ +export function buildMixingSmallMaterialWeighRows( + rows: Recordable[] = [], + tolerance: MixingSmallWeighTolerance = { + lower: MIXING_SMALL_MATERIAL_WEIGH_TOLERANCE, + upper: MIXING_SMALL_MATERIAL_WEIGH_TOLERANCE, + }, +): MixingSmallMaterialWeighRow[] { + let running = 0; + let hasRunning = false; + return (rows || []).map((row) => { + const standardWeight = toMixingMaterialNumber(row.unitWeight); + const lowerLimit = + standardWeight != null ? roundMixingMaterialNumber(standardWeight - tolerance.lower) : null; + const upperLimit = + standardWeight != null ? roundMixingMaterialNumber(standardWeight + tolerance.upper) : null; + if (standardWeight != null) { + running += standardWeight; + hasRunning = true; + } + const materialCode = + String(row.mixerMaterialName || row.mixerMaterialDesc || row.materialKind || '').trim(); + return { + materialCode, + standardWeight, + lowerLimit, + upperLimit, + accumWeight: standardWeight != null && hasRunning ? roundMixingMaterialNumber(running) : null, + }; + }); +} + +/** 小料查看弹窗标题 */ +export function resolveMixingSmallMaterialViewTitle(viewType: MixingSmallMaterialViewType): string { + return viewType === 'manual' ? '混炼母胶药品计量(人工)' : '混炼母胶药品计量(自动)'; +} +//update-end---author:cursor ---date:20260612 for:【XSLMES-20260612-A01】混炼示方列表查看人工/自动小料明细----------- diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpecHistory.api.ts b/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpecHistory.api.ts new file mode 100644 index 00000000..638ca252 --- /dev/null +++ b/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpecHistory.api.ts @@ -0,0 +1,10 @@ +import { defHttp } from '/@/utils/http/axios'; + +enum Api { + listByMixingSpecId = '/xslmes/mesXslMixingSpecHistory/listByMixingSpecId', + queryById = '/xslmes/mesXslMixingSpecHistory/queryById', +} + +export const listHistoryByMixingSpecId = (params) => defHttp.get({ url: Api.listByMixingSpecId, params }); + +export const queryHistoryById = (params) => defHttp.get({ url: Api.queryById, params }); diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpecList.vue b/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpecList.vue index bf786e1e..db6473f9 100644 --- a/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpecList.vue +++ b/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpecList.vue @@ -27,6 +27,9 @@ + + + @@ -37,6 +40,9 @@ import { useListPage } from '/@/hooks/system/useListPage'; import Icon from '/@/components/Icon'; import MesXslMixingSpecModal from './components/MesXslMixingSpecModal.vue'; + import MesXslMixingSpecHistoryModal from './components/MesXslMixingSpecHistoryModal.vue'; + import MesXslMixingSmallMaterialViewModal from './components/MesXslMixingSmallMaterialViewModal.vue'; + import MesXslMixingSmallWeighRangeModal from './components/MesXslMixingSmallWeighRangeModal.vue'; import { columns, searchFormSchema, isMixingSpecEditable } from './MesXslMixingSpec.data'; import { list, deleteOne, batchDelete, getExportUrl, getImportUrl } from './MesXslMixingSpec.api'; import { useMessage } from '/@/hooks/web/useMessage'; @@ -45,6 +51,9 @@ const queryParam = reactive({}); const [registerModal, { openModal }] = useModal(); + const [registerHistoryModal, { openModal: openHistoryModal }] = useModal(); + const [registerSmallMaterialModal, { openModal: openSmallMaterialModal }] = useModal(); + const [registerWeighRangeModal, { openModal: openWeighRangeModal }] = useModal(); const { tableContext, onExportXls, onImportXls } = useListPage({ tableProps: { @@ -60,7 +69,7 @@ actionColumn: { title: '操作', dataIndex: 'action', - width: 160, + width: 260, fixed: 'right', slots: { customRender: 'action' }, }, @@ -84,10 +93,6 @@ } function handleEdit(record: Recordable) { - if (!isMixingSpecEditable(record)) { - createMessage.warning('已进入审批流程的混炼示方不允许编辑'); - return; - } openModal(true, { record, isUpdate: true, showFooter: true }); } @@ -95,6 +100,32 @@ openModal(true, { record, isUpdate: true, showFooter: false }); } + function handleViewManualSmallMaterial(record: Recordable) { + openSmallMaterialModal(true, { record, viewType: 'manual' }); + } + + function handleViewAutoSmallMaterial(record: Recordable) { + openSmallMaterialModal(true, { record, viewType: 'auto' }); + } + + function handleSmallWeighRange(record: Recordable) { + openWeighRangeModal(true, { record }); + } + + function handleHistory(record: Recordable) { + openHistoryModal(true, { record }); + } + + function handleViewHistoryDetail(historyRecord: Recordable) { + openModal(true, { + historyId: historyRecord?.id, + historyVersion: historyRecord?.versionNo, + isUpdate: true, + showFooter: false, + isHistoryView: true, + }); + } + function handleDelete(record: Recordable) { if (!isMixingSpecEditable(record)) { createMessage.warning('已进入审批流程的混炼示方不允许删除'); @@ -116,26 +147,43 @@ const editable = isMixingSpecEditable(record); return [ { - label: '编辑', + icon: 'ant-design:edit-outlined', + tooltip: '编辑', onClick: handleEdit.bind(null, record), auth: 'xslmes:mes_xsl_mixing_spec:edit', - ifShow: editable, }, { - label: '详情', + icon: 'ant-design:eye-outlined', + tooltip: '详情', onClick: handleDetail.bind(null, record), - ifShow: !editable, }, { - label: '删除', + icon: 'ant-design:delete-outlined', + tooltip: '删除', auth: 'xslmes:mes_xsl_mixing_spec:delete', ifShow: editable, popConfirm: { title: '是否确认删除', confirm: handleDelete.bind(null, record) }, }, { - label: '详情', - onClick: handleDetail.bind(null, record), - ifShow: editable, + icon: 'ant-design:user-outlined', + tooltip: '查看人工小料', + onClick: handleViewManualSmallMaterial.bind(null, record), + }, + { + icon: 'ant-design:robot-outlined', + tooltip: '查看自动小料', + onClick: handleViewAutoSmallMaterial.bind(null, record), + }, + { + icon: 'ant-design:column-width-outlined', + tooltip: '小料称重范围', + onClick: handleSmallWeighRange.bind(null, record), + auth: 'xslmes:mes_xsl_mixing_spec:edit', + }, + { + icon: 'ant-design:history-outlined', + tooltip: '历史记录', + onClick: handleHistory.bind(null, record), }, ]; } diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSmallMaterialViewModal.vue b/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSmallMaterialViewModal.vue new file mode 100644 index 00000000..b08b296c --- /dev/null +++ b/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSmallMaterialViewModal.vue @@ -0,0 +1,408 @@ + + + + + diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSmallWeighRangeModal.vue b/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSmallWeighRangeModal.vue new file mode 100644 index 00000000..3bfd11d1 --- /dev/null +++ b/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSmallWeighRangeModal.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSpecHistoryModal.vue b/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSpecHistoryModal.vue new file mode 100644 index 00000000..3dd74665 --- /dev/null +++ b/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSpecHistoryModal.vue @@ -0,0 +1,78 @@ + + + diff --git a/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSpecModal.vue b/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSpecModal.vue index e0736652..b2ff8092 100644 --- a/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSpecModal.vue +++ b/jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSpecModal.vue @@ -536,6 +536,7 @@ import { MIXING_STEP_MIN_COLUMN_WIDTH, } from '../MesXslMixingSpec.data'; import { saveOrUpdate, queryById } from '../MesXslMixingSpec.api'; +import { queryHistoryById } from '../MesXslMixingSpecHistory.api'; import { resolveFormulaSpecUserDisplayName } from '/@/views/xslmes/mesXslFormulaSpec/MesXslFormulaSpec.data'; import { MIXING_SPEC_BIZ_TABLE, loadRecordWithTrace } from '/@/views/xslmes/approval/integration/traceRecordHelper'; import MesXslMixingMaterialColumnSetting from './MesXslMixingMaterialColumnSetting.vue'; @@ -556,6 +557,8 @@ const userStore = useUserStore(); const isUpdate = ref(false); const showFooter = ref(true); +const isHistoryView = ref(false); +const historyVersion = ref(''); const mixerPsCompilePickerId = ref(''); //update-begin---author:cursor ---date:20260522 for:【XSLMES-20260522-A34】混合步骤动作/组合下拉选项----------- const mixerActionOptions = ref<{ title: string; value: string }[]>([]); @@ -1245,11 +1248,21 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data downStepData.value = []; tcuData.value = ensureTcuDefaultRows([]); await loadMixerStepOptions(''); + isHistoryView.value = !!data?.isHistoryView; + historyVersion.value = data?.historyVersion || ''; isUpdate.value = !!data?.isUpdate; - showFooter.value = !!data?.showFooter; + showFooter.value = !!data?.showFooter && !isHistoryView.value; await setProps({ disabled: !showFooter.value }); setModalProps({ showOkBtn: showFooter.value, showCancelBtn: showFooter.value, confirmLoading: false }); - if (isUpdate.value && data?.record?.id) { + if (isHistoryView.value && data?.historyId) { + const row = await queryHistoryById({ id: data.historyId }); + if (!row?.id) { + createMessage.warning('未找到历史记录数据'); + return; + } + historyVersion.value = historyVersion.value || data?.historyVersion || ''; + await applyMixingSpecPageData(row, 'edit'); + } else if (isUpdate.value && data?.record?.id) { const row = await loadRecordWithTrace(data.record.id, MIXING_SPEC_BIZ_TABLE, queryById, data.record); await applyMixingSpecPageData(row, 'edit'); } else { @@ -1271,11 +1284,46 @@ const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data } }); -const title = computed(() => (!showFooter.value && unref(isUpdate) ? '混炼示方详情' : !unref(isUpdate) ? '新增混炼示方' : '编辑混炼示方')); +const title = computed(() => { + if (unref(isHistoryView)) { + const version = unref(historyVersion); + return version ? `混炼示方历史详情(${version})` : '混炼示方历史详情'; + } + return !showFooter.value && unref(isUpdate) ? '混炼示方详情' : !unref(isUpdate) ? '新增混炼示方' : '编辑混炼示方'; +}); + +function resolveValidateErrorMessage(error: unknown) { + const err = error as { errorFields?: { name?: string[]; errors?: string[] }[] }; + const messages = (err?.errorFields || []) + .flatMap((item) => item?.errors || []) + .filter((msg) => !!msg); + if (messages.length) { + return messages.join(';'); + } + const fieldLabelMap: Recordable = { + specName: '规格', + purpose: '用途', + issueNumber: '发行编号', + }; + const fields = (err?.errorFields || []) + .map((item) => item?.name?.[0]) + .filter(Boolean) + .map((name) => fieldLabelMap[name as string] || name); + if (fields.length) { + return `请完善必填项:${fields.join('、')}`; + } + return '请完善表单必填项'; +} async function handleSubmit() { await syncSheetToForm(); - const formValues = await validate(); + let formValues: Recordable; + try { + formValues = await validate(); + } catch (error) { + createMessage.warning(resolveValidateErrorMessage(error)); + return; + } const materialList = resolveMaterialTableRawRows(); applyMaterialAccumWeight(materialList); const stepList = stepRef.value?.getTableData?.() || stepData.value;