pan
2025-12-04 561ff75c5e3c0857ba813290fcf25dae3443f73e
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/outbound/BatchPickingConfirm.vue
@@ -92,7 +92,7 @@
    </div>
    <!-- æ‹†åŒ…弹窗 -->
    <div  v-show="showCustomSplitDialog" class="custom-dialog-overlay">
    <div  v-show="showCustomSplitDialog" class="custom-dialog-overlay" style="z-index: 2001;">
      <div class="custom-dialog-wrapper">
        <div class="custom-dialog">
          <div class="custom-dialog-header">
@@ -154,7 +154,7 @@
    </div>
    <!-- æ’¤é”€æ‹†åŒ…弹窗 -->
    <div  v-show="showRevertSplitDialog" class="custom-dialog-overlay">
    <div  v-show="showRevertSplitDialog" class="custom-dialog-overlay" style="z-index: 2001;">
      <div class="custom-dialog-wrapper">
        <div class="custom-dialog">
          <div class="custom-dialog-header">
@@ -214,7 +214,7 @@
    </div>
    <!-- æ‹†åŒ…链信息弹窗 -->
<div v-show="showSplitChainDialog" class="custom-dialog-overlay">
<div v-show="showSplitChainDialog" class="custom-dialog-overlay" style="z-index: 2003;">
  <div class="custom-dialog-wrapper">
    <div class="custom-dialog" style="width: 750px;">
      <div class="custom-dialog-header">
@@ -338,7 +338,7 @@
</div>
    <!-- æ‰¹é‡å›žåº“弹窗 -->
    <div v-if="showBatchReturnDialog" class="custom-dialog-overlay">
    <div v-if="showBatchReturnDialog" class="custom-dialog-overlay"  style="z-index: 2004;">
      <div class="custom-dialog-wrapper">
        <div class="custom-dialog">
          <div class="custom-dialog-header">
@@ -370,7 +370,7 @@
    </div>
    <!-- å–走空箱弹窗 -->
    <div v-if="showEmptyPalletDialog" class="custom-dialog-overlay">
    <div v-if="showEmptyPalletDialog" class="custom-dialog-overlay"  style="z-index: 2005;">
      <div class="custom-dialog-wrapper">
        <div class="custom-dialog">
          <div class="custom-dialog-header">
@@ -412,11 +412,13 @@
import { useRoute } from 'vue-router'
import printView from "@/extension/outbound/extend/printView.vue"
export default defineComponent({
  name: 'BatchOutboundPicking',
  components: {printView},
  data() {
    return {
      // ä¿æŒåŽŸå§‹æ•°æ®ç»“æž„ä¸å˜
      scanData: {
        orderNo: '',
        palletCode: '',
@@ -432,19 +434,19 @@
      },
      palletStatus: '未知',
      
      // å¼¹çª—状态
      // å¼¹çª—状态 - ä¿æŒåŽŸå§‹çŠ¶æ€
      showCustomSplitDialog: false,
      showRevertSplitDialog: false,
      showBatchReturnDialog: false,
      showEmptyPalletDialog: false,
      showSplitChainDialog: false, // æ–°å¢žï¼šæ‹†åŒ…链信息弹窗
      showSplitChainDialog: false,
      
      // åŠ è½½çŠ¶æ€
      splitLoading: false,
      revertSplitLoading: false,
      batchReturnLoading: false,
      emptypalletOutLoading: false,
      splitChainLoading: false, // æ–°å¢žï¼šæ‹†åŒ…链加载状态
      splitChainLoading: false,
      
      // è¡¨å•数据
      splitForm: {
@@ -472,7 +474,7 @@
        palletCode: ''
      },
      
      // æ–°å¢žï¼šæ‹†åŒ…链相关数据
      // æ‹†åŒ…链相关数据
      splitChainInfo: {
        originalBarcode: '',
        totalSplitTimes: 0,
@@ -501,88 +503,14 @@
          { required: true, message: '请输入托盘码', trigger: 'blur' }
        ]
      },
       // å¼¹çª—状态 - æ”¹ä¸ºä½¿ç”¨å¯¹è±¡ç®¡ç†ï¼Œé¿å…åŒæ—¶æ‰“开多个弹窗
      dialogs: {
        customSplit: false,
        revertSplit: false,
        splitChain: false,
        batchReturn: false,
        emptyPallet: false
      },
      
      // åŠ è½½çŠ¶æ€
      loading: {
        split: false,
        revertSplit: false,
        batchReturn: false,
        emptyPallet: false,
        splitChain: false
      },
      isProcessing: false,
      
      // å½“前活动的弹窗类型
      activeDialog: null,
      isProcessing: false
      // æ–°å¢ž: ç”¨äºŽé˜²æ­¢å¼¹çª—重叠
      isDialogOpening: false
    }
  },
  computed: {
     // è®¡ç®—属性映射到dialogs对象
    showCustomSplitDialog: {
      get() { return this.dialogs.customSplit; },
      set(val) {
        this.dialogs.customSplit = val;
        this.activeDialog = val ? 'customSplit' : null;
      }
    },
    showRevertSplitDialog: {
      get() { return this.dialogs.revertSplit; },
      set(val) {
        this.dialogs.revertSplit = val;
        this.activeDialog = val ? 'revertSplit' : null;
      }
    },
    showSplitChainDialog: {
      get() { return this.dialogs.splitChain; },
      set(val) {
        this.dialogs.splitChain = val;
        this.activeDialog = val ? 'splitChain' : null;
      }
    },
    showBatchReturnDialog: {
      get() { return this.dialogs.batchReturn; },
      set(val) {
        this.dialogs.batchReturn = val;
        this.activeDialog = val ? 'batchReturn' : null;
      }
    },
    showEmptyPalletDialog: {
      get() { return this.dialogs.emptyPallet; },
      set(val) {
        this.dialogs.emptyPallet = val;
        this.activeDialog = val ? 'emptyPallet' : null;
      },
    },
     // åŠ è½½çŠ¶æ€æ˜ å°„
    splitLoading: {
      get() { return this.loading.split; },
      set(val) { this.loading.split = val; }
    },
    revertSplitLoading: {
      get() { return this.loading.revertSplit; },
      set(val) { this.loading.revertSplit = val; }
    },
    batchReturnLoading: {
      get() { return this.loading.batchReturn; },
      set(val) { this.loading.batchReturn = val; }
    },
    emptypalletOutLoading: {
      get() { return this.loading.emptyPallet; },
      set(val) { this.loading.emptyPallet = val; }
    },
    splitChainLoading: {
      get() { return this.loading.splitChain; },
      set(val) { this.loading.splitChain = val; }
    },
    // æ˜¯å¦å¯ä»¥å–消整个拆包链
    canCancelWholeChain() {
      return this.splitChainInfo.splitChain && 
             this.splitChainInfo.splitChain.some(item => !item.isReverted);
@@ -604,7 +532,7 @@
      this.$router.back()
    },
    // åˆ†æ‹£ç›¸å…³æ–¹æ³•
    // ä¿®å¤: æ·»åŠ é˜²é‡å¤ç‚¹å‡»æœºåˆ¶
    async confirmPicking() {
      if (this.isProcessing) return;
      
@@ -644,29 +572,41 @@
      }
    },
    // æ‹†åŒ…相关方法
    // ä¿®å¤: æ‰“开拆包弹窗 - æ·»åŠ é˜²é‡å æœºåˆ¶
    openSplitDialog() {
      if (this.isDialogOpening) return;
      if (!this.scanData.palletCode) {
        this.$message.warning('请先扫描托盘码');
        return;
      }
         // å…³é—­å…¶ä»–所有弹窗
      this.closeAllDialogs();
        // å»¶è¿Ÿæ‰“开新弹窗,确保其他弹窗完全关闭
      this.$nextTick(() => {
        this.showCustomSplitDialog = true;
        this.resetSplitForm();
        this.splitForm.orderNo = this.scanData.orderNo;
        this.splitForm.palletCode = this.scanData.palletCode;
      this.isDialogOpening = true;
      try {
        // ç¡®ä¿å…³é—­å…¶ä»–弹窗
        this.closeAllDialogs();
        
        // ç¡®ä¿è¾“入框获得焦点
        // å»¶è¿Ÿæ‰“开,确保DOM更新完成
        this.$nextTick(() => {
          const inputs = document.querySelectorAll('.custom-dialog input');
          if (inputs.length > 0) {
            inputs[0].focus();
          }
          this.showCustomSplitDialog = true;
          this.resetSplitForm();
          this.splitForm.orderNo = this.scanData.orderNo;
          this.splitForm.palletCode = this.scanData.palletCode;
          // ç¡®ä¿è¾“入框获得焦点
          this.$nextTick(() => {
            const input = document.querySelector('#splitOriginalBarcode');
            if (input) {
              input.focus();
            }
            this.isDialogOpening = false;
          });
        });
      });
      } catch (error) {
        console.error('打开拆包弹窗失败:', error);
        this.isDialogOpening = false;
      }
    },
    async onSplitBarcodeScan() {
@@ -697,17 +637,18 @@
    async handleSplitPackage() {
      if (this.$refs.splitFormRef) {
        const valid = await this.$refs.splitFormRef.validate();
        if (!valid) return;
        this.splitLoading = true;
        try {
          const valid = await this.$refs.splitFormRef.validate();
          if (!valid) return;
          this.splitLoading = true;
          const res = await http.post('/api/OutboundBatchPicking/split-package', {
            orderNo: this.splitForm.orderNo,
            palletCode: this.splitForm.palletCode,
            originalBarcode: this.splitForm.originalBarcode,
            splitQuantity: this.splitForm.splitQuantity
          });
          if (res.status) {
            this.$message.success('拆包成功');
            this.showCustomSplitDialog = false;
@@ -716,45 +657,57 @@
            this.$message.error(res.message || '拆包失败');
          }
        } catch (error) {
          this.$message.error('拆包失败: ' + (error.message || '网络错误'));
          this.$message.error('拆包失败');
        } finally {
          this.splitLoading = false;
        }
      }
    },
// åœ¨æ‹†åŒ…弹窗中查看拆包链
async viewSplitChainFromSplit(barcode) {
  if (!barcode) {
    this.$message.warning('请先输入条码');
    return;
  }
  // å…ˆå…³é—­æ‹†åŒ…弹窗
  this.closeCustomSplitDialog();
  await this.$nextTick();
  // ç„¶åŽæ‰“开拆包链信息弹窗
  await this.viewSplitChain(barcode);
},
    // æ’¤é”€æ‹†åŒ…
    // ä¿®å¤: æ’¤é”€æ‹†åŒ…弹窗
    openRevertSplitDialog() {
      if (this.isDialogOpening) return;
      this.isDialogOpening = true;
      try {
        this.closeAllDialogs();
        this.$nextTick(() => {
          this.showRevertSplitDialog = true;
          this.revertSplitForm.newBarcode = '';
          this.$nextTick(() => {
            const input = document.querySelector('#revertSplitBarcode');
            if (input) {
              input.focus();
            }
            this.isDialogOpening = false;
          });
        });
      } catch (error) {
        console.error('打开撤销拆包弹窗失败:', error);
        this.isDialogOpening = false;
      }
    },
    async onRevertSplitBarcodeScan() {
        if (!this.revertSplitForm.newBarcode) return;
      if (!this.revertSplitForm.newBarcode) return;
      this.revertSplitForm.newBarcode = this.revertSplitForm.newBarcode.replace(/\n/g, '').trim();
    },
    async handleRevertSplit() {
       if (this.$refs.revertSplitFormRef) {
        const valid = await this.$refs.revertSplitFormRef.validate();
        if (!valid) return;
        this.revertSplitLoading = true;
      if (this.$refs.revertSplitFormRef) {
        try {
          const valid = await this.$refs.revertSplitFormRef.validate();
          if (!valid) return;
          this.revertSplitLoading = true;
          const res = await http.post('/api/OutboundBatchPicking/cancel-split', {
            orderNo: this.scanData.orderNo,
            palletCode: this.scanData.palletCode,
            newBarcode: this.revertSplitForm.newBarcode
          });
          if (res.status) {
            this.$message.success('撤销拆包成功');
            this.showRevertSplitDialog = false;
@@ -763,40 +716,22 @@
            this.$message.error(res.message || '撤销拆包失败');
          }
        } catch (error) {
          this.$message.error('撤销拆包失败: ' + (error.message || '网络错误'));
          this.$message.error('撤销拆包失败');
        } finally {
          this.revertSplitLoading = false;
        }
      }
    },
// æŸ¥æ‰¾å®Œæ•´æ‹†åŒ…链(从根条码开始)
async findRootChain(currentBarcode) {
  this.splitChainLoading = true;
  try {
    const res = await http.post('/api/OutboundBatchPicking/find-root-split-chain', {
      orderNo: this.scanData.orderNo,
      barcode: currentBarcode
    });
    if (res.status) {
      this.splitChainInfo = res.data;
      this.$message.success('已加载完整拆包链');
    } else {
      this.$message.error(res.message || '查找完整拆包链失败');
    }
  } catch (error) {
    this.$message.error('查找完整拆包链失败');
  } finally {
    this.splitChainLoading = false;
  }
},
    // æ‹†åŒ…链相关方法
   // æŸ¥çœ‹æ‹†åŒ…链信息
async viewSplitChain(barcode) {
   if (!barcode) {
    // ä¿®å¤: æŸ¥çœ‹æ‹†åŒ…链
    async viewSplitChain(barcode) {
      if (!barcode) {
        this.$message.warning('请先输入条码');
        return;
      }
      if (this.isDialogOpening) return;
      this.isDialogOpening = true;
      
      this.splitChainLoading = true;
      try {
@@ -808,85 +743,40 @@
        if (res.status) {
          this.splitChainInfo = res.data;
          
          // å…³é—­å…¶ä»–弹窗后再打开拆包链弹窗
          // å…³é—­å…¶ä»–弹窗
          this.closeAllDialogs();
          
          await this.$nextTick(() => {
            this.showSplitChainDialog = true;
            // æ˜¾ç¤ºæç¤ºä¿¡æ¯
            let chainType = "当前条码的拆包链";
            if (this.splitChainInfo.chainType === 'root') {
              chainType = "完整拆包链(从原始条码开始)";
            } else if (this.splitChainInfo.chainType === 'branch') {
              chainType = "分支拆包链";
            }
            this.$message.info(`已加载${chainType},共${this.splitChainInfo.totalSplitTimes}次拆包`);
            this.isDialogOpening = false;
          });
        } else {
          this.$message.error(res.message || '获取拆包链信息失败');
          this.isDialogOpening = false;
        }
      } catch (error) {
        this.$message.error('获取拆包链信息失败: ' + (error.message || '网络错误'));
        this.$message.error('获取拆包链信息失败');
        this.isDialogOpening = false;
      } finally {
        this.splitChainLoading = false;
      }
},
    },
    // å…³é—­æ‹†åŒ…链信息弹窗
    // ä¿®å¤: å…³é—­æ‹†åŒ…链信息弹窗
    closeSplitChainDialog() {
      this.showSplitChainDialog = false;
    },
    // åœ¨æ’¤é”€æ‹†åŒ…弹窗中查看拆包链
async viewSplitChainFromRevert(barcode) {
  if (!barcode) {
    this.$message.warning('请先输入条码');
    return;
  }
  // å…ˆå…³é—­æ’¤é”€æ‹†åŒ…弹窗
  this.closeRevertSplitDialog();
  await this.$nextTick();
  // ç„¶åŽæ‰“开拆包链信息弹窗
  await this.viewSplitChain(barcode);
},
// å¿«é€Ÿé‡æ–°æ‰“开拆包链弹窗
async quickReopenSplitChainDialog(barcode) {
  if (!barcode) return;
  this.showSplitChainDialog = true;
  this.splitChainLoading = true;
  try {
    const res = await http.post('/api/OutboundBatchPicking/split-package-chain-info', {
      orderNo: this.scanData.orderNo,
      barcode: barcode
    });
    if (res.status) {
      this.splitChainInfo = res.data;
    }
  } catch (error) {
    console.error('重新加载拆包链信息失败:', error);
  } finally {
    this.splitChainLoading = false;
  }
},
    // å–消单个拆包记录
async cancelSingleSplit(newBarcode) {
   try {
    // ä¿®å¤: å–消单个拆包记录
    async cancelSingleSplit(newBarcode) {
      try {
        await ElMessageBox.confirm(
          `确定要取消条码 ${newBarcode} çš„æ‹†åŒ…操作吗?`, 
          '取消单个拆包', 
          {
            confirmButtonText: '确定取消',
            cancelButtonText: '再想想',
            type: 'warning',
            customClass: 'message-box-top'
            type: 'warning'
          }
        );
        
@@ -911,17 +801,15 @@
        }
      } catch (error) {
        if (error !== 'cancel') {
          this.$message.error('取消拆包失败: ' + error.message);
          this.$message.error('取消拆包失败');
        }
      } finally {
        this.revertSplitLoading = false;
      }
},
    },
// å–消整个拆包链
async cancelWholeSplitChain() {
  if (!this.canCancelWholeChain) return;
    // ä¿®å¤: å–消整个拆包链
    async cancelWholeSplitChain() {
      try {
        await ElMessageBox.confirm(
          `确定要取消整个拆包链吗?\n这将取消从条码 ${this.splitChainInfo.originalBarcode} å¼€å§‹çš„æ‰€æœ‰æ‹†åŒ…操作。`, 
@@ -929,10 +817,7 @@
          {
            confirmButtonText: '确定取消',
            cancelButtonText: '再想想',
            type: 'warning',
            center: true,
            closeOnClickModal: false,
            customClass: 'message-box-top'
            type: 'warning'
          }
        );
        
@@ -953,12 +838,12 @@
        }
      } catch (error) {
        if (error !== 'cancel') {
          this.$message.error('取消拆包链失败: ' + error.message);
          this.$message.error('取消拆包链失败');
        }
      } finally {
        this.revertSplitLoading = false;
      }
},
    },
    // æ£€æŸ¥æ¡ç æ˜¯å¦å·²è¢«åˆ†æ‹£
    hasPicked(barcode) {
@@ -971,22 +856,23 @@
      const date = new Date(dateTime);
      return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date.getSeconds().toString().padStart(2, '0')}`;
    },
 closeAllDialogs() {
    // ä¿®å¤: æ–°å¢žå…³é—­æ‰€æœ‰å¼¹çª—的方法
    closeAllDialogs() {
      this.showCustomSplitDialog = false;
      this.showRevertSplitDialog = false;
      this.showSplitChainDialog = false;
      this.showBatchReturnDialog = false;
      this.showEmptyPalletDialog = false;
      this.activeDialog = null;
    },
    // å›žåº“相关方法
    openBatchReturnDialog() {
     if (!this.scanData.palletCode) {
      if (!this.scanData.palletCode) {
        this.$message.warning('请先扫描托盘码');
        return;
      }
      
      // å…³é—­å…¶ä»–弹窗
      this.closeAllDialogs();
      
      this.$nextTick(() => {
@@ -1013,7 +899,7 @@
          this.$message.error(res.message || '回库失败');
        }
      } catch (error) {
        this.$message.error('回库失败: ' + (error.message || '网络错误'));
        this.$message.error('回库失败');
      } finally {
        this.batchReturnLoading = false;
      }
@@ -1021,7 +907,6 @@
    // å–空箱方法
    handleEmptyPallet() {
      // å…³é—­å…¶ä»–弹窗
      this.closeAllDialogs();
      
      this.$nextTick(() => {
@@ -1046,7 +931,7 @@
          this.$message.error(res.message || '取走空箱失败');
        }
      } catch (error) {
        this.$message.error('取走空箱失败: ' + (error.message || '网络错误'));
        this.$message.error('取走空箱失败');
      } finally {
        this.emptypalletOutLoading = false;
      }
@@ -1054,7 +939,7 @@
    // æ•°æ®åŠ è½½æ–¹æ³•
    async loadPalletData() {
       if (!this.scanData.orderNo || !this.scanData.palletCode) return;
      if (!this.scanData.orderNo || !this.scanData.palletCode) return;
      
      try {
        await Promise.all([
@@ -1068,7 +953,7 @@
    },
    async loadUnpickedList() {
       try {
      try {
        const res = await http.post('/api/OutboundBatchPicking/pallet-locks', {
          orderNo: this.scanData.orderNo,
          palletCode: this.scanData.palletCode
@@ -1104,7 +989,7 @@
    },
    async loadPalletStatus() {
          try {
      try {
        const res = await http.post('/api/OutboundBatchPicking/pallet-status', {
          orderNo: this.scanData.orderNo,
          palletCode: this.scanData.palletCode
@@ -1120,7 +1005,7 @@
    // æ‰«ç ç›¸å…³æ–¹æ³•
    onPalletScan() {
       this.scanData.palletCode = this.scanData.palletCode.replace(/\n/g, '').trim();
      this.scanData.palletCode = this.scanData.palletCode.replace(/\n/g, '').trim();
      if (!this.scanData.palletCode) return;
      
      this.loadPalletData();
@@ -1130,7 +1015,7 @@
    },
    onBarcodeScan() {
       this.scanData.barcode = this.scanData.barcode.replace(/\n/g, '').trim();
      this.scanData.barcode = this.scanData.barcode.replace(/\n/g, '').trim();
      if (!this.scanData.barcode) return;
      this.confirmPicking();
    },
@@ -1165,8 +1050,7 @@
          {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning',
            customClass: 'message-box-top'
            type: 'warning'
          }
        );
        
@@ -1205,16 +1089,6 @@
    closeCustomSplitDialog() {
      this.showCustomSplitDialog = false;
      this.resetSplitForm();
    },
    openRevertSplitDialog() {
       // å…³é—­å…¶ä»–弹窗
      this.closeAllDialogs();
      this.$nextTick(() => {
        this.showRevertSplitDialog = true;
        this.revertSplitForm.newBarcode = '';
      });
    },
    closeRevertSplitDialog() {
@@ -1278,24 +1152,7 @@
  color: #909399;
}
/* è‡ªå®šä¹‰å¼¹çª—样式 */
:deep(.el-message-box) {
  z-index: 9999 !important;
}
:deep(.el-overlay) {
  z-index: 9998 !important;
}
:deep(.el-message) {
  z-index: 10000 !important;
}
/* ç¡®ä¿ç¡®è®¤å¯¹è¯æ¡†åœ¨æœ€å‰é¢ */
:deep(.message-box-top) {
  z-index: 10001 !important;
}
/* è‡ªå®šä¹‰å¼¹çª—样式 - å…³é”®ä¿®å¤: ç§»é™¤å¯èƒ½å¯¼è‡´å†²çªçš„z-index规则 */
.custom-dialog-overlay {
  position: fixed;
  top: 0;
@@ -1306,12 +1163,11 @@
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2000;
  /* ç§»é™¤è¿™é‡Œçš„z-index,在模板中单独设置 */
}
.custom-dialog-wrapper {
  position: relative;
  z-index: 2001;
}
.custom-dialog {