自动化立体仓库 - WMS系统
chen.llin
昨天 0406c675e143bbb08284fd55381261afcc587afc
月结增加明细
1个文件已添加
16个文件已修改
574 ■■■■ 已修改文件
src/main/java/com/zy/asrs/controller/MonthlySettleController.java 49 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/OrderPakinController.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/ReportQueryController.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/OrderPakinLog.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/OrderPakoutLog.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/param/OrderDomainParam.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/result/MonthlySettleDetailFlowVO.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/entity/result/MonthlySettleStatisticsVO.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/mapper/MonthlySettleMapper.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/MonthlySettleServiceImpl.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/MonthlySettleMapper.xml 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/monthlySettle/monthlySettle.js 170 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/orderPakinLog/order.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/orderPakoutLog/order.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/monthlySettle/monthlySettle.html 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/orderPakinLog/order.html 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/orderPakoutLog/order.html 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/controller/MonthlySettleController.java
@@ -143,25 +143,46 @@
    public R exportDetail(@PathVariable("id") Long id) {
        // 获取月结统计信息
        MonthlySettleStatisticsVO statistics = monthlySettleService.getSettleStatistics(id);
        if (statistics == null || statistics.getDetails() == null) {
        if (statistics == null) {
            return R.error("月结明细不存在");
        }
        
        List<MonthlySettleDetail> details = statistics.getDetails();
        List<MonthlySettleDetail> details = statistics.getDetails() != null ? statistics.getDetails() : new ArrayList<>();
        List<com.zy.asrs.entity.result.MonthlySettleDetailFlowVO> detailFlows = statistics.getDetailFlows() != null ? statistics.getDetailFlows() : new ArrayList<>();
        
        // 定义导出字段
        List<String> fields = new ArrayList<>();
        fields.add("matnr");
        fields.add("maktx");
        fields.add("batch");
        fields.add("brand");
        fields.add("beginningQty");
        fields.add("endingQty");
        fields.add("diffQty");
        fields.add("inQty");
        fields.add("outQty");
        // 定义明细流水导出字段
        List<String> flowFields = new ArrayList<>();
        flowFields.add("orderNo");
        flowFields.add("orderTime");
        flowFields.add("pakinPakoutStatusName");
        flowFields.add("matnr");
        flowFields.add("maktx");
        flowFields.add("batch");
        flowFields.add("brand");
        flowFields.add("specs");
        flowFields.add("qty");
        flowFields.add("unit");
        
        return R.ok(exportSupport(details, fields));
        // 定义汇总导出字段
        List<String> summaryFields = new ArrayList<>();
        summaryFields.add("matnr");
        summaryFields.add("maktx");
        summaryFields.add("batch");
        summaryFields.add("brand");
        summaryFields.add("beginningQty");
        summaryFields.add("endingQty");
        summaryFields.add("diffQty");
        summaryFields.add("inQty");
        summaryFields.add("outQty");
        // 构建返回数据结构:第一页是明细流水,第二页是汇总
        java.util.Map<String, Object> result = new java.util.HashMap<>();
        result.put("detailFlows", exportSupport(detailFlows, flowFields));
        result.put("details", exportSupport(details, summaryFields));
        result.put("flowTitles", new String[]{"订单编号", "业务时间", "类型", "物料编码", "物料名称", "批次", "品牌", "规格", "数量", "单位"});
        result.put("summaryTitles", new String[]{"物料编码", "物料名称", "批次", "品牌", "期初库存", "期末库存", "差异数量", "本期入库", "本期出库"});
        return R.ok(result);
    }
}
src/main/java/com/zy/asrs/controller/OrderPakinController.java
@@ -12,7 +12,6 @@
import com.zy.asrs.entity.result.WrkTraceVo;
import com.zy.asrs.service.*;
import com.zy.common.model.DetlDto;
import com.zy.common.properties.CrossDockProperties;
import com.zy.common.web.BaseController;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -47,8 +46,6 @@
    private ClientService clientService;
    @Autowired
    private CrossDockService crossDockService;
    @Autowired
    private CrossDockProperties crossDockProperties;
    @RequestMapping(value = "/order/list/pda/page/auth")
    @ManagerAuth
@@ -212,8 +209,8 @@
            }
        }
        
        // 越库功能:如果是越库入库单,调用越库服务处理
        if (param.getDocType() != null && param.getDocType().equals(crossDockProperties.getInboundDocTypeId())) {
        // 越库功能:如果勾选了越库订单,无论什么类型的订单都按照越库逻辑处理
        if (Boolean.TRUE.equals(param.getIsCrossDock())) {
            String outOrderNo = crossDockService.processCrossDockInbound(order, param, getUserId());
            return R.ok("越库入库单创建成功,已自动生成越库出库单:" + outOrderNo);
        }
src/main/java/com/zy/asrs/controller/ReportQueryController.java
@@ -7,7 +7,6 @@
import com.core.common.R;
import com.zy.asrs.entity.*;
import com.zy.asrs.mapper.ReportQueryMapper;
import com.zy.asrs.service.LocDetlService;
import com.zy.common.web.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
@@ -30,8 +29,6 @@
@RequestMapping("/report")
public class ReportQueryController extends BaseController {
    @Autowired
    private LocDetlService locDetlService;
    @Autowired
    private ReportQueryMapper reportQueryMapper;
src/main/java/com/zy/asrs/entity/OrderPakinLog.java
@@ -8,6 +8,7 @@
import com.core.common.SpringUtils;
import com.zy.asrs.service.DocTypeService;
import com.zy.asrs.service.OrderSettleService;
import com.zy.common.properties.CrossDockProperties;
import com.zy.system.entity.User;
import com.zy.system.service.UserService;
import io.swagger.annotations.ApiModelProperty;
@@ -378,11 +379,40 @@
        DocTypeService service = SpringUtils.getBean(DocTypeService.class);
        DocType docType = service.selectById(this.docType);
        if (!Cools.isEmpty(docType)) {
            return String.valueOf(docType.getDocName());
            String docName = String.valueOf(docType.getDocName());
            // 如果是越库单据,添加"越库"标识
            if (isCrossDock()) {
                return docName + "(越库)";
            }
            return docName;
        }
        return null;
    }
    /**
     * 判断是否为越库单据
     * 通过备注字段判断(备注中包含"越库")或通过订单编号判断(关联的越库出库单)
     *
     * @return true: 越库单据, false: 普通单据
     */
    public boolean isCrossDock() {
        // 判断备注中是否包含"越库"
        if (!Cools.isEmpty(this.memo) && this.memo.contains("越库")) {
            return true;
        }
        // 判断是否为越库入库单类型(虽然现在所有订单类型都可以是越库,但历史数据可能还是通过类型判断)
        try {
            CrossDockProperties crossDockProperties = SpringUtils.getBean(CrossDockProperties.class);
            if (crossDockProperties != null && this.docType != null
                    && this.docType.equals(crossDockProperties.getInboundDocTypeId())) {
                return true;
            }
        } catch (Exception e) {
            // 如果获取配置失败,忽略异常
        }
        return false;
    }
    public String getPayType$() {
        if (null == this.payType) {
            return null;
src/main/java/com/zy/asrs/entity/OrderPakoutLog.java
@@ -8,6 +8,7 @@
import com.core.common.SpringUtils;
import com.zy.asrs.service.DocTypeService;
import com.zy.asrs.service.OrderSettleService;
import com.zy.common.properties.CrossDockProperties;
import com.zy.system.entity.User;
import com.zy.system.service.UserService;
import io.swagger.annotations.ApiModelProperty;
@@ -378,11 +379,44 @@
        DocTypeService service = SpringUtils.getBean(DocTypeService.class);
        DocType docType = service.selectById(this.docType);
        if (!Cools.isEmpty(docType)) {
            return String.valueOf(docType.getDocName());
            String docName = String.valueOf(docType.getDocName());
            // 如果是越库单据,添加"越库"标识
            if (isCrossDock()) {
                return docName + "(越库)";
            }
            return docName;
        }
        return null;
    }
    /**
     * 判断是否为越库单据
     * 通过订单编号判断(越库出库单以"CK"开头)或通过备注字段判断(备注中包含"越库")
     *
     * @return true: 越库单据, false: 普通单据
     */
    public boolean isCrossDock() {
        // 判断订单编号是否以"CK"开头(越库出库单编号格式)
        if (!Cools.isEmpty(this.orderNo) && this.orderNo.startsWith("CK")) {
            return true;
        }
        // 判断备注中是否包含"越库"
        if (!Cools.isEmpty(this.memo) && this.memo.contains("越库")) {
            return true;
        }
        // 判断是否为越库出库单类型
        try {
            CrossDockProperties crossDockProperties = SpringUtils.getBean(CrossDockProperties.class);
            if (crossDockProperties != null && this.docType != null
                    && this.docType.equals(crossDockProperties.getOutboundDocTypeId())) {
                return true;
            }
        } catch (Exception e) {
            // 如果获取配置失败,忽略异常
        }
        return false;
    }
    public String getPayType$() {
        if (null == this.payType) {
            return null;
src/main/java/com/zy/asrs/entity/param/OrderDomainParam.java
@@ -5,7 +5,6 @@
import com.zy.asrs.entity.OrderDetlPakout;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
@@ -24,6 +23,13 @@
    private String cstmrName;
    /**
     * 是否越库订单
     * true: 勾选越库订单,无论什么类型的订单都按照越库逻辑处理
     * false: 不勾选,按照正常流程处理
     */
    private Boolean isCrossDock;
    private List<OrderDetl> orderDetlList;
    private List<OrderDetlPakin> orderDetlPakinList;
    private List<OrderDetlPakout> orderDetlPakoutList;
src/main/java/com/zy/asrs/entity/result/MonthlySettleDetailFlowVO.java
New file
@@ -0,0 +1,106 @@
package com.zy.asrs.entity.result;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
 * 月结明细流水VO(订单明细)
 */
@Data
public class MonthlySettleDetailFlowVO implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 订单ID
     */
    @ApiModelProperty(value = "订单ID")
    private Long orderId;
    /**
     * 订单编号
     */
    @ApiModelProperty(value = "订单编号")
    private String orderNo;
    /**
     * 业务时间
     */
    @ApiModelProperty(value = "业务时间")
    private String orderTime;
    /**
     * 订单明细ID
     */
    @ApiModelProperty(value = "订单明细ID")
    private Long orderDetlId;
    /**
     * 物料编码
     */
    @ApiModelProperty(value = "物料编码")
    private String matnr;
    /**
     * 物料名称
     */
    @ApiModelProperty(value = "物料名称")
    private String maktx;
    /**
     * 批次
     */
    @ApiModelProperty(value = "批次")
    private String batch;
    /**
     * 品牌
     */
    @ApiModelProperty(value = "品牌")
    private String brand;
    /**
     * 规格
     */
    @ApiModelProperty(value = "规格")
    private String specs;
    /**
     * 型号
     */
    @ApiModelProperty(value = "型号")
    private String model;
    /**
     * 颜色
     */
    @ApiModelProperty(value = "颜色")
    private String color;
    /**
     * 单位
     */
    @ApiModelProperty(value = "单位")
    private String unit;
    /**
     * 数量
     */
    @ApiModelProperty(value = "数量")
    private BigDecimal qty;
    /**
     * 入出库类型(1:入库,2:出库)
     */
    @ApiModelProperty(value = "入出库类型(1:入库,2:出库)")
    private Integer pakinPakoutStatus;
    /**
     * 入出库类型名称
     */
    @ApiModelProperty(value = "入出库类型名称")
    private String pakinPakoutStatusName;
}
src/main/java/com/zy/asrs/entity/result/MonthlySettleStatisticsVO.java
@@ -20,9 +20,15 @@
    private MonthlySettle settle;
    
    /**
     * 月结明细列表
     * 月结明细列表(汇总)
     */
    @ApiModelProperty(value = "月结明细列表")
    @ApiModelProperty(value = "月结明细列表(汇总)")
    private List<MonthlySettleDetail> details;
    /**
     * 月结明细流水列表(订单明细)
     */
    @ApiModelProperty(value = "月结明细流水列表(订单明细)")
    private List<MonthlySettleDetailFlowVO> detailFlows;
}
src/main/java/com/zy/asrs/mapper/MonthlySettleMapper.java
@@ -65,5 +65,15 @@
     * 清除出库订单的月结信息
     */
    int clearOrderSettleInfoPakout(@Param("settleId") Long settleId);
    /**
     * 查询月结明细流水(入库订单明细)
     */
    List<com.zy.asrs.entity.result.MonthlySettleDetailFlowVO> selectDetailFlowFromPakin(@Param("settleId") Long settleId);
    /**
     * 查询月结明细流水(出库订单明细)
     */
    List<com.zy.asrs.entity.result.MonthlySettleDetailFlowVO> selectDetailFlowFromPakout(@Param("settleId") Long settleId);
}
src/main/java/com/zy/asrs/service/impl/MonthlySettleServiceImpl.java
@@ -395,12 +395,35 @@
            throw new CoolException("月结记录不存在");
        }
        // 关联物料表查询明细
        // 关联物料表查询明细(汇总)
        List<MonthlySettleDetail> details = monthlySettleDetailMapper.selectDetailWithMat(settleId);
        // 查询明细流水(订单明细)
        List<com.zy.asrs.entity.result.MonthlySettleDetailFlowVO> pakinFlows = this.baseMapper.selectDetailFlowFromPakin(settleId);
        List<com.zy.asrs.entity.result.MonthlySettleDetailFlowVO> pakoutFlows = this.baseMapper.selectDetailFlowFromPakout(settleId);
        // 合并入库和出库的明细流水
        List<com.zy.asrs.entity.result.MonthlySettleDetailFlowVO> detailFlows = new java.util.ArrayList<>();
        if (pakinFlows != null && !pakinFlows.isEmpty()) {
            detailFlows.addAll(pakinFlows);
        }
        if (pakoutFlows != null && !pakoutFlows.isEmpty()) {
            detailFlows.addAll(pakoutFlows);
        }
        // 按业务时间和订单编号排序
        detailFlows.sort((a, b) -> {
            int timeCompare = (a.getOrderTime() != null ? a.getOrderTime() : "").compareTo(b.getOrderTime() != null ? b.getOrderTime() : "");
            if (timeCompare != 0) {
                return timeCompare;
            }
            return (a.getOrderNo() != null ? a.getOrderNo() : "").compareTo(b.getOrderNo() != null ? b.getOrderNo() : "");
        });
        MonthlySettleStatisticsVO result = new MonthlySettleStatisticsVO();
        result.setSettle(settle);
        result.setDetails(details);
        result.setDetailFlows(detailFlows);
        return result;
    }
@@ -424,25 +447,9 @@
        }
        
        wrapper.orderBy("create_time", false);
        List<MonthlySettle> list = this.selectList(wrapper);
        EntityWrapper<MonthlySettle> countWrapper = new EntityWrapper<>();
        countWrapper.eq("is_deleted", 0);
        if (condition != null) {
            if (condition.get("settleNo") != null) {
                countWrapper.like("settle_no", condition.get("settleNo").toString());
            }
            if (condition.get("status") != null) {
                countWrapper.eq("status", condition.get("status"));
            }
            if (condition.get("startDate") != null && condition.get("endDate") != null) {
                countWrapper.ge("start_date", condition.get("startDate"));
                countWrapper.le("end_date", condition.get("endDate"));
            }
        }
        page.setRecords(list);
        page.setTotal(this.selectCount(countWrapper));
        return page;
        // 使用分页查询,在数据库层面进行分页,而不是查询所有记录
        Page<MonthlySettle> resultPage = this.selectPage(page, wrapper);
        return resultPage;
    }
    /**
src/main/resources/mapper/MonthlySettleMapper.xml
@@ -150,6 +150,66 @@
        WHERE monthly_settle_id = #{settleId}
    </update>
    <!-- 查询月结明细流水(入库订单明细) -->
    <select id="selectDetailFlowFromPakin" resultType="com.zy.asrs.entity.result.MonthlySettleDetailFlowVO">
        SELECT
            molpi.id as orderId,
            molpi.order_no as orderNo,
            molpi.order_time as orderTime,
            modlpi.id as orderDetlId,
            modlpi.matnr,
            modlpi.maktx,
            modlpi.batch,
            modlpi.brand,
            modlpi.specs,
            modlpi.model,
            modlpi.color,
            modlpi.unit,
            modlpi.qty,
            COALESCE(modlpi.pakin_pakout_status, molpi.pakin_pakout_status) as pakinPakoutStatus,
            CASE
                WHEN COALESCE(modlpi.pakin_pakout_status, molpi.pakin_pakout_status) = 1 THEN '入库'
                WHEN COALESCE(modlpi.pakin_pakout_status, molpi.pakin_pakout_status) = 2 THEN '出库'
                ELSE '未知'
            END as pakinPakoutStatusName
        FROM man_order_log_pakin molpi
        INNER JOIN man_order_detl_log_pakin modlpi ON molpi.id = modlpi.order_id
        WHERE molpi.monthly_settle_id = #{settleId}
        AND molpi.status = 1
        AND COALESCE(modlpi.pakin_pakout_status, molpi.pakin_pakout_status) IN (1, 2)
        ORDER BY molpi.order_time, molpi.order_no, modlpi.id
    </select>
    <!-- 查询月结明细流水(出库订单明细) -->
    <select id="selectDetailFlowFromPakout" resultType="com.zy.asrs.entity.result.MonthlySettleDetailFlowVO">
        SELECT
            molpo.id as orderId,
            molpo.order_no as orderNo,
            molpo.order_time as orderTime,
            modlpo.id as orderDetlId,
            modlpo.matnr,
            modlpo.maktx,
            modlpo.batch,
            modlpo.brand,
            modlpo.specs,
            modlpo.model,
            modlpo.color,
            modlpo.unit,
            modlpo.qty,
            COALESCE(modlpo.pakin_pakout_status, molpo.pakin_pakout_status) as pakinPakoutStatus,
            CASE
                WHEN COALESCE(modlpo.pakin_pakout_status, molpo.pakin_pakout_status) = 1 THEN '入库'
                WHEN COALESCE(modlpo.pakin_pakout_status, molpo.pakin_pakout_status) = 2 THEN '出库'
                ELSE '未知'
            END as pakinPakoutStatusName
        FROM man_order_log_pakout molpo
        INNER JOIN man_order_detl_log_pakout modlpo ON molpo.id = modlpo.order_id
        WHERE molpo.monthly_settle_id = #{settleId}
        AND molpo.status = 1
        AND COALESCE(modlpo.pakin_pakout_status, molpo.pakin_pakout_status) IN (1, 2)
        ORDER BY molpo.order_time, molpo.order_no, modlpo.id
    </select>
</mapper>
src/main/webapp/static/js/monthlySettle/monthlySettle.js
@@ -1,7 +1,7 @@
var pageCurr;
layui.config({
    base: baseUrl + "/static/layui/lay/modules/"
}).use(['layer', 'form', 'table', 'util', 'admin', 'laydate', 'laytpl'], function () {
}).use(['layer', 'form', 'table', 'util', 'admin', 'laydate', 'laytpl', 'element'], function () {
    var $ = layui.jquery;
    var layer = layui.layer;
    var form = layui.form;
@@ -10,6 +10,7 @@
    var admin = layui.admin;
    var layDate = layui.laydate;
    var laytpl = layui.laytpl;
    var element = layui.element;
    // 渲染表格
    tableIns = table.render({
@@ -52,6 +53,7 @@
            }
            pageCurr = curr;
            // 延迟调用权限控制,确保表格操作列的按钮已经渲染完成
            // 改为异步请求,避免阻塞页面渲染
            setTimeout(function() {
                var param = window.location.href.split("?")[1];
                if (null != param) {
@@ -60,7 +62,7 @@
                        url: baseUrl+"/power/menu/"+resourceId+"/auth",
                        headers: {'token': localStorage.getItem('token')},
                        method: 'GET',
                        async: false,
                        async: true, // 改为异步,不阻塞页面渲染
                        success: function (res) {
                            if (res.code === 200){
                                for(var i = 0, len = res.data.length; i < len; i++) {
@@ -412,7 +414,8 @@
                layer.closeAll('loading');
                if (res.code === 200) {
                    var settle = res.data.settle;
                    var details = res.data.details;
                    var details = res.data.details || [];
                    var detailFlows = res.data.detailFlows || [];
                    
                    // 先渲染模板
                    var template = $('#detailDialog').html();
@@ -424,7 +427,41 @@
                        content: html,
                        area: ['90%', '80%'],
                        success: function (layero, dIndex) {
                            // 渲染明细表格(对账单格式)
                            // 初始化tab组件,使其可以切换
                            element.render('tab', 'monthlySettleDetailTab');
                            // 渲染明细流水表格(第一页)
                            table.render({
                                elem: '#detailFlowTable',
                                data: detailFlows,
                                page: true,
                                cellMinWidth: 100,
                                width: '100%',
                                cols: [[
                                    {type: 'numbers', title: '序号', width: 60, align: 'center'},
                                    {field: 'orderNo', title: '订单编号', width: 150},
                                    {field: 'orderTime', title: '业务时间', width: 180},
                                    {field: 'pakinPakoutStatusName', title: '类型', width: 80, align: 'center'},
                                    {field: 'matnr', title: '物料编码', width: 150},
                                    {field: 'maktx', title: '物料名称', width: 200},
                                    {field: 'batch', title: '批次', width: 120},
                                    {field: 'brand', title: '品牌', width: 120},
                                    {field: 'specs', title: '规格', width: 120},
                                    {
                                        field: 'qty',
                                        align: 'right',
                                        title: '数量',
                                        width: 120,
                                        templet: function (d) {
                                            var qty = parseFloat(d.qty || 0);
                                            return qty.toFixed(2);
                                        }
                                    },
                                    {field: 'unit', title: '单位', width: 80}
                                ]]
                            });
                            // 渲染汇总表格(第二页)
                            table.render({
                                elem: '#detailTable',
                                data: details,
@@ -506,6 +543,46 @@
        });
    }
    // 动态加载 xlsx.js 库
    function loadXLSXLibrary(callback) {
        // 如果已经加载,直接执行回调
        if (typeof XLSX !== 'undefined') {
            callback();
            return;
        }
        // 检查是否正在加载
        if (window._xlsxLoading) {
            // 如果正在加载,等待加载完成
            var checkInterval = setInterval(function() {
                if (typeof XLSX !== 'undefined') {
                    clearInterval(checkInterval);
                    callback();
                }
            }, 100);
            return;
        }
        // 开始加载
        window._xlsxLoading = true;
        layer.load(2, {content: '正在加载导出库...'});
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = 'https://cdn.sheetjs.com/xlsx-0.20.1/package/dist/xlsx.full.min.js';
        script.onload = function() {
            window._xlsxLoading = false;
            layer.closeAll('loading');
            callback();
        };
        script.onerror = function() {
            window._xlsxLoading = false;
            layer.closeAll('loading');
            layer.msg('加载导出库失败,请检查网络连接', {icon: 2});
        };
        document.head.appendChild(script);
    }
    // 导出月结明细
    function exportDetail(data) {
        layer.confirm('确定导出月结明细 "' + data.settleNo + '" 吗?', {
@@ -513,39 +590,57 @@
            skin: 'layui-layer-admin'
        }, function (i) {
            layer.close(i);
            layer.load(2);
            $.ajax({
                url: baseUrl + '/monthlySettle/detail/export/' + data.id + '/auth',
                headers: {'token': localStorage.getItem('token')},
                method: 'POST',
                success: function (res) {
                    layer.closeAll('loading');
                    if (res.code === 200) {
                        // 定义表头
                        var titles = [
                            '物料编码',
                            '物料名称',
                            '批次',
                            '品牌',
                            '期初库存',
                            '期末库存',
                            '差异数量',
                            '本期入库',
                            '本期出库'
                        ];
                        // 使用 table.exportFile 导出
                        table.exportFile(titles, res.data, 'xls');
                        layer.msg('导出成功', {icon: 1});
                    } else if (res.code === 403) {
                        top.location.href = baseUrl + "/";
                    } else {
                        layer.msg(res.msg || '导出失败', {icon: 2});
            // 先加载 xlsx.js 库,然后再执行导出
            loadXLSXLibrary(function() {
                layer.load(2);
                $.ajax({
                    url: baseUrl + '/monthlySettle/detail/export/' + data.id + '/auth',
                    headers: {'token': localStorage.getItem('token')},
                    method: 'POST',
                    success: function (res) {
                        layer.closeAll('loading');
                        if (res.code === 200) {
                            var exportData = res.data;
                            // 第一页:明细流水
                            var flowTitles = exportData.flowTitles || ['订单编号', '业务时间', '类型', '物料编码', '物料名称', '批次', '品牌', '规格', '数量', '单位'];
                            var flowData = exportData.detailFlows || [];
                            // 第二页:汇总
                            var summaryTitles = exportData.summaryTitles || ['物料编码', '物料名称', '批次', '品牌', '期初库存', '期末库存', '差异数量', '本期入库', '本期出库'];
                            var summaryData = exportData.details || [];
                            try {
                                // 使用 xlsx.js 创建包含两个sheet的Excel文件
                                var wb = XLSX.utils.book_new();
                                // 创建明细流水sheet
                                var flowWS = XLSX.utils.aoa_to_sheet([flowTitles].concat(flowData));
                                XLSX.utils.book_append_sheet(wb, flowWS, "明细流水");
                                // 创建汇总sheet
                                var summaryWS = XLSX.utils.aoa_to_sheet([summaryTitles].concat(summaryData));
                                XLSX.utils.book_append_sheet(wb, summaryWS, "汇总");
                                // 导出Excel文件
                                var fileName = '月结明细_' + data.settleNo + '.xlsx';
                                XLSX.writeFile(wb, fileName);
                                layer.msg('导出成功', {icon: 1, time: 2000});
                            } catch (e) {
                                console.error('导出失败:', e);
                                layer.msg('导出失败:' + (e.message || '未知错误'), {icon: 2});
                            }
                        } else if (res.code === 403) {
                            top.location.href = baseUrl + "/";
                        } else {
                            layer.msg(res.msg || '导出失败', {icon: 2});
                        }
                    },
                    error: function() {
                        layer.closeAll('loading');
                        layer.msg('导出失败', {icon: 2});
                    }
                },
                error: function() {
                    layer.closeAll('loading');
                    layer.msg('导出失败', {icon: 2});
                }
                });
            });
        });
    }
@@ -663,6 +758,7 @@
            }
            pageCurr=curr;
            // 延迟调用权限控制,确保表格操作列的按钮已经渲染完成
            // 改为异步请求,避免阻塞页面渲染
            setTimeout(function() {
                var param = window.location.href.split("?")[1];
                if (null != param) {
@@ -671,7 +767,7 @@
                        url: baseUrl+"/power/menu/"+resourceId+"/auth",
                        headers: {'token': localStorage.getItem('token')},
                        method: 'GET',
                        async: false,
                        async: true, // 改为异步,不阻塞页面渲染
                        success: function (res) {
                            if (res.code === 200){
                                for(var i = 0, len = res.data.length; i < len; i++) {
src/main/webapp/static/js/orderPakinLog/order.js
@@ -51,6 +51,7 @@
            { 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: 'monthlySettleNo', align: 'center', title: '月结状态', templet: '#monthlySettleTpl', minWidth: 120, width: 120 },
            { field: 'memo', align: 'center', title: '备注', hide: true },
            { align: 'center', title: '操作', toolbar: '#operate', width: 180 }
        ]],
src/main/webapp/static/js/orderPakoutLog/order.js
@@ -51,6 +51,7 @@
            { 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: 'monthlySettleNo', align: 'center', title: '月结状态', templet: '#monthlySettleTpl', minWidth: 120, width: 120 },
            { field: 'memo', align: 'center', title: '备注', hide: true },
            { align: 'center', title: '操作', toolbar: '#operate', width: 120 }
        ]],
src/main/webapp/views/monthlySettle/monthlySettle.html
@@ -133,14 +133,30 @@
            </div>
        </div>
        <hr class="layui-bg-gray">
        <div style="overflow-x: auto; width: 100%;">
            <table id="detailTable" lay-filter="detailTable"></table>
        <div class="layui-tab layui-tab-brief" lay-filter="monthlySettleDetailTab">
            <ul class="layui-tab-title">
                <li class="layui-this">明细流水</li>
                <li>汇总</li>
            </ul>
            <div class="layui-tab-content" style="padding: 10px 0;">
                <div class="layui-tab-item layui-show">
                    <div style="overflow-x: auto; width: 100%;">
                        <table id="detailFlowTable" lay-filter="detailFlowTable"></table>
                    </div>
                </div>
                <div class="layui-tab-item">
                    <div style="overflow-x: auto; width: 100%;">
                        <table id="detailTable" lay-filter="detailTable"></table>
                    </div>
                </div>
            </div>
        </div>
    </div>
</script>
<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
<!-- xlsx.js 改为按需加载,避免阻塞页面渲染 -->
<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/monthlySettle/monthlySettle.js" charset="utf-8"></script>
src/main/webapp/views/orderPakinLog/order.html
@@ -139,6 +139,13 @@
          {{# } }}
    >{{d.settle$}}</span>
</script>
    <script type="text/html" id="monthlySettleTpl">
    {{# if(d.monthlySettleId && d.monthlySettleId > 0){ }}
    <span class="layui-badge layui-bg-orange" title="月结编号:{{d.monthlySettleNo}}">已月结</span>
    {{# } else { }}
    <span class="layui-badge layui-badge-gray">未月结</span>
    {{# } }}
</script>
    <!-- 订单任务追溯 -->
    <script id="wrkTraceDialog" type="text/html" style="position: relative">
    <div style="position: absolute; top: 0; left: 0;">
src/main/webapp/views/orderPakoutLog/order.html
@@ -139,6 +139,13 @@
          {{# } }}
    >{{d.settle$}}</span>
</script>
    <script type="text/html" id="monthlySettleTpl">
    {{# if(d.monthlySettleId && d.monthlySettleId > 0){ }}
    <span class="layui-badge layui-bg-orange" title="月结编号:{{d.monthlySettleNo}}">已月结</span>
    {{# } else { }}
    <span class="layui-badge layui-badge-gray">未月结</span>
    {{# } }}
</script>
    <!-- 订单任务追溯 -->
    <script id="wrkTraceDialog" type="text/html" style="position: relative">
    <div style="position: absolute; top: 0; left: 0;">