Compare commits

...

3 Commits

Author SHA1 Message Date
zhangzq
64174b4109 add:生产下料同步 2026-06-17 13:22:38 +08:00
zhangzq
189ec73c73 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	nladmin-ui/src/views/wms/st/purchase/index.vue
2026-06-16 15:59:24 +08:00
zhangzq
78fccd3332 add:添加工单页面 2026-06-16 15:58:59 +08:00
33 changed files with 698 additions and 502 deletions

View File

@@ -25,7 +25,7 @@ public class SecurityUtils {
public static CurrentUser getCurrentUser() {
Object loginInfo = StpUtil.getExtra("loginInfo");
if (loginInfo==null){
throw new BadRequestException("用户信息获取失败");
return CurrentUser.getDefaultUser();
}
return JSONObject.parseObject(String.valueOf(loginInfo),CurrentUser.class);
@@ -46,13 +46,7 @@ public class SecurityUtils {
* @return 系统用户名称
*/
public static String getCurrentNickName() {
// CurrentUser user = getCurrentUser();
// if (user!=null){
// return user.getPresonName();
// }
// return null;
//tofix postMan测试完成后改回
return "admin";
return getCurrentUser().getPresonName();
}
/**
@@ -61,9 +55,7 @@ public class SecurityUtils {
* @return 系统用户Id
*/
public static String getCurrentUserId() {
//return getCurrentUser().getId();
//tofix postMan测试完成后改回
return "1";
return getCurrentUser().getId();
}
/**
@@ -72,7 +64,6 @@ public class SecurityUtils {
*/
@Deprecated
public static Long getDeptId() {
// return getCurrentUser().getUser().getDept().getId();
return 1L;
}

View File

@@ -40,7 +40,7 @@ public class CurrentUser implements Serializable {
CurrentUser def = new CurrentUser();
def.setId("2");
def.setUsername("default");
def.setPresonName("外部系统用户");
def.setPresonName("默认用户");
return def;
}
}

View File

@@ -0,0 +1,40 @@
package org.nl.wms.ext_manage.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.nl.wms.ext_manage.service.dto.StructLineOfflineDTO;
import org.nl.wms.sch_manage.service.core.tasks.StructOfflineTask;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* MES需求单同步接口
*/
@Slf4j
@RestController
@RequestMapping("api/mes/structLine")
@RequiredArgsConstructor
public class MesStructLineController {
@Autowired
private StructOfflineTask structOfflineTask;
/**
* 结构车间生产下料同步接口
* @param structLineOffline
* @return
*/
@PostMapping("/offline")
@SaIgnore
public ResponseEntity<Object> confirm(@RequestBody StructLineOfflineDTO structLineOffline) {
structOfflineTask.offline(structLineOffline);
return new ResponseEntity<>(HttpStatus.OK);
}
}

View File

@@ -47,7 +47,6 @@ public class MesApiServiceImpl implements MesApiService {
throw new BadRequestException("请传MES失败,未配置MES_URL地址");
}
String url = param.getValue();
// TODO: add demand order API path to EXTConstant once endpoint is confirmed
try {
JSONObject requestParam = new JSONObject();
requestParam.put("OrderCode",workOrder);

View File

@@ -0,0 +1,51 @@
package org.nl.wms.ext_manage.service.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class StructLineOfflineDTO {
/**
* 产线编码
*/
@JsonProperty("LineCode")
private String lineCode;
/**
* 下料位置
*/
@JsonProperty("UnloadPort")
private String unloadPort;
/**
* 工单编码
*/
@JsonProperty("OrderCode")
private String orderCode;
/**
* 下料数量
*/
@JsonProperty("UnloadQty")
private BigDecimal unloadQty;
/**
* 物料编码
*/
@JsonProperty("MaterialCode")
private String materialCode;
/**
* 物料名称
*/
@JsonProperty("MaterialName")
private String materialName;
/**
* 创建时间
*/
@JsonProperty("CreatedOn")
private String createdOn;
}

View File

@@ -28,7 +28,6 @@ public class WmsToZDWmdServiceImpl implements WmsToZDWmdService {
public ResponseEntity syncDemandOrder(List<PmDemandDto> demands) {
log.info("syncDemandOrder生产领料需求单下发输入参数-------------------" + demands.size());
String url = SpringContextHolder.getBean(SysParamServiceImpl.class).findByCode(SysParamConstant.ZD_URL).getValue();
// TODO: add demand order API path to EXTConstant once endpoint is confirmed
try {
String resultMsg = HttpRequest.post(url)
.body(JSONArray.parseArray(JSON.toJSONString(demands)).toJSONString())
@@ -45,7 +44,6 @@ public class WmsToZDWmdServiceImpl implements WmsToZDWmdService {
public ResponseEntity syncPurchaseReceiving(JSONObject whereJson) {
log.info("syncPurchaseReceiving采购入库单下发输入参数-------------------" + whereJson.toString());
String url = SpringContextHolder.getBean(SysParamServiceImpl.class).findByCode(SysParamConstant.ZD_URL).getValue();
// TODO: add purchase receiving API path to EXTConstant once endpoint is confirmed
try {
String resultMsg = HttpRequest.post(url)
.body(whereJson.toString())

View File

@@ -305,7 +305,6 @@ public class PdaIosOutServiceImpl implements PdaIosOutService {
.eq(GroupPlate::getStatus, IOSEnum.GROUP_PLATE_STATUS.code("入库"))
);
}
//TODO生成手工领料单
final PmStockReturn stockReturn = new PmStockReturn();
stockReturn.setCreate_time(DateUtil.now());
stockReturn.setRequest_type(IOSEnum.BILL_TYPE.code("生产出库"));

View File

@@ -83,6 +83,7 @@ public interface ISchBasePointService extends IService<SchBasePoint> {
* @return SchBasePoint
*/
SchBasePoint selectByIdLock(String id);
SchBasePoint selectByCode(String code);
/**
* 释放点位

View File

@@ -171,11 +171,5 @@ public abstract class AbstractTask {
*/
public abstract void cancel(String task_code);
/**
* 回传mes
* @param task_code 任务号
*/
public abstract void backMes(String task_code);
}

View File

@@ -130,9 +130,7 @@ public class BackInTask extends AbstractTask {
this.cancelTask(taskObj);
}
@Override
public void backMes(String task_code) {
}
@Transactional
public void finishTask(SchBaseTask taskObj) {

View File

@@ -129,9 +129,7 @@ public class MoveTask extends AbstractTask {
this.cancelTask(taskObj);
}
@Override
public void backMes(String task_code) {
}
@Transactional
public void finishTask(SchBaseTask taskObj) {

View File

@@ -130,9 +130,7 @@ public class PdaPointTask extends AbstractTask {
this.cancelTask(taskObj);
}
@Override
public void backMes(String task_code) {
}
@Transactional
public void finishTask(SchBaseTask taskObj) {

View File

@@ -129,10 +129,7 @@ public class StInTask extends AbstractTask {
this.cancelTask(taskObj);
}
@Override
public void backMes(String task_code) {
}
@Transactional(rollbackFor = Exception.class)
public void finishTask(SchBaseTask taskObj) {

View File

@@ -125,9 +125,7 @@ public class StOutTask extends AbstractTask {
this.cancelTask(taskObj);
}
@Override
public void backMes(String task_code) {
}
@Transactional(rollbackFor = Exception.class)
public void finishTask(SchBaseTask taskObj) {

View File

@@ -0,0 +1,174 @@
package org.nl.wms.sch_manage.service.core.tasks;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.commons.lang3.StringUtils;
import org.nl.common.exception.BadRequestException;
import org.nl.common.utils.CodeUtil;
import org.nl.common.utils.IdUtil;
import org.nl.common.utils.SecurityUtils;
import org.nl.config.SpringContextHolder;
import org.nl.wms.basedata_manage.service.IMdMeMaterialbaseService;
import org.nl.wms.basedata_manage.service.IStructattrService;
import org.nl.wms.basedata_manage.service.dao.BsrealStorattr;
import org.nl.wms.basedata_manage.service.dao.MdMeMaterialbase;
import org.nl.wms.basedata_manage.service.dao.Sectattr;
import org.nl.wms.basedata_manage.service.dao.Structattr;
import org.nl.wms.basedata_manage.service.dto.StrategyStructParam;
import org.nl.wms.ext_manage.service.dto.StructLineOfflineDTO;
import org.nl.wms.pm_manage.purchase.ListenerHandler.PurchaseListenerParams;
import org.nl.wms.sch_manage.enums.StatusEnum;
import org.nl.wms.sch_manage.enums.TaskStatus;
import org.nl.wms.sch_manage.service.ISchBasePointService;
import org.nl.wms.sch_manage.service.core.AbstractTask;
import org.nl.wms.sch_manage.service.core.AcsTaskDto;
import org.nl.wms.sch_manage.service.dao.SchBasePoint;
import org.nl.wms.warehouse_manage.enums.IOSEnum;
import org.nl.wms.warehouse_manage.inAndOut.service.IInBillService;
import org.nl.wms.warehouse_manage.inAndOut.service.dao.IOStorInvDtl;
import org.nl.wms.warehouse_manage.inAndOut.service.dto.IOStorInvDisDto;
import org.nl.wms.warehouse_manage.inventory.IStInventoryService;
import org.nl.wms.warehouse_manage.service.IMdPbGroupplateService;
import org.nl.wms.warehouse_manage.service.dao.GroupPlate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import java.util.*;
import static org.nl.wms.warehouse_manage.enums.IOSEnum.GROUP_PLATE_STATUS;
/**
* 焊接区域半成品入库
*/
@Service
public class StructOfflineTask extends AbstractTask {
@Autowired
private IInBillService iInBillService;
@Autowired
private IMdPbGroupplateService iMdPbGroupplateService;
@Autowired
private ISchBasePointService iSchBasePointService;
@Autowired
private IMdMeMaterialbaseService iMdMeMaterialbaseService;
@Autowired
private IStructattrService iStructattrService;
/**
* 生成生产入库任务,任务完成
*/
public void offline(StructLineOfflineDTO offlineDTO){
SchBasePoint schBasePoint = iSchBasePointService.selectByCode(offlineDTO.getUnloadPort());
String storagevehicleCode = schBasePoint.getStoragevehicle_code();
if (StringUtils.isEmpty(storagevehicleCode)){
throw new BadRequestException("下线失败,下料点没有绑定托盘号");
}
final MdMeMaterialbase materialbase = iMdMeMaterialbaseService.getByCode(offlineDTO.getMaterialCode());
if (materialbase == null){
throw new BadRequestException("下线失败,物料编码未同步");
}
final GroupPlate plate = new GroupPlate();
plate.setStoragevehicle_code(storagevehicleCode);
plate.setGroup_id(IdUtil.getStringId());
plate.setCreate_id(SecurityUtils.getCurrentUserId());
plate.setCreate_name("MES");
plate.setCreate_time(DateUtil.now());
plate.setMaterial_code(materialbase.getMaterial_code());
plate.setQty(offlineDTO.getUnloadQty());
plate.setQty_unit_id(materialbase.getQty_unit_id());
plate.setQty_unit_name(materialbase.getQty_unit_id());
plate.setStatus(IOSEnum.GROUP_PLATE_STATUS.code("组盘"));
iMdPbGroupplateService.save(plate);
//强制:下线点对应的区域就是半成品库区编码
final String regionCode = schBasePoint.getRegion_code();
List<Structattr> structattrs = iStructattrService.inBoundSectDiv(StrategyStructParam.builder()
.ioType(StatusEnum.STRATEGY_TYPE.code("入库"))
.sect_code(regionCode)
.storagevehicle_code(plate.getStoragevehicle_code())
.strategyMaters(new ArrayList<>())
.build());
final Structattr structattr = structattrs.get(0);
// 组织主表数据
Map<String, Object> jsonMst = new HashMap<>();
jsonMst.put("stor_id", structattr.getStor_id());
jsonMst.put("bill_status", IOSEnum.BILL_STATUS.code("分配完"));
jsonMst.put("total_qty", plate.getQty());
jsonMst.put("detail_count", 1);
jsonMst.put("bill_type", IOSEnum.BILL_TYPE.code("生产入库"));
jsonMst.put("biz_date", DateUtil.now());
// 组织明细数据
ArrayList<HashMap> tableData = new ArrayList<>();
HashMap<String, Object> dtl = new HashMap<>();
dtl.put("storagevehicle_code", plate.getStoragevehicle_code());
dtl.put("material_id", materialbase.getMaterial_id());
dtl.put("material_code", materialbase.getMaterial_code());
dtl.put("material_name", materialbase.getMaterial_name());
dtl.put("pcsn", plate.getPcsn());
dtl.put("qty_unit_id", materialbase.getQty_unit_id());
dtl.put("qty_unit_name", materialbase.getQty_unit_name());
dtl.put("qty", plate.getQty());
dtl.put("plan_qty", plate.getQty());
// 调用新增
tableData.add(dtl);
jsonMst.put("tableData",tableData);
String iostorinv_id = iInBillService.insertDtl(jsonMst);
// 组织主数据
JSONObject divStructParam= new JSONObject();
divStructParam.put("checked", false);
divStructParam.put("stor_code", structattr.getStor_code());
divStructParam.put("sect_code", structattr.getSect_code());
final JSONObject disRow = new JSONObject();
divStructParam.put("dis_row", disRow);
disRow.put("struct_code",structattr.getStruct_code());
disRow.put("storagevehicle_code", plate.getStoragevehicle_code());
disRow.put("iostorinv_id", iostorinv_id);
// 调用分配,默认自动分配库位
iInBillService.divStruct(divStructParam);
// 下发任务
// 组织 divPoint 参数
Map<String, Object> divPointParam = new HashMap<>();
divPointParam.put("point_code", offlineDTO.getUnloadPort());
ArrayList<HashMap<String, String>> tableMater = new ArrayList<>();
HashMap<String, String> materRow = new HashMap<>();
materRow.put("iostorinv_id", iostorinv_id); // 入库单明细ID
materRow.put("iostorinvdis_id", iostorinvdis_id); // 分配明细ID如果有
materRow.put("storagevehicle_code", storagevehicleCode); // 载具编码
tableMater.add(materRow);
divPointParam.put("tableMater", tableMater);
iInBillService.divPoint(divPointParam);
}
@Override
public String create(JSONObject json) {
String task_id = stInTask.create(task_form);
return null;
}
@Override
public AcsTaskDto sendAcsParam(String taskId) {
return null;
}
@Override
protected void updateStatus(String task_code, TaskStatus status) {
}
@Override
public void forceFinish(String task_code) {
}
@Override
public void cancel(String task_code) {
}
}

View File

@@ -130,9 +130,7 @@ public class VehicleInTask extends AbstractTask {
this.cancelTask(taskObj);
}
@Override
public void backMes(String task_code) {
}
@Transactional
public void finishTask(SchBaseTask taskObj) {

View File

@@ -125,9 +125,7 @@ public class VehicleOutTask extends AbstractTask {
this.cancelTask(taskObj);
}
@Override
public void backMes(String task_code) {
}
@Transactional(rollbackFor = Exception.class)
public void finishTask(SchBaseTask taskObj) {

View File

@@ -195,6 +195,14 @@ public class SchBasePointServiceImpl extends ServiceImpl<SchBasePointMapper, Sch
return pointMapper.selectByIdLock(id);
}
@Override
public SchBasePoint selectByCode(String code) {
if (StringUtils.isEmpty(code)){
throw new BadRequestException("查询点位失败,编码为空");
}
return this.getOne(new LambdaQueryWrapper<SchBasePoint>().eq(SchBasePoint::getPoint_code,code));
}
@Override
public void unLockPoint(String point_code) {
this.update(

View File

@@ -12,6 +12,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.nl.common.domain.query.PageQuery;
import org.nl.common.exception.BadRequestException;
import org.nl.common.utils.CodeUtil;
@@ -28,6 +29,7 @@ import org.nl.wms.basedata_manage.service.dao.mapper.MdPbStoragevehicleinfoMappe
import org.nl.wms.basedata_manage.service.dto.StrategyMater;
import org.nl.wms.basedata_manage.service.dto.StrategyStructParam;
import org.nl.wms.basedata_manage.service.dto.StructattrChangeDto;
import org.nl.wms.sch_manage.enums.StatusEnum;
import org.nl.wms.sch_manage.service.dao.SchBaseTask;
import org.nl.wms.sch_manage.service.core.tasks.StInTask;
import org.nl.wms.warehouse_manage.enums.IOSConstant;
@@ -369,6 +371,7 @@ public class InBillServiceImpl extends ServiceImpl<IOStorInvMapper, IOStorInv> i
String struct_name = "";
//是否自动分配仓位
Boolean checked = mst.getBoolean("checked");
Structattr struct;
if (ObjectUtil.isNotEmpty(checked) && checked) {
//根据载具进行分配货位
List<StrategyMater> maters = new ArrayList<>();
@@ -382,31 +385,25 @@ public class InBillServiceImpl extends ServiceImpl<IOStorInvMapper, IOStorInv> i
}
List<Structattr> structattrs = iStructattrService.inBoundSectDiv(
StrategyStructParam.builder()
.ioType(param.getString("ioType"))
.ioType(StatusEnum.STRATEGY_TYPE.code("入库"))
.sect_code(param.getString("sect_code"))
.stor_code(param.getString("stor_code"))
.storagevehicle_code(param.getString("storagevehicle_code"))
.strategyMaters(maters)
.build());
Structattr struct = structattrs.get(0);
sect_id = struct.getSect_id();
sect_code = struct.getSect_code();
sect_name = struct.getSect_name();
struct_id = struct.getStruct_id();
struct_code = struct.getStruct_code();
struct_name = struct.getStruct_name();
struct = structattrs.get(0);
} else {
Structattr structattr = iStructattrService.findById(map.getString("struct_id"));
MdPbStoragevehicleinfo mdPbStoragevehicleinfo = mdPbStoragevehicleinfoMapper.selectOne(new LambdaQueryWrapper<>(MdPbStoragevehicleinfo.class)
.eq(MdPbStoragevehicleinfo::getStoragevehicle_code, map.get("storagevehicle_code"))
);
sect_id = map.getString("sect_id");
sect_code = map.getString("sect_code");
sect_name = map.getString("sect_name");
struct_id = map.getString("struct_id");
struct_code = map.getString("struct_code");
struct_name = map.getString("struct_name");
if (StringUtils.isEmpty(map.getString("struct_code"))){
throw new BadRequestException("分配失败手动分配struct_code为空");
}
struct = iStructattrService.findByCode(map.getString("struct_code"));
}
sect_id = struct.getSect_id();
sect_code = struct.getSect_code();
sect_name = struct.getSect_name();
struct_id = struct.getStruct_id();
struct_code = struct.getStruct_code();
struct_name = struct.getStruct_name();
JSONObject dis_map = new JSONObject();
dis_map.put("sect_id", sect_id);
dis_map.put("sect_code", sect_code);

View File

@@ -1133,7 +1133,6 @@ public class OutBillServiceImpl extends ServiceImpl<IOStorInvMapper,IOStorInv> i
}
@Override
@Transactional
//TODO调拨还是直接出库
public void disFinish(List<IOStorInvDis> disList) {
// 完成当前任务对应的所有分配明细
List<OutFinishParam> outFinsParams = new ArrayList<>();
@@ -1191,7 +1190,7 @@ public class OutBillServiceImpl extends ServiceImpl<IOStorInvMapper,IOStorInv> i
}
}
iStInventoryService.changeInventory(InventoryChangeType.ADD_INV, addInvParams);
//生成调拨入库单据
//TODO生成调拨入库单据
}
// 如果分配明细全部完成则更新明细表状态
if (countDis == 0){

View File

@@ -68,7 +68,6 @@ public class StInventoryServiceImpl implements IStInventoryService {
.material_code(invParam.getMaterialCode())
.storagevehicle_code(invParam.getStoragevehicleCode())
.pcsn(invParam.getPcsn())
//TODO:先给默认个
.qty_unit_id(invParam.getUnitId())
.qty_unit_name(invParam.getUnitName())
.qty(invParam.getQty())

View File

@@ -24,11 +24,11 @@ public class InteractionController {
}
@PostMapping("/blank")
@Log("产线下半成品入库请求")
@Log("产线下线半成品入库请求")
public ResponseEntity<Object> blank()
{
return null;
}
}
}

View File

@@ -7,6 +7,7 @@ import org.nl.common.domain.query.PageQuery;
import org.nl.common.logging.annotation.Log;
import org.nl.wms.welding_manage.service.work_order.IWorkOrderService;
import org.nl.wms.welding_manage.service.work_order.dto.WorkOrderDto;
import org.nl.wms.welding_manage.service.work_order.dto.WorkOrderQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@@ -29,9 +30,13 @@ public class WorkOrderController {
@GetMapping
@Log("分页查询工单列表和详情")
public ResponseEntity<Object> query(@RequestParam Map params, PageQuery page)
{
return new ResponseEntity<>(TableDataInfo.build(iWorkOrderService.queryList(page, params)), HttpStatus.OK);
public ResponseEntity<Object> query(WorkOrderQuery query, PageQuery page) {
return new ResponseEntity<>(TableDataInfo.build(iWorkOrderService.page(page.build(),query.build())), HttpStatus.OK);
}
@GetMapping("/getOrderItem")
@SaIgnore
public ResponseEntity<Object> getOrderItem(String orderCode) {
return new ResponseEntity<>(TableDataInfo.build(iWorkOrderService.getOrderBomItem(orderCode)), HttpStatus.OK);
}
@PostMapping

View File

@@ -3,10 +3,12 @@ package org.nl.wms.welding_manage.service.work_order;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import org.nl.common.domain.query.PageQuery;
import org.nl.wms.welding_manage.service.work_order.dao.WorkOrderBomDao;
import org.nl.wms.welding_manage.service.work_order.dao.WorkOrderDao;
import org.nl.wms.welding_manage.service.work_order.dto.WorkOrderDto;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
public interface IWorkOrderService extends IService<WorkOrderDao> {
@@ -34,4 +36,6 @@ public interface IWorkOrderService extends IService<WorkOrderDao> {
* 修改工单已使用bom数量
*/
void updateBomUseQty(String id, BigDecimal qty);
List<WorkOrderBomDao> getOrderBomItem(String orderCode);
}

View File

@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.lettuce.core.dynamic.annotation.Param;
import org.apache.ibatis.annotations.Select;
import org.nl.wms.welding_manage.service.work_order.dao.WorkOrderBomDao;
import org.nl.wms.welding_manage.service.work_order.dao.WorkOrderDao;
import org.nl.wms.welding_manage.service.work_order.dto.WorkOrderDto;
@@ -13,6 +15,9 @@ import java.util.Map;
public interface WorkOrderMapper extends BaseMapper<WorkOrderDao> {
@Select("SELECT * FROM md_work_order_bom WHERE work_order_code = #{orderCode} ORDER BY item_no ASC")
List<WorkOrderBomDao> getOrderBomItem(String orderCode);
/**
* 分页查询工单
* @param page

View File

@@ -0,0 +1,7 @@
package org.nl.wms.welding_manage.service.work_order.dto;
import org.nl.common.domain.query.BaseQuery;
import org.nl.wms.welding_manage.service.work_order.dao.WorkOrderDao;
public class WorkOrderQuery extends BaseQuery<WorkOrderDao> {
}

View File

@@ -4,7 +4,10 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.nl.common.domain.query.PageQuery;
import org.nl.common.exception.BadRequestException;
import org.nl.wms.welding_manage.service.work_order.dao.WorkOrderBomDao;
import org.nl.wms.welding_manage.service.work_order.dao.WorkOrderDao;
import org.nl.wms.welding_manage.service.work_order.dao.mapper.WorkOrderMapper;
import org.nl.wms.welding_manage.service.work_order.dto.WorkOrderDto;
@@ -12,7 +15,6 @@ import org.nl.wms.welding_manage.service.work_order.IWorkOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@@ -56,8 +58,7 @@ public class WorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, WorkOrder
}
String snListString = record.getWorkOrderSnListString();
if (StringUtils.hasText(snListString)) {
// 按逗号分割字符串并去除空白字符转换为List
if (!StringUtils.isEmpty(snListString)){
List<String> snList = Arrays.stream(snListString.split(","))
.map(String::trim)
.filter(s -> !s.isEmpty())
@@ -109,4 +110,12 @@ public class WorkOrderServiceImpl extends ServiceImpl<WorkOrderMapper, WorkOrder
public void updateBomUseQty(String id, BigDecimal qty) {
workOrderMapper.updateBomUseQty(id, qty);
}
}
@Override
public List<WorkOrderBomDao> getOrderBomItem(String orderCode) {
if (StringUtils.isEmpty(orderCode)){
throw new BadRequestException("查询工单明细失败,工单编号为空");
}
return workOrderMapper.getOrderBomItem(orderCode);
}
}

View File

@@ -0,0 +1,305 @@
<template>
<div class="app-container">
<div class="head-container">
<!-- 查询区域 -->
<div>
<el-form :inline="true" label-position="right" label-width="100px" label-suffix=":" class="demo-form-inline">
<el-form-item label="工单编号">
<el-input v-model="query.order_code" clearable size="mini" placeholder="请输入工单编号" @keyup.enter.native="crud.toQuery" />
</el-form-item>
<el-form-item label="产品名称">
<el-input v-model="query.product_name" clearable size="mini" placeholder="请输入产品名称" @keyup.enter.native="crud.toQuery" />
</el-form-item>
<el-form-item label="BOM编码">
<el-input v-model="query.bom_code" clearable size="mini" placeholder="请输入BOM编码" @keyup.enter.native="crud.toQuery" />
</el-form-item>
<el-form-item label="工作区域">
<el-input v-model="query.work_area" clearable size="mini" placeholder="请输入工作区域" @keyup.enter.native="crud.toQuery" />
</el-form-item>
<el-form-item label="状态">
<el-select v-model="query.status" clearable size="mini" placeholder="请选择状态" class="filter-item" @change="crud.toQuery">
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<rrOperation :crud="crud" />
</el-form>
</div>
<!-- 操作按钮区域 -->
<div style="margin-bottom: 10px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap;">
<crudOperation :permission="permission" />
</div>
<!-- 新增/编辑对话框 -->
<el-dialog
:close-on-click-modal="false"
:before-close="crud.cancelCU"
:visible.sync="crud.status.cu > 0"
:title="crud.status.title"
width="960px"
>
<el-form ref="form" :model="form" :rules="rules" size="mini" label-width="120px" label-suffix=":" style="border: 1px solid #cfe0df; margin-top: 10px; padding: 10px;">
<el-row>
<el-col :span="12">
<el-form-item label="工单编号" prop="order_code">
<el-input v-model.trim="form.order_code" style="width: 280px;" placeholder="请输入工单编号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="产品编码" prop="product_code">
<el-input v-model.trim="form.product_code" style="width: 280px;" placeholder="请输入产品编码" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="产品名称" prop="product_name">
<el-input v-model.trim="form.product_name" style="width: 280px;" placeholder="请输入产品名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="产品规格" prop="product_spec">
<el-input v-model.trim="form.product_spec" style="width: 280px;" placeholder="请输入产品规格" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="计划数量" prop="order_qty">
<el-input-number v-model="form.order_qty" :min="0" :step="1" style="width: 280px;" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="单位" prop="unit">
<el-input v-model.trim="form.unit" style="width: 280px;" placeholder="请输入单位" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="BOM编码" prop="bom_code">
<el-input v-model.trim="form.bom_code" style="width: 280px;" placeholder="请输入BOM编码" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="工作区域" prop="work_area">
<el-input v-model.trim="form.work_area" style="width: 280px;" placeholder="请输入工作区域" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="计划开始时间" prop="plan_started_on">
<el-date-picker v-model="form.plan_started_on" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择计划开始时间" style="width: 280px;" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="计划完成时间" prop="plan_finished_on">
<el-date-picker v-model="form.plan_finished_on" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择计划完成时间" style="width: 280px;" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="状态" prop="status">
<el-select v-model="form.status" style="width: 280px;" placeholder="请选择状态">
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="备注" prop="remark">
<el-input v-model.trim="form.remark" style="width: 280px;" placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!-- 工单明细对话框 -->
<el-dialog
:visible.sync="detailVisible"
title="工单明细BOM清单"
width="1100px"
:close-on-click-modal="false"
@close="closeDetailDialog"
>
<div style="margin-bottom: 10px; color: #606266; font-size: 13px;">
工单编号:<strong>{{ currentWorkOrder.order_code }}</strong> &nbsp;|&nbsp;
产品:<strong>{{ currentWorkOrder.product_code }} {{ currentWorkOrder.product_name }}</strong> &nbsp;|&nbsp;
计划数量:<strong style="color: #E6A23C;">{{ currentWorkOrder.order_qty }} {{ currentWorkOrder.unit }}</strong>
</div>
<el-table v-loading="detailLoading" :data="bomList" size="small" border max-height="480">
<el-table-column type="index" label="序号" width="55" align="center" />
<el-table-column prop="item_no" label="行号" width="60" align="center" />
<el-table-column prop="material_code" label="物料编码" min-width="130" show-overflow-tooltip />
<el-table-column prop="material_name" label="物料名称" min-width="160" show-overflow-tooltip />
<el-table-column prop="spec" label="规格" min-width="120" show-overflow-tooltip />
<el-table-column prop="unit" label="单位" width="70" align="center" />
<el-table-column prop="bom_qty" label="BOM用量" width="100" align="right" />
<el-table-column prop="use_bom_qty" label="已用数量" width="100" align="right" />
<el-table-column prop="wms_qty" label="WMS数量" width="100" align="right" />
<el-table-column prop="warehouse_name" label="仓库" min-width="120" show-overflow-tooltip />
<el-table-column prop="load_port" label="上料口" width="90" align="center" show-overflow-tooltip />
<el-table-column prop="work_center" label="工作中心" min-width="110" show-overflow-tooltip />
<el-table-column prop="track_no" label="跟踪号" min-width="120" show-overflow-tooltip />
<el-table-column prop="status" label="状态" width="80" align="center">
<template slot-scope="scope">
<el-tag :type="bomStatusTagType(scope.row.status)" size="mini">{{ bomStatusLabel(scope.row.status) }}</el-tag>
</template>
</el-table-column>
</el-table>
<div slot="footer" class="dialog-footer">
<el-button size="mini" @click="closeDetailDialog">关闭</el-button>
</div>
</el-dialog>
<!-- 主列表 -->
<el-table
ref="table"
v-loading="crud.loading"
:data="crud.data"
size="small"
border
row-key="id"
@selection-change="handleSelectionChange"
@expand-change="handleExpandChange"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="id" label="ID" width="220" show-overflow-tooltip />
<el-table-column prop="order_code" label="工单编号" min-width="150" show-overflow-tooltip />
<el-table-column prop="product_code" label="产品编码" min-width="130" show-overflow-tooltip />
<el-table-column prop="product_name" label="产品名称" min-width="160" show-overflow-tooltip />
<el-table-column prop="product_spec" label="产品规格" min-width="120" show-overflow-tooltip />
<el-table-column prop="order_qty" label="计划数量" width="90" align="right" />
<el-table-column prop="use_order_qty" label="已用数量" width="90" align="right" />
<el-table-column prop="finished_qty" label="完成数量" width="90" align="right" />
<el-table-column prop="unit" label="单位" width="70" align="center" />
<el-table-column prop="bom_code" label="BOM编码" min-width="130" show-overflow-tooltip />
<el-table-column prop="work_area" label="工作区域" min-width="110" show-overflow-tooltip />
<el-table-column prop="plan_started_on" label="计划开始" min-width="155" show-overflow-tooltip />
<el-table-column prop="plan_finished_on" label="计划完成" min-width="155" show-overflow-tooltip />
<el-table-column prop="status" label="状态" width="90" align="center">
<template slot-scope="scope">
<el-tag :type="statusTagType(scope.row.status)" size="mini">{{ statusLabel(scope.row.status) }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="create_time" label="创建时间" min-width="155" show-overflow-tooltip />
<el-table-column label="操作" width="210" align="center" fixed="right">
<template slot-scope="scope">
<udOperation style="display: inline" :data="scope.row" :permission="permission" />
<el-button size="mini" type="primary" plain style="margin-left: 4px;" @click="openDetailDialog(scope.row.order_code)">明细</el-button>
</template>
</el-table-column>
</el-table>
<pagination />
</div>
</div>
</template>
<script>
import crudWorkOrder from './workOrder'
import CRUD, { crud, form, header, presenter } from '@crud/crud'
import crudOperation from '@crud/CRUD.operation.vue'
import udOperation from '@crud/UD.operation.vue'
import rrOperation from '@crud/RR.operation.vue'
import pagination from '@crud/Pagination.vue'
const createDefaultForm = () => ({
id: null,
order_code: '',
product_code: '',
product_name: '',
product_spec: '',
order_qty: undefined,
unit: '',
bom_code: '',
work_area: '',
plan_started_on: '',
plan_finished_on: '',
status: undefined,
remark: ''
})
export default {
name: 'WorkOrder',
components: { pagination, crudOperation, rrOperation, udOperation },
mixins: [presenter(), header(), form(createDefaultForm()), crud()],
cruds() {
return CRUD({
title: '工单管理',
url: 'api/workOrder',
idField: 'id',
sort: 'plan_started_on,desc',
crudMethod: { ...crudWorkOrder },
optShow: { add: true, reset: true }
})
},
data() {
return {
permission: {},
statusOptions: [
{ value: 1, label: '待执行' },
{ value: 2, label: '执行中' },
{ value: 3, label: '完成' },
{ value: 4, label: '暂停' },
{ value: 5, label: '撤销' }
],
bomStatusOptions: [
{ value: 0, label: '待备料' },
{ value: 1, label: '备料中' },
{ value: 2, label: '已备料' }
],
detailVisible: false,
detailLoading: false,
currentWorkOrder: {},
bomList: [],
multipleSelection: [],
rules: {
order_code: [{ required: true, message: '请输入工单编号', trigger: 'blur' }],
product_code: [{ required: true, message: '请输入产品编码', trigger: 'blur' }],
product_name: [{ required: true, message: '请输入产品名称', trigger: 'blur' }],
order_qty: [{ required: true, message: '请输入计划数量', trigger: 'change' }],
unit: [{ required: true, message: '请输入单位', trigger: 'blur' }],
bom_code: [{ required: true, message: '请输入BOM编码', trigger: 'blur' }]
}
}
},
methods: {
statusLabel(status) {
const target = this.statusOptions.find(item => Number(item.value) === Number(status))
return target ? target.label : status
},
statusTagType(status) {
const map = { 1: 'info', 2: 'primary', 3: 'success', 4: 'warning', 5: 'danger' }
return map[Number(status)] || ''
},
bomStatusLabel(status) {
const target = this.bomStatusOptions.find(item => Number(item.value) === Number(status))
return target ? target.label : (status !== undefined && status !== null ? status : '-')
},
bomStatusTagType(status) {
const map = { 0: 'info', 1: 'warning', 2: 'success' }
return map[Number(status)] || ''
},
handleSelectionChange(selection) {
this.multipleSelection = selection
},
handleExpandChange(row) {
// BOM data is already loaded with the row from the backend
},
openDetailDialog(order) {
crudWorkOrder.getItem(order).then(result => {
this.bomList = result.content || []
this.detailVisible = true
}).catch(error => {
this.bomList = []
}).finally(() => {
this.detailLoading = false
})
},
closeDetailDialog() {
this.detailVisible = false
this.currentWorkOrder = {}
this.bomList = []
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,35 @@
import request from '@/utils/request'
export function add(data) {
return request({
url: 'api/workOrder',
method: 'post',
data
})
}
export function del(ids) {
return request({
url: 'api/workOrder',
method: 'delete',
data: ids
})
}
export function edit(data) {
return request({
url: 'api/workOrder',
method: 'put',
data
})
}
export function getItem(data) {
return request({
url: 'api/workOrder/getOrderItem',
method: 'get',
params: {
orderCode: data
}
})
}
export default { add, edit, del, getItem }

View File

@@ -53,8 +53,8 @@
<p v-if="scope.row.forwardZD == '0'">-</p>
<template v-else-if="scope.row.forwardZD == '1'">
<el-popconfirm
confirm-button-text=''
cancel-button-text=''
confirm-button-text=""
cancel-button-text=""
icon="el-icon-info"
icon-color="red"
title="是否重新转发中鼎?"
@@ -63,17 +63,17 @@
<a slot="reference" style="color: red;text-decoration: underline">失败</a>
</el-popconfirm>
</template>
<!-- <a style="color: red;text-decoration: underline" v-else-if="scope.row.forwardZD == '1'">-->
<!-- <el-popconfirm-->
<!-- class="box-item"-->
<!-- title="是否重新转发?"-->
<!-- placement="top-start"-->
<!-- >-->
<!-- <template #reference>-->
<!-- 失败-->
<!-- </template>-->
<!-- </el-popconfirm>-->
<!-- </a>-->
<!-- <a style="color: red;text-decoration: underline" v-else-if="scope.row.forwardZD == '1'">-->
<!-- <el-popconfirm-->
<!-- class="box-item"-->
<!-- title="是否重新转发?"-->
<!-- placement="top-start"-->
<!-- >-->
<!-- <template #reference>-->
<!-- 失败-->
<!-- </template>-->
<!-- </el-popconfirm>-->
<!-- </a>-->
<p v-else>成功</p>
</template>
</el-table-column>
@@ -90,8 +90,8 @@
{{ formatDate(scope.row.modify_date) }}
</template>
</el-table-column>
<!-- <el-table-column prop="status" :formatter="auditStatusFormat" width="100" label="审核状态" />-->
<!-- <el-table-column show-overflow-tooltip prop="audit_msg" width="150" label="审核信息" />-->
<!-- <el-table-column prop="status" :formatter="auditStatusFormat" width="100" label="审核状态" />-->
<!-- <el-table-column show-overflow-tooltip prop="audit_msg" width="150" label="审核信息" />-->
</el-table>
<pagination />
</div>
@@ -166,7 +166,7 @@ export default {
const s = String(d.getSeconds()).padStart(2, '0')
return y + '-' + m + '-' + day + ' ' + h + ':' + min + ':' + s
},
toForwardZD(){
toForwardZD() {
console.log(123)
},
toView(index, row) {
@@ -179,7 +179,7 @@ export default {
// this.$refs.table.clearSelection()
// this.$refs.table.toggleRowSelection(val.pop())
this.audit_flag = true
}else {
} else {
this.audit_flag = false
}
},

View File

@@ -28,6 +28,7 @@ const zh = {
'平库调拨出库',
'线边领料出库',
'配盘出库',
'配盘入库'
// '空托上架/下架/注册/呼叫',
// 'AGV配送',
// '人工盘点',

View File

@@ -1,408 +0,0 @@
<template>
<div class="page-container">
<van-nav-bar
:title="t('productGroup.title')"
left-arrow
@click-left="router.push('/')"
/>
<div class="page-content">
<van-form ref="formRef">
<van-field
v-model="workSectText"
is-link
readonly
:label="t('productGroup.workSect')"
:placeholder="t('productGroup.workSectPlaceholder')"
:rules="[{ required: true, message: t('productGroup.workSectRequired') }]"
name="workSect"
@click="showWorkSectPicker = true"
/>
<van-field
v-model="palletCode"
:label="t('productGroup.palletCode')"
:placeholder="t('productGroup.palletCodePlaceholder')"
:rules="[{ required: true, message: t('productGroup.palletCodeRequired') }]"
name="palletCode"
clearable
>
<template #right-icon>
<van-icon name="scan" class="scan-icon" @click="onScanPallet" />
</template>
</van-field>
<van-field
v-model="workOrderText"
is-link
readonly
:label="t('productGroup.workOrder')"
:placeholder="t('productGroup.workOrderPlaceholder')"
:rules="[{ required: true, message: t('productGroup.workOrderRequired') }]"
name="workOrder"
@click="onWorkOrderClick"
/>
<van-field
v-model="loadPortText"
is-link
readonly
:label="t('productGroup.loadPort')"
:placeholder="t('productGroup.loadPortPlaceholder')"
:rules="[{ required: true, message: t('productGroup.loadPortRequired') }]"
name="loadPort"
@click="onLoadPortClick"
/>
</van-form>
<div class="detail-section">
<div class="detail-header">
<span class="detail-header-text">{{ t('productGroup.total', bomList.length) }}</span>
</div>
<div v-if="!bomList.length" class="detail-empty">
<van-empty :description="t('productGroup.noData')" image="search" />
</div>
<div v-else class="card-list">
<div
v-for="(item, idx) in bomList"
:key="idx"
class="detail-card"
>
<div class="card-header">
<span class="card-index">#{{ idx + 1 }}</span>
</div>
<div class="card-body">
<div class="card-row">
<span class="card-label">{{ t('productGroup.materialCode') }}</span>
<span class="card-value">{{ item.MaterialCode }}</span>
</div>
<div class="card-row">
<span class="card-label">{{ t('productGroup.materialName') }}</span>
<span class="card-value">{{ item.MaterialName }}</span>
</div>
<div class="card-row">
<span class="card-label">{{ t('productGroup.bomQty') }}</span>
<span class="card-value card-value--num">{{ item.BomQty }}</span>
</div>
<div class="card-row">
<span class="card-label">{{ t('productGroup.useBomQty') }}</span>
<span class="card-value">{{ item.UseBomQty }}</span>
</div>
<div class="card-row">
<span class="card-label">{{ t('productGroup.planQty') }}</span>
<span class="card-value card-value--num">{{ item.defaultQty }}</span>
</div>
<div class="card-row card-row--input">
<span class="card-label">{{ t('productGroup.inputQty') }}</span>
<van-field
v-model="item.inputQty"
type="number"
class="card-input"
:placeholder="t('productGroup.inputQtyPlaceholder')"
/>
</div>
<div class="card-row">
<span class="card-label">{{ t('productGroup.unit') }}</span>
<span class="card-value">{{ item.Unit }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<van-popup v-model:show="showWorkSectPicker" round position="bottom">
<van-picker
:columns="workSectOptions"
@confirm="onWorkSectConfirm"
@cancel="showWorkSectPicker = false"
/>
</van-popup>
<van-popup v-model:show="showWorkOrderPicker" round position="bottom">
<van-picker
:columns="workOrderOptions"
@confirm="onWorkOrderConfirm"
@cancel="showWorkOrderPicker = false"
/>
</van-popup>
<van-popup v-model:show="showLoadPortPicker" round position="bottom">
<van-picker
:columns="loadPortOptions"
@confirm="onLoadPortConfirm"
@cancel="showLoadPortPicker = false"
/>
</van-popup>
<BottomButton :text="t('productGroup.confirm')" :loading="submitting" @click="onSubmit" />
</div>
</template>
<script setup>
import { ref, computed, watch, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { showToast } from 'vant'
import { useI18n } from '@/i18n'
import BottomButton from '@/components/BottomButton.vue'
import request from '@/utils/request'
const router = useRouter()
const { t } = useI18n()
const workSect = ref('')
const workSectText = ref('')
const palletCode = ref('')
const selectedOrderCode = ref('')
const workOrderText = ref('')
const selectedLoadPort = ref('')
const loadPortText = ref('')
const bomList = ref([])
const formRef = ref()
const submitting = ref(false)
const orderList = ref([])
const currentOrder = ref(null)
const showWorkSectPicker = ref(false)
const showWorkOrderPicker = ref(false)
const showLoadPortPicker = ref(false)
const workSectOptions = [
{ text: 'work1', value: 'work1' },
{ text: 'work2', value: 'work2' },
]
const workOrderOptions = computed(() =>
orderList.value.map((item) => ({
text: item.OrderCode,
value: item.OrderCode,
}))
)
const loadPortOptions = computed(() => {
if (!currentOrder.value || !currentOrder.value.PortList) return []
return currentOrder.value.PortList.map((p) => ({
text: p.Port,
value: p.Port,
}))
})
async function fetchWorkOrders() {
try {
const res = await request.get('/api/workOrder', { params: { status: 2 } })
orderList.value = res.content || []
} catch {
// error handled by interceptor
}
}
function onWorkSectConfirm({ selectedOptions }) {
const opt = selectedOptions[0]
workSect.value = opt.value
workSectText.value = opt.text
showWorkSectPicker.value = false
}
function onWorkOrderClick() {
if (!workOrderOptions.value.length) {
showToast({ message: t('productGroup.noOrder'), type: 'fail' })
return
}
showWorkOrderPicker.value = true
}
function onWorkOrderConfirm({ selectedOptions }) {
const opt = selectedOptions[0]
selectedOrderCode.value = opt.value
workOrderText.value = opt.text
currentOrder.value = orderList.value.find((o) => o.OrderCode === opt.value) || null
selectedLoadPort.value = ''
loadPortText.value = ''
bomList.value = []
showWorkOrderPicker.value = false
}
function onLoadPortClick() {
if (!currentOrder.value || !loadPortOptions.value.length) {
showToast({ message: t('productGroup.noPort'), type: 'fail' })
return
}
showLoadPortPicker.value = true
}
function onLoadPortConfirm({ selectedOptions }) {
const opt = selectedOptions[0]
selectedLoadPort.value = opt.value
loadPortText.value = opt.text
filterBomList()
showLoadPortPicker.value = false
}
function filterBomList() {
if (!currentOrder.value || !selectedLoadPort.value) {
bomList.value = []
return
}
const allBom = currentOrder.value.WorkOrderBomList || []
bomList.value = allBom
.filter((item) => item.LoadPort === selectedLoadPort.value)
.map((item) => ({
...item,
defaultQty: item.BomQty - item.UseBomQty,
inputQty: String(item.BomQty - item.UseBomQty),
}))
}
function onScanPallet() {
palletCode.value = 'PLT' + Date.now().toString().slice(-6)
}
async function onSubmit() {
try {
await formRef.value?.validate()
} catch {
return
}
if (!bomList.value.length) {
showToast({ message: t('productGroup.noData'), type: 'fail' })
return
}
const bomDataList = bomList.value.map((item) => ({
id: item.Id,
materialCode: item.MaterialCode,
unit: item.Unit,
useBomQty: Number(item.inputQty) || 0,
}))
submitting.value = true
try {
const res = await request.post('/api/productGroup', {
workSect: workSect.value,
workOrder: selectedOrderCode.value,
palletCode: palletCode.value,
loadPort: selectedLoadPort.value,
bomDataList,
})
showToast({
message: t('productGroup.submitSuccess'),
type: 'success',
})
workSect.value = ''
workSectText.value = ''
palletCode.value = ''
selectedOrderCode.value = ''
workOrderText.value = ''
selectedLoadPort.value = ''
loadPortText.value = ''
currentOrder.value = null
bomList.value = []
} catch {
// error handled by interceptor
} finally {
submitting.value = false
}
}
onMounted(() => {
fetchWorkOrders()
})
</script>
<style scoped>
.detail-section {
margin-top: 12px;
}
.detail-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 0;
}
.detail-header-text {
font-size: 14px;
color: #666;
font-weight: 600;
}
.detail-empty {
padding: 20px 0;
}
.card-list {
display: flex;
flex-direction: column;
gap: 10px;
padding-bottom: 10px;
}
.detail-card {
background: #fff;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
background: var(--primary-gradient);
color: #fff;
}
.card-index {
font-size: 14px;
font-weight: 600;
}
.card-body {
padding: 10px 12px;
}
.card-row {
display: flex;
align-items: flex-start;
padding: 4px 0;
font-size: 14px;
line-height: 1.5;
}
.card-row--input {
align-items: center;
}
.card-label {
flex-shrink: 0;
width: 80px;
color: #999;
}
.card-value {
flex: 1;
color: #333;
word-break: break-all;
}
.card-value--num {
color: var(--primary-color);
font-weight: 600;
}
.card-input {
flex: 1;
padding: 0;
background: transparent;
}
.card-input :deep(.van-field__control) {
font-size: 14px;
color: var(--primary-color);
font-weight: 600;
}
.card-input :deep(.van-field__body) {
padding: 2px 8px;
border: 1px solid var(--border-color);
border-radius: 4px;
background: #fff;
}
</style>

View File

@@ -34,5 +34,3 @@ CREATE TABLE IF NOT EXISTS `st_ivt_purchasedtl` (
`instock_qty` DECIMAL(18,6) DEFAULT 0 COMMENT '已入库数量'
)COMMENT='采购入库单分录信息表';
ALTER TABLE `wms_nlwq`.`st_ivt_purchasemst`
ADD COLUMN `forwardZD` tinyint(1) NULL DEFAULT 0 COMMENT '0否 1是' AFTER `audit_msg`;