feat: 实现产需单按工序步骤生成排产明细功能
1. 重构接收产需单接口,支持按配置工序步骤生成明细 2. 新增工艺、工艺步骤CRUD接口与管理页面 3. 新增工序选择组件 4. 优化产需单页面,增加历史记录功能 5. 为排产明细添加工序步骤名称展示
This commit is contained in:
@@ -2,6 +2,8 @@ package com.klp.flow.domain.bo;
|
|||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 产需单接收请求对象
|
* 产需单接收请求对象
|
||||||
@@ -18,4 +20,55 @@ public class SchProdScheduleItemReceiveBo {
|
|||||||
@NotNull(message = "排产单主表ID不能为空")
|
@NotNull(message = "排产单主表ID不能为空")
|
||||||
private Long scheduleId;
|
private Long scheduleId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 明细工序配置列表
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "明细工序配置不能为空")
|
||||||
|
private List<DetailProcessConfig> detailProcessList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 明细工序配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class DetailProcessConfig {
|
||||||
|
/**
|
||||||
|
* 产需单明细ID
|
||||||
|
*/
|
||||||
|
@NotNull(message = "明细ID不能为空")
|
||||||
|
private Long scheduleDetailId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工序ID
|
||||||
|
*/
|
||||||
|
@NotNull(message = "工序ID不能为空")
|
||||||
|
private Long processId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工序步骤列表
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "工序步骤不能为空")
|
||||||
|
private List<StepConfig> stepList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工序步骤配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class StepConfig {
|
||||||
|
/**
|
||||||
|
* 步骤ID
|
||||||
|
*/
|
||||||
|
@NotNull(message = "步骤ID不能为空")
|
||||||
|
private Long stepId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 步骤顺序号
|
||||||
|
*/
|
||||||
|
private Long stepOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 步骤名称
|
||||||
|
*/
|
||||||
|
private String stepName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,13 @@ package com.klp.flow.domain.vo;
|
|||||||
|
|
||||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||||
import com.alibaba.excel.annotation.ExcelProperty;
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.klp.common.annotation.ExcelDictFormat;
|
import com.klp.common.annotation.ExcelDictFormat;
|
||||||
import com.klp.common.convert.ExcelDictConvert;
|
import com.klp.common.convert.ExcelDictConvert;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工序定义主视图对象 sch_prod_process
|
* 工序定义主视图对象 sch_prod_process
|
||||||
@@ -44,5 +47,10 @@ public class SchProdProcessVo {
|
|||||||
@ExcelProperty(value = "备注")
|
@ExcelProperty(value = "备注")
|
||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工艺步骤列表
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private List<SchProdProcessStepVo> stepList;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.klp.flow.domain.vo;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||||
import com.alibaba.excel.annotation.ExcelProperty;
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
@@ -195,6 +196,12 @@ public class SchProdScheduleItemVo {
|
|||||||
@ExcelProperty(value = "工序ID")
|
@ExcelProperty(value = "工序ID")
|
||||||
private Long actionId;
|
private Long actionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工序步骤名称
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String stepName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 规格 例:1.0X1250
|
* 规格 例:1.0X1250
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -11,13 +11,18 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import com.klp.flow.domain.bo.SchProdProcessBo;
|
import com.klp.flow.domain.bo.SchProdProcessBo;
|
||||||
import com.klp.flow.domain.vo.SchProdProcessVo;
|
import com.klp.flow.domain.vo.SchProdProcessVo;
|
||||||
|
import com.klp.flow.domain.vo.SchProdProcessStepVo;
|
||||||
import com.klp.flow.domain.SchProdProcess;
|
import com.klp.flow.domain.SchProdProcess;
|
||||||
|
import com.klp.flow.domain.SchProdProcessStep;
|
||||||
import com.klp.flow.mapper.SchProdProcessMapper;
|
import com.klp.flow.mapper.SchProdProcessMapper;
|
||||||
|
import com.klp.flow.mapper.SchProdProcessStepMapper;
|
||||||
import com.klp.flow.service.ISchProdProcessService;
|
import com.klp.flow.service.ISchProdProcessService;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工序定义主Service业务层处理
|
* 工序定义主Service业务层处理
|
||||||
@@ -30,13 +35,18 @@ import java.util.Collection;
|
|||||||
public class SchProdProcessServiceImpl implements ISchProdProcessService {
|
public class SchProdProcessServiceImpl implements ISchProdProcessService {
|
||||||
|
|
||||||
private final SchProdProcessMapper baseMapper;
|
private final SchProdProcessMapper baseMapper;
|
||||||
|
private final SchProdProcessStepMapper stepMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询工序定义主
|
* 查询工序定义主
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public SchProdProcessVo queryById(Long processId){
|
public SchProdProcessVo queryById(Long processId){
|
||||||
return baseMapper.selectVoById(processId);
|
SchProdProcessVo vo = baseMapper.selectVoById(processId);
|
||||||
|
if (vo != null) {
|
||||||
|
fillStepList(Collections.singletonList(vo));
|
||||||
|
}
|
||||||
|
return vo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,6 +56,9 @@ public class SchProdProcessServiceImpl implements ISchProdProcessService {
|
|||||||
public TableDataInfo<SchProdProcessVo> queryPageList(SchProdProcessBo bo, PageQuery pageQuery) {
|
public TableDataInfo<SchProdProcessVo> queryPageList(SchProdProcessBo bo, PageQuery pageQuery) {
|
||||||
LambdaQueryWrapper<SchProdProcess> lqw = buildQueryWrapper(bo);
|
LambdaQueryWrapper<SchProdProcess> lqw = buildQueryWrapper(bo);
|
||||||
Page<SchProdProcessVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
Page<SchProdProcessVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||||
|
if (result.getRecords() != null && !result.getRecords().isEmpty()) {
|
||||||
|
fillStepList(result.getRecords());
|
||||||
|
}
|
||||||
return TableDataInfo.build(result);
|
return TableDataInfo.build(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +68,34 @@ public class SchProdProcessServiceImpl implements ISchProdProcessService {
|
|||||||
@Override
|
@Override
|
||||||
public List<SchProdProcessVo> queryList(SchProdProcessBo bo) {
|
public List<SchProdProcessVo> queryList(SchProdProcessBo bo) {
|
||||||
LambdaQueryWrapper<SchProdProcess> lqw = buildQueryWrapper(bo);
|
LambdaQueryWrapper<SchProdProcess> lqw = buildQueryWrapper(bo);
|
||||||
return baseMapper.selectVoList(lqw);
|
List<SchProdProcessVo> list = baseMapper.selectVoList(lqw);
|
||||||
|
if (list != null && !list.isEmpty()) {
|
||||||
|
fillStepList(list);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量填充工艺步骤列表
|
||||||
|
*/
|
||||||
|
private void fillStepList(List<SchProdProcessVo> voList) {
|
||||||
|
List<Long> processIds = voList.stream()
|
||||||
|
.map(SchProdProcessVo::getProcessId)
|
||||||
|
.filter(id -> id != null)
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (processIds.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LambdaQueryWrapper<SchProdProcessStep> stepQw = Wrappers.lambdaQuery();
|
||||||
|
stepQw.in(SchProdProcessStep::getProcessId, processIds);
|
||||||
|
stepQw.orderByAsc(SchProdProcessStep::getStepOrder);
|
||||||
|
List<SchProdProcessStepVo> allSteps = stepMapper.selectVoList(stepQw);
|
||||||
|
Map<Long, List<SchProdProcessStepVo>> stepMap = allSteps.stream()
|
||||||
|
.collect(Collectors.groupingBy(SchProdProcessStepVo::getProcessId));
|
||||||
|
for (SchProdProcessVo vo : voList) {
|
||||||
|
vo.setStepList(stepMap.getOrDefault(vo.getProcessId(), Collections.emptyList()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private LambdaQueryWrapper<SchProdProcess> buildQueryWrapper(SchProdProcessBo bo) {
|
private LambdaQueryWrapper<SchProdProcess> buildQueryWrapper(SchProdProcessBo bo) {
|
||||||
|
|||||||
@@ -18,15 +18,18 @@ import com.klp.flow.domain.vo.SchProdScheduleItemMergeValidateVo;
|
|||||||
import com.klp.flow.domain.SchProdScheduleItem;
|
import com.klp.flow.domain.SchProdScheduleItem;
|
||||||
import com.klp.flow.domain.SchProdSchedule;
|
import com.klp.flow.domain.SchProdSchedule;
|
||||||
import com.klp.flow.domain.SchProdScheduleDetail;
|
import com.klp.flow.domain.SchProdScheduleDetail;
|
||||||
|
import com.klp.flow.domain.SchProdProcessStep;
|
||||||
import com.klp.flow.mapper.SchProdScheduleItemMapper;
|
import com.klp.flow.mapper.SchProdScheduleItemMapper;
|
||||||
import com.klp.flow.mapper.SchProdScheduleMapper;
|
import com.klp.flow.mapper.SchProdScheduleMapper;
|
||||||
import com.klp.flow.mapper.SchProdScheduleDetailMapper;
|
import com.klp.flow.mapper.SchProdScheduleDetailMapper;
|
||||||
|
import com.klp.flow.mapper.SchProdProcessStepMapper;
|
||||||
import com.klp.flow.service.ISchProdScheduleItemService;
|
import com.klp.flow.service.ISchProdScheduleItemService;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -46,6 +49,7 @@ public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemServi
|
|||||||
private final SchProdScheduleItemMapper baseMapper;
|
private final SchProdScheduleItemMapper baseMapper;
|
||||||
private final SchProdScheduleMapper schProdScheduleMapper;
|
private final SchProdScheduleMapper schProdScheduleMapper;
|
||||||
private final SchProdScheduleDetailMapper schProdScheduleDetailMapper;
|
private final SchProdScheduleDetailMapper schProdScheduleDetailMapper;
|
||||||
|
private final SchProdProcessStepMapper stepMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询排产单主加明细可合并
|
* 查询排产单主加明细可合并
|
||||||
@@ -62,6 +66,9 @@ public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemServi
|
|||||||
public TableDataInfo<SchProdScheduleItemVo> queryPageList(SchProdScheduleItemBo bo, PageQuery pageQuery) {
|
public TableDataInfo<SchProdScheduleItemVo> queryPageList(SchProdScheduleItemBo bo, PageQuery pageQuery) {
|
||||||
LambdaQueryWrapper<SchProdScheduleItem> lqw = buildQueryWrapper(bo);
|
LambdaQueryWrapper<SchProdScheduleItem> lqw = buildQueryWrapper(bo);
|
||||||
Page<SchProdScheduleItemVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
Page<SchProdScheduleItemVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||||
|
if (result.getRecords() != null && !result.getRecords().isEmpty()) {
|
||||||
|
fillStepName(result.getRecords());
|
||||||
|
}
|
||||||
return TableDataInfo.build(result);
|
return TableDataInfo.build(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +78,11 @@ public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemServi
|
|||||||
@Override
|
@Override
|
||||||
public List<SchProdScheduleItemVo> queryList(SchProdScheduleItemBo bo) {
|
public List<SchProdScheduleItemVo> queryList(SchProdScheduleItemBo bo) {
|
||||||
LambdaQueryWrapper<SchProdScheduleItem> lqw = buildQueryWrapper(bo);
|
LambdaQueryWrapper<SchProdScheduleItem> lqw = buildQueryWrapper(bo);
|
||||||
return baseMapper.selectVoList(lqw);
|
List<SchProdScheduleItemVo> list = baseMapper.selectVoList(lqw);
|
||||||
|
if (list != null && !list.isEmpty()) {
|
||||||
|
fillStepName(list);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LambdaQueryWrapper<SchProdScheduleItem> buildQueryWrapper(SchProdScheduleItemBo bo) {
|
private LambdaQueryWrapper<SchProdScheduleItem> buildQueryWrapper(SchProdScheduleItemBo bo) {
|
||||||
@@ -113,6 +124,30 @@ public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemServi
|
|||||||
return lqw;
|
return lqw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量填充工序步骤名称
|
||||||
|
*/
|
||||||
|
private void fillStepName(List<SchProdScheduleItemVo> voList) {
|
||||||
|
List<Long> stepIds = voList.stream()
|
||||||
|
.map(SchProdScheduleItemVo::getActionId)
|
||||||
|
.filter(id -> id != null)
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (stepIds.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LambdaQueryWrapper<SchProdProcessStep> stepQw = Wrappers.lambdaQuery();
|
||||||
|
stepQw.in(SchProdProcessStep::getStepId, stepIds);
|
||||||
|
List<SchProdProcessStep> steps = stepMapper.selectList(stepQw);
|
||||||
|
Map<Long, String> stepNameMap = steps.stream()
|
||||||
|
.collect(Collectors.toMap(SchProdProcessStep::getStepId, SchProdProcessStep::getStepName, (v1, v2) -> v1));
|
||||||
|
for (SchProdScheduleItemVo vo : voList) {
|
||||||
|
if (vo.getActionId() != null) {
|
||||||
|
vo.setStepName(stepNameMap.get(vo.getActionId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增排产单主加明细可合并
|
* 新增排产单主加明细可合并
|
||||||
*/
|
*/
|
||||||
@@ -195,12 +230,13 @@ public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemServi
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 接收产需单:从 sch_prod_schedule + sch_prod_schedule_detail 全字段复制到 sch_prod_schedule_item
|
* 接收产需单:根据配置的工序步骤生成多条排产单明细
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public Boolean receiveByBo(SchProdScheduleItemReceiveBo receiveBo) {
|
public Boolean receiveByBo(SchProdScheduleItemReceiveBo receiveBo) {
|
||||||
Long scheduleId = receiveBo.getScheduleId();
|
Long scheduleId = receiveBo.getScheduleId();
|
||||||
|
List<SchProdScheduleItemReceiveBo.DetailProcessConfig> detailProcessList = receiveBo.getDetailProcessList();
|
||||||
|
|
||||||
// 1. 查询产需单主表
|
// 1. 查询产需单主表
|
||||||
SchProdSchedule header = schProdScheduleMapper.selectById(scheduleId);
|
SchProdSchedule header = schProdScheduleMapper.selectById(scheduleId);
|
||||||
@@ -217,57 +253,74 @@ public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemServi
|
|||||||
throw new RuntimeException("产需单无明细数据,scheduleId=" + scheduleId);
|
throw new RuntimeException("产需单无明细数据,scheduleId=" + scheduleId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 遍历每条 detail,构建 SchProdScheduleItem 列表
|
// 3. 构建明细ID到工序配置的映射
|
||||||
List<SchProdScheduleItem> addList = new ArrayList<>(details.size());
|
Map<Long, SchProdScheduleItemReceiveBo.DetailProcessConfig> processConfigMap = new HashMap<>();
|
||||||
for (SchProdScheduleDetail detail : details) {
|
for (SchProdScheduleItemReceiveBo.DetailProcessConfig config : detailProcessList) {
|
||||||
SchProdScheduleItem item = new SchProdScheduleItem();
|
processConfigMap.put(config.getScheduleDetailId(), config);
|
||||||
|
|
||||||
// 从 header 复制所有字段
|
|
||||||
item.setScheduleNo(header.getScheduleNo());
|
|
||||||
item.setProdDate(header.getProdDate());
|
|
||||||
item.setScheduleStatus(2L); // 已下达
|
|
||||||
item.setTotalPlanWeight(header.getTotalPlanWeight());
|
|
||||||
item.setRelContractNo(header.getRelContractNo());
|
|
||||||
item.setBusinessUser(header.getBusinessUser());
|
|
||||||
item.setBusinessPhone(header.getBusinessPhone());
|
|
||||||
item.setOrderDate(header.getOrderDate());
|
|
||||||
item.setCustomerName(header.getCustomerName());
|
|
||||||
item.setDeliveryCycle(header.getDeliveryCycle());
|
|
||||||
item.setUsePurpose(header.getUsePurpose());
|
|
||||||
item.setProductType(header.getProductType());
|
|
||||||
item.setThicknessTolerance(header.getThicknessTolerance());
|
|
||||||
item.setWidthTolerance(header.getWidthTolerance());
|
|
||||||
item.setSurfaceQuality(header.getSurfaceQuality());
|
|
||||||
item.setSurfaceTreatment(header.getSurfaceTreatment());
|
|
||||||
item.setInnerDiameter(header.getInnerDiameter());
|
|
||||||
item.setOuterDiameter(header.getOuterDiameter());
|
|
||||||
item.setPackReq(header.getPackReq());
|
|
||||||
item.setCutEdgeReq(header.getCutEdgeReq());
|
|
||||||
item.setSingleCoilWeight(header.getSingleCoilWeight());
|
|
||||||
item.setWeightDeviation(header.getWeightDeviation());
|
|
||||||
item.setOtherTechReq(header.getOtherTechReq());
|
|
||||||
item.setPaymentDesc(header.getPaymentDesc());
|
|
||||||
item.setRemark(header.getRemark());
|
|
||||||
// 不复制 returnReason
|
|
||||||
|
|
||||||
// 从 detail 复制
|
|
||||||
item.setSpec(detail.getSpec());
|
|
||||||
item.setMaterial(detail.getMaterial());
|
|
||||||
item.setScheduleWeight(detail.getScheduleWeight());
|
|
||||||
item.setProductItem(detail.getProductType());
|
|
||||||
item.setRowRemark(detail.getRemark());
|
|
||||||
|
|
||||||
// 来源追溯(未合并,各存明细ID)
|
|
||||||
item.setScheduleDetailIds(String.valueOf(detail.getScheduleDetailId()));
|
|
||||||
|
|
||||||
validEntityBeforeSave(item);
|
|
||||||
addList.add(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 批量插入
|
// 4. 遍历每条 detail,根据工序步骤生成多条 SchProdScheduleItem
|
||||||
|
List<SchProdScheduleItem> addList = new ArrayList<>();
|
||||||
|
for (SchProdScheduleDetail detail : details) {
|
||||||
|
SchProdScheduleItemReceiveBo.DetailProcessConfig processConfig = processConfigMap.get(detail.getScheduleDetailId());
|
||||||
|
if (processConfig == null || processConfig.getStepList() == null || processConfig.getStepList().isEmpty()) {
|
||||||
|
throw new RuntimeException("明细ID=" + detail.getScheduleDetailId() + " 未配置工序步骤");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据工序步骤数量生成多条排产单明细
|
||||||
|
for (SchProdScheduleItemReceiveBo.StepConfig step : processConfig.getStepList()) {
|
||||||
|
SchProdScheduleItem item = new SchProdScheduleItem();
|
||||||
|
|
||||||
|
// 从 header 复制所有字段
|
||||||
|
item.setScheduleNo(header.getScheduleNo());
|
||||||
|
item.setProdDate(header.getProdDate());
|
||||||
|
item.setScheduleStatus(2L); // 已下达
|
||||||
|
item.setTotalPlanWeight(header.getTotalPlanWeight());
|
||||||
|
item.setRelContractNo(header.getRelContractNo());
|
||||||
|
item.setBusinessUser(header.getBusinessUser());
|
||||||
|
item.setBusinessPhone(header.getBusinessPhone());
|
||||||
|
item.setOrderDate(header.getOrderDate());
|
||||||
|
item.setCustomerName(header.getCustomerName());
|
||||||
|
item.setDeliveryCycle(header.getDeliveryCycle());
|
||||||
|
item.setUsePurpose(header.getUsePurpose());
|
||||||
|
item.setProductType(header.getProductType());
|
||||||
|
item.setThicknessTolerance(header.getThicknessTolerance());
|
||||||
|
item.setWidthTolerance(header.getWidthTolerance());
|
||||||
|
item.setSurfaceQuality(header.getSurfaceQuality());
|
||||||
|
item.setSurfaceTreatment(header.getSurfaceTreatment());
|
||||||
|
item.setInnerDiameter(header.getInnerDiameter());
|
||||||
|
item.setOuterDiameter(header.getOuterDiameter());
|
||||||
|
item.setPackReq(header.getPackReq());
|
||||||
|
item.setCutEdgeReq(header.getCutEdgeReq());
|
||||||
|
item.setSingleCoilWeight(header.getSingleCoilWeight());
|
||||||
|
item.setWeightDeviation(header.getWeightDeviation());
|
||||||
|
item.setOtherTechReq(header.getOtherTechReq());
|
||||||
|
item.setPaymentDesc(header.getPaymentDesc());
|
||||||
|
item.setRemark(header.getRemark());
|
||||||
|
// 不复制 returnReason
|
||||||
|
|
||||||
|
// 从 detail 复制
|
||||||
|
item.setSpec(detail.getSpec());
|
||||||
|
item.setMaterial(detail.getMaterial());
|
||||||
|
item.setScheduleWeight(detail.getScheduleWeight());
|
||||||
|
item.setProductItem(detail.getProductType());
|
||||||
|
item.setRowRemark(detail.getRemark());
|
||||||
|
|
||||||
|
// 设置工序步骤ID为actionId
|
||||||
|
item.setActionId(step.getStepId());
|
||||||
|
|
||||||
|
// 来源追溯(未合并,各存明细ID)
|
||||||
|
item.setScheduleDetailIds(String.valueOf(detail.getScheduleDetailId()));
|
||||||
|
|
||||||
|
validEntityBeforeSave(item);
|
||||||
|
addList.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 批量插入
|
||||||
baseMapper.insertBatch(addList);
|
baseMapper.insertBatch(addList);
|
||||||
|
|
||||||
// 5. 更新产需单状态为 2(已下达)
|
// 6. 更新产需单状态为 2(已下达)
|
||||||
header.setScheduleStatus(2L);
|
header.setScheduleStatus(2L);
|
||||||
schProdScheduleMapper.updateById(header);
|
schProdScheduleMapper.updateById(header);
|
||||||
|
|
||||||
|
|||||||
44
klp-ui/src/api/aps/process.js
Normal file
44
klp-ui/src/api/aps/process.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询工艺列表
|
||||||
|
export function listProcess(query) {
|
||||||
|
return request({
|
||||||
|
url: '/flow/prodProcess/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询工艺详细
|
||||||
|
export function getProcess(processId) {
|
||||||
|
return request({
|
||||||
|
url: '/flow/prodProcess/' + processId,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增工艺
|
||||||
|
export function addProcess(data) {
|
||||||
|
return request({
|
||||||
|
url: '/flow/prodProcess',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改工艺
|
||||||
|
export function updateProcess(data) {
|
||||||
|
return request({
|
||||||
|
url: '/flow/prodProcess',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除工艺
|
||||||
|
export function delProcess(processIds) {
|
||||||
|
return request({
|
||||||
|
url: '/flow/prodProcess/' + processIds,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
44
klp-ui/src/api/aps/processStep.js
Normal file
44
klp-ui/src/api/aps/processStep.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询工艺步骤列表
|
||||||
|
export function listProcessStep(query) {
|
||||||
|
return request({
|
||||||
|
url: '/flow/prodProcessStep/list',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询工艺步骤详细
|
||||||
|
export function getProcessStep(stepId) {
|
||||||
|
return request({
|
||||||
|
url: '/flow/prodProcessStep/' + stepId,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增工艺步骤
|
||||||
|
export function addProcessStep(data) {
|
||||||
|
return request({
|
||||||
|
url: '/flow/prodProcessStep',
|
||||||
|
method: 'post',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改工艺步骤
|
||||||
|
export function updateProcessStep(data) {
|
||||||
|
return request({
|
||||||
|
url: '/flow/prodProcessStep',
|
||||||
|
method: 'put',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除工艺步骤
|
||||||
|
export function delProcessStep(stepIds) {
|
||||||
|
return request({
|
||||||
|
url: '/flow/prodProcessStep/' + stepIds,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -73,12 +73,12 @@ export function delScheduleItem(ids) {
|
|||||||
|
|
||||||
// ====== 排产单明细项 接收/合并 ======
|
// ====== 排产单明细项 接收/合并 ======
|
||||||
|
|
||||||
// 接收产需单(后端全字段复制)
|
// 接收产需单(根据配置的工序步骤生成排产明细)
|
||||||
export function receiveScheduleItem(scheduleId) {
|
export function receiveScheduleItem(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/flow/prodScheduleItem/receive',
|
url: '/flow/prodScheduleItem/receive',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: { scheduleId }
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
112
klp-ui/src/components/KLPService/ProcessSelect/index.vue
Normal file
112
klp-ui/src/components/KLPService/ProcessSelect/index.vue
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
<template>
|
||||||
|
<div class="process-select">
|
||||||
|
<el-select
|
||||||
|
v-model="processId"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:clearable="clearable"
|
||||||
|
:disabled="disabled"
|
||||||
|
filterable
|
||||||
|
class="process-select-input"
|
||||||
|
@clear="handleClear"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in processList"
|
||||||
|
:key="item.processId"
|
||||||
|
:label="item.processName"
|
||||||
|
:value="item.processId"
|
||||||
|
>
|
||||||
|
<span class="process-option-name">{{ item.processName }}</span>
|
||||||
|
<span v-if="item._steps" class="process-option-steps">{{ item._steps }}</span>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listProcess } from '@/api/aps/process'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ProcessSelect',
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: [String, Number, undefined],
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: '请选择工序'
|
||||||
|
},
|
||||||
|
clearable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
processList: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
processId: {
|
||||||
|
get() {
|
||||||
|
return this.value
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
const process = this.processList.find(item => item.processId === val) || null
|
||||||
|
this.$emit('input', val)
|
||||||
|
this.$emit('change', val, process)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getProcessList()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getProcessList() {
|
||||||
|
listProcess({ pageNum: 1, pageSize: 1000 }).then(res => {
|
||||||
|
const rows = res.rows || []
|
||||||
|
this.processList = rows.map(item => ({
|
||||||
|
...item,
|
||||||
|
_steps: (item.stepList && item.stepList.length > 0)
|
||||||
|
? item.stepList.map(s => s.stepName).join(' → ')
|
||||||
|
: ''
|
||||||
|
}))
|
||||||
|
}).catch(() => {
|
||||||
|
this.processList = []
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleClear() {
|
||||||
|
this.$emit('input', undefined)
|
||||||
|
this.$emit('change', undefined, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.process-select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.process-select-input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.process-option-name {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.process-option-steps {
|
||||||
|
display: block;
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #909399;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
589
klp-ui/src/views/wms/post/aps/processes.vue
Normal file
589
klp-ui/src/views/wms/post/aps/processes.vue
Normal file
@@ -0,0 +1,589 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container" style="height: calc(100vh - 84px); display: flex;">
|
||||||
|
<!-- 左侧工艺列表 -->
|
||||||
|
<div class="left-panel" v-loading="processLoading"
|
||||||
|
style="width: 35%; border-right: 1px solid #e4e7ed; overflow-y: auto;">
|
||||||
|
<!-- 筛选区 -->
|
||||||
|
<div class="filter-section" style="padding: 10px; border-bottom: 1px solid #e4e7ed;">
|
||||||
|
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px;">
|
||||||
|
<div style="display: flex; align-items: center; gap: 4px; flex-wrap: wrap;">
|
||||||
|
<el-input v-model="queryParams.processName" placeholder="工艺名称" clearable @keyup.enter.native="handleSearch"
|
||||||
|
style="width: 160px;" />
|
||||||
|
<el-button class="aps-btn-red" icon="el-icon-search" size="mini" @click="handleSearch">筛选</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" class="aps-btn-silver" @click="resetQuery">重置</el-button>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px;">
|
||||||
|
<el-button class="aps-btn-red" icon="el-icon-plus" size="mini" @click="handleAddProcess">新增工艺</el-button>
|
||||||
|
<span style="font-size: 12px; color: #909399;">
|
||||||
|
共 <span class="aps-total-count">{{ total }}</span> 条
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 列表区域 -->
|
||||||
|
<div class="custom-list">
|
||||||
|
<div class="list-body">
|
||||||
|
<div v-for="item in processList" :key="item.processId" class="list-item"
|
||||||
|
:class="{ 'list-item-active': currentProcess && currentProcess.processId === item.processId }"
|
||||||
|
@click="handleProcessClick(item)">
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
|
||||||
|
<div style="font-weight: bold; font-size: 14px;">{{ item.processName }}</div>
|
||||||
|
<div style="display: flex; gap: 4px;">
|
||||||
|
<el-button type="text" icon="el-icon-edit" size="mini" @click.stop="handleUpdateProcess(item)"></el-button>
|
||||||
|
<el-button type="text" icon="el-icon-delete" size="mini" style="color: #F56C6C;" @click.stop="handleDeleteProcess(item)"></el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 12px; color: #909399; margin-bottom: 4px;">
|
||||||
|
{{ item.processDesc || '暂无描述' }}
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 12px; color: #909399;">
|
||||||
|
备注: {{ item.remark || '-' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="processList.length === 0 && !processLoading"
|
||||||
|
style="padding: 40px; text-align: center; color: #909399;">
|
||||||
|
暂无工艺数据
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||||||
|
@pagination="getProcessList" style="padding: 10px; margin-bottom: 10px !important;" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧工艺步骤区域 -->
|
||||||
|
<div class="right-panel" v-if="currentProcess && currentProcess.processId"
|
||||||
|
style="flex: 1; display: flex; flex-direction: column;" v-loading="stepLoading">
|
||||||
|
<div class="detail-panel">
|
||||||
|
<!-- 工艺基本信息卡片 -->
|
||||||
|
<div class="detail-card">
|
||||||
|
<div class="detail-card-header">
|
||||||
|
<span>工艺基本信息</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-card-body">
|
||||||
|
<div class="form-grid-2">
|
||||||
|
<div class="form-field"><label>工艺名称</label>
|
||||||
|
<div class="field-value">{{ currentProcess.processName }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-field"><label>工艺描述</label>
|
||||||
|
<div class="field-value">{{ currentProcess.processDesc || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-field" style="grid-column:1/3;"><label>备注</label>
|
||||||
|
<div class="field-value">{{ currentProcess.remark || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 工艺步骤卡片 -->
|
||||||
|
<div class="detail-card">
|
||||||
|
<div class="detail-card-header">
|
||||||
|
<span>工艺步骤({{ stepList.length }} 步)</span>
|
||||||
|
<el-button type="text" icon="el-icon-plus" size="mini" style="color: white;" @click="handleAddStep">新增步骤</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="detail-card-body">
|
||||||
|
<div v-if="stepList.length > 0" class="process-timeline-wrap">
|
||||||
|
<el-timeline>
|
||||||
|
<el-timeline-item
|
||||||
|
v-for="(step, index) in stepList"
|
||||||
|
:key="step.stepId"
|
||||||
|
:timestamp="'步骤 ' + step.stepOrder"
|
||||||
|
placement="top"
|
||||||
|
:type="index === stepList.length - 1 ? 'success' : 'primary'"
|
||||||
|
:hollow="index !== stepList.length - 1"
|
||||||
|
size="large"
|
||||||
|
>
|
||||||
|
<div class="timeline-content">
|
||||||
|
<div class="timeline-header">
|
||||||
|
<span class="step-name">{{ step.stepName }}</span>
|
||||||
|
<div class="step-actions">
|
||||||
|
<el-button type="text" icon="el-icon-edit" size="mini" @click="handleUpdateStep(step)"></el-button>
|
||||||
|
<el-button type="text" icon="el-icon-delete" size="mini" style="color: #F56C6C;" @click="handleDeleteStep(step)"></el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="step.remark" class="step-remark">{{ step.remark }}</div>
|
||||||
|
</div>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
</div>
|
||||||
|
<el-empty v-else description="暂无工艺步骤" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else style="flex: 1; display: flex; flex-direction: column;">
|
||||||
|
<el-empty description="选择工艺后查看步骤" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 新增/编辑工艺弹窗 -->
|
||||||
|
<el-dialog :title="processTitle" :visible.sync="processOpen" width="500px" append-to-body>
|
||||||
|
<el-form ref="processForm" :model="processForm" :rules="processRules" label-width="80px">
|
||||||
|
<el-form-item label="工艺名称" prop="processName">
|
||||||
|
<el-input v-model="processForm.processName" placeholder="请输入工艺名称" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="工艺描述" prop="processDesc">
|
||||||
|
<el-input v-model="processForm.processDesc" type="textarea" :rows="3" placeholder="请输入工艺描述" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注" prop="remark">
|
||||||
|
<el-input v-model="processForm.remark" placeholder="请输入备注" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" :loading="submitLoading" @click="submitProcessForm">确 定</el-button>
|
||||||
|
<el-button @click="cancelProcess">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 新增/编辑工艺步骤弹窗 -->
|
||||||
|
<el-dialog :title="stepTitle" :visible.sync="stepOpen" width="500px" append-to-body>
|
||||||
|
<el-form ref="stepForm" :model="stepForm" :rules="stepRules" label-width="80px">
|
||||||
|
<el-form-item label="步骤顺序" prop="stepOrder">
|
||||||
|
<el-input-number v-model="stepForm.stepOrder" :min="1" :max="99" controls-position="right" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="步骤名称" prop="stepName">
|
||||||
|
<el-input v-model="stepForm.stepName" placeholder="请输入步骤名称" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注" prop="remark">
|
||||||
|
<el-input v-model="stepForm.remark" placeholder="请输入备注" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" :loading="submitLoading" @click="submitStepForm">确 定</el-button>
|
||||||
|
<el-button @click="cancelStep">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listProcess, getProcess, addProcess, updateProcess, delProcess } from '@/api/aps/process'
|
||||||
|
import { listProcessStep, addProcessStep, updateProcessStep, delProcessStep } from '@/api/aps/processStep'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ApsProcesses',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 工艺相关
|
||||||
|
processLoading: false,
|
||||||
|
processList: [],
|
||||||
|
total: 0,
|
||||||
|
currentProcess: null,
|
||||||
|
queryParams: {
|
||||||
|
processName: '',
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10
|
||||||
|
},
|
||||||
|
processTitle: '',
|
||||||
|
processOpen: false,
|
||||||
|
processForm: {},
|
||||||
|
processRules: {
|
||||||
|
processName: [
|
||||||
|
{ required: true, message: '工艺名称不能为空', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// 工艺步骤相关
|
||||||
|
stepLoading: false,
|
||||||
|
submitLoading: false,
|
||||||
|
stepList: [],
|
||||||
|
stepTitle: '',
|
||||||
|
stepOpen: false,
|
||||||
|
stepForm: {},
|
||||||
|
stepRules: {
|
||||||
|
stepOrder: [
|
||||||
|
{ required: true, message: '步骤顺序不能为空', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
stepName: [
|
||||||
|
{ required: true, message: '步骤名称不能为空', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getProcessList()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/** 查询工艺列表 */
|
||||||
|
getProcessList() {
|
||||||
|
this.processLoading = true
|
||||||
|
listProcess(this.queryParams).then(res => {
|
||||||
|
this.processList = res.rows || []
|
||||||
|
this.total = res.total || 0
|
||||||
|
}).catch(() => {
|
||||||
|
this.processList = []
|
||||||
|
this.total = 0
|
||||||
|
}).finally(() => {
|
||||||
|
this.processLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/** 搜索 */
|
||||||
|
handleSearch() {
|
||||||
|
this.queryParams.pageNum = 1
|
||||||
|
this.getProcessList()
|
||||||
|
},
|
||||||
|
/** 重置 */
|
||||||
|
resetQuery() {
|
||||||
|
this.queryParams.processName = ''
|
||||||
|
this.handleSearch()
|
||||||
|
},
|
||||||
|
/** 点击工艺 */
|
||||||
|
handleProcessClick(process) {
|
||||||
|
this.currentProcess = process
|
||||||
|
this.getStepList(process.processId)
|
||||||
|
},
|
||||||
|
/** 查询工艺步骤列表 */
|
||||||
|
getStepList(processId) {
|
||||||
|
this.stepLoading = true
|
||||||
|
listProcessStep({ processId }).then(res => {
|
||||||
|
this.stepList = res.rows || []
|
||||||
|
}).catch(() => {
|
||||||
|
this.stepList = []
|
||||||
|
}).finally(() => {
|
||||||
|
this.stepLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/** 新增工艺 */
|
||||||
|
handleAddProcess() {
|
||||||
|
this.resetProcessForm()
|
||||||
|
this.processTitle = '新增工艺'
|
||||||
|
this.processOpen = true
|
||||||
|
},
|
||||||
|
/** 修改工艺 */
|
||||||
|
handleUpdateProcess(row) {
|
||||||
|
this.resetProcessForm()
|
||||||
|
const processId = row.processId || this.currentProcess.processId
|
||||||
|
getProcess(processId).then(res => {
|
||||||
|
this.processForm = res.data
|
||||||
|
this.processTitle = '修改工艺'
|
||||||
|
this.processOpen = true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/** 提交工艺表单 */
|
||||||
|
submitProcessForm() {
|
||||||
|
this.$refs['processForm'].validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.submitLoading = true
|
||||||
|
if (this.processForm.processId != undefined) {
|
||||||
|
updateProcess(this.processForm).then(response => {
|
||||||
|
this.$modal.msgSuccess('修改成功')
|
||||||
|
this.processOpen = false
|
||||||
|
this.getProcessList()
|
||||||
|
}).finally(() => {
|
||||||
|
this.submitLoading = false
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
addProcess(this.processForm).then(response => {
|
||||||
|
this.$modal.msgSuccess('新增成功')
|
||||||
|
this.processOpen = false
|
||||||
|
this.getProcessList()
|
||||||
|
}).finally(() => {
|
||||||
|
this.submitLoading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/** 删除工艺 */
|
||||||
|
handleDeleteProcess(row) {
|
||||||
|
const processIds = row.processId || this.currentProcess.processId
|
||||||
|
this.$modal.confirm('是否确认删除该工艺?').then(() => {
|
||||||
|
return delProcess(processIds)
|
||||||
|
}).then(() => {
|
||||||
|
this.getProcessList()
|
||||||
|
this.$modal.msgSuccess('删除成功')
|
||||||
|
this.currentProcess = null
|
||||||
|
this.stepList = []
|
||||||
|
}).catch(() => {})
|
||||||
|
},
|
||||||
|
/** 取消工艺弹窗 */
|
||||||
|
cancelProcess() {
|
||||||
|
this.processOpen = false
|
||||||
|
this.resetProcessForm()
|
||||||
|
},
|
||||||
|
/** 重置工艺表单 */
|
||||||
|
resetProcessForm() {
|
||||||
|
this.processForm = {
|
||||||
|
processId: undefined,
|
||||||
|
processName: undefined,
|
||||||
|
processDesc: undefined,
|
||||||
|
remark: undefined
|
||||||
|
}
|
||||||
|
this.resetForm('processForm')
|
||||||
|
},
|
||||||
|
/** 新增步骤 */
|
||||||
|
handleAddStep() {
|
||||||
|
this.resetStepForm()
|
||||||
|
this.stepForm.processId = this.currentProcess.processId
|
||||||
|
// 自动计算下一步骤顺序
|
||||||
|
if (this.stepList.length > 0) {
|
||||||
|
const maxOrder = Math.max(...this.stepList.map(s => s.stepOrder || 0))
|
||||||
|
this.stepForm.stepOrder = maxOrder + 1
|
||||||
|
} else {
|
||||||
|
this.stepForm.stepOrder = 1
|
||||||
|
}
|
||||||
|
this.stepTitle = '新增工艺步骤'
|
||||||
|
this.stepOpen = true
|
||||||
|
},
|
||||||
|
/** 修改步骤 */
|
||||||
|
handleUpdateStep(row) {
|
||||||
|
this.resetStepForm()
|
||||||
|
this.stepForm = { ...row }
|
||||||
|
this.stepTitle = '修改工艺步骤'
|
||||||
|
this.stepOpen = true
|
||||||
|
},
|
||||||
|
/** 提交步骤表单 */
|
||||||
|
submitStepForm() {
|
||||||
|
this.$refs['stepForm'].validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
this.submitLoading = true
|
||||||
|
if (this.stepForm.stepId != undefined) {
|
||||||
|
updateProcessStep(this.stepForm).then(response => {
|
||||||
|
this.$modal.msgSuccess('修改成功')
|
||||||
|
this.stepOpen = false
|
||||||
|
this.getStepList(this.currentProcess.processId)
|
||||||
|
}).finally(() => {
|
||||||
|
this.submitLoading = false
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
addProcessStep(this.stepForm).then(response => {
|
||||||
|
this.$modal.msgSuccess('新增成功')
|
||||||
|
this.stepOpen = false
|
||||||
|
this.getStepList(this.currentProcess.processId)
|
||||||
|
}).finally(() => {
|
||||||
|
this.submitLoading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/** 删除步骤 */
|
||||||
|
handleDeleteStep(row) {
|
||||||
|
const stepIds = row.stepId
|
||||||
|
this.$modal.confirm('是否确认删除该工艺步骤?').then(() => {
|
||||||
|
return delProcessStep(stepIds)
|
||||||
|
}).then(() => {
|
||||||
|
this.getStepList(this.currentProcess.processId)
|
||||||
|
this.$modal.msgSuccess('删除成功')
|
||||||
|
}).catch(() => {})
|
||||||
|
},
|
||||||
|
/** 取消步骤弹窗 */
|
||||||
|
cancelStep() {
|
||||||
|
this.stepOpen = false
|
||||||
|
this.resetStepForm()
|
||||||
|
},
|
||||||
|
/** 重置步骤表单 */
|
||||||
|
resetStepForm() {
|
||||||
|
this.stepForm = {
|
||||||
|
stepId: undefined,
|
||||||
|
processId: undefined,
|
||||||
|
stepOrder: undefined,
|
||||||
|
stepName: undefined,
|
||||||
|
remark: undefined
|
||||||
|
}
|
||||||
|
this.resetForm('stepForm')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import './scss/aps-theme.scss';
|
||||||
|
|
||||||
|
.app-container {
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-panel {
|
||||||
|
height: calc(100vh - 84px);
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-panel {
|
||||||
|
height: calc(100vh - 84px);
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-list {
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item {
|
||||||
|
padding: 12px;
|
||||||
|
border-bottom: 2px solid #dddddd;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item:hover {
|
||||||
|
background-color: $aps-silver-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item-active {
|
||||||
|
background-color: $aps-red-1;
|
||||||
|
border-left: 3px solid $aps-red-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aps-total-count {
|
||||||
|
color: $aps-red-2;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aps-btn-red {
|
||||||
|
@include aps-btn-red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aps-btn-silver {
|
||||||
|
@include aps-btn-silver;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-panel {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: scroll;
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
background: $aps-bg;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card {
|
||||||
|
background: $aps-white;
|
||||||
|
border: 1px solid $aps-border;
|
||||||
|
border-radius: $aps-radius;
|
||||||
|
box-shadow: $aps-shadow-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card-header {
|
||||||
|
background: linear-gradient(to right, $aps-red-2, $aps-red-3);
|
||||||
|
color: white;
|
||||||
|
padding: 8px 14px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card-body {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-grid-2 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 10px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field label {
|
||||||
|
font-size: 11px;
|
||||||
|
color: $aps-text-muted;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-field .field-value {
|
||||||
|
font-size: 13px;
|
||||||
|
color: $aps-text;
|
||||||
|
padding: 4px 0;
|
||||||
|
border-bottom: 1px solid $aps-silver-mid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aps-product-table {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
::v-deep th {
|
||||||
|
background: $aps-silver-1 !important;
|
||||||
|
color: $aps-silver-5 !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====== 工艺步骤时间线 ======
|
||||||
|
.process-timeline-wrap {
|
||||||
|
padding: 20px 10px;
|
||||||
|
|
||||||
|
::v-deep .el-timeline {
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-timeline-item__wrapper {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-timeline-item__timestamp {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $aps-text-muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-timeline-item__tail {
|
||||||
|
border-left-color: $aps-silver-3;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-timeline-item__node--primary {
|
||||||
|
background-color: $aps-red-2;
|
||||||
|
border-color: $aps-red-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-timeline-item__node--success {
|
||||||
|
background-color: #67C23A;
|
||||||
|
border-color: #67C23A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-content {
|
||||||
|
background: $aps-white;
|
||||||
|
border: 1px solid $aps-border;
|
||||||
|
border-radius: $aps-radius;
|
||||||
|
padding: 12px 16px;
|
||||||
|
transition: box-shadow 0.2s, border-color 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: $aps-red-2;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-name {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $aps-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
|
||||||
|
.timeline-content:hover & {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-remark {
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: $aps-text-muted;
|
||||||
|
padding-top: 8px;
|
||||||
|
border-top: 1px dashed $aps-silver-3;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
<div class="filter-section" style="padding: 10px; border-bottom: 1px solid #e4e7ed;">
|
<div class="filter-section" style="padding: 10px; border-bottom: 1px solid #e4e7ed;">
|
||||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px;">
|
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px;">
|
||||||
<div style="display: flex; align-items: center; gap: 4px; flex-wrap: wrap;">
|
<div style="display: flex; align-items: center; gap: 4px; flex-wrap: wrap;">
|
||||||
<el-input v-model="queryParams.scheduleNo" placeholder="排产单号" clearable @keyup.enter.native="handleSearch"
|
<el-input v-model="queryParams.scheduleNo" placeholder="产需单号" clearable @keyup.enter.native="handleSearch"
|
||||||
style="width: 130px;" size="small" />
|
style="width: 130px;" size="small" />
|
||||||
<el-input v-model="queryParams.customerName" placeholder="客户名" clearable
|
<el-input v-model="queryParams.customerName" placeholder="客户名" clearable
|
||||||
@keyup.enter.native="handleSearch" style="width: 130px;" size="small" />
|
@keyup.enter.native="handleSearch" style="width: 130px;" size="small" />
|
||||||
@@ -58,15 +58,19 @@
|
|||||||
<div class="detail-card-header">
|
<div class="detail-card-header">
|
||||||
<span>产需单信息</span>
|
<span>产需单信息</span>
|
||||||
<div style="display:flex; gap:6px;">
|
<div style="display:flex; gap:6px;">
|
||||||
<button class="header-btn" :disabled="currentReq.scheduleStatus !== 0 && currentReq.scheduleStatus !== 3" @click="handleEdit(currentReq)">编辑</button>
|
<button class="header-btn"
|
||||||
<button v-if="currentReq.scheduleStatus === 0 || currentReq.scheduleStatus === 3" class="header-btn" style="background:rgba(255,255,255,0.4);border-color:rgba(255,255,255,0.7);" @click="handleDispatch">{{ currentReq.scheduleStatus === 3 ? '重新下发' : '下发' }}</button>
|
:disabled="currentReq.scheduleStatus !== 0 && currentReq.scheduleStatus !== 3"
|
||||||
|
@click="handleEdit(currentReq)">编辑</button>
|
||||||
|
<button v-if="currentReq.scheduleStatus === 0 || currentReq.scheduleStatus === 3" class="header-btn"
|
||||||
|
style="background:rgba(255,255,255,0.4);border-color:rgba(255,255,255,0.7);"
|
||||||
|
@click="handleDispatch">{{ currentReq.scheduleStatus === 3 ? '重新下发' : '下发' }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-card-body">
|
<div class="detail-card-body">
|
||||||
<table class="req-info-table">
|
<table class="req-info-table">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="req-td-label">排产单号</td>
|
<td class="req-td-label">产需单号</td>
|
||||||
<td class="req-td-value">{{ currentReq.scheduleNo }}</td>
|
<td class="req-td-value">{{ currentReq.scheduleNo }}</td>
|
||||||
<td class="req-td-label">生产日期</td>
|
<td class="req-td-label">生产日期</td>
|
||||||
<td class="req-td-value">{{ currentReq.prodDate }}</td>
|
<td class="req-td-value">{{ currentReq.prodDate }}</td>
|
||||||
@@ -75,7 +79,7 @@
|
|||||||
<td class="req-td-label">排产状态</td>
|
<td class="req-td-label">排产状态</td>
|
||||||
<td class="req-td-value"><span class="aps-status-tag"
|
<td class="req-td-value"><span class="aps-status-tag"
|
||||||
:class="'status-' + (currentReq.scheduleStatus || 0)">{{ statusMap[currentReq.scheduleStatus] ||
|
:class="'status-' + (currentReq.scheduleStatus || 0)">{{ statusMap[currentReq.scheduleStatus] ||
|
||||||
'未知' }}</span></td>
|
'未知' }}</span></td>
|
||||||
<td class="req-td-label">业务员</td>
|
<td class="req-td-label">业务员</td>
|
||||||
<td class="req-td-value">{{ currentReq.businessUser }}</td>
|
<td class="req-td-value">{{ currentReq.businessUser }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -139,7 +143,8 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr v-if="currentReq.returnReason">
|
<tr v-if="currentReq.returnReason">
|
||||||
<td class="req-td-label" style="color:#e74c3c;">驳回原因</td>
|
<td class="req-td-label" style="color:#e74c3c;">驳回原因</td>
|
||||||
<td class="req-td-value" colspan="3" style="color:#e74c3c;background:#fdecea;">{{ currentReq.returnReason }}</td>
|
<td class="req-td-value" colspan="3" style="color:#e74c3c;background:#fdecea;">{{
|
||||||
|
currentReq.returnReason }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -190,8 +195,10 @@
|
|||||||
<el-table-column label="操作" width="130" align="center" fixed="right">
|
<el-table-column label="操作" width="130" align="center" fixed="right">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span style="white-space:nowrap;">
|
<span style="white-space:nowrap;">
|
||||||
<el-button v-if="canEdit" type="text" size="small" style="color:#409EFF;" @click="handleDetailEdit(scope.row)">编辑</el-button>
|
<el-button v-if="canEdit" type="text" size="small" style="color:#409EFF;"
|
||||||
<el-button v-if="canEdit" type="text" size="small" style="color:#ff4d4f;" @click="handleDetailDelete(scope.row)">删除</el-button>
|
@click="handleDetailEdit(scope.row)">编辑</el-button>
|
||||||
|
<el-button v-if="canEdit" type="text" size="small" style="color:#ff4d4f;"
|
||||||
|
@click="handleDetailDelete(scope.row)">删除</el-button>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -204,13 +211,15 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<!-- 新增/编辑对话框 -->
|
<!-- 新增/编辑对话框 -->
|
||||||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="700px" append-to-body
|
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="900px" append-to-body
|
||||||
:close-on-click-modal="false">
|
:close-on-click-modal="false">
|
||||||
<el-form ref="reqForm" :model="reqForm" :rules="reqRules" label-width="110px" size="small">
|
<div style="display: flex; gap: 16px;">
|
||||||
<!-- 第一行:必填项 -->
|
<div style="flex: 1; min-width: 0;">
|
||||||
|
<el-form ref="reqForm" :model="reqForm" :rules="reqRules" label-width="80px" size="small">
|
||||||
|
<!-- 第一行:必填项 -->
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="排产单号" prop="scheduleNo">
|
<el-form-item label="产需单号" prop="scheduleNo">
|
||||||
<el-input v-model="reqForm.scheduleNo" placeholder="自动生成或手动填写" />
|
<el-input v-model="reqForm.scheduleNo" placeholder="自动生成或手动填写" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -282,19 +291,42 @@
|
|||||||
<el-form-item label="交货重量偏差" prop="weightDeviation">
|
<el-form-item label="交货重量偏差" prop="weightDeviation">
|
||||||
<el-input v-model="reqForm.weightDeviation" />
|
<el-input v-model="reqForm.weightDeviation" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="其他技术要求" prop="otherTechReq">
|
|
||||||
<el-input v-model="reqForm.otherTechReq" type="textarea" :rows="2" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="付款情况说明" prop="paymentDesc">
|
<el-form-item label="付款情况说明" prop="paymentDesc">
|
||||||
<el-input v-model="reqForm.paymentDesc" type="textarea" :rows="2" />
|
<el-input v-model="reqForm.paymentDesc" type="textarea" :rows="2" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="其他技术要求" prop="otherTechReq">
|
||||||
|
<el-input v-model="reqForm.otherTechReq" type="textarea" :rows="2" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
<el-form-item label="备注" prop="remark">
|
<el-form-item label="备注" prop="remark">
|
||||||
<el-input v-model="reqForm.remark" type="textarea" :rows="2" />
|
<el-input v-model="reqForm.remark" type="textarea" :rows="2" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<!-- 右侧:历史记录 -->
|
||||||
|
<div style="width: 200px; flex-shrink: 0;" v-if="!reqForm.scheduleId && scheduleNoHistory.length > 0">
|
||||||
|
<div style="font-size: 13px; font-weight: 600; color: #2c3e50; margin-bottom: 8px; padding-left: 8px; border-left: 3px solid #ff4d4f;">
|
||||||
|
历史记录
|
||||||
|
</div>
|
||||||
|
<div style="border: 1px solid #e4e7ed; border-radius: 4px; max-height: 450px; overflow-y: auto;">
|
||||||
|
<div v-for="item in scheduleNoHistory" :key="item.scheduleNo" class="history-item"
|
||||||
|
@click="useHistoryRecord(item)"
|
||||||
|
style="padding: 8px 10px; border-bottom: 1px solid #ecf0f1; cursor: pointer; font-size: 12px; transition: background 0.15s;">
|
||||||
|
<div style="font-weight: 500; color: #2c3e50;">{{ item.scheduleNo }}</div>
|
||||||
|
<div style="color: #7f8c8d; margin-top: 2px;">{{ item.customerName || '-' }}</div>
|
||||||
|
<div style="color: #b0b0b0; font-size: 11px;">{{ item.prodDate || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
<el-button :loading="btnLoading" type="danger" @click="submitForm">确 定</el-button>
|
<el-button :loading="btnLoading" type="danger" @click="submitForm">确 定</el-button>
|
||||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||||
@@ -376,7 +408,8 @@
|
|||||||
<el-input v-model="detailForm.material" placeholder="请输入材质" />
|
<el-input v-model="detailForm.material" placeholder="请输入材质" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="排产吨数" prop="scheduleWeight">
|
<el-form-item label="排产吨数" prop="scheduleWeight">
|
||||||
<el-input-number v-model="detailForm.scheduleWeight" :min="0" :precision="3" :controls="false" style="width:100%" />
|
<el-input-number v-model="detailForm.scheduleWeight" :min="0" :precision="3" :controls="false"
|
||||||
|
style="width:100%" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="备注" prop="remark">
|
<el-form-item label="备注" prop="remark">
|
||||||
<el-input v-model="detailForm.remark" type="textarea" :rows="2" placeholder="可选" />
|
<el-input v-model="detailForm.remark" type="textarea" :rows="2" placeholder="可选" />
|
||||||
@@ -458,9 +491,10 @@ export default {
|
|||||||
dialogVisible: false,
|
dialogVisible: false,
|
||||||
dialogTitle: '新增产需单',
|
dialogTitle: '新增产需单',
|
||||||
btnLoading: false,
|
btnLoading: false,
|
||||||
|
scheduleNoHistory: [],
|
||||||
reqForm: this.getEmptyForm(),
|
reqForm: this.getEmptyForm(),
|
||||||
reqRules: {
|
reqRules: {
|
||||||
scheduleNo: [{ required: true, message: '排产单号不能为空', trigger: 'blur' }],
|
scheduleNo: [{ required: true, message: '产需单号不能为空', trigger: 'blur' }],
|
||||||
prodDate: [{ required: true, message: '生产日期不能为空', trigger: 'change' }]
|
prodDate: [{ required: true, message: '生产日期不能为空', trigger: 'change' }]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -564,6 +598,17 @@ export default {
|
|||||||
|
|
||||||
handleAdd() {
|
handleAdd() {
|
||||||
this.resetForm()
|
this.resetForm()
|
||||||
|
this.reqForm.scheduleNo = this.generateScheduleNo()
|
||||||
|
this.reqForm.prodDate = this.getTodayStr()
|
||||||
|
const lastForm = this.getLastFormData()
|
||||||
|
if (lastForm) {
|
||||||
|
Object.keys(lastForm).forEach(key => {
|
||||||
|
if (key !== 'scheduleId' && key !== 'scheduleNo' && key !== 'prodDate' && lastForm[key] !== undefined && lastForm[key] !== '' && lastForm[key] !== null) {
|
||||||
|
this.reqForm[key] = lastForm[key]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.scheduleNoHistory = this.getScheduleNoHistory()
|
||||||
this.dialogTitle = '新增产需单'
|
this.dialogTitle = '新增产需单'
|
||||||
this.dialogVisible = true
|
this.dialogVisible = true
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
@@ -586,9 +631,14 @@ export default {
|
|||||||
this.$refs.reqForm.validate(valid => {
|
this.$refs.reqForm.validate(valid => {
|
||||||
if (!valid) return
|
if (!valid) return
|
||||||
this.btnLoading = true
|
this.btnLoading = true
|
||||||
const action = this.reqForm.scheduleId ? updateRequirement : addRequirement
|
const isNew = !this.reqForm.scheduleId
|
||||||
|
const action = isNew ? addRequirement : updateRequirement
|
||||||
action(this.reqForm).then(() => {
|
action(this.reqForm).then(() => {
|
||||||
this.$modal.msgSuccess(this.reqForm.scheduleId ? '修改成功' : '新增成功')
|
this.$modal.msgSuccess(isNew ? '新增成功' : '修改成功')
|
||||||
|
if (isNew) {
|
||||||
|
this.saveHistoryRecord(this.reqForm)
|
||||||
|
this.saveLastFormData(this.reqForm)
|
||||||
|
}
|
||||||
this.dialogVisible = false
|
this.dialogVisible = false
|
||||||
this.getList()
|
this.getList()
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
@@ -615,6 +665,71 @@ export default {
|
|||||||
}).catch(() => { })
|
}).catch(() => { })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getTodayStr() {
|
||||||
|
return new Date().toISOString().split('T')[0]
|
||||||
|
},
|
||||||
|
|
||||||
|
generateScheduleNo() {
|
||||||
|
const now = new Date()
|
||||||
|
const dateStr = now.getFullYear().toString() +
|
||||||
|
(now.getMonth() + 1).toString().padStart(2, '0') +
|
||||||
|
now.getDate().toString().padStart(2, '0')
|
||||||
|
const prefix = 'PCXD' + dateStr
|
||||||
|
const existingNums = this.reqList
|
||||||
|
.filter(item => item.scheduleNo && item.scheduleNo.startsWith(prefix))
|
||||||
|
.map(item => {
|
||||||
|
const seq = parseInt(item.scheduleNo.substring(prefix.length), 10)
|
||||||
|
return isNaN(seq) ? 0 : seq
|
||||||
|
})
|
||||||
|
const maxSeq = existingNums.length > 0 ? Math.max(...existingNums) : 0
|
||||||
|
const newSeq = (maxSeq + 1).toString().padStart(3, '0')
|
||||||
|
return prefix + newSeq
|
||||||
|
},
|
||||||
|
|
||||||
|
getScheduleNoHistory() {
|
||||||
|
try {
|
||||||
|
return JSON.parse(localStorage.getItem('aps_requirement_history') || '[]')
|
||||||
|
} catch (e) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
saveHistoryRecord(formData) {
|
||||||
|
if (!formData.scheduleNo) return
|
||||||
|
const history = this.getScheduleNoHistory()
|
||||||
|
const idx = history.findIndex(item => item.scheduleNo === formData.scheduleNo)
|
||||||
|
if (idx >= 0) history.splice(idx, 1)
|
||||||
|
history.unshift({ ...formData })
|
||||||
|
delete history[0].scheduleId
|
||||||
|
if (history.length > 20) history.pop()
|
||||||
|
localStorage.setItem('aps_requirement_history', JSON.stringify(history))
|
||||||
|
},
|
||||||
|
|
||||||
|
useHistoryRecord(item) {
|
||||||
|
Object.keys(item).forEach(key => {
|
||||||
|
if (key !== 'scheduleId' && item[key] !== undefined) {
|
||||||
|
this.reqForm[key] = item[key]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.reqForm && this.$refs.reqForm.clearValidate()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
getLastFormData() {
|
||||||
|
try {
|
||||||
|
return JSON.parse(localStorage.getItem('aps_requirement_last_form') || 'null')
|
||||||
|
} catch (e) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
saveLastFormData(formData) {
|
||||||
|
const data = { ...formData }
|
||||||
|
delete data.scheduleId
|
||||||
|
localStorage.setItem('aps_requirement_last_form', JSON.stringify(data))
|
||||||
|
},
|
||||||
|
|
||||||
openBindDialog() {
|
openBindDialog() {
|
||||||
this.bindDialogVisible = true
|
this.bindDialogVisible = true
|
||||||
this.selectedBindOrders = []
|
this.selectedBindOrders = []
|
||||||
@@ -1223,4 +1338,8 @@ export default {
|
|||||||
.aps-bind-dialog ::v-deep .el-dialog__body {
|
.aps-bind-dialog ::v-deep .el-dialog__body {
|
||||||
padding: 16px 20px 0 20px;
|
padding: 16px 20px 0 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.history-item:hover {
|
||||||
|
background: #fdecea;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
<el-button size="small" class="aps-btn-red" icon="el-icon-search" @click="handleQuery">查询</el-button>
|
<el-button size="small" class="aps-btn-red" icon="el-icon-search" @click="handleQuery">查询</el-button>
|
||||||
<el-tabs v-model="activeTab" size="small" style="margin:0 0 0 16px;" @tab-click="handleQuery">
|
<el-tabs v-model="activeTab" size="small" style="margin:0 0 0 16px;" @tab-click="handleQuery">
|
||||||
<el-tab-pane label="待审核" name="pending" />
|
<el-tab-pane label="待审核" name="pending" />
|
||||||
|
<el-tab-pane label="已接收" name="accepted" />
|
||||||
<el-tab-pane label="已排产" name="scheduled" />
|
<el-tab-pane label="已排产" name="scheduled" />
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
<div v-if="summaryText" class="aps-sch-summary">
|
<div v-if="summaryText" class="aps-sch-summary">
|
||||||
@@ -161,6 +162,104 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 已接收 Tab -->
|
||||||
|
<div v-loading="acceptedLoading" v-show="activeTab === 'accepted'" class="detail-card aps-sch-card">
|
||||||
|
<div class="detail-card-header">
|
||||||
|
<span>已接收产需单</span>
|
||||||
|
<span v-if="acceptedScheduleList.length > 0" style="font-weight:normal;font-size:12px;opacity:0.8;">
|
||||||
|
共 {{ acceptedScheduleList.length }} 个产需单
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-card-body" style="padding:0;">
|
||||||
|
<div v-if="acceptedScheduleList.length > 0" class="aps-sch-list">
|
||||||
|
<div v-for="sch in acceptedScheduleList" :key="sch.scheduleId" class="aps-sch-item">
|
||||||
|
<div class="aps-sch-item-header">
|
||||||
|
<div class="aps-sch-item-header-left">
|
||||||
|
<span class="aps-sch-no">{{ sch.scheduleNo }}</span>
|
||||||
|
<span class="aps-status-tag status-2">已接收</span>
|
||||||
|
<span class="aps-sch-header-extra">
|
||||||
|
{{ sch.customerName }} · {{ sch.businessUser }} · {{ sch.deliveryCycle }}天
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="req-info-table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="req-td-label">排产单号</td><td class="req-td-value">{{ sch.scheduleNo }}</td>
|
||||||
|
<td class="req-td-label">生产日期</td><td class="req-td-value">{{ sch.prodDate }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="req-td-label">业务员</td><td class="req-td-value">{{ sch.businessUser }}</td>
|
||||||
|
<td class="req-td-label">联系电话</td><td class="req-td-value">{{ sch.businessPhone }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="req-td-label">订货单位</td><td class="req-td-value">{{ sch.customerName }}</td>
|
||||||
|
<td class="req-td-label">交货期(天)</td><td class="req-td-value">{{ sch.deliveryCycle }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="req-td-label">品名</td><td class="req-td-value" colspan="3">{{ sch.productType }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="req-td-label">产品用途</td><td class="req-td-value" colspan="3">{{ sch.usePurpose }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="req-td-label">厚度公差</td><td class="req-td-value">{{ sch.thicknessTolerance }}</td>
|
||||||
|
<td class="req-td-label">宽度公差</td><td class="req-td-value">{{ sch.widthTolerance }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="req-td-label">表面质量</td><td class="req-td-value">{{ sch.surfaceQuality }}</td>
|
||||||
|
<td class="req-td-label">表面处理</td><td class="req-td-value">{{ sch.surfaceTreatment }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="req-td-label">内径尺寸</td><td class="req-td-value">{{ sch.innerDiameter }}</td>
|
||||||
|
<td class="req-td-label">外径要求</td><td class="req-td-value">{{ sch.outerDiameter }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="req-td-label">包装要求</td><td class="req-td-value">{{ sch.packReq }}</td>
|
||||||
|
<td class="req-td-label">切边要求</td><td class="req-td-value">{{ sch.cutEdgeReq }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="req-td-label">单件重量</td><td class="req-td-value">{{ sch.singleCoilWeight }}</td>
|
||||||
|
<td class="req-td-label">交货重量偏差</td><td class="req-td-value">{{ sch.weightDeviation }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="sch.otherTechReq">
|
||||||
|
<td class="req-td-label">其他技术要求</td><td class="req-td-value" colspan="3">{{ sch.otherTechReq }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="sch.paymentDesc">
|
||||||
|
<td class="req-td-label">付款情况说明</td><td class="req-td-value" colspan="3">{{ sch.paymentDesc }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="sch.remark">
|
||||||
|
<td class="req-td-label">备注</td><td class="req-td-value" colspan="3">{{ sch.remark }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div v-if="(sch.detailList || []).length > 0" class="aps-sch-item-details">
|
||||||
|
<div class="aps-sch-detail-header">
|
||||||
|
<span class="aps-sch-detail-col col-spec">规格</span>
|
||||||
|
<span class="aps-sch-detail-col col-material">材质</span>
|
||||||
|
<span class="aps-sch-detail-col col-weight">排产吨数</span>
|
||||||
|
<span class="aps-sch-detail-col col-type">品名</span>
|
||||||
|
<span class="aps-sch-detail-col col-remark">备注</span>
|
||||||
|
</div>
|
||||||
|
<div v-for="(d, di) in sch.detailList" :key="di" class="aps-sch-detail-row" @click="handleDetailClick(sch, d)">
|
||||||
|
<span class="aps-sch-detail-col col-spec">{{ d.spec }}</span>
|
||||||
|
<span class="aps-sch-detail-col col-material">{{ d.material }}</span>
|
||||||
|
<span class="aps-sch-detail-col col-weight">{{ d.scheduleWeight }}</span>
|
||||||
|
<span class="aps-sch-detail-col col-type">{{ sch.productType || d.productType || '' }}</span>
|
||||||
|
<span class="aps-sch-detail-col col-remark">{{ d.remark }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="aps-sch-item-empty">暂无明细</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="!acceptedLoading" style="padding:40px;text-align:center;color:#909399;">
|
||||||
|
{{ hasQueried ? '该日期暂无已接收产需单' : '请选择日期查询' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 已排产 Tab -->
|
<!-- 已排产 Tab -->
|
||||||
<div v-loading="schLoading" v-show="activeTab === 'scheduled'" class="detail-card aps-sch-card">
|
<div v-loading="schLoading" v-show="activeTab === 'scheduled'" class="detail-card aps-sch-card">
|
||||||
<div class="detail-card-header">
|
<div class="detail-card-header">
|
||||||
@@ -169,85 +268,73 @@
|
|||||||
<span v-if="scheduledItemList.length > 0" style="font-weight:normal;font-size:12px;opacity:0.8;">
|
<span v-if="scheduledItemList.length > 0" style="font-weight:normal;font-size:12px;opacity:0.8;">
|
||||||
共 {{ scheduledItemList.length }} 条
|
共 {{ scheduledItemList.length }} 条
|
||||||
</span>
|
</span>
|
||||||
<span v-if="selectedItems.length >= 2" style="margin-left: auto;">
|
|
||||||
<el-button
|
|
||||||
size="mini"
|
|
||||||
type="warning"
|
|
||||||
icon="el-icon-link"
|
|
||||||
@click.stop="handleMergePrepare"
|
|
||||||
>
|
|
||||||
合并选中项 ({{ selectedItems.length }})
|
|
||||||
</el-button>
|
|
||||||
</span>
|
|
||||||
<span v-else style="margin-left: auto;">
|
|
||||||
<el-button
|
|
||||||
size="mini"
|
|
||||||
type="warning"
|
|
||||||
icon="el-icon-link"
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
合并选中项
|
|
||||||
</el-button>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div element-loading-text="正在加载已排产数据..." class="detail-card-body" style="padding:0;">
|
<div element-loading-text="正在加载已排产数据..." class="detail-card-body" style="padding:0;">
|
||||||
<el-table
|
<template v-if="scheduledItemList.length > 0">
|
||||||
v-if="scheduledItemList.length > 0"
|
<!-- 步骤工序 Tab -->
|
||||||
ref="scheduledTable"
|
<div class="aps-step-tabs">
|
||||||
:data="scheduledItemList"
|
<span
|
||||||
border
|
v-for="tab in scheduledStepTabs"
|
||||||
size="small"
|
:key="tab"
|
||||||
class="aps-table"
|
class="aps-step-tab"
|
||||||
:row-class-name="getItemRowClassName"
|
:class="{ 'aps-step-tab-active': scheduledStepTab === tab }"
|
||||||
@selection-change="handleSelectionChange"
|
@click="scheduledStepTab = tab"
|
||||||
>
|
>
|
||||||
<el-table-column type="selection" width="45" align="center" />
|
{{ tab }}
|
||||||
<el-table-column label="排产单号" prop="scheduleNo" min-width="140" show-overflow-tooltip />
|
<span class="aps-step-tab-count">{{ scheduledItemList.filter(i => normalizeStepName(i.stepName) === tab).length }}</span>
|
||||||
<el-table-column label="生产日期" prop="prodDate" width="110" align="center" show-overflow-tooltip />
|
</span>
|
||||||
<el-table-column label="工序类型" prop="actionId" width="100" align="center" show-overflow-tooltip>
|
</div>
|
||||||
<template slot-scope="scope">
|
<!-- 表格 -->
|
||||||
{{ getActionIdName(scope.row.actionId) }}
|
<el-table
|
||||||
</template>
|
ref="scheduledTable"
|
||||||
</el-table-column>
|
:data="currentStepItems"
|
||||||
<el-table-column label="排产状态" prop="scheduleStatus" width="90" align="center">
|
border
|
||||||
<template slot-scope="scope">
|
size="small"
|
||||||
<span class="aps-status-tag" :class="'status-' + (scope.row.scheduleStatus || 1)">{{ statusMap[scope.row.scheduleStatus] || '未知' }}</span>
|
class="aps-table"
|
||||||
</template>
|
>
|
||||||
</el-table-column>
|
<el-table-column label="排产单号" prop="scheduleNo" min-width="140" show-overflow-tooltip />
|
||||||
<el-table-column label="规格" prop="spec" min-width="120" show-overflow-tooltip />
|
<el-table-column label="生产日期" prop="prodDate" width="110" align="center" show-overflow-tooltip />
|
||||||
<el-table-column label="材质" prop="material" width="90" align="center" show-overflow-tooltip />
|
<el-table-column label="工序步骤" prop="stepName" width="120" align="center" show-overflow-tooltip />
|
||||||
<el-table-column label="排产吨数" prop="scheduleWeight" width="100" align="right" show-overflow-tooltip />
|
<el-table-column label="排产状态" prop="scheduleStatus" width="90" align="center">
|
||||||
<el-table-column label="品名项" prop="productItem" min-width="100" show-overflow-tooltip />
|
<template slot-scope="scope">
|
||||||
<el-table-column label="品名" prop="productType" min-width="90" align="center" show-overflow-tooltip />
|
<span class="aps-status-tag" :class="'status-' + (scope.row.scheduleStatus || 1)">{{ statusMap[scope.row.scheduleStatus] || '未知' }}</span>
|
||||||
<el-table-column label="订货单位" prop="customerName" min-width="140" show-overflow-tooltip />
|
</template>
|
||||||
<el-table-column label="业务员" prop="businessUser" width="90" align="center" show-overflow-tooltip />
|
</el-table-column>
|
||||||
<el-table-column label="联系电话" prop="businessPhone" width="110" align="center" show-overflow-tooltip />
|
<el-table-column label="规格" prop="spec" min-width="120" show-overflow-tooltip />
|
||||||
<el-table-column label="交货期(天)" prop="deliveryCycle" width="100" align="center" show-overflow-tooltip />
|
<el-table-column label="材质" prop="material" width="90" align="center" show-overflow-tooltip />
|
||||||
<el-table-column label="产品用途" prop="usePurpose" min-width="120" show-overflow-tooltip />
|
<el-table-column label="排产吨数" prop="scheduleWeight" width="100" align="right" show-overflow-tooltip />
|
||||||
<el-table-column label="厚度公差" prop="thicknessTolerance" min-width="100" show-overflow-tooltip />
|
<el-table-column label="品名项" prop="productItem" min-width="100" show-overflow-tooltip />
|
||||||
<el-table-column label="宽度公差" prop="widthTolerance" min-width="100" show-overflow-tooltip />
|
<el-table-column label="品名" prop="productType" min-width="90" align="center" show-overflow-tooltip />
|
||||||
<el-table-column label="表面质量" prop="surfaceQuality" min-width="100" show-overflow-tooltip />
|
<el-table-column label="订货单位" prop="customerName" min-width="140" show-overflow-tooltip />
|
||||||
<el-table-column label="表面处理" prop="surfaceTreatment" min-width="100" show-overflow-tooltip />
|
<el-table-column label="业务员" prop="businessUser" width="90" align="center" show-overflow-tooltip />
|
||||||
<el-table-column label="内径尺寸" prop="innerDiameter" width="90" align="center" show-overflow-tooltip />
|
<el-table-column label="联系电话" prop="businessPhone" width="110" align="center" show-overflow-tooltip />
|
||||||
<el-table-column label="外径要求" prop="outerDiameter" width="90" align="center" show-overflow-tooltip />
|
<el-table-column label="交货期(天)" prop="deliveryCycle" width="100" align="center" show-overflow-tooltip />
|
||||||
<el-table-column label="包装要求" prop="packReq" min-width="100" show-overflow-tooltip />
|
<el-table-column label="产品用途" prop="usePurpose" min-width="120" show-overflow-tooltip />
|
||||||
<el-table-column label="切边要求" prop="cutEdgeReq" min-width="100" show-overflow-tooltip />
|
<el-table-column label="厚度公差" prop="thicknessTolerance" min-width="100" show-overflow-tooltip />
|
||||||
<el-table-column label="单件重量" prop="singleCoilWeight" width="90" align="center" show-overflow-tooltip />
|
<el-table-column label="宽度公差" prop="widthTolerance" min-width="100" show-overflow-tooltip />
|
||||||
<el-table-column label="交货重量偏差" prop="weightDeviation" min-width="110" show-overflow-tooltip />
|
<el-table-column label="表面质量" prop="surfaceQuality" min-width="100" show-overflow-tooltip />
|
||||||
<el-table-column label="其他技术要求" prop="otherTechReq" min-width="130" show-overflow-tooltip />
|
<el-table-column label="表面处理" prop="surfaceTreatment" min-width="100" show-overflow-tooltip />
|
||||||
<el-table-column label="付款情况说明" prop="paymentDesc" min-width="130" show-overflow-tooltip />
|
<el-table-column label="内径尺寸" prop="innerDiameter" width="90" align="center" show-overflow-tooltip />
|
||||||
<el-table-column label="单行排产备注" prop="rowRemark" min-width="100" show-overflow-tooltip />
|
<el-table-column label="外径要求" prop="outerDiameter" width="90" align="center" show-overflow-tooltip />
|
||||||
<el-table-column label="备注" prop="remark" min-width="100" show-overflow-tooltip />
|
<el-table-column label="包装要求" prop="packReq" min-width="100" show-overflow-tooltip />
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
<el-table-column label="切边要求" prop="cutEdgeReq" min-width="100" show-overflow-tooltip />
|
||||||
<template slot-scope="scope">
|
<el-table-column label="单件重量" prop="singleCoilWeight" width="90" align="center" show-overflow-tooltip />
|
||||||
<div style="display:flex; justify-content:center; gap:0;">
|
<el-table-column label="交货重量偏差" prop="weightDeviation" min-width="110" show-overflow-tooltip />
|
||||||
<el-button type="text" size="small" style="color:#409EFF;padding:0 6px;" @click="handleEditScheduled(scope.row)">编辑</el-button>
|
<el-table-column label="其他技术要求" prop="otherTechReq" min-width="130" show-overflow-tooltip />
|
||||||
<el-button type="text" size="small" style="color:#ff4d4f;padding:0 6px;" @click="handleDeleteScheduled(scope.row)">删除</el-button>
|
<el-table-column label="付款情况说明" prop="paymentDesc" min-width="130" show-overflow-tooltip />
|
||||||
</div>
|
<el-table-column label="单行排产备注" prop="rowRemark" min-width="100" show-overflow-tooltip />
|
||||||
</template>
|
<el-table-column label="备注" prop="remark" min-width="100" show-overflow-tooltip />
|
||||||
</el-table-column>
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
</el-table>
|
<template slot-scope="scope">
|
||||||
|
<div style="display:flex; justify-content:center; gap:0;">
|
||||||
|
<el-button type="text" size="small" style="color:#409EFF;padding:0 6px;" @click="handleEditScheduled(scope.row)">编辑</el-button>
|
||||||
|
<el-button type="text" size="small" style="color:#ff4d4f;padding:0 6px;" @click="handleDeleteScheduled(scope.row)">删除</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</template>
|
||||||
<div v-else-if="!schLoading" style="padding:40px;text-align:center;color:#909399;">
|
<div v-else-if="!schLoading" style="padding:40px;text-align:center;color:#909399;">
|
||||||
{{ hasQueried ? '该日期暂无已排产数据' : '请选择日期查询' }}
|
{{ hasQueried ? '该日期暂无已排产数据' : '请选择日期查询' }}
|
||||||
</div>
|
</div>
|
||||||
@@ -493,149 +580,48 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 合并排产明细对话框 -->
|
<!-- 接收产需单弹窗 - 配置工序 -->
|
||||||
<el-dialog
|
<el-dialog
|
||||||
title="合并排产明细"
|
title="接收产需单 - 配置工序"
|
||||||
:visible.sync="mergeDialogVisible"
|
:visible.sync="receiveDialogVisible"
|
||||||
width="850px"
|
width="900px"
|
||||||
append-to-body
|
append-to-body
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
>
|
>
|
||||||
<div style="margin-bottom:12px;font-size:13px;color:#e67e22;">
|
<div style="margin-bottom:12px;font-size:13px;color:#e67e22;">
|
||||||
选中 {{ mergeForm.itemCount }} 条明细,请选择一个作为合并模板,下方字段将自动填充,您可修改后确认合并。
|
请为每条明细配置工序,所有明细配置完成后才能接收。
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 选择模板 -->
|
<el-table :data="receiveDetailList" border size="small" class="aps-table" max-height="400">
|
||||||
<el-table :data="mergeSourceRows" border size="small" style="margin-bottom:12px;" @row-click="pickMergeTemplate">
|
<el-table-column label="规格" prop="spec" min-width="120" show-overflow-tooltip />
|
||||||
<el-table-column width="50" align="center">
|
<el-table-column label="材质" prop="material" width="90" align="center" show-overflow-tooltip />
|
||||||
|
<el-table-column label="排产吨数" prop="scheduleWeight" width="100" align="right" />
|
||||||
|
<el-table-column label="品名" prop="productType" min-width="90" align="center" show-overflow-tooltip />
|
||||||
|
<el-table-column label="配置工序" min-width="200">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-radio v-model="mergeTemplateIndex" :label="scope.$index" @click.native.stop />
|
<ProcessSelect
|
||||||
|
v-model="scope.row.processId"
|
||||||
|
placeholder="请选择工序"
|
||||||
|
@change="(val, data) => handleReceiveProcessChange(scope.row, val, data)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="工序步骤" min-width="300">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<div v-if="scope.row.stepList && scope.row.stepList.length > 0" class="receive-steps-flow">
|
||||||
|
<template v-for="(step, index) in scope.row.stepList">
|
||||||
|
<span :key="step.stepId" class="receive-step-tag">{{ step.stepName }}</span>
|
||||||
|
<span v-if="index < scope.row.stepList.length - 1" :key="'arrow-' + index" class="receive-step-arrow">→</span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<span v-else style="color:#909399;font-size:12px;">—</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="排产单号" prop="scheduleNo" min-width="130" show-overflow-tooltip />
|
|
||||||
<el-table-column label="规格" prop="spec" min-width="100" show-overflow-tooltip />
|
|
||||||
<el-table-column label="材质" prop="material" width="80" show-overflow-tooltip />
|
|
||||||
<el-table-column label="排产吨数" prop="scheduleWeight" width="90" align="right" />
|
|
||||||
<el-table-column label="品名" prop="productType" min-width="80" show-overflow-tooltip />
|
|
||||||
<el-table-column label="订货单位" prop="customerName" min-width="120" show-overflow-tooltip />
|
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<!-- 合并表单 -->
|
|
||||||
<el-form ref="mergeFormRef" :model="mergeForm" label-width="110px" size="small">
|
|
||||||
<el-row :gutter="16">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="排产单号"><el-input v-model="mergeForm.scheduleNo" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="工序类型">
|
|
||||||
<el-select v-model="mergeForm.actionId" placeholder="请选择工序类型" clearable filterable style="width:100%">
|
|
||||||
<el-option v-for="p in processOptions" :key="p.actionId" :label="p.name" :value="p.actionId" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row :gutter="16">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="订货单位"><el-input v-model="mergeForm.customerName" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="规格"><el-input v-model="mergeForm.spec" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="材质"><el-input v-model="mergeForm.material" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row :gutter="16">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="排产吨数(累加)">
|
|
||||||
<el-input-number v-model="mergeForm.scheduleWeight" :min="0" :precision="3" :controls="false" style="width:100%" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="品名"><el-input v-model="mergeForm.productType" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row :gutter="16">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="品名项"><el-input v-model="mergeForm.productItem" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="业务员"><el-input v-model="mergeForm.businessUser" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row :gutter="16">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="联系电话"><el-input v-model="mergeForm.businessPhone" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="交货期(天)">
|
|
||||||
<el-input-number v-model="mergeForm.deliveryCycle" :min="0" :controls="false" style="width:100%" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row :gutter="16">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="产品用途"><el-input v-model="mergeForm.usePurpose" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="厚度公差"><el-input v-model="mergeForm.thicknessTolerance" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row :gutter="16">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="宽度公差"><el-input v-model="mergeForm.widthTolerance" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="表面质量"><el-input v-model="mergeForm.surfaceQuality" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row :gutter="16">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="表面处理"><el-input v-model="mergeForm.surfaceTreatment" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="内径尺寸"><el-input v-model="mergeForm.innerDiameter" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row :gutter="16">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="外径要求"><el-input v-model="mergeForm.outerDiameter" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="包装要求"><el-input v-model="mergeForm.packReq" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row :gutter="16">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="切边要求"><el-input v-model="mergeForm.cutEdgeReq" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="单件重量"><el-input v-model="mergeForm.singleCoilWeight" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row :gutter="16">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="交货重量偏差"><el-input v-model="mergeForm.weightDeviation" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="其他技术要求"><el-input v-model="mergeForm.otherTechReq" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-row :gutter="16">
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="付款情况说明"><el-input v-model="mergeForm.paymentDesc" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="备注"><el-input v-model="mergeForm.remark" /></el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-form>
|
|
||||||
<div style="margin-top:8px;font-size:12px;color:#909399;">
|
|
||||||
合并后 schedule_detail_ids:{{ mergeForm.scheduleDetailIds }}
|
|
||||||
</div>
|
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
<el-button :loading="mergeBtnLoading" type="warning" @click="confirmMerge">确 定 合 并</el-button>
|
<el-button :loading="receiveBtnLoading" type="primary" :disabled="!canConfirmReceive" @click="confirmReceive">确 定 接 收</el-button>
|
||||||
<el-button @click="mergeDialogVisible = false">取 消</el-button>
|
<el-button @click="receiveDialogVisible = false">取 消</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
@@ -651,13 +637,14 @@ import {
|
|||||||
listScheduleItem,
|
listScheduleItem,
|
||||||
updateScheduleItem,
|
updateScheduleItem,
|
||||||
delScheduleItem,
|
delScheduleItem,
|
||||||
receiveScheduleItem,
|
receiveScheduleItem
|
||||||
mergeScheduleItem
|
|
||||||
} from '@/api/aps/schedule'
|
} from '@/api/aps/schedule'
|
||||||
import { PROCESSES } from '@/utils/meta'
|
import { PROCESSES } from '@/utils/meta'
|
||||||
|
import ProcessSelect from '@/components/KLPService/ProcessSelect/index.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ApsSchedule',
|
name: 'ApsSchedule',
|
||||||
|
components: { ProcessSelect },
|
||||||
data() {
|
data() {
|
||||||
const today = new Date()
|
const today = new Date()
|
||||||
const y = today.getFullYear()
|
const y = today.getFullYear()
|
||||||
@@ -676,26 +663,13 @@ export default {
|
|||||||
statusMap: { 1: '待审核', 2: '已接收', 3: '已驳回' },
|
statusMap: { 1: '待审核', 2: '已接收', 3: '已驳回' },
|
||||||
processOptions: PROCESSES,
|
processOptions: PROCESSES,
|
||||||
|
|
||||||
|
// 已接收
|
||||||
|
acceptedScheduleList: [],
|
||||||
|
acceptedLoading: false,
|
||||||
|
|
||||||
// 已排产
|
// 已排产
|
||||||
scheduledItemList: [],
|
scheduledItemList: [],
|
||||||
selectedItems: [],
|
scheduledStepTab: '',
|
||||||
sourceColorMap: {},
|
|
||||||
|
|
||||||
// 合并对话框
|
|
||||||
mergeDialogVisible: false,
|
|
||||||
mergeBtnLoading: false,
|
|
||||||
mergeTemplateIndex: 0,
|
|
||||||
mergeSourceRows: [],
|
|
||||||
mergeForm: {
|
|
||||||
itemCount: 0, scheduleNo: '', actionId: '', customerName: '', spec: '', material: '',
|
|
||||||
scheduleWeight: 0, productType: '', productItem: '', businessUser: '',
|
|
||||||
businessPhone: '', deliveryCycle: undefined, usePurpose: '',
|
|
||||||
thicknessTolerance: '', widthTolerance: '', surfaceQuality: '',
|
|
||||||
surfaceTreatment: '', innerDiameter: '', outerDiameter: '',
|
|
||||||
packReq: '', cutEdgeReq: '', singleCoilWeight: '',
|
|
||||||
weightDeviation: '', otherTechReq: '', paymentDesc: '',
|
|
||||||
remark: '', scheduleDetailIds: ''
|
|
||||||
},
|
|
||||||
|
|
||||||
// 驳回
|
// 驳回
|
||||||
rejectDialogVisible: false,
|
rejectDialogVisible: false,
|
||||||
@@ -714,14 +688,37 @@ export default {
|
|||||||
|
|
||||||
// 下钻
|
// 下钻
|
||||||
drillDialogVisible: false,
|
drillDialogVisible: false,
|
||||||
drillOrder: null
|
drillOrder: null,
|
||||||
|
|
||||||
|
// 接收产需单弹窗
|
||||||
|
receiveDialogVisible: false,
|
||||||
|
receiveBtnLoading: false,
|
||||||
|
receiveScheduleId: null,
|
||||||
|
receiveDetailList: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
computed: {
|
||||||
activeTab() {
|
canConfirmReceive() {
|
||||||
this.selectedItems = []
|
if (!this.receiveDetailList || this.receiveDetailList.length === 0) return false
|
||||||
|
return this.receiveDetailList.every(d => d.processId)
|
||||||
|
},
|
||||||
|
// 已排产按步骤分组:去掉括号及括号内文字
|
||||||
|
scheduledStepTabs() {
|
||||||
|
const names = this.scheduledItemList
|
||||||
|
.map(item => this.normalizeStepName(item.stepName))
|
||||||
|
.filter(Boolean)
|
||||||
|
return [...new Set(names)]
|
||||||
|
},
|
||||||
|
// 当前选中的步骤tab对应的数据
|
||||||
|
currentStepItems() {
|
||||||
|
if (!this.scheduledStepTab) return this.scheduledItemList
|
||||||
|
const tab = this.scheduledStepTab
|
||||||
|
return this.scheduledItemList.filter(
|
||||||
|
item => this.normalizeStepName(item.stepName) === tab
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {},
|
||||||
created() {
|
created() {
|
||||||
this.handleQuery()
|
this.handleQuery()
|
||||||
},
|
},
|
||||||
@@ -775,6 +772,8 @@ export default {
|
|||||||
this.hasQueried = true
|
this.hasQueried = true
|
||||||
if (this.activeTab === 'pending') {
|
if (this.activeTab === 'pending') {
|
||||||
this.queryPending()
|
this.queryPending()
|
||||||
|
} else if (this.activeTab === 'accepted') {
|
||||||
|
this.queryAccepted()
|
||||||
} else {
|
} else {
|
||||||
this.queryScheduled()
|
this.queryScheduled()
|
||||||
}
|
}
|
||||||
@@ -808,25 +807,82 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ====== 已接收 ======
|
||||||
|
queryAccepted() {
|
||||||
|
this.acceptedLoading = true
|
||||||
|
this.acceptedScheduleList = []
|
||||||
|
|
||||||
|
listRequirement({
|
||||||
|
prodDate: this.queryDate,
|
||||||
|
scheduleStatus: 2,
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 999
|
||||||
|
}).then(res => {
|
||||||
|
this.acceptedScheduleList = res.rows || []
|
||||||
|
const totalCount = this.acceptedScheduleList.reduce((sum, sch) => sum + (sch.detailList || []).length, 0)
|
||||||
|
this.summaryText = `共 ${this.acceptedScheduleList.length} 个已接收产需单,${totalCount} 条明细`
|
||||||
|
}).catch(() => {
|
||||||
|
this.acceptedScheduleList = []
|
||||||
|
this.summaryText = ''
|
||||||
|
}).finally(() => {
|
||||||
|
this.acceptedLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
handleAccept(sch) {
|
handleAccept(sch) {
|
||||||
const details = sch.detailList || []
|
const details = sch.detailList || []
|
||||||
if (details.length === 0) {
|
if (details.length === 0) {
|
||||||
this.$message.warning('该产需单无可排产的明细')
|
this.$message.warning('该产需单无可排产的明细')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.$confirm(
|
// 初始化接收明细列表,为每条明细添加 processId 和 stepList 字段
|
||||||
`确认接收产需单「${sch.scheduleNo}」的全部排产明细吗?共 ${details.length} 条明细`,
|
this.receiveScheduleId = sch.scheduleId
|
||||||
'提示',
|
this.receiveDetailList = details.map(d => ({
|
||||||
{ confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }
|
...d,
|
||||||
).then(() => {
|
processId: undefined,
|
||||||
this.$message.info('正在处理接收...')
|
stepList: []
|
||||||
receiveScheduleItem(sch.scheduleId).then(() => {
|
}))
|
||||||
this.$modal.msgSuccess('接收成功,排产明细已写入')
|
this.receiveDialogVisible = true
|
||||||
this.queryPending()
|
},
|
||||||
}).catch(() => {
|
|
||||||
this.$modal.msgError('接收失败')
|
handleReceiveProcessChange(row, processId, processData) {
|
||||||
})
|
if (processData && processData.stepList) {
|
||||||
}).catch(() => {})
|
row.stepList = processData.stepList
|
||||||
|
} else {
|
||||||
|
row.stepList = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
confirmReceive() {
|
||||||
|
// 验证所有明细都配置了工序
|
||||||
|
const unconfigured = this.receiveDetailList.filter(d => !d.processId)
|
||||||
|
if (unconfigured.length > 0) {
|
||||||
|
this.$message.warning(`还有 ${unconfigured.length} 条明细未配置工序,请为所有明细配置工序`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 构建接收数据,传递每条明细的 processId 和 stepList
|
||||||
|
const receiveData = {
|
||||||
|
scheduleId: this.receiveScheduleId,
|
||||||
|
detailProcessList: this.receiveDetailList.map(d => ({
|
||||||
|
scheduleDetailId: d.scheduleDetailId || d.scheduleDetailIds,
|
||||||
|
processId: d.processId,
|
||||||
|
stepList: (d.stepList || []).map(s => ({
|
||||||
|
stepId: s.stepId,
|
||||||
|
stepOrder: s.stepOrder,
|
||||||
|
stepName: s.stepName
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
this.receiveBtnLoading = true
|
||||||
|
receiveScheduleItem(receiveData).then(() => {
|
||||||
|
this.$modal.msgSuccess('接收成功,排产明细已写入')
|
||||||
|
this.receiveDialogVisible = false
|
||||||
|
this.queryPending()
|
||||||
|
}).catch(() => {
|
||||||
|
this.$modal.msgError('接收失败')
|
||||||
|
}).finally(() => {
|
||||||
|
this.receiveBtnLoading = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
handleReject(sch) {
|
handleReject(sch) {
|
||||||
@@ -864,16 +920,14 @@ export default {
|
|||||||
queryScheduled() {
|
queryScheduled() {
|
||||||
this.schLoading = true
|
this.schLoading = true
|
||||||
this.scheduledItemList = []
|
this.scheduledItemList = []
|
||||||
this.sourceColorMap = {}
|
this.scheduledStepTab = ''
|
||||||
this.selectedItems = []
|
|
||||||
|
|
||||||
listScheduleItem({ prodDate: this.queryDate, pageNum: 1, pageSize: 999 }).then(res => {
|
listScheduleItem({ prodDate: this.queryDate, pageNum: 1, pageSize: 999 }).then(res => {
|
||||||
this.scheduledItemList = (res.rows || []).sort((a, b) => (a.scheduleNo || '').localeCompare(b.scheduleNo || ''))
|
this.scheduledItemList = (res.rows || []).sort((a, b) => (a.scheduleNo || '').localeCompare(b.scheduleNo || ''))
|
||||||
try {
|
// 默认选中第一个步骤tab
|
||||||
this.sourceColorMap = this.buildGroupColorMap(this.scheduledItemList)
|
const tabs = this.scheduledStepTabs
|
||||||
} catch (e) {
|
if (tabs.length > 0) {
|
||||||
console.error('buildGroupColorMap error:', e)
|
this.scheduledStepTab = tabs[0]
|
||||||
this.sourceColorMap = {}
|
|
||||||
}
|
}
|
||||||
const totalWeight = this.scheduledItemList.reduce((sum, d) => {
|
const totalWeight = this.scheduledItemList.reduce((sum, d) => {
|
||||||
const w = parseFloat(d.scheduleWeight)
|
const w = parseFloat(d.scheduleWeight)
|
||||||
@@ -934,98 +988,6 @@ export default {
|
|||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
},
|
},
|
||||||
|
|
||||||
// ====== 合并 ======
|
|
||||||
handleSelectionChange(rows) {
|
|
||||||
this.selectedItems = rows
|
|
||||||
},
|
|
||||||
|
|
||||||
getItemRowClassName({ row }) {
|
|
||||||
const key = row.scheduleNo
|
|
||||||
if (!key) return ''
|
|
||||||
const colorIndex = this.sourceColorMap[key]
|
|
||||||
return colorIndex !== undefined ? `merge-source-${colorIndex}` : ''
|
|
||||||
},
|
|
||||||
|
|
||||||
buildGroupColorMap(list) {
|
|
||||||
const map = {}
|
|
||||||
let colorIdx = 0
|
|
||||||
list.forEach(item => {
|
|
||||||
const key = item.scheduleNo
|
|
||||||
if (!key) return
|
|
||||||
if (!(key in map)) {
|
|
||||||
map[key] = colorIdx % 5
|
|
||||||
colorIdx++
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return map
|
|
||||||
},
|
|
||||||
|
|
||||||
handleMergePrepare() {
|
|
||||||
if (this.selectedItems.length < 2) {
|
|
||||||
this.$message.warning('请至少选择2条排产明细进行合并')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.mergeSourceRows = [...this.selectedItems]
|
|
||||||
this.mergeTemplateIndex = 0
|
|
||||||
this.pickMergeTemplate(this.mergeSourceRows[0])
|
|
||||||
this.mergeDialogVisible = true
|
|
||||||
},
|
|
||||||
|
|
||||||
pickMergeTemplate(row) {
|
|
||||||
const idx = this.mergeSourceRows.indexOf(row)
|
|
||||||
if (idx >= 0) this.mergeTemplateIndex = idx
|
|
||||||
this.mergeForm = {
|
|
||||||
itemCount: this.mergeSourceRows.length,
|
|
||||||
scheduleNo: row.scheduleNo || '',
|
|
||||||
actionId: row.actionId != null ? Number(row.actionId) : '',
|
|
||||||
customerName: row.customerName || '',
|
|
||||||
spec: row.spec || '',
|
|
||||||
material: row.material || '',
|
|
||||||
scheduleWeight: this.mergeSourceRows.reduce((sum, r) => sum + (parseFloat(r.scheduleWeight) || 0), 0),
|
|
||||||
productType: row.productType || '',
|
|
||||||
productItem: row.productItem || '',
|
|
||||||
businessUser: row.businessUser || '',
|
|
||||||
businessPhone: row.businessPhone || '',
|
|
||||||
deliveryCycle: row.deliveryCycle,
|
|
||||||
usePurpose: row.usePurpose || '',
|
|
||||||
thicknessTolerance: row.thicknessTolerance || '',
|
|
||||||
widthTolerance: row.widthTolerance || '',
|
|
||||||
surfaceQuality: row.surfaceQuality || '',
|
|
||||||
surfaceTreatment: row.surfaceTreatment || '',
|
|
||||||
innerDiameter: row.innerDiameter || '',
|
|
||||||
outerDiameter: row.outerDiameter || '',
|
|
||||||
packReq: row.packReq || '',
|
|
||||||
cutEdgeReq: row.cutEdgeReq || '',
|
|
||||||
singleCoilWeight: row.singleCoilWeight || '',
|
|
||||||
weightDeviation: row.weightDeviation || '',
|
|
||||||
otherTechReq: row.otherTechReq || '',
|
|
||||||
paymentDesc: row.paymentDesc || '',
|
|
||||||
remark: row.remark || '',
|
|
||||||
scheduleDetailIds: this.mergeSourceRows.map(r => r.scheduleDetailIds || '').filter(Boolean).join(',')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
confirmMerge() {
|
|
||||||
this.mergeBtnLoading = true
|
|
||||||
const ids = this.mergeSourceRows.map(r => r.scheduleId)
|
|
||||||
const mergedBo = {
|
|
||||||
...this.mergeForm,
|
|
||||||
prodDate: this.queryDate,
|
|
||||||
scheduleStatus: 2
|
|
||||||
}
|
|
||||||
delete mergedBo.itemCount
|
|
||||||
mergeScheduleItem({ ids, mergedBo }).then(() => {
|
|
||||||
this.$modal.msgSuccess(`合并成功:${ids.length} 条明细合并为 1 条`)
|
|
||||||
this.mergeDialogVisible = false
|
|
||||||
this.selectedItems = []
|
|
||||||
this.queryScheduled()
|
|
||||||
}).catch(() => {
|
|
||||||
this.$modal.msgError('合并失败')
|
|
||||||
}).finally(() => {
|
|
||||||
this.mergeBtnLoading = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// ====== 下钻 & 辅助方法 ======
|
// ====== 下钻 & 辅助方法 ======
|
||||||
scheduleTotalWeight(sch) {
|
scheduleTotalWeight(sch) {
|
||||||
const details = sch.detailList || []
|
const details = sch.detailList || []
|
||||||
@@ -1038,6 +1000,12 @@ export default {
|
|||||||
return p ? p.name : (actionId || '')
|
return p ? p.name : (actionId || '')
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 去掉步骤名称中的括号及括号内文字:镀锌(毛化)=> 镀锌
|
||||||
|
normalizeStepName(name) {
|
||||||
|
if (!name) return ''
|
||||||
|
return name.replace(/[((][^))]*[))]/g, '').trim()
|
||||||
|
},
|
||||||
|
|
||||||
handleDetailClick(sch, detail) {
|
handleDetailClick(sch, detail) {
|
||||||
// 点击明细行查看来源订单
|
// 点击明细行查看来源订单
|
||||||
if (!sch || !sch.orderList || sch.orderList.length === 0) {
|
if (!sch || !sch.orderList || sch.orderList.length === 0) {
|
||||||
@@ -1350,20 +1318,85 @@ export default {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====== 合并候选行颜色高亮(同一源排产单 → 同色) ======
|
// ====== 接收弹窗步骤展示 ======
|
||||||
::v-deep .el-table__body tr.merge-source-0 > td {
|
.receive-steps-flow {
|
||||||
background-color: #f0f5ff !important;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
}
|
}
|
||||||
::v-deep .el-table__body tr.merge-source-1 > td {
|
|
||||||
background-color: #f6ffed !important;
|
.receive-step-tag {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 2px 8px;
|
||||||
|
background: $aps-silver-1;
|
||||||
|
border: 1px solid $aps-silver-mid;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: $aps-text;
|
||||||
}
|
}
|
||||||
::v-deep .el-table__body tr.merge-source-2 > td {
|
|
||||||
background-color: #fff7e6 !important;
|
.receive-step-arrow {
|
||||||
|
color: $aps-red-2;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 0 2px;
|
||||||
}
|
}
|
||||||
::v-deep .el-table__body tr.merge-source-3 > td {
|
|
||||||
background-color: #f9f0ff !important;
|
// ====== 已排产步骤工序 Tab ======
|
||||||
|
.aps-step-tabs {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding: 8px 12px;
|
||||||
|
gap: 6px;
|
||||||
|
background: $aps-silver-1;
|
||||||
|
border-bottom: 1px solid $aps-border;
|
||||||
}
|
}
|
||||||
::v-deep .el-table__body tr.merge-source-4 > td {
|
|
||||||
background-color: #fff0f6 !important;
|
.aps-step-tab {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 4px 14px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: $aps-text-muted;
|
||||||
|
background: $aps-white;
|
||||||
|
border: 1px solid $aps-silver-mid;
|
||||||
|
border-radius: $aps-radius;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $aps-red-2;
|
||||||
|
border-color: $aps-red-2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.aps-step-tab-active {
|
||||||
|
color: #fff;
|
||||||
|
background: $aps-red-2;
|
||||||
|
border-color: $aps-red-2;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.aps-step-tab-count {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 18px;
|
||||||
|
height: 16px;
|
||||||
|
padding: 0 5px;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
.aps-step-tab-active & {
|
||||||
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user