647556386
2026-02-02 95c92db3c831c73b05068b09221c13ad4a250322
条码扫描成功音频播放功能,代码优化
已修改10个文件
1177 ■■■■■ 文件已修改
项目代码/WIDESEA_WMSClient/src/assets/audio/error.mp3 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/assets/audio/success.mp3 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/inbound/extend/Pallet.vue 1008 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/inbound/extend/allocateOrderDetail.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/outbound/extend/newAllocateOrderDetail.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/outbound/extend/outOrderDetail.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/outbound/outPicking.vue 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundService.cs 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Jobs/QuartzJobMildd.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/assets/audio/error.mp3
Binary files differ
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/assets/audio/success.mp3
Binary files differ
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/inbound/extend/Pallet.vue
@@ -1,15 +1,42 @@
<template>
  <vol-box v-model="show" :title="'组盘操作 - å•据号:' + orderNo" :height="1000" :width="1100" :padding="20" :modal="true">
  <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-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"
                :value="item.warehouseType" />
        <el-form
          :model="form"
          :rules="rules"
          ref="locationForm"
          class="compact-form"
        >
          <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"
                :value="item.warehouseType"
              />
            </el-select>
          </el-form-item>
        </el-form>
@@ -17,12 +44,33 @@
      <!-- ä»“库区域选择 - ç´§å‡‘布局 -->
      <div class="location-section compact">
        <el-form :model="form" :rules="rules" ref="locationForm" class="compact-form">
          <el-form-item label="仓库区域" prop="locationType" class="location-select compact-item">
            <el-select v-model="form.locationType" placeholder="请先选择仓库" clearable filterable
              @change="handleLocationChange" style="width: 100%" :loading="locationLoading" size="medium">
              <el-option v-for="item in locationTypes" :key="item.locationType" :label="item.locationTypeDesc"
                :value="item.locationType" />
        <el-form
          :model="form"
          :rules="rules"
          ref="locationForm"
          class="compact-form"
        >
          <el-form-item
            label="仓库区域"
            prop="locationType"
            class="location-select compact-item"
          >
            <el-select
              v-model="form.locationType"
              placeholder="请先选择仓库"
              clearable
              filterable
              @change="handleLocationChange"
              style="width: 100%"
              :loading="locationLoading"
              size="medium"
            >
              <el-option
                v-for="item in locationTypes"
                :key="item.locationType"
                :label="item.locationTypeDesc"
                :value="item.locationType"
              />
            </el-select>
          </el-form-item>
        </el-form>
@@ -46,19 +94,39 @@
            <span><i class="el-icon-scanner"></i> æ‰«ç åŒºåŸŸ</span>
            <span class="scan-status">
              <span class="scan-indicator"></span>
              {{ form.locationType && form.warehouseType ? '扫码就绪' : '请先选择仓库和仓库区域' }}
              {{
                form.locationType && form.warehouseType
                  ? "扫码就绪"
                  : "请先选择仓库和仓库区域"
              }}
            </span>
          </div>
          <!-- æ‰˜ç›˜æ¡ç è¾“å…¥ -->
          <div class="input-wrapper custom-input-group compact-input">
            <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">
            <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"
            >
              <template slot="append">
                <el-button @click="handleTraySubmit" type="primary" icon="el-icon-position"
                  :disabled="!form.locationType || !trayBarcode || !form.warehouseType" size="medium">
                <el-button
                  @click="handleTraySubmit"
                  type="primary"
                  icon="el-icon-position"
                  :disabled="
                    !form.locationType || !trayBarcode || !form.warehouseType
                  "
                  size="medium"
                >
                  ç¡®è®¤
                </el-button>
              </template>
@@ -68,14 +136,35 @@
          <!-- ç‰©æ–™æ¡ç è¾“å…¥ -->
          <div class="input-wrapper custom-input-group compact-input">
            <div class="input-label">物料条码</div>
            <el-input ref="barcodeInput" v-model="barcode" placeholder="请扫描或输入物料条码后按回车键" clearable
              :disabled="!form.locationType || !trayBarcode || !form.warehouseType"
              @keyup.enter.native="debounceHandleBarcodeSubmit" @clear="handleClear" @input="handleBarcodeInput"
              class="custom-input" size="medium">
            <el-input
              ref="barcodeInput"
              v-model="barcode"
              placeholder="请扫描或输入物料条码后按回车键"
              clearable
              :disabled="
                !form.locationType || !trayBarcode || !form.warehouseType
              "
              @keyup.enter.native="debounceHandleBarcodeSubmit"
              @clear="handleClear"
              @input="handleBarcodeInput"
              class="custom-input"
              size="medium"
            >
              <template slot="append">
                <el-button :loading="loading" @click="debounceHandleBarcodeSubmit" type="primary" icon="el-icon-search"
                  :disabled="!form.locationType || !trayBarcode || !barcode || !form.warehouseType" size="medium">
                  {{ loading ? '查询中...' : '查询' }}
                <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>
            </el-input>
@@ -83,11 +172,22 @@
          <div class="input-tips compact-tips">
            <p>提示:请先选择仓库 â†’ é€‰æ‹©ä»“库区域 â†’ è¾“入料箱码 â†’ è¾“入物料条码</p>
            <p v-if="!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>
            <p v-if="!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>
        </el-card>
      </div>
@@ -99,7 +199,13 @@
      <!-- é”™è¯¯æç¤º -->
      <div v-if="error" class="error-message compact">
        <el-alert :title="error" type="error" show-icon closable @close="error = ''" />
        <el-alert
          :title="error"
          type="error"
          show-icon
          closable
          @close="error = ''"
        />
      </div>
      <!-- æœªç»„盘列表 -->
@@ -108,24 +214,66 @@
          <div slot="header" class="compact-header">
            <span><i class="el-icon-tickets"></i> æœªç»„盘条码</span>
            <span class="list-actions">
              <el-tag type="primary" size="small">未组盘 {{ totalStockCount }}</el-tag>
              <el-tag type="primary" size="small"
                >未组盘 {{ totalStockCount }}</el-tag
              >
            </span>
          </div>
          <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>
              <el-table-column prop="batchNo" label="批次" min-width="150" show-overflow-tooltip></el-table-column>
              <el-table-column prop="orderQuantity" label="数量" min-width="130" align="right"></el-table-column>
              <el-table-column prop="unit" label="单位" width="80" align="center"></el-table-column>
              <el-table-column prop="supplyCode" label="供应商" min-width="130" show-overflow-tooltip></el-table-column>
            <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>
              <el-table-column
                prop="batchNo"
                label="批次"
                min-width="150"
                show-overflow-tooltip
              ></el-table-column>
              <el-table-column
                prop="orderQuantity"
                label="数量"
                min-width="130"
                align="right"
              ></el-table-column>
              <el-table-column
                prop="unit"
                label="单位"
                width="80"
                align="center"
              ></el-table-column>
              <el-table-column
                prop="supplyCode"
                label="供应商"
                min-width="130"
                show-overflow-tooltip
              ></el-table-column>
            </el-table>
          </div>
        </el-card>
      </div>
      <!-- ç‰©æ–™åˆ—表 - å›ºå®šé«˜åº¦å¸¦æ»šåŠ¨æ¡ -->
@@ -134,46 +282,106 @@
          <div slot="header" class="compact-header">
            <span><i class="el-icon-tickets"></i> ç»„盘数据</span>
            <span class="list-actions">
              <el-tag type="primary" size="small">共 {{ materials.length }} æ¡</el-tag>
              <el-tag type="primary" size="small">未入库数量 {{ totalStockSum }}{{ uniqueUnit }}</el-tag>
              <el-tag v-if="trayBarcode" type="success" size="small">托盘: {{ trayBarcode }}</el-tag>
              <el-tag v-if="form.warehouseType" type="info" size="small">仓库: {{ currentWarehouseName }}</el-tag>
              <el-tag v-if="form.locationType" type="info" size="small">区域: {{ currentLocationDesc }}</el-tag>
              <el-tag type="primary" size="small"
                >共 {{ materials.length }} æ¡</el-tag
              >
              <el-tag type="primary" size="small"
                >未入库数量 {{ totalStockSum }}{{ uniqueUnit }}</el-tag
              >
              <el-tag v-if="trayBarcode" type="success" size="small"
                >托盘: {{ trayBarcode }}</el-tag
              >
              <el-tag v-if="form.warehouseType" type="info" size="small"
                >仓库: {{ currentWarehouseName }}</el-tag
              >
              <el-tag v-if="form.locationType" type="info" size="small"
                >区域: {{ currentLocationDesc }}</el-tag
              >
            </span>
          </div>
          <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.warehouseType && !form.locationType">请先选择仓库区域</p>
            <p v-if="form.warehouseType && !form.locationType">
              è¯·å…ˆé€‰æ‹©ä»“库区域
            </p>
            <p v-else-if="!trayBarcode">请先输入料箱条码</p>
            <p v-else>暂无物料数据,请扫描或输入物料条码</p>
          </div>
          <div class="table-container" v-else>
            <el-table :data="materials" stripe style="width: 100%" height="100%" size="small">
              <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>
              <el-table-column prop="batchNo" label="批次" min-width="150" show-overflow-tooltip></el-table-column>
              <el-table-column prop="stockQuantity" label="数量" min-width="130" align="right"></el-table-column>
              <el-table-column prop="unit" label="单位" width="80" align="center"></el-table-column>
              <el-table-column prop="supplyCode" label="供应商" min-width="130" show-overflow-tooltip></el-table-column>
              <el-table-column prop="warehouseType" label="仓库" min-width="120" show-overflow-tooltip></el-table-column>
            <el-table
              :data="materials"
              stripe
              style="width: 100%"
              height="100%"
              size="small"
            >
              <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>
              <el-table-column
                prop="batchNo"
                label="批次"
                min-width="150"
                show-overflow-tooltip
              ></el-table-column>
              <el-table-column
                prop="stockQuantity"
                label="数量"
                min-width="130"
                align="right"
              ></el-table-column>
              <el-table-column
                prop="unit"
                label="单位"
                width="80"
                align="center"
              ></el-table-column>
              <el-table-column
                prop="supplyCode"
                label="供应商"
                min-width="130"
                show-overflow-tooltip
              ></el-table-column>
              <el-table-column
                prop="warehouseType"
                label="仓库"
                min-width="120"
                show-overflow-tooltip
              ></el-table-column>
            </el-table>
          </div>
        </el-card>
      </div>
    </div>
    <template #footer>
      <el-button type="danger" size="small" @click="handleDialogClose()">关闭</el-button>
      <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 http from "@/api/http.js";
import VolBox from "@/components/basic/VolBox.vue";
// é˜²æŠ–函数
function debounce(func, wait) {
@@ -196,99 +404,102 @@
      orderNo: "",
      palletVisible: this.visible,
      trayBarcodeReg: /^[A-Z]\d{9}$/,
      trayBarcode: '',
      barcode: '',
      trayBarcode: "",
      barcode: "",
      materials: [],
      loading: false,
      error: '',
      error: "",
      debugMode: false,
      currentFocus: 'warehouse',
      currentFocus: "warehouse",
      unpalletBarcodes: [],
      unpalletBarcodesLoading: false,
      unpalletMaterials: [],
      scanCode: '',
      scanCode: "",
      lastKeyTime: null,
      isManualInput: false,
      isScanning: false,
      scanTimer: null,
      manualInputTimer: null,
      scanTarget: 'tray',
      scanTarget: "tray",
      isSubmitting: false,
      palletGroupedBarcodes: {},
      // éŸ³é¢‘配置
      audioConfig: {
        successPath: '/assets/audio/success.mp3', // å¯¹åº” public ä¸‹çš„路径
        errorPath: '/assets/audio/error.mp3'
     },
        successPath: require("@/assets/audio/success.mp3"),
        errorPath: require("@/assets/audio/error.mp3"),
      },
      // ç¼“存音频实例,避免重复创建
      successAudio: null,
      errorAudio: null,
      totalStockSum: 0,
      totalStockCount: 0,
      uniqueUnit: '',
      uniqueUnit: "",
      sumLoading: false,
      sumError: '',
      sumError: "",
      warehouseTypes: [],
      warehouseLoading: false,
      locationTypes: [],
      locationLoading: false,
      form: {
        warehouseType: null,
        locationType: null
        locationType: null,
      },
      rules: {
        locationType: [
          {
            validator: this.validateLocationType,
            trigger: 'change'
          }
            trigger: "change",
          },
        ],
        trayBarcode: [
          {
            pattern: this.trayBarcodeReg,
            message: '托盘号格式错误(需为1个大写字母+9个数字,如A000008024)',
            trigger: 'blur'
          }
            message: "托盘号格式错误(需为1个大写字母+9个数字,如A000008024)",
            trigger: "blur",
          },
        ],
        warehouseType: [
          {
            message: '请选择仓库',
            trigger: 'change'
          }
        ]
            message: "请选择仓库",
            trigger: "change",
          },
        ],
      },
      // æ–°å¢žï¼šé”®ç›˜äº‹ä»¶ç›‘听标记
      keyPressListenerAdded: false,
      isDialogClosing: false
    }
      isDialogClosing: false,
    };
  },
  computed: {
    currentWarehouseName() {
      const warehouse = this.warehouseTypes.find(item => item.warehouseType === this.form.warehouseType);
      return warehouse ? warehouse.warehouseTypeDesc : '';
      const warehouse = this.warehouseTypes.find(
        (item) => item.warehouseType === this.form.warehouseType
      );
      return warehouse ? warehouse.warehouseTypeDesc : "";
    },
    currentLocationDesc() {
      const location = this.locationTypes.find(item => item.locationType === this.form.locationType)
      return location ? location.locationTypeDesc : ''
      const location = this.locationTypes.find(
        (item) => item.locationType === this.form.locationType
      );
      return location ? location.locationTypeDesc : "";
    },
    debounceHandleBarcodeSubmit() {
      return debounce(this.handleBarcodeSubmit, 500);
    }
    },
  },
  watch: {
    // ç›‘听show变量变化
    show(newVal) {
      if (newVal === true) {
        console.log('弹框打开,重置数据');
        console.log("弹框打开,重置数据");
        this.isDialogClosing = false;
        this.resetData();
        this.$nextTick(() => {
@@ -298,18 +509,18 @@
          }, 300);
        });
      } else if (newVal === false && !this.isDialogClosing) {
        console.log('弹框关闭,移除事件监听');
        console.log("弹框关闭,移除事件监听");
        this.isDialogClosing = true;
        this.removeKeyPressListener(); // ç§»é™¤é”®ç›˜äº‹ä»¶ç›‘听
        this.resetData();
      }
    },
    visible(newVal, oldVal) {
      this.palletVisible = newVal;
      if (newVal === true && oldVal === false) {
        console.log('弹框打开,重置数据');
        console.log("弹框打开,重置数据");
        this.resetData();
        this.$nextTick(() => {
          setTimeout(() => {
@@ -319,129 +530,129 @@
      }
      if (newVal === false && oldVal === true) {
        console.log('弹框关闭,重置数据');
        console.log("弹框关闭,重置数据");
        this.resetData();
      }
    },
    palletVisible(newVal) {
      this.$emit('update:visible', newVal);
      this.$emit("update:visible", newVal);
    },
    docNo(newVal) {
      if (newVal) {
        this.palletForm = { palletCode: '', barcode: '' };
        this.palletForm = { palletCode: "", barcode: "" };
        this.backData = [];
        this.$refs.palletForm?.reset();
        this.fetchUnpalletMaterialDetails();
      }
    }
    },
  },
  mounted() {
    // ä¸åœ¨mounted时添加监听,在弹窗打开时添加
  },
  beforeDestroy() {
    // ç¡®ä¿ç»„件销毁时移除监听
    this.removeKeyPressListener();
    this.clearAllTimers();
     this.removeKeyPressListener();
     this.clearAllTimers();
   //销毁音频实例
    this.removeKeyPressListener();
    this.clearAllTimers();
    //销毁音频实例
    this.successAudio = null;
    this.errorAudio = null;
  },
  methods: {
    /**
     * åˆå§‹åŒ–音频实例(懒加载,只创建一次)
     * @param {String} type - éŸ³é¢‘类型:success/error
     * @returns {Audio} éŸ³é¢‘实例
     */
    initAudioInstance(type) {
      if (type === "success" && this.successAudio) {
        return this.successAudio;
      }
      if (type === "error" && this.errorAudio) {
        return this.errorAudio;
      }
      const audioPath =
        type === "success"
          ? this.audioConfig.successPath
          : this.audioConfig.errorPath;
      const audioInstance = new Audio(audioPath);
      // ç¼“存音频实例
      if (type === "success") {
        this.successAudio = audioInstance;
      } else {
        this.errorAudio = audioInstance;
      }
      // éŸ³é¢‘加载失败回调(可选,用于调试)
      audioInstance.onerror = (err) => {
        console.error(`【${type} éŸ³é¢‘】加载失败`, err);
      };
      return audioInstance;
    },
    /**
   * åˆå§‹åŒ–音频实例(懒加载,只创建一次)
   * @param {String} type - éŸ³é¢‘类型:success/error
   * @returns {Audio} éŸ³é¢‘实例
   */
  initAudioInstance(type) {
    if (type === 'success' && this.successAudio) {
      return this.successAudio;
    }
    if (type === 'error' && this.errorAudio) {
      return this.errorAudio;
    }
    const audioPath = type === 'success'
      ? this.audioConfig.successPath
      : this.audioConfig.errorPath;
    const audioInstance = new Audio(audioPath);
    // ç¼“存音频实例
    if (type === 'success') {
      this.successAudio = audioInstance;
    } else {
      this.errorAudio = audioInstance;
    }
    // éŸ³é¢‘加载失败回调(可选,用于调试)
    audioInstance.onerror = (err) => {
      console.error(`【${type} éŸ³é¢‘】加载失败`, err);
    };
    return audioInstance;
  },
  /**
   * æ’­æ”¾éŸ³é¢‘
   * @param {String} type - éŸ³é¢‘类型:success/error
   */
  playAudio(type) {
    try {
      const audioInstance = this.initAudioInstance(type);
      // é‡ç½®æ’­æ”¾è¿›åº¦ï¼ˆé¿å…é‡å¤æ’­æ”¾æ—¶éŸ³é¢‘未结束)
      audioInstance.currentTime = 0;
      // æ’­æ”¾éŸ³é¢‘(返回 Promise å¤„理播放结果,兼容部分浏览器限制)
      audioInstance.play().catch((err) => {
        console.warn('音频播放失败(可能是浏览器自动播放策略限制)', err);
      });
    } catch (err) {
      console.error('播放音频时发生异常', err);
    }
  },
  /**
   * æ’­æ”¾æˆåŠŸéŸ³é¢‘
   */
  playSuccessAudio() {
    this.playAudio('success');
  },
  /**
   * æ’­æ”¾å¤±è´¥éŸ³é¢‘
   */
  playErrorAudio() {
    this.playAudio('error');
  },
     * æ’­æ”¾éŸ³é¢‘
     * @param {String} type - éŸ³é¢‘类型:success/error
     */
    playAudio(type) {
      try {
        const audioInstance = this.initAudioInstance(type);
        // é‡ç½®æ’­æ”¾è¿›åº¦ï¼ˆé¿å…é‡å¤æ’­æ”¾æ—¶éŸ³é¢‘未结束)
        audioInstance.currentTime = 0;
        // æ’­æ”¾éŸ³é¢‘(返回 Promise å¤„理播放结果,兼容部分浏览器限制)
        audioInstance.play().catch((err) => {
          console.warn("音频播放失败(可能是浏览器自动播放策略限制)", err);
        });
      } catch (err) {
        console.error("播放音频时发生异常", err);
      }
    },
    /**
     * æ’­æ”¾æˆåŠŸéŸ³é¢‘
     */
    playSuccessAudio() {
      this.playAudio("success");
    },
    /**
     * æ’­æ”¾å¤±è´¥éŸ³é¢‘
     */
    playErrorAudio() {
      this.playAudio("error");
    },
    // æ·»åŠ é”®ç›˜äº‹ä»¶ç›‘å¬
    addKeyPressListener() {
      if (!this.keyPressListenerAdded) {
        document.addEventListener('keypress', this.handleKeyPress);
        document.addEventListener("keypress", this.handleKeyPress);
        this.keyPressListenerAdded = true;
        console.log('键盘事件监听已添加');
        console.log("键盘事件监听已添加");
      }
    },
    // ç§»é™¤é”®ç›˜äº‹ä»¶ç›‘听
    removeKeyPressListener() {
      if (this.keyPressListenerAdded) {
        document.removeEventListener('keypress', this.handleKeyPress);
        document.removeEventListener("keypress", this.handleKeyPress);
        this.keyPressListenerAdded = false;
        console.log('键盘事件监听已移除');
        console.log("键盘事件监听已移除");
      }
    },
    open() {
      this.show = true;
      this.orderNo = "";
@@ -449,7 +660,7 @@
      this.initLocationTypes();
      this.initwarehouseTypes();
      this.fetchUnpalletMaterialDetails();
      // å¼¹çª—打开时添加键盘事件监听
      this.$nextTick(() => {
        setTimeout(() => {
@@ -457,12 +668,12 @@
        }, 100);
      });
    },
    validateLocationType(rule, value, callback) {
      if (!this.form.warehouseType) {
        callback(new Error('请先选择仓库'));
      } else if (value === null || value === undefined || value === '') {
        callback(new Error('请选择仓库区域'));
        callback(new Error("请先选择仓库"));
      } else if (value === null || value === undefined || value === "") {
        callback(new Error("请选择仓库区域"));
      } else {
        callback();
      }
@@ -471,62 +682,70 @@
    // èŽ·å–æœªç»„ç›˜ç‰©æ–™è¯¦æƒ…
    fetchUnpalletMaterialDetails() {
      this.unpalletBarcodesLoading = true;
      http.post('/api/InboundOrder/UnPalletGroupBarcode?orderNo=' + this.orderNo, {})
        .then(response => {
      http
        .post(
          "/api/InboundOrder/UnPalletGroupBarcode?orderNo=" + this.orderNo,
          {}
        )
        .then((response) => {
          if (response.status && Array.isArray(response.data)) {
            this.unpalletMaterials = response.data;
            this.unpalletBarcodes = response.data.map(item => item.barcode || '');
            this.unpalletBarcodes = response.data.map(
              (item) => item.barcode || ""
            );
            this.totalStockCount = response.data.length;
          } else {
            this.unpalletMaterials = [];
          }
        })
        .catch(err => {
          console.error('获取未组盘物料失败:', err);
          this.unpalletMaterials = this.unpalletBarcodes.map(barcode => ({
        .catch((err) => {
          console.error("获取未组盘物料失败:", err);
          this.unpalletMaterials = this.unpalletBarcodes.map((barcode) => ({
            barcode: barcode,
            materielCode: '-',
            batchNo: '-',
            stockQuantity: '-',
            unit: '-',
            supplyCode: '-',
            warehouseType: '-'
            materielCode: "-",
            batchNo: "-",
            stockQuantity: "-",
            unit: "-",
            supplyCode: "-",
            warehouseType: "-",
          }));
        })
        .finally(() => {
          this.unpalletBarcodesLoading = false;
        });
    },
    // åˆå§‹åŒ–仓库区域类型
    initLocationTypes() {
      this.locationLoading = true;
      this.http.post("api/LocationInfo/GetLocationTypes")
      this.http
        .post("api/LocationInfo/GetLocationTypes")
        .then(({ data }) => {
          this.locationTypes = data;
        })
        .catch(e => {
          console.error('获取区域类型失败:', e);
          this.$message.error('获取区域类型失败');
        .catch((e) => {
          console.error("获取区域类型失败:", e);
          this.$message.error("获取区域类型失败");
        })
        .finally(() => {
          this.locationLoading = false;
        });
    },
    // åˆå§‹åŒ–仓库类型
    initwarehouseTypes() {
      this.warehouseLoading = true;
      this.http.post("api/Warehouse/GetwarehouseTypes")
      this.http
        .post("api/Warehouse/GetwarehouseTypes")
        .then(({ data }) => {
          this.warehouseTypes = data;
        })
        .catch(e => {
          console.error('获取仓库类型失败:', e);
          this.$message.error('获取仓库类型失败');
        .catch((e) => {
          console.error("获取仓库类型失败:", e);
          this.$message.error("获取仓库类型失败");
        })
        .finally(() => {
          this.warehouseLoading = false;
@@ -536,25 +755,26 @@
    // èŽ·å–åº“å­˜ç»Ÿè®¡
    fetchStockStatistics(orderNo) {
      if (!orderNo) {
        this.sumError = '单据号为空,无法统计';
        this.sumError = "单据号为空,无法统计";
        return Promise.resolve(null);
      }
      this.sumLoading = true;
      this.sumError = '';
      return http.post('/api/InboundOrder/UnPalletQuantity?orderNo=' + orderNo, {})
        .then(response => {
      this.sumError = "";
      return http
        .post("/api/InboundOrder/UnPalletQuantity?orderNo=" + orderNo, {})
        .then((response) => {
          if (response.data) {
            this.totalStockSum = response.data.stockSumQuantity || 0;
            this.totalStockCount = response.data.stockCount || 0;
            this.uniqueUnit = response.data.uniqueUnit || '';
            this.uniqueUnit = response.data.uniqueUnit || "";
          }
          return response.data;
        })
        .catch(err => {
          console.error('统计加载失败:', err);
          this.sumError = '统计加载失败';
        .catch((err) => {
          console.error("统计加载失败:", err);
          this.sumError = "统计加载失败";
          this.totalStockSum = 0;
          this.totalStockCount = 0;
          throw err;
@@ -568,37 +788,37 @@
    validateForm() {
      return new Promise((resolve) => {
        if (!this.$refs.locationForm) {
          this.error = '表单未初始化';
          this.$message.warning('请先选择仓库区域');
          this.error = "表单未初始化";
          this.$message.warning("请先选择仓库区域");
          resolve(false);
          return;
        }
        this.$refs.locationForm.validate((valid) => {
          if (valid) {
            this.error = '';
            this.error = "";
            resolve(true);
          } else {
            if (!this.form.warehouseType) {
              this.error = '请先选择仓库';
              this.error = "请先选择仓库";
            } else if (!this.form.locationType) {
              this.error = '请先选择仓库区域';
              this.error = "请先选择仓库区域";
            } else {
              this.error = '请检查表单填写是否正确';
              this.error = "请检查表单填写是否正确";
            }
            resolve(false);
          }
        });
      });
    },
    focusTrayInput() {
      if (this.$refs.trayInput && this.$refs.trayInput.$el) {
        const inputEl = this.$refs.trayInput.$el.querySelector('input');
        const inputEl = this.$refs.trayInput.$el.querySelector("input");
        if (inputEl) {
          inputEl.focus();
          this.currentFocus = 'tray';
          this.scanTarget = 'tray';
          this.currentFocus = "tray";
          this.scanTarget = "tray";
          inputEl.select();
        }
      }
@@ -606,44 +826,44 @@
    focusBarcodeInput() {
      if (this.$refs.barcodeInput && this.$refs.barcodeInput.$el) {
        const inputEl = this.$refs.barcodeInput.$el.querySelector('input');
        const inputEl = this.$refs.barcodeInput.$el.querySelector("input");
        if (inputEl) {
          inputEl.focus();
          this.currentFocus = 'material';
          this.scanTarget = 'material';
          this.currentFocus = "material";
          this.scanTarget = "material";
          inputEl.select();
        }
      }
    },
    resetData() {
      this.palletGroupedBarcodes = {};
      this.isSubmitting = false;
      this.trayBarcode = '';
      this.barcode = '';
      this.trayBarcode = "";
      this.barcode = "";
      this.materials = [];
      this.unpalletBarcodes = [];
      this.unpalletMaterials = [];
      this.loading = false;
      this.error = '';
      this.scanCode = '';
      this.error = "";
      this.scanCode = "";
      this.lastKeyTime = null;
      this.isManualInput = false;
      this.isScanning = false;
      this.currentFocus = 'warehouse';
      this.scanTarget = 'tray';
      this.currentFocus = "warehouse";
      this.scanTarget = "tray";
      this.clearAllTimers();
      this.totalStockSum = 0;
      this.totalStockCount = 0;
      this.sumLoading = false;
      this.sumError = '';
      this.sumError = "";
      this.form = {
        warehouseType: null,
        locationType: null
        locationType: null,
      };
      this.warehouseTypes = [];
      this.locationTypes = [];
      this.$nextTick(() => {
        if (this.$refs.locationForm) {
          this.$refs.locationForm.clearValidate();
@@ -661,7 +881,7 @@
        this.scanTimer = null;
      }
    },
    handleDialogClose() {
      // å…ˆç§»é™¤é”®ç›˜äº‹ä»¶ç›‘听
      this.removeKeyPressListener();
@@ -671,35 +891,34 @@
    // ç¡®è®¤æŒ‰é’®
    handleConfirm() {
      this.validateForm()
        .then(valid => {
          if (!valid) return;
          if (this.materials.length === 0) {
            this.$message.warning('请至少添加一个物料');
            return;
          }
      this.validateForm().then((valid) => {
        if (!valid) return;
          if (!this.trayBarcode) {
            this.$message.warning('请输入托盘条码');
            return;
          }
        if (this.materials.length === 0) {
          this.$message.warning("请至少添加一个物料");
          return;
        }
          const result = {
            warehouseType: this.form.warehouseType,
            warehouseName: this.currentWarehouseName,
            locationType: this.form.locationType,
            locationDesc: this.currentLocationDesc,
            trayBarcode: this.trayBarcode,
            materials: this.materials,
            docNo: this.docNo
          };
        if (!this.trayBarcode) {
          this.$message.warning("请输入托盘条码");
          return;
        }
          this.$emit('back-success', result);
          this.palletVisible = false;
        });
        const result = {
          warehouseType: this.form.warehouseType,
          warehouseName: this.currentWarehouseName,
          locationType: this.form.locationType,
          locationDesc: this.currentLocationDesc,
          trayBarcode: this.trayBarcode,
          materials: this.materials,
          docNo: this.docNo,
        };
        this.$emit("back-success", result);
        this.palletVisible = false;
      });
    },
    handleTrayInput() {
      this.isManualInput = true;
      this.isScanning = false;
@@ -728,61 +947,60 @@
    // å¤„理托盘条码提交
    handleTraySubmit() {
      this.barcode = '';
      this.barcode = "";
      this.materials = [];
      this.error = '';
      this.error = "";
      if (!this.form.warehouseType) {
        this.error = '请先选择仓库';
        this.error = "请先选择仓库";
        return;
      }
      if (!this.form.locationType) {
        this.error = '请先选择仓库区域';
        this.error = "请先选择仓库区域";
        return;
      }
      this.validateForm()
        .then(valid => {
          if (!valid) return;
          const currentTrayBarcode = this.trayBarcode.trim();
      this.validateForm().then((valid) => {
        if (!valid) return;
          if (!currentTrayBarcode) {
            this.error = '请输入或扫描托盘条码';
            return;
          }
        const currentTrayBarcode = this.trayBarcode.trim();
          this.error = '';
        if (!currentTrayBarcode) {
          this.error = "请输入或扫描托盘条码";
          return;
        }
          if (!this.trayBarcodeReg.test(currentTrayBarcode)) {
            this.$message("托盘号格式错误");
            this.focusTrayInput();
            return;
          }
        this.error = "";
          this.focusBarcodeInput();
          this.$message.success(`托盘条码已设置: ${currentTrayBarcode}`);
        });
        if (!this.trayBarcodeReg.test(currentTrayBarcode)) {
          this.$message("托盘号格式错误");
          this.focusTrayInput();
          return;
        }
        this.focusBarcodeInput();
        this.$message.success(`托盘条码已设置: ${currentTrayBarcode}`);
      });
    },
    clearTray() {
      this.trayBarcode = '';
      this.trayBarcode = "";
      this.materials = [];
      this.focusTrayInput();
      this.$message({
        message: '托盘条码已清除',
        type: 'info',
        duration: 2000
        message: "托盘条码已清除",
        type: "info",
        duration: 2000,
      });
    },
    handleTrayClear() {
      this.error = '';
      this.error = "";
    },
    handleClear() {
      this.error = '';
      this.scanCode = '';
      this.error = "";
      this.scanCode = "";
      this.isManualInput = false;
      this.isScanning = false;
    },
@@ -790,73 +1008,73 @@
    // å¤„理物料条码提交
    handleBarcodeSubmit() {
      if (this.isSubmitting) {
        this.$message.warning('正在处理中,请稍候');
        this.$message.warning("正在处理中,请稍候");
        return;
      }
      const currentBarcode = this.barcode.trim();
      const currentTrayGrouped = this.palletGroupedBarcodes[this.trayBarcode] || [];
      const currentTrayGrouped =
        this.palletGroupedBarcodes[this.trayBarcode] || [];
      if (currentTrayGrouped.includes(currentBarcode)) {
        this.error = `条码 ${currentBarcode} å·²è¢«å½“前托盘组盘,请勿重复操作`;
        this.barcode = '';
        this.barcode = "";
        this.focusBarcodeInput();
        this.playErrorAudio();
        return;
      }
      this.isSubmitting = true;
      this.validateForm()
        .then(valid => {
        .then((valid) => {
          if (!valid) {
            this.isSubmitting = false;
            this.playErrorAudio();
            return;
          }
          if (!this.trayBarcode) {
            this.error = '请先输入托盘条码';
            this.error = "请先输入托盘条码";
            this.focusTrayInput();
            this.isSubmitting = false;
            return;
          }
          if (!currentBarcode) {
            this.error = '请输入或扫描物料条码';
            this.error = "请输入或扫描物料条码";
            this.isSubmitting = false;
            return;
          }
          this.focusBarcodeInput();
          this.error = '';
          this.error = "";
          this.loading = true;
          console.log('组盘请求参数:', {
          console.log("组盘请求参数:", {
            palletCode: this.trayBarcode,
            barcode: currentBarcode,
            locationTypeDesc: this.currentLocationDesc,
            locationType: this.form.locationType,
            warehouseType: this.form.warehouseType
            warehouseType: this.form.warehouseType,
          });
          return this.fetchMaterialData(currentBarcode);
        })
        .then(materialData => {
        .then((materialData) => {
          if (!materialData || materialData.length === 0) {
            return;
          }
          this.materials = [];
          const newBarcodes = [];
          materialData.forEach(item => {
          materialData.forEach((item) => {
            this.materials.push({
              ...item,
              trayCode: this.trayBarcode,
              locationType: this.form.locationType,
              locationDesc: this.currentLocationDesc,
              scanTime: this.formatTime(new Date())
              scanTime: this.formatTime(new Date()),
            });
            newBarcodes.push(item.barcode);
          });
@@ -864,32 +1082,36 @@
          if (!this.palletGroupedBarcodes[this.trayBarcode]) {
            this.palletGroupedBarcodes[this.trayBarcode] = [];
          }
          this.palletGroupedBarcodes[this.trayBarcode] = [...new Set([...this.palletGroupedBarcodes[this.trayBarcode], ...newBarcodes])];
          this.palletGroupedBarcodes[this.trayBarcode] = [
            ...new Set([
              ...this.palletGroupedBarcodes[this.trayBarcode],
              ...newBarcodes,
            ]),
          ];
          this.orderNo = materialData[0].orderNo;
          return Promise.all([
            this.fetchStockStatistics(materialData[0].orderNo),
            this.fetchUnpalletMaterialDetails()
            this.fetchUnpalletMaterialDetails(),
          ]);
        })
        .then(() => {
          this.barcode = '';
          this.scanCode = '';
          this.barcode = "";
          this.scanCode = "";
          this.isScanning = false;
          this.playSuccessAudio();
          setTimeout(() => {
            this.focusBarcodeInput();
          }, 100);
        })
        .catch(err => {
          console.error('处理物料条码失败:', err);
          this.error = err.message || '查询条码信息失败,请重试';
        .catch((err) => {
          console.error("处理物料条码失败:", err);
          this.error = err.message || "查询条码信息失败,请重试";
          this.focusBarcodeInput();
          setTimeout(() => {
            const inputEl = this.$refs.barcodeInput?.$el?.querySelector('input');
            const inputEl =
              this.$refs.barcodeInput?.$el?.querySelector("input");
            if (inputEl) inputEl.select();
          }, 100);
        })
@@ -901,36 +1123,42 @@
    // API请求
    fetchMaterialData(barcode) {
      return http.post('/api/Inbound/GroupPallet', {
        palletCode: this.trayBarcode,
        barcode: barcode,
        locationTypeDesc: this.currentLocationDesc,
        locationType: this.form.locationType,
        warehouseType: this.form.warehouseType
      })
        .then(response => {
      return http
        .post("/api/Inbound/GroupPallet", {
          palletCode: this.trayBarcode,
          barcode: barcode,
          locationTypeDesc: this.currentLocationDesc,
          locationType: this.form.locationType,
          warehouseType: this.form.warehouseType,
        })
        .then((response) => {
          let materialData;
          if (typeof response.data === 'string') {
          if (typeof response.data === "string") {
            try {
              materialData = JSON.parse(response.data);
            } catch (e) {
              console.error('解析响应数据失败:', e);
              console.error("解析响应数据失败:", e);
              this.playErrorAudio(); // è§£æžå¤±è´¥æ’­æ”¾é”™è¯¯éŸ³
              throw new Error("响应数据格式错误");
            }
          } else {
            materialData = response.data;
          }
          if (!response.status) {
            this.error = response.message || '查询条码信息失败,请重试';
            this.error = response.message || "查询条码信息失败,请重试";
            this.playErrorAudio(); // æŽ¥å£è¿”回失败播放错误音
            return [];
          }
          this.playSuccessAudio(); // æŽ¥å£è¿”回成功播放成功音
          return materialData || [];
        })
        .catch(error => {
          console.error('API调用失败:', error);
          this.$message.error('接口请求失败,请联系管理员');
        .catch((error) => {
          console.error("API调用失败:", error);
          this.$message.error("接口请求失败,请联系管理员");
          this.playErrorAudio(); // è¯·æ±‚异常播放错误音
          throw error;
        });
    },
@@ -941,7 +1169,7 @@
      if (!this.show || this.isDialogClosing) {
        return;
      }
      if (this.isManualInput || this.isSubmitting) {
        return;
      }
@@ -949,27 +1177,27 @@
      const key = event.key;
      const currentTime = new Date().getTime();
      if (key === 'Enter') {
      if (key === "Enter") {
        event.preventDefault();
        if (this.scanCode.length > 0) {
          if (this.scanTarget === 'material' && !this.trayBarcode) {
            this.$message.warning('请先设置托盘条码');
            this.scanCode = '';
          if (this.scanTarget === "material" && !this.trayBarcode) {
            this.$message.warning("请先设置托盘条码");
            this.scanCode = "";
            this.lastKeyTime = null;
            return;
          }
          this.isScanning = false;
          if (this.scanTarget === 'tray') {
          if (this.scanTarget === "tray") {
            this.trayBarcode = this.scanCode;
            this.handleTraySubmit();
          } else if (this.scanTarget === 'material') {
          } else if (this.scanTarget === "material") {
            this.barcode = this.scanCode;
            this.handleBarcodeSubmit();
          }
        }
        this.scanCode = '';
        this.scanCode = "";
        this.lastKeyTime = null;
        return;
      }
@@ -995,11 +1223,11 @@
    // æ ¼å¼åŒ–æ—¶é—´
    formatTime(date) {
      const year = date.getFullYear();
      const month = String(date.getMonth() + 1).padStart(2, '0');
      const day = String(date.getDate()).padStart(2, '0');
      const hours = String(date.getHours()).padStart(2, '0');
      const minutes = String(date.getMinutes()).padStart(2, '0');
      const seconds = String(date.getSeconds()).padStart(2, '0');
      const month = String(date.getMonth() + 1).padStart(2, "0");
      const day = String(date.getDate()).padStart(2, "0");
      const hours = String(date.getHours()).padStart(2, "0");
      const minutes = String(date.getMinutes()).padStart(2, "0");
      const seconds = String(date.getSeconds()).padStart(2, "0");
      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
    },
@@ -1007,21 +1235,21 @@
    // ä»“库切换事件
    handleWarehouseChange() {
      this.form.locationType = null;
      this.trayBarcode = '';
      this.barcode = '';
      this.trayBarcode = "";
      this.barcode = "";
      this.materials = [];
      this.error = '';
      this.error = "";
    },
    // åŒºåŸŸåˆ‡æ¢äº‹ä»¶
    handleLocationChange() {
      this.trayBarcode = '';
      this.barcode = '';
      this.trayBarcode = "";
      this.barcode = "";
      this.materials = [];
      this.error = '';
    }
  }
}
      this.error = "";
    },
  },
};
</script>
<style scoped>
@@ -1052,7 +1280,7 @@
  margin-bottom: 0;
}
.compact-card>>>.el-card__body {
.compact-card >>> .el-card__body {
  padding: 12px;
}
@@ -1063,7 +1291,7 @@
  padding: 0 !important;
}
.compact-header>>>.el-card__header {
.compact-header >>> .el-card__header {
  padding: 8px 12px;
}
@@ -1081,7 +1309,7 @@
  margin-bottom: 8px;
}
.location-section.compact>>>.el-form-item {
.location-section.compact >>> .el-form-item {
  margin-bottom: 0;
}
@@ -1107,13 +1335,13 @@
  flex-direction: column;
}
.material-list.compact>>>.el-card {
.material-list.compact >>> .el-card {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.material-list.compact>>>.el-card__body {
.material-list.compact >>> .el-card__body {
  flex: 1;
  display: flex;
  flex-direction: column;
@@ -1127,11 +1355,11 @@
  overflow: hidden;
}
.material-list.compact>>>.el-table {
.material-list.compact >>> .el-table {
  flex: 1;
}
.material-list.compact>>>.el-table__body-wrapper {
.material-list.compact >>> .el-table__body-wrapper {
  overflow-y: auto;
}
@@ -1162,7 +1390,7 @@
.scan-status {
  font-size: 12px;
  color: #67C23A;
  color: #67c23a;
}
.scan-indicator {
@@ -1170,7 +1398,7 @@
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background-color: #67C23A;
  background-color: #67c23a;
  margin-right: 5px;
  animation: pulse 1.5s infinite;
}
@@ -1199,7 +1427,7 @@
}
.warning-text {
  color: #E6A23C;
  color: #e6a23c;
  font-weight: bold;
}
@@ -1211,7 +1439,7 @@
.loading.compact p {
  margin-top: 5px;
  color: #409EFF;
  color: #409eff;
  font-size: 12px;
}
@@ -1219,7 +1447,7 @@
  margin: 5px 0;
}
.error-message.compact>>>.el-alert {
.error-message.compact >>> .el-alert {
  padding: 6px 12px;
}
@@ -1229,7 +1457,7 @@
  gap: 4px;
}
.list-actions>>>.el-tag {
.list-actions >>> .el-tag {
  height: 24px;
  line-height: 22px;
  padding: 0 6px;
@@ -1240,9 +1468,9 @@
}
.material-code {
  font-family: 'Courier New', monospace;
  font-family: "Courier New", monospace;
  font-weight: bold;
  color: #409EFF;
  color: #409eff;
}
.location-info {
@@ -1270,7 +1498,7 @@
  align-items: center;
  width: 100%;
  margin: 8px 0;
  border: 1px solid #DCDFE6;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  overflow: hidden;
  background: #fff;
@@ -1278,8 +1506,8 @@
.input-label {
  padding: 0 12px;
  background: #F5F7FA;
  border-right: 1px solid #DCDFE6;
  background: #f5f7fa;
  border-right: 1px solid #dcdfe6;
  color: #606266;
  font-size: 13px;
  white-space: nowrap;
@@ -1300,7 +1528,7 @@
  flex: 1;
}
.custom-input>>>.el-input__inner {
.custom-input >>> .el-input__inner {
  border: none;
  border-radius: 0;
  height: 36px;
@@ -1322,13 +1550,13 @@
  .input-label {
    width: 100%;
    border-right: none;
    border-bottom: 1px solid #DCDFE6;
    border-bottom: 1px solid #dcdfe6;
    margin-bottom: 5px;
  }
  .input-container {
    width: 100%;
    border: 1px solid #DCDFE6;
    border: 1px solid #dcdfe6;
    border-radius: 4px;
  }
@@ -1350,7 +1578,7 @@
    overflow-y: auto;
  }
  .unpallet-barcode-list>>>.el-tag {
  .unpallet-barcode-list >>> .el-tag {
    cursor: pointer;
    max-width: calc(33.333% - 4px);
    overflow: hidden;
@@ -1359,7 +1587,7 @@
  }
  @media (max-width: 768px) {
    .unpallet-barcode-list>>>.el-tag {
    .unpallet-barcode-list >>> .el-tag {
      max-width: calc(50% - 4px);
    }
  }
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/inbound/extend/allocateOrderDetail.vue
@@ -209,7 +209,7 @@
        size: 30,
        Wheres: [],
        page: 1,
        rows: 30,
        rows: 240,
      },
      searchFormOptions: [
        [
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/extend/newAllocateOrderDetail.vue
@@ -209,7 +209,7 @@
        size: 30,
        Wheres: [],
        page: 1,
        rows: 30,
        rows: 240,
      },
      searchFormOptions: [
        [
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/extend/outOrderDetail.vue
@@ -309,7 +309,7 @@
        size: 30,
        Wheres: [],
        page: 1,
        rows: 30,
        rows: 240,
      },
      searchFormOptions: [
        [
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/outbound/outPicking.vue
@@ -11,8 +11,6 @@
                    <span class="order-value">{{ orderNo }}</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 +155,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 +212,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 +227,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 +327,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 +371,10 @@
            wholeOutDialogVisible: false,
            wholeOutInfo: null,
            globalLoading: false,
            loadingInstance: null
            loadingInstance: null,
            // éŸ³é¢‘实例(缓存,避免重复创建)
            successAudio: null,
            errorAudio: null
        }
    },
    computed: {
@@ -442,8 +384,54 @@
    },
    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)
                }
            }
        },
        // æ’­æ”¾æˆåŠŸéŸ³é¢‘
        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 || ''
@@ -632,7 +620,6 @@
        handlePalletScan(flag = true) {
            if (this.scanForm.palletCode) {
                // this.$message.success(`托盘码: ${this.scanForm.palletCode}`)
                this.loadPalletData(flag)
            } else {
            }
@@ -675,15 +662,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()
@@ -735,8 +724,6 @@
                    this.$message.success('操作成功')
                    this.confirmDialogVisible = false
                    this.resetForm()
                    // this.loadUnpickedData()
                    // this.loadPickedData()
                } else {
                    this.$message.error(response.message || '操作失败')
                }
@@ -841,11 +828,6 @@
                }
            }
        },
        // handleUnpickedRowClick(row) {
        //     // ç‚¹å‡»æœªæ‹£é€‰è¡Œæ—¶è‡ªåŠ¨å¡«å……ç‰©æ–™æ¡ç 
        //     this.scanForm.materialBarcode = row.materielCode
        // },
        refreshUnpickedTable() {
            if (this.scanForm.palletCode) {
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundService.cs
@@ -1205,7 +1205,22 @@
                    response.Success = true;
                    response.Message = "出库完成";
                    response.UpdatedDetails = updateDetails;
                    if (CheckOutboundOrderDetailCompletedByMatCode(request.OrderNo, lockInfo.MaterielCode, outboundOrderDetails))
                    {
                        Func<Dt_OutStockLockInfo, bool> supWhere = x => string.IsNullOrEmpty(outboundOrderDetails.First().SupplyCode) ? true : x.SupplyCode == outboundOrderDetails.First().SupplyCode;
                        Func<Dt_OutStockLockInfo, bool> wareWhere = x => string.IsNullOrEmpty(outboundOrderDetails.First().WarehouseCode) ? true : x.WarehouseCode == outboundOrderDetails.First().WarehouseCode;
                        List<Dt_OutStockLockInfo> stockLockInfos = _outboundLockInfoRepository.QueryData(x =>
                                x.OrderNo == request.OrderNo &&
                                x.MaterielCode == stockInfoDetail.MaterielCode).Where(supWhere).Where(wareWhere).ToList();
                        if (stockLockInfos != null && stockLockInfos.Any())
                        {
                            _outboundLockInfoRepository.DeleteAndMoveIntoHty(stockLockInfos, WIDESEA_Core.Enums.OperateTypeEnum.自动删除);
                        }
                    }
                    // æ£€æŸ¥å‡ºåº“单是否完成
                    if (CheckOutboundOrderCompleted(request.OrderNo))
                    {
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -747,9 +747,9 @@
                string reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                string requestData = string.Empty;
                List<string> lineNos = new List<string>();
                if (outboundOrder.OrderStatus == OutOrderStatusEnum.出库完成.ObjToInt())
                Dt_AllocateMaterialInfo allocateMaterialInfo = _allocateMaterialInfo.QueryFirst(x => x.OrderNo == outboundOrder.OrderNo);
                if (outboundOrder.OrderStatus == OutOrderStatusEnum.出库完成.ObjToInt() && outboundOrder.OrderStatus == 0 && allocateMaterialInfo == null)
                {
                    Dt_AllocateOrder allocateOrder = _allocateOrderRepository.QueryFirst(x => x.OrderNo == outboundOrder.OrderNo);
                    if (allocateOrder == null)
                    {
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Jobs/QuartzJobMildd.cs
@@ -63,10 +63,10 @@
                };
                //if (App.HostEnvironment.IsDevelopment())
                //{
                //    return;
                //}
                if (App.HostEnvironment.IsDevelopment())
                {
                    return;
                }
                foreach (var item in allQzServices)
                {
                    var ResuleModel = schedulerCenter.AddScheduleJobAsync(item).Result;