heshaofeng
2026-02-05 6dbcd7df8e116cb83dbbd34d0acd6508f9b99991
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/outbound/outPicking.vue
@@ -22,10 +22,10 @@
        <!-- æ‰«ç æ“ä½œåŒºåŸŸ -->
        <el-card class="scan-section-card" shadow="never">
            <div class="scan-section">
                <el-alert title="请使用扫码枪扫描/手动输入托盘码,回车触发查询" type="info" :closable="false" show-icon class="scan-alert">
                <el-alert title="请使用扫码枪扫描,支持回车自动确认" type="info" :closable="false" show-icon class="scan-alert">
                    <template #default>
                        <div>
                            <div>1. è¾“å…¥/扫描托盘码 â†’ 2. å›žè½¦è§¦å‘系统获取订单号并加载数据</div>
                            <div>1. è¯·å…ˆæ‰«ææ‰˜ç›˜ç  â†’ 2. å†æ‰«æç‰©æ–™æ ‡ç­¾ç </div>
                            <div style="margin-top: 8px; font-size: 13px; color: #666;">
                                <i class="el-icon-info" style="color: #409EFF;"></i>
                                æ”¯æŒæ‰«æ‰˜ç›˜ç æ•´ç®±å‡ºåº“:扫描托盘码后可直接进行整箱物料拣选,无需再扫描物料标签码。
@@ -38,8 +38,7 @@
                    <el-row :gutter="20">
                        <el-col :span="8">
                            <el-form-item label="托盘码" prop="palletCode">
                                <!-- ã€æ ¸å¿ƒè°ƒæ•´ã€‘仅保留回车触发,无任何额外事件,恢复原有样式 -->
                                <el-input ref="palletInput" v-model="scanForm.palletCode" placeholder="请扫描/输入托盘码" size="large"
                                <el-input ref="palletInput" v-model="scanForm.palletCode" placeholder="请扫描托盘码" size="large"
                                    clearable @keyup.enter="handlePalletScan">
                                    <template #prefix>
                                        <i class="el-icon-box"></i>
@@ -297,11 +296,13 @@
                        </div>
                        <div class="info-item">
                            <label>整出数量:</label>
                            <span class="info-value highlight">{{ wholeOutInfo.assignQuantity }} {{ wholeOutInfo.unit }}</span>
                            <span class="info-value highlight">{{ wholeOutInfo.assignQuantity }} {{ wholeOutInfo.unit
                            }}</span>
                        </div>
                        <div class="info-item">
                            <label>当前库存:</label>
                            <span class="info-value">{{ wholeOutInfo.currentStock || wholeOutInfo.originalQuantity }} {{ wholeOutInfo.unit }}</span>
                            <span class="info-value">{{ wholeOutInfo.currentStock || wholeOutInfo.originalQuantity }} {{
                                wholeOutInfo.unit }}</span>
                        </div>
                    </div>
                </div>
@@ -347,7 +348,7 @@
            },
            scanRules: {
                palletCode: [
                    { required: true, message: '请扫描/输入托盘码', trigger: 'blur' }
                    { required: true, message: '请扫描托盘码', trigger: 'blur' }
                ],
                materialBarcode: [
                    { required: true, message: '请扫描物料条码', trigger: 'blur' }
@@ -373,8 +374,7 @@
            loadingInstance: null,
            // éŸ³é¢‘实例(缓存,避免重复创建)
            successAudio: null,
            errorAudio: null,
            selectedPalletCode: ''
            errorAudio: null
        }
    },
    computed: {
@@ -391,7 +391,6 @@
        // é”€æ¯éŸ³é¢‘实例,释放资源
        this.successAudio = null
        this.errorAudio = null
        // ã€å·²æ¸…理】移除即时触发相关的防抖定时器清除代码
    },
    methods: {
        // åˆå§‹åŒ–音频实例(核心:适配src/assets路径,缓存实例)
@@ -410,6 +409,42 @@
                }
            }
        },
        // ============== æ–°å¢žï¼šæ ¹æ®æ‰˜ç›˜å·èŽ·å–è®¢å•å·ï¼ˆæ ¸å¿ƒåŠŸèƒ½ï¼‰ ==============
        async getOrderNoByPallet(palletCode) {
            if (!palletCode) {
                this.$message.warning('托盘号不能为空');
                return null;
            }
            try {
                this.showFullScreenLoading();
                // è°ƒç”¨èŽ·å–è®¢å•å·çš„æŽ¥å£
                const response = await this.http.get(`/api/OutboundOrder/GetOrderNoByPalletCode?palletCode=${palletCode}`);
                if (response.status && response.data) {
                    const validOrderNo = response.data;
                    this.playSuccessAudio();
                    this.$message.success(`成功获取订单号:${validOrderNo}`);
                    return validOrderNo;
                } else {
                    this.playErrorAudio();
                    const errorMsg = response.message || '该托盘号未关联任何订单';
                    this.$message.error(errorMsg);
                    return null;
                }
            } catch (error) {
                this.playErrorAudio();
                const errorMsg = `获取订单号异常:${error.message || '网络错误'}`;
                this.$message.error(errorMsg);
                console.error("【托盘号查订单号接口异常】", error);
                return null;
            } finally {
                this.hideFullScreenLoading();
            }
        },
        // ============== æ–°å¢žç»“束 ==============
        // æ’­æ”¾æˆåŠŸéŸ³é¢‘
        playSuccessAudio() {
            try {
@@ -435,292 +470,235 @@
        },
        initPage() {
            // ä»Žè·¯ç”±å‚数获取订单号
            this.orderNo = this.$route.query.orderNo || '';
            // æ”¯æŒé€šè¿‡æ‰˜ç›˜å·èŽ·å–è®¢å•å·ï¼Œä¸å¼ºåˆ¶è¿”å›žä¸Šä¸€é¡µ
            if (this.orderNo) {
                this.loadOrderInfo();
            } else {
                this.$message.info('请扫描/输入托盘码获取关联订单');
            }
            this.orderNo = this.$route.query.orderNo || ''
            // ============== å¾®è°ƒï¼šæ³¨é‡Šå¼ºåˆ¶è¿”回逻辑,兼容托盘号查订单号 ==============
            // if (!this.orderNo) {
            //     this.$message.error('订单号不能为空')
            //     this.$router.back()
            //     return
            // }
            // ============== å¾®è°ƒç»“束 ==============
            // åŠ è½½è®¢å•ä¿¡æ¯
            this.loadOrderInfo()
            // è‡ªåŠ¨èšç„¦åˆ°æ‰˜ç›˜ç è¾“å…¥æ¡†
            this.$nextTick(() => {
                if (this.$refs.palletInput) {
                    this.$refs.palletInput.focus();
                    this.$refs.palletInput.focus()
                }
            });
            })
        },
        async loadOrderInfo() {
            if (!this.orderNo) return;
            try {
                this.showFullScreenLoading();
                const response = await this.http.get(`/api/Outbound/GetOrderInfo?orderNo=${this.orderNo}`);
                this.showFullScreenLoading()
                const response = await this.http.get(`/api/Outbound/GetOrderInfo?orderNo=${this.orderNo}`)
                if (response.status) {
                    this.orderInfo = response.data;
                    this.orderInfo = response.data
                } else {
                    this.$message.error(response.message || '获取订单信息失败');
                }
            } catch (error) {
                this.$message.error(`获取订单信息异常:${error.message || '网络错误'}`);
                console.error('【加载订单信息失败】', error);
            }
            catch (error) {
            } finally {
                this.hideFullScreenLoading();
                this.hideFullScreenLoading()
            }
        },
        async loadPalletData() {
            if (!this.scanForm.palletCode || !this.orderNo) {
                this.unpickedData = [];
                return;
            if (!this.scanForm.palletCode) {
                this.unpickedData = []
                return
            }
            try {
                this.showFullScreenLoading();
                this.showFullScreenLoading()
                await Promise.all([
                    this.loadUnpickedData(),
                    this.loadPickedData()
                ]);
            } catch (error) {
                this.unpickedData = [];
                console.error('【加载托盘数据失败】', error);
                this.unpickedData = []
            } finally {
                this.hideFullScreenLoading();
                this.hideFullScreenLoading()
            }
        },
        // æ ¸å¿ƒé€»è¾‘:自动提取CK订单号并赋值,隐藏指定提示
        loadUnpickedData() {
            return new Promise((resolve, reject) => {
                // å…ˆæ¸…空之前的提示,避免重复提示
                this.$message.closeAll();
                this.http.post(`/api/Outbound/QueryPickingTasks?orderNo=${this.orderNo}&palletCode=${this.scanForm.palletCode}`, {}).then(response => {
                    if (response.status) {
                        if (response.data.outStockLockInfos && response.data.outStockLockInfos.length > 0) {
                            this.unpickedData = response.data.outStockLockInfos;
                            this.matMixed = response.data.isMatMixed;
                            this.orderOver = response.data.orderOver;
                            this.calculateUnpickedStats();
                            this.$nextTick(() => {
                                if (this.hasWholeOut()) {
                                    this.showWholeOutConfirm();
                                }
                            });
                            this.$nextTick(() => {
                                if (this.$refs.materialInput) {
                                    this.$refs.materialInput.focus();
                                }
                            });
                        } else {
                            this.unpickedData = [];
                            this.calculateUnpickedStats();
                            this.$message.success({
                                message: `托盘【${this.scanForm.palletCode}】已拣选完成,暂无拣选记录`,
                                duration: 3000,
                                showClose: true
                            });
                            this.$nextTick(() => {
                                if (this.$refs.palletInput) {
                                    this.$refs.palletInput.focus();
                                    this.$refs.palletInput.$el.querySelector('input').select();
                                }
                                // è¡¨æ ¼é«˜äº®é€‰ä¸­å½“前托盘
                                if (this.$refs.unpickedTable) {
                                    const targetRow = this.unpickedData.find(item => item.palletCode === this.scanForm.palletCode);
                                    if (targetRow) this.$refs.unpickedTable.setCurrentRow(targetRow);
                                }
                            });
                            this.scanForm.materialBarcode = '';
                            this.$nextTick(() => {
                                if (this.$refs.palletInput) {
                                    this.$refs.palletInput.focus();
                                }
                            });
                        }
                        resolve();
                    } else {
                        const errorMsg = response.message || `获取托盘【${this.scanForm.palletCode}】拣选数据失败`;
                        const targetOrderNo = this.extractCKOrderNo(errorMsg);
                        if (targetOrderNo) {
                            // è‡ªåŠ¨èµ‹å€¼ï¼Œä¸æ˜¾ç¤ºé”™è¯¯æç¤ºï¼Œé—­çŽ¯æµç¨‹
                            this.orderNo = targetOrderNo;
                            this.$forceUpdate();
                            this.loadOrderInfo().then(() => {
                                this.loadPalletData();
                            });
                            resolve("自动赋值新订单号,重新请求数据");
                        } else {
                            this.$message.error({
                                message: errorMsg,
                                duration: 5000,
                                showClose: true
                            });
                            this.scanForm.palletCode = '';
                            this.$nextTick(() => {
                                if (this.$refs.palletInput) {
                                    this.$refs.palletInput.focus();
                                }
                            });
                            reject(errorMsg);
                        }
                        this.unpickedData = [];
                        this.calculateUnpickedStats();
                    }
                }).catch(error => {
                    this.unpickedData = [];
                    this.calculateUnpickedStats();
    return new Promise((resolve, reject) => {
        // å…ˆæ¸…空之前的提示,避免重复提示
        this.$message.closeAll();
        this.http.post(`/api/Outbound/QueryPickingTasks?orderNo=${this.orderNo}&palletCode=${this.scanForm.palletCode}`, {}).then(response => {
            if (response.status) {
                // æƒ…况1:有未拣选数据
                if (response.data.outStockLockInfos && response.data.outStockLockInfos.length > 0) {
                    this.unpickedData = response.data.outStockLockInfos;
                    this.matMixed = response.data.isMatMixed;
                    this.orderOver = response.data.orderOver;
                    this.calculateUnpickedStats()
                    
                    this.$message.error({
                        message: `获取托盘【${this.scanForm.palletCode}】拣选数据异常:${error.message || '网络错误,请重试'}`,
                        duration: 5000,
                        showClose: true
                    // æ£€æŸ¥æ•´å‡ºç¡®è®¤
                    this.$nextTick(() => {
                        if (this.hasWholeOut()) {
                            this.showWholeOutConfirm()
                        }
                    })
                    // èšç„¦ç‰©æ–™æ¡ç è¾“入框
                    this.$nextTick(() => {
                        if (this.$refs.materialInput) {
                            this.$refs.materialInput.focus()
                        }
                    })
                }
                // æƒ…况2:无未拣选数据
                else {
                    this.unpickedData = [];
                    this.calculateUnpickedStats(); // é‡ç½®ç»Ÿè®¡æ•°æ®
                    // å‹å¥½æç¤º + äº¤äº’优化
                    this.$message.success({
                        message: `托盘【${this.scanForm.palletCode}】已拣选完成,暂无拣选记录`,
                        duration: 3000, // æç¤ºæ˜¾ç¤º3秒
                        showClose: true // å…è®¸æ‰‹åЍ关闭
                    });
                    console.error("【QueryPickingTasks ç½‘络异常】", error);
                    reject(error);
                })
            })
        },
                    // æ¸…空物料条码并聚焦回托盘码输入框
                    this.scanForm.materialBarcode = '';
                    this.$nextTick(() => {
                        if (this.$refs.palletInput) {
                            this.$refs.palletInput.focus();
                        }
                    });
                }
                resolve();
            }
            // æƒ…况3:接口返回失败
            else {
                this.unpickedData = [];
                this.calculateUnpickedStats();
                this.$message.error({
                    message: response.message || `获取托盘【${this.scanForm.palletCode}】拣选数据失败`,
                    duration: 5000,
                    showClose: true
                });
                reject(response.message || '获取托盘数据失败');
            }
        }).catch(error => {
            // æƒ…况4:网络/请求异常
            this.unpickedData = [];
            this.calculateUnpickedStats();
            this.$message.error({
                message: `获取托盘【${this.scanForm.palletCode}】拣选数据异常:${error.message || '网络错误,请重试'}`,
                duration: 5000,
                showClose: true
            });
            reject(error);
        })
    })
},
        loadPickedData() {
            return new Promise((resolve, reject) => {
                this.http.post(`/api/Outbound/QueryPickedList?orderNo=${this.orderNo}&palletCode=${this.scanForm.palletCode}`, {}).then(response => {
                    if (response.status) {
                        if (response.data.length > 0) {
                            this.pickedData = response.data;
                            this.calculatePickedStats();
                            this.pickedData = response.data
                            this.calculatePickedStats()
                        } else {
                            this.pickedData = [];
                            this.pickedData = []
                        }
                        resolve();
                    } else {
                        const errorMsg = response.message || '获取托盘已拣选数据失败';
                        const targetOrderNo = this.extractCKOrderNo(errorMsg);
                        resolve()
                        
                        if (targetOrderNo) {
                            this.orderNo = targetOrderNo;
                            this.$forceUpdate();
                            resolve("自动赋值新订单号");
                        } else {
                            reject(errorMsg);
                        }
                        this.pickedData = [];
                    } else {
                        this.$message.error(response.message || '获取托盘数据失败')
                        this.pickedData = []
                        reject(response.message || '获取托盘数据失败')
                    }
                }).catch(error => {
                    console.error("【QueryPickedList ç½‘络异常】", error);
                    reject(error);
                    reject(error)
                })
            })
        },
        // è®¡ç®—未拣选
        calculateUnpickedStats() {
            this.unpickedCount = this.unpickedData.length;
            // æœªæ‹£é€‰æ¡ç›®æ•°é‡
            this.unpickedCount = this.unpickedData.length
            // è®¡ç®—未拣选的总数量(分拣数量 - å·²åˆ†æ‹£æ•°é‡ï¼‰
            this.unpickedQuantity = this.unpickedData.reduce((sum, item) => {
                const assignQty = item.assignQuantity || 0;
                const sortedQty = item.sortedQuantity || 0;
                const remainingQty = Math.max(0, assignQty - sortedQty);
                return sum + remainingQty;
            }, 0);
            this.unpickedTotal = this.unpickedCount;
                const assignQty = item.assignQuantity || 0
                const sortedQty = item.sortedQuantity || 0
                const remainingQty = Math.max(0, assignQty - sortedQty)
                return sum + remainingQty
            }, 0)
            // æ€»æ¡ç›®æ•°ï¼ˆä¸Žæ¡ç›®æ•°é‡ç›¸åŒï¼Œè¿™é‡Œä¿ç•™å˜é‡ä¸€è‡´æ€§ï¼‰
            this.unpickedTotal = this.unpickedCount
        },
        // è®¡ç®—已拣选
        calculatePickedStats() {
            this.pickedCount = this.pickedData.length;
            // å·²æ‹£é€‰æ¡ç›®æ•°é‡
            this.pickedCount = this.pickedData.length
            // è®¡ç®—已拣选的总数量
            this.pickedQuantity = 0 - this.pickedData.reduce((sum, item) => {
                return (sum + item.changeQuantity);
            }, 0);
            this.pickedTotal = this.pickedCount;
        },
        // å·¥å…·æ–¹æ³• - æå–CK开头订单号
        extractCKOrderNo(message) {
            if (!message) return null;
            const ckOrderReg = /CK\d+/g;
            const matchResult = message.match(ckOrderReg);
            return matchResult && matchResult.length > 0 ? matchResult[0] : null;
        },
        // æ ¸å¿ƒï¼šè°ƒç”¨èŽ·å–è®¢å•å·æŽ¥å£ï¼ˆå®Œæ•´è·¯å¾„ï¼Œå›žè½¦è§¦å‘ï¼‰
        async getOrderNoByPallet(palletCode) {
            if (!palletCode) {
                this.$message.warning('托盘号不能为空');
                return null;
            }
                return (sum + item.changeQuantity)
            }, 0)
            try {
                this.showFullScreenLoading();
                const response = await this.http.get(`/api/OutboundOrder/GetOrderNoByPalletCode?palletCode=${palletCode}`);
                if (response.status && response.data) {
                    const validOrderNo = response.data;
                    this.playSuccessAudio();
                    this.$message.success(`成功获取订单号:${validOrderNo}`);
                    return validOrderNo;
                } else {
                    this.playErrorAudio();
                    const errorMsg = response.message || '该托盘号未关联任何订单';
                    this.$message.error(errorMsg);
                    return null;
                }
            } catch (error) {
                this.playErrorAudio();
                const errorMsg = `接口调用异常:${error.message || '网络错误/接口不存在'}`;
                this.$message.error(errorMsg);
                console.error("【接口调用失败】", error);
                return null;
            } finally {
                this.hideFullScreenLoading();
            }
            // æ€»æ¡ç›®æ•°ï¼ˆä¸Žæ¡ç›®æ•°é‡ç›¸åŒï¼‰
            this.pickedTotal = this.pickedCount
        },
        // å›žè½¦è§¦å‘的核心方法,无即时触发,避免频繁调用接口
        async handlePalletScan(flag = true) {
        // ============== å¾®è°ƒï¼šæ–°å¢žèŽ·å–è®¢å•å·é€»è¾‘ï¼Œå…¶ä½™ä¸å˜ ==============
        handlePalletScan(flag = true) {
            const palletCode = this.scanForm.palletCode.trim();
            if (!palletCode) {
                this.$message.warning('请输入有效的托盘号');
                return;
            }
            // è°ƒç”¨è®¢å•号接口,仅回车触发一次
            const orderNoFromApi = await this.getOrderNoByPallet(palletCode);
            if (orderNoFromApi) {
                this.orderNo = orderNoFromApi;
                this.$forceUpdate();
            }
            if (this.orderNo) {
                await this.loadOrderInfo();
            // å…ˆæ ¹æ®æ‰˜ç›˜å·èŽ·å–è®¢å•å·
            this.getOrderNoByPallet(palletCode).then((orderNo) => {
                if (orderNo) {
                    // èµ‹å€¼è®¢å•号,供后续逻辑使用
                    this.orderNo = orderNo;
                    // åˆ·æ–°è®¢å•信息
                    this.loadOrderInfo();
                }
                this.loadPalletData(flag);
            } else {
                this.loadPalletData(flag);
            }
            });
        },
        // ============== å¾®è°ƒç»“束 ==============
        handleMaterialScan() {
            if (!this.scanForm.palletCode) {
                this.$message.warning('请先扫描/输入托盘码');
                this.$refs.palletInput.focus();
                return;
                this.$message.warning('请先扫描托盘码')
                this.$refs.palletInput.focus()
                return
            }
            if (!this.scanForm.materialBarcode) {
                this.$message.warning('请扫描物料条码');
                return;
                this.$message.warning('请扫描物料条码')
                return
            }
            this.handleConfirmPick();
            // è‡ªåŠ¨æ‰§è¡Œç¡®è®¤æ‹£é€‰
            this.handleConfirmPick()
        },
        async handleConfirmPick() {
            if (!this.scanForm.palletCode || !this.scanForm.materialBarcode) {
                this.$message.warning('请先扫描托盘码和物料条码');
                return;
                this.$message.warning('请先扫描托盘码和物料条码')
                return
            }
            this.confirmLoading = true;
            this.showFullScreenLoading();
            this.confirmLoading = true
            this.showFullScreenLoading()
            try {
                const response = await this.http.post('/api/Outbound/CompleteOutboundWithBarcode', {
@@ -728,86 +706,94 @@
                    palletCode: this.scanForm.palletCode,
                    barcode: this.scanForm.materialBarcode,
                    operator: this.getUserName()
                });
                })
                if (response.status) {
                    if (response.data.scannedDetail.isUnpacked && response.data.scannedDetail.materialCodes.length > 0) {
                        this.$refs.printView.open(response.data.scannedDetail.materialCodes);
                    }
                    this.$message.success('拣选确认成功');
                    this.playSuccessAudio();
                    this.resetMaterialBarcode();
                    await this.loadPalletData(false);
                    this.$message.success('拣选确认成功')
                    // æŽ¥å£æˆåŠŸï¼šæ’­æ”¾æˆåŠŸéŸ³é¢‘
                    this.playSuccessAudio()
                    this.resetMaterialBarcode()
                    await this.loadPalletData(false)
                } else {
                    this.$message.error(response.message || '拣选确认失败');
                    this.playErrorAudio();
                    this.$message.error(response.message || '拣选确认失败')
                    this.playErrorAudio()
                }
            } catch (error) {
                this.$message.error('拣选确认失败');
                this.playErrorAudio();
                this.$message.error('拣选确认失败')
                this.playErrorAudio()
            } finally {
                this.confirmLoading = false;
                this.hideFullScreenLoading();
                this.confirmLoading = false
                this.hideFullScreenLoading()
            }
        },
        handleEmptyBox() {
            if (!this.scanForm.palletCode) {
                this.$message.warning('请先扫描/输入托盘码');
                return;
                this.$message.warning('请先扫描托盘码')
                return
            }
            this.confirmMessage = `确定要取空托盘 ${this.scanForm.palletCode} å—?`;
            this.currentAction = 'emptyBox';
            this.confirmDialogVisible = true;
            this.confirmMessage = `确定要取空托盘 ${this.scanForm.palletCode} å—?`
            this.currentAction = 'emptyBox'
            this.confirmDialogVisible = true
        },
        handleReturnToWarehouse() {
            if (!this.scanForm.palletCode) {
                this.$message.warning('请先扫描/输入托盘码');
                return;
                this.$message.warning('请先扫描托盘码')
                return
            }
            this.confirmMessage = `确定要将托盘 ${this.scanForm.palletCode} å›žåº“吗?`;
            this.currentAction = 'returnToWarehouse';
            this.confirmDialogVisible = true;
            this.confirmMessage = `确定要将托盘 ${this.scanForm.palletCode} å›žåº“吗?`
            this.currentAction = 'returnToWarehouse'
            this.confirmDialogVisible = true
        },
        async executeConfirm() {
            this.executeLoading = true;
            this.showFullScreenLoading();
            this.executeLoading = true
            this.showFullScreenLoading()
            try {
                let apiUrl = '';
                let apiUrl = ''
                let params = {
                    orderNo: this.orderNo,
                    palletCode: this.scanForm.palletCode,
                    station: stationManager.getStation()
                };
                }
                if (this.currentAction === 'emptyBox') {
                    apiUrl = '/api/Outbound/EmptyBox';
                    apiUrl = '/api/Outbound/EmptyBox'
                } else if (this.currentAction === 'returnToWarehouse') {
                    apiUrl = '/api/Outbound/ReturnToWarehouse';
                    apiUrl = '/api/Outbound/ReturnToWarehouse'
                }
                const response = await this.http.post(apiUrl, params);
                const response = await this.http.post(apiUrl, params)
                if (response.status) {
                    this.$message.success('操作成功');
                    this.confirmDialogVisible = false;
                    this.resetForm();
                    this.$message.success('操作成功')
                    this.confirmDialogVisible = false
                    this.resetForm()
                } else {
                    this.$message.error(response.message || '操作失败');
                    this.$message.error(response.message || '操作失败')
                }
            } catch (error) {
                this.$message.error('操作失败');
                this.$message.error('操作失败')
            } finally {
                this.executeLoading = false;
                this.hideFullScreenLoading();
                this.executeLoading = false
                this.hideFullScreenLoading()
            }
        },
        handleDialogClose() {
            if (!this.executeLoading) {
                this.confirmDialogVisible = false;
                this.confirmDialogVisible = false
            }
        },
        // æ•´å‡ºç¡®è®¤ç›¸å…³æ–¹æ³•
        showWholeOutConfirm() {
            // èŽ·å–éœ€è¦æ•´å‡ºçš„ç‰©æ–™ä¿¡æ¯
            const wholeOutItem = this.unpickedData.find(item => item.assignQuantity === item.originalQuantity);
            if (wholeOutItem) {
                this.wholeOutInfo = {
@@ -824,12 +810,14 @@
                this.wholeOutDialogVisible = true;
            }
        },
        handleWholeOutDialogClose() {
            if (!this.executeLoading) {
                this.wholeOutDialogVisible = false;
                this.wholeOutInfo = null;
            }
        },
        async executeWholeOut() {
            if (!this.wholeOutInfo) {
                this.$message.error('整出信息无效');
@@ -840,6 +828,7 @@
            this.showFullScreenLoading();
            try {
                // è°ƒç”¨æ•´å‡ºæŽ¥å£ï¼Œè¿™é‡Œä½¿ç”¨ç‰©æ–™ç¼–码作为条码
                const response = await this.http.post('/api/Outbound/CompleteOutboundWithPallet', {
                    orderNo: this.orderNo,
                    palletCode: this.scanForm.palletCode,
@@ -860,122 +849,195 @@
                this.hideFullScreenLoading();
            }
        },
        quickPick(row) {
            this.scanForm.materialBarcode = row.materielCode;
            this.handleConfirmPick();
            this.scanForm.materialBarcode = row.materielCode
            this.handleConfirmPick()
        },
        undoPick(row) {
            try {
                this.$confirm('确定要撤销这条拣选记录吗?', '提示', {
                    type: 'warning'
                }).then(() => {
                    this.http.post('/api/Outbound/UndoPicking', {
                        id: row.id
                    }).then(response => {
                        if (response.status) {
                            this.$message.success('撤销成功');
                            this.loadUnpickedData();
                            this.loadPickedData();
                        } else {
                            this.$message.error(response.message || '撤销失败');
                        }
                    });
                }).catch(() => {});
                })
                const response = this.http.post('/api/Outbound/UndoPicking', {
                    id: row.id
                }).then(response => {
                    if (response.status) {
                        this.$message.success('撤销成功')
                        this.loadUnpickedData()
                        this.loadPickedData()
                    } else {
                        this.$message.error(response.message || '撤销失败')
                    }
                })
            } catch (error) {
                if (error !== 'cancel') {
                    this.$message.error('撤销失败');
                    this.$message.error('撤销失败')
                }
            }
        },
        refreshUnpickedTable() {
            if (this.scanForm.palletCode) {
                this.loadPalletData();
                this.loadPalletData()
            }
        },
        refreshPickedTable() {
            this.loadPickedData().catch(error => {
                console.error('刷新已拣选列表失败:', error);
            });
                console.error('刷新已拣选列表失败:', error)
            })
        },
        resetMaterialBarcode() {
            this.scanForm.materialBarcode = '';
            this.scanForm.materialBarcode = ''
            this.$nextTick(() => {
                if (this.$refs.materialInput) {
                    this.$refs.materialInput.focus();
                    this.$refs.materialInput.focus()
                }
            });
            })
        },
        resetForm() {
            this.scanForm.palletCode = '';
            this.scanForm.materialBarcode = '';
            this.unpickedData = [];
            this.scanForm.palletCode = ''
            this.scanForm.materialBarcode = ''
            this.unpickedData = []
            this.$nextTick(() => {
                if (this.$refs.palletInput) {
                    this.$refs.palletInput.focus();
                    this.$refs.palletInput.focus()
                }
            });
            })
        },
        getUserName() {
            // å°è¯•从 Vuex store èŽ·å–ç”¨æˆ·å
            if (this.$store && this.$store.state && this.$store.state.userInfo) {
                return this.$store.state.userInfo.userName || this.$store.state.userInfo.username || '未登录用户';
                return this.$store.state.userInfo.userName || this.$store.state.userInfo.username || '未登录用户'
            }
            // å°è¯•从 localStorage èŽ·å–
            try {
                const userInfo = localStorage.getItem('user');
                const userInfo = localStorage.getItem('user')
                if (userInfo) {
                    const user = JSON.parse(userInfo);
                    return user.userName || user.username || '未登录用户';
                    const user = JSON.parse(userInfo)
                    return user.userName || user.username || '未登录用户'
                }
            } catch (error) {}
            } catch (error) {
            }
            return '未登录用户';
            return '未登录用户'
        },
        // è®¡ç®—分拣总数
        calculateTotalAssignQuantity() {
            return this.unpickedData.reduce((sum, item) => {
                return sum + (item.assignQuantity || 0);
            }, 0);
                return sum + (item.assignQuantity || 0)
            }, 0)
        },
        // è®¡ç®—已分拣总数
        calculateTotalSortedQuantity() {
            return this.unpickedData.reduce((sum, item) => {
                return sum + (item.sortedQuantity || 0);
            }, 0);
                return sum + (item.sortedQuantity || 0)
            }, 0)
        },
        // è®¡ç®—未分拣总数
        calculateTotalUnsortedQuantity() {
            return this.unpickedData.reduce((sum, item) => {
                const assignQty = item.assignQuantity || 0;
                const sortedQty = item.sortedQuantity || 0;
                return sum + Math.max(0, assignQty - sortedQty);
            }, 0);
                const assignQty = item.assignQuantity || 0
                const sortedQty = item.sortedQuantity || 0
                return sum + Math.max(0, assignQty - sortedQty)
            }, 0)
        },
        // æ£€æŸ¥æ˜¯å¦åŒ…含整出
        hasWholeOut() {
            return this.unpickedData.some(item => item.assignQuantity === item.originalQuantity) && !this.matMixed && !this.orderOver;
            return this.unpickedData.some(item => item.assignQuantity === item.originalQuantity) && !this.matMixed && !this.orderOver
        },
        // è®¡ç®—剩余库存
        calculateRemainingStock(row) {
            const currentStock = row.currentStock || 0
            const assignQty = row.assignQuantity || 0
            return Math.max(0, currentStock - assignQty)
        },
        // èŽ·å–åº“å­˜çŠ¶æ€ç±»åž‹
        getStockStatusType(row) {
            const currentStock = row.currentStock || 0
            const assignQty = row.assignQuantity || 0
            const sortedQty = row.sortedQuantity || 0
            if (sortedQty >= assignQty) {
                return 'success' // å·²å®Œæˆ
            } else if (currentStock < assignQty) {
                return 'danger' // åº“存不足
            } else {
                return 'warning' // è¿›è¡Œä¸­
            }
        },
        // èŽ·å–åº“å­˜çŠ¶æ€æ–‡æœ¬
        getStockStatusText(row) {
            const currentStock = row.currentStock || 0
            const assignQty = row.assignQuantity || 0
            const sortedQty = row.sortedQuantity || 0
            if (sortedQty >= assignQty) {
                return '已完成'
            } else if (currentStock < assignQty) {
                return '库存不足'
            } else {
                return '分拣中'
            }
        },
        getStatusType(status) {
            const statusMap = {
                0: 'info',
                1: 'warning',
                20: 'primary',
                30: 'success',
                40: 'danger'
            };
            return statusMap[status] || 'info';
                0: 'info',    // å¾…处理
                1: 'warning', // è¿›è¡Œä¸­
                20: 'primary', // æ‹£é€‰ä¸­
                30: 'success', // å·²å®Œæˆ
                40: 'danger'   // å¼‚常
            }
            return statusMap[status] || 'info'
        },
        // æ˜¾ç¤ºå…¨å±é®ç½©å±‚
        showFullScreenLoading() {
            if (this.loadingInstance) {
                this.loadingInstance.close();
                this.loadingInstance.close()
            }
            this.loadingInstance = ElLoading.service({
                lock: true,
                text: '处理中...',
                background: 'rgba(0, 0, 0, 0.7)',
                customClass: 'custom-full-loading'
            });
            })
        },
        // éšè—å…¨å±é®ç½©å±‚
        hideFullScreenLoading() {
            if (this.loadingInstance) {
                this.loadingInstance.close();
                this.loadingInstance = null;
                this.loadingInstance.close()
                this.loadingInstance = null
            } else {
            }
        },
        // æµ‹è¯•遮罩层显示
        testLoading() {
            // æµ‹è¯•两种方式
            this.globalLoading = true
            // åŒæ—¶ä¹Ÿæµ‹è¯•全屏loading
            this.showFullScreenLoading()
            setTimeout(() => {
                this.globalLoading = false
                this.hideFullScreenLoading()
            }, 3000)
        }
    }
}
@@ -996,6 +1058,7 @@
    overflow: hidden;
}
/* è®¢å•信息卡片 */
.order-info-card {
    margin-bottom: 20px;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
@@ -1006,8 +1069,6 @@
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-direction: column;
    align-items: flex-start;
}
.order-title {
@@ -1033,6 +1094,7 @@
    letter-spacing: 1px;
}
/* æ‰«ç åŒºåŸŸ */
.scan-section-card {
    margin-bottom: 20px;
}
@@ -1072,6 +1134,7 @@
    margin: 0;
}
/* è¡¨æ ¼åŒºåŸŸ */
.tables-section {
    margin-top: 20px;
}
@@ -1110,6 +1173,7 @@
    border-top: 1px solid #ebeef5;
}
/* è¡¨æ ¼è¡Œæ ·å¼ */
.el-table tbody tr:hover>td {
    background-color: #f0f9ff !important;
}
@@ -1118,6 +1182,7 @@
    background-color: #e1f3ff !important;
}
/* å¯¹è¯æ¡†æ ·å¼ */
.confirm-content {
    padding: 20px 0;
    text-align: center;
@@ -1132,12 +1197,14 @@
    text-align: center;
}
/* å“åº”式设计 */
@media (max-width: 1200px) {
    .action-buttons .el-button {
        font-size: 14px;
    }
}
/* Element UI ç»„件样式覆盖 */
::v-deep .el-card__header {
    padding: 18px 20px;
    border-bottom: 1px solid #ebeef5;
@@ -1162,6 +1229,7 @@
    border: none;
}
/* å›¾æ ‡æ ·å¼ */
.el-icon-document,
.el-icon-box,
.el-icon-s-grid,
@@ -1173,6 +1241,7 @@
    font-size: 18px;
}
/* æè¿°åˆ—表样式 */
::v-deep .el-descriptions__label {
    font-weight: bold;
    color: #909399;
@@ -1182,6 +1251,7 @@
    color: #606266;
}
/* è¡¨æ ¼å¢žå¼ºæ ·å¼ */
::v-deep .el-table th {
    background-color: #fafafa;
    font-weight: 600;
@@ -1192,10 +1262,12 @@
    font-weight: 500;
}
/* æ ‡ç­¾æ ·å¼å¢žå¼º */
::v-deep .el-tag--small {
    font-weight: 500;
}
/* æç¤ºä¿¡æ¯æ ·å¼å¢žå¼º */
.scan-alert ::v-deep .el-alert__content {
    width: 100%;
}
@@ -1204,6 +1276,7 @@
    margin-top: 8px;
}
/* åˆ†æ‹£ç»Ÿè®¡ä¿¡æ¯æ ·å¼ */
.picking-stats {
    margin-top: 20px;
    padding: 0 10px;
@@ -1239,11 +1312,194 @@
    font-size: 14px;
}
/* åˆ†å‰²çº¿æ ·å¼ */
::v-deep .el-divider__text {
    background-color: #f5f5f5;
    padding: 0 20px;
}
/* æ‰˜ç›˜åº“存信息样式 */
.pallet-inventory {
    margin-top: 20px;
    padding: 0 10px;
}
.inventory-container {
    margin-top: 10px;
}
.inventory-table {
    width: 100%;
}
.inventory-table ::v-deep .el-table__header {
    background-color: #f0f9ff;
}
.inventory-table ::v-deep .el-table__header th {
    background-color: #e1f3ff;
    color: #1f2937;
    font-weight: 600;
    font-size: 12px;
    padding: 8px 0;
}
.inventory-table ::v-deep .el-table__body td {
    padding: 6px 0;
    font-size: 12px;
}
.inventory-table ::v-deep .el-table__row {
    cursor: pointer;
}
.inventory-table ::v-deep .el-table__row:hover {
    background-color: #f0f9ff;
}
/* åº“存表格中的标签样式 */
.inventory-table ::v-deep .el-tag--mini {
    font-size: 11px;
    padding: 1px 6px;
    height: 18px;
    line-height: 16px;
}
/* æ•´å‡ºç¡®è®¤å¼¹çª—样式 */
::v-deep .whole-out-dialog {
    border-radius: 8px;
}
::v-deep .whole-out-dialog .el-dialog__header {
    background: linear-gradient(135deg, #E6A23C 0%, #d9971a 100%);
    color: white;
    padding: 15px 20px;
    border-radius: 8px 8px 0 0;
}
::v-deep .whole-out-dialog .el-dialog__title {
    color: white;
    font-weight: bold;
    font-size: 16px;
}
::v-deep .whole-out-dialog .el-dialog__headerbtn .el-dialog__close {
    color: white;
    font-size: 18px;
}
.whole-out-content {
    padding: 0 10px;
}
.whole-out-alert {
    margin-bottom: 20px;
}
.whole-out-alert ::v-deep .el-alert__description {
    margin-top: 5px;
    font-size: 14px;
    color: #666;
}
.info-section {
    margin-bottom: 20px;
}
.section-title {
    color: #303133;
    font-size: 15px;
    font-weight: bold;
    margin-bottom: 10px;
    padding-bottom: 5px;
    border-bottom: 2px solid #E6A23C;
    display: flex;
    align-items: center;
}
.section-title i {
    margin-right: 8px;
    color: #E6A23C;
    font-size: 16px;
}
.info-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}
.info-item {
    display: flex;
    align-items: center;
    padding: 8px 0;
    border-bottom: 1px solid #f0f0f0;
}
.info-item label {
    font-weight: 500;
    color: #606266;
    min-width: 80px;
    font-size: 14px;
}
.info-item .info-value {
    color: #303133;
    font-size: 14px;
    flex: 1;
}
.info-item .info-value.highlight {
    color: #E6A23C;
    font-weight: bold;
    font-size: 15px;
}
.operation-tip {
    background-color: #fff7e6;
    border: 1px solid #ffd591;
    border-radius: 6px;
    padding: 12px 15px;
    margin-top: 20px;
    display: flex;
    align-items: center;
}
.operation-tip i {
    color: #E6A23C;
    margin-right: 8px;
    font-size: 16px;
    flex-shrink: 0;
}
.operation-tip span {
    color: #606266;
    font-size: 14px;
    line-height: 1.5;
}
/* å“åº”式调整 */
@media (max-width: 768px) {
    .info-grid {
        grid-template-columns: 1fr;
        gap: 8px;
    }
    .info-item {
        padding: 6px 0;
    }
    .info-item label {
        min-width: 70px;
        font-size: 13px;
    }
    .info-item .info-value {
        font-size: 13px;
    }
}
/* Element Plus Loading é®ç½©å±‚样式修复 */
::v-deep .custom-loading {
    background-color: rgba(0, 0, 0, 0.7) !important;
    z-index: 9999 !important;
@@ -1263,6 +1519,7 @@
    font-size: 16px !important;
}
/* å…¨å±€ Loading æ ·å¼ */
::v-deep .el-loading-mask {
    background-color: rgba(0, 0, 0, 0.7) !important;
    z-index: 9999 !important;
@@ -1277,6 +1534,7 @@
    font-weight: bold !important;
}
/* å…¨å±Loading自定义样式 */
::v-deep .custom-full-loading {
    z-index: 999999 !important;
}
@@ -1295,6 +1553,7 @@
    font-size: 18px !important;
}
/* ç¡®ä¿å¯¹è¯æ¡†ä¸ä¼šé®ç›–loading */
::v-deep .el-dialog {
    z-index: 2000 !important;
}
@@ -1302,4 +1561,10 @@
::v-deep .el-dialog__wrapper {
    z-index: 2000 !important;
}
/* ç¡®ä¿å®¹å™¨ç›¸å¯¹å®šä½ */
.picking-container {
    position: relative !important;
    min-height: 100vh;
}
</style>