04bd9fa1060d711e0e90492c103699f50b49ba17..dcdb87f1cb6cfd66d3fc01bc2248e4876c37f223
2025-11-30 pan
Merge branch 'master' of http://115.159.85.185:8098/r/ZhongRui/ALDbanyunxia...
dcdb87 对比 | 目录
2025-11-30 pan
提交
458dea 对比 | 目录
2025-11-30 heshaofeng
提交
4537c5 对比 | 目录
2025-11-30 647556386
1
1b9406 对比 | 目录
2025-11-30 pan
提交
c10010 对比 | 目录
2025-11-30 heshaofeng
代码提交
deece6 对比 | 目录
已添加1个文件
已修改17个文件
3021 ■■■■ 文件已修改
项目代码/WIDESEA_WMSClient/src/extension/inbound/allocateinboundOrder.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/outbound/extend/NoStockOut.vue 269 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/outbound/extend/printView.vue 771 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/outbound/extend/printView1.vue 588 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/CopilotIndices/17.14.878.3237/CodeChunks.db-shm 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/CopilotIndices/17.14.878.3237/SemanticSymbols.db-shm 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_BasicService/InvokeMESService.cs 114 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_DTO/Allocate/AllocateDto.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_DTO/Outbound/BatchOutBoundDto.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundOrderDetail.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_PickingRecord.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundBatchPickingService.cs 1071 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderDetailService.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderService.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs 178 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_StockService/StockInfoService.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Inbound.cs 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/inbound/allocateinboundOrder.js
@@ -194,8 +194,8 @@
    // æ”¶é›†æ‰€æœ‰é€‰ä¸­å•据的编号(过滤无单据号的异常行)
    const inboundOrderNos = selectedRows
      .filter(row => row.inboundOrderNo)
      .map(row => row.inboundOrderNo);
      .filter(row => row.orderNo)
      .map(row => row.orderNo);
    // æ ¡éªŒ2:是否有有效单据号
    if (inboundOrderNos.length === 0) {
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/extend/NoStockOut.vue
@@ -9,51 +9,29 @@
      class="custom-vol-box"
    >
      <div>
        <!-- å•据选择区域(可输入搜索) -->
        <!-- å•据输入区域(支持扫码) -->
        <el-form :inline="true" :model="orderForm" style="margin-bottom: 20px; align-items: flex-end;">
          <el-form-item label="出库单据:" name="outboundOrderId">
            <el-select
              v-model="orderForm.outboundOrderId"
              placeholder="请选择或扫描出库单据号"
          <el-form-item label="出库单据:" name="outboundOrderNo">
            <el-input
              v-model="orderForm.outboundOrderNo"
              placeholder="请输入或扫描出库单据号"
              clearable
              style="width: 220px; margin-right: 10px;"
              @change="handleOrderChange"
              @visible-change="handleOutboundVisibleChange"
              :loading="loading"
              filterable
              :filter-method="filterOutboundOrders"
              @input="handleOutboundScanInput"
              ref="outboundSelectRef"
            >
              <el-option
                v-for="order in filteredOutboundOrders"
                :key="order.id"
                :label="order.orderNo"
                :value="order.id"
              ></el-option>
            </el-select>
              @input="handleOutboundInput"
              @keyup.enter="focusPurchaseInput"
              ref="outboundInputRef"
            ></el-input>
          </el-form-item>
          <el-form-item label="采购单据:" name="purchaseOrderId">
            <el-select
              v-model="orderForm.purchaseOrderId"
              placeholder="请选择或扫描采购单据号"
          <el-form-item label="采购单据:" name="purchaseOrderNo">
            <el-input
              v-model="orderForm.purchaseOrderNo"
              placeholder="请输入或扫描采购单据号"
              clearable
              style="width: 220px; margin-right: 10px;"
              @change="handleOrderChange"
              @visible-change="handlePurchaseVisibleChange"
              :loading="loading"
              filterable
              :filter-method="filterPurchaseOrders"
              @input="handlePurchaseScanInput"
              ref="purchaseSelectRef"
            >
              <el-option
                v-for="order in filteredPurchaseOrders"
                :key="order.id"
                :label="order.orderNo"
                :value="order.id"
              ></el-option>
            </el-select>
              @input="handlePurchaseInput"
              @keyup.enter="focusBarcodeInput"
              ref="purchaseInputRef"
            ></el-input>
          </el-form-item>
        </el-form>
@@ -72,7 +50,7 @@
              @keyup.enter="handleScan"
              autofocus
              class="custom-input"
              :disabled="!orderForm.outboundOrderId || !orderForm.purchaseOrderId"
              :disabled="!orderForm.outboundOrderNo || !orderForm.purchaseOrderNo"
            ></el-input>
          </el-form-item>
          <el-form-item>
@@ -81,7 +59,7 @@
              size="small" 
              @click="handleScan" 
              class="custom-button"
              :disabled="!orderForm.outboundOrderId || !orderForm.purchaseOrderId || loading"
              :disabled="!orderForm.outboundOrderNo || !orderForm.purchaseOrderNo || loading"
            >
              <Search /> ç¡®è®¤æ‰«æ
            </el-button>
@@ -109,7 +87,7 @@
                  </div>
                </transition-group>
                <div class="empty-tip" v-if="scannedBarcodes.length === 0">
                  <span>暂无扫描记录,请先选择单据后扫描条码</span>
                  <span>暂无扫描记录,请先输入单据后扫描条码</span>
                </div>
              </el-scrollbar>
            </div>
@@ -123,7 +101,7 @@
            type="primary" 
            size="small" 
            @click="submit" 
            :disabled="scannedBarcodes.length === 0 || !orderForm.outboundOrderId || !orderForm.purchaseOrderId || loading"
            :disabled="scannedBarcodes.length === 0 || !orderForm.outboundOrderNo || !orderForm.purchaseOrderNo || loading"
            class="submit-btn"
          >
            <Check /> æäº¤å‡ºåº“
@@ -140,6 +118,7 @@
<script setup>
import { ref, reactive, onMounted, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
import { Search } from '@element-plus/icons-vue';
import VolBox from "@/components/basic/VolBox.vue";
import http from '@/api/http';
@@ -147,32 +126,26 @@
// å“åº”式数据
const showDetailBox = ref(false);
const orderForm = reactive({
  outboundOrderId: "",
  purchaseOrderId: ""
  outboundOrderNo: "",
  purchaseOrderNo: ""
});
const formData = reactive({
  barcode: "",
});
const scannedBarcodes = ref([]);
const outboundOrders = ref([]);
const purchaseOrders = ref([]);
const filteredOutboundOrders = ref([]);
const filteredPurchaseOrders = ref([]);
const loading = ref(false);
// æ¨¡æ¿å¼•用
const formRef = ref(null);
const barcodeInputRef = ref(null);
const outboundSelectRef = ref(null); // æ–°å¢žï¼šå‡ºåº“单select的ref
const purchaseSelectRef = ref(null); // æ–°å¢žï¼šé‡‡è´­å•select的ref
const outboundInputRef = ref(null);
const purchaseInputRef = ref(null);
// ç”¨äºŽé˜²æ­¢è¾“入事件和change事件冲突的锁
const isProcessingScan = ref(false);
// ç»„件挂载时初始化过滤后的列表
// ç»„件挂载时聚焦到出库单输入框
onMounted(() => {
  filteredOutboundOrders.value = [...outboundOrders.value];
  filteredPurchaseOrders.value = [...purchaseOrders.value];
  nextTick(() => {
    outboundInputRef.value?.focus();
  });
});
// æ‰“开弹窗
@@ -180,169 +153,44 @@
  showDetailBox.value = true;
  scannedBarcodes.value = [];
  formData.barcode = "";
  orderForm.outboundOrderId = "";
  orderForm.purchaseOrderId = "";
  orderForm.outboundOrderNo = "";
  orderForm.purchaseOrderNo = "";
  nextTick(() => {
    barcodeInputRef.value?.focus();
    outboundInputRef.value?.focus();
  });
};
// åŠ è½½å‡ºåº“å•æ®åˆ—è¡¨
const loadOutboundOrders = async () => {
  if (outboundOrders.value.length > 0) return;
  try {
    loading.value = true;
    const res = await http.get("/api/OutboundPicking/GetAvailablePickingOrders");
    if (res.code === 0) {
      outboundOrders.value = res.data.map(orderNo => ({
        id: orderNo,
        orderNo: orderNo
      }));
      filteredOutboundOrders.value = [...outboundOrders.value];
    } else {
      ElMessage.error("加载出库单据失败:" + (res.message || '未知错误'));
    }
  } catch (error) {
    ElMessage.error("加载出库单据异常:" + error.message);
  } finally {
    loading.value = false;
// å‡ºåº“单输入处理(扫码或手动输入)
const handleOutboundInput = (value) => {
  // æ‰«ç æžªè¾“入通常会自动触发enter事件,这里主要处理手动输入的情况
  if (value && value.trim()) {
    // å¯ä»¥åœ¨è¿™é‡Œæ·»åŠ å‡ºåº“å•å·çš„æ ¼å¼éªŒè¯é€»è¾‘
  }
};
// åŠ è½½é‡‡è´­å•æ®åˆ—è¡¨
const loadPurchaseOrders = async () => {
  if (purchaseOrders.value.length > 0) return;
  try {
    loading.value = true;
    const res = await http.get("/api/OutboundPicking/GetAvailablePurchaseOrders");
    if (res.status === true) {
      purchaseOrders.value = res.data.map(orderNo => ({
        id: orderNo,
        orderNo: orderNo
      }));
      filteredPurchaseOrders.value = [...purchaseOrders.value];
    } else {
      ElMessage.error("加载采购单据失败:" + (res.message || '未知错误'));
    }
  } catch (error) {
    ElMessage.error("加载采购单据异常:" + error.message);
  } finally {
    loading.value = false;
// é‡‡è´­å•输入处理(扫码或手动输入)
const handlePurchaseInput = (value) => {
  if (value && value.trim()) {
    // å¯ä»¥åœ¨è¿™é‡Œæ·»åŠ é‡‡è´­å•å·çš„æ ¼å¼éªŒè¯é€»è¾‘
  }
};
// å‡ºåº“单据过滤方法
const filterOutboundOrders = (value) => {
  if (!value) {
    filteredOutboundOrders.value = [...outboundOrders.value];
// ç„¦ç‚¹è·³è½¬å‡½æ•°
const focusPurchaseInput = () => {
  if (orderForm.outboundOrderNo.trim()) {
    purchaseInputRef.value?.focus();
  } else {
    const lowerValue = value.toLowerCase();
    filteredOutboundOrders.value = outboundOrders.value.filter(order =>
      order.orderNo.toLowerCase().includes(lowerValue)
    );
    ElMessage.warning("请先输入有效的出库单据号");
  }
};
// é‡‡è´­å•据过滤方法
const filterPurchaseOrders = (value) => {
  if (!value) {
    filteredPurchaseOrders.value = [...purchaseOrders.value];
  } else {
    const lowerValue = value.toLowerCase();
    filteredPurchaseOrders.value = purchaseOrders.value.filter(order =>
      order.orderNo.toLowerCase().includes(lowerValue)
    );
  }
};
// å‡ºåº“单据下拉框显示/隐藏时触发
const handleOutboundVisibleChange = (visible) => {
  if (visible) {
    loadOutboundOrders();
  } else {
    // å½“下拉框关闭时,如果是扫描操作导致的,清空输入框
    if (isProcessingScan.value) {
        nextTick(() => {
            outboundSelectRef.value?.clearInput();
            isProcessingScan.value = false;
        });
    }
  }
};
// é‡‡è´­å•据下拉框显示/隐藏时触发
const handlePurchaseVisibleChange = (visible) => {
  if (visible) {
    loadPurchaseOrders();
  } else {
    // å½“下拉框关闭时,如果是扫描操作导致的,清空输入框
    if (isProcessingScan.value) {
        nextTick(() => {
            purchaseSelectRef.value?.clearInput();
            isProcessingScan.value = false;
        });
    }
  }
};
// å•据选择变化时触发
const handleOrderChange = () => {
  // å¦‚果是手动选择,则不清空输入框
  isProcessingScan.value = false;
  scannedBarcodes.value = [];
  nextTick(() => {
const focusBarcodeInput = () => {
  if (orderForm.purchaseOrderNo.trim()) {
    barcodeInputRef.value?.focus();
  });
};
/**
 * å¤„理出库单扫描输入
 * @param {string} scanText - æ‰«ææžªè¾“入的文本
 */
const handleOutboundScanInput = async (scanText) => {
  // é¿å…å¤„理空字符串或由change事件触发的内部操作
  if (!scanText || isProcessingScan.value) return;
  // æŸ¥æ‰¾å®Œå…¨åŒ¹é…çš„订单
  const matchedOrder = outboundOrders.value.find(order => order.orderNo === scanText);
  if (matchedOrder) {
    isProcessingScan.value = true; // æ ‡è®°ä¸ºæ­£åœ¨å¤„理扫描
    // æ‰‹åŠ¨èµ‹å€¼
    orderForm.outboundOrderId = matchedOrder.id;
    ElMessage.success(`成功选择出库单:${matchedOrder.orderNo}`);
    // å»¶è¿Ÿèšç„¦åˆ°ä¸‹ä¸€ä¸ªè¾“入框,确保赋值完成
    setTimeout(() => {
        purchaseSelectRef.value?.focus();
    }, 100);
  }
  // å¦‚果没有匹配项,不做任何事,让el-select保持过滤状态,用户可以手动选择
};
/**
 * å¤„理采购单扫描输入
 * @param {string} scanText - æ‰«ææžªè¾“入的文本
 */
const handlePurchaseScanInput = async (scanText) => {
  if (!scanText || isProcessingScan.value) return;
  const matchedOrder = purchaseOrders.value.find(order => order.orderNo === scanText);
  if (matchedOrder) {
    isProcessingScan.value = true; // æ ‡è®°ä¸ºæ­£åœ¨å¤„理扫描
    orderForm.purchaseOrderId = matchedOrder.id;
    ElMessage.success(`成功选择采购单:${matchedOrder.orderNo}`);
    setTimeout(() => {
        barcodeInputRef.value?.focus();
    }, 100);
  } else {
    ElMessage.warning("请先输入有效的采购单据号");
  }
};
// æ‰«ææ¡ç å¤„理
const handleScan = async () => {
@@ -360,9 +208,10 @@
  try {
    loading.value = true;
    // è¿™é‡Œä¿ç•™äº†åŽŸæœ‰çš„æ¡ç éªŒè¯æŽ¥å£ï¼Œä½ å¯ä»¥æ ¹æ®å®žé™…éœ€æ±‚ä¿®æ”¹æˆ–ä¿ç•™
    const res = await http.post("/api/OutboundPicking/BarcodeValidate", {
      outOder: orderForm.outboundOrderId,
      inOder: orderForm.purchaseOrderId,
      outOder: orderForm.outboundOrderNo, // æ³¨æ„ï¼šè¿™é‡ŒçŽ°åœ¨ä¼ é€’çš„æ˜¯å•æ®å·å­—ç¬¦ä¸²ï¼Œè€Œä¸æ˜¯ID
      inOder: orderForm.purchaseOrderNo,  // æ³¨æ„ï¼šè¿™é‡ŒçŽ°åœ¨ä¼ é€’çš„æ˜¯å•æ®å·å­—ç¬¦ä¸²ï¼Œè€Œä¸æ˜¯ID
      barCode: barcode
    });
@@ -385,9 +234,10 @@
const removeItem = async (index, barcode) => {
  try {
    loading.value = true;
    // è¿™é‡Œä¿ç•™äº†åŽŸæœ‰çš„åˆ é™¤æ¡ç æŽ¥å£ï¼Œä½ å¯ä»¥æ ¹æ®å®žé™…éœ€æ±‚ä¿®æ”¹æˆ–ä¿ç•™
    const res = await http.post("/api/OutboundPicking/DeleteBarcode", {
      outOder: orderForm.outboundOrderId,
      inOder: orderForm.purchaseOrderId,
      outOder: orderForm.outboundOrderNo,
      inOder: orderForm.purchaseOrderNo,
      barCode: barcode
    });
@@ -415,9 +265,10 @@
  try {
    loading.value = true;
    // è¿™é‡Œä¿ç•™äº†åŽŸæœ‰çš„æäº¤æŽ¥å£ï¼Œæ³¨æ„å‚æ•°çŽ°åœ¨ä¼ é€’çš„æ˜¯å•æ®å·å­—ç¬¦ä¸²
    const res = await http.post("/api/OutboundPicking/NoStockOutSubmit", {
      OutOderSubmit: orderForm.outboundOrderId,
      InOderSubmit: orderForm.purchaseOrderId,
      OutOderSubmit: orderForm.outboundOrderNo,
      InOderSubmit: orderForm.purchaseOrderNo,
      BarCodeSubmit: barcodes
    });
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/extend/printView.vue
@@ -1,239 +1,246 @@
<template>
  <div>
    <vol-box v-model="showDetialBox" :lazy="true" width="500px" :padding="15" title="">
      <!-- æ‰“印专用容器 -->
      <div id="printContainer" style="position: absolute; left: -9999px;">
        <div class="print-page" v-for="(item, index) in printData" :key="index">
          <div class="material-card">
            <!-- <div class="header">卓力能物料标识卡(小包)</div> -->
            <div class="dual-column preview-card-header-qrcode">
              <div class="preview-card-header">物料卡 {{ idx + 1 }}</div>
              <VueQrcode :value="generateQr(item)" :size="20" id="qrcode" />
    <div>
        <vol-box v-model="showDetialBox" :lazy="true" width="500px" :padding="15" title="">
            <!-- æ‰“印专用容器 -->
            <div id="printContainer" style="position: absolute; left: -9999px">
                <div class="print-page" v-for="(item, index) in printData" :key="index">
                    <div class="material-card">
                        <div class="dual-column header-section">
                            <div class="header">卓力能物料标识卡(小包)</div>
                            <VueQrcode :value="generateQr(item)" :size="60" class="qrcode" />
                        </div>
                        <div class="content">
                            <div class="row dual-column">
                                <div class="column">
                                    <span class="label">料号</span>
                                    <span class="value code-value full-width">{{ item.materialCode }}</span>
                                </div>
                                <div class="column">
                                    <span class="label">供应商编码</span>
                                    <span class="value full-width">{{ item.supplierCode }}</span>
                                </div>
                            </div>
                            <div class="row dual-column">
                                <div class="column">
                                    <span class="label">品名</span>
                                    <span class="value full-width">{{ item.materialName }}</span>
                                </div>
                                <div class="column">
                                    <span class="label">采购单号</span>
                                    <span class="value full-width">{{ item.purchaseOrderNo }}</span>
                                </div>
                            </div>
                            <div class="row dual-column">
                                <div class="column">
                                    <span class="label">规格</span>
                                    <span class="value full-width">{{ item.specification }}</span>
                                </div>
                                <div class="column">
                                    <span class="label">数量/总数</span>
                                    <span class="value full-width">{{ item.quantityTotal }}</span>
                                </div>
                            </div>
                            <div class="row dual-column">
                                <div class="column">
                                    <span class="label">批号</span>
                                    <span class="value full-width">{{ item.batchNumber }}</span>
                                </div>
                                <div class="column">
                                    <span class="label">批次</span>
                                    <span class="value full-width">{{ item.batch }}</span>
                                </div>
                            </div>
                            <div class="row dual-column">
                                <div class="column">
                                    <span class="label">厂区</span>
                                    <span class="value full-width">{{ item.factory }}</span>
                                </div>
                                <div class="column">
                                    <span class="label">日期</span>
                                    <span class="value full-width">{{ item.date }}</span>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="content">
              <!-- äºŒç»´ç æ”¾åœ¨å³ä¸Šè§’ -->
              <!-- <div class="qr-section"> -->
              <!-- <VueQrcode :value="generateQr(item)" id="qrcode" /> -->
              <!-- </div> -->
              <div class="row dual-column">
                <div class="column">
                  <span class="label">料号</span>
                  <span class="value code-value">{{ item.materialCode }}</span>
                </div>
                <div class="column">
                  <span class="label">供应商编码</span>
                  <span class="value">{{ item.supplierCode }}</span>
                </div>
              </div>
              <div class="row dual-column">
                <div class="column">
                  <span class="label">品名</span>
                  <span class="value">{{ item.materialName }}</span>
                </div>
                <div class="column">
                  <span class="label">采购单号</span>
                  <span class="value">{{ item.purchaseOrderNo }}</span>
                </div>
              </div>
            <!-- é¢„览区域保持不变 -->
            <div id="previewContent" class="preview-container">
                <div class="preview-header">卓力能物料标识卡(小包) - é¢„览 (共{{ printData.length }}个)</div>
                <div class="preview-scroll">
                    <div v-for="(item, idx) in printData" :key="idx" class="preview-card">
                        <div class="dual-column preview-card-header-qrcode">
                            <div class="preview-card-header">卓力能物料标识卡(小包)</div>
                            <VueQrcode :value="generateQr(item)" :size="40" class="qrcode-preview" />
                        </div>
                        <div class="preview-wrapper">
                            <div class="content-preview">
                                <div class="row-preview dual-column">
                                    <div class="column-preview">
                                        <span class="label-preview">料号</span>
                                        <span class="value-preview code-value full-width">{{ item.materialCode }}</span>
                                    </div>
                                    <div class="column-preview">
                                        <span class="label-preview">供应商编码</span>
                                        <span class="value-preview full-width">{{ item.supplierCode }}</span>
                                    </div>
                                </div>
              <div class="row dual-column">
                <div class="column">
                  <span class="label">规格</span>
                  <span class="value full-width">{{ item.specification }}</span>
                </div>
                <div class="column">
                  <span class="label">数量/总数</span>
                  <span class="value">{{ item.quantityTotal }}</span>
                </div>
              </div>
                                <div class="row-preview dual-column">
                                    <div class="column-preview">
                                        <span class="label-preview">品名</span>
                                        <span class="value-preview full-width">{{ item.materialName }}</span>
                                    </div>
                                    <div class="column-preview">
                                        <span class="label-preview">采购单号</span>
                                        <span class="value-preview full-width">{{ item.purchaseOrderNo }}</span>
                                    </div>
                                </div>
              <div class="row dual-column">
                <div class="column">
                  <span class="label">批号</span>
                  <span class="value">{{ item.batchNumber }}</span>
                </div>
                <div class="column">
                  <span class="label">批次</span>
                  <span class="value">{{ item.batch }}</span>
                </div>
              </div>
                                <div class="row-preview dual-column">
                                    <div class="column-preview">
                                        <span class="label-preview">规格</span>
                                        <span class="value-preview full-width">{{ item.specification }}</span>
                                    </div>
                                    <div class="column-preview">
                                        <span class="label-preview">数量/总数</span>
                                        <span class="value-preview full-width">{{ item.quantityTotal }}</span>
                                    </div>
                                </div>
              <div class="row dual-column">
                <div class="column">
                  <span class="label">厂区</span>
                  <span class="value">{{ item.factory }}</span>
                                <div class="row-preview dual-column">
                                    <div class="column-preview">
                                        <span class="label-preview">批号</span>
                                        <span class="value-preview full-width">{{ item.batchNumber }}</span>
                                    </div>
                                    <div class="column-preview">
                                        <span class="label-preview">批次</span>
                                        <span class="value-preview full-width">{{ item.batch }}</span>
                                    </div>
                                </div>
                                <div class="row-preview dual-column">
                                    <div class="column-preview">
                                        <span class="label-preview">厂区</span>
                                        <span class="value-preview full-width">{{ item.factory }}</span>
                                    </div>
                                    <div class="column-preview">
                                        <span class="label-preview">日期</span>
                                        <span class="value-preview full-width">{{ item.date }}</span>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="column">
                  <span class="label">日期</span>
                  <span class="value">{{ item.date }}</span>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <!-- é¢„览区域 -->
      <div id="previewContent" class="preview-container">
        <div class="preview-header">卓力能物料标识卡(小包) - é¢„览 (共{{ printData.length }}个)</div>
        <div class="preview-scroll">
          <div v-for="(item, idx) in printData" :key="idx" class="preview-card">
            <div class="dual-column preview-card-header-qrcode">
              <div class="preview-card-header">物料卡 {{ idx + 1 }}</div>
              <VueQrcode :value="generateQr(item)" :size="20" id="qrcode" />
            </div>
            <div class="preview-wrapper">
              <div class="content-preview">
                <div class="row-preview dual-column">
                  <div class="column-preview">
                    <span class="label-preview">料号</span>
                    <span class="value-preview code-value">{{ item.materialCode }}</span>
                  </div>
                  <div class="column-preview">
                    <span class="label-preview">供应商编码</span>
                    <span class="value-preview">{{ item.supplierCode }}</span>
                  </div>
            <template #footer>
                <div class="footer-actions">
                    <span class="print-count">共 {{ printData.length }} ä¸ªç‰©æ–™å¡å¾…打印</span>
                    <div>
                        <el-button type="primary" size="small" @click="print">打印全部</el-button>
                        <el-button type="danger" size="small" @click="showDetialBox = false">关闭</el-button>
                    </div>
                </div>
                <div class="row-preview dual-column">
                  <div class="column-preview">
                    <span class="label-preview">品名</span>
                    <span class="value-preview">{{ item.materialName }}</span>
                  </div>
                  <div class="column-preview">
                    <span class="label-preview">采购单号</span>
                    <span class="value-preview">{{ item.purchaseOrderNo }}</span>
                  </div>
                </div>
                <div class="row-preview dual-column">
                  <div class="column-preview">
                    <span class="label-preview">规格</span>
                    <span class="value-preview full-width">{{ item.specification }}</span>
                  </div>
                  <div class="column-preview">
                    <span class="label-preview">数量/总数</span>
                    <span class="value-preview">{{ item.quantityTotal }}</span>
                  </div>
                </div>
                <div class="row-preview dual-column">
                  <div class="column-preview">
                    <span class="label-preview">批号</span>
                    <span class="value-preview">{{ item.batchNumber }}</span>
                  </div>
                  <div class="column-preview">
                    <span class="label-preview">批次</span>
                    <span class="value-preview">{{ item.batch }}</span>
                  </div>
                </div>
                <div class="row-preview dual-column">
                  <div class="column-preview">
                    <span class="label-preview">厂区</span>
                    <span class="value-preview">{{ item.factory }}</span>
                  </div>
                  <div class="column-preview">
                    <span class="label-preview">日期</span>
                    <span class="value-preview">{{ item.date }}</span>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <template #footer>
        <div class="footer-actions">
          <span class="print-count">共 {{ printData.length }} ä¸ªç‰©æ–™å¡å¾…打印</span>
          <div>
            <el-button type="primary" size="small" @click="print">打印全部</el-button>
            <el-button type="danger" size="small" @click="showDetialBox = false">关闭</el-button>
          </div>
        </div>
      </template>
    </vol-box>
  </div>
            </template>
        </vol-box>
    </div>
</template>
<script>
import VolBox from "@/components/basic/VolBox.vue";
import VueQrcode from "vue-qrcode";
import VolBox from '@/components/basic/VolBox.vue'
import VueQrcode from 'vue-qrcode'
export default {
  components: { VolBox, VueQrcode },
  data() {
    return {
      showDetialBox: false,
      printData: [], // å­˜å‚¨æ‰“印数据集合
    };
  },
  methods: {
    generateQr(item) {
      return `${item.batchNumber}`;
    components: { VolBox, VueQrcode },
    data() {
        return {
            showDetialBox: false,
            printData: []
        }
    },
    methods: {
        generateQr(item) {
            return `${item.batchNumber}`
        },
    open(rows) {
      this.showDetialBox = true;
      this.printData = Array.isArray(rows) ? rows : [rows];
      console.log('打印数据:', this.printData);
    },
        open(rows) {
            this.showDetialBox = true
            this.printData = Array.isArray(rows) ? rows : [rows]
            console.log('打印数据:', this.printData);
        },
    async print() {
      if (this.printData.length === 0) {
        this.$message.warning('没有可打印的数据');
        return;
      }
        async print() {
            if (this.printData.length === 0) {
                this.$message.warning('没有可打印的数据')
                return
            }
      this.$nextTick(() => {
        try {
          const printContent = document.getElementById("printContainer").innerHTML;
          const printWindow = window.open("", "_blank", "width=800,height=600,scrollbars=yes");
            this.$nextTick(() => {
                try {
                    const printContent = document.getElementById('printContainer').innerHTML
                    const printWindow = window.open('', '_blank', 'width=800,height=600,scrollbars=yes')
          if (!printWindow) {
            this.$message.error('无法打开打印窗口,请检查浏览器弹窗设置');
            return;
          }
                    if (!printWindow) {
                        this.$message.error('无法打开打印窗口,请检查浏览器弹窗设置')
                        return
                    }
          printWindow.document.write(`
                    printWindow.document.write(`
            <!DOCTYPE html>
            <html>
            <head>
              <meta charset="UTF-8">
              <title>物料标识卡打印 - å…±${this.printData.length}个</title>
              <style>
                /* é‡ç½®æ‰€æœ‰æ ·å¼ */
                * {
                  margin: 0;
                  padding: 0;
                  box-sizing: border-box;
                }
                body {
                  font-family: SimSun, "宋体", sans-serif;
                  -webkit-print-color-adjust: exact !important;
                  print-color-adjust: exact !important;
                  color-adjust: exact !important;
                }
                #qrcode {
                  width: 60px;
                  height: 60px;
                  display: block;
                  margin: 0, 15px
                body {
                  font-family: "Microsoft YaHei", "SimHei", Arial, sans-serif;
                  background: white !important;
                  margin: 0 !important;
                  padding: 0 !important;
                  width: 80mm !important;
                }
                .qrcode {
                  width: 15mm !important;
                  height: 15mm !important;
                  display: block;
                }
                .qrcode canvas {
                  width: 15mm !important;
                  height: 15mm !important;
                  image-rendering: -webkit-optimize-contrast;
                  image-rendering: crisp-edges;
                }
                @media print {
                  @page {
                    size: 80mm 60mm;
                    margin: 0;
                    padding: 0;
                    size: 80mm 60mm !important;
                    margin: 0 !important;
                    padding: 0 !important;
                  }
                  
                  body {
                    width: 80mm !important;
                    height: 60mm !important;
                    margin: 0 !important;
                    padding: 0 !important;
                    width: 80mm !important;
                    background: white !important;
                  }
                  
@@ -241,44 +248,52 @@
                    width: 80mm !important;
                    height: 60mm !important;
                    page-break-after: always !important;
                    break-after: page !important;
                    margin: 0 !important;
                    padding: 2mm !important;
                    display: block !important;
                  }
                  .print-page:last-child {
                    page-break-after: auto !important;
                  }
                  .material-card {
                    width: 80mm !important;
                    height: 56mm !important;
                    border: 0.3mm solid #000 !important;
                    padding: 1mm !important;
                    box-sizing: border-box !important;
                    position: relative !important;
                    display: block !important;
                    background: white !important;
                  }
                  
                  .print-page:last-child {
                    page-break-after: avoid !important;
                  }
                  .material-card {
                    width: 78mm !important;
                    height: 58mm !important;
                    border: 0.5mm solid #000 !important;
                    padding: 1.5mm !important;
                    box-sizing: border-box !important;
                    position: relative !important;
                    background: white !important;
                    font-family: "Microsoft YaHei", "SimHei", Arial, sans-serif !important;
                  }
                  .header-section {
                    display: flex !important;
                    justify-content: space-between !important;
                    align-items: center !important;
                    margin-bottom: 1.5mm !important;
                    padding-bottom: 1mm !important;
                    border-bottom: 0.3mm solid #000 !important;
                  }
                  .header {
                    font-size: 3.5mm !important;
                    font-size: 4mm !important;
                    font-weight: bold !important;
                    text-align: center !important;
                    margin-bottom: 1mm !important;
                    padding-bottom: 0.5mm !important;
                    border-bottom: 0.2mm solid #000 !important;
                    white-space: nowrap !important;
                  }
                  
                  .content {
                    height: calc(100% - 6mm) !important;
                    height: calc(100% - 10mm) !important;
                    position: relative !important;
                  }
                  
                  .row {
                    display: flex !important;
                    margin-bottom: 0.8mm !important;
                    min-height: 6mm !important;
                    margin-bottom: 1.2mm !important;
                    min-height: 6.5mm !important;
                    align-items: stretch !important;
                  }
                  
                  .dual-column {
@@ -290,101 +305,44 @@
                    flex: 1 !important;
                    display: flex !important;
                    align-items: center !important;
                    min-height: 5mm !important;
                  }
                  
                  .column:first-child {
                    margin-right: 2mm !important;
                    margin-right: 2.5mm !important;
                  }
                  
                  .label {
                    font-weight: bold !important;
                    flex: 0 0 15mm !important;
                    flex: 0 0 8mm !important;
                    white-space: nowrap !important;
                    font-size: 2.2mm !important;
                    font-size: 2.5mm !important;
                    line-height: 1.3 !important;
                  }
                  
                  .value {
                    flex: 1 !important;
                    overflow: hidden !important;
                    text-overflow: ellipsis !important;
                    white-space: nowrap !important;
                    border-bottom: 0.1mm dashed #666 !important;
                    padding-bottom: 0.2mm !important;
                    min-height: 2.5mm !important;
                    font-size: 2.2mm !important;
                    white-space: normal !important;
                    word-break: break-all !important;
                    word-wrap: break-word !important;
                    min-height: 4mm !important;
                    font-size: 2.5mm !important;
                    line-height: 1.3 !important;
                    border-bottom: none !important;
                    padding: 0.5mm !important;
                  }
                  .full-width {
                    display: block !important;
                    white-space: normal !important;
                    word-break: break-all !important;
                    word-wrap: break-word !important;
                    line-height: 1.3 !important;
                  }
                  
                  .code-value {
                    font-weight: bold !important;
                  }
                  .full-width {
                    white-space: normal !important;
                    word-break: break-all !important;
                    line-height: 1.2 !important;
                  }
                  /* äºŒç»´ç æ ·å¼ */
                  .qr-section {
                    width: 10mm !important;
                    height: 12mm !important;
                    font-siz:10px;
                  }
                  .qr-section canvas {
                    width: 10mm !important;
                    height: 10mm !important;
                    max-width: 100% !important;
                    max-height: 100% !important;
                  }
                }
                /* å±å¹•预览样式 */
                @media screen {
                  body {
                    background: #f5f5f5;
                    padding: 10px;
                    display: flex;
                    flex-direction: column;
                    align-items: center;
                  }
                  .print-page {
                    width: 80mm;
                    height: 60mm;
                    margin: 10px 0;
                    padding: 2mm;
                    background: white;
                    box-shadow: 0 2px 10px rgba(0,0,0,0.1);
                    border: 1px dashed #ccc;
                  }
                  .material-card {
                    width: 80mm;
                    height: 60mm;
                    border: 0.3mm solid #000;
                    padding: 1.5mm;
                    background: white;
                    position: relative;
                  }
                  .qr-section {
                    position: absolute;
                    top: 4mm;
                    right: 2mm;
                    width: 12mm;
                    height: 12mm;
                    border: 0.2mm solid #ccc;
                    padding: 0.5mm;
                    background: white;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                  }
                  .qr-section canvas {
                    width: 10mm !important;
                    height: 10mm !important;
                  }
                }
              </style>
@@ -393,196 +351,181 @@
              ${printContent}
              <script>
                window.onload = function() {
                  console.log('打印页面加载完成,共${this.printData.length}个物料卡');
                  // ç­‰å¾…二维码渲染完成
                  setTimeout(() => {
                    // å¼ºåˆ¶è®¾ç½®äºŒç»´ç canvas尺寸
                    const canvases = document.querySelectorAll('canvas');
                    canvases.forEach(canvas => {
                      canvas.style.width = '10mm !important';
                      canvas.style.height = '10mm !important';
                      canvas.setAttribute('width', '60');
                      canvas.setAttribute('height', '60');
                      canvas.style.width = '15mm';
                      canvas.style.height = '15mm';
                    });
                    window.print();
                    setTimeout(() => { window.close(); }, 500);
                  }, 800);
                };
                window.onafterprint = function() {
                  setTimeout(() => {
                    window.close();
                  }, 500);
                };
              <\/script>
            </body>
            </html>
          `);
          `)
          printWindow.document.close();
        } catch (error) {
          console.error('打印错误:', error);
          this.$message.error('打印失败: ' + error.message);
                    printWindow.document.close()
                } catch (error) {
                    console.error('打印错误:', error)
                    this.$message.error('打印失败: ' + error.message)
                }
            })
        }
      });
    }
  }
};
}
</script>
<style scoped>
#qrcode {
  width: 60px;
  height: 60px;
  display: block;
  margin: 0, 15px
.qrcode {
    width: 60px;
    height: 60px;
    display: block;
}
.qrcode-preview {
    width: 40px;
    height: 40px;
    display: block;
}
.preview-container {
  max-height: 500px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
    max-height: 500px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}
.preview-header {
  text-align: center;
  font-size: 16px;
  font-weight: bold;
  margin-bottom: 10px;
  padding: 8px;
  background: #f0f0f0;
  border-radius: 4px;
    text-align: center;
    font-size: 16px;
    font-weight: bold;
    margin-bottom: 10px;
    padding: 8px;
    background: #f0f0f0;
    border-radius: 4px;
}
.preview-scroll {
  max-height: 400px;
  overflow-y: auto;
  padding: 10px;
  border: 1px solid #e0e0e0;
  border-radius: 4px;
  background: #f9f9f9;
    max-height: 400px;
    overflow-y: auto;
    padding: 10px;
    border: 1px solid #e0e0e0;
    border-radius: 4px;
    background: #f9f9f9;
}
.preview-card {
  width: 320px;
  height: 240px;
  margin: 0 auto 15px auto;
  padding: 8px;
  border: 1px solid #ccc;
  font-family: SimSun;
  box-sizing: border-box;
  border: 2px solid #000;
  background: white;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
  position: relative;
    width: 320px;
    height: 240px;
    margin: 0 auto 15px auto;
    padding: 8px;
    border: 2px solid #000;
    font-family: 'Microsoft YaHei', sans-serif;
    box-sizing: border-box;
    background: white;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    position: relative;
}
.preview-card:last-child {
  margin-bottom: 0;
    margin-bottom: 0;
}
.preview-card-header {
  text-align: center;
  font-size: 14px;
  /* font-weight: bold;
  margin-bottom: 8px;
  padding-bottom: 4px;
  border-bottom: 1px solid #000;
  background: #f5f5f5;
  padding: 4px; */
  height: 60px;
  width: 60px;
    font-size: 14px;
    font-weight: bold;
    white-space: nowrap;
}
.preview-wrapper {
  position: relative;
  height: calc(100% - 40px);
    position: relative;
    height: calc(100% - 30px);
}
.content-preview {
  width: 100%;
  height: 100%;
    width: 100%;
    height: 100%;
}
.row-preview {
  /* margin-bottom: px; */
  min-height: 32px;
    margin-bottom: 6px;
    min-height: 24px;
    display: flex;
    align-items: center;
}
.dual-column {
  display: flex;
  justify-content: space-between;
    display: flex;
    justify-content: space-between;
}
.column-preview {
  flex: 1;
  display: flex;
  align-items: center;
    flex: 1;
    display: flex;
    align-items: center;
    min-height: 22px;
}
.column-preview:first-child {
  margin-right: 10px;
    margin-right: 8px;
}
.label-preview {
  font-weight: bold;
  flex: 0 0 60px;
  white-space: nowrap;
  font-size: 12px;
    font-weight: bold;
    flex: 0 0 30px;
    white-space: nowrap;
    font-size: 12px;
    line-height: 1.2;
}
.value-preview {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12px;
  border-bottom: 1px dashed #666;
  padding-bottom: 2px;
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 10px;
    padding-bottom: 2px;
    line-height: 1.2;
    font-weight: normal;
}
.code-value {
  font-weight: bold;
    font-weight: bold;
}
.full-width {
  display: block;
  margin-top: 2px;
  white-space: normal;
  word-break: break-all;
  line-height: 1.2;
  font-size: 12px;
    display: block;
    white-space: normal;
    word-break: break-all !important;
    line-height: 1.2;
    font-size: 11px;
    min-height: auto;
    word-wrap: break-word !important;
}
/* é¢„览区域二维码样式 */
.qr-section-preview {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 150px;
  height: 175px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid #ddd;
  background: white;
  padding: 2px;
}
.qr-section-preview canvas {
  width: 56px !important;
  height: 56px !important;
.preview-card-header-qrcode {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    margin-bottom: 8px;
    border-bottom: 2px solid #000;
    padding-bottom: 4px;
}
.footer-actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
}
.print-count {
  font-size: 14px;
  color: #409EFF;
  font-weight: bold;
    font-size: 14px;
    color: #409eff;
    font-weight: bold;
}
</style>
</style>
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/extend/printView1.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,588 @@
<template>
  <div>
    <vol-box v-model="showDetialBox" :lazy="true" width="500px" :padding="15" title="">
      <!-- æ‰“印专用容器 -->
      <div id="printContainer" style="position: absolute; left: -9999px;">
        <div class="print-page" v-for="(item, index) in printData" :key="index">
          <div class="material-card">
            <!-- <div class="header">卓力能物料标识卡(小包)</div> -->
            <div class="dual-column preview-card-header-qrcode">
              <div class="preview-card-header">物料卡 {{ idx + 1 }}</div>
              <VueQrcode :value="generateQr(item)" :size="20" id="qrcode" />
            </div>
            <div class="content">
              <!-- äºŒç»´ç æ”¾åœ¨å³ä¸Šè§’ -->
              <!-- <div class="qr-section"> -->
              <!-- <VueQrcode :value="generateQr(item)" id="qrcode" /> -->
              <!-- </div> -->
              <div class="row dual-column">
                <div class="column">
                  <span class="label">料号</span>
                  <span class="value code-value">{{ item.materialCode }}</span>
                </div>
                <div class="column">
                  <span class="label">供应商编码</span>
                  <span class="value">{{ item.supplierCode }}</span>
                </div>
              </div>
              <div class="row dual-column">
                <div class="column">
                  <span class="label">品名</span>
                  <span class="value">{{ item.materialName }}</span>
                </div>
                <div class="column">
                  <span class="label">采购单号</span>
                  <span class="value">{{ item.purchaseOrderNo }}</span>
                </div>
              </div>
              <div class="row dual-column">
                <div class="column">
                  <span class="label">规格</span>
                  <span class="value full-width">{{ item.specification }}</span>
                </div>
                <div class="column">
                  <span class="label">数量/总数</span>
                  <span class="value">{{ item.quantityTotal }}</span>
                </div>
              </div>
              <div class="row dual-column">
                <div class="column">
                  <span class="label">批号</span>
                  <span class="value">{{ item.batchNumber }}</span>
                </div>
                <div class="column">
                  <span class="label">批次</span>
                  <span class="value">{{ item.batch }}</span>
                </div>
              </div>
              <div class="row dual-column">
                <div class="column">
                  <span class="label">厂区</span>
                  <span class="value">{{ item.factory }}</span>
                </div>
                <div class="column">
                  <span class="label">日期</span>
                  <span class="value">{{ item.date }}</span>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <!-- é¢„览区域 -->
      <div id="previewContent" class="preview-container">
        <div class="preview-header">卓力能物料标识卡(小包) - é¢„览 (共{{ printData.length }}个)</div>
        <div class="preview-scroll">
          <div v-for="(item, idx) in printData" :key="idx" class="preview-card">
            <div class="dual-column preview-card-header-qrcode">
              <div class="preview-card-header">物料卡 {{ idx + 1 }}</div>
              <VueQrcode :value="generateQr(item)" :size="20" id="qrcode" />
            </div>
            <div class="preview-wrapper">
              <div class="content-preview">
                <div class="row-preview dual-column">
                  <div class="column-preview">
                    <span class="label-preview">料号</span>
                    <span class="value-preview code-value">{{ item.materialCode }}</span>
                  </div>
                  <div class="column-preview">
                    <span class="label-preview">供应商编码</span>
                    <span class="value-preview">{{ item.supplierCode }}</span>
                  </div>
                </div>
                <div class="row-preview dual-column">
                  <div class="column-preview">
                    <span class="label-preview">品名</span>
                    <span class="value-preview">{{ item.materialName }}</span>
                  </div>
                  <div class="column-preview">
                    <span class="label-preview">采购单号</span>
                    <span class="value-preview">{{ item.purchaseOrderNo }}</span>
                  </div>
                </div>
                <div class="row-preview dual-column">
                  <div class="column-preview">
                    <span class="label-preview">规格</span>
                    <span class="value-preview full-width">{{ item.specification }}</span>
                  </div>
                  <div class="column-preview">
                    <span class="label-preview">数量/总数</span>
                    <span class="value-preview">{{ item.quantityTotal }}</span>
                  </div>
                </div>
                <div class="row-preview dual-column">
                  <div class="column-preview">
                    <span class="label-preview">批号</span>
                    <span class="value-preview">{{ item.batchNumber }}</span>
                  </div>
                  <div class="column-preview">
                    <span class="label-preview">批次</span>
                    <span class="value-preview">{{ item.batch }}</span>
                  </div>
                </div>
                <div class="row-preview dual-column">
                  <div class="column-preview">
                    <span class="label-preview">厂区</span>
                    <span class="value-preview">{{ item.factory }}</span>
                  </div>
                  <div class="column-preview">
                    <span class="label-preview">日期</span>
                    <span class="value-preview">{{ item.date }}</span>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <template #footer>
        <div class="footer-actions">
          <span class="print-count">共 {{ printData.length }} ä¸ªç‰©æ–™å¡å¾…打印</span>
          <div>
            <el-button type="primary" size="small" @click="print">打印全部</el-button>
            <el-button type="danger" size="small" @click="showDetialBox = false">关闭</el-button>
          </div>
        </div>
      </template>
    </vol-box>
  </div>
</template>
<script>
import VolBox from "@/components/basic/VolBox.vue";
import VueQrcode from "vue-qrcode";
export default {
  components: { VolBox, VueQrcode },
  data() {
    return {
      showDetialBox: false,
      printData: [], // å­˜å‚¨æ‰“印数据集合
    };
  },
  methods: {
    generateQr(item) {
      return `${item.batchNumber}`;
    },
    open(rows) {
      this.showDetialBox = true;
      this.printData = Array.isArray(rows) ? rows : [rows];
      console.log('打印数据:', this.printData);
    },
    async print() {
      if (this.printData.length === 0) {
        this.$message.warning('没有可打印的数据');
        return;
      }
      this.$nextTick(() => {
        try {
          const printContent = document.getElementById("printContainer").innerHTML;
          const printWindow = window.open("", "_blank", "width=800,height=600,scrollbars=yes");
          if (!printWindow) {
            this.$message.error('无法打开打印窗口,请检查浏览器弹窗设置');
            return;
          }
          printWindow.document.write(`
            <!DOCTYPE html>
            <html>
            <head>
              <meta charset="UTF-8">
              <title>物料标识卡打印 - å…±${this.printData.length}个</title>
              <style>
                /* é‡ç½®æ‰€æœ‰æ ·å¼ */
                * {
                  margin: 0;
                  padding: 0;
                  box-sizing: border-box;
                }
                body {
                  font-family: SimSun, "宋体", sans-serif;
                  -webkit-print-color-adjust: exact !important;
                  print-color-adjust: exact !important;
                  color-adjust: exact !important;
                }
                #qrcode {
                  width: 60px;
                  height: 60px;
                  display: block;
                  margin: 0, 15px
                }
                @media print {
                  @page {
                    size: 80mm 60mm;
                    margin: 0;
                    padding: 0;
                  }
                  body {
                    margin: 0 !important;
                    padding: 0 !important;
                    width: 80mm !important;
                    background: white !important;
                  }
                  .print-page {
                    width: 80mm !important;
                    height: 60mm !important;
                    page-break-after: always !important;
                    break-after: page !important;
                    margin: 0 !important;
                    padding: 2mm !important;
                    display: block !important;
                  }
                  .print-page:last-child {
                    page-break-after: auto !important;
                  }
                  .material-card {
                    width: 80mm !important;
                    height: 56mm !important;
                    border: 0.3mm solid #000 !important;
                    padding: 1mm !important;
                    box-sizing: border-box !important;
                    position: relative !important;
                    background: white !important;
                  }
                  .header {
                    font-size: 3.5mm !important;
                    font-weight: bold !important;
                    text-align: center !important;
                    margin-bottom: 1mm !important;
                    padding-bottom: 0.5mm !important;
                    border-bottom: 0.2mm solid #000 !important;
                  }
                  .content {
                    height: calc(100% - 6mm) !important;
                    position: relative !important;
                  }
                  .row {
                    display: flex !important;
                    margin-bottom: 0.8mm !important;
                    min-height: 6mm !important;
                  }
                  .dual-column {
                    display: flex !important;
                    justify-content: space-between !important;
                  }
                  .column {
                    flex: 1 !important;
                    display: flex !important;
                    align-items: center !important;
                  }
                  .column:first-child {
                    margin-right: 2mm !important;
                  }
                  .label {
                    font-weight: bold !important;
                    flex: 0 0 15mm !important;
                    white-space: nowrap !important;
                    font-size: 2.2mm !important;
                  }
                  .value {
                    flex: 1 !important;
                    overflow: hidden !important;
                    text-overflow: ellipsis !important;
                    white-space: nowrap !important;
                    border-bottom: 0.1mm dashed #666 !important;
                    padding-bottom: 0.2mm !important;
                    min-height: 2.5mm !important;
                    font-size: 2.2mm !important;
                  }
                  .code-value {
                    font-weight: bold !important;
                  }
                  .full-width {
                    white-space: normal !important;
                    word-break: break-all !important;
                    line-height: 1.2 !important;
                  }
                  /* äºŒç»´ç æ ·å¼ */
                  .qr-section {
                    width: 10mm !important;
                    height: 12mm !important;
                    font-siz:10px;
                  }
                  .qr-section canvas {
                    width: 10mm !important;
                    height: 10mm !important;
                    max-width: 100% !important;
                    max-height: 100% !important;
                  }
                }
                /* å±å¹•预览样式 */
                @media screen {
                  body {
                    background: #f5f5f5;
                    padding: 10px;
                    display: flex;
                    flex-direction: column;
                    align-items: center;
                  }
                  .print-page {
                    width: 80mm;
                    height: 60mm;
                    margin: 10px 0;
                    padding: 2mm;
                    background: white;
                    box-shadow: 0 2px 10px rgba(0,0,0,0.1);
                    border: 1px dashed #ccc;
                  }
                  .material-card {
                    width: 80mm;
                    height: 60mm;
                    border: 0.3mm solid #000;
                    padding: 1.5mm;
                    background: white;
                    position: relative;
                  }
                  .qr-section {
                    position: absolute;
                    top: 4mm;
                    right: 2mm;
                    width: 12mm;
                    height: 12mm;
                    border: 0.2mm solid #ccc;
                    padding: 0.5mm;
                    background: white;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                  }
                  .qr-section canvas {
                    width: 10mm !important;
                    height: 10mm !important;
                  }
                }
              </style>
            </head>
            <body>
              ${printContent}
              <script>
                window.onload = function() {
                  console.log('打印页面加载完成,共${this.printData.length}个物料卡');
                  // ç­‰å¾…二维码渲染完成
                  setTimeout(() => {
                    // å¼ºåˆ¶è®¾ç½®äºŒç»´ç canvas尺寸
                    const canvases = document.querySelectorAll('canvas');
                    canvases.forEach(canvas => {
                      canvas.style.width = '10mm !important';
                      canvas.style.height = '10mm !important';
                    });
                    window.print();
                  }, 800);
                };
                window.onafterprint = function() {
                  setTimeout(() => {
                    window.close();
                  }, 500);
                };
              <\/script>
            </body>
            </html>
          `);
          printWindow.document.close();
        } catch (error) {
          console.error('打印错误:', error);
          this.$message.error('打印失败: ' + error.message);
        }
      });
    }
  }
};
</script>
<style scoped>
#qrcode {
  width: 60px;
  height: 60px;
  display: block;
  margin: 0, 15px
}
.preview-container {
  max-height: 500px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.preview-header {
  text-align: center;
  font-size: 16px;
  font-weight: bold;
  margin-bottom: 10px;
  padding: 8px;
  background: #f0f0f0;
  border-radius: 4px;
}
.preview-scroll {
  max-height: 400px;
  overflow-y: auto;
  padding: 10px;
  border: 1px solid #e0e0e0;
  border-radius: 4px;
  background: #f9f9f9;
}
.preview-card {
  width: 320px;
  height: 240px;
  margin: 0 auto 15px auto;
  padding: 8px;
  border: 1px solid #ccc;
  font-family: SimSun;
  box-sizing: border-box;
  border: 2px solid #000;
  background: white;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
  position: relative;
}
.preview-card:last-child {
  margin-bottom: 0;
}
.preview-card-header {
  text-align: center;
  font-size: 14px;
  /* font-weight: bold;
  margin-bottom: 8px;
  padding-bottom: 4px;
  border-bottom: 1px solid #000;
  background: #f5f5f5;
  padding: 4px; */
  height: 60px;
  width: 60px;
}
.preview-wrapper {
  position: relative;
  height: calc(100% - 40px);
}
.content-preview {
  width: 100%;
  height: 100%;
}
.row-preview {
  /* margin-bottom: px; */
  min-height: 32px;
}
.dual-column {
  display: flex;
  justify-content: space-between;
}
.column-preview {
  flex: 1;
  display: flex;
  align-items: center;
}
.column-preview:first-child {
  margin-right: 10px;
}
.label-preview {
  font-weight: bold;
  flex: 0 0 60px;
  white-space: nowrap;
  font-size: 12px;
}
.value-preview {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12px;
  border-bottom: 1px dashed #666;
  padding-bottom: 2px;
}
.code-value {
  font-weight: bold;
}
.full-width {
  display: block;
  margin-top: 2px;
  white-space: normal;
  word-break: break-all;
  line-height: 1.2;
  font-size: 12px;
}
/* é¢„览区域二维码样式 */
.qr-section-preview {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 150px;
  height: 175px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid #ddd;
  background: white;
  padding: 2px;
}
.qr-section-preview canvas {
  width: 56px !important;
  height: 56px !important;
}
.footer-actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
}
.print-count {
  font-size: 14px;
  color: #409EFF;
  font-weight: bold;
}
</style>
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/CopilotIndices/17.14.878.3237/CodeChunks.db-shm
Binary files differ
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/CopilotIndices/17.14.878.3237/SemanticSymbols.db-shm
Binary files differ
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_BasicService/InvokeMESService.cs
@@ -14,6 +14,7 @@
using WIDESEA_Common.StockEnum;
using WIDESEA_Core;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Core.Helper;
using WIDESEA_DTO.Allocate;
using WIDESEA_DTO.Basic;
using WIDESEA_DTO.Inbound;
@@ -115,9 +116,11 @@
        public async Task<ResponseModel> FeedbackAllocate(AllocateDto model)
        {
            _logger.LogInformation($"InvokeMESService  FeedbackAllocate  åºåˆ—化前: {JsonConvert.SerializeObject(model)}");
            string json = JsonConvert.SerializeObject(model, new JsonSerializerSettings
            {
                ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
                ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver(),
                NullValueHandling = NullValueHandling.Include
            });
            var content = new StringContent(json, Encoding.UTF8, "application/json");
            var _client = _httpClientFactory.CreateClient("MESUrl");
@@ -246,42 +249,89 @@
                                var inboundOrder = _inboundOrderRepository.Db.Queryable<Dt_InboundOrder>().First(x => x.InboundOrderNo == lists.FirstOrDefault().OrderNo);
                                if (inboundOrder != null)
                                {
                                    var feedmodel = new FeedbackInboundRequestModel
                                    if (inboundOrder.OrderType == (int)InOrderTypeEnum.AllocatInbound)//调拨入库
                                    {
                                        reqCode = Guid.NewGuid().ToString(),
                                        reqTime = DateTime.Now.ToString(),
                                        business_type = inboundOrder.BusinessType,
                                        factoryArea = inboundOrder.FactoryArea,
                                        operationType = 1,
                                        Operator = inboundOrder.Operator,
                                        orderNo = inboundOrder.UpperOrderNo,
                                        status = inboundOrder.OrderStatus,
                                        details = new List<FeedbackInboundDetailsModel>()
                                        var allocate = SqlSugarHelper.DbWMS.Queryable<Dt_AllocateOrder>().Where(x => x.OrderNo == inboundOrder.InboundOrderNo).First();
                                        var allocatefeedmodel = new AllocateDto
                                        {
                                            ReqCode = Guid.NewGuid().ToString(),
                                            ReqTime = DateTime.Now.ToString(),
                                            BusinessType = "2",
                                            FactoryArea = inboundOrder.FactoryArea,
                                            OperationType = 1,
                                            Operator = inboundOrder.Operator,
                                            OrderNo = inboundOrder.UpperOrderNo,
                                            fromWarehouse = allocate?.FromWarehouse ?? "",
                                            toWarehouse = allocate?.ToWarehouse ?? "",
                                            Details = new List<AllocateDtoDetail>()
                                    };
                                        };
                                    var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.SupplyCode, item.BatchNo, item.InboundOrderRowNo, item.BarcodeUnit, item.WarehouseCode })
                                       .Select(group => new FeedbackInboundDetailsModel
                                       {
                                           materialCode = group.Key.MaterielCode,
                                           supplyCode = group.Key.SupplyCode,
                                           batchNo = group.Key.BatchNo,
                                           lineNo = group.Key.InboundOrderRowNo,
                                           qty = group.Sum(x => x.BarcodeQty),
                                           // warehouseCode = group.Key.WarehouseCode=="0"?"1072": group.Key.WarehouseCode,
                                           warehouseCode = group.Key.WarehouseCode,
                                           unit = group.Key.BarcodeUnit,
                                           barcodes = group.Select(row => new FeedbackBarcodesModel
                                        var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.SupplyCode, item.BatchNo, item.InboundOrderRowNo, item.BarcodeUnit, item.WarehouseCode })
                                           .Select(group => new AllocateDtoDetail
                                           {
                                               barcode = row.Barcode,
                                               qty = row.BarcodeQty
                                           }).ToList()
                                       }).ToList();
                                    feedmodel.details = groupedData;
                                    var result = await FeedbackInbound(feedmodel);
                                    if (result != null && result.code == 200)
                                               MaterialCode = group.Key.MaterielCode,
                                               LineNo = group.Key.InboundOrderRowNo,
                                               WarehouseCode = group.Key.WarehouseCode,
                                               Qty = group.Sum(x => x.BarcodeQty),
                                               // warehouseCode= "1072",
                                               Unit = group.Key.BarcodeUnit,
                                               Barcodes = group.Select(row => new BarcodeInfo
                                               {
                                                   Barcode = row.Barcode,
                                                   Qty = row.BarcodeQty,
                                                   BatchNo = row.BatchNo,
                                                   SupplyCode = row.SupplyCode,
                                                   Unit = row.Unit
                                               }).ToList()
                                           }).ToList();
                                        allocatefeedmodel.Details = groupedData;
                                        var result = await FeedbackAllocate(allocatefeedmodel);
                                        if (result != null && result.code == 200)
                                        {
                                            _feedbacktomesRepository.Db.Insertable(new Dt_FeedbackToMes { OrderNo = orderNo, PalletCode = item.PalletCode, ReportStatus = 1 }).ExecuteCommand();
                                        }
                                    }
                                    else
                                    {
                                        _feedbacktomesRepository.Db.Insertable(new Dt_FeedbackToMes { OrderNo = orderNo, PalletCode = item.PalletCode, ReportStatus = 1 }).ExecuteCommand();
                                        var feedmodel = new FeedbackInboundRequestModel
                                        {
                                            reqCode = Guid.NewGuid().ToString(),
                                            reqTime = DateTime.Now.ToString(),
                                            business_type = inboundOrder.BusinessType,
                                            factoryArea = inboundOrder.FactoryArea,
                                            operationType = 1,
                                            Operator = inboundOrder.Operator,
                                            orderNo = inboundOrder.UpperOrderNo,
                                            status = inboundOrder.OrderStatus,
                                            details = new List<FeedbackInboundDetailsModel>()
                                        };
                                        var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.SupplyCode, item.BatchNo, item.InboundOrderRowNo, item.BarcodeUnit, item.WarehouseCode })
                                           .Select(group => new FeedbackInboundDetailsModel
                                           {
                                               materialCode = group.Key.MaterielCode,
                                               supplyCode = group.Key.SupplyCode,
                                               batchNo = group.Key.BatchNo,
                                               lineNo = group.Key.InboundOrderRowNo,
                                               qty = group.Sum(x => x.BarcodeQty),
                                               // warehouseCode = group.Key.WarehouseCode=="0"?"1072": group.Key.WarehouseCode,
                                               warehouseCode = group.Key.WarehouseCode,
                                               unit = group.Key.BarcodeUnit,
                                               barcodes = group.Select(row => new FeedbackBarcodesModel
                                               {
                                                   barcode = row.Barcode,
                                                   qty = row.BarcodeQty
                                               }).ToList()
                                           }).ToList();
                                        feedmodel.details = groupedData;
                                        var result = await FeedbackInbound(feedmodel);
                                        if (result != null && result.code == 200)
                                        {
                                            _feedbacktomesRepository.Db.Insertable(new Dt_FeedbackToMes { OrderNo = orderNo, PalletCode = item.PalletCode, ReportStatus = 1 }).ExecuteCommand();
                                        }
                                    }
                                }
                            }
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_DTO/Allocate/AllocateDto.cs
@@ -39,8 +39,10 @@
        [JsonProperty("business_type")]
        public string BusinessType { get; set; }
        [JsonProperty("fromWarehouse")]
        public string fromWarehouse { get; set; }
        [JsonProperty("toWarehouse")]
        public string toWarehouse { get; set; }
        /// <summary>
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_DTO/Outbound/BatchOutBoundDto.cs
@@ -186,6 +186,8 @@
        public decimal SplitQuantity { get; set; }
        public string Operator { get; set; }
        public bool IsReverted { get; set; }
        public bool IsAutoSplit { get; set; }
    }
    #endregion
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundOrderDetail.cs
@@ -148,7 +148,7 @@
        public decimal AllocatedQuantity { get; set; }
        public string BatchAllocateStatus { get; set; }
        public int BatchAllocateStatus { get; set; }
        /// <summary>
        /// è™šæ‹Ÿå‡ºå…¥åº“数量
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_PickingRecord.cs
@@ -133,6 +133,8 @@
        public string PalletCode { get; set; }
        public int StockId { get; set; }
        public bool IsReverted { get; set; } = false;
        public bool IsAutoSplit { get; set; } = false;
        public int OutStockLockInfoId { get; set; } // å…³è”的出库锁定信息
        public string OriginalBarcode { get; set; } // åŽŸæ¡ç 
        public string NewBarcode { get; set; } // æ–°æ¡ç 
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundBatchPickingService.cs
@@ -1,5 +1,6 @@
using Microsoft.Extensions.Logging;
using SqlSugar;
using SqlSugar.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -267,7 +268,7 @@
                await UpdateOrderStatusAfterPalletRemoval(orderNo);
                // è®°å½•操作历史
                await RecordEmptyPalletRemoval(orderNo, palletCode, completedLocks);
               // await RecordEmptyPalletRemoval(orderNo, palletCode, completedLocks);
                _unitOfWorkManage.CommitTran();
@@ -411,32 +412,68 @@
            {
                _unitOfWorkManage.BeginTran();
                // 1. éªŒè¯åˆ†æ‹£è¯·æ±‚
                // éªŒè¯åˆ†æ‹£è¯·æ±‚
                var validationResult = await ValidatePickingRequest(orderNo, palletCode, barcode);
                if (!validationResult.IsValid)
                    return WebResponseContent.Instance.Error(validationResult.ErrorMessage);
                var (lockInfo, orderDetail, stockDetail, batch) = validationResult.Data;
                // ä½¿ç”¨é”å®šä¿¡æ¯çš„分配数量作为实际分拣数量
                var actualPickedQty = lockInfo.AssignQuantity;
                // æ£€æŸ¥æ˜¯å¦éœ€è¦è‡ªåŠ¨æ‹†åŒ…
                var autoSplitResult = await CheckAndAutoSplitIfNeeded(lockInfo, stockDetail, palletCode);
                if (autoSplitResult != null)
                {
                    // å¦‚果执行了自动拆包,重新获取最新的锁定信息和库存信息
                    var refreshedValidation = await ValidatePickingRequest(orderNo, palletCode, barcode);
                    if (!refreshedValidation.IsValid)
                    {
                        _unitOfWorkManage.RollbackTran();
                        return WebResponseContent.Instance.Error(refreshedValidation.ErrorMessage);
                    }
                // 2. æ‰§è¡Œåˆ†æ‹£é€»è¾‘
                var pickingResult = await ExecutePickingLogic(lockInfo, orderDetail, stockDetail, actualPickedQty);
                    (lockInfo, orderDetail, stockDetail, batch) = refreshedValidation.Data;
                // 3. æ›´æ–°æ‰¹æ¬¡å’Œè®¢å•数据
                await UpdateBatchAndOrderData(batch, orderDetail, actualPickedQty, orderNo);
                    // é‡è¦ä¿®å¤ï¼šè‡ªåŠ¨æ‹†åŒ…åŽï¼Œç«‹å³æ‰§è¡ŒåŽŸæ¡ç çš„åˆ†æ‹£
                    var actualPickedQty = lockInfo.AssignQuantity;
                    var pickingResult = await ExecutePickingLogic(lockInfo, orderDetail, stockDetail, actualPickedQty);
                // 4. è®°å½•拣选历史
                await RecordPickingHistory(pickingResult, orderNo, palletCode);
                    // æ›´æ–°æ‰¹æ¬¡å’Œè®¢å•数据
                    await UpdateBatchAndOrderData(batch, orderDetail, actualPickedQty, orderNo);
                    // è®°å½•拣选历史
                    await RecordPickingHistory(pickingResult, orderNo, palletCode);
                    _unitOfWorkManage.CommitTran();
                    return WebResponseContent.Instance.OK("自动拆包并分拣成功", new
                    {
                        AutoSplitted = true,
                        NewBarcode = autoSplitResult.NewBarcode,
                        OriginalBarcode = barcode,
                        SplitQuantity = autoSplitResult.SplitQuantity,
                        PickedQuantity = actualPickedQty,
                        MaterialCode = lockInfo.MaterielCode
                    });
                }
                // æ­£å¸¸åˆ†æ‹£æµç¨‹ï¼ˆä¸éœ€è¦è‡ªåŠ¨æ‹†åŒ…ï¼‰
                var normalPickedQty = lockInfo.AssignQuantity;
                var normalPickingResult = await ExecutePickingLogic(lockInfo, orderDetail, stockDetail, normalPickedQty);
                // æ›´æ–°æ‰¹æ¬¡å’Œè®¢å•数据
                await UpdateBatchAndOrderData(batch, orderDetail, normalPickedQty, orderNo);
                // è®°å½•拣选历史
                await RecordPickingHistory(normalPickingResult, orderNo, palletCode);
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK("分拣成功", new
                {
                    PickedQuantity = actualPickedQty,
                    PickedQuantity = normalPickedQty,
                    Barcode = barcode,
                    MaterialCode = lockInfo.MaterielCode
                    MaterialCode = lockInfo.MaterielCode,
                    AutoSplitted = false
                });
            }
            catch (Exception ex)
@@ -494,6 +531,8 @@
        #region æ‰‹åŠ¨æ‹†åŒ…
        #region æ‰‹åŠ¨æ‹†åŒ…ç›¸å…³æ–¹æ³•
        /// <summary>
        /// æ‰‹åŠ¨æ‹†åŒ…
        /// </summary>
@@ -503,15 +542,15 @@
            {
                _unitOfWorkManage.BeginTran();
                //  éªŒè¯æ‹†åŒ…请求
                // éªŒè¯æ‹†åŒ…请求
                var validationResult = await ValidateSplitRequest(orderNo, palletCode, originalBarcode, splitQuantity);
                if (!validationResult.IsValid)
                    return WebResponseContent.Instance.Error(validationResult.ErrorMessage);
                var (lockInfo, stockDetail) = validationResult.Data;
                // . æ‰§è¡Œæ‹†åŒ…逻辑
                var splitResult = await ExecuteSplitLogic(lockInfo, stockDetail, splitQuantity, palletCode);
                // æ‰§è¡Œæ‹†åŒ…逻辑
                var splitResult = await ExecuteManualSplitLogic(lockInfo, stockDetail, splitQuantity, palletCode);
                _unitOfWorkManage.CommitTran();
@@ -530,9 +569,224 @@
            }
        }
        /// <summary>
        /// éªŒè¯æ‹†åŒ…请求 - å¢žå¼ºåˆ†é…æ•°é‡æŽ§åˆ¶
        /// </summary>
        private async Task<ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>> ValidateSplitRequest(
            string orderNo, string palletCode, string originalBarcode, decimal splitQuantity)
        {
            _logger.LogInformation($"开始验证拆包请求 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}, åŽŸæ¡ç : {originalBarcode}, æ‹†åŒ…数量: {splitQuantity}");
            // æŸ¥æ‰¾é”å®šä¿¡æ¯
            var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.OrderNo == orderNo &&
                           x.PalletCode == palletCode &&
                           x.CurrentBarcode == originalBarcode &&
                           x.Status == (int)OutLockStockStatusEnum.出库中)
                .FirstAsync();
            if (lockInfo == null)
            {
                _logger.LogWarning($"未找到有效的锁定信息 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}, æ¡ç : {originalBarcode}");
                return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("未找到有效的锁定信息");
            }
            _logger.LogInformation($"找到锁定信息 - åˆ†é…æ•°é‡: {lockInfo.AssignQuantity}, å·²æ‹£é€‰: {lockInfo.PickedQty}, è®¢å•数量: {lockInfo.OrderQuantity}");
            // èŽ·å–è®¢å•æ˜Žç»†
            var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                .FirstAsync(x => x.Id == lockInfo.OrderDetailId);
            if (orderDetail == null)
            {
                _logger.LogWarning($"未找到订单明细 - OrderDetailId: {lockInfo.OrderDetailId}");
                return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("未找到订单明细");
            }
            _logger.LogInformation($"找到订单明细 - å·²åˆ†é…æ•°é‡: {orderDetail.AllocatedQuantity}, é”å®šæ•°é‡: {orderDetail.LockQuantity}");
            // èŽ·å–åº“å­˜ä¿¡æ¯
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == originalBarcode && x.StockId == lockInfo.StockId);
            if (stockDetail == null)
            {
                _logger.LogWarning($"未找到库存信息 - æ¡ç : {originalBarcode}, StockId: {lockInfo.StockId}");
                return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("未找到对应的库存信息");
            }
            _logger.LogInformation($"找到库存信息 - åº“存数量: {stockDetail.StockQuantity}, å‡ºåº“数量: {stockDetail.OutboundQuantity}");
            // é‡è¦ä¿®å¤ï¼šéªŒè¯æ‹†åŒ…数量不能大于库存数量
            if (stockDetail.StockQuantity < splitQuantity)
            {
                _logger.LogWarning($"拆包数量大于库存数量 - æ‹†åŒ…数量: {splitQuantity}, åº“存数量: {stockDetail.StockQuantity}");
                return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error($"拆包数量不能大于库存数量,当前库存:{stockDetail.StockQuantity}");
            }
            // é‡è¦ä¿®å¤ï¼šéªŒè¯æ‹†åŒ…数量不能大于锁定信息的分配数量
            if (lockInfo.AssignQuantity < splitQuantity)
            {
                _logger.LogWarning($"拆包数量大于分配数量 - æ‹†åŒ…数量: {splitQuantity}, åˆ†é…æ•°é‡: {lockInfo.AssignQuantity}");
                return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error($"拆包数量不能大于分配数量,当前分配数量:{lockInfo.AssignQuantity}");
            }
            // é‡è¦ä¿®å¤ï¼šéªŒè¯æ‹†åŒ…数量不能大于锁定信息的未拣选数量
            decimal remainingToPick = lockInfo.AssignQuantity - lockInfo.PickedQty;
            if (remainingToPick < splitQuantity)
            {
                _logger.LogWarning($"拆包数量大于未拣选数量 - æ‹†åŒ…数量: {splitQuantity}, æœªæ‹£é€‰æ•°é‡: {remainingToPick}");
                return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error($"拆包数量不能大于未拣选数量,当前未拣选:{remainingToPick}");
            }
            // é‡è¦ä¿®å¤ï¼šéªŒè¯æ‹†åŒ…后原锁定信息的分配数量不会为负数
            if (lockInfo.AssignQuantity - splitQuantity < 0)
            {
                _logger.LogWarning($"拆包后分配数量为负数 - å½“前分配数量: {lockInfo.AssignQuantity}, æ‹†åŒ…数量: {splitQuantity}");
                return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error($"拆包后分配数量不能为负数");
            }
            // é‡è¦ä¿®å¤ï¼šéªŒè¯è®¢å•明细的分配数量是否足够
            // æ³¨æ„ï¼šæ‰‹åŠ¨æ‹†åŒ…ä¸ä¼šæ”¹å˜è®¢å•æ˜Žç»†çš„åˆ†é…æ•°é‡ï¼Œå› ä¸ºæ€»åˆ†é…æ•°é‡ä¸å˜
            // åªæ˜¯ä»Žä¸€ä¸ªé”å®šä¿¡æ¯è½¬ç§»åˆ°å¦ä¸€ä¸ªé”å®šä¿¡æ¯
            decimal totalLockAssignQuantity = await GetTotalLockAssignQuantity(orderDetail.Id);
            if (orderDetail.AllocatedQuantity != totalLockAssignQuantity)
            {
                _logger.LogWarning($"订单明细分配数量与锁定信息不一致 - è®¢å•明细分配数量: {orderDetail.AllocatedQuantity}, é”å®šä¿¡æ¯æ€»åˆ†é…æ•°é‡: {totalLockAssignQuantity}");
                // è¿™é‡Œä¸ç›´æŽ¥è¿”回错误,因为拆包操作本身不会导致不一致,只是记录警告
            }
            _logger.LogInformation($"拆包验证通过 - åŽŸæ¡ç : {originalBarcode}, æ‹†åŒ…数量: {splitQuantity}");
            return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Success((lockInfo, stockDetail));
        }
        /// <summary>
        /// èŽ·å–è®¢å•æ˜Žç»†çš„æ‰€æœ‰é”å®šä¿¡æ¯çš„æ€»åˆ†é…æ•°é‡
        /// </summary>
        private async Task<decimal> GetTotalLockAssignQuantity(long orderDetailId)
        {
            var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.OrderDetailId == orderDetailId)
                .ToListAsync();
            return lockInfos.Sum(x => x.AssignQuantity);
        }
        /// <summary>
        /// æ‰§è¡Œæ‰‹åŠ¨æ‹†åŒ…é€»è¾‘ - å¢žå¼ºåˆ†é…æ•°é‡æŽ§åˆ¶
        /// </summary>
        private async Task<SplitResultDto> ExecuteManualSplitLogic(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail,
            decimal splitQuantity, string palletCode)
        {
            _logger.LogInformation($"开始执行手动拆包逻辑 - åŽŸæ¡ç : {stockDetail.Barcode}, æ‹†åŒ…数量: {splitQuantity}");
            // é‡è¦ä¿®å¤ï¼šèŽ·å–è®¢å•æ˜Žç»†å¹¶éªŒè¯åˆ†é…æ•°é‡
            var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                .FirstAsync(x => x.Id == lockInfo.OrderDetailId);
            if (orderDetail == null)
                throw new InvalidOperationException("未找到订单明细");
            // éªŒè¯æ‹†åŒ…数量不会导致分配数量为负数
            if (lockInfo.AssignQuantity < splitQuantity)
            {
                throw new InvalidOperationException($"拆包数量超过锁定信息分配数量,拆包数量: {splitQuantity}, åˆ†é…æ•°é‡: {lockInfo.AssignQuantity}");
            }
            // ç”Ÿæˆæ–°æ¡ç 
            string newBarcode = await GenerateNewBarcode();
            // è®°å½•拆包前的分配数量
            decimal originalAssignQtyBefore = lockInfo.AssignQuantity;
            decimal originalOrderQtyBefore = lockInfo.OrderQuantity;
            // 1. åˆ›å»ºæ–°åº“存明细
            var newStockDetail = new Dt_StockInfoDetail
            {
                StockId = stockDetail.StockId,
                MaterielCode = stockDetail.MaterielCode,
                OrderNo = stockDetail.OrderNo,
                BatchNo = stockDetail.BatchNo,
                StockQuantity = splitQuantity,
                OutboundQuantity = 0,
                Barcode = newBarcode,
                Status = (int)StockStatusEmun.出库锁定,
                SupplyCode = stockDetail.SupplyCode,
                Unit = stockDetail.Unit,
                BarcodeQty = stockDetail.BarcodeQty,
                BarcodeUnit = stockDetail.BarcodeUnit,
                BusinessType = stockDetail.BusinessType,
                InboundOrderRowNo = stockDetail.InboundOrderRowNo,
            };
            await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
            _logger.LogInformation($"创建新库存明细 - æ¡ç : {newBarcode}, åº“存数量: {splitQuantity}");
            // 2. æ›´æ–°åŽŸåº“å­˜æ˜Žç»†
            decimal originalStockQtyBefore = stockDetail.StockQuantity;
            stockDetail.StockQuantity -= splitQuantity;
            await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            _logger.LogInformation($"更新原库存明细 - åº“存数量从 {originalStockQtyBefore} å‡å°‘到 {stockDetail.StockQuantity}");
            // 3. åˆ›å»ºæ–°é”å®šä¿¡æ¯
            var newLockInfo = new Dt_OutStockLockInfo
            {
                OrderNo = lockInfo.OrderNo,
                OrderDetailId = lockInfo.OrderDetailId,
                OutboundBatchNo = lockInfo.OutboundBatchNo,
                MaterielCode = lockInfo.MaterielCode,
                MaterielName = lockInfo.MaterielName,
                StockId = lockInfo.StockId,
                OrderQuantity = splitQuantity,
                AssignQuantity = splitQuantity,
                PickedQty = 0,
                LocationCode = lockInfo.LocationCode,
                PalletCode = lockInfo.PalletCode,
                TaskNum = lockInfo.TaskNum,
                Status = (int)OutLockStockStatusEnum.出库中,
                Unit = lockInfo.Unit,
                SupplyCode = lockInfo.SupplyCode,
                OrderType = lockInfo.OrderType,
                CurrentBarcode = newBarcode,
                IsSplitted = 1,
                ParentLockId = lockInfo.Id,
                Operator = App.User.UserName,
                FactoryArea = lockInfo.FactoryArea,
                lineNo = lockInfo.lineNo,
                WarehouseCode = lockInfo.WarehouseCode,
                BarcodeQty = lockInfo.BarcodeQty,
                BarcodeUnit = lockInfo.BarcodeUnit,
            };
            await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteCommandAsync();
            _logger.LogInformation($"创建新锁定信息 - æ¡ç : {newBarcode}, åˆ†é…æ•°é‡: {splitQuantity}");
            // 4. æ›´æ–°åŽŸé”å®šä¿¡æ¯
            lockInfo.AssignQuantity -= splitQuantity;
            lockInfo.OrderQuantity -= splitQuantity;
            _logger.LogInformation($"更新原锁定信息 - åˆ†é…æ•°é‡ä»Ž {originalAssignQtyBefore} å‡å°‘到 {lockInfo.AssignQuantity}");
            _logger.LogInformation($"更新原锁定信息 - è®¢å•数量从 {originalOrderQtyBefore} å‡å°‘到 {lockInfo.OrderQuantity}");
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
            // é‡è¦ä¿®å¤ï¼šæ‰‹åŠ¨æ‹†åŒ…ä¸ä¼šæ”¹å˜è®¢å•æ˜Žç»†çš„åˆ†é…æ•°é‡ï¼Œå› ä¸ºæ€»åˆ†é…æ•°é‡ä¸å˜
            // åªæ˜¯ä»Žä¸€ä¸ªé”å®šä¿¡æ¯è½¬ç§»åˆ°å¦ä¸€ä¸ªé”å®šä¿¡æ¯
            _logger.LogInformation($"订单明细分配数量保持不变 - å·²åˆ†é…æ•°é‡: {orderDetail.AllocatedQuantity}");
            // 5. è®°å½•拆包历史
            await RecordSplitHistory(lockInfo, stockDetail, splitQuantity, newBarcode, false);
            _logger.LogInformation($"手动拆包逻辑执行完成");
            return new SplitResultDto { NewBarcode = newBarcode };
        }
        #endregion
        #endregion
        #region å–消拆包 
@@ -588,15 +842,24 @@
            Dt_OutStockLockInfo originalLockInfo, Dt_OutStockLockInfo newLockInfo,
            Dt_StockInfoDetail newStockDetail)
        {
            // 1. æ¢å¤åŽŸé”å®šä¿¡æ¯
            // æ³¨æ„ï¼šè¿™é‡Œéœ€è¦ç´¯åŠ ï¼Œè€Œä¸æ˜¯ç®€å•çš„èµ‹å€¼ï¼Œå› ä¸ºå¯èƒ½æœ‰å¤šæ¬¡æ‹†åŒ…
            _logger.LogInformation($"开始执行取消拆包逻辑 - åŽŸæ¡ç : {splitRecord.OriginalBarcode}, æ–°æ¡ç : {splitRecord.NewBarcode}, æ‹†åŒ…数量: {splitRecord.SplitQty}");
            // æ¢å¤åŽŸé”å®šä¿¡æ¯
            decimal originalAssignQtyBefore = originalLockInfo.AssignQuantity;
            decimal originalOrderQtyBefore = originalLockInfo.OrderQuantity;
            // é‡è¦ä¿®å¤ï¼šæ¢å¤åˆ†é…æ•°é‡å’Œè®¢å•数量
            originalLockInfo.AssignQuantity += splitRecord.SplitQty;
            originalLockInfo.OrderQuantity += splitRecord.SplitQty;
            _logger.LogInformation($"恢复原锁定信息 - åˆ†é…æ•°é‡ä»Ž {originalAssignQtyBefore} å¢žåŠ åˆ° {originalLockInfo.AssignQuantity}");
            _logger.LogInformation($"恢复原锁定信息 - è®¢å•数量从 {originalOrderQtyBefore} å¢žåŠ åˆ° {originalLockInfo.OrderQuantity}");
            // å¦‚果原锁定信息的状态是拣选完成,需要重新设置为出库中
            if (originalLockInfo.Status == (int)OutLockStockStatusEnum.拣选完成)
            {
                originalLockInfo.Status = (int)OutLockStockStatusEnum.出库中;
                _logger.LogInformation($"原锁定信息状态从拣选完成恢复为出库中");
            }
            await _outStockLockInfoService.Db.Updateable(originalLockInfo).ExecuteCommandAsync();
@@ -605,35 +868,47 @@
            var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == splitRecord.OriginalBarcode && x.StockId == splitRecord.StockId);
            originalStock.StockQuantity += splitRecord.SplitQty;
            // å¦‚果原库存状态是出库完成,需要重新设置为出库锁定
            if (originalStock.Status == (int)StockStatusEmun.出库完成)
            if (originalStock != null)
            {
                originalStock.Status = (int)StockStatusEmun.出库锁定;
                decimal originalStockQtyBefore = originalStock.StockQuantity;
                originalStock.StockQuantity += splitRecord.SplitQty;
                _logger.LogInformation($"恢复原库存明细 - åº“存数量从 {originalStockQtyBefore} å¢žåŠ åˆ° {originalStock.StockQuantity}");
                // å¦‚果原库存状态是出库完成,需要重新设置为出库锁定
                if (originalStock.Status == (int)StockStatusEmun.出库完成)
                {
                    originalStock.Status = (int)StockStatusEmun.出库锁定;
                    _logger.LogInformation($"原库存状态从出库完成恢复为出库锁定");
                }
                await _stockInfoDetailService.Db.Updateable(originalStock).ExecuteCommandAsync();
            }
            await _stockInfoDetailService.Db.Updateable(originalStock).ExecuteCommandAsync();
            // åˆ é™¤æ–°é”å®šä¿¡æ¯
            _logger.LogInformation($"删除新锁定信息 - æ¡ç : {newLockInfo.CurrentBarcode}, åˆ†é…æ•°é‡: {newLockInfo.AssignQuantity}");
            await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>()
                .Where(x => x.Id == newLockInfo.Id)
                .ExecuteCommandAsync();
            // åˆ é™¤æ–°åº“存明细
            //  åˆ é™¤æ–°åº“存明细
            _logger.LogInformation($"删除新库存明细 - æ¡ç : {newStockDetail.Barcode}, åº“存数量: {newStockDetail.StockQuantity}");
            await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>()
                .Where(x => x.Barcode == newLockInfo.CurrentBarcode)
                .ExecuteCommandAsync();
            //标记拆包记录为已撤销
            // æ ‡è®°æ‹†åŒ…记录为已撤销
            splitRecord.IsReverted = true;
            splitRecord.RevertTime = DateTime.Now;
            splitRecord.RevertOperator = App.User.UserName;
            await _splitPackageService.Db.Updateable(splitRecord).ExecuteCommandAsync();
            _logger.LogInformation($"标记拆包记录为已撤销");
            // æ£€æŸ¥å¹¶æ›´æ–°æ‰¹æ¬¡å’Œè®¢å•状态
            await CheckAndUpdateBatchStatus(originalLockInfo.BatchNo);
            await CheckAndUpdateOrderStatus(originalLockInfo.OrderNo);
            _logger.LogInformation($"取消拆包逻辑执行完成");
        }
        /// <summary>
@@ -642,6 +917,8 @@
        private async Task<ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>> ValidateCancelSplitRequest(
            string orderNo, string palletCode, string newBarcode)
        {
            _logger.LogInformation($"开始验证取消拆包请求 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}, æ¡ç : {newBarcode}");
            // æŸ¥æ‰¾æ‹†åŒ…记录
            var splitRecord = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                .Where(x => x.NewBarcode == newBarcode &&
@@ -651,6 +928,8 @@
            if (splitRecord == null)
                return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("未找到拆包记录");
            _logger.LogInformation($"找到拆包记录 - åŽŸæ¡ç : {splitRecord.OriginalBarcode}, æ–°æ¡ç : {splitRecord.NewBarcode}, æ‹†åŒ…数量: {splitRecord.SplitQty}");
            // æŸ¥æ‰¾æ–°é”å®šä¿¡æ¯
            var newLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
@@ -662,13 +941,35 @@
            if (newLockInfo == null)
                return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("未找到新锁定信息");
            // æ£€æŸ¥æ–°æ¡ç æ˜¯å¦å·²è¢«åˆ†æ‹£
            var pickingRecord = await Db.Queryable<Dt_PickingRecord>()
                .Where(x => x.Barcode == newBarcode && !x.IsCancelled)
                .FirstAsync();
            _logger.LogInformation($"找到新锁定信息 - çŠ¶æ€: {newLockInfo.Status}, å·²æ‹£é€‰: {newLockInfo.PickedQty}, åˆ†é…æ•°é‡: {newLockInfo.AssignQuantity}");
            if (pickingRecord != null)
                return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("该条码已被分拣,无法取消拆包");
            // é‡è¦ä¿®å¤ï¼šæ£€æŸ¥æ–°æ¡ç æ˜¯å¦å·²è¢«åˆ†æ‹£
            var newBarcodePickingRecords = await Db.Queryable<Dt_PickingRecord>()
                .Where(x => x.Barcode == newBarcode && x.OrderNo == orderNo && !x.IsCancelled)
                .ToListAsync();
            if (newBarcodePickingRecords.Any())
            {
                var totalPickedQty = newBarcodePickingRecords.Sum(x => x.PickQuantity);
                _logger.LogWarning($"新条码 {newBarcode} å·²è¢«åˆ†æ‹£ï¼Œæ€»æ‹£é€‰æ•°é‡: {totalPickedQty}");
                return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error(
                    $"新条码已被分拣(已拣选数量:{totalPickedQty}),请先取消分拣,然后再取消拆包");
            }
            // é‡è¦ä¿®å¤ï¼šæ£€æŸ¥åŽŸæ¡ç æ˜¯å¦å·²è¢«åˆ†æ‹£
            var originalBarcodePickingRecords = await Db.Queryable<Dt_PickingRecord>()
                .Where(x => x.Barcode == splitRecord.OriginalBarcode && x.OrderNo == orderNo && !x.IsCancelled)
                .ToListAsync();
            if (originalBarcodePickingRecords.Any())
            {
                var totalPickedQty = originalBarcodePickingRecords.Sum(x => x.PickQuantity);
                _logger.LogWarning($"原条码 {splitRecord.OriginalBarcode} å·²è¢«åˆ†æ‹£ï¼Œæ€»æ‹£é€‰æ•°é‡: {totalPickedQty}");
                return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error(
                    $"原条码已被分拣(已拣选数量:{totalPickedQty}),请先取消分拣,然后再取消拆包");
            }
            _logger.LogInformation($"新旧条码均未被分拣,可以取消拆包");
            // æ£€æŸ¥æ–°æ¡ç æ˜¯å¦è¢«å†æ¬¡æ‹†åŒ…
            var childSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
@@ -678,12 +979,18 @@
            if (childSplitRecords.Any())
            {
                var childBarcodes = string.Join(", ", childSplitRecords.Select(x => x.NewBarcode));
                _logger.LogWarning($"条码 {newBarcode} å·²è¢«å†æ¬¡æ‹†åŒ…,生成的新条码: {childBarcodes}");
                return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error(
                    $"该条码已被再次拆包,生成的新条码:{childBarcodes},请先取消后续拆包");
            }
            var newStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == newBarcode);
            if (newStockDetail == null)
                return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("未找到新库存明细");
            _logger.LogInformation($"取消拆包验证通过 - æ¡ç : {newBarcode}");
            return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Success((splitRecord, newLockInfo, newStockDetail));
        }
@@ -707,11 +1014,28 @@
                if (!splitChain.Any())
                    return WebResponseContent.Instance.Error("未找到拆包记录");
                // 2. æŒ‰æ‹†åŒ…顺序倒序取消(从最新的开始取消)
                _logger.LogInformation($"找到拆包链,共 {splitChain.Count} æ¡è®°å½•");
                // 2. æ”¶é›†æ‹†åŒ…链中涉及的所有条码(包括原条码和新条码)
                var allBarcodesInChain = new List<string> { startBarcode };
                allBarcodesInChain.AddRange(splitChain.Select(x => x.NewBarcode));
                // 3. æ£€æŸ¥æ‹†åŒ…链中是否有已被分拣的条码
                var pickedBarcodesInfo = await GetPickedBarcodesInfo(orderNo, allBarcodesInChain);
                if (pickedBarcodesInfo.Any())
                {
                    var pickedBarcodes = string.Join(", ", pickedBarcodesInfo.Select(x => $"{x.Barcode}(已拣选{x.PickedQty})"));
                    return WebResponseContent.Instance.Error(
                        $"以下条码已被分拣,请先取消分拣:{pickedBarcodes}");
                }
                // 4. æŒ‰æ‹†åŒ…顺序倒序取消(从最新的开始取消)
                var reversedChain = splitChain.OrderByDescending(x => x.SplitTime).ToList();
                foreach (var splitRecord in reversedChain)
                {
                    _logger.LogInformation($"取消拆包记录 - åŽŸæ¡ç : {splitRecord.OriginalBarcode}, æ–°æ¡ç : {splitRecord.NewBarcode}");
                    await CancelSingleSplitPackage(splitRecord, palletCode);
                }
@@ -727,6 +1051,133 @@
            }
        }
        /// <summary>
        /// èŽ·å–æ¡ç çš„æ‹†åŒ…å’Œæ‹£é€‰çŠ¶æ€
        /// </summary>
        public async Task<WebResponseContent> GetBarcodeSplitAndPickStatus(string orderNo, string barcode)
        {
            try
            {
                // 1. èŽ·å–æ‹†åŒ…ä¿¡æ¯
                var splitChain = await GetSplitPackageChain(orderNo, barcode);
                var isOriginalBarcode = !splitChain.Any(x => x.NewBarcode == barcode);
                // 2. èŽ·å–æ‹£é€‰ä¿¡æ¯
                var pickingRecords = await Db.Queryable<Dt_PickingRecord>()
                    .Where(x => x.Barcode == barcode && x.OrderNo == orderNo && !x.IsCancelled)
                    .ToListAsync();
                var totalPickedQty = pickingRecords.Sum(x => x.PickQuantity);
                // 3. èŽ·å–é”å®šä¿¡æ¯
                var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.CurrentBarcode == barcode && x.OrderNo == orderNo)
                    .FirstAsync();
                // 4. èŽ·å–åº“å­˜ä¿¡æ¯
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(x => x.Barcode == barcode)
                    .FirstAsync();
                var statusInfo = new BarcodeStatusInfoDto
                {
                    Barcode = barcode,
                    OrderNo = orderNo,
                    IsOriginalBarcode = isOriginalBarcode,
                    SplitChainCount = splitChain.Count,
                    HasBeenPicked = pickingRecords.Any(),
                    TotalPickedQuantity = totalPickedQty,
                    PickRecordCount = pickingRecords.Count,
                    LockInfoStatus = lockInfo?.Status ?? 0,
                    LockInfoPickedQty = lockInfo?.PickedQty ?? 0,
                    LockInfoAssignQty = lockInfo?.AssignQuantity ?? 0,
                    StockQuantity = stockDetail?.StockQuantity ?? 0,
                    StockStatus = stockDetail?.Status ?? 0,
                    CanCancelSplit = !pickingRecords.Any(), // æœªè¢«åˆ†æ‹£æ‰èƒ½å–消拆包
                    NeedCancelPickFirst = pickingRecords.Any() // éœ€è¦å…ˆå–消分拣
                };
                // 5. èŽ·å–æ“ä½œå»ºè®®
                statusInfo.OperationSuggestions = GetOperationSuggestions(statusInfo);
                return WebResponseContent.Instance.OK("获取状态成功", statusInfo);
            }
            catch (Exception ex)
            {
                _logger.LogError($"获取条码状态失败 - OrderNo: {orderNo}, Barcode: {barcode}, Error: {ex.Message}");
                return WebResponseContent.Instance.Error("获取条码状态失败");
            }
        }
         /// <summary>
        /// èŽ·å–æ“ä½œå»ºè®®
        /// </summary>
        private List<string> GetOperationSuggestions(BarcodeStatusInfoDto statusInfo)
        {
            var suggestions = new List<string>();
            if (statusInfo.HasBeenPicked)
            {
                suggestions.Add($"该条码已被分拣(数量:{statusInfo.TotalPickedQuantity}),如需取消拆包,请先取消分拣");
                if (statusInfo.IsOriginalBarcode)
                {
                    suggestions.Add("这是原条码,取消分拣后将恢复为可分拣状态");
                }
                else
                {
                    suggestions.Add("这是拆包生成的新条码,取消分拣后才能取消拆包");
                }
            }
            else
            {
                if (statusInfo.IsOriginalBarcode && statusInfo.SplitChainCount > 0)
                {
                    suggestions.Add("这是原条码,可以取消拆包链");
                }
                else if (!statusInfo.IsOriginalBarcode)
                {
                    suggestions.Add("这是拆包生成的新条码,可以单独取消拆包");
                }
            }
            if (statusInfo.LockInfoStatus == (int)OutLockStockStatusEnum.拣选完成)
            {
                suggestions.Add("锁定状态:拣选完成");
            }
            else if (statusInfo.LockInfoStatus == (int)OutLockStockStatusEnum.出库中)
            {
                suggestions.Add("锁定状态:出库中");
            }
            return suggestions;
        }
        /// <summary>
        /// èŽ·å–å·²è¢«åˆ†æ‹£çš„æ¡ç ä¿¡æ¯
        /// </summary>
        private async Task<List<PickedBarcodeInfo>> GetPickedBarcodesInfo(string orderNo, List<string> barcodes)
        {
            var pickedBarcodes = new List<PickedBarcodeInfo>();
            foreach (var barcode in barcodes)
            {
                var pickingRecords = await Db.Queryable<Dt_PickingRecord>()
                    .Where(x => x.Barcode == barcode && x.OrderNo == orderNo && !x.IsCancelled)
                    .ToListAsync();
                if (pickingRecords.Any())
                {
                    var totalPickedQty = pickingRecords.Sum(x => x.PickQuantity);
                    pickedBarcodes.Add(new PickedBarcodeInfo
                    {
                        Barcode = barcode,
                        PickedQty = totalPickedQty,
                        PickRecordCount = pickingRecords.Count
                    });
                }
            }
            return pickedBarcodes;
        }
        /// <summary>
        /// èŽ·å–æ‹†åŒ…é“¾ - æŸ¥æ‰¾æŸä¸ªæ¡ç çš„æ‰€æœ‰æ‹†åŒ…记录(包括后续拆包)
        /// </summary>
@@ -771,6 +1222,27 @@
        /// </summary>
        private async Task CancelSingleSplitPackage(Dt_SplitPackageRecord splitRecord, string palletCode)
        {
            _logger.LogInformation($"开始取消单个拆包记录 - åŽŸæ¡ç : {splitRecord.OriginalBarcode}, æ–°æ¡ç : {splitRecord.NewBarcode}");
            // å†æ¬¡éªŒè¯åˆ†æ‹£çŠ¶æ€ï¼ˆé˜²æ­¢å¹¶å‘æ“ä½œï¼‰
            var newBarcodePickingRecords = await Db.Queryable<Dt_PickingRecord>()
                .Where(x => x.Barcode == splitRecord.NewBarcode && !x.IsCancelled)
                .ToListAsync();
            if (newBarcodePickingRecords.Any())
            {
                throw new InvalidOperationException($"新条码 {splitRecord.NewBarcode} åœ¨éªŒè¯åŽè¢«åˆ†æ‹£ï¼Œæ— æ³•取消拆包");
            }
            var originalBarcodePickingRecords = await Db.Queryable<Dt_PickingRecord>()
                .Where(x => x.Barcode == splitRecord.OriginalBarcode && !x.IsCancelled)
                .ToListAsync();
            if (originalBarcodePickingRecords.Any())
            {
                throw new InvalidOperationException($"原条码 {splitRecord.OriginalBarcode} åœ¨éªŒè¯åŽè¢«åˆ†æ‹£ï¼Œæ— æ³•取消拆包");
            }
            // æŸ¥æ‰¾ç›¸å…³æ•°æ®
            var newLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.CurrentBarcode == splitRecord.NewBarcode &&
@@ -785,6 +1257,8 @@
            // æ‰§è¡Œå–消逻辑
            await ExecuteCancelSplitLogic(splitRecord, originalLockInfo, newLockInfo, newStockDetail);
            _logger.LogInformation($"取消单个拆包记录完成 - åŽŸæ¡ç : {splitRecord.OriginalBarcode}, æ–°æ¡ç : {splitRecord.NewBarcode}");
        }
        #endregion
@@ -810,7 +1284,8 @@
                        NewBarcode = x.NewBarcode,
                        SplitQuantity = x.SplitQty,
                        Operator = x.Operator,
                        IsReverted = x.IsReverted
                        IsReverted = x.IsReverted ,
                        IsAutoSplit = x.IsAutoSplit
                    }).ToList()
                };
@@ -899,9 +1374,7 @@
        #endregion
        #region åˆ†æ‰¹å›žåº“
        /// <summary>
@@ -961,6 +1434,8 @@
        private async Task<ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>> ValidatePickingRequest(
       string orderNo, string palletCode, string barcode)
        {
            _logger.LogInformation($"开始验证分拣请求 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}, æ¡ç : {barcode}");
            // æŸ¥æ‰¾é”å®šä¿¡æ¯
            var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.OrderNo == orderNo &&
@@ -972,6 +1447,8 @@
            if (lockInfo == null)
                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error("未找到有效的锁定信息");
            _logger.LogInformation($"找到锁定信息 - åˆ†é…æ•°é‡: {lockInfo.AssignQuantity}, å·²æ‹£é€‰: {lockInfo.PickedQty}");
            // æ£€æŸ¥æ˜¯å¦å·²ç»åˆ†æ‹£å®Œæˆ
            if (lockInfo.PickedQty >= lockInfo.AssignQuantity)
                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error("该条码已分拣完成");
@@ -980,132 +1457,106 @@
            var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                .FirstAsync(x => x.Id == lockInfo.OrderDetailId);
            if (orderDetail == null)
                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error("未找到订单明细");
            _logger.LogInformation($"找到订单明细 - å·²åˆ†é…æ•°é‡: {orderDetail.AllocatedQuantity}, é”å®šæ•°é‡: {orderDetail.LockQuantity}");
            // é‡è¦ä¿®å¤ï¼šæ£€æŸ¥è®¢å•明细的已分配数量是否足够
            decimal remainingToPick = lockInfo.AssignQuantity - lockInfo.PickedQty;
            if (orderDetail.AllocatedQuantity < remainingToPick)
            {
                _logger.LogWarning($"订单明细已分配数量不足 - éœ€è¦æ‹£é€‰: {remainingToPick}, å¯ç”¨åˆ†é…æ•°é‡: {orderDetail.AllocatedQuantity}");
                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error(
                    $"订单明细分配数量不足,需要拣选:{remainingToPick},可用分配数量:{orderDetail.AllocatedQuantity}");
            }
            // é‡è¦ä¿®å¤ï¼šæ£€æŸ¥é”å®šæ•°é‡æ˜¯å¦è¶³å¤Ÿ
            if (orderDetail.LockQuantity < remainingToPick)
            {
                _logger.LogWarning($"订单明细锁定数量不足 - éœ€è¦æ‹£é€‰: {remainingToPick}, å¯ç”¨é”å®šæ•°é‡: {orderDetail.LockQuantity}");
                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error(
                    $"订单明细锁定数量不足,需要拣选:{remainingToPick},可用锁定数量:{orderDetail.LockQuantity}");
            }
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == barcode && x.StockId == lockInfo.StockId);
            if (stockDetail == null)
                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error("未找到对应的库存信息");
            // éªŒè¯åº“存数量
            if (stockDetail.StockQuantity < lockInfo.AssignQuantity)
            if (stockDetail.StockQuantity < remainingToPick)
                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error(
                    $"库存数量不足,需要:{lockInfo.AssignQuantity},实际:{stockDetail.StockQuantity}");
                    $"库存数量不足,需要拣选:{remainingToPick},实际库存:{stockDetail.StockQuantity}");
            // éªŒè¯åº“存状态
            if (stockDetail.Status != (int)StockStatusEmun.出库锁定)
            {
                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error(
                    $"库存状态异常,当前状态:{stockDetail.Status},期望状态:出库锁定");
            }
            // ä½¿ç”¨ OutboundBatchNo æŸ¥æ‰¾æ‰¹æ¬¡
            var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>()
                .FirstAsync(x => x.BatchNo == lockInfo.OutboundBatchNo); // ä¿®æ­£ä¸º OutboundBatchNo
                .FirstAsync(x => x.BatchNo == lockInfo.OutboundBatchNo);
            _logger.LogInformation($"分拣验证通过 - æ¡ç : {barcode}, å‰©ä½™éœ€æ‹£é€‰: {remainingToPick}, å¯ç”¨åº“å­˜: {stockDetail.StockQuantity}");
            return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Success((lockInfo, orderDetail, stockDetail, batch));
        }
        private async Task<ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>> ValidateSplitRequest(
            string orderNo, string palletCode, string originalBarcode, decimal splitQuantity)
        /// <summary>
        /// æ£€æŸ¥å¹¶æ‰§è¡Œè‡ªåŠ¨æ‹†åŒ…ï¼ˆå¦‚æžœéœ€è¦ï¼‰
        /// </summary>
        private async Task<AutoSplitResult> CheckAndAutoSplitIfNeeded(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, string palletCode)
        {
            var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.OrderNo == orderNo &&
                           x.PalletCode == palletCode &&
                           x.CurrentBarcode == originalBarcode &&
                           x.Status == (int)OutLockStockStatusEnum.出库中)
                .FirstAsync();
            // æ£€æŸ¥æ˜¯å¦éœ€è¦è‡ªåŠ¨æ‹†åŒ…çš„æ¡ä»¶ï¼š
            // 1. åº“存数量大于分配数量
            // 2. é”å®šä¿¡æ¯çŠ¶æ€ä¸ºå‡ºåº“ä¸­
            // 3. æœªæ‹£é€‰æ•°é‡ç­‰äºŽåˆ†é…æ•°é‡ï¼ˆè¡¨ç¤ºè¿˜æœªå¼€å§‹æ‹£é€‰ï¼‰
            bool needAutoSplit = stockDetail.StockQuantity > lockInfo.AssignQuantity &&
                                lockInfo.Status == (int)OutLockStockStatusEnum.出库中 &&
                                lockInfo.PickedQty == 0;
            if (lockInfo == null)
                return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("未找到有效的锁定信息");
            if (!needAutoSplit)
                return null;
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == originalBarcode && x.StockId == lockInfo.StockId);
            // è®¡ç®—拆包数量 = åº“存数量 - åˆ†é…æ•°é‡
            decimal splitQuantity = stockDetail.StockQuantity - lockInfo.AssignQuantity;
            if (stockDetail.StockQuantity < splitQuantity)
                return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("拆包数量不能大于库存数量");
            // æ‰§è¡Œè‡ªåŠ¨æ‹†åŒ…
            var splitResult = await ExecuteAutoSplitLogic(lockInfo, stockDetail, splitQuantity, palletCode);
            if (lockInfo.AssignQuantity - lockInfo.PickedQty < splitQuantity)
                return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("拆包数量不能大于未拣选数量");
            return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Success((lockInfo, stockDetail));
        }
        #endregion
        #region æ ¸å¿ƒé€»è¾‘方法
        private async Task<PickingResult> ExecutePickingLogic(
            Dt_OutStockLockInfo lockInfo, Dt_OutboundOrderDetail orderDetail,
            Dt_StockInfoDetail stockDetail, decimal actualPickedQty)
        {
            // æ›´æ–°é”å®šä¿¡æ¯
            lockInfo.PickedQty += actualPickedQty;
            if (lockInfo.PickedQty >= lockInfo.AssignQuantity)
            return new AutoSplitResult
            {
                lockInfo.Status = (int)OutLockStockStatusEnum.拣选完成;
            }
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
            // æ›´æ–°åº“å­˜
            stockDetail.StockQuantity -= actualPickedQty;
            stockDetail.OutboundQuantity += actualPickedQty;
            if (stockDetail.StockQuantity <= 0)
            {
                stockDetail.Status = (int)StockStatusEmun.出库完成;
            }
            await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            return new PickingResult
            {
                FinalLockInfo = lockInfo,
                ActualPickedQty = actualPickedQty
                NewBarcode = splitResult.NewBarcode,
                SplitQuantity = splitQuantity
            };
        }
        private async Task<RevertPickingResult> RevertPickingData(Dt_PickingRecord pickingRecord)
        /// <summary>
        /// æ‰§è¡Œè‡ªåŠ¨æ‹†åŒ…é€»è¾‘
        /// </summary>
        private async Task<SplitResultDto> ExecuteAutoSplitLogic(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail,
            decimal splitQuantity, string palletCode)
        {
            // æ¢å¤é”å®šä¿¡æ¯
            var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .FirstAsync(x => x.Id == pickingRecord.OutStockLockId);
            _logger.LogInformation($"开始执行自动拆包逻辑 - åŽŸæ¡ç : {stockDetail.Barcode}, æ‹†åŒ…数量: {splitQuantity}");
            lockInfo.PickedQty -= pickingRecord.PickQuantity;
            // æ ¹æ®æ‹£é€‰æ•°é‡åˆ¤æ–­çŠ¶æ€
            if (lockInfo.PickedQty <= 0)
            {
                lockInfo.Status = (int)OutLockStockStatusEnum.出库中;
            }
            else if (lockInfo.PickedQty < lockInfo.AssignQuantity)
            {
                lockInfo.Status = (int)OutLockStockStatusEnum.出库中;
            }
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
            // æ¢å¤åº“å­˜
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == pickingRecord.Barcode);
            stockDetail.StockQuantity += pickingRecord.PickQuantity;
            stockDetail.OutboundQuantity -= pickingRecord.PickQuantity;
            // æ¢å¤åº“存状态
            if (stockDetail.StockQuantity > 0)
            {
                stockDetail.Status = (int)StockStatusEmun.出库锁定;
            }
            await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            return new RevertPickingResult
            {
                LockInfo = lockInfo,
                StockDetail = stockDetail
            };
        }
        private async Task<SplitResultDto> ExecuteSplitLogic(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail,
     decimal splitQuantity, string palletCode)
        {
            // ç”Ÿæˆæ–°æ¡ç 
            string newBarcode = await GenerateNewBarcode();
            // åˆ›å»ºæ–°åº“存明细
            // é‡è¦ä¿®å¤ï¼šè®°å½•拆包前的分配数量
            decimal originalAssignQtyBefore = lockInfo.AssignQuantity;
            decimal originalOrderQtyBefore = lockInfo.OrderQuantity;
            // 1. åˆ›å»ºæ–°åº“存明细(剩余部分)
            var newStockDetail = new Dt_StockInfoDetail
            {
                StockId = stockDetail.StockId,
                MaterielCode = stockDetail.MaterielCode,
                OrderNo = stockDetail.OrderNo,
                BatchNo = stockDetail.BatchNo, // ç‰©æ–™æ‰¹æ¬¡
                BatchNo = stockDetail.BatchNo,
                StockQuantity = splitQuantity,
                OutboundQuantity = 0,
                Barcode = newBarcode,
@@ -1115,20 +1566,25 @@
                BarcodeQty = stockDetail.BarcodeQty,
                BarcodeUnit = stockDetail.BarcodeUnit,
                BusinessType = stockDetail.BusinessType,
                InboundOrderRowNo = stockDetail.InboundOrderRowNo,
                InboundOrderRowNo = stockDetail.InboundOrderRowNo,
            };
            await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
            _logger.LogInformation($"创建新库存明细 - æ¡ç : {newBarcode}, åº“存数量: {splitQuantity}");
            // æ›´æ–°åŽŸåº“å­˜æ˜Žç»†
            stockDetail.StockQuantity -= splitQuantity;
            await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            // 2. æ›´æ–°åŽŸåº“å­˜æ˜Žç»†
            // é‡è¦ä¿®å¤ï¼šåŽŸåº“å­˜æ•°é‡è®¾ç½®ä¸ºåˆ†é…æ•°é‡ï¼ˆä¿æŒä¸å˜ï¼‰
            decimal originalStockQtyBefore = stockDetail.StockQuantity;
            // æ³¨æ„ï¼šè‡ªåŠ¨æ‹†åŒ…æ—¶ï¼ŒåŽŸåº“å­˜æ•°é‡ä¿æŒä¸å˜ï¼Œå› ä¸ºæˆ‘ä»¬è¦åˆ†æ‹£çš„å°±æ˜¯åˆ†é…æ•°é‡
            // stockDetail.StockQuantity ä¿æŒä¸å˜
            // åˆ›å»ºæ–°é”å®šä¿¡æ¯ - ä½¿ç”¨æ­£ç¡®çš„ OutboundBatchNo
            _logger.LogInformation($"原库存明细保持不变 - åº“存数量: {stockDetail.StockQuantity}");
            // 3. åˆ›å»ºæ–°é”å®šä¿¡æ¯ï¼ˆå‰©ä½™éƒ¨åˆ†ï¼‰
            var newLockInfo = new Dt_OutStockLockInfo
            {
                OrderNo = lockInfo.OrderNo,
                OrderDetailId = lockInfo.OrderDetailId,
                OutboundBatchNo = lockInfo.OutboundBatchNo, // ä½¿ç”¨ OutboundBatchNo
                OutboundBatchNo = lockInfo.OutboundBatchNo,
                MaterielCode = lockInfo.MaterielCode,
                MaterielName = lockInfo.MaterielName,
                StockId = lockInfo.StockId,
@@ -1150,111 +1606,310 @@
                lineNo = lockInfo.lineNo,
                WarehouseCode = lockInfo.WarehouseCode,
                BarcodeQty = lockInfo.BarcodeQty,
                BarcodeUnit = lockInfo.BarcodeUnit,
                BarcodeUnit = lockInfo.BarcodeUnit,
            };
            await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteCommandAsync();
            _logger.LogInformation($"创建新锁定信息 - æ¡ç : {newBarcode}, åˆ†é…æ•°é‡: {splitQuantity}");
            // æ›´æ–°åŽŸé”å®šä¿¡æ¯
            lockInfo.AssignQuantity -= splitQuantity;
            lockInfo.OrderQuantity -= splitQuantity;
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
            // 4. é‡è¦ä¿®å¤ï¼šè‡ªåŠ¨æ‹†åŒ…æ—¶ï¼ŒåŽŸé”å®šä¿¡æ¯çš„åˆ†é…æ•°é‡ä¿æŒä¸å˜
            // å› ä¸ºè‡ªåŠ¨æ‹†åŒ…åŽï¼ŒåŽŸæ¡ç ä»ç„¶éœ€è¦è¢«åˆ†æ‹£ï¼Œåˆ†é…æ•°é‡ä¸åº”è¯¥æ”¹å˜
            _logger.LogInformation($"原锁定信息保持不变 - åˆ†é…æ•°é‡: {lockInfo.AssignQuantity}, è®¢å•数量: {lockInfo.OrderQuantity}");
            // è®°å½•拆包历史
            await RecordSplitHistory(lockInfo, stockDetail, splitQuantity, newBarcode);
            // 5. è®°å½•拆包历史
            await RecordSplitHistory(lockInfo, stockDetail, splitQuantity, newBarcode, true, originalStockQtyBefore);
            _logger.LogInformation($"自动拆包逻辑执行完成");
            return new SplitResultDto { NewBarcode = newBarcode };
        }
        private async Task ExecuteCancelSplitLogic(Dt_SplitPackageRecord splitRecord, Dt_OutStockLockInfo newLockInfo, Dt_StockInfoDetail newStockDetail)
        #endregion
        #region æ ¸å¿ƒé€»è¾‘方法
        private async Task<PickingResult> ExecutePickingLogic(
            Dt_OutStockLockInfo lockInfo, Dt_OutboundOrderDetail orderDetail,
            Dt_StockInfoDetail stockDetail, decimal actualPickedQty)
        {
            // æ¢å¤åŽŸåº“å­˜
            var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == splitRecord.OriginalBarcode && x.StockId == splitRecord.StockId);
            _logger.LogInformation($"开始执行分拣逻辑 - æ¡ç : {stockDetail.Barcode}, åˆ†é…æ•°é‡: {lockInfo.AssignQuantity}, å®žé™…拣选: {actualPickedQty}");
            originalStock.StockQuantity += splitRecord.SplitQty;
            await _stockInfoDetailService.Db.Updateable(originalStock).ExecuteCommandAsync();
            // é‡è¦ä¿®å¤ï¼šå†æ¬¡éªŒè¯è®¢å•明细的分配数量(防止并发操作)
            if (orderDetail.AllocatedQuantity < actualPickedQty)
            {
                throw new InvalidOperationException($"订单明细分配数量不足,需要拣选 {actualPickedQty},可用分配数量 {orderDetail.AllocatedQuantity}");
            }
            // æ¢å¤åŽŸé”å®šä¿¡æ¯
            var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .FirstAsync(x => x.Id == splitRecord.OutStockLockInfoId);
            if (orderDetail.LockQuantity < actualPickedQty)
            {
                throw new InvalidOperationException($"订单明细锁定数量不足,需要拣选 {actualPickedQty},可用锁定数量 {orderDetail.LockQuantity}");
            }
            originalLockInfo.AssignQuantity += splitRecord.SplitQty;
            originalLockInfo.OrderQuantity += splitRecord.SplitQty;
            await _outStockLockInfoService.Db.Updateable(originalLockInfo).ExecuteCommandAsync();
            // 1. æ›´æ–°é”å®šä¿¡æ¯
            lockInfo.PickedQty += actualPickedQty;
            _logger.LogInformation($"更新锁定信息 - å·²æ‹£é€‰æ•°é‡ä»Ž {lockInfo.PickedQty - actualPickedQty} å¢žåŠ åˆ° {lockInfo.PickedQty}");
            // åˆ é™¤æ–°åº“存明细
            await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>()
                .Where(x => x.Barcode == newLockInfo.CurrentBarcode)
                .ExecuteCommandAsync();
            // å‡†ç¡®åˆ¤æ–­æ‹£é€‰å®ŒæˆçŠ¶æ€
            if (Math.Abs(lockInfo.PickedQty - lockInfo.AssignQuantity) < 0.001m)
            {
                lockInfo.Status = (int)OutLockStockStatusEnum.拣选完成;
                _logger.LogInformation($"锁定信息状态更新为拣选完成");
            }
            else if (lockInfo.PickedQty > 0)
            {
                lockInfo.Status = (int)OutLockStockStatusEnum.出库中;
                _logger.LogInformation($"锁定信息状态保持为出库中(部分拣选)");
            }
            // åˆ é™¤æ–°é”å®šä¿¡æ¯
            await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>()
                .Where(x => x.Id == newLockInfo.Id)
                .ExecuteCommandAsync();
            lockInfo.Operator = App.User.UserName;
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
            // æ ‡è®°æ‹†åŒ…记录为已撤销
            splitRecord.IsReverted = true;
            splitRecord.RevertTime = DateTime.Now;
            splitRecord.RevertOperator = App.User.UserName;
            await _splitPackageService.Db.Updateable(splitRecord).ExecuteCommandAsync();
            // 2. æ›´æ–°åº“存信息
            decimal originalStockQty = stockDetail.StockQuantity;
            decimal originalOutboundQty = stockDetail.OutboundQuantity;
            stockDetail.StockQuantity -= actualPickedQty;
            stockDetail.OutboundQuantity += actualPickedQty;
            _logger.LogInformation($"更新库存信息 - åº“存数量从 {originalStockQty} å‡å°‘到 {stockDetail.StockQuantity}");
            _logger.LogInformation($"更新库存信息 - å‡ºåº“数量从 {originalOutboundQty} å¢žåŠ åˆ° {stockDetail.OutboundQuantity}");
            // å‡†ç¡®åˆ¤æ–­åº“存状态
            if (stockDetail.StockQuantity <= 0)
            {
                stockDetail.Status = (int)StockStatusEmun.出库完成;
                _logger.LogInformation($"库存状态更新为出库完成");
            }
            else
            {
                stockDetail.Status = (int)StockStatusEmun.出库锁定;
                _logger.LogInformation($"库存状态保持为出库锁定");
            }
            await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            _logger.LogInformation($"分拣逻辑执行完成 - æ¡ç : {stockDetail.Barcode}");
            return new PickingResult
            {
                FinalLockInfo = lockInfo,
                ActualPickedQty = actualPickedQty
            };
        }
        private async Task<RevertPickingResult> RevertPickingData(Dt_PickingRecord pickingRecord)
        {
            _logger.LogInformation($"开始恢复拣选数据 - æ‹£é€‰è®°å½•ID: {pickingRecord.Id}, æ¡ç : {pickingRecord.Barcode}, æ‹£é€‰æ•°é‡: {pickingRecord.PickQuantity}");
            // 1. æ¢å¤é”å®šä¿¡æ¯
            var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .FirstAsync(x => x.Id == pickingRecord.OutStockLockId);
            if (lockInfo == null)
            {
                throw new InvalidOperationException($"未找到对应的锁定信息,ID: {pickingRecord.OutStockLockId}");
            }
            decimal originalPickedQty = lockInfo.PickedQty;
            decimal originalAssignQty = lockInfo.AssignQuantity; // è®°å½•原始分配数量
            // é‡è¦ä¿®å¤ï¼šåªæ¢å¤å·²æ‹£é€‰æ•°é‡ï¼Œä¸ä¿®æ”¹åˆ†é…æ•°é‡
            lockInfo.PickedQty -= pickingRecord.PickQuantity;
            // ç¡®ä¿å·²æ‹£é€‰æ•°é‡ä¸ä¼šä¸ºè´Ÿæ•°
            if (lockInfo.PickedQty < 0)
            {
                _logger.LogWarning($"已拣选数量出现负数,重置为0。原值: {lockInfo.PickedQty + pickingRecord.PickQuantity}, æ¢å¤æ•°é‡: {pickingRecord.PickQuantity}");
                lockInfo.PickedQty = 0;
            }
            _logger.LogInformation($"恢复锁定信息 - å·²æ‹£é€‰æ•°é‡ä»Ž {originalPickedQty} å‡å°‘到 {lockInfo.PickedQty}");
            _logger.LogInformation($"锁定信息分配数量保持不变: {originalAssignQty}");
            // æ¢å¤é”å®šçŠ¶æ€
            if (lockInfo.PickedQty <= 0)
            {
                lockInfo.Status = (int)OutLockStockStatusEnum.出库中;
                _logger.LogInformation($"锁定信息状态恢复为出库中");
            }
            else if (lockInfo.PickedQty < lockInfo.AssignQuantity)
            {
                lockInfo.Status = (int)OutLockStockStatusEnum.出库中; // éƒ¨åˆ†æ‹£é€‰çŠ¶æ€
                _logger.LogInformation($"锁定信息状态恢复为出库中(部分拣选)");
            }
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
            // 2. æ¢å¤åº“存信息
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == pickingRecord.Barcode);
            if (stockDetail == null)
            {
                throw new InvalidOperationException($"未找到对应的库存信息,条码: {pickingRecord.Barcode}");
            }
            decimal originalStockQty = stockDetail.StockQuantity;
            decimal originalOutboundQty = stockDetail.OutboundQuantity;
            stockDetail.StockQuantity += pickingRecord.PickQuantity;
            stockDetail.OutboundQuantity -= pickingRecord.PickQuantity;
            // ç¡®ä¿å‡ºåº“数量不会为负数
            if (stockDetail.OutboundQuantity < 0)
            {
                _logger.LogWarning($"出库数量出现负数,重置为0。原值: {stockDetail.OutboundQuantity + pickingRecord.PickQuantity}, æ¢å¤æ•°é‡: {pickingRecord.PickQuantity}");
                stockDetail.OutboundQuantity = 0;
            }
            _logger.LogInformation($"恢复库存信息 - åº“存数量从 {originalStockQty} å¢žåŠ åˆ° {stockDetail.StockQuantity}");
            _logger.LogInformation($"恢复库存信息 - å‡ºåº“数量从 {originalOutboundQty} å‡å°‘到 {stockDetail.OutboundQuantity}");
            // æ¢å¤åº“存状态
            if (stockDetail.StockQuantity > 0)
            {
                stockDetail.Status = (int)StockStatusEmun.出库锁定;
                _logger.LogInformation($"库存状态恢复为出库锁定");
            }
            await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            _logger.LogInformation($"恢复拣选数据完成 - æ¡ç : {pickingRecord.Barcode}");
            return new RevertPickingResult
            {
                LockInfo = lockInfo,
                StockDetail = stockDetail
            };
        }
        #endregion
        #region æ•°æ®æ›´æ–°æ–¹æ³•
        private async Task UpdateBatchAndOrderData(Dt_OutboundBatch batch, Dt_OutboundOrderDetail orderDetail, decimal pickedQty, string orderNo)
        {
            // æ›´æ–°æ‰¹æ¬¡å®Œæˆæ•°é‡
            _logger.LogInformation($"开始更新批次和订单数据 - æ‹£é€‰æ•°é‡: {pickedQty}");
            // é‡è¦ä¿®å¤ï¼šéªŒè¯åˆ†é…æ•°é‡ä¸ä¼šå˜æˆè´Ÿæ•°
            if (orderDetail.AllocatedQuantity < pickedQty)
            {
                throw new InvalidOperationException($"更新订单数据时分配数量不足,需要减少 {pickedQty},当前分配数量 {orderDetail.AllocatedQuantity}");
            }
            if (orderDetail.LockQuantity < pickedQty)
            {
                throw new InvalidOperationException($"更新订单数据时锁定数量不足,需要减少 {pickedQty},当前锁定数量 {orderDetail.LockQuantity}");
            }
            // 1. æ›´æ–°æ‰¹æ¬¡å®Œæˆæ•°é‡
            decimal originalBatchCompletedQty = batch.CompletedQuantity;
            batch.CompletedQuantity += pickedQty;
            _logger.LogInformation($"更新批次完成数量 - ä»Ž {originalBatchCompletedQty} å¢žåŠ åˆ° {batch.CompletedQuantity}");
            if (batch.CompletedQuantity >= batch.BatchQuantity)
            {
                batch.BatchStatus = (int)BatchStatusEnum.已完成;
                _logger.LogInformation($"批次状态更新为已完成");
            }
            await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync();
            // æ›´æ–°è®¢å•明细
            // 2. æ›´æ–°è®¢å•明细
            decimal originalOverOutQty = orderDetail.OverOutQuantity;
            decimal originalAllocatedQty = orderDetail.AllocatedQuantity;
            decimal originalLockQty = orderDetail.LockQuantity;
            orderDetail.OverOutQuantity += pickedQty;
            orderDetail.AllocatedQuantity -= pickedQty;
            // LockQuantity åŒæ­¥å‡å°‘
            orderDetail.LockQuantity = orderDetail.AllocatedQuantity;
            _logger.LogInformation($"更新订单明细 - å·²å‡ºåº“数量从 {originalOverOutQty} å¢žåŠ åˆ° {orderDetail.OverOutQuantity}");
            _logger.LogInformation($"更新订单明细 - å·²åˆ†é…æ•°é‡ä»Ž {originalAllocatedQty} å‡å°‘到 {orderDetail.AllocatedQuantity}");
            _logger.LogInformation($"更新订单明细 - é”å®šæ•°é‡ä»Ž {originalLockQty} å‡å°‘到 {orderDetail.LockQuantity}");
            await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
            // æ£€æŸ¥è®¢å•状态
            // 3. æ£€æŸ¥è®¢å•状态
            await CheckAndUpdateOrderStatus(orderNo);
            _logger.LogInformation($"批次和订单数据更新完成");
        }
        private async Task RevertBatchAndOrderData(Dt_PickingRecord pickingRecord, RevertPickingResult revertResult)
        {
            // æ¢å¤æ‰¹æ¬¡å®Œæˆæ•°é‡
            _logger.LogInformation($"开始恢复批次和订单数据");
            // 1. æ¢å¤æ‰¹æ¬¡å®Œæˆæ•°é‡
            var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>()
                .FirstAsync(x => x.BatchNo == revertResult.LockInfo.OutboundBatchNo); // ä½¿ç”¨ OutboundBatchNo
                .FirstAsync(x => x.BatchNo == revertResult.LockInfo.OutboundBatchNo);
            if (batch != null)
            {
                decimal originalCompletedQty = batch.CompletedQuantity;
                batch.CompletedQuantity -= pickingRecord.PickQuantity;
                _logger.LogInformation($"恢复批次完成数量 - ä»Ž {originalCompletedQty} å‡å°‘到 {batch.CompletedQuantity}");
                // é‡æ–°è®¡ç®—批次状态
                if (batch.CompletedQuantity <= 0)
                {
                    batch.BatchStatus = (int)BatchStatusEnum.分配中;
                    _logger.LogInformation($"批次状态恢复为分配中");
                }
                else if (batch.CompletedQuantity < batch.BatchQuantity)
                {
                    batch.BatchStatus = (int)BatchStatusEnum.执行中;
                    _logger.LogInformation($"批次状态恢复为执行中");
                }
                await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync();
            }
            // æ¢å¤è®¢å•明细
            // 2. æ¢å¤è®¢å•明细
            var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                .FirstAsync(x => x.Id == pickingRecord.OrderDetailId);
            orderDetail.OverOutQuantity -= pickingRecord.PickQuantity;
            orderDetail.AllocatedQuantity += pickingRecord.PickQuantity;
            await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
            if (orderDetail != null)
            {
                decimal originalOverOutQty = orderDetail.OverOutQuantity;
                decimal originalAllocatedQty = orderDetail.AllocatedQuantity;
                decimal originalLockQty = orderDetail.LockQuantity;
            // é‡æ–°æ£€æŸ¥è®¢å•状态
                // é‡è¦ä¿®å¤ï¼šåªæ¢å¤ç›¸å…³æ•°é‡ï¼Œåˆ†é…æ•°é‡ä¿æŒä¸å˜
                orderDetail.OverOutQuantity -= pickingRecord.PickQuantity;
                // æ³¨æ„ï¼šAllocatedQuantity å’Œ LockQuantity åœ¨å–消分拣时不应该改变
                _logger.LogInformation($"恢复订单明细 - å·²å‡ºåº“数量从 {originalOverOutQty} å‡å°‘到 {orderDetail.OverOutQuantity}");
                _logger.LogInformation($"订单明细分配数量保持不变: {originalAllocatedQty}");
                _logger.LogInformation($"订单明细锁定数量保持不变: {originalLockQty}");
                await UpdateBatchAllocateStatus(orderDetail);
                await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
            }
            // 3. é‡æ–°æ£€æŸ¥è®¢å•状态
            await CheckAndUpdateOrderStatus(pickingRecord.OrderNo);
            _logger.LogInformation($"恢复批次和订单数据完成");
        }
        private async Task UpdateBatchAllocateStatus(Dt_OutboundOrderDetail orderDetail)
        {
            if (orderDetail.AllocatedQuantity >= orderDetail.NeedOutQuantity)
            {
                orderDetail.BatchAllocateStatus = OrderDetailStatusEnum.AssignOver.ObjToInt();
            }
            else if (orderDetail.AllocatedQuantity > 0)
            {
                orderDetail.BatchAllocateStatus = OrderDetailStatusEnum.AssignOverPartial.ObjToInt();
            }
            else
            {
                orderDetail.BatchAllocateStatus = OrderDetailStatusEnum.New.ObjToInt();
            }
        }
        private async Task ReleaseLockAndStock(Dt_OutStockLockInfo lockInfo)
        {
@@ -1326,6 +1981,11 @@
                if (orderDetail != null)
                {
                    orderDetail.AllocatedQuantity -= returnedQty;
                    // LockQuantity åŒæ­¥å‡å°‘,保持与已分配数量一致
                    orderDetail.LockQuantity = orderDetail.AllocatedQuantity;
                    await UpdateBatchAllocateStatus(orderDetail);
                    await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
                }
            }
@@ -1340,7 +2000,7 @@
            return "WSLOT" + DateTime.Now.ToString("yyyyMMdd") + seq.ToString().PadLeft(5, '0');
        }
        private async Task RecordSplitHistory(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, decimal splitQty, string newBarcode)
        private async Task RecordSplitHistory(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, decimal splitQty, string newBarcode, bool isAutoSplit, decimal? originalStockQuantity = null)
        {
            var splitHistory = new Dt_SplitPackageRecord
            {
@@ -1352,7 +2012,11 @@
                NewBarcode = newBarcode,
                SplitQty = splitQty,
                SplitTime = DateTime.Now,
                Status = (int)SplitPackageStatusEnum.已拆包
                Status = (int)SplitPackageStatusEnum.已拆包,
                IsAutoSplit = isAutoSplit  ,
                // SplitType = isAutoSplit ? "自动拆包" : "手动拆包"
                OriginalStockQuantity = originalStockQuantity ?? stockDetail.StockQuantity,
                //RemainingStockQuantity = stockDetail.StockQuantity - splitQty
            };
            await _splitPackageService.Db.Insertable(splitHistory).ExecuteCommandAsync();
@@ -1414,9 +2078,44 @@
            };
        }
        #endregion
        #region DTOç±»
        /// <summary>
        /// æ¡ç çŠ¶æ€ä¿¡æ¯DTO
        /// </summary>
        public class BarcodeStatusInfoDto
        {
            public string Barcode { get; set; }
            public string OrderNo { get; set; }
            public bool IsOriginalBarcode { get; set; }
            public int SplitChainCount { get; set; }
            public bool HasBeenPicked { get; set; }
            public decimal TotalPickedQuantity { get; set; }
            public int PickRecordCount { get; set; }
            public int LockInfoStatus { get; set; }
            public decimal LockInfoPickedQty { get; set; }
            public decimal LockInfoAssignQty { get; set; }
            public decimal StockQuantity { get; set; }
            public int StockStatus { get; set; }
            public bool CanCancelSplit { get; set; }
            public bool NeedCancelPickFirst { get; set; }
            public List<string> OperationSuggestions { get; set; } = new List<string>();
        }
        public class PickedBarcodeInfo
        {
            public string Barcode { get; set; }
            public decimal PickedQty { get; set; }
            public int PickRecordCount { get; set; }
        }
        /// <summary>
        /// è‡ªåŠ¨æ‹†åŒ…ç»“æžœ
        /// </summary>
        public class AutoSplitResult
        {
            public string NewBarcode { get; set; }
            public decimal SplitQuantity { get; set; }
        }
        public class PickingResult
        {
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderDetailService.cs
@@ -417,6 +417,7 @@
                    // æ›´æ–°æ˜Žç»†çš„已分配数量
                    detail.AllocatedQuantity += assignQuantity;
                    detail.LockQuantity = detail.AllocatedQuantity;
                    remainingAllocate -= assignQuantity;
                    allocatedQuantity += assignQuantity;
                }
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderService.cs
@@ -35,6 +35,7 @@
            _materialUnitService = materialUnitService;
        }
        private int[] OrderTypes = new int[] { InOrderTypeEnum.AllocatOutbound.ObjToInt(), InOrderTypeEnum.InternalAllocat.ObjToInt(), InOrderTypeEnum.ReCheck.ObjToInt() };
        public async Task<WebResponseContent> ReceiveOutboundOrder(Dt_OutboundOrder model, int operateType)
        {
            try
@@ -71,11 +72,11 @@
                    var moveissueoStockResult = await _materialUnitService.ConvertFromToStockAsync(item.MaterielCode, item.BarcodeUnit, item.BarcodeMoveQty);
                    item.MoveQty = moveissueoStockResult.Quantity;
                }
                if (model.OrderType != InOrderTypeEnum.AllocatOutbound.ObjToInt() || model.OrderType != InOrderTypeEnum.InternalAllocat.ObjToInt())
                if (!OrderTypes.Contains(model.OrderType))
                {
                    model.OrderNo = CreateCodeByRule(nameof(RuleCodeEnum.OutboundOrderRule));
                }
                Db.InsertNav(model).Include(x => x.Details).ExecuteCommand();
                return WebResponseContent.Instance.OK();
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs
@@ -684,9 +684,7 @@
            if (newOverOutQuantity > currentOrderDetail.NeedOutQuantity)
            {
                _logger.LogError($"防超拣检查失败 - OrderDetailId: {orderDetailId}, å·²å‡ºåº“: {newOverOutQuantity}, éœ€æ±‚: {currentOrderDetail.NeedOutQuantity}, æœ¬æ¬¡åˆ†æ‹£: {pickedQty}");
                decimal adjustedQty = currentOrderDetail.NeedOutQuantity - currentOrderDetail.OverOutQuantity;
@@ -695,6 +693,7 @@
                    _logger.LogWarning($"自动调整分拣数量防止超拣:从{pickedQty}调整为{adjustedQty}");
                    newOverOutQuantity = currentOrderDetail.NeedOutQuantity;
                    newPickedQty = currentOrderDetail.PickedQty + adjustedQty;
                    pickedQty = adjustedQty; // æ›´æ–°å®žé™…拣选数量
                }
                else
                {
@@ -702,15 +701,21 @@
                }
            }
            // æ›´æ–°è®¢å•明细
            // æ›´æ–°è®¢å•明细数量和状态
            await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                .SetColumns(it => new Dt_OutboundOrderDetail
                {
                    PickedQty = newPickedQty,
                    OverOutQuantity = newOverOutQuantity,
                    OrderDetailStatus = newOverOutQuantity >= currentOrderDetail.NeedOutQuantity ?
                        OrderDetailStatusEnum.Over.ObjToInt() :
                        OrderDetailStatusEnum.Outbound.ObjToInt()
                })
                .Where(it => it.Id == orderDetailId)
                .ExecuteCommandAsync();
            // æ›´æ–°é”å®šæ•°é‡
            await UpdateOrderDetailLockQuantity(orderDetailId);
            // æ£€æŸ¥å¹¶æ›´æ–°è®¢å•状态
            await CheckAndUpdateOrderStatus(orderNo);
@@ -1112,12 +1117,28 @@
            if (newPickedQty < 0)
                throw new Exception($"取消分拣将导致已拣选数量({newPickedQty})为负数");
            // ç¡®å®šæ–°çš„状态
            int newStatus;
            if (newOverOutQuantity >= currentOrderDetail.NeedOutQuantity)
            {
                newStatus = OrderDetailStatusEnum.Over.ObjToInt();
            }
            else if (newOverOutQuantity > 0)
            {
                newStatus = OrderDetailStatusEnum.Outbound.ObjToInt();
            }
            else
            {
                newStatus = OrderDetailStatusEnum.New.ObjToInt();
            }
            // æ›´æ–°è®¢å•明细
            var updateResult = await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                .SetColumns(it => new Dt_OutboundOrderDetail
                {
                    PickedQty = newPickedQty,
                    OverOutQuantity = newOverOutQuantity
                    OverOutQuantity = newOverOutQuantity,
                    OrderDetailStatus = newStatus
                })
                .Where(it => it.Id == orderDetailId)
                .ExecuteCommandAsync();
@@ -1125,9 +1146,13 @@
            if (updateResult <= 0)
                throw new Exception("更新订单明细失败");
            // æ›´æ–°é”å®šæ•°é‡
            await UpdateOrderDetailLockQuantity(orderDetailId);
            _logger.LogInformation($"更新订单明细 - OrderDetailId: {orderDetailId}, " +
                                  $"扣减已出库: {cancelQty}, æ–°å·²å‡ºåº“: {newOverOutQuantity}, " +
                                  $"扣减已拣选: {cancelQty}, æ–°å·²æ‹£é€‰: {newPickedQty}");
                                  $"扣减已拣选: {cancelQty}, æ–°å·²æ‹£é€‰: {newPickedQty}, " +
                                  $"新状态: {newStatus}");
        }
        #endregion
@@ -1464,6 +1489,7 @@
                if (stockDetail != null)
                {
                    stockDetail.StockQuantity += returnQty;
                    // æ¢å¤åº“存状态
                    stockDetail.OutboundQuantity = Math.Max(0, stockDetail.OutboundQuantity - returnQty);
                    stockDetail.Status = StockStatusEmun.入库确认.ObjToInt();
@@ -1471,6 +1497,7 @@
                }
                else
                {
                    _logger.LogWarning($"未找到对应的库存明细 - æ¡ç : {lockInfo.CurrentBarcode}, åº“å­˜ID: {lockInfo.StockId}");
                    // åˆ›å»ºæ–°çš„库存记录
                    //var newStockDetail = new Dt_StockInfoDetail
                    //{
@@ -1490,7 +1517,34 @@
                    //};
                    //await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
                }
                try
                {
                    var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                        .FirstAsync(x => x.Id == lockInfo.OrderDetailId);
                    if (orderDetail != null)
                    {
                        decimal newLockQuantity = Math.Max(0, orderDetail.LockQuantity - returnQty);
                        await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                            .SetColumns(it => new Dt_OutboundOrderDetail
                            {
                                LockQuantity = newLockQuantity
                            })
                            .Where(it => it.Id == lockInfo.OrderDetailId)
                            .ExecuteCommandAsync();
                        _logger.LogInformation($"更新订单明细锁定数量 - OrderDetailId: {lockInfo.OrderDetailId}, " +
                                             $"扣减锁定: {returnQty}, æ–°é”å®šæ•°é‡: {newLockQuantity}");
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError($"更新订单明细锁定数量失败 - OrderDetailId: {lockInfo.OrderDetailId}, Error: {ex.Message}");
                }
            }
        }
        private async Task UpdateOrderDetailsOnReturn(List<Dt_OutStockLockInfo> remainingLocks)
@@ -1692,13 +1746,25 @@
                    .ToListAsync();
                bool allCompleted = true;
                bool hasPartial = false;
                bool hasLocked = false;
                foreach (var detail in orderDetails)
                {
                    if (detail.OverOutQuantity < detail.NeedOutQuantity)
                    {
                        allCompleted = false;
                        break;
                    }
                    if (detail.OrderDetailStatus == OrderDetailStatusEnum.Outbound.ObjToInt())
                    {
                        hasPartial = true;
                    }
                    //if (detail.OrderDetailStatus == OrderDetailStatusEnum.Locked.ObjToInt())
                    //{
                    //    hasLocked = true;
                    //}
                }
                var outboundOrder = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>()
@@ -1706,7 +1772,19 @@
                if (outboundOrder == null) return;
                int newStatus = allCompleted ? (int)OutOrderStatusEnum.出库完成 : (int)OutOrderStatusEnum.出库中;
                int newStatus;
                if (allCompleted)
                {
                    newStatus = (int)OutOrderStatusEnum.出库完成;
                }
                else if (hasPartial )
                {
                    newStatus = (int)OutOrderStatusEnum.出库中;
                }
                else
                {
                    newStatus = (int)OutOrderStatusEnum.未开始;
                }
                if (outboundOrder.OrderStatus != newStatus)
                {
@@ -1719,11 +1797,13 @@
                        .Where(x => x.OrderNo == orderNo)
                        .ExecuteCommandAsync();
                    _logger.LogInformation($"订单状态更新 - OrderNo: {orderNo}, æ—§çŠ¶æ€: {outboundOrder.OrderStatus}, æ–°çŠ¶æ€: {newStatus}");
                    // åªæœ‰æ­£å¸¸åˆ†æ‹£å®Œæˆæ—¶æ‰å‘MES反馈
                    //if (allCompleted && newStatus == (int)OutOrderStatusEnum.出库完成)
                    //{
                    //    await HandleOrderCompletion(outboundOrder, orderNo);
                    //}
                    if (allCompleted && newStatus == (int)OutOrderStatusEnum.出库完成)
                    {
                        await HandleOrderCompletion(outboundOrder, orderNo);
                    }
                }
            }
            catch (Exception ex)
@@ -1732,8 +1812,70 @@
            }
        }
        /// <summary>
        /// æ›´æ–°è®¢å•明细状态(基于已出库数量)
        /// </summary>
        private async Task UpdateOrderDetailStatus(int orderDetailId)
        {
            var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                .FirstAsync(x => x.Id == orderDetailId);
            if (orderDetail == null) return;
            int newStatus = orderDetail.OrderDetailStatus;
            if (orderDetail.OverOutQuantity >= orderDetail.NeedOutQuantity)
            {
                // å·²å‡ºåº“数量 >= éœ€æ±‚数量,标记为完成
                newStatus = OrderDetailStatusEnum.Over.ObjToInt();
            }
            else if (orderDetail.OverOutQuantity > 0)
            {
                // æœ‰éƒ¨åˆ†å‡ºåº“,但未完成
                newStatus = OrderDetailStatusEnum.Outbound.ObjToInt();
            }
            else if (orderDetail.LockQuantity > 0)
            {
                // æœ‰é”å®šæ•°é‡ï¼Œä½†æœªå‡ºåº“
                //newStatus = OrderDetailStatusEnum.Locked.ObjToInt();
            }
            else
            {
                // æ–°å»ºçŠ¶æ€
                newStatus = OrderDetailStatusEnum.New.ObjToInt();
            }
            // åªæœ‰çŠ¶æ€å‘ç”Ÿå˜åŒ–æ—¶æ‰æ›´æ–°
            if (orderDetail.OrderDetailStatus != newStatus)
            {
                await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                    .SetColumns(it => new Dt_OutboundOrderDetail
                    {
                        OrderDetailStatus = newStatus,
                    })
                    .Where(it => it.Id == orderDetailId)
                    .ExecuteCommandAsync();
            }
        }
        /// <summary>
        /// æ›´æ–°è®¢å•明细锁定数量
        /// </summary>
        private async Task UpdateOrderDetailLockQuantity(int orderDetailId)
        {
            var totalLockedQty = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.OrderDetailId == orderDetailId &&
                           x.Status == (int)OutLockStockStatusEnum.出库中)
                .SumAsync(x => x.AssignQuantity - x.PickedQty);
            await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                .SetColumns(it => new Dt_OutboundOrderDetail
                {
                    LockQuantity = totalLockedQty
                })
                .Where(it => it.Id == orderDetailId)
                .ExecuteCommandAsync();
        }
        private async Task UpdateOrderStatusForReturn(string orderNo)
        {
            try
@@ -2581,17 +2723,27 @@
                }
                //判断入库单据明细是否全部是完成状态
                bool inoderOver = inboundOrder.Details.Count() == inboundOrder.Details.Select(x => x.OrderDetailStatus == OrderDetailStatusEnum.Over.ObjToInt()).Count();
                int e = inboundOrder.Details.Count();
                int w = inboundOrder.Details.Where(x => x.OrderDetailStatus == OrderDetailStatusEnum.Over.ObjToInt()).Count();
                bool inoderOver = inboundOrder.Details.Count() == inboundOrder.Details.Where(x => x.OrderDetailStatus == OrderDetailStatusEnum.Over.ObjToInt()).Count();
                if (inoderOver)
                {
                    inboundOrder.OrderStatus = InOrderStatusEnum.入库完成.ObjToInt();
                }
                else
                {
                    inboundOrder.OrderStatus = InOrderStatusEnum.入库中.ObjToInt();
                }
                //判断出库单据明细是否全部是完成状态
                bool outOderOver = outboundOrder.Details.Count() == outboundOrder.Details.Select(x => x.OrderDetailStatus == OrderDetailStatusEnum.Over.ObjToInt()).Count();
                bool outOderOver = outboundOrder.Details.Count() == outboundOrder.Details.Where(x => x.OrderDetailStatus == OrderDetailStatusEnum.Over.ObjToInt()).Count();
                if (outOderOver)
                {
                    outboundOrder.OrderStatus = OutOrderStatusEnum.出库完成.ObjToInt();
                }
                else
                {
                    outboundOrder.OrderStatus = OutOrderStatusEnum.出库中.ObjToInt();
                }
                //数据处理
                _unitOfWorkManage.BeginTran();
                _inboundOrderDetailService.UpdateData(inboundOrderDetails);
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_StockService/StockInfoService.cs
@@ -186,7 +186,7 @@
        public List<Dt_StockInfo> GetStockInfos(string materielCode, string lotNo, string supplyCode, List<string> locationCodes)
        {
            var query = Db.Queryable<Dt_StockInfo>()
             .Where(x => locationCodes.Contains(x.LocationCode)
             .Where(x => locationCodes.Contains(x.LocationCode) && x.StockStatus==StockStatusEmun.入库完成.ObjToInt()
             //  && x.StockStatus == (int)StockStatusEmun.正常)
             ).Includes(x => x.Details);
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -593,6 +593,7 @@
                bool allCompleted = true;
                foreach (var detail in orderDetails)
                {
                    _logger.LogInformation($"TaskService  HandleOutboundOrderToMESCompletion: {outboundOrder.OrderNo} , {detail.NeedOutQuantity}");
                    if (detail.OverOutQuantity < detail.NeedOutQuantity)
                    {
                        allCompleted = false;
@@ -662,7 +663,6 @@
                            allocatefeedmodel.Details.Add(detailModel);
                        }
                        var result = await _invokeMESService.FeedbackAllocate(allocatefeedmodel);
                        if (result != null && result.code == 200)
                        {
@@ -728,7 +728,6 @@
                            feedmodel.details.Add(detailModel);
                        }
                        var result = await _invokeMESService.FeedbackOutbound(feedmodel);
                        if (result != null && result.code == 200)
                        {
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Inbound.cs
@@ -32,7 +32,12 @@
                Dt_Task dbtask = Repository.Db.Queryable<Dt_Task>().Where(x => x.PalletCode == palletCode).OrderByDescending(x=>x.CreateDate).First();
                if (dbtask != null)
                {
                    if (dbtask.TaskType == TaskTypeEnum.Outbound.ObjToInt() || dbtask.TaskType == TaskTypeEnum.OutAllocate.ObjToInt())
                    _logger.LogInformation(dbtask.TaskType.ToString());
                    if (dbtask.TaskType == TaskTypeEnum.Outbound.ObjToInt())
                    {
                        return WebResponseContent.Instance.Error($"出库待分拣任务");
                    }
                    else if (dbtask.TaskType == TaskTypeEnum.OutAllocate.ObjToInt())
                    {
                        return WebResponseContent.Instance.Error($"出库待分拣任务");
                    }