diff --git a/klp-flow/src/main/java/com/klp/flow/domain/bo/SchProdScheduleItemReceiveBo.java b/klp-flow/src/main/java/com/klp/flow/domain/bo/SchProdScheduleItemReceiveBo.java index a200e24bd..b33dc2250 100644 --- a/klp-flow/src/main/java/com/klp/flow/domain/bo/SchProdScheduleItemReceiveBo.java +++ b/klp-flow/src/main/java/com/klp/flow/domain/bo/SchProdScheduleItemReceiveBo.java @@ -2,6 +2,8 @@ package com.klp.flow.domain.bo; import lombok.Data; import javax.validation.constraints.NotNull; +import javax.validation.constraints.NotEmpty; +import java.util.List; /** * 产需单接收请求对象 @@ -18,4 +20,55 @@ public class SchProdScheduleItemReceiveBo { @NotNull(message = "排产单主表ID不能为空") private Long scheduleId; + /** + * 明细工序配置列表 + */ + @NotEmpty(message = "明细工序配置不能为空") + private List detailProcessList; + + /** + * 明细工序配置 + */ + @Data + public static class DetailProcessConfig { + /** + * 产需单明细ID + */ + @NotNull(message = "明细ID不能为空") + private Long scheduleDetailId; + + /** + * 工序ID + */ + @NotNull(message = "工序ID不能为空") + private Long processId; + + /** + * 工序步骤列表 + */ + @NotEmpty(message = "工序步骤不能为空") + private List stepList; + } + + /** + * 工序步骤配置 + */ + @Data + public static class StepConfig { + /** + * 步骤ID + */ + @NotNull(message = "步骤ID不能为空") + private Long stepId; + + /** + * 步骤顺序号 + */ + private Long stepOrder; + + /** + * 步骤名称 + */ + private String stepName; + } } diff --git a/klp-flow/src/main/java/com/klp/flow/domain/vo/SchProdProcessVo.java b/klp-flow/src/main/java/com/klp/flow/domain/vo/SchProdProcessVo.java index a7bdcd57c..a23650fed 100644 --- a/klp-flow/src/main/java/com/klp/flow/domain/vo/SchProdProcessVo.java +++ b/klp-flow/src/main/java/com/klp/flow/domain/vo/SchProdProcessVo.java @@ -2,10 +2,13 @@ package com.klp.flow.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; +import com.baomidou.mybatisplus.annotation.TableField; import com.klp.common.annotation.ExcelDictFormat; import com.klp.common.convert.ExcelDictConvert; import lombok.Data; +import java.util.List; + /** * 工序定义主视图对象 sch_prod_process @@ -44,5 +47,10 @@ public class SchProdProcessVo { @ExcelProperty(value = "备注") private String remark; + /** + * 工艺步骤列表 + */ + @TableField(exist = false) + private List stepList; } diff --git a/klp-flow/src/main/java/com/klp/flow/domain/vo/SchProdScheduleItemVo.java b/klp-flow/src/main/java/com/klp/flow/domain/vo/SchProdScheduleItemVo.java index 87ee27df3..13dd55394 100644 --- a/klp-flow/src/main/java/com/klp/flow/domain/vo/SchProdScheduleItemVo.java +++ b/klp-flow/src/main/java/com/klp/flow/domain/vo/SchProdScheduleItemVo.java @@ -2,6 +2,7 @@ package com.klp.flow.domain.vo; import java.math.BigDecimal; import java.util.Date; +import com.baomidou.mybatisplus.annotation.TableField; import com.fasterxml.jackson.annotation.JsonFormat; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; @@ -195,6 +196,12 @@ public class SchProdScheduleItemVo { @ExcelProperty(value = "工序ID") private Long actionId; + /** + * 工序步骤名称 + */ + @TableField(exist = false) + private String stepName; + /** * 规格 例:1.0X1250 */ diff --git a/klp-flow/src/main/java/com/klp/flow/service/impl/SchProdProcessServiceImpl.java b/klp-flow/src/main/java/com/klp/flow/service/impl/SchProdProcessServiceImpl.java index 92a327760..e8eb32b07 100644 --- a/klp-flow/src/main/java/com/klp/flow/service/impl/SchProdProcessServiceImpl.java +++ b/klp-flow/src/main/java/com/klp/flow/service/impl/SchProdProcessServiceImpl.java @@ -11,13 +11,18 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import com.klp.flow.domain.bo.SchProdProcessBo; 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.SchProdProcessStep; import com.klp.flow.mapper.SchProdProcessMapper; +import com.klp.flow.mapper.SchProdProcessStepMapper; import com.klp.flow.service.ISchProdProcessService; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Collection; +import java.util.stream.Collectors; /** * 工序定义主Service业务层处理 @@ -30,13 +35,18 @@ import java.util.Collection; public class SchProdProcessServiceImpl implements ISchProdProcessService { private final SchProdProcessMapper baseMapper; + private final SchProdProcessStepMapper stepMapper; /** * 查询工序定义主 */ @Override 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 queryPageList(SchProdProcessBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + if (result.getRecords() != null && !result.getRecords().isEmpty()) { + fillStepList(result.getRecords()); + } return TableDataInfo.build(result); } @@ -55,7 +68,34 @@ public class SchProdProcessServiceImpl implements ISchProdProcessService { @Override public List queryList(SchProdProcessBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); - return baseMapper.selectVoList(lqw); + List list = baseMapper.selectVoList(lqw); + if (list != null && !list.isEmpty()) { + fillStepList(list); + } + return list; + } + + /** + * 批量填充工艺步骤列表 + */ + private void fillStepList(List voList) { + List processIds = voList.stream() + .map(SchProdProcessVo::getProcessId) + .filter(id -> id != null) + .distinct() + .collect(Collectors.toList()); + if (processIds.isEmpty()) { + return; + } + LambdaQueryWrapper stepQw = Wrappers.lambdaQuery(); + stepQw.in(SchProdProcessStep::getProcessId, processIds); + stepQw.orderByAsc(SchProdProcessStep::getStepOrder); + List allSteps = stepMapper.selectVoList(stepQw); + Map> stepMap = allSteps.stream() + .collect(Collectors.groupingBy(SchProdProcessStepVo::getProcessId)); + for (SchProdProcessVo vo : voList) { + vo.setStepList(stepMap.getOrDefault(vo.getProcessId(), Collections.emptyList())); + } } private LambdaQueryWrapper buildQueryWrapper(SchProdProcessBo bo) { diff --git a/klp-flow/src/main/java/com/klp/flow/service/impl/SchProdScheduleItemServiceImpl.java b/klp-flow/src/main/java/com/klp/flow/service/impl/SchProdScheduleItemServiceImpl.java index 342c8be37..e6c3bd07b 100644 --- a/klp-flow/src/main/java/com/klp/flow/service/impl/SchProdScheduleItemServiceImpl.java +++ b/klp-flow/src/main/java/com/klp/flow/service/impl/SchProdScheduleItemServiceImpl.java @@ -18,15 +18,18 @@ import com.klp.flow.domain.vo.SchProdScheduleItemMergeValidateVo; import com.klp.flow.domain.SchProdScheduleItem; import com.klp.flow.domain.SchProdSchedule; import com.klp.flow.domain.SchProdScheduleDetail; +import com.klp.flow.domain.SchProdProcessStep; import com.klp.flow.mapper.SchProdScheduleItemMapper; import com.klp.flow.mapper.SchProdScheduleMapper; import com.klp.flow.mapper.SchProdScheduleDetailMapper; +import com.klp.flow.mapper.SchProdProcessStepMapper; import com.klp.flow.service.ISchProdScheduleItemService; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -46,6 +49,7 @@ public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemServi private final SchProdScheduleItemMapper baseMapper; private final SchProdScheduleMapper schProdScheduleMapper; private final SchProdScheduleDetailMapper schProdScheduleDetailMapper; + private final SchProdProcessStepMapper stepMapper; /** * 查询排产单主加明细可合并 @@ -62,6 +66,9 @@ public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemServi public TableDataInfo queryPageList(SchProdScheduleItemBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + if (result.getRecords() != null && !result.getRecords().isEmpty()) { + fillStepName(result.getRecords()); + } return TableDataInfo.build(result); } @@ -71,7 +78,11 @@ public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemServi @Override public List queryList(SchProdScheduleItemBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); - return baseMapper.selectVoList(lqw); + List list = baseMapper.selectVoList(lqw); + if (list != null && !list.isEmpty()) { + fillStepName(list); + } + return list; } private LambdaQueryWrapper buildQueryWrapper(SchProdScheduleItemBo bo) { @@ -113,6 +124,30 @@ public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemServi return lqw; } + /** + * 批量填充工序步骤名称 + */ + private void fillStepName(List voList) { + List stepIds = voList.stream() + .map(SchProdScheduleItemVo::getActionId) + .filter(id -> id != null) + .distinct() + .collect(Collectors.toList()); + if (stepIds.isEmpty()) { + return; + } + LambdaQueryWrapper stepQw = Wrappers.lambdaQuery(); + stepQw.in(SchProdProcessStep::getStepId, stepIds); + List steps = stepMapper.selectList(stepQw); + Map 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 @Transactional(rollbackFor = Exception.class) public Boolean receiveByBo(SchProdScheduleItemReceiveBo receiveBo) { Long scheduleId = receiveBo.getScheduleId(); + List detailProcessList = receiveBo.getDetailProcessList(); // 1. 查询产需单主表 SchProdSchedule header = schProdScheduleMapper.selectById(scheduleId); @@ -217,57 +253,74 @@ public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemServi throw new RuntimeException("产需单无明细数据,scheduleId=" + scheduleId); } - // 3. 遍历每条 detail,构建 SchProdScheduleItem 列表 - List addList = new ArrayList<>(details.size()); - for (SchProdScheduleDetail detail : details) { - 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) - item.setScheduleDetailIds(String.valueOf(detail.getScheduleDetailId())); - - validEntityBeforeSave(item); - addList.add(item); + // 3. 构建明细ID到工序配置的映射 + Map processConfigMap = new HashMap<>(); + for (SchProdScheduleItemReceiveBo.DetailProcessConfig config : detailProcessList) { + processConfigMap.put(config.getScheduleDetailId(), config); } - // 4. 批量插入 + // 4. 遍历每条 detail,根据工序步骤生成多条 SchProdScheduleItem + List 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); - // 5. 更新产需单状态为 2(已下达) + // 6. 更新产需单状态为 2(已下达) header.setScheduleStatus(2L); schProdScheduleMapper.updateById(header); diff --git a/klp-ui/src/api/aps/process.js b/klp-ui/src/api/aps/process.js new file mode 100644 index 000000000..4406d3757 --- /dev/null +++ b/klp-ui/src/api/aps/process.js @@ -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' + }) +} diff --git a/klp-ui/src/api/aps/processStep.js b/klp-ui/src/api/aps/processStep.js new file mode 100644 index 000000000..299aa86be --- /dev/null +++ b/klp-ui/src/api/aps/processStep.js @@ -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' + }) +} diff --git a/klp-ui/src/api/aps/schedule.js b/klp-ui/src/api/aps/schedule.js index 8e1615358..71056bdf5 100644 --- a/klp-ui/src/api/aps/schedule.js +++ b/klp-ui/src/api/aps/schedule.js @@ -73,12 +73,12 @@ export function delScheduleItem(ids) { // ====== 排产单明细项 接收/合并 ====== -// 接收产需单(后端全字段复制) -export function receiveScheduleItem(scheduleId) { +// 接收产需单(根据配置的工序步骤生成排产明细) +export function receiveScheduleItem(data) { return request({ url: '/flow/prodScheduleItem/receive', method: 'post', - data: { scheduleId } + data: data }) } diff --git a/klp-ui/src/components/KLPService/ProcessSelect/index.vue b/klp-ui/src/components/KLPService/ProcessSelect/index.vue new file mode 100644 index 000000000..d8f9b479c --- /dev/null +++ b/klp-ui/src/components/KLPService/ProcessSelect/index.vue @@ -0,0 +1,112 @@ + + + + + diff --git a/klp-ui/src/views/wms/post/aps/processes.vue b/klp-ui/src/views/wms/post/aps/processes.vue new file mode 100644 index 000000000..fd2e15a8e --- /dev/null +++ b/klp-ui/src/views/wms/post/aps/processes.vue @@ -0,0 +1,589 @@ + + + + + diff --git a/klp-ui/src/views/wms/post/aps/requirement.vue b/klp-ui/src/views/wms/post/aps/requirement.vue index 6226376cf..d36e06b29 100644 --- a/klp-ui/src/views/wms/post/aps/requirement.vue +++ b/klp-ui/src/views/wms/post/aps/requirement.vue @@ -7,7 +7,7 @@
- @@ -58,15 +58,19 @@
产需单信息
- - + +
- + @@ -75,7 +79,7 @@ + '未知' }} @@ -139,7 +143,8 @@ - +
排产单号产需单号 {{ currentReq.scheduleNo }} 生产日期 {{ currentReq.prodDate }}排产状态 {{ statusMap[currentReq.scheduleStatus] || - '未知' }} 业务员 {{ currentReq.businessUser }}
驳回原因{{ currentReq.returnReason }}{{ + currentReq.returnReason }}
@@ -190,8 +195,10 @@ @@ -204,13 +211,15 @@ - - - +
+
+ + - + @@ -282,19 +291,42 @@ - - - + + + + + + + + + +
+ +
+
+ 历史记录 +
+
+
+
{{ item.scheduleNo }}
+
{{ item.customerName || '-' }}
+
{{ item.prodDate || '-' }}
+
+
+
+
+ +
+
+ 已接收产需单 + + 共 {{ acceptedScheduleList.length }} 个产需单 + +
+
+
+
+
+
+ {{ sch.scheduleNo }} + 已接收 + + {{ sch.customerName }} · {{ sch.businessUser }} · {{ sch.deliveryCycle }}天 + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
排产单号{{ sch.scheduleNo }}生产日期{{ sch.prodDate }}
业务员{{ sch.businessUser }}联系电话{{ sch.businessPhone }}
订货单位{{ sch.customerName }}交货期(天){{ sch.deliveryCycle }}
品名{{ sch.productType }}
产品用途{{ sch.usePurpose }}
厚度公差{{ sch.thicknessTolerance }}宽度公差{{ sch.widthTolerance }}
表面质量{{ sch.surfaceQuality }}表面处理{{ sch.surfaceTreatment }}
内径尺寸{{ sch.innerDiameter }}外径要求{{ sch.outerDiameter }}
包装要求{{ sch.packReq }}切边要求{{ sch.cutEdgeReq }}
单件重量{{ sch.singleCoilWeight }}交货重量偏差{{ sch.weightDeviation }}
其他技术要求{{ sch.otherTechReq }}
付款情况说明{{ sch.paymentDesc }}
备注{{ sch.remark }}
+ +
+
+ 规格 + 材质 + 排产吨数 + 品名 + 备注 +
+
+ {{ d.spec }} + {{ d.material }} + {{ d.scheduleWeight }} + {{ sch.productType || d.productType || '' }} + {{ d.remark }} +
+
+
暂无明细
+
+
+
+ {{ hasQueried ? '该日期暂无已接收产需单' : '请选择日期查询' }} +
+
+
+
@@ -169,85 +268,73 @@ 共 {{ scheduledItemList.length }} 条 - - - 合并选中项 ({{ selectedItems.length }}) - - - - - 合并选中项 - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
{{ hasQueried ? '该日期暂无已排产数据' : '请选择日期查询' }}
@@ -493,149 +580,48 @@
- +
- 选中 {{ mergeForm.itemCount }} 条明细,请选择一个作为合并模板,下方字段将自动填充,您可修改后确认合并。 + 请为每条明细配置工序,所有明细配置完成后才能接收。
- - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- 合并后 schedule_detail_ids:{{ mergeForm.scheduleDetailIds }} -
@@ -651,13 +637,14 @@ import { listScheduleItem, updateScheduleItem, delScheduleItem, - receiveScheduleItem, - mergeScheduleItem + receiveScheduleItem } from '@/api/aps/schedule' import { PROCESSES } from '@/utils/meta' +import ProcessSelect from '@/components/KLPService/ProcessSelect/index.vue' export default { name: 'ApsSchedule', + components: { ProcessSelect }, data() { const today = new Date() const y = today.getFullYear() @@ -676,26 +663,13 @@ export default { statusMap: { 1: '待审核', 2: '已接收', 3: '已驳回' }, processOptions: PROCESSES, + // 已接收 + acceptedScheduleList: [], + acceptedLoading: false, + // 已排产 scheduledItemList: [], - selectedItems: [], - 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: '' - }, + scheduledStepTab: '', // 驳回 rejectDialogVisible: false, @@ -714,14 +688,37 @@ export default { // 下钻 drillDialogVisible: false, - drillOrder: null + drillOrder: null, + + // 接收产需单弹窗 + receiveDialogVisible: false, + receiveBtnLoading: false, + receiveScheduleId: null, + receiveDetailList: [] } }, - watch: { - activeTab() { - this.selectedItems = [] + computed: { + canConfirmReceive() { + 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() { this.handleQuery() }, @@ -775,6 +772,8 @@ export default { this.hasQueried = true if (this.activeTab === 'pending') { this.queryPending() + } else if (this.activeTab === 'accepted') { + this.queryAccepted() } else { 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) { const details = sch.detailList || [] if (details.length === 0) { this.$message.warning('该产需单无可排产的明细') return } - this.$confirm( - `确认接收产需单「${sch.scheduleNo}」的全部排产明细吗?共 ${details.length} 条明细`, - '提示', - { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' } - ).then(() => { - this.$message.info('正在处理接收...') - receiveScheduleItem(sch.scheduleId).then(() => { - this.$modal.msgSuccess('接收成功,排产明细已写入') - this.queryPending() - }).catch(() => { - this.$modal.msgError('接收失败') - }) - }).catch(() => {}) + // 初始化接收明细列表,为每条明细添加 processId 和 stepList 字段 + this.receiveScheduleId = sch.scheduleId + this.receiveDetailList = details.map(d => ({ + ...d, + processId: undefined, + stepList: [] + })) + this.receiveDialogVisible = true + }, + + handleReceiveProcessChange(row, processId, processData) { + if (processData && processData.stepList) { + 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) { @@ -864,16 +920,14 @@ export default { queryScheduled() { this.schLoading = true this.scheduledItemList = [] - this.sourceColorMap = {} - this.selectedItems = [] + this.scheduledStepTab = '' listScheduleItem({ prodDate: this.queryDate, pageNum: 1, pageSize: 999 }).then(res => { this.scheduledItemList = (res.rows || []).sort((a, b) => (a.scheduleNo || '').localeCompare(b.scheduleNo || '')) - try { - this.sourceColorMap = this.buildGroupColorMap(this.scheduledItemList) - } catch (e) { - console.error('buildGroupColorMap error:', e) - this.sourceColorMap = {} + // 默认选中第一个步骤tab + const tabs = this.scheduledStepTabs + if (tabs.length > 0) { + this.scheduledStepTab = tabs[0] } const totalWeight = this.scheduledItemList.reduce((sum, d) => { const w = parseFloat(d.scheduleWeight) @@ -934,98 +988,6 @@ export default { }).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) { const details = sch.detailList || [] @@ -1038,6 +1000,12 @@ export default { return p ? p.name : (actionId || '') }, + // 去掉步骤名称中的括号及括号内文字:镀锌(毛化)=> 镀锌 + normalizeStepName(name) { + if (!name) return '' + return name.replace(/[((][^))]*[))]/g, '').trim() + }, + handleDetailClick(sch, detail) { // 点击明细行查看来源订单 if (!sch || !sch.orderList || sch.orderList.length === 0) { @@ -1350,20 +1318,85 @@ export default { font-size: 12px; } -// ====== 合并候选行颜色高亮(同一源排产单 → 同色) ====== -::v-deep .el-table__body tr.merge-source-0 > td { - background-color: #f0f5ff !important; +// ====== 接收弹窗步骤展示 ====== +.receive-steps-flow { + 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); + } }