647556386
2025-11-22 68628c6cc163cddfcc745c225a9f3f34767261ef
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/outbound/PickingConfirm.vue
@@ -1,354 +1,1290 @@
<template>
  <div class="picking-confirm">
  <div class="OutboundPicking-container">
    <div class="page-header">
      <el-page-header @back="goBack">
        <template #content>
          <span class="title">出库拣选确认 - {{ orderInfo.orderNo }}</span>
          <span class="title">出库拣选确认 - {{ this.$route.query.orderNo }}</span>
        </template>
      </el-page-header>
    </div>
    <!-- æ‰«ç åŒºåŸŸ -->
    <div class="scanner-area">
      <el-card>
        <div class="scanner-form">
          <el-input
            ref="palletInput"
            v-model="scanData.palletCode"
            placeholder="扫描托盘码"
            @change="onPalletScan"
            @keyup.enter.native="onPalletScan">
          </el-input>
          <el-input
           ref="barcodeInput"
            v-model="scanData.barcode"
            placeholder="扫描物料条码"
            @change="onBarcodeScan"
            @keyup.enter.native="onBarcodeScan">
          </el-input>
          <el-button type="success" @click="confirmPicking">确认拣选</el-button>
     <!--      <el-button type="warning" @click="openSplitDialog">拆包</el-button>
          <el-button type="info" @click="openRevertSplitDialog">撤销拆包</el-button> -->
          <el-button type="primary" @click="openBatchReturnDialog">回库</el-button>
          <!-- <el-button type="danger" @click="handleDirectOutbound">直接出库</el-button>  -->
    <el-row :gutter="20" class="main-content">
      <el-col :span="8">
        <div class="scan-section">
          <el-card header="扫码区域">
            <el-form label-width="100px" size="small">
              <el-form-item label="托盘条码">
                <el-input
                  v-model="scanForm.palletCode"
                  placeholder="扫描或输入托盘条码"
                  @keyup.enter="handlePalletScan"
                  clearable
                >
                  <template #append>
                    <el-button @click="handlePalletScan">确认</el-button>
                  </template>
                </el-input>
              </el-form-item>
              <el-form-item label="物料条码">
                <el-input
                  v-model="scanForm.barcode"
                  placeholder="扫描或输入物料条码"
                  @keyup.enter="handleBarcodeScan"
                  :disabled="!currentPallet"
                  clearable
                >
                  <template #append>
                    <el-button @click="handleBarcodeScan" :disabled="!currentPallet">确认</el-button>
                  </template>
                </el-input>
              </el-form-item>
              <el-form-item label="拣选数量">
                <el-input-number
                  v-model="scanForm.quantity"
                  :min="1"
                  :max="maxPickQuantity"
                  :disabled="!currentLockInfo"
                />
              </el-form-item>
            </el-form>
            <div class="current-info" v-if="currentPallet">
              <p>当前托盘: {{ currentPallet.palletCode }}</p>
              <p>货位: {{ currentPallet.locationCode }}</p>
              <p>状态: {{ currentPallet.statusText }}</p>
            </div>
          </el-card>
          <div class="action-buttons">
            <el-button
              type="warning"
              @click="handleBackToStock"
              :disabled="!currentPallet"
              style="margin-bottom: 10px;"
            >
              å›žåº“
            </el-button>
            <el-button
              type="success"
              @click="handleDirectOutbound"
              :disabled="!currentPallet"
              style="margin-bottom: 10px;"
            >
              ç›´æŽ¥å‡ºåº“
            </el-button>
            <el-button
              type="primary"
              @click="handleOpenSplit"
              :disabled="!currentLockInfo"
            >
              æ‹†åŒ…
            </el-button>
          </div>
        </div>
      </el-col>
      </el-card>
    </div>
      <el-col :span="16">
        <el-card header="拣选结果">
          <div class="summary-info">
            <el-alert
              :title="`未拣货: ${unpickedCount} æ¡, ${unpickedQuantity} ä¸ª`"
              type="warning"
              :closable="false"
            />
          </div>
    <!-- æ±‡æ€»ä¿¡æ¯ -->
    <div class="summary-area">
      <el-card>
        <div class="summary-info">
          <el-tag type="warning">未拣选条数: {{summary.unpickedCount}}</el-tag>
          <el-tag type="danger">未拣选数量: {{summary.unpickedQuantity}}</el-tag>
    <!--       <el-tag type="success">已拣选条数: {{summary.pickedCount}}</el-tag> -->
          <el-tag type="info">托盘状态: {{palletStatus}}</el-tag>
        </div>
      </el-card>
    </div>
          <vol-table
            :data="pickedList"
            :columns="pickedColumns"
            :pagination="false"
            :height="400"
          >
            <template #action="{ row }">
              <el-button type="text" @click="handleCancelPick(row)">撤销</el-button>
            </template>
          </vol-table>
        </el-card>
      </el-col>
    </el-row>
    <!-- æ•°æ®åˆ—表 -->
    <div class="content-area">
      <el-row :gutter="20">
        <el-col :span="12">
          <el-card header="未拣选列表">
            <el-table :data="unpickedList" border height="440">
              <el-table-column prop="materielCode" label="物料编码" width="120"></el-table-column>
              <el-table-column prop="assignQuantity" label="分配数量" width="100"></el-table-column>
              <el-table-column prop="remainQuantity" label="剩余数量" width="100"></el-table-column>
              <el-table-column prop="locationCode" label="货位" width="100"></el-table-column>
              <el-table-column prop="currentBarcode" label="条码"></el-table-column>
             <!--  <el-table-column label="操作" width="100">
                <template slot-scope="scope">
                  <el-button
                    size="mini"
                    type="primary"
                    @click="handleSingleReturn(scope.row)">
                    å›žåº“
                  </el-button>
                </template>
              </el-table-column> -->
            </el-table>
          </el-card>
        </el-col>
        <el-col :span="12">
          <el-card header="已拣选列表">
             <div class="table-actions">
              <el-button
                size="mini"
                type="danger"
                :disabled="selectedPickedRows.length === 0"
                @click="batchCancelSelected">
                å–消拣选
              </el-button>
              <span class="selection-count">已选择 {{selectedPickedRows.length}} é¡¹</span>
            </div>
            <el-table :data="pickedList" border height="400"  style="width: 100%"    @selection-change="handlePickedSelectionChange">
              <el-table-column type="selection" width="55"></el-table-column>
              <el-table-column prop="materielCode" label="物料编码" width="120"></el-table-column>
              <el-table-column prop="pickedQty" label="已拣数量" width="100"></el-table-column>
              <el-table-column prop="locationCode" label="货位" width="100"></el-table-column>
              <el-table-column prop="currentBarcode" label="条码"></el-table-column>
            </el-table>
          </el-card>
        </el-col>
      </el-row>
    </div>
    <!-- æ‹†åŒ…弹窗 -->
    <vol-box
      v-model="splitVisible"
      title="拆包操作"
      :width="600"
      :height="500"
    >
      <SplitPackageModal
        v-if="splitVisible"
        :lockInfo="currentLockInfo"
        @success="handleSplitSuccess"
        @close="splitVisible = false"
      />
    </vol-box>
<!-- æ‹†åŒ…弹窗 -->
     <div v-if="showCustomSplitDialog" class="custom-dialog-overlay">
      <div class="custom-dialog-wrapper">
        <div class="custom-dialog">
          <div class="custom-dialog-header">
            <h3>拆包操作</h3>
             <!--   <el-button
              type="text"
              icon="el-icon-close"
              @click="closeCustomSplitDialog"
              class="close-button">
            </el-button> -->
                  <el-button
              type="text"
              @click="closeCustomSplitDialog"
              class="close-button">
              X
            </el-button>
          </div>
          <div class="custom-dialog-body">
            <el-form :model="splitForm"  :rules="splitFormRules" ref="splitFormRef" label-width="100px">
              <el-form-item label="订单编号">
                <el-input v-model="splitForm.orderNo" disabled></el-input>
              </el-form-item>
              <el-form-item label="托盘编号">
                <el-input v-model="splitForm.palletCode" disabled></el-input>
              </el-form-item>
              <el-form-item label="原条码"  prop="originalBarcode">
                <el-input
                  v-model="splitForm.originalBarcode"
                  placeholder="扫描原条码"
                  @keyup.enter.native="onSplitBarcodeScan"
                  @change="onSplitBarcodeScan"
                  clearable>
                </el-input>
              </el-form-item>
              <el-form-item label="物料编码">
                <el-input v-model="splitForm.materielCode" disabled></el-input>
              </el-form-item>
              <el-form-item label="剩余数量">
                <el-input v-model="splitForm.maxQuantity" disabled></el-input>
              </el-form-item>
              <el-form-item label="拆包数量" prop="splitQuantity">
                <el-input-number
                  v-model="splitForm.splitQuantity"
                  :min="1"
                  :max="splitForm.maxQuantity"
                  :precision="2"
                  :step="1"
                  style="width: 100%">
                </el-input-number>
              </el-form-item>
            </el-form>
          </div>
          <div class="custom-dialog-footer">
            <el-button @click="closeCustomSplitDialog">取消</el-button>
            <el-button type="primary" @click="handleSplitPackage" :loading="splitLoading">确认拆包</el-button>
          </div>
        </div>
      </div>
    </div>
    <!-- æ’¤é”€æ‹†åŒ…弹窗 -->
    <div v-if="showRevertSplitDialog" class="custom-dialog-overlay">
      <div class="custom-dialog-wrapper">
        <div class="custom-dialog">
          <div class="custom-dialog-header">
            <h3>撤销拆包</h3>
            <el-button
              type="text"
              @click="closeRevertSplitDialog"
              class="close-button">
              Ã—
            </el-button>
          </div>
          <div class="custom-dialog-body">
            <el-form
              :model="revertSplitForm"
              :rules="revertSplitFormRules"
              ref="revertSplitFormRef"
              label-width="100px">
              <el-form-item label="原条码" prop="originalBarcode">
                <el-input
                  v-model="revertSplitForm.originalBarcode"
                  placeholder="扫描原条码"
                  @keyup.enter.native="onRevertSplitBarcodeScan"
                  @change="onRevertSplitBarcodeScan"
                  clearable>
                </el-input>
              </el-form-item>
            </el-form>
          </div>
          <div class="custom-dialog-footer">
            <el-button @click="closeRevertSplitDialog">取消</el-button>
            <el-button type="primary" @click="handleRevertSplit" :loading="revertSplitLoading">确认撤销</el-button>
          </div>
        </div>
      </div>
    </div>
     <!-- æ‰¹é‡å›žåº“弹窗 -->
    <div v-if="showBatchReturnDialog" class="custom-dialog-overlay">
      <div class="custom-dialog-wrapper">
        <div class="custom-dialog">
          <div class="custom-dialog-header">
            <h3>回库</h3>
            <el-button
              type="text"
              @click="closeBatchReturnDialog"
              class="close-button">
              Ã—
            </el-button>
          </div>
          <div class="custom-dialog-body">
            <el-form
              :model="batchReturnForm"
              :rules="batchReturnFormRules"
              ref="batchReturnFormRef"
              label-width="100px">
              <el-form-item label="订单编号">
                <el-input v-model="batchReturnForm.orderNo" disabled></el-input>
              </el-form-item>
              <el-form-item label="托盘编号" prop="palletCode">
                <el-input
                  v-model="batchReturnForm.palletCode"
                  placeholder="扫描托盘码"
                  @keyup.enter.native="onBatchReturnPalletScan"
                  @change="onBatchReturnPalletScan"
                  clearable>
                </el-input>
              </el-form-item>
              <el-form-item label="未拣选数量">
                <el-input v-model="batchReturnForm.unpickedCount" disabled></el-input>
              </el-form-item>
              <el-form-item label="未拣选条数">
                <el-input v-model="batchReturnForm.unpickedQuantity" disabled></el-input>
              </el-form-item>
            </el-form>
          </div>
          <div class="custom-dialog-footer">
            <el-button @click="closeBatchReturnDialog">取消</el-button>
            <el-button type="primary" @click="handleBatchReturnConfirm" :loading="batchReturnLoading">确认回库</el-button>
          </div>
        </div>
      </div>
    </div>
    <!-- ç›´æŽ¥å‡ºåº“弹窗 -->
    <div v-if="showDirectOutDialog" class="custom-dialog-overlay">
      <div class="custom-dialog-wrapper">
        <div class="custom-dialog">
          <div class="custom-dialog-header">
            <h3>直接出库</h3>
            <el-button
              type="text"
              @click="closeDirectOutDialog"
              class="close-button">
              Ã—
            </el-button>
          </div>
          <div class="custom-dialog-body">
            <el-form
              :model="directOutForm"
              :rules="directOutFormRules"
              ref="directOutFormRef"
              label-width="100px">
              <el-form-item label="订单编号">
                <el-input v-model="directOutForm.orderNo" disabled></el-input>
              </el-form-item>
              <el-form-item label="托盘编号" prop="palletCode">
                <el-input
                  v-model="directOutForm.palletCode"
                  placeholder="扫描托盘码"
                  @keyup.enter.native="onDirectOutPalletScan"
                  @change="onDirectOutPalletScan"
                  clearable>
                </el-input>
              </el-form-item>
            </el-form>
          </div>
          <div class="custom-dialog-footer">
            <el-button @click="closeDirectOutDialog">取消</el-button>
            <el-button type="primary" @click="handleDirectOutConfirm" :loading="directOutLoading">确认出库</el-button>
          </div>
        </div>
      </div>
    </div>
  </div>
  <print-view ref="childs" @parentcall="parentcall"></print-view>
</template>
<script>
import SplitPackageModal from './SplitPackageModal.vue'
import http from '@/api/http.js'
import { ref, defineComponent } from "vue";
import { ElMessage } from 'element-plus'
import { useRoute } from 'vue-router'
import printView from "@/extension/outbound/extend/printView.vue"
export default {
  components: { SplitPackageModal },
export default defineComponent({
  name: 'PickingConfirm',
  components: {printView},
  props: {
    orderNo: {
      type: String,
      required: true
    }
  },
  emits: ['confirm', 'close'],
  data() {
        // å®šä¹‰æ‹†åŒ…表单验证规则
    const validateOriginalBarcode = (rule, value, callback) => {
      if (!value || value.trim() === '') {
        callback(new Error('请输入原条码'));
      } else {
        callback();
      }
    };
    const validateSplitQuantity = (rule, value, callback) => {
      if (value === null || value === undefined || value === '') {
        callback(new Error('请输入拆包数量'));
      } else if (value <= 0) {
        callback(new Error('拆包数量必须大于0'));
      } else if (this.splitForm.maxQuantity && value > this.splitForm.maxQuantity) {
        callback(new Error('拆包数量不能大于剩余数量'));
      } else {
        callback();
      }
    };
    // å®šä¹‰æ’¤é”€æ‹†åŒ…表单验证规则
    const validateRevertOriginalBarcode = (rule, value, callback) => {
      if (!value || value.trim() === '') {
        callback(new Error('请输入原条码'));
      } else {
        callback();
      }
    };
        // å®šä¹‰æ‰¹é‡å›žåº“表单验证规则
    const validateBatchReturnPalletCode = (rule, value, callback) => {
      if (!value || value.trim() === '') {
        callback(new Error('请输入托盘码'));
      } else {
        callback();
      }
    };
     // å®šä¹‰ç›´æŽ¥å‡ºåº“表单验证规则
    const validateDirectOutPalletCode = (rule, value, callback) => {
      if (!value || value.trim() === '') {
        callback(new Error('请输入托盘码'));
      } else {
        callback();
      }
    };
    return {
      orderInfo: {},
      scanForm: {
      scanData: {
        orderNo: '',
        palletCode: '',
        barcode: ''
      },
      unpickedList: [],
      pickedList: [],
       selectedUnpickedRows: [], // æœªæ‹£é€‰åˆ—表选中的行
      selectedPickedRows: [], // å·²æ‹£é€‰åˆ—表选中的行
      summary: {
        unpickedCount: 0,
        unpickedQuantity: 0,
        pickedCount: 0
      },
      palletStatus: '未知',
      showSplitDialog: false,
      showRevertSplitDialog: false,
      showCustomSplitDialog: false, // è‡ªå®šä¹‰æ‹†åŒ…弹窗显示状态
      showBatchReturnDialog: false, // æ‰¹é‡å›žåº“弹窗显示状态
      showReturnDialog: false,
      splitLoading: false,
       revertSplitLoading: false,
      batchReturnLoading: false, // æ‰¹é‡å›žåº“加载状态
      splitForm: {
        orderNo: '',
        palletCode: '',
        originalBarcode: '',
        materielCode: '',
        splitQuantity: 0,
        maxQuantity: 0
      },
            // æ‹†åŒ…表单验证规则
      splitFormRules: {
        originalBarcode: [
          { required: true, validator: validateOriginalBarcode, trigger: 'blur' }
        ],
        splitQuantity: [
          { required: true, validator: validateSplitQuantity, trigger: 'blur' }
        ]
      },
      revertSplitForm: {
        originalBarcode: ''
      },
          // æ’¤é”€æ‹†åŒ…表单验证规则
      revertSplitFormRules: {
        originalBarcode: [
          { required: true, validator: validateRevertOriginalBarcode, trigger: 'blur' }
        ]
      },
         // æ‰¹é‡å›žåº“表单
      batchReturnForm: {
        orderNo: '',
        palletCode: '',
        unpickedCount: 0,
        unpickedQuantity: 0
      },
      // æ‰¹é‡å›žåº“表单验证规则
      batchReturnFormRules: {
        palletCode: [
          { required: true, validator: validateBatchReturnPalletCode, trigger: 'blur' }
        ]
      },
       showDirectOutDialog: false, // ç›´æŽ¥å‡ºåº“弹窗显示状态
      directOutLoading: false, // ç›´æŽ¥å‡ºåº“加载状态
      directOutForm: {
        orderNo: '',
        palletCode: ''
      },
      directOutFormRules: {
        palletCode: [
          { required: true, validator: validateDirectOutPalletCode, trigger: 'blur' }
        ]
      },
      returnForm: {
        orderNo: '',
        palletCode: '',
        barcode: '',
        quantity: 1
        materielCode: '',
        returnQuantity: 0
      },
      currentPallet: null,
      currentLockInfo: null,
      pickedList: [],
      pickedColumns: [
        { field: 'barcode', title: '物料条码', width: 150 },
        { field: 'materielCode', title: '物料编码', width: 120 },
        { field: 'materielName', title: '物料名称', width: 150 },
        { field: 'pickQuantity', title: '拣选数量', width: 100 },
        { field: 'palletCode', title: '托盘编号', width: 120 },
        { field: 'pickTime', title: '拣选时间', width: 160 },
        { field: 'operator', title: '操作人', width: 100 },
        { field: 'action', title: '操作', width: 80, slot: true }
      ],
      splitVisible: false,
      maxPickQuantity: 0
    }
  },
  computed: {
    unpickedCount() {
      return this.orderInfo.unpickedCount || 0
    },
    unpickedQuantity() {
      return this.orderInfo.unpickedQuantity || 0
    }
  },
  methods: {
    goBack() {
      this.$router.back()
    },
    async loadOrderInfo() {
      const orderId = this.$route.query.orderId
      if (!orderId) return
      try {
        const result = await this.http.post(`api/OutboundOrder/GetById?id=${orderId}`)
        if (result.status) {
          this.orderInfo = result.data
        }
      } catch (error) {
        this.$message.error('加载出库单信息失败')
      }
    },
    async handlePalletScan() {
      if (!this.scanForm.palletCode) {
        this.$message.warning('请输入托盘条码')
        return
      }
      try {
        const result = await this.http.get(
          `api/OutboundPicking/GetPalletOutboundStatus?palletCode=${this.scanForm.palletCode}`
        )
        if (result.status) {
          this.currentPallet = result.data
          this.loadPalletLockInfo()
          this.$message.success(`托盘 ${this.scanForm.palletCode} è¯†åˆ«æˆåŠŸ`)
        } else {
          this.$message.error(result.message)
        }
      } catch (error) {
        this.$message.error('托盘识别失败')
      }
    },
    async loadPalletLockInfo() {
      if (!this.currentPallet) return
      try {
        const result = await this.http.get(
          `api/OutboundPicking/GetPalletLockInfos?palletCode=${this.currentPallet.palletCode}`
        )
        if (result.status && result.data.length > 0) {
          this.currentLockInfo = result.data[0]
          this.maxPickQuantity = this.currentLockInfo.assignQuantity - this.currentLockInfo.pickedQty
        }
      } catch (error) {
        console.error('加载锁定信息失败:', error)
      }
    },
    async handleBarcodeScan() {
      // å®žçŽ°æ‰«ç ç¡®è®¤é€»è¾‘
      if (!this.scanForm.barcode) {
        this.$message.warning('请输入物料条码')
        return
      }
      try {
        const request = {
          barcode: this.scanForm.barcode,
          quantity: this.scanForm.quantity,
          palletCode: this.currentPallet.palletCode,
          orderId: this.orderInfo.id
        }
        const result = await this.http.post('api/OutboundPicking/ConfirmPicking', request)
        if (result.status) {
          this.$message.success('拣选确认成功')
          this.scanForm.barcode = ''
          this.scanForm.quantity = 1
          this.loadPickedHistory()
          this.loadOrderInfo()
        } else {
          this.$message.error(result.message)
        }
      } catch (error) {
        this.$message.error('拣选确认失败')
      }
    },
    async handleBackToStock() {
      if (!this.currentPallet) return
      try {
        await this.$confirm(`确定将托盘 ${this.currentPallet.palletCode} å›žåº“吗?`, '提示', {
          type: 'warning'
        })
        const result = await this.http.post('api/BackToStock/GenerateBackToStockTask', {
          palletCode: this.currentPallet.palletCode,
          currentLocation: '拣选位'
        })
        if (result.status) {
          this.$message.success('回库任务已生成')
          this.resetCurrentPallet()
        }
      } catch (error) {
        // ç”¨æˆ·å–消
      }
    },
    async handleDirectOutbound() {
      if (!this.currentPallet) return
      try {
        await this.$confirm(`确定将托盘 ${this.currentPallet.palletCode} ç›´æŽ¥å‡ºåº“吗?`, '提示', {
          type: 'warning'
        })
        const result = await this.http.post('api/OutboundPicking/DirectOutbound', {
          palletCode: this.currentPallet.palletCode
        })
        if (result.status) {
          this.$message.success('直接出库成功')
          this.resetCurrentPallet()
          this.loadOrderInfo()
        }
      } catch (error) {
        // ç”¨æˆ·å–消
      }
    },
    handleOpenSplit() {
      if (!this.currentLockInfo) {
        this.$message.warning('请先选择锁定信息')
        return
      }
      this.splitVisible = true
    },
    handleSplitSuccess() {
      this.$message.success('拆包成功')
      this.loadPalletLockInfo()
    },
    resetCurrentPallet() {
      this.currentPallet = null
      this.currentLockInfo = null
      this.scanForm.palletCode = ''
    },
    async loadPickedHistory() {
      const orderId = this.$route.query.orderId
      if (!orderId) return
      try {
        const result = await this.http.get(`api/OutboundPicking/GetPickingHistory?orderId=${orderId}`)
        if (result.status) {
          this.pickedList = result.data
        }
      } catch (error) {
        console.error('加载拣选历史失败:', error)
      }
    },
    async handleCancelPick(row) {
      try {
        await this.$confirm('确定撤销这条拣选记录吗?', '提示', { type: 'warning' })
        const result = await this.http.post('api/OutboundPicking/CancelPicking', {
          pickingHistoryId: row.id
        })
        if (result.status) {
          this.$message.success('撤销成功')
          this.loadPickedHistory()
          this.loadOrderInfo()
        }
      } catch (error) {
        // ç”¨æˆ·å–消
      }
      isProcessing: false // é˜²æ­¢é‡å¤æäº¤
    }
  },
  mounted() {
    this.loadOrderInfo()
    this.loadPickedHistory()
    // ä»Žè·¯ç”±å‚数获取订单编号
    if (this.$route.query.orderNo) {
      this.scanData.orderNo = this.$route.query.orderNo;
      this.splitForm.orderNo = this.$route.query.orderNo;
      this.returnForm.orderNo = this.$route.query.orderNo;
    }
        // é¡µé¢åŠ è½½åŽè‡ªåŠ¨èšç„¦åˆ°æ‰˜ç›˜ç è¾“å…¥æ¡†
    this.$nextTick(() => {
      this.$refs.palletInput.focus();
    });
  },
  methods: {
    goBack(){
       this.$router.back()
    },
     openSplitDialog() {
      console.log('打开自定义拆包弹窗');
         if (!this.scanData.palletCode) {
        this.$message.warning('请先扫描托盘码');
        return;
      }
      this.showCustomSplitDialog = true;
      // é‡ç½®è¡¨å•
      this.resetSplitForm();
      // è®¾ç½®è®¢å•和托盘信息
      this.splitForm.orderNo = this.scanData.orderNo;
      this.splitForm.palletCode = this.scanData.palletCode;
        // æ¸…除表单验证
      this.$nextTick(() => {
        if (this.$refs.splitFormRef) {
          this.$refs.splitFormRef.clearValidate();
        }
      });
    },
    // å…³é—­è‡ªå®šä¹‰æ‹†åŒ…弹窗
    closeCustomSplitDialog() {
      this.showCustomSplitDialog = false;
      this.resetSplitForm();
         // æ¸…除表单验证
      if (this.$refs.splitFormRef) {
        this.$refs.splitFormRef.clearValidate();
      }
    },
    // æ‰“开撤销拆包弹窗
    openRevertSplitDialog() {
      console.log('打开撤销拆包弹窗');
      this.showRevertSplitDialog = true;
      // é‡ç½®è¡¨å•
      this.resetRevertSplitForm();
      // æ¸…除表单验证
      this.$nextTick(() => {
        if (this.$refs.revertSplitFormRef) {
          this.$refs.revertSplitFormRef.clearValidate();
        }
      });
    },
    // å…³é—­æ’¤é”€æ‹†åŒ…弹窗
    closeRevertSplitDialog() {
      this.showRevertSplitDialog = false;
      this.resetRevertSplitForm();
      // æ¸…除表单验证
      if (this.$refs.revertSplitFormRef) {
        this.$refs.revertSplitFormRef.clearValidate();
      }
    },
     // æ‰“开批量回库弹窗
    openBatchReturnDialog() {
      console.log('打开批量回库弹窗');
      this.showBatchReturnDialog = true;
      // é‡ç½®è¡¨å•
      this.resetBatchReturnForm();
      // è®¾ç½®è®¢å•信息
      this.batchReturnForm.orderNo = this.scanData.orderNo;
      // æ›´æ–°æœªæ‹£é€‰ä¿¡æ¯
      this.batchReturnForm.unpickedCount = this.summary.unpickedCount || 0;
      this.batchReturnForm.unpickedQuantity = this.summary.unpickedQuantity || 0;
      // æ¸…除表单验证
      this.$nextTick(() => {
        if (this.$refs.batchReturnFormRef) {
          this.$refs.batchReturnFormRef.clearValidate();
        }
      });
    },
    // å…³é—­æ‰¹é‡å›žåº“弹窗
    closeBatchReturnDialog() {
      this.showBatchReturnDialog = false;
      this.resetBatchReturnForm();
      // æ¸…除表单验证
      if (this.$refs.batchReturnFormRef) {
        this.$refs.batchReturnFormRef.clearValidate();
      }
    },
    // å›žåº“托盘码扫码
    onBatchReturnPalletScan() {
      if (!this.batchReturnForm.palletCode) return;
      this.batchReturnForm.palletCode = this.batchReturnForm.palletCode.replace(/\n/g, '').trim();
      // æ¸…除验证状态
      if (this.$refs.batchReturnFormRef) {
        this.$refs.batchReturnFormRef.clearValidate(['palletCode']);
      }
    },
    // å›žåº“确认
    async handleBatchReturnConfirm() {
      // è¡¨å•验证
      if (this.$refs.batchReturnFormRef) {
        this.$refs.batchReturnFormRef.validate((valid) => {
          if (valid) {
            this.submitBatchReturn();
          } else {
            this.$message.warning('请扫描托盘码');
            return false;
          }
        });
      } else {
        // å¦‚果没有表单引用,使用原有的验证
        if (!this.batchReturnForm.palletCode) {
          this.$message.warning('请扫描托盘码');
          return;
        }
        this.submitBatchReturn();
      }
    },
    // æäº¤å›žåº“请求
    async submitBatchReturn() {
      this.batchReturnLoading = true;
      try {
        const res = await this.http.post('/api/OutboundPicking/return-to-stock', {
          orderNo: this.batchReturnForm.orderNo,
          palletCode: this.batchReturnForm.palletCode
        });
        if (res.status) {
          this.$message.success('回库成功');
          this.showBatchReturnDialog = false;
          this.loadData();
        } else {
          this.$message.error(res.message || '回库失败');
        }
      } catch (error) {
        this.$message.error('回库失败');
      } finally {
        this.batchReturnLoading = false;
      }
    },
    // æ‰“开直接出库弹窗
    openDirectOutDialog() {
      console.log('打开直接出库弹窗');
      this.showDirectOutDialog = true;
      // é‡ç½®è¡¨å•
      this.resetDirectOutForm();
      // è®¾ç½®è®¢å•信息
      this.directOutForm.orderNo = this.scanData.orderNo;
      // æ¸…除表单验证
      this.$nextTick(() => {
        if (this.$refs.directOutFormRef) {
          this.$refs.directOutFormRef.clearValidate();
        }
      });
    },
    // å…³é—­ç›´æŽ¥å‡ºåº“弹窗
    closeDirectOutDialog() {
      this.showDirectOutDialog = false;
      this.resetDirectOutForm();
      // æ¸…除表单验证
      if (this.$refs.directOutFormRef) {
        this.$refs.directOutFormRef.clearValidate();
      }
    },
    // ç›´æŽ¥å‡ºåº“托盘码扫码
    onDirectOutPalletScan() {
      if (!this.directOutForm.palletCode) return;
      this.directOutForm.palletCode = this.directOutForm.palletCode.replace(/\n/g, '').trim();
      // æ¸…除验证状态
      if (this.$refs.directOutFormRef) {
        this.$refs.directOutFormRef.clearValidate(['palletCode']);
      }
    },
    // ç›´æŽ¥å‡ºåº“确认
    async handleDirectOutConfirm() {
      // è¡¨å•验证
      if (this.$refs.directOutFormRef) {
        this.$refs.directOutFormRef.validate((valid) => {
          if (valid) {
            this.submitDirectOut();
          } else {
            this.$message.warning('请扫描托盘码');
            return false;
          }
        });
      } else {
        // å¦‚果没有表单引用,使用原有的验证
        if (!this.directOutForm.palletCode) {
          this.$message.warning('请扫描托盘码');
          return;
        }
        this.submitDirectOut();
      }
    },
    // æäº¤ç›´æŽ¥å‡ºåº“请求
    async submitDirectOut() {
      this.directOutLoading = true;
      try {
        const res = await this.http.post('/api/OutboundPicking/direct-outbound', {
          orderNo: this.directOutForm.orderNo,
          palletCode: this.directOutForm.palletCode
        });
        debugger;
        if (res.status) {
          this.$message.success('直接出库成功');
          this.showDirectOutDialog = false;
          this.loadData();
        } else {
          this.$message.error(res.message || '直接出库失败');
        }
      } catch (error) {
        this.$message.error('直接出库失败');
      } finally {
        this.directOutLoading = false;
      }
    },
    // é‡ç½®ç›´æŽ¥å‡ºåº“表单
    resetDirectOutForm() {
      this.directOutForm.palletCode = '';
    },
    // ä¿®æ”¹åŽŸæœ‰çš„ç›´æŽ¥å‡ºåº“æŒ‰é’®ç‚¹å‡»äº‹ä»¶
    handleDirectOutbound() {
      this.openDirectOutDialog();
    },
    async loadData() {
      if (!this.scanData.orderNo || !this.scanData.palletCode) {
        return;
      }
      try {
        // åŠ è½½æœªæ‹£é€‰åˆ—è¡¨
        const unpickedRes = await this.http.post('/api/OutboundPicking/unpicked-list', this.scanData);
        this.unpickedList = unpickedRes.data || [];
        // åŠ è½½å·²æ‹£é€‰åˆ—è¡¨
        const pickedRes = await this.http.post('/api/OutboundPicking/picked-list', this.scanData);
        this.pickedList = pickedRes.data || [];
        // åŠ è½½æ±‡æ€»ä¿¡æ¯
        const summaryRes = await this.http.post('/api/OutboundPicking/picking-summary',this.scanData);
        this.summary = summaryRes.data || {};
        // æ›´æ–°æ‰˜ç›˜çŠ¶æ€
        this.updatePalletStatus();
      } catch (error) {
        this.$message.error('加载数据失败');
      }
    },
    updatePalletStatus() {
      if (this.unpickedList.length === 0 && this.pickedList.length > 0) {
        this.palletStatus = '已全部拣选';
      } else if (this.unpickedList.length > 0 && this.pickedList.length === 0) {
        this.palletStatus = '待拣选';
      } else if (this.unpickedList.length > 0 && this.pickedList.length > 0) {
        this.palletStatus = '部分拣选';
      } else {
        this.palletStatus = '无数据';
      }
    },
    // å·²æ‹£é€‰åˆ—表选择变化
    handlePickedSelectionChange(selection) {
      this.selectedPickedRows = selection;
    },
       // æ‰¹é‡å–消选中的已拣选项
    async batchCancelSelected() {
      if (this.selectedPickedRows.length === 0) {
        this.$message.warning('请先选择要取消的项');
        return;
      }
      this.$confirm(`确定要取消选中的 ${this.selectedPickedRows.length} é¡¹å—?`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(async () => {
        try {
          let successCount = 0;
          let errorCount = 0;
          for (const row of this.selectedPickedRows) {
            try {
              const res = await this.http.post('/api/OutboundPicking/CancelPicking', {
                orderNo: this.scanData.orderNo,
                palletCode: this.scanData.palletCode,
                barcode: row.currentBarcode
              });
              if (res.status) {
                successCount++;
              } else {
                errorCount++;
                console.error(`取消拣选失败: ${row.Barcode}`, res.message);
              }
            } catch (error) {
              errorCount++;
              console.error(`取消拣选失败: ${row.Barcode}`, error);
            }
          }
          if (errorCount === 0) {
            this.$message.success(`成功取消 ${successCount} é¡¹`);
          } else {
            this.$message.warning(`成功取消 ${successCount} é¡¹ï¼Œå¤±è´¥ ${errorCount} é¡¹`);
          }
          this.loadData();
          this.selectedPickedRows = [];
        } catch (error) {
          this.$message.error('批量取消操作失败');
        }
      }).catch(() => {
        this.$message.info('已取消批量操作');
      });
    },
    // æ‰˜ç›˜ç æ‰«ç 
    onPalletScan() {
     // åŽ»é™¤å›žè½¦ç¬¦å’Œå‰åŽç©ºæ ¼
      this.scanData.palletCode = this.scanData.palletCode.replace(/\n/g, '').trim();
      if (!this.scanData.palletCode) return;
      this.splitForm.palletCode = this.scanData.palletCode;
      this.returnForm.palletCode = this.scanData.palletCode;
      // åŠ è½½æ•°æ®
      this.loadData();
      // è‡ªåŠ¨è·³è½¬åˆ°ç‰©æ–™æ¡ç è¾“å…¥æ¡†
      this.$nextTick(() => {
        this.$refs.barcodeInput.focus();
      });
    },
    onBarcodeScan() {
       // åŽ»é™¤å›žè½¦ç¬¦å’Œå‰åŽç©ºæ ¼
      this.scanData.barcode = this.scanData.barcode.replace(/\n/g, '').trim();
      if (!this.scanData.barcode) return;
      // è‡ªåŠ¨ç¡®è®¤æ‹£é€‰
      this.confirmPicking();
    },
    async confirmPicking() {
       if (this.isProcessing) return;
      if (!this.scanData.orderNo || !this.scanData.palletCode || !this.scanData.barcode) {
        this.$message.warning('请先扫描托盘码和物料条码');
        this.focusBarcodeInput();
        return;
      }
      this.isProcessing = true;
      try {
        const res = await this.http.post('/api/OutboundPicking/confirm-picking', this.scanData);
        if (res.status) {
          this.$message.success('拣选确认成功');
          this.scanData.barcode = ''; // æ¸…空物料条码
          this.loadData();
          console.log(res.data.splitResults)
          if(res.data && res.data.splitResults.length>0){
            // è°ƒç”¨å­ç»„件打印方法
            this.$refs.childs.open(res.data.splitResults);
            //this.$refs.childs.printSplitLabel(res.data.splitResults);
          }
          // æˆåŠŸåŽç»§ç»­èšç„¦åˆ°ç‰©æ–™æ¡ç è¾“å…¥æ¡†ï¼Œå‡†å¤‡ä¸‹ä¸€ä¸ªæ‰«ç 
          this.$nextTick(() => {
            this.$refs.barcodeInput.focus();
          });
        } else {
          // æ˜¾ç¤ºåŽç«¯è¿”回的错误信息
          this.$message.error(res.message || '拣选确认失败');
          // å¤±è´¥æ—¶èšç„¦å¹¶é€‰ä¸­ç‰©æ–™æ¡ç è¾“入框内容
          this.focusBarcodeInput(true);
        }
      } catch (error) {
        this.$message.error('拣选确认失败: ' + (error.message || '网络错误'));
        // å¤±è´¥æ—¶èšç„¦å¹¶é€‰ä¸­ç‰©æ–™æ¡ç è¾“入框内容
        this.focusBarcodeInput(true);
      } finally {
        this.isProcessing = false;
      }
    },
    // èšç„¦åˆ°ç‰©æ–™æ¡ç è¾“入框
    focusBarcodeInput(selectText = false) {
      this.$nextTick(() => {
        const input = this.$refs.barcodeInput;
        if (input && input.$el && input.$el.querySelector('input')) {
          const inputEl = input.$el.querySelector('input');
          inputEl.focus();
          if (selectText) {
            inputEl.select();
          }
        }
      });
    },
    async cancelPicking(row) {
      try {
        const res = await this.http.post('/api/OutboundPicking/CancelPicking', {
          orderNo: this.scanData.orderNo,
          palletCode: this.scanData.palletCode,
          barcode: row.Barcode
        });
        if (res.status) {
          this.$message.success('取消拣选成功');
          this.loadData();
        } else {
          this.$message.error(res.message || '取消拣选失败');
        }
      } catch (error) {
        this.$message.error('取消拣选失败');
      }
    },
   /*  // å›žåº“操作 - å›žåº“整个托盘未拣选的货物
    async handleBatchReturn() {
      if (!this.scanData.orderNo || !this.scanData.palletCode) {
        this.$message.warning('请先扫描托盘码');
        return;
      }
      if (this.unpickedList.length === 0) {
        this.$message.warning('该托盘没有可回库的货物');
        return;
      }
      this.$confirm(`确定要回库整个托盘的未拣选货物吗?共 ${this.unpickedList.length} æ¡è®°å½•`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(async () => {
        try {
          const res = await this.http.post('/api/OutboundPicking/batch-return-to-stock', {
            orderNo: this.scanData.orderNo,
            palletCode: this.scanData.palletCode
          });
          if (res.success) {
            this.$message.success('批量回库成功');
            this.loadData();
          } else {
            this.$message.error(res.message || '批量回库失败');
          }
        } catch (error) {
          this.$message.error('批量回库失败');
        }
      }).catch(() => {
        this.$message.info('已取消批量回库');
      });
    }, */
   /*  // ç›´æŽ¥å‡ºåº“操作
    async handleDirectOutbound() {
      if (!this.scanData.orderNo || !this.scanData.palletCode) {
        this.$message.warning('请先扫描托盘码');
        return;
      }
      this.$confirm('确定要直接出库整个托盘吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(async () => {
        try {
          const res = await this.http.post('/api/OutboundPicking/direct-outbound', {
            orderNo: this.scanData.orderNo,
            palletCode: this.scanData.palletCode
          });
          if (res.success) {
            this.$message.success('直接出库成功');
            this.loadData();
          } else {
            this.$message.error(res.message || '直接出库失败');
          }
        } catch (error) {
          this.$message.error('直接出库失败');
        }
      }).catch(() => {
        this.$message.info('已取消直接出库');
      });
    }, */
    // ç¡®è®¤å›žåº“(通过弹窗)
    async handleReturnConfirm() {
      if (!this.returnForm.barcode) {
        this.$message.warning('请扫描回库条码');
        return;
      }
      try {
        const res = await this.http.post('/api/OutboundPicking/return-to-stock', {
          orderNo: this.returnForm.orderNo,
          palletCode: this.returnForm.palletCode,
          barcode: this.returnForm.barcode
        });
        if (res.status) {
          this.$message.success('回库成功');
          this.showReturnDialog = false;
          this.resetReturnForm();
          this.loadData();
        } else {
          this.$message.error(res.message || '回库失败');
        }
      } catch (error) {
        this.$message.error('回库失败');
      }
    },
    // æ‹†åŒ…扫码
    async onSplitBarcodeScan() {
      if (!this.splitForm.originalBarcode) return;
      // åŽ»é™¤å›žè½¦ç¬¦å’Œå‰åŽç©ºæ ¼
      this.splitForm.originalBarcode = this.splitForm.originalBarcode.replace(/\n/g, '').trim();
      try {
        const res = await this.http.post('/api/OutboundPicking/split-package-info', {
            orderNo: this.splitForm.orderNo,
            palletCode: this.splitForm.palletCode,
            barcode: this.splitForm.originalBarcode
        });
        if (res.status) {
          this.splitForm.materielCode = res.data.materielCode;
          this.splitForm.maxQuantity = res.data.remainQuantity;
          this.splitForm.splitQuantity = Math.min(1, this.splitForm.maxQuantity);
           // æ¸…除验证状态
          if (this.$refs.splitFormRef) {
            this.$refs.splitFormRef.clearValidate(['originalBarcode']);
          }
        } else {
          this.$message.error(res.message || '获取拆包信息失败');
            // éªŒè¯å¤±è´¥ï¼Œè®¾ç½®é”™è¯¯çŠ¶æ€
          if (this.$refs.splitFormRef) {
            this.$refs.splitFormRef.validateField('originalBarcode');
          }
        }
      } catch (error) {
        this.$message.error('获取拆包信息失败');
         // éªŒè¯å¤±è´¥ï¼Œè®¾ç½®é”™è¯¯çŠ¶æ€
        if (this.$refs.splitFormRef) {
          this.$refs.splitFormRef.validateField('originalBarcode');
        }
      }
    },
    async handleSplitPackage() {
       // è¡¨å•验证
      if (this.$refs.splitFormRef) {
        this.$refs.splitFormRef.validate((valid) => {
          if (valid) {
            this.submitSplitPackage();
          } else {
            this.$message.warning('请填写完整的拆包信息');
            return false;
          }
        });
      } else {
        // å¦‚果没有表单引用,使用原有的验证
        if (!this.splitForm.originalBarcode || this.splitForm.splitQuantity <= 0) {
          this.$message.warning('请填写完整的拆包信息');
          return;
        }
        if (this.splitForm.splitQuantity > this.splitForm.maxQuantity) {
          this.$message.warning('拆包数量不能大于剩余数量');
          return;
        }
        this.submitSplitPackage();
      }
    },
    // æäº¤æ‹†åŒ…请求
    async submitSplitPackage() {
    this.splitLoading = true;
     try {
        const res = await this.http.post('/api/OutboundPicking/split-package', this.splitForm);
        if (res.status) {
          this.$message.success('拆包成功');
          this.showSplitDialog = false;
              this.splitLoading = false;
          this.resetSplitForm();
          this.loadData();
        } else {
               this.splitLoading = false;
          this.$message.error(res.message || '拆包失败');
        }
      } catch (error) {
             this.splitLoading = false;
        this.$message.error('拆包失败');
      }
    },
        // æ’¤é”€æ‹†åŒ…扫码
    onRevertSplitBarcodeScan() {
      if (!this.revertSplitForm.originalBarcode) return;
      this.revertSplitForm.originalBarcode = this.revertSplitForm.originalBarcode.replace(/\n/g, '').trim();
      // æ¸…除验证状态
      if (this.$refs.revertSplitFormRef) {
        this.$refs.revertSplitFormRef.clearValidate(['originalBarcode']);
      }
    },
    async handleRevertSplit() {
      // è¡¨å•验证
      if (this.$refs.revertSplitFormRef) {
        this.$refs.revertSplitFormRef.validate((valid) => {
          if (valid) {
            this.submitRevertSplit();
          } else {
            this.$message.warning('请输入原条码');
            return false;
          }
        });
      } else {
        // å¦‚果没有表单引用,使用原有的验证
        if (!this.revertSplitForm.originalBarcode) {
          this.$message.warning('请输入原条码');
          return;
        }
        this.submitRevertSplit();
      }
    },
    // æäº¤æ’¤é”€æ‹†åŒ…请求
    async submitRevertSplit() {
      this.revertSplitLoading = true;
      try {
        const res = await this.http.post('/api/OutboundPicking/revert-split-package', {
          originalBarcode: this.revertSplitForm.originalBarcode
        });
        if (res.status) {
          this.$message.success('撤销拆包成功');
          this.showRevertSplitDialog = false;
           this.revertSplitLoading = false;
          this.revertSplitForm.originalBarcode = '';
          this.loadData();
        } else {
         this.revertSplitLoading = false;
          this.$message.error(res.message || '撤销拆包失败');
        }
      } catch (error) {
         this.revertSplitLoading = false;
        this.$message.error('撤销拆包失败');
      }
    },
    resetSplitForm() {
      this.splitForm.originalBarcode = '';
      this.splitForm.materielCode = '';
      this.splitForm.splitQuantity = 0;
      this.splitForm.maxQuantity = 0;
    },
    // é‡ç½®æ‰¹é‡å›žåº“表单
    resetBatchReturnForm() {
      this.batchReturnForm.palletCode = '';
      this.batchReturnForm.unpickedCount = 0;
      this.batchReturnForm.unpickedQuantity = 0;
    },
    resetReturnForm() {
      this.returnForm.barcode = '';
      this.returnForm.materielCode = '';
      this.returnForm.returnQuantity = 0;
    }
  }
})
</script>
<style scoped>
.picking-container {
  padding: 20px;
  position: relative; /* ä¸ºå¼¹çª—定位提供上下文 */
}
.scanner-form {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
}
.scanner-form .el-input {
  width: 200px;
}
.summary-info {
  display: flex;
  gap: 20px;
  flex-wrap: wrap;
}
/* è¡¨æ ¼æ“ä½œåŒºåŸŸæ ·å¼ */
.table-actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10px;
  padding: 0 10px;
}
.selection-count {
  font-size: 12px;
  color: #909399;
}
/* è¡¨æ ¼æ ·å¼è°ƒæ•´ */
.content-area .el-table {
  margin-top: 0;
}
/* ç¡®ä¿è¡¨æ ¼é«˜åº¦é€‚应 */
.content-area .el-card__body {
  padding: 15px;
}
/* è‡ªå®šä¹‰å¼¹çª—样式 */
.custom-dialog-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9999;
}
.custom-dialog-wrapper {
  position: relative;
  z-index: 10000;
}
.custom-dialog {
  background: white;
  border-radius: 4px;
  width: 500px;
  max-width: 90vw;
  max-height: 90vh;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  overflow: auto;
}
.custom-dialog-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20px 20px 10px;
  border-bottom: 1px solid #ebeef5;
}
.custom-dialog-header h3 {
  margin: 0;
  color: #303133;
}
/* å…³é—­æŒ‰é’®æ ·å¼ */
.close-button {
  font-size: 18px;
  color: #909399;
  padding: 0;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.close-button:hover {
  color: #409EFF;
  background-color: transparent;
}
.custom-dialog-body {
  padding: 20px;
}
.custom-dialog-footer {
  padding: 10px 20px 20px;
  text-align: right;
  border-top: 1px solid #ebeef5;
}
.custom-dialog-footer .el-button {
  margin-left: 10px;
}
/* ç¡®ä¿å¼¹çª—在移动设备上也能正常显示 */
@media (max-width: 768px) {
  .custom-dialog {
    width: 95vw;
    margin: 10px;
  }
}
</script>
</style>