1
heshaofeng
2026-01-20 5dfd83bd540c2e43af2e0449c246c79a22cb1296
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/inbound/extend/Pallet.vue
@@ -1,12 +1,11 @@
<template>
  <vol-box v-model="groupPalletVisible" :title="'组盘操作 - å•据号:' + currentDocNo" :height="1000" :width="1100" :padding="20"
    :modal="true" @open="handleDialogOpen" @close="handleDialogClose">
  <vol-box v-model="show" :title="'组盘操作 - å•据号:' + orderNo" :height="1000" :width="1100" :padding="20" :modal="true">
    <div class="barcode-scanner-container">
      <!-- ä»“库选择 - ç´§å‡‘布局 -->
      <div class="location-section compact">
        <el-form :model="form" :rules="rules" ref="locationForm" class="compact-form">
          <el-form-item label="仓库" prop="warehouseType" class="location-select compact-item">
          <el-form-item label="入库仓库" prop="warehouseType" class="location-select compact-item">
            <el-select v-model="form.warehouseType" placeholder="请选择仓库" clearable filterable
              @change="handleWarehouseChange" style="width: 100%" :loading="warehouseLoading" size="medium">
              <el-option v-for="item in warehouseTypes" :key="item.warehouseType" :label="item.warehouseTypeDesc"
@@ -53,7 +52,7 @@
          <!-- æ‰˜ç›˜æ¡ç è¾“å…¥ -->
          <div class="input-wrapper custom-input-group compact-input">
            <div class="input-label">料箱码</div>
            <div class="input-label">托盘条码</div>
            <el-input ref="trayInput" v-model="trayBarcode" placeholder="请扫描或输入料箱码后按回车键" clearable
              :disabled="!form.locationType || !form.warehouseType" @keyup.enter.native="handleTraySubmit"
              @clear="handleTrayClear" @input="handleTrayInput" class="custom-input" size="medium">
@@ -71,11 +70,11 @@
            <div class="input-label">物料条码</div>
            <el-input ref="barcodeInput" v-model="barcode" placeholder="请扫描或输入物料条码后按回车键" clearable
              :disabled="!form.locationType || !trayBarcode || !form.warehouseType"
              @keyup.enter.native="handleBarcodeSubmit" @clear="handleClear" @input="handleBarcodeInput"
              @keyup.enter.native="debounceHandleBarcodeSubmit" @clear="handleClear" @input="handleBarcodeInput"
              class="custom-input" size="medium">
              <template slot="append">
                <el-button :loading="loading" @click="handleBarcodeSubmit" type="primary" icon="el-icon-search"
                  :disabled="!form.locationType || !trayBarcode || !barcode || !from.warehouseType" size="medium">
                <el-button :loading="loading" @click="debounceHandleBarcodeSubmit" type="primary" icon="el-icon-search"
                  :disabled="!form.locationType || !trayBarcode || !barcode || !form.warehouseType" size="medium">
                  {{ loading ? '查询中...' : '查询' }}
                </el-button>
              </template>
@@ -85,7 +84,7 @@
          <div class="input-tips compact-tips">
            <p>提示:请先选择仓库 â†’ é€‰æ‹©ä»“库区域 â†’ è¾“入料箱码 â†’ è¾“入物料条码</p>
            <p v-if="!form.warehouseType" class="warning-text">⚠️ è¯·å…ˆé€‰æ‹©ä»“库</p>
            <p v-if="!form.locationType && !form.warehouseType" class="warning-text">⚠️ è¯·å…ˆé€‰æ‹©ä»“库区域</p>
            <p v-if="form.warehouseType && !form.locationType" class="warning-text">⚠️ è¯·å…ˆé€‰æ‹©ä»“库区域</p>
            <p v-if="form.warehouseType && form.locationType && !trayBarcode" class="warning-text">⚠️ è¯·å…ˆè¾“入料箱码</p>
          </div>
@@ -113,9 +112,9 @@
            </span>
          </div>
          <div  class="table-container">
            <el-table :data="unpalletMaterials" stripe style="width: 100%" height="100%" size="small" v-loading="unpalletBarcodesLoading">
          <div class="table-container">
            <el-table :data="unpalletMaterials" stripe style="width: 100%" height="100%" size="small"
              v-loading="unpalletBarcodesLoading">
              <el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
              <el-table-column prop="barcode" label="条码" min-width="140" show-overflow-tooltip></el-table-column>
              <el-table-column prop="materielCode" label="物料编码" min-width="150" show-overflow-tooltip></el-table-column>
@@ -126,7 +125,7 @@
            </el-table>
          </div>
        </el-card>
      </div>
      <!-- ç‰©æ–™åˆ—表 - å›ºå®šé«˜åº¦å¸¦æ»šåŠ¨æ¡ -->
@@ -146,7 +145,7 @@
          <div v-if="materials.length === 0" class="empty-state compact">
            <i class="el-icon-document"></i>
            <p v-if="!form.warehouseType">请先选择仓库</p>
            <p v-if="!form.locationType">请先选择仓库区域</p>
            <p v-if="form.warehouseType && !form.locationType">请先选择仓库区域</p>
            <p v-else-if="!trayBarcode">请先输入料箱条码</p>
            <p v-else>暂无物料数据,请扫描或输入物料条码</p>
          </div>
@@ -166,35 +165,37 @@
        </el-card>
      </div>
    </div>
    <!--      <div slot="footer" class="dialog-footer">
      <el-button @click="handleCancel">取消</el-button>
      <el-button type="primary" @click="handleConfirm">确认</el-button>
    </div> -->
    <template #footer>
      <el-button type="danger" size="small" @click="handleDialogClose()">关闭</el-button>
    </template>
  </vol-box>
</template>
<script>
import http from '@/api/http.js';
import VolBox from '@/components/basic/VolBox.vue';
import VolForm from '@/components/basic/VolForm.vue';
import VolTable from '@/components/basic/VolTable.vue';
import { ElLoading, ElMessage, ElMessageBox } from 'element-plus';
import { ref, onMounted, onUnmounted } from 'vue'
import InboundOrder from '../../../views/inbound/inboundOrder.vue';
import { th } from 'element-plus/es/locales.mjs';
// æ–°å¢žï¼šé˜²æŠ–函数
function debounce(func, wait) {
  let timeout;
  return function () {
    const context = this;
    const args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(context, args);
    }, wait);
  };
}
export default {
  name: 'BarcodeScanner',
  components: { VolBox, VolForm, VolTable },
  props: {
    docNo: { type: String, required: true, default: '' },
    visible: { type: Boolean, required: true, default: false }
  },
  components: { VolBox },
  data() {
    return {
      show: false,
      orderNo: "",
      palletVisible: this.visible,
      trayBarcodeReg:/^[A-Z]\d{9}$/,
      trayBarcodeReg: /^[A-Z]\d{9}$/,
      trayBarcode: '',
      barcode: '',
      materials: [],
@@ -203,7 +204,7 @@
      debugMode: false,
      currentFocus: 'warehouse',
      unpalletBarcodes:[],
      unpalletBarcodes: [],
      unpalletBarcodesLoading: false,
      unpalletMaterials: [], // æœªç»„盘详细数据列表
@@ -215,6 +216,9 @@
      scanTimer: null,
      manualInputTimer: null,
      scanTarget: 'tray', // å½“前扫码目标: tray æˆ– material
      isSubmitting: false, // æ–°å¢žï¼šæäº¤é”å®šï¼Œé˜²æ­¢é‡å¤è¯·æ±‚
      // ä¿®å¤ï¼šæ”¹ä¸ºæŒ‰æ‰˜ç›˜å­˜å‚¨å·²ç»„盘条码,避免状态混淆
      palletGroupedBarcodes: {}, // æ ¼å¼ï¼š{ [trayBarcode]: [barcode1, barcode2, ...] }
      // åº“存统计相关变量
      totalStockSum: 0,
@@ -239,12 +243,12 @@
            trigger: 'change'
          }
        ],
        trayBarcode:[
        trayBarcode: [
          {
            pattern: this.trayBarcodeReg,
            message: '托盘号格式错误(需为1个大写字母+9个数字,如A000008024)',
            trigger: 'blur'
         }
          }
        ],
        warehouseType: [
          {
@@ -255,12 +259,8 @@
      }
    }
  },
  computed: {
    groupPalletVisible: {
      get() { return this.visible; },
      set(newVal) { this.$emit('update:visible', newVal); }
    },
    currentDocNo() { return this.docNo; },
    // å½“前选择的仓库名称
    currentWarehouseName() {
      const warehouse = this.warehouseTypes.find(item => item.warehouseType === this.form.warehouseType);
@@ -270,6 +270,10 @@
    currentLocationDesc() {
      const location = this.locationTypes.find(item => item.locationType === this.form.locationType)
      return location ? location.locationTypeDesc : ''
    },
    // æ–°å¢žï¼šé˜²æŠ–后的物料提交方法
    debounceHandleBarcodeSubmit() {
      return debounce(this.handleBarcodeSubmit, 500);
    }
  },
  watch: {
@@ -282,10 +286,6 @@
        this.resetData();
        this.$nextTick(() => {
          setTimeout(() => {
            // this.focusTrayInput();
            this.initwarehouseTypes(); // åˆå§‹åŒ–仓库
            this.initLocationTypes(); // åˆå§‹åŒ–仓库区域
            this.fetchStockStatistics(); // åŠ è½½ç»Ÿè®¡æ•°æ®
            this.fetchUnpalletMaterialDetails();
          }, 300);
        });
@@ -305,42 +305,27 @@
        this.palletForm = { palletCode: '', barcode: '' };
        this.backData = [];
        this.$refs.palletForm?.reset();
        this.fetchStockStatistics(); // å•据号变了,刷新统计
        this.fetchUnpalletMaterialDetails();
      }
    }
  },
  'form.warehouseType'(newVal) {
    if (newVal) {
      this.form.locationType = null;
    } else {
      this.locationTypes = [];
    }
  },
  mounted() {
    // æ·»åŠ å…¨å±€é”®ç›˜ç›‘å¬
    document.addEventListener('keypress', this.handleKeyPress);
    // ä½¿ç”¨setTimeout确保DOM完全渲染后再聚焦
    setTimeout(() => {
      // this.focusTrayInput();
      this.focusLocationSelect();
    }, 300);
  },
  beforeDestroy() {
    // æ¸…理事件监听
    document.removeEventListener('keypress', this.handleKeyPress);
    this.clearAllTimers();
  },
  methods: {
    /**
* è‡ªå®šä¹‰ä»“库区域验证
* å…è®¸å€¼ä¸º0,因为0是合法的locationType
*/
    open() {
      this.show = true;
      this.orderNo = "";
      this.resetData();
      this.initLocationTypes();
      this.initwarehouseTypes();
      this.fetchUnpalletMaterialDetails();
    },
    validateLocationType(rule, value, callback) {
      // æ£€æŸ¥å€¼æ˜¯å¦ä¸ºnull、undefined或空字符串,但允许数字0
      if (!this.form.warehouseType) {
        callback(new Error('请先选择仓库'));
      } else if (value === null || value === undefined || value === '') {
@@ -349,32 +334,19 @@
        callback();
      }
    },
    // æ ¹æ®æ¡ç åˆ—表获取详细数据
    async fetchUnpalletMaterialDetails() {
      try {
        // å°è¯•调用接口获取详细数据
        // æ³¨æ„ï¼šå¦‚果这个接口不存在,可以注释掉或根据实际API调整
        const response = await http.post('/api/InboundOrder/UnPalletGroupBarcode?orderNo='+this.docNo, {
        });
        console.log('未组盘数据:', response.data);
        const response = await http.post('/api/InboundOrder/UnPalletGroupBarcode?orderNo=' + this.orderNo, {});
        if (response.status && Array.isArray(response.data)) {
          this.unpalletMaterials = response.data;
          this.unpalletBarcodes = response.data.map(item => item.barcode || '');
          this.totalStockCount = response.data.length;
        } else {
          // å¦‚果接口返回格式不同,尝试其他方式
          // å¦‚果接口不存在,这里会进入catch,设置为空数组
          this.unpalletMaterials = [];
        }
      } catch (err) {
        console.warn('获取未组盘详细数据接口可能不存在,使用条码列表显示:', err);
        // å¦‚果接口不存在,将条码列表转换为简单的显示格式
        // æˆ–者保持为空,让用户知道需要选择仓库和区域来查看详细数据
        this.unpalletMaterials = this.unpalletBarcodes.map(barcode => ({
          barcode: barcode,
          materielCode: '-',
@@ -386,126 +358,28 @@
        }));
      }
    },
    /**
    * åˆå§‹åŒ–仓库区域数据
    */
    async initLocationTypes() {
      this.locationLoading = true;
      this.error = '';
      try {
        const response = await http.post('/api/LocationInfo/GetLocationTypes');
        if (response.status && Array.isArray(response.data)) {
          this.locationTypes = response.data;
          if (this.locationTypes.length === 0) {
            this.error = '未获取到仓库区域数据';
          } else {
            // å¦‚果有默认区域,可以在这里设置
            // this.form.locationType = this.locationTypes[0].locationType;
          }
        } else {
          this.error = '获取仓库区域数据失败';
        }
      } catch (error) {
        console.error('获取仓库区域失败:', error);
        this.error = `获取仓库区域失败: ${error.message || '网络错误'}`;
      } finally {
        this.locationLoading = false;
        // ä¿®å¤ï¼šhttp æ”¹ä¸º this.$http(原代码错误)
        const { data } = await this.http.post("api/LocationInfo/GetLocationTypes")
        this.locationTypes = data
      } catch (e) {
        this.$message.error('获取区域类型失败')
      }
    },
    /**
     * åˆå§‹åŒ–仓库数据
     */
    async initwarehouseTypes() {
      this.warehouseLoading = true;
      this.error = '';
      try {
        const response = await http.post('/api/Warehouse/GetwarehouseTypes');
        if (response.status && Array.isArray(response.data)) {
          this.warehouseTypes = response.data;
          if (this.warehouseTypes.length === 0) {
            this.error = '未获取到仓库数据';
          } else {
            // å¦‚果有默认区域,可以在这里设置
            // this.form.locationType = this.locationTypes[0].locationType;
          }
        } else {
          this.error = '获取仓库数据失败';
        }
      } catch (error) {
        console.error('获取仓库失败:', error);
        this.error = `获取仓库失败: ${error.message || '网络错误'}`;
      } finally {
        this.warehouseLoading = false;
        // ä¿®å¤ï¼šhttp æ”¹ä¸º this.$http(原代码错误)
        const { data } = await this.http.post("api/Warehouse/GetwarehouseTypes")
        this.warehouseTypes = data
      } catch (e) {
        this.$message.error('获取区域类型失败')
      }
    },
    /**
    * ä»“库区域变更处理
    */
    handleLocationChange(value) {
      console.log('选择仓库区域:', value, '类型:', typeof value, this.currentLocationDesc);
      // ç«‹å³æ¸…除错误信息
      this.error = '';
      // æ‰‹åŠ¨è§¦å‘è¡¨å•éªŒè¯æ›´æ–°
      this.$nextTick(() => {
        if (this.$refs.locationForm) {
          // æ¸…除该字段的验证状态,然后重新验证
          this.$refs.locationForm.clearValidate('locationType');
          // çŸ­æš‚延迟后重新验证,确保DOM已更新
          setTimeout(() => {
            this.$refs.locationForm.validateField('locationType', (errorMsg) => {
              if (!errorMsg && (value === 0 || value)) {
                console.log('仓库区域验证通过:', value);
                // åŒºåŸŸé€‰æ‹©åŽï¼Œè‡ªåŠ¨èšç„¦åˆ°æ‰˜ç›˜è¾“å…¥æ¡†
                this.focusLocationSelect();
                // åˆ·æ–°æœªç»„盘数据(根据选择的仓库和区域过滤)
              }
            });
          }, 100);
        }
      });
    },
    /**
     * ä»“库变更处理
     */
    handleWarehouseChange(value) {
      console.log('选择仓库:', value, '类型:', typeof value, this.currentWarehouseName);
      // ç«‹å³æ¸…除错误信息
      this.error = '';
      // æ‰‹åŠ¨è§¦å‘è¡¨å•éªŒè¯æ›´æ–°
      this.$nextTick(() => {
        if (this.$refs.locationForm) {
          // æ¸…除该字段的验证状态,然后重新验证
          this.$refs.locationForm.clearValidate('warehouseType');
          // çŸ­æš‚延迟后重新验证,确保DOM已更新
          setTimeout(() => {
            this.$refs.locationForm.validateField('warehouseType', (errorMsg) => {
              if (!errorMsg && (value === 0 || value)) {
                console.log('仓库验证通过:', value);
                this.focusLocationSelect();
              }
            });
          }, 100);
        }
      });
    },
    async fetchStockStatistics() {
    async fetchStockStatistics(orderNo) {
      // å•据号为空时不查询
      if (!this.docNo) {
      if (!orderNo) {
        this.sumError = '单据号为空,无法统计';
        return;
      }
@@ -513,29 +387,21 @@
      this.sumLoading = true;
      this.sumError = '';
      try {
        // è°ƒç”¨åŽç«¯ç»Ÿè®¡æŽ¥å£ï¼ˆæ›¿æ¢ä¸ºä½ çš„实际接口路径)
        const response = await http.post('/api/InboundOrder/UnPalletQuantity?orderNo=' + this.docNo, {
        });
        // ç»‘定数据(匹配 PalletSumQuantityDTO ç»“构)
        const response = await http.post('/api/InboundOrder/UnPalletQuantity?orderNo=' + orderNo, {});
        if (response.data) {
          this.totalStockSum = response.data.stockSumQuantity || 0; // æ€»åº“存数量
          this.totalStockCount = response.data.stockCount || 0;     // æ€»åº“存记录数
          this.uniqueUnit = response.data.uniqueUnit || '';               // è®¡é‡å•位
          this.totalStockSum = response.data.stockSumQuantity || 0;
          this.totalStockCount = response.data.stockCount || 0;
          this.uniqueUnit = response.data.uniqueUnit || '';
        }
      } catch (err) {
        this.sumError = '统计加载失败';
        this.totalStockSum = 0;
        this.totalStockCount = 0;
        console.error('库存统计查询异常:', err);
      } finally {
        this.sumLoading = false;
      }
    },
    /**
     * è¡¨å•验证
     */
    async validateForm() {
      return new Promise((resolve) => {
        if (!this.$refs.locationForm) {
@@ -550,40 +416,18 @@
            this.error = '';
            resolve(true);
          } else {
            // æ‰‹åŠ¨æ£€æŸ¥locationType,正确处理值为0的情况
            if (!this.from.warehouseType) {
            // ä¿®å¤ï¼šfrom æ”¹ä¸º form(原代码拼写错误)
            if (!this.form.warehouseType) {
              this.error = '请先选择仓库';
            }
            else if (this.form.locationType === null || this.form.locationType === undefined || this.form.locationType === '') {
            } else if (this.form.locationType === null || this.form.locationType === undefined || this.form.locationType === '') {
              this.error = '请先选择仓库区域';
              //this.$message.warning('请先选择仓库区域');
            } else {
              // å¦‚果值存在(包括0),但验证不通过,可能是其他验证错误
              this.error = '请检查表单填写是否正确';
            }
            resolve(false);
          }
        });
      });
    },
    focusWarehouseSelect() {
      if (this.$refs.locationForm) {
        const selectEl = this.$el.querySelector('.location-select:first-child .el-input__inner');
        if (selectEl) {
          selectEl.focus();
          this.currentFocus = 'warehouse';
        }
      }
    },
    // èšç„¦åˆ°ä»“库区域选择
    focusLocationSelect() {
      if (this.$refs.locationForm) {
        const selectEl = this.$el.querySelector('.location-select:nth-child(2) .el-input__inner');
        if (selectEl) {
          selectEl.focus();
          this.currentFocus = 'location';
        }
      }
    },
    // èšç„¦åˆ°æ‰˜ç›˜è¾“入框
    focusTrayInput() {
@@ -606,21 +450,15 @@
          inputEl.focus();
          this.currentFocus = 'material';
          this.scanTarget = 'material';
            inputEl.select();
            console.log('物料输入框内容已选中');
          inputEl.select();
        }
      }
    },
    // é‡ç½®æ‰€æœ‰æ•°æ®
    resetData() {
      console.log('重置弹框数据');
      // ä¿®å¤ï¼šæ¸…空按托盘存储的已组盘条码
      this.palletGroupedBarcodes = {};
      this.isSubmitting = false; // é‡ç½®æäº¤é”
      this.trayBarcode = '';
      this.barcode = '';
      this.materials = [];
@@ -628,12 +466,12 @@
      this.unpalletMaterials = [];
      this.loading = false;
      this.error = '';
      this.scanCode = '';
      this.scanCode = ''; // æ¸…空扫码缓存
      this.lastKeyTime = null;
      this.isManualInput = false;
      this.isScanning = false;
      this.currentFocus = 'warehouse';
      this.scanTarget = 'tray';
      this.scanTarget = 'tray'; // é‡ç½®æ‰«ç ç›®æ ‡
      this.clearAllTimers();
      this.totalStockSum = 0;
      this.totalStockCount = 0;
@@ -664,38 +502,9 @@
        this.scanTimer = null;
      }
    },
    // å¼¹æ¡†æ‰“开时重置数据
    handleDialogOpen() {
      console.log('弹框打开,重置数据');
      this.resetData();
      // ä½¿ç”¨setTimeout确保DOM完全渲染后再聚焦
      this.$nextTick(() => {
        setTimeout(() => {
          this.initwarehouseTypes();
          this.initLocationTypes(); // åˆå§‹åŒ–仓库区域
          // ç¡®ä¿è¡¨å•引用存在后再聚焦
          if (this.$refs.locationForm) {
            this.focusWarehouseSelect();
          } else {
            // å¦‚果表单引用还不存在,稍后重试
            setTimeout(() => {
              this.focusWarehouseSelect();
            }, 500);
          }
        }, 300);
      });
    },
    // å¼¹æ¡†å…³é—­æ—¶é‡ç½®æ•°æ®
    handleDialogClose() {
      console.log('弹框关闭,重置数据');
      this.show = false;
      this.resetData();
    },
    // å–消按钮
    handleCancel() {
      this.palletVisible = false;
    },
    // ç¡®è®¤æŒ‰é’®
@@ -762,6 +571,11 @@
    // å¤„理托盘条码提交
    async handleTraySubmit() {
      // ä¿®å¤ï¼šåˆ‡æ¢æ‰˜ç›˜æ—¶æ¸…空物料相关状态,避免旧数据残留
      this.barcode = '';
      this.materials = [];
      this.error = '';
      // å…ˆç›´æŽ¥æ£€æŸ¥locationType,避免表单验证的异步问题
      if (!this.form.warehouseType) {
        this.error = '请先选择仓库';
@@ -769,7 +583,6 @@
      }
      if (!this.form.locationType) {
        this.error = '请先选择仓库区域';
        //this.$message.warning('请先选择仓库区域');
        return;
      }
@@ -785,12 +598,8 @@
      this.error = '';
      if(!this.trayBarcodeReg.test(currentTrayBarcode)){
        ElMessage.warning({
          message: '托盘号格式错误',
          type: 'warning',
          duration: 3000
        })
      if (!this.trayBarcodeReg.test(currentTrayBarcode)) {
        this.$message("托盘号格式错误");
        this.focusTrayInput();
        return;
      }
@@ -798,11 +607,7 @@
      // è®¾ç½®æ‰˜ç›˜æ¡ç åŽï¼Œè‡ªåŠ¨èšç„¦åˆ°ç‰©æ–™è¾“å…¥æ¡†
      this.focusBarcodeInput();
      this.$message({
        message: `托盘条码已设置: ${currentTrayBarcode}`,
        type: 'success',
        duration: 2000
      });
      this.$message.success(`托盘条码已设置: ${currentTrayBarcode}`);
    },
    // æ¸…除托盘
@@ -829,12 +634,25 @@
      this.isManualInput = false;
      this.isScanning = false;
    },
    // å¤„理物料条码提交
    async handleBarcodeSubmit() {
      if (!await this.validateForm()) return;
      if (this.isSubmitting) {
        this.$message.warning('正在处理中,请稍候');
        return;
      }
      // æ–°å¢žï¼šæ ¡éªŒå½“前条码是否已被当前托盘组盘
      const currentBarcode = this.barcode.trim();
      const currentTrayGrouped = this.palletGroupedBarcodes[this.trayBarcode] || [];
      if (currentTrayGrouped.includes(currentBarcode)) {
        this.error = `条码 ${currentBarcode} å·²è¢«å½“前托盘组盘,请勿重复操作`;
        this.barcode = '';
        this.focusBarcodeInput();
        return;
      }
      if (!await this.validateForm()) return;
      if (!this.trayBarcode) {
        this.error = '请先输入托盘条码';
@@ -850,103 +668,87 @@
      this.focusBarcodeInput();
      this.error = '';
      this.loading = true;
      this.isSubmitting = true; // å¼€å¯æäº¤é”
      try {
        // æ–°å¢žï¼šè°ƒè¯•日志,打印请求参数
        console.log('组盘请求参数:', {
          palletCode: this.trayBarcode,
          barcode: currentBarcode,
          locationTypeDesc: this.currentLocationDesc,
          locationType: this.form.locationType,
          warehouseType: this.form.warehouseType
        });
        // è°ƒç”¨API查询物料信息
        const materialData = await this.fetchMaterialData(currentBarcode);
        if (!materialData || materialData.length === 0) {
          return;
        }
        // æ£€æŸ¥æ˜¯å¦å·²å­˜åœ¨ç›¸åŒç‰©æ–™ç¼–码的记录
        const exists = this.materials.some(item =>
          item.barcode === this.trayBarcode
        );
        console.log('API:', materialData)
        if (exists) {
          this.$message({
            message: '该条码已存在当前托盘的列表中',
            type: 'warning',
            duration: 2000
        this.materials = [];
        const newBarcodes = []; // ä¸´æ—¶å­˜å‚¨æœ¬æ¬¡æ–°å¢žçš„æ¡ç 
        materialData.forEach(item => {
          this.materials.push({
            ...item,
            trayCode: this.trayBarcode,
            locationType: this.form.locationType,
            locationDesc: this.currentLocationDesc,
            scanTime: this.formatTime(new Date())
          });
        } else {
          newBarcodes.push(item.barcode); // æ”¶é›†æœ¬æ¬¡ç»„盘的条码
        });
          materialData.forEach(item => {
            // å¦‚果不存在,添加新物料
            this.materials.push({
              ...item,
              trayCode: this.trayBarcode,
              locationType: this.form.locationType,
              locationDesc: this.currentLocationDesc,
              scanTime: this.formatTime(new Date())
            });
          });
          const removeIndex = this.unpalletMaterials.findIndex(item => item.barcode === currentBarcode);
          if (removeIndex > -1) {
            this.unpalletMaterials.splice(removeIndex, 1); // åˆ é™¤æœªç»„盘对应数据
            this.unpalletBarcodes = this.unpalletMaterials.map(item => item.barcode || ''); // æ›´æ–°æ¡ç æ•°ç»„
            this.totalStockCount = Math.max(0, this.totalStockCount - 1);
          }
          this.$message({
            message: `成功添加条码: ${currentBarcode}`,
            type: 'success',
            duration: 2000
          });
          this.fetchStockStatistics();
          // æ¸…空物料输入框并保持聚焦
          this.barcode = '';
          this.scanCode = ''; // æ¸…空扫码缓存
          this.isScanning = false;
          setTimeout(() => {
            this.focusBarcodeInput();
          }, 100);
        // ä¿®å¤ï¼šæŒ‰æ‰˜ç›˜å­˜å‚¨å·²ç»„盘条码
        if (!this.palletGroupedBarcodes[this.trayBarcode]) {
          this.palletGroupedBarcodes[this.trayBarcode] = [];
        }
        this.palletGroupedBarcodes[this.trayBarcode] = [...new Set([...this.palletGroupedBarcodes[this.trayBarcode], ...newBarcodes])];
        this.orderNo = materialData[0].orderNo;
        await this.fetchStockStatistics(materialData[0].orderNo);
        await this.fetchUnpalletMaterialDetails();
        // æ¸…空物料输入框并保持聚焦
        this.barcode = '';
        this.scanCode = ''; // æ¸…空扫码缓存
        this.isScanning = false;
        setTimeout(() => {
          this.focusBarcodeInput();
        }, 100);
      } catch (err) {
        this.error = err.message || '查询条码信息失败,请重试';
        this.focusBarcodeInput();
        setTimeout(() => {
      // é€‰ä¸­è¾“入框内的错误内容(确保focus完成后执行)
      const inputEl = this.$refs.barcodeInput?.$el?.querySelector('input');
      if (inputEl) inputEl.select();
    }, 100);
          // é€‰ä¸­è¾“入框内的错误内容(确保focus完成后执行)
          const inputEl = this.$refs.barcodeInput?.$el?.querySelector('input');
          if (inputEl) inputEl.select();
        }, 100);
      } finally {
        this.loading = false;
        this.isSubmitting = false; // å…³é—­æäº¤é”
      }
    },
    // API请求 - æ›¿æ¢ä¸ºå®žé™…çš„API调用
    async fetchMaterialData(barcode) {
      try {
        const response = await http.post('/api/InboundOrder/BarcodeMaterielGroup',
        const response = await http.post('/api/Inbound/GroupPallet',
          {
            palletCode: this.trayBarcode,
            orderNo: this.docNo,
            barcodes: barcode,
            barcode: barcode,
            locationTypeDesc: this.currentLocationDesc,
            locationType: this.form.locationType, // æ·»åŠ ä»“åº“åŒºåŸŸä¿¡æ¯
            warehouseType: this.form.warehouseType
          }
        );
        let materialData;
        if (typeof response.data === 'string') {
          try {
            materialData = JSON.parse(response.data);
          } catch (e) {
          }
          } catch (e) { }
        } else {
          // å¦‚果返回的是JSON对象,直接使用
          materialData = response.data;
        }
        if (!response.status) {
@@ -957,15 +759,15 @@
      } catch (error) {
        console.error('API调用失败:', error);
        this.$message.error('接口请求失败,请联系管理员');
        return [];
      }
    },
    // å¤„理扫码枪输入
    handleKeyPress(event) {
      // å¦‚果是手动输入模式,不处理扫码枪逻辑
      if (this.isManualInput) {
      // å¦‚果是手动输入模式或正在提交,不处理扫码枪逻辑
      if (this.isManualInput || this.isSubmitting) {
        return;
      }
@@ -974,9 +776,15 @@
      // å¿½ç•¥ç›´æŽ¥æŒ‰ä¸‹çš„回车键(由handleBarcodeSubmit处理)
      if (key === 'Enter') {
        event.preventDefault(); // å¼ºåˆ¶é˜»æ­¢é»˜è®¤è¡Œä¸ºï¼Œé¿å…é‡å¤è§¦å‘
        if (this.scanCode.length > 0) {
          // é˜»æ­¢é»˜è®¤å›žè½¦è¡Œä¸ºï¼Œé¿å…è¡¨å•提交
          event.preventDefault();
          // æ–°å¢žï¼šæ‰«ç ç›®æ ‡ä¸ºç‰©æ–™æ—¶ï¼Œæ ¡éªŒå½“前托盘是否存在
          if (this.scanTarget === 'material' && !this.trayBarcode) {
            this.$message.warning('请先设置托盘条码');
            this.scanCode = '';
            this.lastKeyTime = null;
            return;
          }
          // æ‰«ç å®Œæˆï¼Œè‡ªåŠ¨è§¦å‘æŸ¥è¯¢
          this.isScanning = false;
@@ -1015,44 +823,6 @@
      this.lastKeyTime = currentTime;
    },
    // åˆ é™¤ç‰©æ–™
    removeMaterial(index) {
      this.$confirm('确定要删除这条物料记录吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.materials.splice(index, 1);
        this.$message({
          type: 'success',
          message: '删除成功!'
        });
        this.fetchStockStatistics();
      }).catch(() => {
        // å–消删除
      });
    },
    // æ¸…空所有物料
    clearAllMaterials() {
      if (this.materials.length === 0) return;
      this.$confirm('确定要清空所有物料记录吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.materials = [];
        this.$message({
          type: 'success',
          message: '已清空所有记录!'
        });
      }).catch(() => {
        // å–消清空
      });
    },
    // æ ¼å¼åŒ–æ—¶é—´
    formatTime(date) {
      const year = date.getFullYear();
@@ -1063,6 +833,25 @@
      const seconds = String(date.getSeconds()).padStart(2, '0');
      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
    },
    // ä»“库切换事件(补充实现,原代码缺失)
    handleWarehouseChange() {
      // åˆ‡æ¢ä»“库时清空区域和托盘相关状态
      this.form.locationType = null;
      this.trayBarcode = '';
      this.barcode = '';
      this.materials = [];
      this.error = '';
    },
    // åŒºåŸŸåˆ‡æ¢äº‹ä»¶ï¼ˆè¡¥å……实现,原代码缺失)
    handleLocationChange() {
      // åˆ‡æ¢åŒºåŸŸæ—¶æ¸…空托盘相关状态
      this.trayBarcode = '';
      this.barcode = '';
      this.materials = [];
      this.error = '';
    }
  }
}
@@ -1377,35 +1166,35 @@
  }
  .unpallet-section.compact {
  margin-bottom: 8px;
  flex-shrink: 0;
}
.unpallet-card {
  flex-shrink: 0;
}
.unpallet-barcode-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 8px 0;
  max-height: 180px;
  overflow-y: auto;
}
.unpallet-barcode-list>>>.el-tag {
  cursor: pointer;
  max-width: calc(33.333% - 4px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
@media (max-width: 768px) {
  .unpallet-barcode-list>>>.el-tag {
    max-width: calc(50% - 4px);
    margin-bottom: 8px;
    flex-shrink: 0;
  }
}
  .unpallet-card {
    flex-shrink: 0;
  }
  .unpallet-barcode-list {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    padding: 8px 0;
    max-height: 180px;
    overflow-y: auto;
  }
  .unpallet-barcode-list>>>.el-tag {
    cursor: pointer;
    max-width: calc(33.333% - 4px);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  @media (max-width: 768px) {
    .unpallet-barcode-list>>>.el-tag {
      max-width: calc(50% - 4px);
    }
  }
}
</style>