自动化立体仓库 - WMS系统
chen.lin
2026-02-14 371462edc6b3ee1de97c235d4a019b544badda0d
AGV工作档界面和逻辑调整
19个文件已修改
764 ■■■■ 已修改文件
src/main/java/com/zy/asrs/controller/BasWrkStatusController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/TaskController.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/TaskLogController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/WrkMastController.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/TaskService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/MobileServiceImpl.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/TaskServiceImpl.java 194 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/AgvScheduler.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/handler/AgvHandler.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/config/CoolExceptionHandler.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/config/WebConfig.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/system/timer/LicenseTimer.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/cool.js 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/task/task.js 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/taskLog/taskLog.js 95 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/wrkMast/wrkMast.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/pda/login.html 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/task/task.html 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/taskLog/taskLog.html 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/BasWrkStatusController.java
@@ -136,6 +136,26 @@
        return R.ok(result);
    }
    /** AGV任务管理用:仅返回AGV相关工作状态 7、8、9、10 */
    @RequestMapping(value = "/basWrkStatusQueryAgv/auth")
    @ManagerAuth
    public R queryAgv(String condition) {
        EntityWrapper<BasWrkStatus> wrapper = new EntityWrapper<>();
        wrapper.in("wrk_sts", 7L, 8L, 9L, 10L);
        if (!Cools.isEmpty(condition)) {
            wrapper.like("wrk_desc", condition);
        }
        Page<BasWrkStatus> page = basWrkStatusService.selectPage(new Page<>(0, 100), wrapper);
        List<Map<String, Object>> result = new ArrayList<>();
        for (BasWrkStatus basWrkStatus : page.getRecords()) {
            Map<String, Object> map = new HashMap<>();
            map.put("id", basWrkStatus.getWrkSts());
            map.put("value", basWrkStatus.getWrkDesc());
            result.add(map);
        }
        return R.ok(result);
    }
    @RequestMapping(value = "/basWrkStatus/check/column/auth")
    @ManagerAuth
    public R query(@RequestBody JSONObject param) {
src/main/java/com/zy/asrs/controller/TaskController.java
@@ -39,6 +39,9 @@
                  @RequestParam Map<String, Object> param) {
        EntityWrapper<Task> wrapper = new EntityWrapper<>();
        excludeTrash(param);
        // 移除非表字段参数,避免 SQL 报错「列名 wrk_sts_cb 无效」(agv_task 无此列,多为前端下拉描述字段)
        param.remove("wrk_sts_cb");
        param.remove("wrkStsCb");
        convert(param, wrapper);
        wrapper.eq("is_deleted", 0);
        allLike(Task.class, param.keySet(), wrapper, condition);
@@ -58,6 +61,25 @@
                String[] dates = val.split(RANGE_TIME_LINK);
                wrapper.ge(entry.getKey(), DateUtils.convert(dates[0]));
                wrapper.le(entry.getKey(), DateUtils.convert(dates[1]));
            } else if (val.contains(",") && !Cools.isEmpty(val) && !val.equals("null")) {
                // 多值查询:使用 IN 查询(适用于工作状态等多选场景)
                String[] values = val.split(",");
                List<Object> valueList = new ArrayList<>();
                for (String v : values) {
                    v = v.trim();
                    if (!Cools.isEmpty(v) && !v.equals("null")) {
                        // 尝试转换为数字类型(工作状态是Long类型)
                        try {
                            valueList.add(Long.parseLong(v));
                        } catch (NumberFormatException e) {
                            // 如果不是数字,保持字符串类型
                            valueList.add(v);
                        }
                    }
                }
                if (!valueList.isEmpty()) {
                    wrapper.in(entry.getKey(), valueList);
                }
            } else {
                wrapper.like(entry.getKey(), val);
            }
@@ -104,6 +126,9 @@
        } else if (type == 3) {
            taskService.pickWrkMast(workNo, getUserId());
            return R.ok("工作档已拣料");
        } else if (type == 4) {
            taskService.deleteWrkMast(workNo, getUserId());
            return R.ok("工作档已删除");
        }
        return R.ok();
    }
src/main/java/com/zy/asrs/controller/TaskLogController.java
@@ -65,7 +65,6 @@
    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());
            // 跳过空值和 "null" 字符串
            if (Cools.isEmpty(val) || "null".equals(val)) {
                continue;
            }
@@ -73,6 +72,23 @@
                String[] dates = val.split(RANGE_TIME_LINK);
                wrapper.ge(entry.getKey(), DateUtils.convert(dates[0]));
                wrapper.le(entry.getKey(), DateUtils.convert(dates[1]));
            } else if (val.contains(",") && !val.equals("null")) {
                // 多值查询:工作状态等多选用 IN(与 AGV 任务管理一致)
                String[] values = val.split(",");
                List<Object> valueList = new ArrayList<>();
                for (String v : values) {
                    v = v.trim();
                    if (!Cools.isEmpty(v) && !v.equals("null")) {
                        try {
                            valueList.add(Long.parseLong(v));
                        } catch (NumberFormatException e) {
                            valueList.add(v);
                        }
                    }
                }
                if (!valueList.isEmpty()) {
                    wrapper.in(entry.getKey(), valueList);
                }
            } else {
                wrapper.like(entry.getKey(), val);
            }
src/main/java/com/zy/asrs/controller/WrkMastController.java
@@ -1,6 +1,5 @@
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;
@@ -90,19 +89,6 @@
        wrkMast.setModiUser(getUserId());
        wrkMast.setModiTime(new Date());
        wrkMastService.updateById(wrkMast);
        return R.ok();
    }
    @RequestMapping(value = "/wrkMast/delete/auth")
    @ManagerAuth(memo = "工作档删除")
    public R delete(@RequestParam String param) {
        List<WrkMast> list = JSONArray.parseArray(param, WrkMast.class);
        if (Cools.isEmpty(list)) {
            return R.error();
        }
        for (WrkMast entity : list) {
            wrkMastService.delete(new EntityWrapper<>(entity));
        }
        return R.ok();
    }
src/main/java/com/zy/asrs/service/TaskService.java
@@ -13,6 +13,13 @@
    void pickWrkMast(String workNo, Long userId);
    /**
     * 删除AGV任务(尝试取消AGV任务,但不会因为AGV取消失败而阻止删除)
     * @param workNo 工作号
     * @param userId 用户ID
     */
    void deleteWrkMast(String workNo, Long userId);
    List<Task> selectToBeCompleteData();
    List<Task> selectToBeHistoryData();
src/main/java/com/zy/asrs/service/impl/MobileServiceImpl.java
@@ -207,20 +207,6 @@
                    throw new CoolException("条码组托档已存在:" + barcode);
                }
                
                // 检查是否有任何工作档(空托盘不应该有任何工作档,包括已完成的工作档)
                int wrkMastCount = wrkMastService.selectCount(new EntityWrapper<WrkMast>().eq("barcode", barcode));
                if (wrkMastCount != 0) {
                    log.warn("条码任务档已存在,不是空托盘:{}", barcode);
                    throw new CoolException("条码任务档已存在:" + barcode);
                }
                // 检查库位上是否有该条码
                if (locCache != null && !Cools.isEmpty(locCache.getBarcode()) && locCache.getBarcode().equals(barcode)) {
                    log.warn("空托盘入库时,库位上已有该条码,不允许呼叫AGV:库位={},条码={}", sourceSite, barcode);
                    throw new CoolException("条码在库,请更换条码" + barcode);
                }
                // 通过所有检查,确认为空托盘,设置为空托入库
                ioType = 10;
                log.info("确认为空托盘,设置为空托入库,条码:{},ioType:{}", barcode, ioType);
@@ -228,8 +214,6 @@
            default:
                throw new CoolException("入库类型错误,type:" + type);
        }
        // 统一校验:在所有AGV呼叫场景中都需要检查以下条件,有任何一个满足则不允许呼叫AGV
        
        // 1. 校验库位上的条码是否在库:检查LocCache表上是否有该条码,且该条码在LocDetl库存中
        // 注意:空托盘入库(type=3)不校验库存明细,只在校验库位上是否有条码(已在case 3中处理)
@@ -243,26 +227,26 @@
                    throw new CoolException("条码在库,请更换条码" + barcode);
                }
            }
        }else {
            // 检查库位上是否有该条码
            if (locCache != null && !Cools.isEmpty(locCache.getBarcode()) && locCache.getBarcode().equals(barcode)) {
                log.warn("空托盘入库时,库位上已有该条码,不允许呼叫AGV:库位={},条码={}", sourceSite, barcode);
                throw new CoolException("条码在库,请更换条码" + barcode);
            }
        }
        // 2. 校验工作档中运行中的托盘码:检查是否有未完成的工作档(wrk_sts不等于4、14和15)
        // 4=入库完成,14=已出库未确认,15=出库更新完成
        int runningWrkMastCount = wrkMastService.selectCount(new EntityWrapper<WrkMast>()
                .eq("barcode", barcode)
                .notIn("wrk_sts", Arrays.asList(4L, 14L, 15L))); // 不等于入库完成(4)和出库完成(14,15)
        if (runningWrkMastCount > 0) {
            log.warn("AGV呼叫时,托盘码在工作档中运行中,不允许呼叫AGV:{}", barcode);
        // 2. 校验工作档中运行中的托盘码
        int wrkMastCount = wrkMastService.selectCount(new EntityWrapper<WrkMast>().eq("barcode", barcode));
        if (wrkMastCount != 0) {
            log.warn("条码在工作档已存在:{}", barcode);
            throw new CoolException("条码在工作档,请更换条码:" + barcode);
        }
        // 3. 校验AGV任务列表中未完成的托盘码:检查是否有未完成的AGV任务(is_deleted=0且wrk_sts不等于4、14和15)
        // 4=入库完成,14=已出库未确认,15=出库更新完成
        // 3. 校验AGV任务列表中未完成的托盘码:检查是否有未完成的AGV任务(is_deleted=0)
        int unfinishedTaskCount = taskService.selectCount(new EntityWrapper<Task>()
                .eq("barcode", barcode)
                .eq("is_deleted", 0)
                .notIn("wrk_sts", Arrays.asList(4L, 14L, 15L))); // 不等于入库完成(4)和出库完成(14,15)
                .eq("is_deleted", 0));
        if (unfinishedTaskCount > 0) {
            log.warn("AGV呼叫时,托盘码在AGV任务列表中未完成,不允许呼叫AGV:{}", barcode);
            log.warn("AGV呼叫时,条码在AGV任务档,不允许呼叫AGV:{}", barcode);
            throw new CoolException("条码在AGV任务档,请更换条码:" + barcode);
        }
src/main/java/com/zy/asrs/service/impl/TaskServiceImpl.java
@@ -8,6 +8,8 @@
import com.zy.asrs.entity.*;
import com.zy.asrs.mapper.TaskMapper;
import com.zy.asrs.service.*;
import com.zy.asrs.task.handler.AgvHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -17,6 +19,7 @@
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service("taskService")
public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements TaskService {
@@ -36,6 +39,8 @@
    private OrderPakoutService orderPakoutService;
    @Autowired
    private OrderDetlPakoutService orderDetlPakoutService;
    @Autowired
    private AgvHandler agvHandler;
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -143,6 +148,35 @@
        if (Cools.isEmpty(wrkMast)) {
            throw new CoolException(workNo + "工作档不存在");
        }
        // 如果是AGV任务,根据任务状态决定是否需要调用AGV接口取消任务
        // 仅状态7(新建AGV任务)不访问AGV直接取消;其他状态必须先调用AGV取消接口,AGV返回成功才能取消
        if ("agv".equals(wrkMast.getTaskType())) {
            Long wrkSts = wrkMast.getWrkSts();
            if (wrkSts != null && wrkSts == 7L) {
                // 状态7:新建AGV任务,未发送给AGV,无需调用AGV接口,直接取消
                String displayTaskId = (wrkMast.getWrkNo() != null) ? String.valueOf(wrkMast.getWrkNo()) : String.valueOf(wrkMast.getId());
                log.info("取消AGV任务:任务ID:{},状态:7(新建AGV任务),无需调用AGV接口,直接取消", displayTaskId);
            } else {
                // 非状态7(含8、9、10等):必须先调用AGV取消接口,AGV返回成功才能取消
                if (wrkMast.getId() == null) {
                    throw new CoolException("取消AGV任务失败:任务ID为空");
                }
                String agvWrkNo = wrkMast.getAgvWrkNo();
                Integer wrkNo = wrkMast.getWrkNo();
                Long taskId = wrkMast.getId();
                if ((agvWrkNo == null || agvWrkNo.isEmpty()) && wrkNo == null && taskId == null) {
                    throw new CoolException("取消AGV任务失败:无法获取有效的任务标识(agvWrkNo、wrkNo和id都为空)");
                }
                String errorMsg = agvHandler.cancelAgvTask(wrkMast);
                if (errorMsg != null) {
                    log.warn("取消AGV任务失败,AGV未返回成功 - 任务ID:{},错误:{}",
                            (wrkMast.getWrkNo() != null) ? String.valueOf(wrkMast.getWrkNo()) : String.valueOf(wrkMast.getId()), errorMsg);
                    throw new CoolException("取消AGV任务失败:" + errorMsg);
                }
            }
        }
        String locNo = ""; // 待修改目标库位
        String locSts = ""; // 待修改目标库位状态
        // 入库取消(修改目标库位)
@@ -244,6 +278,166 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteWrkMast(String workNo, Long userId) {
        Date now = new Date();
        Task wrkMast = this.selectOne(new EntityWrapper<Task>()
                .eq("wrk_no", workNo)
                .andNew("(is_deleted = 0)"));
        if (Cools.isEmpty(wrkMast)) {
            throw new CoolException(workNo + "工作档不存在");
        }
        String displayTaskId = (wrkMast.getWrkNo() != null) ? String.valueOf(wrkMast.getWrkNo()) : String.valueOf(wrkMast.getId());
        // 如果是AGV任务,尝试同步申请AGV取消(但不会被AGV返回的错误校验拦截,会继续删除)
        if ("agv".equals(wrkMast.getTaskType())) {
            Long wrkSts = wrkMast.getWrkSts();
            // 状态8和9:已发送给AGV任务,尝试调用AGV接口取消任务
            // 状态10:AGV呼叫异常,可以取消,但不会请求AGV取消接口
            // 状态7或更早:还未发送给AGV,无需调用AGV接口
            if (wrkSts != null && wrkSts >= 8L && wrkSts != 10L) {
                try {
                    // 验证任务信息是否完整
                    if (wrkMast.getId() != null) {
                        // 验证taskId是否可用(agvWrkNo、wrkNo或id至少有一个)
                        String agvWrkNo = wrkMast.getAgvWrkNo();
                        Integer wrkNo = wrkMast.getWrkNo();
                        Long taskId = wrkMast.getId();
                        if ((agvWrkNo != null && !agvWrkNo.isEmpty()) || wrkNo != null || taskId != null) {
                            // 同步申请AGV取消,但不抛出异常(不会被错误校验拦截)
                            String errorMsg = agvHandler.cancelAgvTask(wrkMast);
                            if (errorMsg != null) {
                                log.warn("删除AGV任务:尝试取消AGV任务失败,但继续执行删除操作,任务ID:{},错误:{}", displayTaskId, errorMsg);
                            } else {
                                log.info("删除AGV任务:成功取消AGV任务,任务ID:{}", displayTaskId);
                            }
                        } else {
                            log.warn("删除AGV任务:无法获取有效的任务标识(agvWrkNo、wrkNo和id都为空),跳过AGV取消,继续执行删除操作,任务ID:{}", displayTaskId);
                        }
                    } else {
                        log.warn("删除AGV任务:任务ID为空,跳过AGV取消,继续执行删除操作,任务ID:{}", displayTaskId);
                    }
                } catch (Exception e) {
                    // 捕获所有异常,记录日志但不阻止删除操作(不会被AGV返回的错误校验拦截)
                    log.error("删除AGV任务:尝试取消AGV任务时发生异常,但继续执行删除操作,任务ID:{}", displayTaskId, e);
                }
            } else {
                // 状态10(AGV呼叫异常)或状态7或更早,无需调用AGV接口
                if (wrkSts != null && wrkSts == 10L) {
                    log.info("删除AGV任务:任务ID:{},状态:{}(AGV呼叫异常),无需调用AGV接口,直接删除", displayTaskId, wrkSts);
                } else {
                    log.info("删除AGV任务:任务ID:{},状态:{}(待呼叫AGV或更早),无需调用AGV接口,直接删除", displayTaskId, wrkSts);
                }
            }
        }
        // 释放库位等资源(类似cancelWrkMast的逻辑)
        String locNo = ""; // 待修改目标库位
        String locSts = ""; // 待修改目标库位状态
        // 入库取消(修改目标库位)
        if (wrkMast.getIoType() < 100) {
            locNo = wrkMast.getLocNo();
            locSts = "O";
            // 库位转移
            if (wrkMast.getIoType() == 11) {
                // 库位转移:源库位
                LocCache locMast = locCacheService.selectOne(new EntityWrapper<LocCache>().eq("loc_no", wrkMast.getSourceLocNo()));
                if (!Cools.isEmpty(locMast)) {
                    locMast.setLocSts(wrkMast.getFullPlt().equalsIgnoreCase("N") ? "D" : "F");
                    locMast.setModiTime(now);
                    locMast.setModiUser(userId);
                    locCacheService.updateById(locMast);
                }
            }
            // 出库取消(修改源库位)
        } else {
            locNo = wrkMast.getSourceLocNo();
            // 出库 ===>> F.在库
            if (wrkMast.getIoType() > 100 && wrkMast.getIoType() != 110) {
                locSts = "F";
                // 空板出库 ===>> D.空桶/空栈板
            } else if (wrkMast.getIoType() == 110) {
                locSts = "D";
                // 库位转移 ===>> D.空桶/空栈板
            } else if (wrkMast.getIoType() == 11) {
                locSts = wrkMast.getFullPlt().equalsIgnoreCase("N") ? "D" : "F";
                // 库位转移:目标库位
                LocCache locMast = locCacheService.selectOne(new EntityWrapper<LocCache>().eq("loc_no", wrkMast.getLocNo()));
                if (!Cools.isEmpty(locMast)) {
                    locMast.setLocSts("O");
                    locMast.setModiTime(now);
                    locMast.setModiUser(userId);
                    locCacheService.updateById(locMast);
                }
            }
        }
        //取消入库工作档时,查询组托表,如果有将状态改为待处理
        if (wrkMast.getIoType() == 1) {
            List<WaitPakin> waitPakins = waitPakinService.selectList(new EntityWrapper<WaitPakin>().eq("zpallet", wrkMast.getBarcode()));
            for (WaitPakin waitPakin : waitPakins) {
                if (!Cools.isEmpty(waitPakin)) {
                    waitPakin.setIoStatus("N");
                    waitPakin.setLocNo("");
                    waitPakinService.update(waitPakin, new EntityWrapper<WaitPakin>()
                            .eq("zpallet", waitPakin.getZpallet())
                            .eq("matnr", waitPakin.getMatnr())
                            .eq("batch", waitPakin.getBatch()));
                }
            }
        }
        // 删除操作人员记录
        wrkMast.setManuType("手动删除");
        wrkMast.setModiUser(userId);
        wrkMast.setModiTime(now);
        // 逻辑删除工作主档
        wrkMast.setIsDeleted(1);
        if (!taskService.updateById(wrkMast)) {
            throw new CoolException("删除工作档失败");
        }
        if (wrkMast.getIoType() != 10 && wrkMast.getIoType() != 110) {
            // 删除工作档明细
            taskDetlService.delete(new EntityWrapper<TaskDetl>().eq("wrk_no", workNo));
        }
        // 修改库位状态(如果库位不为空)
        if (!Cools.isEmpty(locNo) && wrkMast.getIoType() > 100) {
            LocCache locMast = locCacheService.selectOne(new EntityWrapper<LocCache>().eq("loc_no", locNo));
            if (!Cools.isEmpty(locMast) && !Cools.isEmpty(locSts)) {
                locMast.setLocSts(locSts);
                locMast.setModiTime(now);
                locMast.setModiUser(userId);
                locCacheService.updateById(locMast);
            }
        }
        // 如果是入库任务,释放预约库位
        if (wrkMast.getIoType() != null && wrkMast.getIoType() < 100 && !Cools.isEmpty(wrkMast.getLocNo())) {
            try {
                LocCache locCache = locCacheService.selectOne(
                        new EntityWrapper<LocCache>().eq("loc_no", wrkMast.getLocNo())
                );
                if (locCache != null && "S".equals(locCache.getLocSts())) {
                    // 库位状态为S(入库预约),释放为O(闲置)
                    locCache.setLocSts("O");
                    locCache.setModiTime(now);
                    locCache.setModiUser(userId);
                    locCacheService.updateById(locCache);
                    log.info("删除AGV任务:已释放预约库位:{}(S→O),任务ID:{}", wrkMast.getLocNo(), displayTaskId);
                }
            } catch (Exception e) {
                log.warn("删除AGV任务:释放预约库位失败,库位:{},任务ID:{}", wrkMast.getLocNo(), displayTaskId, e);
            }
        }
        log.info("删除AGV任务成功:任务ID:{},工作号:{}", displayTaskId, workNo);
    }
    @Override
    public List<Task> selectToBeHistoryData() {
        return this.baseMapper.selectToBeHistoryData();
    }
src/main/java/com/zy/asrs/task/AgvScheduler.java
@@ -27,6 +27,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
@@ -90,7 +91,7 @@
     * 每次只处理一个任务,避免高并发执行
     * 使用AtomicBoolean确保单线程执行循环
     */
    @Scheduled(cron = "0/5 * * * * ? ")
    @Scheduled(cron = "0/15 * * * * ? ")
    private void allocateSite() {
        if (!schedulerProperties.isEnabled()) {
            log.debug("定时任务allocateSite:调度器未启用,跳过执行");
@@ -276,7 +277,7 @@
        if (!schedulerProperties.isEnabled()) {
            return;
        }
        List<Task> taskList = taskService.selectList(new EntityWrapper<Task>().eq("wrk_sts", 9).eq("is_deleted", 0));
        List<Task> taskList = taskService.selectList(new EntityWrapper<Task>().in("wrk_sts", 9L,10L).eq("is_deleted", 0));
        if (taskList.isEmpty()) {
            return;
        }
@@ -538,12 +539,33 @@
                    );
                }
                // 如果通过wrk_no没找到,且有条码,则通过条码查询
                // 注意:通过条码查询时,需要验证工作档是否真的已完成(通过wrk_no匹配),避免重复条码导致误判
                if (wrkMastLog == null && !Cools.isEmpty(agvTask.getBarcode())) {
                    List<WrkMastLog> logList = wrkMastLogService.selectList(
                            new EntityWrapper<WrkMastLog>().eq("barcode", agvTask.getBarcode())
                    );
                    if (!logList.isEmpty()) {
                        wrkMastLog = logList.get(0); // 取第一个
                    // 构建查询条件:条码匹配,且历史档创建时间在AGV订单创建时间之后,且不超过AGV创建时间之后6小时
                    Wrapper<WrkMastLog> logWrapper = new EntityWrapper<WrkMastLog>()
                            .eq("barcode", agvTask.getBarcode());
                    // 如果AGV订单有创建时间,添加时间过滤条件
                    if (agvTask.getAppeTime() != null) {
                        // 计算AGV创建时间之后6小时的时间
                        Calendar calendar = Calendar.getInstance();
                        calendar.setTime(agvTask.getAppeTime());
                        calendar.add(Calendar.HOUR_OF_DAY, 6); // 加6小时
                        Date endTime = calendar.getTime();
                        // 查询条件:历史档创建时间 >= AGV订单创建时间 且 <= AGV创建时间之后6小时
                        logWrapper = logWrapper.ge("appe_time", agvTask.getAppeTime())
                                .le("appe_time", endTime)
                                .eq("barcode", agvTask.getBarcode())
                        ;
                    }
                    List<WrkMastLog> logList = wrkMastLogService.selectList(logWrapper);
                    // 过滤:只保留工作号匹配的历史档,避免重复条码导致误判
                    for (WrkMastLog log : logList) {
                        if (agvTask.getWrkNo() != null && log.getWrkNo() != null &&
                                log.getWrkNo().equals(agvTask.getWrkNo())) {
                            wrkMastLog = log;
                            break;
                        }
                    }
                }
@@ -607,13 +629,11 @@
                String displayTaskId = (agvTask.getWrkNo() != null) ? String.valueOf(agvTask.getWrkNo()) : String.valueOf(agvTask.getId());
                // 如果订单创建超过五分钟,查询AGV订单状态
                if (shouldCheckAgvStatus) {
                    String agvOrderStatus = queryAgvOrderStatus(agvTask, displayTaskId);
                    if (agvOrderStatus != null) {
                        // 根据订单状态处理
                        boolean shouldComplete = processAgvOrderStatus(agvTask, agvOrderStatus, displayTaskId, now);
                        if (shouldComplete) {
                            completedTasks.add(agvTask);
                        }
                    // queryAgvOrderStatus内部已经调用processAgvOrderStatus进行归类处理
                    // 返回true表示应该完结订单,添加到完成列表
                    boolean shouldComplete = queryAgvOrderStatus(agvTask, displayTaskId);
                    if (shouldComplete) {
                        completedTasks.add(agvTask);
                    }
                }
@@ -638,12 +658,12 @@
    }
    /**
     * 查询AGV订单状态
     * 查询AGV订单状态并归类处理
     * @param agvTask AGV任务
     * @param displayTaskId 显示的任务ID
     * @return 订单状态(Building/Created/Assigned/Failed/Done/Cancelled),如果查询失败返回null
     * @return true表示应该完结订单,false表示不应该完结(跳过),如果查询失败返回false
     */
    private String queryAgvOrderStatus(Task agvTask, String displayTaskId) {
    private boolean queryAgvOrderStatus(Task agvTask, String displayTaskId) {
        try {
            // 构建订单ID,优先使用agvWrkNo,如果为空则使用T+wrkNo(向后兼容)
            String orderId = agvTask.getAgvWrkNo();
@@ -656,7 +676,7 @@
                    agvTask.setErrorMemo2(errorMsg);
                    agvTask.setErrorTime2(new Date());
                    taskService.updateById(agvTask);
                    return null;
                    return false;
                }
            }
@@ -687,7 +707,7 @@
                agvTask.setErrorMemo2(errorMsg);
                agvTask.setErrorTime2(new Date());
                taskService.updateById(agvTask);
                return null;
                return false;
            }
            // 解析响应
@@ -697,7 +717,7 @@
                agvTask.setErrorMemo2(errorMsg);
                agvTask.setErrorTime2(new Date());
                taskService.updateById(agvTask);
                return null;
                return false;
            }
            try {
@@ -712,7 +732,7 @@
                    agvTask.setErrorMemo2(errorMsg);
                    agvTask.setErrorTime2(new Date());
                    taskService.updateById(agvTask);
                    return null;
                    return false;
                }
                // 获取订单信息
@@ -721,14 +741,17 @@
                    String status = entityValue.getString("status");
                    if (status != null) {
                        log.info("查询AGV订单状态成功 - 任务ID:{},订单ID:{},状态:{}", displayTaskId, orderId, status);
                        return status;
                        // 查询后使用processAgvOrderStatus方法归类
                        Date now = new Date();
                        boolean shouldComplete = processAgvOrderStatus(agvTask, status, displayTaskId, now);
                        return shouldComplete;
                    } else {
                        String errorMsg = String.format("查询AGV订单状态响应中缺少status字段,请求:%s,响应:%s", requestBody, response);
                        log.warn("查询AGV订单状态失败 - 任务ID:{},订单ID:{},{}", displayTaskId, orderId, errorMsg);
                        agvTask.setErrorMemo2(errorMsg);
                        agvTask.setErrorTime2(new Date());
                        taskService.updateById(agvTask);
                        return null;
                        return false;
                    }
                } else {
                    String errorMsg = String.format("查询AGV订单状态响应中缺少entityValue字段,请求:%s,响应:%s", requestBody, response);
@@ -736,7 +759,7 @@
                    agvTask.setErrorMemo2(errorMsg);
                    agvTask.setErrorTime2(new Date());
                    taskService.updateById(agvTask);
                    return null;
                    return false;
                }
            } catch (com.alibaba.fastjson.JSONException e) {
                String errorMsg = String.format("解析AGV订单状态响应JSON失败:%s,请求:%s,响应:%s", e.getMessage(), requestBody, response);
@@ -744,7 +767,7 @@
                agvTask.setErrorMemo2(errorMsg);
                agvTask.setErrorTime2(new Date());
                taskService.updateById(agvTask);
                return null;
                return false;
            }
        } catch (Exception e) {
            String errorMsg = String.format("查询AGV订单状态异常:%s", e.getMessage());
@@ -752,7 +775,7 @@
            agvTask.setErrorMemo2(errorMsg);
            agvTask.setErrorTime2(new Date());
            taskService.updateById(agvTask);
            return null;
            return false;
        }
    }
src/main/java/com/zy/asrs/task/handler/AgvHandler.java
@@ -3,28 +3,20 @@
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.core.common.Cools;
import com.zy.asrs.entity.Task;
import com.zy.asrs.entity.TaskLog;
import com.zy.asrs.entity.WrkMast;
import com.zy.asrs.entity.BasDevp;
import com.zy.asrs.entity.LocCache;
import com.zy.asrs.entity.*;
import com.zy.asrs.mapper.BasDevpMapper;
import com.zy.asrs.mapper.BasStationMapper;
import com.zy.asrs.mapper.WrkMastMapper;
import com.zy.asrs.service.ApiLogService;
import com.zy.asrs.service.LocCacheService;
import com.zy.asrs.service.TaskLogService;
import com.zy.asrs.service.TaskService;
import com.zy.asrs.service.WrkMastService;
import com.zy.asrs.service.WrkMastLogService;
import com.zy.asrs.entity.WrkMastLog;
import com.zy.asrs.service.*;
import com.zy.common.constant.ApiInterfaceConstant;
import com.zy.common.properties.AgvProperties;
import com.zy.common.utils.AgvUtils;
import com.zy.common.utils.HttpHandler;
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;
@@ -72,7 +64,8 @@
    @Resource
    private WrkMastLogService wrkMastLogService;
    @Resource
    private LocMastService locMastService;
    /**
     * 站点轮询计数器,用于平均分配站点
     * Key: 站点组标识(如 "east" 或 "west"),Value: 当前轮询索引
@@ -91,6 +84,7 @@
     * @param taskList 任务列表(通常只包含一个任务)
     * @return 是否成功处理了任务(成功呼叫AGV,状态从7变为8)
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean callAgv(List<Task> taskList) {
        // 记录调用堆栈,确保只能从定时任务调用
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
@@ -515,24 +509,36 @@
            WrkMast wrkMast = null;
            if (agvTask.getWrkNo() != null) {
                wrkMast = wrkMastService.selectOne(
                        new EntityWrapper<WrkMast>().eq("wrk_no", agvTask.getWrkNo())
                        new EntityWrapper<WrkMast>()
                                .eq("barcode", agvTask.getBarcode())
                );
            }
            // 检查历史档是否存在
            WrkMastLog wrkMastLog = null;
            if (agvTask.getWrkNo() != null) {
                wrkMastLog = wrkMastLogService.selectOne(
                        new EntityWrapper<WrkMastLog>().eq("wrk_no", agvTask.getWrkNo())
            if (agvTask.getBarcode() != null) {
                Wrapper<WrkMastLog> wrkMastLogMapper = new EntityWrapper<WrkMastLog>()
                        .eq("barcode", agvTask.getBarcode());
                if (agvTask.getAppeTime() != null) {
                    // 计算AGV创建时间之后6小时的时间
                    Calendar calendar = Calendar.getInstance();
                    calendar.setTime(agvTask.getAppeTime());
                    calendar.add(Calendar.HOUR_OF_DAY, 6); // 加6小时
                    Date endTime = calendar.getTime();
                    // 查询条件:历史档创建时间 >= AGV订单创建时间 且 <= AGV创建时间之后6小时
                    wrkMastLogMapper = wrkMastLogMapper.ge("appe_time", agvTask.getAppeTime())
                            .le("appe_time", endTime).eq("barcode", agvTask.getBarcode()).orderBy("appe_time DESC");
                }
                List<WrkMastLog> wrkMastLogList = wrkMastLogService.selectList(
                        wrkMastLogMapper
                );
            }
            // 如果通过wrk_no没找到,且有条码,则通过条码查询
            if (wrkMastLog == null && !Cools.isEmpty(agvTask.getBarcode())) {
                List<WrkMastLog> logList = wrkMastLogService.selectList(
                        new EntityWrapper<WrkMastLog>().eq("barcode", agvTask.getBarcode())
                );
                if (!logList.isEmpty()) {
                    wrkMastLog = logList.get(0);
                if(wrkMastLogList.size()==1){
                    wrkMastLog=wrkMastLogList.get(0);
                }else if(wrkMastLogList.size()>1){
                    int locmastCount = locMastService.selectCount(new EntityWrapper<LocMast>().eq("loc_sts", "F").eq("barcode", agvTask.getBarcode()));
                        if(locmastCount>0){
                            wrkMastLog=wrkMastLogList.get(0);
                        }
                }
            }
@@ -1075,16 +1081,16 @@
     * 取消AGV任务(仙工M4接口)
     *
     * @param task 任务对象
     * @return 是否成功
     * @return 成功返回 null,失败返回错误原因
     */
    public boolean cancelAgvTask(Task task) {
    public String cancelAgvTask(Task task) {
        if (!agvProperties.isSendTask()) {
            return false;
            return "未启用AGV任务下发";
        }
        if (task == null || task.getId() == null) {
            log.error("取消AGV任务失败:任务或任务ID为空");
            return false;
            return "任务或任务ID为空";
        }
        String response = "";
@@ -1147,10 +1153,16 @@
                String displayTaskId = (task.getWrkNo() != null) ? String.valueOf(task.getWrkNo()) : String.valueOf(task.getId());
                log.info(namespace + "取消AGV任务成功:{}", displayTaskId);
            } else {
                String errMsg = jsonObject.getString("message");
                if (errMsg == null || errMsg.isEmpty()) {
                    errMsg = "response: " + response;
                }
                log.error(namespace + "取消AGV任务失败!!!url:{};request:{};response:{}", url, body, response);
                return errMsg;
            }
        } catch (Exception e) {
            log.error(namespace + "取消AGV任务异常", e);
            return e.getMessage() != null ? e.getMessage() : "取消AGV任务异常";
        } finally {
            try {
                // 保存接口日志
@@ -1168,6 +1180,6 @@
            }
        }
        return success;
        return success ? null : "取消AGV任务失败";
    }
}
src/main/java/com/zy/common/config/CoolExceptionHandler.java
@@ -3,6 +3,7 @@
import com.core.common.R;
import com.core.exception.CoolException;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.ClientAbortException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
src/main/java/com/zy/common/config/WebConfig.java
@@ -24,7 +24,7 @@
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(adminInterceptor)
                .addPathPatterns("/**").excludePathPatterns("/license/getServerlnfos")
                .addPathPatterns("/**").excludePathPatterns("/license/getServerInfos")
        ;
    }
src/main/java/com/zy/system/timer/LicenseTimer.java
@@ -58,9 +58,6 @@
    //每天晚上11点更新系统激活状态
    @Scheduled(cron = "0 0 23 * * ? ")
    public void timer() {
        if (!schedulerProperties.isEnabled()) {
            return;
        }
        try {
            getRemoteLicense();
        } catch (Exception e) {
src/main/webapp/static/js/cool.js
@@ -79,14 +79,39 @@
    var cacd = cacw.parent().find(".cool-auto-complete-div");
    var realDom = cacd.prev();
    // id字段
    var selectOptionDom = selectDom.find("option:selected");
    var html = selectOptionDom.html();
    if (html === "取消选择") {
        cacd.val("");
        realDom.val("");
    var selectOptionDoms = selectDom.find("option:selected");
    // 检查是否是多选
    if (selectDom.attr("multiple") === "multiple" || selectDom.attr("multiple") === true) {
        // 多选处理
        var selectedValues = [];
        var selectedTexts = [];
        selectOptionDoms.each(function() {
            var option = $(this);
            var html = option.html();
            if (html !== "取消选择") {
                selectedValues.push(option.attr("title"));
                selectedTexts.push(html);
            }
        });
        if (selectedValues.length === 0) {
            cacd.val("");
            realDom.val("");
        } else {
            cacd.val(selectedTexts.join(", "));
            realDom.val(selectedValues.join(","));
        }
    } else {
        cacd.val(selectOptionDom.html());
        realDom.val(selectOptionDom.attr("title"));
        // 单选处理(原有逻辑)
        var html = selectOptionDoms.html();
        if (html === "取消选择") {
            cacd.val("");
            realDom.val("");
        } else {
            cacd.val(selectOptionDoms.html());
            realDom.val(selectOptionDoms.attr("title"));
        }
    }
    inputDom.val("");
}
src/main/webapp/static/js/task/task.js
@@ -1,4 +1,5 @@
var pageCurr;
var tableIns;
layui.config({
    base: baseUrl + "/static/layui/lay/modules/"
}).use(['table', 'laydate', 'form', 'admin'], function () {
@@ -9,11 +10,68 @@
    var form = layui.form;
    var admin = layui.admin;
    // 数据渲染
    tableIns = table.render({
    // AGV任务管理:工作状态用多选框(勾选多项),仅本页;用原生 checkbox 不经过 layui 美化,保证勾选与框内显示始终一致
    function updateTaskWrkStsFromCheckbox() {
        var ids = [], texts = [];
        $('#taskWrkStsCheckboxWrap input:checkbox:checked').each(function () {
            var $input = $(this);
            ids.push($input.val());
            var text = $input.data('text') || $input.closest('label').clone().children().remove().end().text().trim() || $input.val();
            texts.push(text);
        });
        $('input[name="wrk_sts"]').val(ids.join(','));
        $('#wrkSts\\$').val(texts.join(', '));
    }
    $(document).on('change', '#taskWrkStsCheckboxWrap input:checkbox', updateTaskWrkStsFromCheckbox);
    function initWrkStsDropdownThenTable() {
        $.ajax({
            url: baseUrl + '/basWrkStatusQueryAgv/auth',
            headers: {token: localStorage.getItem('token')},
            data: {condition: ''},
            method: 'POST',
            traditional: true,
            success: function (res) {
                var $wrap = $('#taskWrkStsCheckboxWrap');
                $wrap.empty();
                var defaultIds = {'7': true, '8': true, '9': true};
                if (res.code === 200 && res.data && res.data.length) {
                    res.data.forEach(function (item) {
                        var id = String(item.id);
                        var text = item.value || id;
                        var checked = defaultIds[id] ? ' checked' : '';
                        $wrap.append(
                            '<label class="task-wrksts-cb-label" style="display: inline-block; margin: 4px 12px 4px 0; cursor: pointer;"><input type="checkbox" value="' + id + '" data-text="' + (text.replace(/"/g, '&quot;')) + '"' + checked + '> ' + text + '</label>'
                        );
                    });
                    updateTaskWrkStsFromCheckbox();
                }
                // 点击输入框展开/收起下拉(不调用 autoShow,避免走全局逻辑)
                $('#wrkSts\\$').off('click').on('click', function () {
                    var $win = $('#taskWrkStsWindow');
                    $win.toggle();
                });
                var initialWhere = {};
                $.each($('#search-box [name]').serializeArray(), function () {
                    initialWhere[this.name] = this.value;
                });
                renderTaskTable(initialWhere);
            },
            error: function () {
                renderTaskTable({});
            }
        });
    }
    function renderTaskTable(initialWhere) {
        tableIns = table.render({
        elem: '#task',
        headers: {token: localStorage.getItem('token')},
        url: baseUrl + '/task/list/auth',
        where: initialWhere,
        page: true,
        limit: 15,
        limits: [15, 30, 50, 100, 200, 500],
@@ -35,8 +93,8 @@
            , {field: 'staNo$', align: 'center', title: '目标站', width: 120}
            , {field: 'locNo', align: 'center', title: '目标库位', width: 120}
            , {field: 'barcode', align: 'center', title: '条码', width: 110}
            , {field: 'errorMemo', align: 'center', title: '错误原因', width: 200}
            , {field: 'errorMemo2', align: 'center', title: '错误原因2', width: 200, hide: true}
            , {field: 'errorMemo', align: 'center', title: 'AGV回复报文', width: 200, hide: true}
            , {field: 'errorMemo2', align: 'center', title: 'AGV回复报文2', width: 200, hide: true}
            , {field: 'errorTime$', align: 'center', title: '错误时间', width: 160, hide: true}
            , {field: 'preHave', align: 'center', title: '先入品', hide: true}
            , {field: 'takeNone', align: 'center', title: '空操作', hide: true}
@@ -66,7 +124,10 @@
            pageCurr = curr;
            limit();
        }
    });
        });
    }
    initWrkStsDropdownThenTable();
    // 监听排序事件
    table.on('sort(task)', function (obj) {
@@ -225,6 +286,34 @@
                }, function () {
                });
                break;
            //  删除(单条)
            case 'delete':
                layer.confirm('确定要删除该条AGV任务吗?', {
                    title: '工作号:' + data.wrkNo,
                    shadeClose: true
                }, function (i) {
                    layer.close(i);
                    var loadIndex = layer.load(2);
                    $.ajax({
                        url: baseUrl + "/task/delete/auth",
                        headers: {'token': localStorage.getItem('token')},
                        data: {'ids[]': [data.id]},
                        traditional: true,
                        method: 'POST',
                        success: function (res) {
                            layer.close(loadIndex);
                            if (res.code === 200) {
                                layer.msg(res.msg || '删除成功', {icon: 1});
                                tableReload();
                            } else if (res.code === 403) {
                                top.location.href = baseUrl + "/";
                            } else {
                                layer.msg(res.msg, {icon: 2});
                            }
                        }
                    });
                });
                break;
        }
    });
@@ -278,7 +367,7 @@
            $.ajax({
                url: baseUrl + "/task/delete/auth",
                headers: {'token': localStorage.getItem('token')},
                data: {ids: ids},
                data: {'ids[]': ids},
                method: 'POST',
                success: function (res) {
                    layer.close(loadIndex);
@@ -452,6 +541,7 @@
});
function tableReload(child) {
    if (!tableIns) return;
    var searchData = {};
    $.each($('#search-box [name]').serializeArray(), function () {
        searchData[this.name] = this.value;
src/main/webapp/static/js/taskLog/taskLog.js
@@ -1,6 +1,7 @@
var pageCurr;
var wrkNo;
var ioTime;
var tableIns;
layui.use(['table', 'laydate', 'form'], function () {
    var table = layui.table;
    var $ = layui.jquery;
@@ -8,22 +9,75 @@
    var layDate = layui.laydate;
    var form = layui.form;
    // 数据渲染
    tableIns = table.render({
        elem: '#taskLog',
        headers: { token: localStorage.getItem('token') },
        url: baseUrl + '/taskLog/list/auth',
        page: true,
        limit: 16,
        limits: [16, 30, 50, 100, 200, 500],
    // AGV工作档日志:工作状态多选(与AGV任务管理一致),默认 7、8、9
    function updateTaskLogWrkStsFromCheckbox() {
        var ids = [], texts = [];
        $('#taskLogWrkStsCheckboxWrap input:checkbox:checked').each(function () {
            var $input = $(this);
            ids.push($input.val());
            var text = $input.data('text') || $input.closest('label').clone().children().remove().end().text().trim() || $input.val();
            texts.push(text);
        });
        $('#taskLogWrkSts').val(ids.join(','));
        $('#taskLogWrkStsDisp').val(texts.join(', '));
    }
    $(document).on('change', '#taskLogWrkStsCheckboxWrap input:checkbox', updateTaskLogWrkStsFromCheckbox);
    function initTaskLogWrkStsThenTable() {
        $.ajax({
            url: baseUrl + '/basWrkStatusQueryAgv/auth',
            headers: { token: localStorage.getItem('token') },
            data: { condition: '' },
            method: 'POST',
            traditional: true,
            success: function (res) {
                var $wrap = $('#taskLogWrkStsCheckboxWrap');
                $wrap.empty();
                var defaultIds = { '7': true, '8': true, '9': true };
                if (res.code === 200 && res.data && res.data.length) {
                    res.data.forEach(function (item) {
                        var id = String(item.id);
                        var text = item.value || id;
                        var checked = defaultIds[id] ? ' checked' : '';
                        $wrap.append(
                            '<label class="taskLog-wrksts-cb-label" style="display: inline-block; margin: 4px 12px 4px 0; cursor: pointer;"><input type="checkbox" value="' + id + '" data-text="' + (text.replace(/"/g, '&quot;')) + '"' + checked + '> ' + text + '</label>'
                        );
                    });
                    updateTaskLogWrkStsFromCheckbox();
                }
                $('#taskLogWrkStsDisp').off('click').on('click', function () {
                    $('#taskLogWrkStsWindow').toggle();
                });
                var initialWhere = {};
                $.each($('#search-box [name]').serializeArray(), function () {
                    initialWhere[this.name] = this.value;
                });
                renderTaskLogTable(initialWhere);
            },
            error: function () {
                renderTaskLogTable({});
            }
        });
    }
    function renderTaskLogTable(initialWhere) {
        tableIns = table.render({
            elem: '#taskLog',
            headers: { token: localStorage.getItem('token') },
            url: baseUrl + '/taskLog/list/auth',
            where: initialWhere || {},
            page: true,
            limit: 15,
        limits: [15, 30, 50, 100, 200, 500],
        even: true,
        toolbar: '#toolbar',
        cellMinWidth: 50,
        height: 'full-120',
        cols: [[
            // {field: 'id', align: 'center', title: 'id'}
            { field: 'wrkNo', align: 'center', title: '工作号', sort: true, width: 105 }
            , { field: 'ioTime$', align: 'center', title: '工作时间', }
            , { field: 'wrkSts$', align: 'center', title: '工作状态', }
            , { field: 'agvWrkNo', align: 'center', title: 'AGV工作号', width: 180 }
            , { field: 'ioTime$', align: 'center', title: '工作时间', width: 160 }
            , { field: 'wrkSts$', align: 'center', title: '工作状态', width: 150 }
            , { field: 'ioType$', align: 'center', title: '入出库类型', width: 150 }
            , { field: 'ioPri', align: 'center', title: '优先级', width: 80 }
            , { field: 'taskType$', align: 'center', title: '任务类型', width: 120 }
@@ -33,11 +87,14 @@
            , { field: 'staNo$', align: 'center', title: '目标站', width: 120 }
            , { field: 'locNo', align: 'center', title: '目标库位', width: 120 }
            , { field: 'barcode', align: 'center', title: '条码', width: 110 }
            , { field: 'errorMemo', align: 'center', title: 'AGV回复报文', width: 200, hide: true }
            , { field: 'errorMemo2', align: 'center', title: 'AGV回复报文2', width: 200, hide: true }
            , { field: 'errorTime$', align: 'center', title: '错误时间', width: 160, hide: true }
            , { field: 'preHave', align: 'center', title: '先入品', hide: true }
            , { field: 'takeNone', align: 'center', title: '空操作', hide: true }
            , { field: 'modiUser$', align: 'center', title: '修改人员', hide: true }
            , { field: 'modiTime$', align: 'center', title: '修改时间', hide: true, width: 160 }
            , { fixed: 'right', title: '操作', align: 'center', toolbar: '#operate', width: 200 }
            , { fixed: 'right', title: '操作', align: 'center', toolbar: '#operate', width: 250 }
        ]],
        request: {
            pageName: 'curr',
@@ -78,7 +135,10 @@
                }
            });
        }
    });
        });
    }
    initTaskLogWrkStsThenTable();
    // 监听排序事件
    table.on('sort(taskLog)', function (obj) {
@@ -250,6 +310,9 @@
    form.on('submit(reset)', function (data) {
        pageCurr = 1;
        clearFormVal($('#search-box'));
        $('#taskLogWrkStsCheckboxWrap input:checkbox').prop('checked', false);
        $('#taskLogWrkSts').val('');
        $('#taskLogWrkStsDisp').val('');
        tableReload(false);
    });
@@ -316,11 +379,13 @@
});
function tableReload(child) {
    var ins = child ? parent.tableIns : tableIns;
    if (!ins) return;
    var searchData = {};
    $.each($('#search-box [name]').serializeArray(), function () {
        searchData[this.name] = this.value;
    });
    (child ? parent.tableIns : tableIns).reload({
    ins.reload({
        where: searchData,
        page: {
            curr: pageCurr
@@ -331,7 +396,7 @@
            }
            pageCurr = curr;
            if (res.data.length === 0 && count !== 0) {
                tableIns.reload({
                ins.reload({
                    where: searchData,
                    page: {
                        curr: pageCurr - 1
src/main/webapp/static/js/wrkMast/wrkMast.js
@@ -21,7 +21,6 @@
        cols: [[
            {type: 'checkbox'}
            ,{field: 'wrkNo', align: 'center',title: '工作号',sort: true, width: 95}
            ,{field: 'agvWrkNo', align: 'center',title: 'AGV工作号', width: 180}
            ,{field: 'ioTime$', align: 'center',title: '工作时间',sort: true, width: 170}
            ,{field: 'wrkSts$', align: 'center',title: '工作状态', width: 120}
            ,{field: 'ioType$', align: 'center',title: '入出库类型', width: 140}
src/main/webapp/views/pda/login.html
@@ -120,7 +120,6 @@
                username: username,
                password: hex_md5(password)
            };
            debugger
            $.ajax({
                url: baseUrl+"/login.action",
                data: user,
src/main/webapp/views/task/task.html
@@ -9,6 +9,16 @@
    <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">
    <style>
        /* AGV任务管理-工作状态多选:选中后高亮 */
        .task-wrksts-cb-wrap .task-wrksts-cb-label:has(input:checked) {
            color: #1890ff;
            font-weight: 500;
            background-color: #e6f7ff;
            border-radius: 4px;
            padding: 2px 6px;
        }
    </style>
</head>
<body>
@@ -22,9 +32,19 @@
                            <input class="layui-input" type="text" name="id" placeholder="编号" autocomplete="off">
                        </div>
                    </div>
                    <div class="layui-inline">
                        <div class="layui-input-inline cool-auto-complete" id="taskWrkStsWrap">
                            <input id="wrkSts" class="layui-input" name="wrk_sts" type="text" placeholder="请输入" autocomplete="off" style="display: none">
                            <input id="wrkSts$" class="layui-input cool-auto-complete-div" type="text" placeholder="工作状态" readonly onfocus="this.blur()">
                            <div class="cool-auto-complete-window" id="taskWrkStsWindow" style="display: none;">
                                <div style="padding: 6px 8px 0; color: #999; font-size: 12px;">可多选,直接点击勾选</div>
                                <div id="taskWrkStsCheckboxWrap" class="task-wrksts-cb-wrap" style="padding: 8px; max-height: 200px; overflow-y: auto;"></div>
                            </div>
                        </div>
                    </div>
                    <div class="layui-inline" style="width: 300px">
                        <div class="layui-input-inline">
                            <input class="layui-input layui-laydate-range" name="create_time" type="text"
                            <input class="layui-input layui-laydate-range" name="appe_time" type="text"
                                   placeholder="起始时间 - 终止时间" autocomplete="off" style="width: 300px">
                        </div>
                    </div>
@@ -89,6 +109,12 @@
    </div>
</div>
<script type="text/html" id="toolbar">
    <div class="layui-btn-container">
        <button class="layui-btn layui-btn-primary layui-btn-sm" lay-event="exportData"><i class="layui-icon">&#xe67d;</i>导出</button>
    </div>
</script>
<script type="text/html" id="operate">
    {{#if (d.preHave === 'Y' && d.wrkSts === 3) { }}
    <a class="layui-btn layui-btn-primary layui-border-red layui-btn-xs btn-error" lay-event="preHave">先入品</a>
@@ -99,6 +125,7 @@
    <a class="layui-btn layui-btn-xs btn-detlShow" lay-event="detlShow">明细</a>
    <a class="layui-btn layui-btn-danger layui-btn-xs btn-complete" lay-event="complete">完成</a>
    <a class="layui-btn layui-btn-primary layui-btn-xs btn-cancel" lay-event="cancel">取消</a>
    <a class="layui-btn layui-btn-primary layui-btn-xs btn-delete" lay-event="delete">删除</a>
    {{#if (d.ioType === 103) { }}
    <a class="layui-btn layui-btn-warm layui-btn-xs btn-pick" lay-event="pick">拣</a>
    {{# } }}
src/main/webapp/views/taskLog/taskLog.html
@@ -9,6 +9,15 @@
    <link rel="stylesheet" href="../../static/layui/css/layui.css" media="all">
    <link rel="stylesheet" href="../../static/css/cool.css" media="all">
    <link rel="stylesheet" href="../../static/css/common.css" media="all">
    <style>
        .taskLog-wrksts-cb-wrap .taskLog-wrksts-cb-label:has(input:checked) {
            color: #1890ff;
            font-weight: 500;
            background-color: #e6f7ff;
            border-radius: 4px;
            padding: 2px 6px;
        }
    </style>
</head>
<body>
<!-- 搜索栏 -->
@@ -19,13 +28,12 @@
        </div>
    </div>
    <div class="layui-inline">
        <div class="layui-input-inline cool-auto-complete">
            <input id="wrkSts" class="layui-input" name="wrk_sts" type="text" placeholder="请输入" autocomplete="off" style="display: none">
            <input id="wrkSts$" 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="basWrkStatusQueryBywrkSts" onkeyup="autoLoad(this.getAttribute('data-key'))">
                <select class="cool-auto-complete-window-select" data-key="basWrkStatusQueryBywrkStsSelect" onchange="confirmed(this.getAttribute('data-key'))" multiple="multiple">
                </select>
        <div class="layui-input-inline cool-auto-complete" id="taskLogWrkStsWrap">
            <input id="taskLogWrkSts" class="layui-input" name="wrk_sts" type="text" placeholder="请输入" autocomplete="off" style="display: none">
            <input id="taskLogWrkStsDisp" class="layui-input cool-auto-complete-div" type="text" placeholder="工作状态" readonly onfocus="this.blur()">
            <div class="cool-auto-complete-window" id="taskLogWrkStsWindow" style="display: none;">
                <div style="padding: 6px 8px 0; color: #999; font-size: 12px;">可多选,直接点击勾选</div>
                <div id="taskLogWrkStsCheckboxWrap" class="taskLog-wrksts-cb-wrap" style="padding: 8px; max-height: 200px; overflow-y: auto;"></div>
            </div>
        </div>
    </div>