自动化立体仓库 - WMS系统
13个文件已添加
32个文件已修改
2117 ■■■■■ 已修改文件
src/main/java/com/zy/api/enums/MatLocType.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/MobileController.java 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/OrderPakoutController.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/OutController.java 99 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/StationRelaController.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/OrderDetlPakout.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/OrderPakout.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/StationRela.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/TaskDetl.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/TaskDetlLog.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/WaitPakin.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/WaitPakinLog.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/param/OrderDomainParam.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/enums/CommonStation.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/enums/OrderTypeEnum.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/enums/TaskIOType.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/mapper/LocDetlMapper.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/mapper/StationRelaMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/LocDetlService.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/MobileService.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/StationRelaService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/WorkService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/LocDetlServiceImpl.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/MobileServiceImpl.java 317 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/StationRelaServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/TaskDetlLogServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/TaskLogServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/TaskServiceImpl.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/WorkServiceImpl.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/handler/WorkMastHandler.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/CodeBuilder.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/model/LocDto.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/model/TaskDto.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/model/enums/IoWorkType.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/stationRela.sql 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/LocDetlMapper.xml 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/StationRelaMapper.xml 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/orderPakout/agvOut.js 386 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/orderPakout/order.js 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/orderPakout/out.js 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/orderTablePakout.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/orderTablePakoutAGV.js 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/task/task.js 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/orderPakout/agvOut.html 332 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/orderPakout/order.html 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/api/enums/MatLocType.java
New file
@@ -0,0 +1,26 @@
package com.zy.api.enums;
/**
 * 物料所属库位类型
 * @author Ryan
 * @date 2025/12/6 10:41
 * @return null
 */
public enum MatLocType {
    /** 小件 */
    AUTOMATED("12", "小件"),
    /** 中件 */
    SO_HOLDING("13", "中件"),
    /** 大件 */
    EO_HOLDING("14", "滤芯");
    public String type;
    public String desc;
    MatLocType(String type, String desc) {
        this.type = type;
        this.desc = desc;
    }
}
src/main/java/com/zy/asrs/controller/MobileController.java
@@ -64,7 +64,7 @@
    @PostMapping("/agv/callEmptyCar")
    @ManagerAuth
    public R pdaAgvFinishedCall(@RequestBody AgvCallParams params){
        return mobileService.callEmptyCar(params);
        return mobileService.callEmptyCar(params, getUserId());
    }
    @RequestMapping("/pda/WarehouseOut/v1")
@@ -173,11 +173,21 @@
        return mobileService.callAgvMove(params, getUserId());
    }
    @PostMapping("/collection/agv/call")
    @ApiOperation("集货区入库")
    @ManagerAuth
    public R CollectionInCall(@RequestBody AgvCallParams params) {
        if (Objects.isNull(params)) {
            return R.error("参数不能为空!!");
        }
        return mobileService.collectionInCall(params, getUserId());
    }
    /**
     * @author Ryan
     * @date 2025/11/3
     * @description: 呼叫AGV搬运缓存区/EO/SO
     * @description: 呼叫AGV搬运入库缓存区/EO/SO
     * @version 1.0
     */
    @PostMapping("/cache/out/call")
src/main/java/com/zy/asrs/controller/OrderPakoutController.java
@@ -10,6 +10,7 @@
import com.zy.asrs.entity.*;
import com.zy.asrs.entity.param.OrderDomainParam;
import com.zy.asrs.entity.result.WrkTraceVo;
import com.zy.asrs.enums.OrderTypeEnum;
import com.zy.asrs.service.*;
import com.zy.common.model.DetlDto;
import com.zy.common.web.BaseController;
@@ -51,6 +52,37 @@
            wrapper.like("order_no", orderNo);
        }
        wrapper.le("settle", 2).eq("status", 1);
        // 筛选出库单,不为OrderTypeEnum.STOCK
        wrapper.ne("doc_type", OrderTypeEnum.STOCK.type);
        wrapper.orderBy("create_time", false);
        List<OrderPakout> orders = orderService.selectList(wrapper);
        // 保留出库单
        if (!Cools.isEmpty(orders)) {
            Iterator<OrderPakout> iterator = orders.iterator();
            while (iterator.hasNext()) {
                OrderPakout order = iterator.next();
                if (order.getDocType() != null) {
                    DocType docType = docTypeService.selectById(order.getDocType());
                    if (docType != null) {
                        if (docType.getPakout() == 0) {
                            iterator.remove();
                        }
                    }
                }
            }
        }
        return R.ok().add(orders);
    }
    @RequestMapping(value = "/order/AGV/nav/list/auth")
    @ManagerAuth
    public R AGVnavList(@RequestParam(required = false) String orderNo){
        EntityWrapper<OrderPakout> wrapper = new EntityWrapper<>();
        if (!Cools.isEmpty(orderNo)) {
            wrapper.like("order_no", orderNo);
        }
        wrapper.le("settle", 2).eq("status", 1);
        wrapper.eq("doc_type", OrderTypeEnum.STOCK.type);
        wrapper.orderBy("create_time", false);
        List<OrderPakout> orders = orderService.selectList(wrapper);
        // 保留出库单
@@ -117,7 +149,7 @@
                DateUtils.convert(now),    // 单据日期
                param.getDocType(),    // 单据类型
                null,    // 项目编号
                null,    //
                param.getItemName(),    // 区域ID
                null,    // 调拨项目编号
                null,    // 初始票据号
                null,    // 票据号
@@ -191,7 +223,8 @@
        Date now = new Date();
        Long userId = getUserId();
        // 修改主档
        if (!param.getDocType().equals(order.getDocType())) {
        if (!param.getDocType().equals(order.getDocType()) || !param.getItemName().equals(order.getItemName())) {
            order.setItemName(param.getItemName());
            order.setDocType(param.getDocType());
            order.setUpdateBy(userId);
            order.setUpdateTime(now);
src/main/java/com/zy/asrs/controller/OutController.java
@@ -6,8 +6,12 @@
import com.core.common.Cools;
import com.core.common.R;
import com.core.exception.CoolException;
import com.zy.api.enums.LocAreaType;
import com.zy.asrs.entity.*;
import com.zy.asrs.service.*;
import com.zy.asrs.service.impl.BasAreasServiceImpl;
import com.zy.asrs.service.impl.BasStationServiceImpl;
import com.zy.asrs.service.impl.LocCacheServiceImpl;
import com.zy.common.model.LocDto;
import com.zy.common.model.TaskDto;
import com.zy.common.web.BaseController;
@@ -50,7 +54,12 @@
    private OrderPakoutService orderPakOutService;
    @Autowired
    private CheckOrderService checkOrderService;
    @Autowired
    private BasAreasService basAreasService;
    @Autowired
    private LocCacheServiceImpl locCacheService;
    @Autowired
    private BasStationServiceImpl basStationService;
    @PostMapping("/out/pakout/orderDetlIds/auth")
@@ -74,37 +83,46 @@
        List<LocDto> locDtos = new ArrayList<>();
        Set<String> exist = new HashSet<>();
        Set<String> exist = new HashSet<>();
        // 获取订单主表
        OrderPakout orderPakOut = orderPakOutService.selectByNo(orderDetlPakouts.get(0).getOrderNo());
        // 盘点单出库
        if (orderPakOut.getDocType() == 8) {
        // 备货单
        if (orderPakOut.getDocType() == 5) {
            List<BasAreas> basAreas = basAreasService.selectList(new EntityWrapper<BasAreas>().in("whs_type_id", LocAreaType.SO_HOLDING.type, LocAreaType.EO_HOLDING.type));
            List<Long> areaIds = basAreas.stream()
                    .map(BasAreas::getId)
                    .collect(Collectors.toList());
            for (OrderDetlPakout orderDetl : orderDetlPakouts) {
                double issued = Optional.of(orderDetl.getAnfme() - orderDetl.getWorkQty()).orElse(0.0D);
                if (issued <= 0.0D) {
                    continue;
                }
                List<LocDetl> locDetls = locDetlService.queryStockAll(null, exist,orderDetl.getMatnr(), orderDetl.getBatch(),
                        orderDetl.getBrand(),orderDetl.getStandby1(),orderDetl.getStandby2(),orderDetl.getStandby3(),orderDetl.getBoxType1(),orderDetl.getBoxType2(),orderDetl.getBoxType3());
                List<LocDetl> locDetls = locDetlService.queryStockAllCache(null, exist,orderDetl.getMatnr(), orderDetl.getBatch(),
                        orderDetl.getBrand(),orderDetl.getStandby1(),orderDetl.getStandby2(),orderDetl.getStandby3(),orderDetl.getBoxType1(),orderDetl.getBoxType2(),orderDetl.getBoxType3(),areaIds);
                for (LocDetl locDetl : locDetls) {
                    if (issued > 0) {
                        LocDto locDto = new LocDto(locDetl.getLocNo(), locDetl.getMatnr(), locDetl.getMaktx(), locDetl.getBatch(), orderDetl.getOrderNo(),
                                issued >= locDetl.getAnfme() ? locDetl.getAnfme() : issued);
                        LocMast locMast = locMastService.selectOne(new EntityWrapper<LocMast>().eq("loc_no", locDetl.getLocNo()));
//                        LocMast locMast = locMastService.selectOne(new EntityWrapper<LocMast>().eq("loc_no", locDetl.getLocNo()));
                        LocCache locCache = locCacheService.selectOne(new EntityWrapper<LocCache>().eq("loc_no", locDetl.getLocNo()));
                        locDto.setFrozen(locDetl.getFrozen());
                        locDto.setFrozenLoc(locMast.getFrozen());
                        List<Integer> staNos = staDescService.queryOutStaNosByLocNo(locDetl.getLocNo(), 107);
                        locDto.setFrozenLoc(locCache.getFrozen());
                        List<BasStation> basStations = basStationService.selectList(new EntityWrapper<BasStation>().in("area_id", areaIds));
                        List<String> collect = basStations.stream().map(BasStation::getDevNo).collect(Collectors.toList());
                        locDto.setAgvStaNos(collect);
                        locDto.setBrand(orderDetl.getBrand());
                        locDto.setStandby1(orderDetl.getStandby1());
                        locDto.setStandby2(orderDetl.getStandby2());
                        locDto.setStandby3(orderDetl.getStandby3());
                        locDto.setBoxType1(orderDetl.getBoxType1());
                        locDto.setBoxType2(orderDetl.getBoxType2());
                        locDto.setBoxType3(orderDetl.getBoxType3());
                        locDto.setStaNos(staNos);
                        locDtos.add(locDto);
                        exist.add(locDetl.getLocNo());
                        // 剩余待出数量递减
                        issued = issued - locDetl.getAnfme();
                    } else {
@@ -114,7 +132,6 @@
                if (issued > 0) {
                    LocDto locDto = new LocDto(null, orderDetl.getMatnr(), orderDetl.getMaktx(), orderDetl.getBatch(), orderDetl.getOrderNo(), issued);
                    locDto.setBrand(orderDetl.getBrand());
                    locDto.setStandby1(orderDetl.getStandby1());
                    locDto.setStandby2(orderDetl.getStandby2());
                    locDto.setStandby3(orderDetl.getStandby3());
@@ -126,13 +143,17 @@
                }
            }
        } else {
            List<BasAreas> basAreas = basAreasService.selectList(new EntityWrapper<BasAreas>().in("whs_type_id", LocAreaType.AUTOMATED.type));
            List<Long> areaIds = basAreas.stream()
                    .map(BasAreas::getId)
                    .collect(Collectors.toList());
            for (OrderDetlPakout orderDetl : orderDetlPakouts) {
                double issued = Optional.of(orderDetl.getAnfme() - orderDetl.getWorkQty()).orElse(0.0D);
                if (issued <= 0.0D) {
                    continue;
                }
                List<LocDetl> locDetls = locDetlService.queryStockAll(null, exist,orderDetl.getMatnr(), orderDetl.getBatch(),
                        orderDetl.getBrand(),orderDetl.getStandby1(),orderDetl.getStandby2(),orderDetl.getStandby3(),orderDetl.getBoxType1(),orderDetl.getBoxType2(),orderDetl.getBoxType3());
                        orderDetl.getBrand(),orderDetl.getStandby1(),orderDetl.getStandby2(),orderDetl.getStandby3(),orderDetl.getBoxType1(),orderDetl.getBoxType2(),orderDetl.getBoxType3(),areaIds);
                for (LocDetl locDetl : locDetls) {
                    if (issued > 0) {
                        LocDto locDto = new LocDto(locDetl.getLocNo(), locDetl.getMatnr(), locDetl.getMaktx(), locDetl.getBatch(), orderDetl.getOrderNo(),
@@ -282,4 +303,56 @@
        return R.ok();
    }
    @PostMapping("/out/agvPakOut/auth")
    @ManagerAuth(memo = "订单出库")
    public synchronized R agvPakOut(@RequestBody List<LocDto> locDtos) throws InterruptedException {
        if (Cools.isEmpty(locDtos)) {
            return R.parse(BaseRes.PARAM);
        }
        List<LocDto> locDtoArrayList = new ArrayList<>();
        for (LocDto locDto : locDtos){
            if (locDto.getFrozen()!=1 && locDto.getFrozenLoc()!=1){
                locDtoArrayList.add(locDto);
            }
        }
        locDtos = locDtoArrayList;
        if (Cools.isEmpty(locDtos)) {
            return R.parse("库存/库位被冻结,请处理后出库!!!");
        }
        boolean lack = true;
        for (LocDto locDto : locDtos) {
            if (!locDto.isLack()) {
                lack = false;
                break;
            }
        }
        if (lack) {
            return R.error("库存不足");
        }
        Thread.sleep(1000L);
        List<TaskDto> taskDtos = new ArrayList<>();
        // 根据 (库位 & 出库站) 分组; 理想状态:一组为一次出库任务
        for (LocDto locDto : locDtos) {
            if (locDto.isLack()) { continue; }
            TaskDto taskDto = new TaskDto(locDto.getLocNo(), locDto.getAgvStaNo(), locDto);
            if (TaskDto.has(taskDtos, taskDto)) {
                TaskDto dto = TaskDto.find(taskDtos, taskDto);
                assert dto != null;
                dto.getLocDtos().addAll(taskDto.getLocDtos());
            } else {
                taskDtos.add(taskDto);
            }
        }
        // -----------------------------------------------------------------------------------------------
        for (TaskDto taskDto : taskDtos) {
//            BasDevp staNo = basDevpService.checkSiteStatus(taskDto.getStaNo());
            BasStation station = basStationService.selectOne(new EntityWrapper<BasStation>().eq("dev_no", taskDto.getAgvStaNo()));
            workService.agvStockOut(station, taskDto, getUserId());
        }
        return R.ok();
    }
}
src/main/java/com/zy/asrs/controller/StationRelaController.java
New file
@@ -0,0 +1,123 @@
package com.zy.asrs.controller;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.core.common.DateUtils;
import com.zy.asrs.entity.StationRela;
import com.zy.asrs.service.StationRelaService;
import com.core.annotations.ManagerAuth;
import com.core.common.BaseRes;
import com.core.common.Cools;
import com.core.common.R;
import com.zy.common.web.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@RestController
public class StationRelaController extends BaseController {
    @Autowired
    private StationRelaService stationRelaService;
    @RequestMapping(value = "/stationRela/{id}/auth")
    @ManagerAuth
    public R get(@PathVariable("id") String id) {
        return R.ok(stationRelaService.selectById(String.valueOf(id)));
    }
    @RequestMapping(value = "/stationRela/list/auth")
    @ManagerAuth
    public R list(@RequestParam(defaultValue = "1")Integer curr,
                  @RequestParam(defaultValue = "10")Integer limit,
                  @RequestParam(required = false)String orderByField,
                  @RequestParam(required = false)String orderByType,
                  @RequestParam Map<String, Object> param){
        EntityWrapper<StationRela> wrapper = new EntityWrapper<>();
        excludeTrash(param);
        convert(param, wrapper);
        if (!Cools.isEmpty(orderByField)){wrapper.orderBy(humpToLine(orderByField), "asc".equals(orderByType));}
        return R.ok(stationRelaService.selectPage(new Page<>(curr, limit), wrapper));
    }
    private <T> void convert(Map<String, Object> map, EntityWrapper<T> wrapper){
        for (Map.Entry<String, Object> entry : map.entrySet()){
            String val = String.valueOf(entry.getValue());
            if (val.contains(RANGE_TIME_LINK)){
                String[] dates = val.split(RANGE_TIME_LINK);
                wrapper.ge(entry.getKey(), DateUtils.convert(dates[0]));
                wrapper.le(entry.getKey(), DateUtils.convert(dates[1]));
            } else {
                wrapper.like(entry.getKey(), val);
            }
        }
    }
    @RequestMapping(value = "/stationRela/add/auth")
    @ManagerAuth
    public R add(StationRela stationRela) {
        stationRelaService.insert(stationRela);
        return R.ok();
    }
    @RequestMapping(value = "/stationRela/update/auth")
    @ManagerAuth
    public R update(StationRela stationRela){
        if (Cools.isEmpty(stationRela) || null==stationRela.getId()){
            return R.error();
        }
        stationRelaService.updateById(stationRela);
        return R.ok();
    }
    @RequestMapping(value = "/stationRela/delete/auth")
    @ManagerAuth
    public R delete(@RequestParam(value="ids[]") Long[] ids){
         for (Long id : ids){
            stationRelaService.deleteById(id);
        }
        return R.ok();
    }
    @RequestMapping(value = "/stationRela/export/auth")
    @ManagerAuth
    public R export(@RequestBody JSONObject param){
        EntityWrapper<StationRela> wrapper = new EntityWrapper<>();
        List<String> fields = JSONObject.parseArray(param.getJSONArray("fields").toJSONString(), String.class);
        Map<String, Object> map = excludeTrash(param.getJSONObject("stationRela"));
        convert(map, wrapper);
        List<StationRela> list = stationRelaService.selectList(wrapper);
        return R.ok(exportSupport(list, fields));
    }
    @RequestMapping(value = "/stationRelaQuery/auth")
    @ManagerAuth
    public R query(String condition) {
        EntityWrapper<StationRela> wrapper = new EntityWrapper<>();
        wrapper.like("id", condition);
        Page<StationRela> page = stationRelaService.selectPage(new Page<>(0, 10), wrapper);
        List<Map<String, Object>> result = new ArrayList<>();
        for (StationRela stationRela : page.getRecords()){
            Map<String, Object> map = new HashMap<>();
            map.put("id", stationRela.getId());
            map.put("value", stationRela.getId());
            result.add(map);
        }
        return R.ok(result);
    }
    @RequestMapping(value = "/stationRela/check/column/auth")
    @ManagerAuth
    public R query(@RequestBody JSONObject param) {
        Wrapper<StationRela> wrapper = new EntityWrapper<StationRela>().eq(humpToLine(String.valueOf(param.get("key"))), param.get("val"));
        if (null != stationRelaService.selectOne(wrapper)){
            return R.parse(BaseRes.REPEAT).add(getComment(StationRela.class, String.valueOf(param.get("key"))));
        }
        return R.ok();
    }
}
src/main/java/com/zy/asrs/entity/OrderDetlPakout.java
@@ -90,7 +90,7 @@
     * 批号
     */
    @ApiModelProperty(value= "批号")
    private String batch;
    private String batch = "1";
    /**
     * 规格
@@ -396,6 +396,12 @@
        this.memo = memo;
    }
    //小松项目无批次,默认1方便后续修改
    public void setBatch(String batch) {
        this.batch = batch == null ? "1" : batch;
    }
    public String getOrderId$(){
        OrderService service = SpringUtils.getBean(OrderService.class);
        Order order = service.selectById(this.orderId);
src/main/java/com/zy/asrs/entity/OrderPakout.java
@@ -6,6 +6,8 @@
import com.baomidou.mybatisplus.enums.IdType;
import com.core.common.Cools;
import com.core.common.SpringUtils;
import com.zy.asrs.entity.BasAreas;
import com.zy.asrs.service.BasAreasService;
import com.zy.asrs.service.DocTypeService;
import com.zy.asrs.service.OrderSettleService;
import com.zy.common.utils.Synchro;
@@ -66,7 +68,7 @@
    @TableField("item_id")
    private Long itemId;
    @ApiModelProperty(value= "")
    @ApiModelProperty(value= "区域ID")
    @TableField("item_name")
    private String itemName;
@@ -352,6 +354,23 @@
//            null    // 备注
//    );
    public String getItemName$() {
        if (Cools.isEmpty(this.itemName)) {
            return null;
        }
        try {
            BasAreasService service = SpringUtils.getBean(BasAreasService.class);
            BasAreas area = service.selectById(Long.parseLong(this.itemName));
            if (!Cools.isEmpty(area)) {
                return area.getName();
            }
        } catch (Exception e) {
            return this.itemName;
        }
        return null;
    }
    public String getDocType$(){
        DocTypeService service = SpringUtils.getBean(DocTypeService.class);
        DocType docType = service.selectById(this.docType);
src/main/java/com/zy/asrs/entity/StationRela.java
New file
@@ -0,0 +1,60 @@
package com.zy.asrs.entity;
import com.core.common.Cools;import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.enums.IdType;
import com.baomidou.mybatisplus.annotations.TableField;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.baomidou.mybatisplus.annotations.TableName;
import java.io.Serializable;
@Data
@TableName("agv_station_rela")
public class StationRela implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value= "")
    @TableId(value = "id", type = IdType.INPUT)
    private Long id;
    /**
     * AGV站点
     */
    @ApiModelProperty(value= "AGV站点")
    @TableField("agv_sta")
    private String agvSta;
    /**
     * 堆垛机站点
     */
    @ApiModelProperty(value= "堆垛机站点")
    @TableField("crn_sta")
    private String crnSta;
    /**
     * 站点状态
     */
    @ApiModelProperty(value= "站点状态")
    @TableField("use_status")
    private String useStatus;
    public StationRela() {}
    public StationRela(Long id,String agvSta,String crnSta,String useStatus) {
        this.id = id;
        this.agvSta = agvSta;
        this.crnSta = crnSta;
        this.useStatus = useStatus;
    }
//    StationRela stationRela = new StationRela(
//            null,    // [非空]
//            null,    // AGV站点
//            null,    // 堆垛机站点
//            null    // 站点状态
//    );
}
src/main/java/com/zy/asrs/entity/TaskDetl.java
@@ -1,6 +1,7 @@
package com.zy.asrs.entity;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.enums.IdType;
import com.core.common.Cools;import com.baomidou.mybatisplus.annotations.TableField;
import java.text.SimpleDateFormat;
import java.util.Date;
src/main/java/com/zy/asrs/entity/TaskDetlLog.java
@@ -1,5 +1,7 @@
package com.zy.asrs.entity;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.enums.IdType;
import com.core.common.Cools;import com.baomidou.mybatisplus.annotations.TableField;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -26,6 +28,7 @@
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @TableField("log_id")
src/main/java/com/zy/asrs/entity/WaitPakin.java
@@ -1,7 +1,9 @@
package com.zy.asrs.entity;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.IdType;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.core.common.Cools;
import com.core.common.SpringUtils;
@@ -21,6 +23,9 @@
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @ApiModelProperty(value= "托盘条码")
    private String zpallet;
src/main/java/com/zy/asrs/entity/WaitPakinLog.java
@@ -20,6 +20,8 @@
    private static final long serialVersionUID = 1L;
    private Long id;
    @ApiModelProperty(value= "托盘条码")
    private String zpallet;
src/main/java/com/zy/asrs/entity/param/OrderDomainParam.java
@@ -17,6 +17,8 @@
    private String orderNo;
    private String itemName;
    private List<OrderDetl> orderDetlList;
    private List<OrderDetlPakin> orderDetlPakinList;
src/main/java/com/zy/asrs/enums/CommonStation.java
New file
@@ -0,0 +1,24 @@
package com.zy.asrs.enums;
/**
 * 通用站点
 * @author Ryan
 * @date 2025/12/6 13:56
 * @return null
 */
public enum CommonStation {
    //通用类型
    COMMON_STATION_Y("Y", "是"),
    //通用
    COMMON_STATION_N("N", "否");
    public String type;
    public String desc;
    CommonStation(String type, String desc) {
        this.type = type;
        this.desc = desc;
    }
}
src/main/java/com/zy/asrs/enums/OrderTypeEnum.java
@@ -1,25 +1,16 @@
package com.zy.asrs.enums;
public enum OrderTypeEnum {
    //上架派工单
    PICKING(1, "上架派工单"),
    //备货调拨单
    TRANSFER(2, "备货调拨单"),
    INVENTORY(3, "盘点单"),
    ADJUSTMENT(4, "库存调整单"),
    //备货单
    STOCK(5, "备货单");
    MATERIAL(1, "物料档案"),
    OUT_NOTICE(2, "发货通知单"),
    OTHER_IN(3, "其他入库单"),
    IN_NOTICE(4, "收料通知单"),
    PRODUCTION(5, "生产汇报单"),
    RETURN(6, "退货通知单"),
    OUT_APPLY(7, "出库申请单"),
    INVENTORY(8, "盘点单"),
    TRANSFER_IN(9, "调拨入库单"),
    ADJUSTMENT(10, "库存调整单"),
    SALE_OUT(27, "发货通知单-销售出库单"),
    PURCHASE_IN(28, "收料通知单-采购入库单"),
    PRODUCTION_IN(29, "生产汇报单-生产入库单"),
    PRODUCTION_OTHER_IN(30, "生产汇报单-其他入库单"),
    SALE_RETURN(31, "退货通知单-销售退货单"),
    OTHER_OUT(32, "出库申请单-其他出库单"),
    PRODUCT_IN(33, "成品入库单"),
    PRODUCT_OUT(34, "成品出库单");
    public Integer type;
    public String desc;
src/main/java/com/zy/asrs/enums/TaskIOType.java
New file
@@ -0,0 +1,51 @@
package com.zy.asrs.enums;
/**
 * 任务出入库类型
 * @author Ryan
 * @date 2025/12/6 14:55
 * @param null
 * @return null
 */
public enum TaskIOType {
    //料箱
    ALL_IN("1", "1.入库"),
    //托盘
    STATION_STATION("3", "3.站到站"),
    DEVICE_OUT("6", "6.设备上退出"),
    PICKING_MEGER("8", "8.拣料途中并板"),
    EMPTY_IN("10", "10.空板入库"),
    MOVE("11", "11.库格移载"),
    PICK_IN("53", "53.拣料入库"),
    MERGE_IN("54", "53.并板入库"),
    CHECK_IN("57", "57.盘点入库"),
    ALL_OUT("101", "101.出库"),
    PICK_OUT("103", "103.拣料出库"),
    MERGE_OUT("104", "104.并板出库"),
    CHECK_OUT("107", "107.盘点出库"),
    EMPTY_OUT("110", "110.空板出库"),
    ;
    public Integer type;
    public String desc;
    TaskIOType(String type, String desc) {
        this.type = Integer.valueOf(type);
        this.desc = desc;
    }
}
src/main/java/com/zy/asrs/mapper/LocDetlMapper.java
@@ -73,7 +73,10 @@
    List<LocDetl> queryStock(@Param("matnr")String matnr, @Param("batch")String batch, @Param("orderNo")String orderNo, @Param("locNos") Set<String> locNos);
    List<LocDetl> queryStockAll(@Param("orderNo")String orderNo, @Param("locNos") Set<String> locNos, @Param("matnr")String matnr, @Param("batch")String batch,
                                @Param("brand")String brand,@Param("standby1")String standby1,@Param("standby2")String standby2,
                                @Param("standby3")String standby3,@Param("boxType1")String boxType1,@Param("boxType2")String boxType2,@Param("boxType3")String boxType3);
                                @Param("standby3")String standby3,@Param("boxType1")String boxType1,@Param("boxType2")String boxType2,@Param("boxType3")String boxType3,@Param("areaIds")List<Long> areaIds);
    List<LocDetl> queryStockAllCache(@Param("orderNo")String orderNo, @Param("locNos") Set<String> locNos, @Param("matnr")String matnr, @Param("batch")String batch,
                                @Param("brand")String brand,@Param("standby1")String standby1,@Param("standby2")String standby2,
                                @Param("standby3")String standby3,@Param("boxType1")String boxType1,@Param("boxType2")String boxType2,@Param("boxType3")String boxType3,@Param("areaIds")List<Long> areaIds);
    Double queryStockAnfme(String matnr, String batch);
src/main/java/com/zy/asrs/mapper/StationRelaMapper.java
New file
@@ -0,0 +1,12 @@
package com.zy.asrs.mapper;
import com.zy.asrs.entity.StationRela;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface StationRelaMapper extends BaseMapper<StationRela> {
}
src/main/java/com/zy/asrs/service/LocDetlService.java
@@ -67,7 +67,10 @@
    List<LocDetl> queryStock(String matnr, String batch, String orderNo, Set<String> locNos);
    List<LocDetl> queryStockAll(String orderNo, Set<String> locNos,String matnr, String batch, String brand, String standby1, String standby2, String standby3, String boxType1, String boxType2, String boxType3);
    List<LocDetl> queryStockAll(String orderNo, Set<String> locNos,String matnr, String batch, String brand, String standby1, String standby2, String standby3, String boxType1, String boxType2, String boxType3 ,List<Long> areaIds);
    List<LocDetl> queryStockAllCache(String orderNo, Set<String> locNos,String matnr, String batch, String brand, String standby1, String standby2, String standby3, String boxType1, String boxType2, String boxType3 ,List<Long> areaIds);
    Double queryStockAnfme(String matnr, String batch);
src/main/java/com/zy/asrs/service/MobileService.java
@@ -2,14 +2,10 @@
import com.alibaba.fastjson.JSONObject;
import com.core.common.R;
import com.zy.asrs.entity.BasDevp;
import com.zy.asrs.entity.LocCache;
import com.zy.asrs.entity.LocDetl;
import com.zy.asrs.entity.OrderDetl;
import com.zy.asrs.entity.*;
import com.zy.asrs.entity.param.*;
import java.util.Date;
import java.util.Map;
public interface MobileService {
@@ -108,7 +104,9 @@
     * @date 2025/12/3 8:07
     * @param locCaches
     */
    void generateCRNOutTask(LocCache locCaches);
    void generateCRNOutTask(BasStation station, LocCache locCaches, Long userId);
    R callEmptyCar(AgvCallParams params);
    R callEmptyCar(AgvCallParams params, Long userId);
    R collectionInCall(AgvCallParams params, Long userId);
}
src/main/java/com/zy/asrs/service/StationRelaService.java
New file
@@ -0,0 +1,8 @@
package com.zy.asrs.service;
import com.zy.asrs.entity.StationRela;
import com.baomidou.mybatisplus.service.IService;
public interface StationRelaService extends IService<StationRela> {
}
src/main/java/com/zy/asrs/service/WorkService.java
@@ -1,6 +1,7 @@
package com.zy.asrs.service;
import com.zy.asrs.entity.BasDevp;
import com.zy.asrs.entity.BasStation;
import com.zy.asrs.entity.WaitPakin;
import com.zy.asrs.entity.WrkMast;
import com.zy.asrs.entity.param.EmptyPlateOutParam;
@@ -44,6 +45,8 @@
    void checkStockOut(BasDevp staNo, TaskDto taskDto, Long userId);
    void agvStockOut(BasStation staNo, TaskDto taskDto, Long userId);
    /**
     * 空板入库
     * @return 库位号
src/main/java/com/zy/asrs/service/impl/LocDetlServiceImpl.java
@@ -131,8 +131,13 @@
    @Override
    public List<LocDetl> queryStockAll(String orderNo, Set<String> locNos,String matnr, String batch, String brand, String standby1, String standby2, String standby3, String boxType1, String boxType2, String boxType3) {
        return this.baseMapper.queryStockAll(orderNo,locNos, matnr, batch,brand,standby1,standby2,standby3,boxType1,boxType2,boxType3);
    public List<LocDetl> queryStockAll(String orderNo, Set<String> locNos,String matnr, String batch, String brand, String standby1, String standby2, String standby3, String boxType1, String boxType2, String boxType3,List<Long> areaIds) {
        return this.baseMapper.queryStockAll(orderNo,locNos, matnr, batch,brand,standby1,standby2,standby3,boxType1,boxType2,boxType3, areaIds);
    }
    @Override
    public List<LocDetl> queryStockAllCache(String orderNo, Set<String> locNos,String matnr, String batch, String brand, String standby1, String standby2, String standby3, String boxType1, String boxType2, String boxType3,List<Long> areaIds) {
        return this.baseMapper.queryStockAllCache(orderNo,locNos, matnr, batch,brand,standby1,standby2,standby3,boxType1,boxType2,boxType3, areaIds);
    }
    @Override
src/main/java/com/zy/asrs/service/impl/MobileServiceImpl.java
@@ -11,9 +11,10 @@
import com.zy.asrs.entity.result.ForwardAGVTaskDTO;
import com.zy.asrs.entity.result.HIKApiDTO;
import com.zy.asrs.entity.result.HIKResultDTO;
import com.zy.asrs.enums.*;
import com.zy.asrs.enums.OrderSettle;
import com.zy.asrs.enums.LocAreaType;
import com.zy.asrs.enums.LocStsType;
import com.zy.asrs.enums.OrderSettle;
import com.zy.asrs.mapper.LocMastMapper;
import com.zy.asrs.mapper.ManLocDetlMapper;
import com.zy.asrs.service.*;
@@ -23,7 +24,9 @@
import com.zy.common.constant.MesConstant;
import com.zy.common.entity.Parameter;
import com.zy.common.model.DetlDto;
import com.zy.common.model.LocDetlDto;
import com.zy.common.model.MesCombParam;
import com.zy.common.model.enums.IoWorkType;
import com.zy.common.model.enums.WorkNoType;
import com.zy.common.service.CommonService;
import com.zy.common.utils.HttpHandler;
@@ -121,6 +124,8 @@
    private BasContainerService basContainerService;
    @Autowired
    private BasAreasService basAreasService;
    @Autowired
    private StationRelaService stationRelaService;
    @Override
    @Transactional
@@ -623,6 +628,12 @@
                waitPakin.setIoStatus("N");
                waitPakin.setAnfme(detlDto.getAnfme());
                waitPakin.setStatus("Y");
                waitPakin.setStandby1(detlDto.getStandby1());
                waitPakin.setStandby2(detlDto.getStandby2());
                waitPakin.setStandby3(detlDto.getStandby3());
                waitPakin.setBoxType1(detlDto.getBoxType1());
                waitPakin.setBoxType2(detlDto.getBoxType2());
                waitPakin.setBoxType3(detlDto.getBoxType3());
                waitPakin.setAppeUser(userId);
                waitPakin.setAppeTime(now);
                waitPakin.setModiUser(userId);
@@ -653,7 +664,7 @@
                }
                if (elem.getAnfme() > detls.getEnableQty()) {
                    throw new CoolException(detls.getMatnr() + "入库数量不合法");
                    throw new CoolException(detls.getMatnr() + "入库数量不合规则");
                }
                OrderInAndOutUtil.increaseWorkQty(Boolean.TRUE, order.getId(), elem.getMatnr(), elem.getBatch(), elem.getBrand(), elem.getStandby1(), elem.getStandby2(), elem.getStandby3(),
                        elem.getBoxType1(), elem.getBoxType2(), elem.getBoxType3(), elem.getAnfme());
@@ -1276,32 +1287,75 @@
        /**生成缓存区出库任务*/
        generateCacheOutTask(station, locCaches, userId);
        /**生成立库出库任务*/
        generateCRNOutTask(locCaches);
//        /**生成立库出库任务*/
        generateCRNOutTask(station, locCaches, userId);
        return R.ok();
    }
    @Override
    public R callEmptyCar(AgvCallParams params) {
        List<LocCache> locSts = locCacheService.selectList(new EntityWrapper<LocCache>().eq("loc_sts", LocStsType.LOC_STS_TYPE_D.type));
        if (locSts.isEmpty()){
    public R callEmptyCar(AgvCallParams params, Long userId) {
        BasAreas basAreas = basAreasService.selectOne(new EntityWrapper<BasAreas>().eq("name", params.getOrgSite()));
        List<LocCache> locCaches = locCacheService.selectList(new EntityWrapper<LocCache>()
                .eq("loc_sts", LocStsType.LOC_STS_TYPE_D.type)
                .eq("area_id", basAreas.getId())
        );
        if (locCaches.isEmpty()){
            throw new CoolException("暂无空板库位");
        }
        HIKApiDTO hikApiDTO =new HIKApiDTO()
                .setOrg(locSts.get(0).getLocNo())
                .setOrgType("05")
                .setTar(params.getTarSite())
                .setTarType("05")
                .setTaskType("GT5")
                .setPriority("1")
                .setCtnrType("2")
                ;
        HIKResultDTO hikResultDTO = sendAgvTask(hikApiDTO, HIKApiConstant.AGV_CALL_IN_PATH);
        if (!hikResultDTO.isSuccess()){
            return R.error(hikResultDTO.getMessage());
        BasStation station = basStationService.selectOne(new EntityWrapper<BasStation>()
                .eq("loc_sts", LocStsType.LOC_STS_TYPE_O.type)
                .eq("dev_no", params.getTarSite()));
        if (Objects.isNull(station)) {
            throw new CoolException("站点正在执行任务!!");
        }
        generateCacheOutTask(station, locCaches.get(0), userId);
//        HIKApiDTO hikApiDTO =new HIKApiDTO()
//                .setOrg(locCaches.get(0).getLocNo())
//                .setOrgType("05")
//                .setTar(params.getTarSite())
//                .setTarType("05")
//                .setTaskType("GT5")
//                .setPriority("1")
//                .setCtnrType("2")
//                ;
//        HIKResultDTO hikResultDTO = sendAgvTask(hikApiDTO, HIKApiConstant.AGV_CALL_IN_PATH);
//        if (!hikResultDTO.isSuccess()){
//            return R.error(hikResultDTO.getMessage());
//        }
        return R.ok();
    }
    @Override
//    @Transactional(rollbackFor = Exception.class)
    public R collectionInCall(AgvCallParams params, Long userId) {
        if (Objects.isNull(params.getOrgSite())) {
            throw new CoolException("源站点不能为空!!");
        }
        List<Task> tasks = taskService.selectList(new EntityWrapper<Task>().eq("barcode", params.getBarcode()));
        if (!tasks.isEmpty()){
            throw new CoolException("托盘码已生成任务");
        }
        BasAreas basAreas = basAreasService.selectOne(new EntityWrapper<BasAreas>().eq("name", params.getTarSite()));
        List<LocCache> locCaches = locCacheService.selectList(new EntityWrapper<LocCache>()
                .in("loc_sts", LocStsType.LOC_STS_TYPE_O.type, LocStsType.LOC_STS_TYPE_D.type)
                .eq("area_id", basAreas.getId())
        );
        if (locCaches.isEmpty()) {
            throw new CoolException("当前暂无空库位!!");
        }
        generateAgvTask("agv", locCaches.get(0), params.getOrgSite(), params.getBarcode(), userId);
        return R.ok();
    }
@@ -1314,31 +1368,165 @@
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void generateCRNOutTask(LocCache locCaches) {
    public void generateCRNOutTask(BasStation station, LocCache locCaches, Long userId) {
        if (Objects.isNull(locCaches)) {
            throw new CoolException("库位不能为空!!");
        }
        //获取缓存区信息
        BasAreas basAreas = basAreasService.selectOne(new EntityWrapper<BasAreas>().eq("whs_type_id", LocAreaType.LOC_AREA_TYPE_IN_CACHE.type));
        if (Objects.isNull(basAreas)) {
            throw new CoolException("库区不存在!!");
        }
        List<LocDetl> locDetls = locDetlService.selectList(new EntityWrapper<LocDetl>()
                .eq("area_id", basAreas.getAreaNo())
                .eq("area_id", basAreas.getId())
                .eq("loc_id", locCaches.getId()));
        if (Objects.isNull(locDetls)) {
            throw new CoolException("库存明细不存在!!");
        }
        //获取立库区信息
        BasAreas one = basAreasService.selectOne(new EntityWrapper<BasAreas>().eq("whs_type_id", LocAreaType.LOC_AREA_TYPE_CRN.type));
        if (Objects.isNull(one)) {
            throw new CoolException("数据错误:库区不存在!!");
        }
        //按物料编码分类
        List<StationRela> relas = stationRelaService.selectList(new EntityWrapper<StationRela>().eq("agv_sta", station.getDevNo()));
        if (Objects.isNull(relas) || relas.isEmpty()) {
            throw new CoolException("站点未关联堆垛机作业站点!!");
        }
        Set<String> crnStas = relas.stream().map(StationRela::getCrnSta).collect(Collectors.toSet());
        List<BasDevp> devps = basDevpService.selectList(new EntityWrapper<BasDevp>().in("dev_no", crnStas).eq("loading", CommonStation.COMMON_STATION_Y.type));
        if (Objects.isNull(devps) || devps.isEmpty()) {
            throw new CoolException("无站点可用!");
        }
        Collections.shuffle(devps);
        BasDevp basDevp = devps.stream().findFirst().get();
        List<LocMast> locMasts = new ArrayList<>();
        Map<String, List<LocDetl>> listMap = locDetls.stream().collect(Collectors.groupingBy(LocDetl::getMatnr));
        listMap.forEach((key, detls) -> {
        listMap.forEach((matnr, detls) -> {
            //根据supId(供应商)分类,得到出库总数
            Map<String, List<LocDetl>> supIds = detls.stream().collect(Collectors.groupingBy(LocDetl::getStandby1));
            supIds.forEach((supId, sups) -> {
                Double sum = sups.stream().mapToDouble(LocDetl::getAnfme).sum();
                //获取当前供应商+ 物料在库
                List<LocDetl> detlList = locDetlService.selectList(new EntityWrapper<LocDetl>()
                        .eq("matnr", matnr)
                        .eq("area_id", one.getId())
                        .eq("standby1", supId).orderAsc(Arrays.asList("appe_time")));
                //TODO  判断是否有新库位,没有新库位,再找有空格的位置放   1. 判断当前物料是否有库存 2. 没有余料查询新库位
                if (!Objects.isNull(detlList) && !detlList.isEmpty()) {
                    Map<String, List<LocDetl>> locMaps = detlList.stream().collect(Collectors.groupingBy(LocDetl::getLocNo));
                    locMaps.forEach((locNo, adetls) -> {
                        LocMast locMast = locMastService.selectById(locNo);
                        if (Objects.isNull(locMast)) {
                            throw new CoolException("数据错误,库位信息不存在!!");
                        }
                        BasContainer container = basContainerService.selectOne(new EntityWrapper<BasContainer>().eq("barcode", locMast.getBarcode()));
                        if (Objects.isNull(container)) {
                            throw new CoolException("数据错误,容器不存在!!");
                        }
                        Set<String> sets = adetls.stream().map(LocDetl::getMatnr).collect(Collectors.toSet());
                        //判断容器是否还可混放,及当前物料可放多少
                        if (container.getMixMax() > sets.size()) {
                            int suplus = container.getMixMax() - sets.size();
                            Mat mats = matService.selectOne(new EntityWrapper<Mat>().eq("matnr", matnr));
                            if (Objects.isNull(mats)) {
                                throw new CoolException("物料不存在!!");
                            }
                            Double v = mats.getUpQty() * suplus;
                            //小于零
                            if (sum.compareTo(v) <= 0) {
                                //可放下
                                locMasts.add(locMast);
                            }
                        }
                    });
                }
            });
        });
        locDetls.forEach(locDetl -> {
        if (!locMasts.isEmpty()) {
            //生成堆垛机出库任务
            generateOutTask(locMasts, TaskIOType.MERGE_OUT.type, basDevp, userId);
        }
    }
        });
    /**
     * 生成堆垛机出库任务
     * @author Ryan
     * @date 2025/12/6 14:44
     * @param locMasts
     */
    @Transactional(rollbackFor = Exception.class)
    public void generateOutTask(List<LocMast> locMasts, Integer ioType, BasDevp devp, Long userId) {
        Date now = new Date();
        for (LocMast locMast : locMasts) {
            if (Objects.isNull(ioType)) {
                continue;
            }
            Integer outSta = devp.getDevNo();
            // 获取路径
            StaDesc staDesc = staDescService.queryCrnStn(ioType, locMast.getCrnNo(), outSta);
            // 生成工作号
            int workNo = commonService.getWorkNo(WorkNoType.getWorkNoType(ioType));
            // 生成工作档
            WrkMast wrkMast = new WrkMast();
            wrkMast.setWrkNo(workNo);
            wrkMast.setIoTime(now);
            wrkMast.setWrkSts(11L); // 工作状态:11.生成出库ID
            wrkMast.setIoType(ioType); // 入出库类型
            wrkMast.setIoPri(13D); // 优先级:13
            wrkMast.setCrnNo(locMast.getCrnNo());
            wrkMast.setSourceStaNo(staDesc.getCrnStn() + ""); // 源站
            wrkMast.setStaNo(staDesc.getStnNo() + ""); // 目标站
            wrkMast.setSourceLocNo(locMast.getLocNo()); // 源库位
            wrkMast.setFullPlt("Y"); // 满板:Y
            wrkMast.setPicking("N"); // 拣料
            wrkMast.setExitMk("N"); // 退出
            wrkMast.setEmptyMk("N"); // 空板
            wrkMast.setLinkMis("N");
            wrkMast.setBarcode(locMast.getBarcode());
            wrkMast.setAppeUser(userId); // 操作人员数据
            wrkMast.setAppeTime(now);
            wrkMast.setModiUser(userId);
            wrkMast.setModiTime(now);
            if (!wrkMastService.insert(wrkMast)) {
                throw new CoolException("保存工作档失败,出库库位号:" + locMast.getLocNo());
            }
            List<LocDetl> locDetls = locDetlService.selectList(new EntityWrapper<LocDetl>().eq("loc_no", locMast.getLocNo()));
            // 生成工作档明细
            for (LocDetl detlDto : locDetls) {
                WrkDetl wrkDetl = new WrkDetl();
                BeanUtils.copyProperties(detlDto, wrkDetl);
                wrkDetl.setOrderNo(""); // 手动出库不需要带出库存中的单据编号
                wrkDetl.setWrkNo(workNo);
                wrkDetl.setIoTime(now);
                wrkDetl.setAppeTime(now);
                wrkDetl.setAppeUser(userId);
                wrkDetl.setModiTime(now);
                wrkDetl.setModiUser(userId);
                if (!wrkDetlService.insert(wrkDetl)) {
                    throw new CoolException("保存工作档明细失败");
                }
            }
            // 修改库位状态:   F.在库 ====>>> R.出库预约/P.拣料/盘点/并板出库中
            locMast = locMastService.selectById(locMast.getLocNo());
            if (locMast.getLocSts().equals(LocStsType.LOC_STS_TYPE_F.type)) {
                locMast.setLocSts(ioType == 101 ? "R" : "P");
                locMast.setModiUser(userId);
                locMast.setModiTime(now);
                if (!locMastService.updateById(locMast)) {
                    throw new CoolException("预约库位状态失败,库位号:" + locMast.getLocNo());
                }
            } else {
                throw new CoolException(locMast.getLocNo() + "库位不是在库状态");
            }
        }
    }
    /**
@@ -1356,10 +1544,10 @@
        task.setWrkNo(workNo)
                .setIoTime(new Date())
                .setWrkSts(11L) // 工作状态:11.生成出库ID
                .setIoType(101) // 入出库状态: 11.库格移载
                .setIoType(loc.getLocSts().equals("D") ? 110 : 101) // 入出库状态: 11.库格移载
                .setTaskType("agv")
                .setIoPri(10D)
                .setFullPlt("Y") // 满板:Y
                .setFullPlt(loc.getLocSts().equals("D") ? "N" : "Y") // 满板:Y
                .setPicking("N") // 拣料
                .setExitMk("N")// 退出
                .setStaNo(station.getDevNo())
@@ -1375,34 +1563,30 @@
            throw new CoolException("保存工作档失败");
        }
        List<LocDetl> detls = locDetlService.selectList(new EntityWrapper<LocDetl>().eq("loc_id", loc.getId()));
        if (Objects.isNull(detls) || detls.isEmpty()) {
            throw new CoolException("数据错误:库位明细为空!!");
        }
        List<TaskDetl> taskDetls = new ArrayList<>();
        detls.forEach(pakin -> {
            TaskDetl wrkDetl = new TaskDetl();
            BeanUtils.copyProperties(pakin, wrkDetl);
            wrkDetl.setWrkNo(workNo)
                    .setIoTime(new Date())
                    .setOrderNo(pakin.getOrderNo())
                    .setAnfme(pakin.getAnfme())
                    .setZpallet(pakin.getZpallet())
                    .setBatch(pakin.getBatch())
                    .setMatnr(pakin.getMatnr())
                    .setMaktx(pakin.getMaktx())
                    .setAppeUser(userId)
                    .setUnit(pakin.getUnit())
                    .setModel(pakin.getModel())
                    .setAppeTime(new Date())
                    .setModiUser(userId);
            taskDetls.add(wrkDetl);
        });
        //保存工作档明细
        if (!taskDetlService.insertBatch(taskDetls)) {
            throw new CoolException("保存工作档明细失败");
        if (!detls.isEmpty()) {
            List<TaskDetl> taskDetls = new ArrayList<>();
            detls.forEach(pakin -> {
                TaskDetl wrkDetl = new TaskDetl();
                BeanUtils.copyProperties(pakin, wrkDetl);
                wrkDetl.setWrkNo(workNo)
                        .setIoTime(new Date())
                        .setOrderNo(pakin.getOrderNo())
                        .setAnfme(pakin.getAnfme())
                        .setZpallet(pakin.getZpallet())
                        .setBatch(pakin.getBatch())
                        .setMatnr(pakin.getMatnr())
                        .setMaktx(pakin.getMaktx())
                        .setAppeUser(userId)
                        .setUnit(pakin.getUnit())
                        .setModel(pakin.getModel())
                        .setAppeTime(new Date())
                        .setModiUser(userId);
                taskDetls.add(wrkDetl);
            });
            //保存工作档明细
            if (!taskDetlService.insertBatch(taskDetls)) {
                throw new CoolException("保存工作档明细失败");
            }
        }
        loc.setLocSts(LocStsType.LOC_STS_TYPE_R.type);
@@ -1488,6 +1672,13 @@
        if (!taskDetlService.insertBatch(taskDetls)) {
            throw new CoolException("保存工作档明细失败");
        }
        pakins.forEach(pakin -> {pakin.setIoStatus("Y");});
        if (!waitPakinService.updateBatchById(pakins)) {
            throw new CoolException("更新组托信息失败");
        }
        // 修改目标库位状态
        if (loc.getLocSts().equals(LocStsType.LOC_STS_TYPE_O.type)) {
            loc.setLocSts(LocStsType.LOC_STS_TYPE_S.type); // S.入库预约
@@ -1499,6 +1690,20 @@
        } else {
            throw new CoolException("移转失败,目标库位状态:" + loc.getLocSts$());
        }
        // 修改目标站点信息
        BasStation station = basStationService.selectOne(new EntityWrapper<BasStation>().eq("dev_no", orgSite));
        if (station.getLocSts().equals("O")) {
            station.setLocSts("R"); // S.入库预约
            station.setBarcode(barcode);
            station.setModiTime(new Date());
            station.setModiUser(userId);
            if (!basStationService.updateById(station)) {
                throw new CoolException("更新目标库位状态失败");
            }
        } else {
            throw new CoolException("移转失败,目标库位状态:" + station.getLocSts());
        }
    }
src/main/java/com/zy/asrs/service/impl/StationRelaServiceImpl.java
New file
@@ -0,0 +1,12 @@
package com.zy.asrs.service.impl;
import com.zy.asrs.mapper.StationRelaMapper;
import com.zy.asrs.entity.StationRela;
import com.zy.asrs.service.StationRelaService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service("stationRelaService")
public class StationRelaServiceImpl extends ServiceImpl<StationRelaMapper, StationRela> implements StationRelaService {
}
src/main/java/com/zy/asrs/service/impl/TaskDetlLogServiceImpl.java
@@ -42,6 +42,6 @@
                throw new CoolException("工作档明细历史保存失败!!");
            }
        });
        return false;
        return true;
    }
}
src/main/java/com/zy/asrs/service/impl/TaskLogServiceImpl.java
@@ -44,6 +44,6 @@
            throw new CoolException("任务日志保存失败!!");
        }
        return false;
        return true;
    }
}
src/main/java/com/zy/asrs/service/impl/TaskServiceImpl.java
@@ -34,6 +34,8 @@
    private TaskService taskService;
    @Autowired
    private TaskDetlLogService taskDetlLogService;
    @Autowired
    private BasStationService basStationService;
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -154,6 +156,18 @@
                locMast.setModiUser(userId);
                locCacheService.updateById(locMast);
            }
            BasStation station = basStationService.selectOne(new EntityWrapper<BasStation>().eq("dev_no", wrkMast.getSourceStaNo()));
            if (station.getLocSts().equals("R")) {
                station.setLocSts(wrkMast.getIoType().equals(1)?"F":"D");
                station.setModiTime(new Date());
                station.setModiUser(userId);
                if (!basStationService.updateById(station)) {
                    throw new CoolException("更新源站点状态失败");
                }
            } else {
                throw new CoolException("更新源站点状态失败,目标库位状态:" + station.getLocSts());
            }
            // 出库取消(修改源库位)
        } else if (wrkMast.getWrkSts() > 10 && wrkMast.getWrkSts() != 14) {
            locNo = wrkMast.getSourceLocNo();
@@ -176,6 +190,18 @@
                locMast.setModiUser(userId);
                locCacheService.updateById(locMast);
            }
            BasStation station = basStationService.selectOne(new EntityWrapper<BasStation>().eq("dev_no", wrkMast.getStaNo()));
            if (station.getLocSts().equals("S")) {
                station.setLocSts("O");
                station.setModiTime(new Date());
                station.setModiUser(userId);
                if (!basStationService.updateById(station)) {
                    throw new CoolException("更新源站点状态失败");
                }
            } else {
                throw new CoolException("更新源站点状态失败,目标库位状态:" + station.getLocSts());
            }
        } else {
            throw new CoolException("当前工作状态无法取消");
        }
src/main/java/com/zy/asrs/service/impl/WorkServiceImpl.java
@@ -25,6 +25,7 @@
import com.zy.common.service.CommonService;
import com.zy.common.web.WcsController;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -91,6 +92,12 @@
    @Autowired
    private CheckOrderDetlService checkOrderDetlService;
    @Autowired
    private LocCacheServiceImpl locCacheService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private TaskDetlService taskDetlService;
    @Override
    @Transactional
@@ -578,6 +585,98 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void agvStockOut(BasStation staNo, TaskDto taskDto, Long userId) {
        Date now = new Date();
        List<LocDto> locDtos = taskDto.getLocDtos();
        for (LocDto locDto : locDtos) {
            if (!taskDto.getLocNo().equals(locDto.getLocNo()) && !taskDto.getStaNo().equals(locDto.getStaNo())) {
                throw new CoolException("订单出库异常,请联系管理员");
            }
        }
        // 获取库位
        LocCache locCache = locCacheService.selectOne(new EntityWrapper<LocCache>().eq("loc_no", taskDto.getLocNo()));
        int ioType = (taskDto.isAll() ? 101 : 103);
        // 生成工作号
        int workNo = commonService.getWorkNo(WorkNoType.getWorkNoType(ioType));
        Task task = new Task();
        task.setWrkNo(workNo)
                .setIoTime(new Date())
                .setWrkSts(11L) // 工作状态:11.生成出库ID
                .setIoType(ioType) // 入出库状态
                .setTaskType("agv")
                .setIoPri(10D)
                .setFullPlt("Y") // 满板:Y
                .setPicking("N") // 拣料
                .setExitMk("N")// 退出
                .setStaNo(staNo.getDevNo())
                .setSourceLocNo(locCache.getLocNo())
                .setEmptyMk("N")// 空板
                .setBarcode(locCache.getBarcode())// 托盘码
                .setLinkMis("N")
                .setAppeUser(userId)
                .setAppeTime(new Date())
                .setModiUser(userId)
                .setModiTime(new Date());
        if (!taskService.insert(task)) {
            throw new CoolException("保存工作档失败");
        }
        // 生成工作档明细
        for (LocDto locDto : taskDto.getLocDtos()) {
            if (locDto.getAnfme() == null || locDto.getAnfme() <= 0.0D) {
                continue;
            }
//            OrderDetl orderDetl = orderDetlService.selectItem(locDto.getOrderNo(), locDto.getMatnr(), locDto.getBatch());
            OrderDetl orderDetl = OrderInAndOutUtil.selectItem(Boolean.FALSE, locDto.getOrderNo(), locDto.getMatnr(), locDto.getBatch(), locDto.getBrand()
                    , locDto.getStandby1(), locDto.getStandby2(), locDto.getStandby3(), locDto.getBoxType1(), locDto.getBoxType2(), locDto.getBoxType3());
//            if (orderDetl == null) {
////                orderDetl = orderDetlService.selectItem(locDto.getOrderNo(), locDto.getMatnr(), null);
//                orderDetl = OrderInAndOutUtil.selectItem(Boolean.FALSE, locDto.getOrderNo(), locDto.getMatnr(), null);
//
//            }
            TaskDetl wrkDetl = new TaskDetl();
            BeanUtils.copyProperties(orderDetl, wrkDetl);
            wrkDetl.setWrkNo(workNo)
                    .setId(null)
                    .setIoTime(new Date())
                    .setOrderNo(locDto.getOrderNo())
                    .setAnfme(locDto.getAnfme())
                    .setZpallet(locCache.getBarcode())
                    .setBatch(locDto.getBatch())
                    .setAppeUser(userId)
                    .setAppeTime(new Date())
                    .setModiUser(userId);
            if (!taskDetlService.insert(wrkDetl)) {
                throw new CoolException("保存工作档明细失败");
            }
//            // 修改订单明细
//            if (!orderDetlService.increaseWorkQty(orderDetl.getOrderId(), orderDetl.getMatnr(), orderDetl.getBatch(), locDto.getAnfme())) {
//                throw new CoolException("修改订单明细数量失败");
//            }
//            orderService.updateSettle(orderDetl.getOrderId(), 2L, userId);
            OrderInAndOutUtil.increaseWorkQty(Boolean.FALSE, orderDetl.getOrderId(), orderDetl.getMatnr(), orderDetl.getBatch(),
                    orderDetl.getBrand(), orderDetl.getStandby1(), orderDetl.getStandby2(), orderDetl.getStandby3(),
                    orderDetl.getBoxType1(), orderDetl.getBoxType2(), orderDetl.getBoxType3()
                    , locDto.getAnfme());
            OrderInAndOutUtil.updateOrder(Boolean.FALSE, orderDetl.getOrderId(), 2L, userId);
        }
        // 修改库位状态:   F.在库 ====>>> R.出库预约/P.拣料/盘点/并板出库中
        if (locCache.getLocSts().equals("F")) {
            locCache.setLocSts(ioType == 101 ? "R" : "P");
            locCache.setModiUser(userId);
            locCache.setModiTime(now);
            if (!locCacheService.updateById(locCache)) {
                throw new CoolException("预约库位状态失败,库位号:" + taskDto.getLocNo());
            }
        } else {
            throw new CoolException(taskDto.getLocNo() + "库位不是在库状态");
        }
    }
    @Override
    @Transactional
    public String emptyPlateIn(Integer devpNo, Long userId) {
        // 源站点状态检测
src/main/java/com/zy/asrs/task/handler/WorkMastHandler.java
@@ -786,6 +786,42 @@
            if (!taskService.updateById(task)) {
                throw new CoolException("任务状态修改失败!!");
            }
        } else if (task.getIoType().equals(110)) {
            Date now = new Date();
            LocCache locMast = locCacheService
                    .selectOne(new EntityWrapper<LocCache>().eq("loc_no", task.getSourceLocNo()));
            if (Objects.isNull(locMast)) {
                throw new RuntimeException("数据错误:库位信息不能为空!!");
            }
            // 修改源库位状态 R ===>> O
            if (locMast.getLocSts().equals(LocStsType.LOC_STS_TYPE_R.type)) {
                locMast.setLocSts(LocStsType.LOC_STS_TYPE_O.type);
                locMast.setBarcode("");
                locMast.setModiTime(now);
                locMast.setIoTime(now);
                if (!locCacheService.updateById(locMast)) {
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                    return FAIL.setMsg("全板出库 ===>> 修改源库位状态失败; [workNo=" + task.getWrkNo() + "],[locNo="
                            + task.getSourceLocNo() + "]");
                }
            } else {
                throw new CoolException("当前库位状态" + locMast.getLocSts() + ", 无法执行出库操作!!");
            }
            BasStation devNo = basStationService
                    .selectOne(new EntityWrapper<BasStation>().eq("dev_no", task.getStaNo()));
            if (Objects.isNull(devNo)) {
                throw new CoolException("站点:" + task.getSourceStaNo() + ", 不存在!!");
            }
            devNo.setLocSts(LocStsType.LOC_STS_TYPE_D.type);
            devNo.setModiTime(new Date());
            if (!basStationService.updateById(devNo)) {
                throw new CoolException("站点信息修改失败!!");
            }
            task.setWrkSts(15L);
            if (!taskService.updateById(task)) {
                throw new CoolException("任务状态修改失败!!");
            }
        } else {
        }
@@ -845,7 +881,7 @@
        Set<Long> list = apallet.stream().map(WaitPakin::getOrderId).collect(Collectors.toSet());
        List<OrderPakin> pakins = orderPakinService.selectList(new EntityWrapper<OrderPakin>().in("id", list));
        if (Objects.isNull(pakins) || pakins.isEmpty()) {
            throw new CoolException("单据不存在!!");
//            throw new CoolException("单据不存在!!");
        }
        return SUCCESS;
src/main/java/com/zy/common/CodeBuilder.java
@@ -20,7 +20,7 @@
        generator.url="192.168.4.15:1433;databasename=jsxsasrs";
        generator.username="sa";
        generator.password="sa@123";
        generator.table="man_check_order_detl";
        generator.table="agv_station_rela";
        generator.packagePath="com.zy.asrs";
        generator.html = false;
        generator.js = false;
src/main/java/com/zy/common/model/LocDto.java
@@ -47,6 +47,10 @@
    private Integer staNo;
    private List<String> agvStaNos;
    private String agvStaNo;
    public LocDto() {
    }
@@ -85,6 +89,13 @@
        }
    }
    public void setAgvStaNos(List<String> agvStaNos) {
        this.agvStaNos = agvStaNos;
        if (!Cools.isEmpty(agvStaNos)) {
            this.agvStaNo = agvStaNos.get(0);
        }
    }
    public String getFrozen$() {
        if (null == this.frozen){ return null; }
        switch (this.frozen){
src/main/java/com/zy/common/model/TaskDto.java
@@ -22,6 +22,10 @@
    private List<LocDto> locDtos;
    private String agvStaNo;
    {
        locDtos = new ArrayList<>();
    }
@@ -37,6 +41,12 @@
        this.locDtos.add(locDto);
    }
    public TaskDto(String locNo, String staNo, LocDto locDto) {
        this.locNo = locNo;
        this.agvStaNo = staNo;
        this.locDtos.add(locDto);
    }
    public TaskDto(String locNo, Integer staNo, List<LocDto> locDtos) {
        this.locNo = locNo;
        this.staNo = staNo;
src/main/java/com/zy/common/model/enums/IoWorkType.java
@@ -6,6 +6,7 @@
    PICK_IN,
    ALL_OUT,
    PICK_OUT,
    MERGE_OUT,
    CHECK_OUT,
    ;
src/main/java/stationRela.sql
New file
@@ -0,0 +1,18 @@
-- save stationRela record
-- mysql
insert into `sys_resource` ( `code`, `name`, `resource_id`, `level`, `sort`, `status`) values ( 'stationRela/stationRela.html', 'stationRela管理', null , '2', null , '1');
insert into `sys_resource` ( `code`, `name`, `resource_id`, `level`, `sort`, `status`) values ( 'stationRela#view', '查询', '', '3', '0', '1');
insert into `sys_resource` ( `code`, `name`, `resource_id`, `level`, `sort`, `status`) values ( 'stationRela#btn-add', '新增', '', '3', '1', '1');
insert into `sys_resource` ( `code`, `name`, `resource_id`, `level`, `sort`, `status`) values ( 'stationRela#btn-edit', '编辑', '', '3', '2', '1');
insert into `sys_resource` ( `code`, `name`, `resource_id`, `level`, `sort`, `status`) values ( 'stationRela#btn-delete', '删除', '', '3', '3', '1');
insert into `sys_resource` ( `code`, `name`, `resource_id`, `level`, `sort`, `status`) values ( 'stationRela#btn-export', '导出', '', '3', '4', '1');
-- sqlserver
insert [dbo].[sys_resource] ( [code], [name], [resource_id], [level], [sort], [status]) values ( N'stationRela/stationRela.html', N'stationRela管理', null, '2', null, '1');
insert [dbo].[sys_resource] ( [code], [name], [resource_id], [level], [sort], [status]) values ( N'stationRela#view', N'查询', '', '3', '0', '1');
insert [dbo].[sys_resource] ( [code], [name], [resource_id], [level], [sort], [status]) values ( N'stationRela#btn-add', N'新增', '', '3', '1', '1');
insert [dbo].[sys_resource] ( [code], [name], [resource_id], [level], [sort], [status]) values ( N'stationRela#btn-edit', N'编辑', '', '3', '2', '1');
insert [dbo].[sys_resource] ( [code], [name], [resource_id], [level], [sort], [status]) values ( N'stationRela#btn-delete', N'删除', '', '3', '3', '1');
insert [dbo].[sys_resource] ( [code], [name], [resource_id], [level], [sort], [status]) values ( N'stationRela#btn-export', N'导出', '', '3', '4', '1');
src/main/resources/mapper/LocDetlMapper.xml
@@ -552,6 +552,13 @@
                #{item}
            </foreach>
        </if>
        <if test="areaIds != null and areaIds.size > 0">
            and a.area_id in
            <foreach item="item" collection="areaIds" index="index"  separator="," open="(" close=")">
                #{item}
            </foreach>
        </if>
        order by
        DATEPART(yyyy,a.modi_time),DATEPART(mm,a.modi_time),DATEPART(dd,a.modi_time), a.anfme
@@ -611,6 +618,49 @@
        desc
    </select>
    <select id="queryStockAllCache" resultMap="BaseResultMap">
        select a.*
        from asr_loc_detl a
        left join asr_loc_cache b on a.loc_no = b.loc_no
        where 1=1
        and b.loc_sts = 'F'
        and a.matnr = #{matnr}
        <!--        <choose>-->
        <!--            <when test="batch != null and batch != ''">-->
        <!--                and a.batch = #{batch}-->
        <!--            </when>-->
        <!--            <otherwise>-->
        <!--                and (a.batch IS NULL OR a.batch = '')-->
        <!--            </otherwise>-->
        <!--        </choose>-->
        <if test="orderNo != null and orderNo != ''">
            and a.order_no = #{orderNo}
        </if>
        <include refid="batchSeqA"></include>
        <if test="locNos != null and locNos.size > 0">
            and b.loc_no not in
            <foreach item="item" collection="locNos" index="index"  separator="," open="(" close=")">
                #{item}
            </foreach>
        </if>
        <if test="areaIds != null and areaIds.size > 0">
            and a.area_id in
            <foreach item="item" collection="areaIds" index="index"  separator="," open="(" close=")">
                #{item}
            </foreach>
        </if>
        order by
        DATEPART(yyyy,a.modi_time),DATEPART(mm,a.modi_time),DATEPART(dd,a.modi_time), a.anfme
        desc,
        NEWID()
    </select>
    <select id="queryStockAnfme" resultType="java.lang.Double">
        select sum(anfme) as count from man_loc_detl
         where 1=1
src/main/resources/mapper/StationRelaMapper.xml
New file
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zy.asrs.mapper.StationRelaMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.zy.asrs.entity.StationRela">
        <result column="id" property="id" />
        <result column="agv_sta" property="agvSta" />
        <result column="crn_sta" property="crnSta" />
        <result column="use_status" property="useStatus" />
    </resultMap>
</mapper>
src/main/webapp/static/js/orderPakout/agvOut.js
New file
@@ -0,0 +1,386 @@
var pageCurr;
var insTb2;
layui.config({
    base: baseUrl + "/static/layui/lay/modules/"
}).extend({
    notice: 'notice/notice',
}).use(['table','laydate', 'form', 'util', 'admin', 'notice', 'treeTable', 'xmSelect', 'tableMerge', 'tableX'], function(){
    var table = layui.table;
    var $ = layui.jquery;
    var layer = layui.layer;
    var layDate = layui.laydate;
    var form = layui.form;
    var admin = layui.admin;
    var util = layui.util;
    var notice = layui.notice;
    var treeTable = layui.treeTable;
    var xmSelect = layui.xmSelect;
    var tableMerge = layui.tableMerge;
    var tableX = layui.tableX;
    insTb2 = table.render({
        elem: '#orderDetlTable',
        headers: {token: localStorage.getItem('token')},
        data: [],
        page: true,
        limit: 15,
        limits: [15, 30, 50, 100, 200, 500],
        toolbar: '#orderDetToolbar',
        height: 'full-120',
        where: {doc_type: 5},
        cols: [[
            {type: 'checkbox'}
            ,{type: 'numbers', title: '#'}
            ,{field: 'orderNo', align: 'center',title: '单据编号', templet: '#orderNoTpl', width: 160}
            ,{field: 'matnr', align: 'center',title: '商品编码', width: 160}
            ,{field: 'maktx', align: 'center',title: '商品名称', width: 200}
            ,{field: 'standby1', align: 'center',title: '供应商代码'}
            ,{field: 'specs', align: 'center',title: '规格'}
            // ,{field: 'anfme', align: 'center',title: '数量'}
            // ,{field: 'qty', align: 'center',title: '作业数量', style: 'font-weight: bold'}
            ,{field: 'enableQty', align: 'center',title: '待出数量', style: 'font-weight: bold'}
            // ,{field: 'name', align: 'center',title: '名称'}
            // ,{field: 'model', align: 'center',title: '型号'}
            ,{field: 'unit', align: 'center',title: '单位', hide: true}
            ,{field: 'barcode', align: 'center',title: '商品条码', hide: true}
            // ,{field: 'supplier', align: 'center',title: '供应商'}
            // ,{field: 'unitPrice', align: 'center',title: '单价'}
            // ,{field: 'itemNum', align: 'center',title: '品项数'}
            // ,{field: 'count', align: 'center',title: '数量'}
            // ,{field: 'weight', align: 'center',title: '重量'}
            // ,{field: 'status$', align: 'center',title: '状态'}
            // ,{field: 'createBy$', align: 'center',title: '添加人员'}
            // ,{field: 'createTime$', align: 'center',title: '添加时间'}
            // ,{field: 'updateBy$', align: 'center',title: '修改人员'}
            // ,{field: 'updateTime$', align: 'center',title: '修改时间'}
            // ,{field: 'memo', align: 'center',title: '备注'}
            ,{fixed: 'right', title:'操作', align: 'center', toolbar: '#operate', width: 160}
        ]],
        request: {
            pageName: 'curr',
            pageSize: 'limit'
        },
        parseData: function (res) {
            return {
                'code': res.code,
                'msg': res.msg,
                'count': res.data.total,
                'data': res.data.records
            }
        },
        response: {
            statusCode: 200
        },
        done: function(res, curr, count) {
            if (res.code === 403) {
                top.location.href = baseUrl+"/";
            }
            pageCurr=curr;
            limit();
        }
    });
    /* 表格2搜索 */
    form.on('submit(sensorTbSearch)', function (data) {
        insTb2.reload({url: baseUrl + '/order/pakout/orderDetl/pakout/list/auth',where: data.field, page: {curr: 1}});
        return false;
    });
    /* 表格2头工具栏点击事件 */
    table.on('toolbar(orderDetlTable)', function (obj) {
        var checkStatus = table.checkStatus(obj.config.id).data;
        if (obj.event === 'pakoutPreview') { // 添加
            if (checkStatus.length === 0) {
                layer.msg('请选择至少一条出库明细', {icon: 2});
                return;
            }
            pakoutPreview(checkStatus.map(function (d) {
                return d.id;
            }));
        } else if (obj.event === 'del') { // 删除
            var checkRows = table.checkStatus('sensorTable');
            if (checkRows.data.length === 0) {
                layer.msg('请选择要删除的数据', {icon: 2});
                return;
            }
            var ids = checkRows.data.map(function (d) {
                return d.id;
            });
            doDelSensor({ids: ids});
        }
    });
    /* 表格2工具条点击事件 */
    table.on('tool(orderDetlTable)', function (obj) {
        console.log(obj);
        var data = obj.data;
        switch (obj.event) {
            // 出库
            case 'pakoutPreview':
                pakoutPreview([data.id])
                break;
        }
    });
    function pakoutPreview(ids) {
        let loadIndex = layer.load(2);
        $.ajax({
            url: baseUrl + "/out/pakout/preview/auth",
            headers: {'token': localStorage.getItem('token')},
            contentType: 'application/json;charset=UTF-8',
            data: JSON.stringify(ids),
            method: 'POST',
            success: function (res) {
                layer.close(loadIndex);
                var tableCache;
                if (res.code === 200){
                    layer.open({
                        type: 1
                        ,title: false
                        ,closeBtn: false
                        ,offset: '50px'
                        ,area: ['1200px', '700px']
                        ,shade: 0.5
                        ,shadeClose: false
                        ,btn: ['立即出库', '稍后处理']
                        ,btnAlign: 'c'
                        ,moveType: 1 //拖拽模式,0或者1
                        ,content: $('#pakoutPreviewBox').html()
                        ,success: function(layero, index){
                            stoPreTabIdx = table.render({
                                elem: '#stoPreTab',
                                data: res.data,
                                height: 520,
                                page: false,
                                limit: Number.MAX_VALUE,
                                cellMinWidth: 100,
                                cols: [[
                                    // {type: 'checkbox', merge: ['orderNo']},
                                    {field: 'orderNo', title: '单据编号', merge: true, align: 'center'},
                                    {field: 'title', title: '商品', merge: true, align: 'center', width: 250},
                                    {field: 'standby1', align: 'center',title: '供应商代码'},
                                    {field: 'anfme', title: '数量', align: 'center', width: 90, style: 'font-weight: bold'},
                                    {field: 'locNo', title: '货位', align: 'center', templet: '#locNoTpl'},
                                    {field: 'frozen$', title: '明细', align: 'center', width: 90, templet: '#locFrozen'},
                                    {field: 'frozenLoc$', title: '库位', align: 'center', width: 90, templet: '#locFrozenLoc'},
                                    {field: 'agvStaNos', align: 'center', title: '出库站', merge: ['locNo'], templet: '#tbBasicTbStaNos'},
                                    {type: 'checkbox', merge: ['locNo']},
                                ]],
                                done: function (res) {
                                    tableMerge.render(this);
                                    $('.layui-table-body.layui-table-main').css("overflow", "auto");
                                    tableCache = tableData = table.cache.stoPreTab;
                                }
                            });
                            // 修改出库站
                            form.on('select(tbBasicTbStaNos)', function (obj) {
                                let index  = obj.othis.parents('tr').attr("data-index");
                                let data = tableCache[index];
                                for (let i = 0; i<tableCache.length; i++) {
                                    if (tableCache[i].locNo === data.locNo) {
                                        tableCache[i]['staNo'] = Number(obj.elem.value);
                                    }
                                }
                                obj.othis.children().find("input").css("color", "blue");
                                return false;
                            });
                            // 批量修改出库站
                            form.on('submit(batchModifySta)', function () {
                                let stoPreTabData = layui.table.checkStatus('stoPreTab').data;
                                if (stoPreTabData.length < 1) {
                                    layer.msg("请至少选择一条以上合并数据", {icon: 7});
                                    return false;
                                }
                                modifySta(stoPreTabData);
                            });
                            // 批量修改出库站 - 站点选择
                            function modifySta(stoPreTabData) {
                                // 出库站取交集
                                let staBatchSelectVal = [];
                                for(let i = 0; i<stoPreTabData.length; i++) {
                                    let staNos = stoPreTabData[i].staNos;
                                    if (staNos !== null) {
                                        if (staBatchSelectVal.length === 0) {
                                            staBatchSelectVal = staNos;
                                        } else {
                                            staBatchSelectVal = staBatchSelectVal.filter(val =>
                                                {
                                                    return new Set(staNos).has(val)
                                                }
                                            )
                                        }
                                    }
                                }
                                if (staBatchSelectVal.length === 0) {
                                    layer.msg("出库站没有交集,无法批量修改", {icon: 2});
                                    return;
                                }
                                admin.open({
                                    type: 1,
                                    area: '300px',
                                    offset: 'auto',
                                    title: '请选择站点',
                                    content: $('#staBatchSelectDialog').html(),
                                    success: function (layero, ddIndex) {
                                        // 渲染下拉框
                                        let template = Handlebars.compile($('#batchStaSelectTpl').html());
                                        $('#batchSelectStaBox').html(template({list: staBatchSelectVal}));
                                        // 确认
                                        form.on('submit(staBatchSelectConfirm)', function (obj) {
                                            let loadIdx = layer.load(2);
                                            let batchSta = Number(obj.field.batchSta);
                                            let arr = [];
                                            for (let j = 0; j<stoPreTabData.length; j++) {
                                                for (let i = 0; i<tableCache.length; i++) {
                                                    if (tableCache[i].orderNo === stoPreTabData[j].orderNo
                                                        && tableCache[i].matnr === stoPreTabData[j].matnr
                                                        && tableCache[i].locNo === stoPreTabData[j].locNo) {
                                                        tableCache[i]['staNo'] = batchSta;
                                                        arr.push(i);
                                                    }
                                                }
                                            }
                                            stoPreTabIdx.reload({data: tableCache});
                                            arr.forEach(item => {
                                                $('div[lay-id=stoPreTab] tr[data-index="' + item + '"] .order-sta-select').val(batchSta);
                                            });
                                            layui.form.render('select');
                                            arr.forEach(item => {
                                                $('div[lay-id=stoPreTab] tr[data-index="' + item + '"] .layui-select-title').find("input").css("color", "blue");
                                            });
                                            layer.close(loadIdx); layer.close(ddIndex);
                                            return false;
                                        });
                                        // 弹窗不出现滚动条
                                        $(layero).children('.layui-layer-content').css('overflow', 'visible');
                                        layui.form.render('select');
                                    },
                                })
                            }
                        }
                        ,yes: function(index, layero){
                            //按钮【立即出库】的回调
                            pakout(tableCache, index);
                        }
                        ,btn2: function(index, layero){
                            //按钮【稍后处理】的回调
                            layer.close(index)
                            //return false 开启该代码可禁止点击该按钮关闭
                        }
                    });
                } else if (res.code === 403){
                    top.location.href = baseUrl+"/";
                } else {
                    layer.msg(res.msg, {icon: 2})
                }
            }
        })
    }
    function pakout(tableCache, layerIndex) {
        // let loadIndex = layer.load(2);
        notice.msg('正在生成出库任务......', {icon: 4});
        $.ajax({
            url: baseUrl + "/out/agvPakOut/auth",
            headers: {'token': localStorage.getItem('token')},
            contentType: 'application/json;charset=UTF-8',
            data: JSON.stringify(tableCache),
            method: 'POST',
            success: function (res) {
                notice.destroy();
                if (res.code === 200) {
                    layer.close(layerIndex);
                    layer.msg(res.msg, {icon: 1});
                    insTb.reload({where: null});
                    insTb2.reload({url: baseUrl + '/order/pakout/orderDetl/pakout/list/auth',where: null, page: {curr: 1}});
                } else if (res.code === 403) {
                    top.location.href = baseUrl + "/";
                } else {
                    layer.msg(res.msg, {icon: 2})
                }
            }
        });
    }
    /* 删除订单 */
    function doDelSensor(obj) {
        layer.confirm('确定要删除选中数据吗?', {
            skin: 'layui-layer-admin',
            shade: .1
        }, function (i) {
            layer.close(i);
            var loadIndex = layer.load(2);
            $.ajax({
                url: baseUrl+"/sensor/delete/auth",
                headers: {'token': localStorage.getItem('token')},
                data: {ids: obj.ids},
                method: 'POST',
                success: function (res) {
                    layer.close(loadIndex);
                    if (res.code === 200){
                        layer.msg(res.msg, {icon: 1});
                        $(".layui-laypage-btn")[0].click();
                    } else if (res.code === 403){
                        top.location.href = baseUrl+"/";
                    }else {
                        layer.msg(res.msg, {icon: 2});
                    }
                }
            })
        });
    }
    // 修改状态
    form.on('switch(statusSwitch)', function (obj) {
        var index  = obj.othis.parents('tr').attr("data-index");
        var data = tableData[index];
        data[this.name] = obj.elem.checked?1:0;
        http.post(baseUrl+"/sensor/edit/auth", {id: data.id, status: data[this.name]}, function (res) {
            layer.msg(res.msg, {icon: 1});
        })
    })
    window.pakoutPreview = pakoutPreview;
});
function tableReload(child) {
    var searchData = {};
    $.each($('#search-box [name]').serializeArray(), function() {
        searchData[this.name] = this.value;
    });
    (child ? parent.tableIns : tableIns).reload({
        where: searchData,
        page: {
            curr: pageCurr
        }
    });
}
/**
 * 一键出库
 */
function autoOut(orderId) {
    let loadIndex = layer.msg('请求中...', {icon: 16, shade: 0.01, time: false});
    $.ajax({
        url: baseUrl + "/out/pakout/orderDetlIds/auth",
        headers: {'token': localStorage.getItem('token')},
        data: { orderId : orderId },
        method: 'POST',
        success: function (res) {
            layer.close(loadIndex);
            if (res.code === 200){
                pakoutPreview(res.data);
            } else if (res.code === 403){
                top.location.href = baseUrl+"/";
            } else {
                layer.msg(res.msg, {icon: 2});
            }
        }
    })
}
src/main/webapp/static/js/orderPakout/order.js
@@ -43,12 +43,13 @@
        cols: [[
            {type: 'numbers'},
            {field: 'orderNo', title: '单据编号', templet: '#orderNoTpl'},
            {field: 'itemName$', align: 'center', title: '出库区域',  minWidth: 160, width: 160},
            {field: 'docType$', align: 'center', title: '类型',  minWidth: 160, width: 160},
            {align: 'center', title: '明细', toolbar: '#tbLook', minWidth: 160, width: 160},
            {field: 'createTime$', title: '创建时间', minWidth: 200, width: 200},
            {field: 'settle$', align: 'center', title: '状态', templet: '#settleTpl',  minWidth: 160, width: 160},
            {field: 'memo', align: 'center',title: '备注', hide: true},
            {align: 'center', title: '操作', toolbar: '#operate', width: 180}
            {align: 'center', title: '操作', toolbar: '#operate'}
        ]],
        request: {
            pageName: 'curr',
@@ -122,9 +123,10 @@
                        cellMinWidth: 100,
                        cols: [[
                            {type: 'numbers'},
                            {field: 'matnr', title: '商品编码', width: 160},
                            {field: 'maktx', title: '商品名称', width: 160},
                            {field: 'batch', title: '批号'},
                            {field: 'matnr', title: '零件代码', width: 160},
                            {field: 'maktx', title: '零件名称', width: 160},
                            // {field: 'batch', title: '批号'},
                            {field: 'standby1', title: '供应商代码'},
                            {field: 'anfme', title: '数量'},
                            {field: 'workQty', title: '作业数量'},
                            {field: 'qty', title: '完成数量', style: 'font-weight: bold'},
@@ -173,6 +175,30 @@
            success: function (layero, dIndex) {
                $(layero).children('.layui-layer-content').css('overflow', 'visible');
                var isExpAdd = !expTpe;
                // 加载区域下拉数据
                $.ajax({
                    url: baseUrl + "/basAreas/list/auth",
                    headers: {'token': localStorage.getItem('token')},
                    data: { limit: 9999 },
                    method: 'POST',
                    success: function (res) {
                        if (res.code === 200) {
                            var areaSelect = $(layero).find('select[name="itemName"]');
                            var html = '<option value="">请选择区域</option>';
                            var records = res.data.records || res.data;
                            for (var i = 0; i < records.length; i++) {
                                var selected = (expTpe && expTpe.itemName == records[i].id) ? ' selected' : '';
                                html += '<option value="' + records[i].id + '"' + selected + '>' + records[i].name + '</option>';
                            }
                            areaSelect.html(html);
                            form.render('select', 'editForm');
                        } else if (res.code === 403) {
                            top.location.href = baseUrl + "/";
                        } else {
                            layer.msg(res.msg, {icon: 2});
                        }
                    }
                });
                // 回显数据
                form.val('editForm', expTpe);
                if (expTpe) {
@@ -204,6 +230,7 @@
                            orderId: Number(data.field.id),
                            docType: Number(data.field.docType),
                            orderNo: data.field.orderNo,
                            itemName: data.field.itemName ? Number(data.field.itemName) : null,
                            orderDetlPakoutList: nList
                        }),
                        contentType:'application/json;charset=UTF-8',
@@ -234,9 +261,10 @@
                    cellMinWidth: 100,
                    cols: [[
                        {type: 'numbers', title: '#'},
                        {field: 'matnr', title: '商品编码', width: 160},
                        {field: 'maktx', title: '商品名称', width: 200},
                        {field: 'batch', title: '批号', edit: true},
                        {field: 'matnr', title: '零件代码', width: 160},
                        {field: 'maktx', title: '零件名称', width: 200},
                        // {field: 'batch', title: '批号', edit: true},
                        {field: 'standby1', title: '供应商代码', edit: true},
                        {field: 'specs', title: '规格'},
                        {field: 'anfme', title: '数量(修改)', style: 'color: blue;font-weight: bold', edit: true, minWidth: 110, width: 110},
                        {field: 'workQty', title: '作业数量',  minWidth: 100, width: 100},
src/main/webapp/static/js/orderPakout/out.js
@@ -21,7 +21,7 @@
    insTb2 = table.render({
        elem: '#orderDetlTable',
        headers: {token: localStorage.getItem('token')},
        url: baseUrl+'/order/pakout/orderDetl/pakout/list/auth',
        data:[],
        page: true,
        limit: 15,
        limits: [15, 30, 50, 100, 200, 500],
@@ -82,7 +82,7 @@
    /* 表格2搜索 */
    form.on('submit(sensorTbSearch)', function (data) {
        insTb2.reload({where: data.field, page: {curr: 1}});
        insTb2.reload({url: baseUrl + '/order/pakout/orderDetl/pakout/list/auth',where: data.field, page: {curr: 1}});
        return false;
    });
@@ -294,7 +294,7 @@
                    layer.close(layerIndex);
                    layer.msg(res.msg, {icon: 1});
                    insTb.reload({where: null});
                    insTb2.reload({where: null, page: {curr: 1}});
                    insTb2.reload({url: baseUrl + '/order/pakout/orderDetl/pakout/list/auth',where: null, page: {curr: 1}});
                } else if (res.code === 403) {
                    top.location.href = baseUrl + "/";
                } else {
src/main/webapp/static/js/orderTablePakout.js
@@ -73,7 +73,7 @@
    /* 表格重置 */
    form.on('submit(originTbReset)', function (data) {
        insTb.reload({where: null});
        insTb2.reload({where: null, page: {curr: 1}});
        insTb2.reload({url: baseUrl + '/order/pakout/orderDetl/pakout/list/auth',where: null, page: {curr: 1}});
        return false;
    });
@@ -101,7 +101,7 @@
        selObj = obj;
        obj.tr.addClass('layui-table-click').siblings().removeClass('layui-table-click');
        insTb2.reload({where: {order_id: obj.data.id}, page: {curr: 1}});
        insTb2.reload({url: baseUrl + '/order/pakout/orderDetl/pakout/list/auth',where: {order_id: obj.data.id}, page: {curr: 1}});
    });
    /* 显示表单弹窗 */
src/main/webapp/static/js/orderTablePakoutAGV.js
New file
@@ -0,0 +1,174 @@
var insTb;
layui.config({
    base: baseUrl + "/static/layui/lay/modules/"  // 配置模块所在的目录
}).use(['table','laydate', 'form',  'admin', 'tableX'], function() {
    var table = layui.table;
    var $ = layui.jquery;
    var layer = layui.layer;
    var layDate = layui.laydate;
    var form = layui.form;
    var admin = layui.admin;
    var tableX = layui.tableX;
    /****************************************** 左边表 *************************************************/
    insTb = table.render({
        elem: '#originTable',
        url: baseUrl + '/order/pakout/order/AGV/nav/list/auth',
        height: 'full-120',
        headers: {token: localStorage.getItem('token')},
        request: {
            pageName: 'curr',
            pageSize: 'limit'
        },
        page: false,
        parseData: function (res) {
            return {
                'code': res.code,
                'msg': res.msg,
                'data': res.data
            }
        },
        response: {
            statusCode: 200
        },
        // toolbar: ['<p>',
        //     '<button lay-event="add" class="layui-btn layui-btn-sm icon-btn"><i class="layui-icon">&#xe654;</i>添加</button>&nbsp;',
        //     '<button lay-event="edit" class="layui-btn layui-btn-sm layui-btn-warm icon-btn"><i class="layui-icon">&#xe642;</i>修改</button>&nbsp;',
        //     '<button lay-event="del" class="layui-btn layui-btn-sm layui-btn-danger icon-btn"><i class="layui-icon">&#xe640;</i>删除</button>',
        //     '</p>'].join(''),
        defaultToolbar: [],
        cols: [[
            // {type: 'numbers', title: '#'},
            {field: 'orderTime', title: '日期', width: 160},
            {field: 'orderNo', title: '单据编号', align: 'center', width: 155},
            {field: 'docType$', title: '单据类型', align: 'center'}
        ]],
        done: function (res, curr, count) {
            $('#dictTable+.layui-table-view .layui-table-body tbody>tr:first').trigger('click');
            // 绑定鼠标右键
            tableX.bindCtxMenu('originTable', function (d) {
                return [
                    {
                        icon: 'layui-icon layui-icon-ok',
                        name: '一键出库',
                        click: function (d) {
                            autoOut(d.id);
                        }
                    }
                ]
            })
        }
    });
    /* 表格搜索 */
    form.on('submit(originTableSearch)', function (data) {
        insTb.reload({where: data.field});
        return false;
    });
    /* 表格重置 */
    form.on('submit(originTbReset)', function (data) {
        insTb.reload({where: null});
        insTb2.reload({url: baseUrl + '/order/pakout/orderDetl/pakout/list/auth',where: null, page: {curr: 1}});
        return false;
    });
    /* 表格头工具栏点击事件 */
    table.on('toolbar(originTable)', function (obj) {
        if (obj.event === 'add') { // 添加
            showEdit();
        } else if (obj.event === 'edit') { // 修改
            if (selObj == null) {
                return;
            }
            showEdit(selObj.data);
        } else if (obj.event === 'del') { // 删除
            if (selObj == null) {
                return;
            }
            doDel(selObj);
        }
    });
    /* 监听行单击事件 */
    var selObj;
    table.on('row(originTable)', function (obj) {
        selObj = obj;
        obj.tr.addClass('layui-table-click').siblings().removeClass('layui-table-click');
        insTb2.reload({url: baseUrl + '/order/pakout/orderDetl/pakout/list/auth',where: {order_id: obj.data.id}, page: {curr: 1}});
    });
    /* 显示表单弹窗 */
    function showEdit(mData) {
        admin.open({
            type: 1,
            title: (mData ? '修改' : '添加') + '项目',
            content: $('#hostEditDialog').html(),
            success: function (layero, dIndex) {
                // 回显表单数据
                form.val('hostEditForm', mData);
                // 表单提交事件
                form.on('submit(hostEditSubmit)', function (data) {
                    var loadIndex = layer.load(2);
                    $.ajax({
                        url: baseUrl+"/host/"+(mData?'update':'add')+"/auth",
                        headers: {'token': localStorage.getItem('token')},
                        data: data.field,
                        method: 'POST',
                        success: function (res) {
                            layer.close(loadIndex);
                            selObj = null;
                            if (res.code === 200){
                                layer.close(dIndex);
                                layer.msg(res.msg, {icon: 1});
                                insTb.reload();
                            } else if (res.code === 403){
                                top.location.href = baseUrl+"/";
                            } else {
                                layer.msg(res.msg, {icon: 2});
                            }
                        }
                    })
                    return false;
                });
            }
        });
    }
    /* 删除 */
    function doDel(obj) {
        layer.confirm('确定要删除此单据类型吗?', {
            skin: 'layui-layer-admin',
            shade: .1
        }, function (i) {
            layer.close(i);
            var loadIndex = layer.load(2);
            $.ajax({
                url: baseUrl+"/host/delete/one/auth",
                headers: {'token': localStorage.getItem('token')},
                data: {param: JSON.stringify(obj.data)},
                method: 'POST',
                success: function (res) {
                    selObj = null;
                    layer.close(loadIndex);
                    if (res.code === 200){
                        layer.closeAll();
                        insTb.reload();
                        $('#dictTable+.layui-table-view .layui-table-body tbody>tr:first').trigger('click');
                    } else if (res.code === 403){
                        top.location.href = baseUrl+"/";
                    } else {
                        layer.msg(res.msg, {icon: 2});
                    }
                }
            })
        });
    }
})
src/main/webapp/static/js/task/task.js
@@ -171,7 +171,7 @@
                        title: '工作号:' + data.wrkNo,
                        shadeClose: true
                    }, function () {
                        http.post(baseUrl + "/hand/control/wrkMast", {workNo: data.wrkNo, type: 2}, function (res) {
                        http.post(baseUrl + "/task/control", {workNo: data.wrkNo, type: 2}, function (res) {
                            $(".layui-laypage-btn")[0].click();
                            layer.msg(data.wrkNo + res.msg);
                        })
@@ -182,7 +182,7 @@
                        title: '工作号:' + data.wrkNo,
                        shadeClose: true
                    }, function () {
                        http.post(baseUrl + "/hand/control/wrkMast", {workNo: data.wrkNo, type: 2}, function (res) {
                        http.post(baseUrl + "/task/control", {workNo: data.wrkNo, type: 2}, function (res) {
                            $(".layui-laypage-btn")[0].click();
                            layer.msg(data.wrkNo + res.msg);
                        })
@@ -193,7 +193,7 @@
            //  拣料入库
            case 'pick':
                layer.confirm('拣料入库该笔工作档?', {title: '工作号:' + data.wrkNo, shadeClose: true}, function () {
                    http.post(baseUrl + "/hand/control/wrkMast", {workNo: data.wrkNo, type: 3}, function (res) {
                    http.post(baseUrl + "/task/control", {workNo: data.wrkNo, type: 3}, function (res) {
                        $(".layui-laypage-btn")[0].click();
                        layer.msg(data.wrkNo + res.msg);
                    })
src/main/webapp/views/orderPakout/agvOut.html
New file
@@ -0,0 +1,332 @@
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title></title>
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <link rel="stylesheet" href="../../static/layui/css/layui.css" media="all">
    <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all">
    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
    <link rel="stylesheet" href="../../static/css/originTable.css" media="all">
    <style>
        body {
            color: #595959;
            background-color: #f5f7f9;
        }
        .admin-form {
            padding: 25px 30px 0 0 !important;
            margin: 0 !important;
        }
        .layui-table-view .layui-table-cell .layui-select-title .layui-input {
            height: 28px;
            line-height: 28px;
        }
        .layui-table-view [lay-size="lg"] .layui-table-cell .layui-select-title .layui-input {
            height: 40px;
            line-height: 40px;
        }
        .layui-table-view [lay-size="lg"] .layui-table-cell .layui-select-title .layui-input {
            height: 40px;
            line-height: 40px;
        }
        .layui-table-view [lay-size="sm"] .layui-table-cell .layui-select-title .layui-input {
            height: 20px;
            line-height: 20px;
        }
        .layui-table-view [lay-size="sm"] .layui-table-cell .layui-btn-xs {
            height: 18px;
            line-height: 18px;
        }
        /* 权限控制 */
        #btn-pakoutPreview {
            display: none;
        }
        /*#btn-delete {*/
        /*    display: none;*/
        /*}*/
        /*.btn-edit {*/
        /*    display: none;*/
        /*}*/
        /*.btn-more {*/
        /*    display: none;*/
        /*}*/
    </style>
</head>
<body>
<!-- 正文开始 -->
<div class="layui-fluid" style="padding-bottom: 0;">
    <div class="layui-row layui-col-space15">
        <!-- 左 -->
        <div class="layui-col-md3" id="left-table">
            <div class="layui-card">
                <div class="layui-card-body" style="padding: 10px;">
                    <form class="layui-form toolbar">
                        <div class="layui-form-item">
                            <div class="layui-inline" style="max-width: 300px;">
                                <input name="orderNo" class="layui-input" placeholder="输入单据编号" autocomplete="off"/>
                            </div>
                            <div class="layui-inline">
                                <button class="layui-btn icon-btn" lay-filter="originTableSearch" lay-submit>
                                    <i class="layui-icon">&#xe615;</i>搜索
                                </button>
                                <button class="layui-btn icon-btn" lay-filter="originTbReset" lay-submit>
                                    <i class="layui-icon">&#xe666;</i>重置
                                </button>
                            </div>
                        </div>
                    </form>
                    <table id="originTable" lay-filter="originTable"></table>
                </div>
            </div>
        </div>
        <!-- 右 -->
        <div class="layui-col-md9">
            <div class="layui-card">
                <div class="layui-card-body" style="padding: 10px;">
                    <form class="layui-form toolbar">
                        <div class="layui-form-item">
                            <div class="layui-inline">
                                <label class="layui-form-label">商品编码:</label>
                                <div class="layui-input-inline">
                                    <input name="matnr" class="layui-input" placeholder="商品编码"/>
                                </div>
                            </div>
                            <div class="layui-inline">
                                <label class="layui-form-label">商品名称:</label>
                                <div class="layui-input-inline">
                                    <input name="maktx" class="layui-input" placeholder="商品名称"/>
                                </div>
                            </div>
                            <div class="layui-inline">
                                <label class="layui-form-label">序列码:</label>
                                <div class="layui-input-inline">
                                    <input name="batch" class="layui-input" placeholder="序列码"/>
                                </div>
                            </div>
                            <!--                            <div class="layui-inline">-->
                            <!--                                <label class="layui-form-label">状态:</label>-->
                            <!--                                <div class="layui-input-inline">-->
                            <!--                                    <select name="isOnline">-->
                            <!--                                        <option value="1">充电中</option>-->
                            <!--                                        <option value="1">充电中</option>-->
                            <!--                                        <option value="0">不在充电</option>-->
                            <!--                                    </select>-->
                            <!--                                </div>-->
                            <!--                            </div>-->
                            <div class="layui-inline">&emsp;
                                <button class="layui-btn icon-btn" lay-filter="sensorTbSearch" lay-submit>
                                    <i class="layui-icon">&#xe615;</i>搜索
                                </button>
                            </div>
                        </div>
                    </form>
                    <table id="orderDetlTable" lay-filter="orderDetlTable"></table>
                </div>
            </div>
        </div>
    </div>
</div>
<!-- 头工具栏 -->
<script type="text/html" id="orderDetToolbar">
    <!--    <div class="layui-btn-container">-->
    <!--        <div class="layui-col-md3">-->
    <!--            <select id="staNoSelect" lay-verify="required">-->
    <!--                <option value="">请选择站点</option>-->
    <!--            </select>-->
    <!--        </div>-->
    <!--    </div>-->
    <button class="layui-btn layui-btn-sm layui-btn-danger btn-pakoutPreview" id="btn-pakoutPreview" lay-event="pakoutPreview">批量出库</button>
</script>
<!-- 行工具栏 -->
<script type="text/html" id="operate">
    {{#if (d.enableQty > 0){ }}
    <a class="layui-btn layui-btn-xs layui-btn-danger btn-pakoutPreview" lay-event="pakoutPreview"><i class="layui-icon layui-icon-prev-circle"></i>出库</a>
    {{# } }}
</script>
<!-- 出库预览 -->
<script type="text/html" id="pakoutPreviewBox" style="display: none">
    <div style="padding: 25px; line-height: 22px; background-color: #393D49; color: #fff; font-weight: 300;">
        <span style="font-size: large; font-weight: bold">出库预览</span>
    </div>
    <div class="layui-card">
        <div class="layui-card-body" style="padding: 10px">
            <table id="stoPreTab" lay-filter="stoPreTab"></table>
        </div>
        <button class="layui-btn layui-btn-primary layui-border-black layui-btn-sm" lay-filter="batchModifySta" lay-submit style="display: block;float: right;margin-right: 1rem">
            批量修改
        </button>
    </div>
</script>
<script type="text/html" id="tbBasicTbStaNos">
    <div class="ew-select-fixed">
        <select class="order-sta-select" lay-filter="tbBasicTbStaNos">
            {{#if (d.agvStaNos!=null) {}}
            {{# for(let i=0; i<d.agvStaNos.length; i++) { }}
            <option value="{{d.agvStaNos[i]}}">{{d.agvStaNos[i]}}</option>
            {{# } }}
            {{# } }}
        </select>
    </div>
</script>
<script type="text/html" id="staBatchSelectDialog">
    <form class="layui-form" style="padding: 25px 50px 30px 50px;text-align: center">
        <select id="batchSelectStaBox" name="batchSta" lay-vertype="tips" lay-verify="required" required="">
        </select>
        <button style="margin-top: 30px" class="layui-btn" lay-filter="staBatchSelectConfirm" lay-submit="">确定</button>
    </form>
</script>
<script type="text/html" id="locNoTpl">
    <span name="locNo"
          {{# if( d.lack === false){ }}
          class="layui-badge layui-badge-green" >{{d.locNo}}</span>
    {{# } else { }}
    class="layui-badge layui-badge-red" >库存不足</span>
    {{# } }}
</script>
<script type="text/html" id="locFrozen">
    <span name="frozen"
          {{# if( d.lack === false){ }}
          class="layui-badge layui-badge-green" >{{d.frozen$}}</span>
    {{# } else { }}
    class="layui-badge layui-badge-red" >未知</span>
    {{# } }}
</script>
<script type="text/html" id="locFrozenLoc">
    <span name="frozenLoc"
          {{# if( d.lack === false){ }}
          class="layui-badge layui-badge-green" >{{d.frozenLoc$}}</span>
    {{# } else { }}
    class="layui-badge layui-badge-red" >未知</span>
    {{# } }}
</script>
<!-- 行样式 -->
<script type="text/html" id="orderNoTpl">
    <span name="orderNo" class="layui-badge layui-badge-gray">{{d.orderNo}}</span>
</script>
<script type="text/html" id="statusTpl">
    <input type="checkbox" name="status" value="{{d.status}}" lay-skin="switch" lay-text="正常|禁用" lay-filter="statusSwitch" {{ d.status === 1 ? 'checked' : '' }}>
</script>
<!-- 表单弹窗 -->
<script type="text/html" id="editDialog">
    <form id="detail" lay-filter="detail" class="layui-form admin-form">
        <input name="id" type="hidden">
        <input name="status" type="hidden">
        <div class="layui-row">
            <div class="layui-col-md4">
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">设备编号</label>
                    <div class="layui-input-block">
                        <input name="uuid" placeholder="请输入设备编号" class="layui-input" lay-vertype="tips" lay-verify="required" required="">
                    </div>
                </div>
            </div>
            <div class="layui-col-md4">
                <div class="layui-form-item">
                    <label class="layui-form-label">设备类型:</label>
                    <div class="layui-input-block">
                        <div id="modelSel" class="ew-xmselect-tree"></div>
                    </div>
                </div>
            </div>
            <div class="layui-col-md4">
                <div class="layui-form-item">
                    <label class="layui-form-label layui-form-required">所属项目: </label>
                    <div class="layui-input-block cool-auto-complete">
                        <input name="hostId" class="layui-input" style="display: none">
                        <input id="hostId$" name="hostId$" class="layui-input cool-auto-complete-div" onclick="autoShow(this.id)" type="text" placeholder="请选择所属项目" onfocus=this.blur()>
                        <div class="cool-auto-complete-window">
                            <input class="cool-auto-complete-window-input" data-key="hostQueryByhostId" onkeyup="autoLoad(this.getAttribute('data-key'))">
                            <select class="cool-auto-complete-window-select" data-key="hostQueryByhostIdSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
                            </select>
                        </div>
                    </div>
                </div>
            </div>
            <div class="layui-col-md12">
                <div class="layui-form-item">
                    <label class="layui-form-label">详细地址</label>
                    <div class="layui-input-block">
                        <input name="addr" placeholder="请输入详细地址" class="layui-input">
                    </div>
                </div>
            </div>
            <hr class="layui-bg-gray">
            <div class="layui-col-md12" style="text-align: center">
                <iframe id="mapIframe" src="map.html" scrolling="no" frameborder="0"
                        style="display: inline-block; width: 90%;height: 400px;margin: auto">
                </iframe>
            </div>
        </div>
        <hr class="layui-bg-gray">
        <div class="layui-form-item text-right">
            <button class="layui-btn" lay-filter="editSubmit" lay-submit="">保存</button>
            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button>
        </div>
    </form>
</script>
<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="../../static/js/handlebars/handlebars-v4.5.3.js"></script>
<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/tools/md5.js"></script>
<script type="text/javascript" src="../../static/js/orderTablePakoutAGV.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/orderPakout/agvOut.js" charset="utf-8"></script>
<!--<script type="text/template" id="takeSiteSelectTemplate">-->
<!--    {{#each data}}-->
<!--    <option value="{{siteId}}">{{desc}}</option>-->
<!--    {{/each}}-->
<!--</script>-->
<!-- 项目编辑窗口 -->
<script type="text/html" id="hostEditDialog">
    <form id="hostEditForm" lay-filter="hostEditForm" class="layui-form model-form">
        <input name="id" type="hidden"/>
        <div class="layui-form-item">
            <label class="layui-form-label layui-form-required">项目名称:</label>
            <div class="layui-input-block">
                <input name="name" placeholder="请输入类型名称" class="layui-input"
                       lay-verType="tips" lay-verify="required" required/>
            </div>
        </div>
        <div class="layui-form-item text-right">
            <button class="layui-btn" lay-filter="hostEditSubmit" lay-submit>保存</button>
            <button class="layui-btn layui-btn-primary" type="button" ew-event="closeDialog">取消</button>
        </div>
    </form>
</script>
</body>
<script type="text/template" id="batchStaSelectTpl">
    <option value="">选择出库站</option>
    {{#each list}}
    <option value="{{this}}">{{this}}</option>
    {{/each}}
</script>
</html>
src/main/webapp/views/orderPakout/order.html
@@ -163,6 +163,14 @@
<!--                lay-verify="required"-->
            </div>
        </div>
        <div class="layui-form-item">
            <label class="layui-form-label">出库区域:</label>
            <div class="layui-input-block">
                <select id="itemName" name="itemName" lay-verType="tips">
                    <option value="">请选择区域</option>
                </select>
            </div>
        </div>
        <div class="layui-form-item" style="position: relative;">
            <label class="layui-form-label">单据明细:</label>
            <div class="layui-input-block">