heshaofeng
2026-02-06 cefe93f0197d675b19fe68d6758aabb010c3fbb0
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/outbound/outPicking.vue
@@ -9,10 +9,10 @@
                    <i class="el-icon-document"></i>
                    <span class="order-label">订单号:</span>
                    <span class="order-value">{{ orderNo }}</span>
                    <span class="order-label" style="margin-left: 20px;">产线名称:</span>
                    <span class="order-value">{{ orderInfo?.departmentName || '' }}</span>
                </div>
                <div class="order-status">
                    <!-- æµ‹è¯•按钮 -->
                    <el-tag v-if="orderInfo" :type="getStatusType(orderInfo.orderStatus)" size="medium"
                        style="margin-left: 10px;">
                        {{ orderInfo.statusName || '进行中' }}
@@ -157,14 +157,6 @@
                            </el-table-column>
                            <el-table-column prop="unit" label="单位" width="60" />
                            <el-table-column prop="locationCode" label="库位" />
                            <!-- <el-table-column label="操作" width="80" align="center">
                                <template #default="scope">
                                    <el-button type="text" size="small" @click="quickPick(scope.row)"
                                        :disabled="!scanForm.palletCode">
                                        æ‹£é€‰
                                    </el-button>
                                </template>
                            </el-table-column> -->
                        </el-table>
                        <div class="table-footer">
@@ -222,13 +214,6 @@
                            <el-table-column prop="createDate" label="拣选时间" width="160" />
                            <el-table-column prop="originalBarcode" label="原物料码" width="160" />
                            <el-table-column prop="newBarcode" label="新物料码" width="160" />
                            <!-- <el-table-column label="操作" width="80" align="center">
                                <template #default="scope">
                                    <el-button type="text" size="small" @click="undoPick(scope.row)">
                                        æ’¤é”€
                                    </el-button>
                                </template>
                            </el-table-column> -->
                        </el-table>
                        <div class="table-footer">
@@ -244,53 +229,6 @@
                    </el-card>
                </el-col>
            </el-row>
            <!-- æ‰˜ç›˜ç‰©æ–™åº“存信息 -->
            <!-- <div class="pallet-inventory" v-if="scanForm.palletCode && unpickedData.length > 0">
                <el-divider content-position="left">
                    <span style="color: #67C23A; font-size: 14px;">
                        <i class="el-icon-goods"></i> æ‰˜ç›˜ç‰©æ–™åº“存信息
                    </span>
                </el-divider>
                <div class="inventory-container">
                    <el-table :data="unpickedData" size="small" :show-header="true" :border="true" stripe
                        highlight-current-row max-height="200" class="inventory-table">
                        <el-table-column type="index" label="序号" width="50" align="center" />
                        <el-table-column prop="materielCode" label="物料编码" width="100" show-overflow-tooltip />
                        <el-table-column prop="materielName" label="物料名称" width="120" show-overflow-tooltip />
                        <el-table-column prop="batchNo" label="批次号" width="90" />
                        <el-table-column label="当前库存" width="80" align="right">
                            <template #default="scope">
                                <el-text type="primary" tag="b">{{ scope.row.currentStock || 0 }}</el-text>
                            </template>
                        </el-table-column>
                        <el-table-column label="分拣数量" width="80" align="right">
                            <template #default="scope">
                                <el-text type="warning">{{ scope.row.assignQuantity }}</el-text>
                            </template>
                        </el-table-column>
                        <el-table-column label="已分拣" width="70" align="right">
                            <template #default="scope">
                                <el-text type="success">{{ scope.row.sortedQuantity || 0 }}</el-text>
                            </template>
                        </el-table-column>
                        <el-table-column label="剩余库存" width="80" align="right">
                            <template #default="scope">
                                <el-text type="info">{{ calculateRemainingStock(scope.row) }}</el-text>
                            </template>
                        </el-table-column>
                        <el-table-column prop="unit" label="单位" width="100" align="center" />
                        <el-table-column prop="locationCode" label="库位" width="150" />
                        <el-table-column label="状态" width="80" align="center">
                            <template #default="scope">
                                <el-tag :type="getStockStatusType(scope.row)" size="mini">
                                    {{ getStockStatusText(scope.row) }}
                                </el-tag>
                            </template>
                        </el-table-column>
                    </el-table>
                </div>
            </div> -->
        </div>
        <print-view ref="printView" @parentcall="parentcall"></print-view>
@@ -391,10 +329,13 @@
</template>
<script>
import printView from "@/extension/outbound/extend/printView.vue"
import { stationManager, STATION_STORAGE_KEY } from "@/../src/uitils/stationManager";
import { ElLoading } from 'element-plus'
// å¯¼å…¥éŸ³é¢‘文件(适配src/assets目录,webpack自动处理)
const successAudioSrc = require('@/assets/audio/success.mp3');
const errorAudioSrc = require('@/assets/audio/error.mp3');
export default {
    components: { printView },
@@ -432,7 +373,10 @@
            wholeOutDialogVisible: false,
            wholeOutInfo: null,
            globalLoading: false,
            loadingInstance: null
            loadingInstance: null,
            // éŸ³é¢‘实例(缓存,避免重复创建)
            successAudio: null,
            errorAudio: null
        }
    },
    computed: {
@@ -442,16 +386,100 @@
    },
    mounted() {
        this.initPage()
        // åˆå§‹åŒ–音频实例(懒加载,仅创建一次)
        this.initAudioInstance()
    },
    beforeDestroy() {
        // é”€æ¯éŸ³é¢‘实例,释放资源
        this.successAudio = null
        this.errorAudio = null
    },
    methods: {
        // åˆå§‹åŒ–音频实例(核心:适配src/assets路径,缓存实例)
        initAudioInstance() {
            // æˆåŠŸéŸ³é¢‘å®žä¾‹
            if (!this.successAudio) {
                this.successAudio = new Audio(successAudioSrc)
                this.successAudio.onerror = (err) => {
                    console.error('【成功音频】加载失败', err)
                }
            }
            if (!this.errorAudio) {
                this.errorAudio = new Audio(errorAudioSrc)
                this.errorAudio.onerror = (err) => {
                    console.error('【错误音频】加载失败', err)
                }
            }
        },
        // ============== æ–°å¢žï¼šæ ¹æ®æ‰˜ç›˜å·èŽ·å–è®¢å•å·ï¼ˆæ ¸å¿ƒåŠŸèƒ½ï¼‰ ==============
        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 {
                // é‡ç½®æ’­æ”¾è¿›åº¦ï¼ˆé¿å…é‡å¤æ’­æ”¾æ—¶éŸ³é¢‘未结束)
                this.successAudio.currentTime = 0
                // æ’­æ”¾ï¼ˆå…¼å®¹æµè§ˆå™¨è‡ªåŠ¨æ’­æ”¾ç­–ç•¥é™åˆ¶ï¼‰
                this.successAudio.play().catch((err) => {
                    console.warn('成功音频播放失败(浏览器自动播放策略限制)', err)
                })
            } catch (err) {
                console.error('播放成功音频异常', err)
            }
        },
        playErrorAudio() {
            try {
                this.errorAudio.currentTime = 0
                this.errorAudio.play().catch((err) => {
                    console.warn('错误音频播放失败(浏览器自动播放策略限制)', err)
                })
            } catch (err) {
                console.error('播放错误音频异常', err)
            }
        },
        initPage() {
            // ä»Žè·¯ç”±å‚数获取订单号
            this.orderNo = this.$route.query.orderNo || ''
            if (!this.orderNo) {
                this.$message.error('订单号不能为空')
                this.$router.back()
                return
            }
            // ============== å¾®è°ƒï¼šæ³¨é‡Šå¼ºåˆ¶è¿”回逻辑,兼容托盘号查订单号 ==============
            // if (!this.orderNo) {
            //     this.$message.error('订单号不能为空')
            //     this.$router.back()
            //     return
            // }
            // ============== å¾®è°ƒç»“束 ==============
            // åŠ è½½è®¢å•ä¿¡æ¯
            this.loadOrderInfo()
@@ -499,42 +527,81 @@
            }
        },
        loadUnpickedData() {
            return new Promise((resolve, reject) => {
                this.http.post(`/api/Outbound/QueryPickingTasks?orderNo=${this.orderNo}&palletCode=${this.scanForm.palletCode}`, {}).then(response => {
                    if (response.status) {
                        if (response.data.outStockLockInfos.length > 0) {
                            this.unpickedData = response.data.outStockLockInfos;
                            this.matMixed = response.data.isMatMixed;
                            this.calculateUnpickedStats()
                            // æ£€æŸ¥æ˜¯å¦éœ€è¦æ•´å‡ºç¡®è®¤
                            this.$nextTick(() => {
                                if (this.hasWholeOut()) {
                                    this.showWholeOutConfirm()
                                }
                            })
                            // è‡ªåŠ¨èšç„¦åˆ°ç‰©æ–™æ¡ç è¾“å…¥æ¡†
                            this.$nextTick(() => {
                                if (this.$refs.materialInput) {
                                    this.$refs.materialInput.focus()
                                }
                            })
                        } else {
                            if (flag) {
                                this.$message.warning('该托盘无未拣选任务')
                            }
                            this.unpickedData = []
    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.$nextTick(() => {
                        if (this.hasWholeOut()) {
                            this.showWholeOutConfirm()
                        }
                        resolve()
                    } else {
                        this.$message.error(response.message || '获取托盘数据失败')
                        this.unpickedData = []
                        reject(response.message || '获取托盘数据失败')
                    }
                }).catch(error => {
                    reject(error)
                })
            })
        },
                    })
                    // èšç„¦ç‰©æ–™æ¡ç è¾“入框
                    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 // å…è®¸æ‰‹åЍ关闭
                    });
                    // æ¸…空物料条码并聚焦回托盘码输入框
                    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) => {
@@ -545,8 +612,10 @@
                            this.calculatePickedStats()
                        } else {
                            this.pickedData = []
                        }
                        resolve()
                    } else {
                        this.$message.error(response.message || '获取托盘数据失败')
                        this.pickedData = []
@@ -590,11 +659,20 @@
        },
        handlePalletScan(flag = true) {
            if (this.scanForm.palletCode) {
                // this.$message.success(`托盘码: ${this.scanForm.palletCode}`)
                this.loadPalletData(flag)
            } else {
            const palletCode = this.scanForm.palletCode.trim();
            if (!palletCode) {
                return;
            }
            // å…ˆæ ¹æ®æ‰˜ç›˜å·èŽ·å–è®¢å•å·
            this.getOrderNoByPallet(palletCode).then((orderNo) => {
                if (orderNo) {
                    // èµ‹å€¼è®¢å•号,供后续逻辑使用
                    this.orderNo = orderNo;
                    // åˆ·æ–°è®¢å•信息
                    this.loadOrderInfo();
                }
                this.loadPalletData(flag);
            });
        },
        handleMaterialScan() {
@@ -634,15 +712,17 @@
                        this.$refs.printView.open(response.data.scannedDetail.materialCodes);
                    }
                    this.$message.success('拣选确认成功')
                    // æŽ¥å£æˆåŠŸï¼šæ’­æ”¾æˆåŠŸéŸ³é¢‘
                    this.playSuccessAudio()
                    this.resetMaterialBarcode()
                    // this.loadUnpickedData()
                    // this.loadPickedData()
                    await this.loadPalletData(false)
                } else {
                    this.$message.error(response.message || '拣选确认失败')
                    this.playErrorAudio()
                }
            } catch (error) {
                this.$message.error('拣选确认失败')
                this.playErrorAudio()
            } finally {
                this.confirmLoading = false
                this.hideFullScreenLoading()
@@ -694,8 +774,6 @@
                    this.$message.success('操作成功')
                    this.confirmDialogVisible = false
                    this.resetForm()
                    // this.loadUnpickedData()
                    // this.loadPickedData()
                } else {
                    this.$message.error(response.message || '操作失败')
                }
@@ -801,11 +879,6 @@
            }
        },
        // handleUnpickedRowClick(row) {
        //     // ç‚¹å‡»æœªæ‹£é€‰è¡Œæ—¶è‡ªåŠ¨å¡«å……ç‰©æ–™æ¡ç 
        //     this.scanForm.materialBarcode = row.materielCode
        // },
        refreshUnpickedTable() {
            if (this.scanForm.palletCode) {
                this.loadPalletData()
@@ -882,7 +955,7 @@
        // æ£€æŸ¥æ˜¯å¦åŒ…含整出
        hasWholeOut() {
            return this.unpickedData.some(item => item.assignQuantity === item.originalQuantity) && !this.matMixed
            return this.unpickedData.some(item => item.assignQuantity === item.originalQuantity) && !this.matMixed && !this.orderOver
        },
        // è®¡ç®—剩余库存