647556386
6 天以前 f29f358f589f5b1c2589c07b65845650be7aafef
代码优化
已修改11个文件
613 ■■■■ 文件已修改
项目代码/WIDESEA_WMSClient/src/extension/basic/locationInfo.js 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/outbound/extend/NoStockOut.vue 174 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/stock/stockView.js 84 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/stock/stockView.vue 64 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutboundPickingService.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_StockService/StockInfoDetailService.cs 60 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_StockService/StockViewService.cs 93 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_SystemService/Sys_DictionaryService.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_SystemService/Sys_RoleService.cs 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Outbound/OutboundPickingController.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/basic/locationInfo.js
@@ -48,21 +48,7 @@
        }
      }
      this.columns.push({
        field: '操作',
        title: '操作',
        width: 90,
        fixed: 'right',
        align: 'center',
        formatter: (row) => {
          return (
            '<i style="cursor: pointer;color: #2d8cf0;"class="el-icon-view">查看明细</i>'
          );
        },
        click: (row) => {
          this.$refs.gridBody.open(row);
        }
      });
    },
    onInited() {
      //框架初始化配置后
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/extend/NoStockOut.vue
@@ -10,7 +10,12 @@
    >
      <div>
        <!-- å•据输入区域(支持扫码) -->
        <el-form :inline="true" :model="orderForm" style="margin-bottom: 20px; align-items: flex-end;">
        <el-form
          :inline="true"
          :model="orderForm"
          style="margin-bottom: 20px; align-items: flex-end;"
          @submit.prevent
        >
          <el-form-item label="出库单据:" name="outboundOrderNo">
            <el-input
              v-model="orderForm.outboundOrderNo"
@@ -18,25 +23,35 @@
              clearable
              style="width: 220px; margin-right: 10px;"
              @input="handleOutboundInput"
              @keyup.enter="focusPurchaseInput"
              @keyup.enter="(e) => {
                e.stopPropagation(); // é˜»æ­¢äº‹ä»¶å†’泡
                e.preventDefault(); // é˜»æ­¢é»˜è®¤è¡Œä¸º
                focusBarcodeInputDirectly();
              }"
              ref="outboundInputRef"
            ></el-input>
          </el-form-item>
          <el-form-item label="采购单据:" name="purchaseOrderNo">
            <el-input
              v-model="orderForm.purchaseOrderNo"
              placeholder="请输入或扫描采购单据号"
              placeholder="扫码条码后自动填充"
              clearable
              style="width: 220px; margin-right: 10px;"
              @input="handlePurchaseInput"
              @keyup.enter="focusBarcodeInput"
              readonly
              ref="purchaseInputRef"
            ></el-input>
          </el-form-item>
        </el-form>
        <!-- ä¸Šæ–¹è¾“入框 -->
        <el-form :inline="true" :model="formData" ref="formRef" style="margin-bottom: 20px; align-items: flex-end;">
        <el-form
          :inline="true"
          :model="formData"
          ref="formRef"
          style="margin-bottom: 20px; align-items: flex-end;"
          @submit.prevent
        >
          <el-form-item
            label="扫描条码:"
            style="width: 80%"
@@ -47,10 +62,10 @@
              ref="barcodeInputRef"
              v-model="formData.barcode"
              placeholder="请使用扫码枪扫描条码,或手动输入"
              @keyup.enter="handleScan"
              @keydown.enter="debouncedHandleScan"
              autofocus
              class="custom-input"
              :disabled="!orderForm.outboundOrderNo || !orderForm.purchaseOrderNo"
              :disabled="!orderForm.outboundOrderNo || loading"
            ></el-input>
          </el-form-item>
          <el-form-item>
@@ -59,7 +74,7 @@
              size="small" 
              @click="handleScan" 
              class="custom-button"
              :disabled="!orderForm.outboundOrderNo || !orderForm.purchaseOrderNo || loading"
              :disabled="!orderForm.outboundOrderNo || loading"
            >
              <Search /> ç¡®è®¤æ‰«æ
            </el-button>
@@ -87,7 +102,7 @@
                  </div>
                </transition-group>
                <div class="empty-tip" v-if="scannedBarcodes.length === 0">
                  <span>暂无扫描记录,请先输入单据后扫描条码</span>
                  <span>暂无扫描记录,请先输入出库单据后扫描条码</span>
                </div>
              </el-scrollbar>
            </div>
@@ -106,7 +121,17 @@
          >
            <Check /> æäº¤å‡ºåº“
          </el-button>
          <el-button type="text" size="small" @click="showDetailBox = false" class="cancel-btn" :disabled="loading">
          <el-button
            type="text"
            size="small"
            @click="(e) => {
              e.stopPropagation();
              e.preventDefault();
              showDetailBox = false;
            }"
            class="cancel-btn"
            :disabled="loading"
          >
            å–消
          </el-button>
        </div>
@@ -116,9 +141,9 @@
</template>
<script setup>
import { ref, reactive, onMounted, nextTick } from 'vue';
import { ref, reactive, onMounted, nextTick, watch } from 'vue';
import { ElMessage } from 'element-plus';
import { Search } from '@element-plus/icons-vue';
import { Search, Check } from '@element-plus/icons-vue';
import VolBox from "@/components/basic/VolBox.vue";
import http from '@/api/http';
@@ -134,6 +159,8 @@
});
const scannedBarcodes = ref([]);
const loading = ref(false);
// æ–°å¢žï¼šå­˜å‚¨é¦–次扫描的采购单号(用于一致性校验)
const firstPurchaseOrderNo = ref(null);
// æ¨¡æ¿å¼•用
const formRef = ref(null);
@@ -148,6 +175,25 @@
  });
});
// ç›‘听扫描列表,若为空则重置首次采购单号
watch(scannedBarcodes, (newVal) => {
  if (newVal.length === 0) {
    firstPurchaseOrderNo.value = null;
    orderForm.purchaseOrderNo = ""; // åŒæ­¥æ¸…空采购单输入框
  }
}, { deep: true });
// ç®€å•防抖函数(无需依赖lodash)
const debounce = (fn, delay = 100) => {
  let timer = null;
  return (...args) => {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
};
// æ‰“开弹窗
const open = () => {
  showDetailBox.value = true;
@@ -155,6 +201,8 @@
  formData.barcode = "";
  orderForm.outboundOrderNo = "";
  orderForm.purchaseOrderNo = "";
  // é‡ç½®é¦–次采购单号
  firstPurchaseOrderNo.value = null;
  nextTick(() => {
    outboundInputRef.value?.focus();
  });
@@ -162,43 +210,55 @@
// å‡ºåº“单输入处理(扫码或手动输入)
const handleOutboundInput = (value) => {
  // æ‰«ç æžªè¾“入通常会自动触发enter事件,这里主要处理手动输入的情况
  if (value && value.trim()) {
    // å¯ä»¥åœ¨è¿™é‡Œæ·»åŠ å‡ºåº“å•å·çš„æ ¼å¼éªŒè¯é€»è¾‘
    // å‡ºåº“单号格式验证逻辑(按需保留)
  }
};
// é‡‡è´­å•输入处理(扫码或手动输入)
const handlePurchaseInput = (value) => {
  if (value && value.trim()) {
    // å¯ä»¥åœ¨è¿™é‡Œæ·»åŠ é‡‡è´­å•å·çš„æ ¼å¼éªŒè¯é€»è¾‘
    // é‡‡è´­å•号格式验证逻辑(按需保留)
  }
};
// ç„¦ç‚¹è·³è½¬å‡½æ•°
const focusPurchaseInput = () => {
// ç›´æŽ¥è·³è½¬åˆ°æ¡ç è¾“入框(无需先填采购单)
const focusBarcodeInputDirectly = () => {
  if (orderForm.outboundOrderNo.trim()) {
    purchaseInputRef.value?.focus();
    barcodeInputRef.value?.focus();
  } else {
    ElMessage.warning("请先输入有效的出库单据号");
  }
};
const focusBarcodeInput = () => {
  if (orderForm.purchaseOrderNo.trim()) {
    barcodeInputRef.value?.focus();
  } else {
    ElMessage.warning("请先输入有效的采购单据号");
/**
 * æ ¹æ®æ¡ç æŸ¥è¯¢é‡‡è´­å•接口(完全对齐示例请求格式)
 * @param {string} barcode æ¡ç 
 * @returns {Promise<string>} é‡‡è´­å•号
 */
const getPurchaseOrderByBarcode = async (barcode) => {
  // å®Œå…¨æŒ‰ç…§ç¤ºä¾‹æ ¼å¼ï¼šurl拼接参数 + ç¬¬äºŒä¸ªå‚数传提示文本
  const res = await http.post(`/api/OutboundPicking/GetPurchaseOrderByBarcode?barCode=${encodeURIComponent(barcode)}`, "查询采购单中...");
  if (res.status !== true) {
    throw new Error(res.message || "查询采购单失败");
  }
  if (!res.data?.purchaseOrderNo) {
    throw new Error("未查询到该条码对应的采购单");
  }
  return res.data.purchaseOrderNo;
};
// æ‰«ææ¡ç å¤„理
// æ‰«ææ¡ç æ ¸å¿ƒé€»è¾‘
const handleScan = async () => {
  if (!formRef.value) return;
  // éªŒè¯æ¡ç å¿…å¡«
  await formRef.value.validateField('barcode');
  const barcode = formData.barcode.trim();
  const outboundOrderNo = orderForm.outboundOrderNo.trim();
  
  // æ£€æŸ¥æ¡ç æ˜¯å¦å·²æ‰«æ
  if (scannedBarcodes.value.some(item => item.barcode === barcode)) {
    ElMessage.warning(`条码 ${barcode} å·²æ‰«æè¿‡ï¼Œè¯·å‹¿é‡å¤æ‰«æ`);
    formData.barcode = "";
@@ -208,33 +268,72 @@
  try {
    loading.value = true;
    // è¿™é‡Œä¿ç•™äº†åŽŸæœ‰çš„æ¡ç éªŒè¯æŽ¥å£ï¼Œä½ å¯ä»¥æ ¹æ®å®žé™…éœ€æ±‚ä¿®æ”¹æˆ–ä¿ç•™
    const res = await http.post("/api/OutboundPicking/BarcodeValidate", {
      outOder: orderForm.outboundOrderNo, // æ³¨æ„ï¼šè¿™é‡ŒçŽ°åœ¨ä¼ é€’çš„æ˜¯å•æ®å·å­—ç¬¦ä¸²ï¼Œè€Œä¸æ˜¯ID
      inOder: orderForm.purchaseOrderNo,  // æ³¨æ„ï¼šè¿™é‡ŒçŽ°åœ¨ä¼ é€’çš„æ˜¯å•æ®å·å­—ç¬¦ä¸²ï¼Œè€Œä¸æ˜¯ID
    // æ­¥éª¤1:仅传条码查询采购单(使用示例格式的请求)
    const purchaseOrderNo = await getPurchaseOrderByBarcode(barcode);
    // æ ¸å¿ƒæ ¡éªŒï¼šé‡‡è´­å•一致性检查
    if (firstPurchaseOrderNo.value) {
      // éžé¦–次扫描,校验采购单号是否一致
      if (purchaseOrderNo !== firstPurchaseOrderNo.value) {
        throw new Error(`当前条码对应的采购单【${purchaseOrderNo}】与首次扫描的采购单【${firstPurchaseOrderNo.value}】不一致,禁止扫描!`);
      }
    } else {
      // é¦–次扫描,记录采购单号
      firstPurchaseOrderNo.value = purchaseOrderNo;
    }
    // èµ‹å€¼é‡‡è´­å•到输入框
    orderForm.purchaseOrderNo = purchaseOrderNo;
    ElMessage.success(`成功查询到采购单:${purchaseOrderNo}`);
    // æ­¥éª¤2:调用原有条码验证接口(如需对齐格式可同步修改,此处保留原有格式)
    const validateRes = await http.post("/api/OutboundPicking/BarcodeValidate", {
      outOder: outboundOrderNo,
      inOder: purchaseOrderNo,
      barCode: barcode
    });
    if (res.status === true) {
    if (validateRes.status === true) {
      scannedBarcodes.value.push({ barcode });
      ElMessage.success("扫描成功");
      formData.barcode = "";
    } else {
      ElMessage.error("扫描失败:" + (res.message || '验证失败'));
      ElMessage.error("扫描失败:" + (validateRes.message || '条码验证失败'));
    }
  } catch (error) {
    ElMessage.error("扫描验证异常:" + error.message);
    // æ•获采购单不一致等错误并提示
    ElMessage.error(error.message);
    // æ¸…空当前输入框,聚焦条码输入框
    formData.barcode = "";
    nextTick(() => barcodeInputRef.value?.focus());
  } finally {
    loading.value = false;
    nextTick(() => barcodeInputRef.value?.focus());
    // å¼ºåˆ¶èšç„¦æ¡ç è¾“入框,避免焦点跳到弹窗外
    nextTick(() => {
      if (barcodeInputRef.value) {
        barcodeInputRef.value.focus();
        // æ¸…空输入框选中状态(扫码枪可能残留选中)
        if (barcodeInputRef.value.input) {
          barcodeInputRef.value.input.select = () => {};
        }
      }
    });
  }
};
// å¸¦é˜²æŠ–和事件拦截的扫描处理(适配扫码枪)
const debouncedHandleScan = debounce(async (e) => {
  // é˜»æ­¢äº‹ä»¶å†’泡和默认行为
  e.stopPropagation();
  e.preventDefault();
  await handleScan();
}, 100);
// ç§»é™¤å•条扫描记录
const removeItem = async (index, barcode) => {
  try {
    loading.value = true;
    // è¿™é‡Œä¿ç•™äº†åŽŸæœ‰çš„åˆ é™¤æ¡ç æŽ¥å£ï¼Œä½ å¯ä»¥æ ¹æ®å®žé™…éœ€æ±‚ä¿®æ”¹æˆ–ä¿ç•™
    const res = await http.post("/api/OutboundPicking/DeleteBarcode", {
      outOder: orderForm.outboundOrderNo,
      inOder: orderForm.purchaseOrderNo,
@@ -244,6 +343,11 @@
    if (res.status === true) {
      scannedBarcodes.value.splice(index, 1);
      ElMessage.success("删除成功");
      // è‹¥åˆ é™¤åŽæ— æ¡ç ï¼Œè‡ªåŠ¨é‡ç½®é¦–æ¬¡é‡‡è´­å•å·å’Œé‡‡è´­å•è¾“å…¥æ¡†
      if (scannedBarcodes.value.length === 0) {
        firstPurchaseOrderNo.value = null;
        orderForm.purchaseOrderNo = "";
      }
    } else {
      ElMessage.error("删除失败:" + (res.message || '删除失败'));
    }
@@ -265,7 +369,6 @@
  try {
    loading.value = true;
    // è¿™é‡Œä¿ç•™äº†åŽŸæœ‰çš„æäº¤æŽ¥å£ï¼Œæ³¨æ„å‚æ•°çŽ°åœ¨ä¼ é€’çš„æ˜¯å•æ®å·å­—ç¬¦ä¸²
    const res = await http.post("/api/OutboundPicking/NoStockOutSubmit", {
      OutOderSubmit: orderForm.outboundOrderNo,
      InOderSubmit: orderForm.purchaseOrderNo,
@@ -275,6 +378,9 @@
    if (res.status === true) {
      ElMessage.success("出库提交成功");
      showDetailBox.value = false;
      // æäº¤æˆåŠŸåŽé‡ç½®çŠ¶æ€
      firstPurchaseOrderNo.value = null;
      scannedBarcodes.value = [];
    } else {
      ElMessage.error("出库提交失败:" + (res.message || '提交失败'));
    }
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/stock/stockView.js
@@ -31,7 +31,7 @@
      //       });
      //   }
      // }
      // this.columns.forEach(column => {
      this.columns.forEach(column => {
      //   if (column.field == 'materielCode') {
      //     column.formatter = (row) => {
      //       var str = '';
@@ -74,22 +74,74 @@
      //     }
      //   }
      //   if (column.field == 'sumStock') {
      //     column.formatter = (row) => {
      //       if (row.details.length > 0) {
      //         var sum = 0;
      //         const closestDate = row.details
      //           .map(x => {
      //             sum += (x.stockQuantity)
      //           })
      //         return '<span style="color: #F56C6C">' + sum + row.details[0].unit + '</span>';
      //       } else {
      //         return '<span style="color: #F56C6C">' + "1个" + '</span>';
      //       }
        if (column.field == 'sumStock') {
          column.formatter = (row) => {
            if (row.details.length > 0) {
              var sum = 0;
              const closestDate = row.details
                .map(x => {
                  sum += (x.stockQuantity)
                })
              return '<span style="color: #F56C6C">' + sum + row.details[0].unit + '</span>';
            } else {
              return '<span style="color: #F56C6C">' + "1个" + '</span>';
            }
      //     }
      //   }
      // })
          }
        }
        if (column.field === 'orderStatistics') {
          column.formatter = (row) => {
            // æ ¡éªŒdetails是否存在且有数据
            if (row.details && row.details.length > 0) {
              // æŒ‰materielCode分组统计stockQuantity总和,并记录单位(取第一个非空单位)
              const materielSumMap = row.details.reduce((acc, item) => {
                const materielCode = item.materielCode || '未知物料';
                const quantity = Number(item.stockQuantity) || 0;
                const unit = item.unit || ''; // èŽ·å–å•ä½ï¼Œæ— åˆ™ä¸ºç©º
                // ç´¯åŠ æ•°é‡
                acc[materielCode] = {
                  total: (acc[materielCode]?.total || 0) + quantity,
                  unit: acc[materielCode]?.unit || unit // ä¿ç•™ç¬¬ä¸€ä¸ªéžç©ºå•位
                };
                return acc;
              }, {});
              // æ¯ä¸ªç‰©æ–™é¡¹ç”Ÿæˆç‹¬ç«‹div,跨行显示(包含单位)
              const displayItems = Object.entries(materielSumMap).map(([code, data]) => {
                // å¤„理单位显示:有单位则加空格显示,无则不显示
                const unitText = data.unit ? ` ${data.unit}` : '';
                return `<div style="line-height: 1.5; white-space: normal;">${code}:${data.total}${unitText}</div>`;
              });
              const displayContent = displayItems.join('');
              return `<div style="color: #F56C6C; white-space: normal; word-break: break-all;">${displayContent}</div>`;
            } else {
              return '<span style="color: #F56C6C">空箱</span>';
            }
          };
        }
        if (column.field == 'stockOrderNo') {
          column.formatter = (row) => {
            // æœ‰æ˜Žç»†æ•°æ®æ—¶å¤„理
            if (row.details && row.details.length > 0) {
              // æå–所有非空的orderNO并去重
              const uniqueOrderNOs = [...new Set(
                row.details.map(item => item.orderNo).filter(no => no) // è¿‡æ»¤ç©ºå•据号
              )];
              // æœ‰æœ‰æ•ˆå•据号则换行显示,否则显示默认文本
              if (uniqueOrderNOs.length > 0) {
                return `<span style="color: #F56C6C">${uniqueOrderNOs.join('<br>')}</span>`;
              } else {
                return '<span style="color: #F56C6C">暂无单据</span>';
              }
            } else {
              // æ— æ˜Žç»†æ•°æ®æ—¶æ˜¾ç¤ºé»˜è®¤æ–‡æœ¬
              return '<span style="color: #F56C6C">暂无单据</span>';
            }
          }
        }
      })
    },
    onInited() {
      //框架初始化配置后
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/stock/stockView.vue
@@ -37,7 +37,7 @@
    const searchFormOptions = ref([
      [
        { title: "托盘编号", field: "palletCode",type: "like" },
        // { title: "货位编号", field: "locationCode",type: "like" },
        { title: "货位编号", field: "locationCode",type: "like" },
        { title: "货位状态", field: "locationStatus" ,type: "selectList",dataKey: "locationStatusEnum",data: [],},
        { title: "库存状态", field: "stockStatus",type: "selectList",dataKey: "stockStatusEmun",data: [],},
      ],
@@ -77,16 +77,10 @@
        field: "locationName",
        title: "货位名称",
        type: "string",
        width: 270,
        width: 150,
        align: "left",
      },
      {
        field: "warehouseId",
        title: "所属仓库",
        type: "string",
        width: 80,
        align: "left",
      },
      {
        field: "roadwayNo",
        title: "巷道编号",
@@ -96,15 +90,22 @@
        hidden:true
      },
      {
        field: "materielCode",
        title: "所含物料编号",
        field: "orderStatistics",
        title: "所含物料明细",
        type: "string",
        width: 120,
        width: 240,
        align: "left",
      },
      {
        field: "sumStock",
        title: "总库存",
        type: "string",
        width: 140,
        align: "left",
      },
      {
        field: "stockOrderNo",
        title: "所含单据",
        type: "string",
        width: 140,
        align: "left",
@@ -125,6 +126,45 @@
        align: "left",
      },
      {
        field: "enalbeStatus",
        title: "货位禁用状态",
        type: "string",
        width: 140,
        align: "left",
        bind: { key: "enableStatusEnum", data: [] },
      },
      {
        field: "locationStatus",
        title: "货位状态",
        type: "string",
        width: 140,
        align: "left",
        bind: { key: "locationStatusEnum", data: [] },
      },
      {
        field: "locationType",
        title: "货位类型",
        type: "string",
        width: 140,
        align: "left",
        bind:{key: "locationTypeEnum", data: []}
      },
      {
        field: "stockStatus",
        title: "库存状态",
        type: "string",
        width: 140,
        align: "left",
        bind:{key: "stockStatusEmun", data: []}
      },
      {
        field: "warehouseId",
        title: "所属仓库",
        type: "string",
        width: 80,
        align: "left",
      },
      {
        field: "modifier",
        title: "修改人",
        type: "string",
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutboundPickingService.cs
@@ -51,5 +51,12 @@
        WebResponseContent UnPalletQuantity(string orderNo);
        WebResponseContent BarcodeMaterielGroup(BarcodeMaterielGroupDTO materielGroupDTO);
        /// <summary>
        /// æ¡ç æŸ¥è¯¢å…¥åº“单
        /// </summary>
        /// <param name="barcode"></param>
        /// <returns></returns>
        public WebResponseContent GetPurchaseOrderByBarcode(string barcode);
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs
@@ -2948,6 +2948,30 @@
                return WebResponseContent.Instance.Error(ex.Message);
            }
        }
        public WebResponseContent GetPurchaseOrderByBarcode(string barcode)
        {
            try
            {
                Dt_InboundOrderDetail inboundOrderDetail = _stockInfoDetailService.Db.Queryable<Dt_InboundOrderDetail>().Where(x=>x.Barcode == barcode&& x.OrderDetailStatus !=OrderDetailStatusEnum.Over.ObjToInt()).First();
                if(inboundOrderDetail == null)
                {
                    return WebResponseContent.Instance.Error($"未找到该条码{barcode}的入库明细或者明细状态已入智仓完成");
                }
                Dt_InboundOrder inboundOrder = _inboundOrderRepository.QueryFirst(x => x.Id == inboundOrderDetail.OrderId && x.OrderStatus != InOrderStatusEnum.入库完成.ObjToInt());
                if (inboundOrder == null)
                {
                    return WebResponseContent.Instance.Error($"该入库单{inboundOrder}状态已入智仓完成");
                }
                var resultData = new { purchaseOrderNo = inboundOrder.UpperOrderNo };
                return WebResponseContent.Instance.OK("查询采购单成功", data: resultData);
            }
            catch(Exception ex)
            {
                return WebResponseContent.Instance.Error(ex.Message);
            }
        }
        #endregion
        public WebResponseContent UnPalletQuantity(string orderNo)
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_StockService/StockInfoDetailService.cs
@@ -2,6 +2,8 @@
using WIDESEA_Core;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Core.BaseServices;
using WIDESEA_Core.Enums;
using WIDESEA_Core.Helper;
using WIDESEA_Core.HttpContextUser;
using WIDESEA_DTO.Stock;
using WIDESEA_IStockService;
@@ -31,16 +33,53 @@
        public PageGridData<StockInfoDetailWithPalletDto> GetPageData2(PageDataOptions options)
        {
            PageGridData<Dt_StockInfoDetail> lists =  base.GetPageData (options);
            // 1. èŽ·å–åŸºç¡€åˆ†é¡µæ•°æ®
            PageGridData<Dt_StockInfoDetail> pageData = base.GetPageData(options);
            List<Dt_StockInfoDetail> filteredDetails = pageData.Rows.ToList(); // å…ˆæ‹·è´åŽŸå§‹æ•°æ®
            List<int> stockIds = lists.Rows.Select(detail => detail.StockId).Distinct().ToList();
            var stocks= _stockinfoRepository.QueryData(x => stockIds.Contains(x.Id)).ToList();
            if (!string.IsNullOrEmpty(options.Wheres))
            {
                try
                {
                    List<SearchParameters> searchParametersList = options.Wheres.DeserializeObject<List<SearchParameters>>();
                    if (searchParametersList?.Any() == true)
                    {
                        foreach (var param in searchParametersList)
                        {
                            // åŒ¹é…æ‰˜ç›˜ç¼–号查询条件(小写字段名)
                            if (param.Name.Equals(nameof(Dt_StockInfo.PalletCode).FirstLetterToLower(), StringComparison.OrdinalIgnoreCase)
                                && !string.IsNullOrEmpty(param.Value?.ToString()))
                            {
                                // ä¼˜åŒ–:批量查询(如果有多个托盘码,这里也可以扩展)
                                string palletCode = param.Value.ToString().Trim();
                                var targetStock = _stockinfoRepository.QueryFirst(x => x.PalletCode == palletCode);
 
                                // ç©ºå€¼æ ¡éªŒï¼šæœªæ‰¾åˆ°å¯¹åº”托盘的库存,直接过滤为空
                                if (targetStock != null)
                                {
                                    filteredDetails = filteredDetails.Where(x => x.StockId == targetStock.Id).ToList();
                                }
                                else
                                {
                                    filteredDetails = new List<Dt_StockInfoDetail>();
                                }
                                break; // å•个托盘码查询,匹配后退出循环
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                }
            }
            List<StockInfoDetailWithPalletDto> dtoList = lists.Rows
            List<int> stockIds = filteredDetails.Select(detail => detail.StockId).Distinct().ToList();
            var stockDict = _stockinfoRepository.QueryData(x => stockIds.Contains(x.Id))
                                                .ToDictionary(x => x.Id, x => x.PalletCode ?? "无托盘编号");
            List<StockInfoDetailWithPalletDto> dtoList = filteredDetails
                .Select(detail => new StockInfoDetailWithPalletDto
                {
                    Id = detail.Id,
                    StockId = detail.StockId,
                    MaterielCode = detail.MaterielCode,
@@ -64,13 +103,16 @@
                    CreateDate = detail.CreateDate,
                    Modifier = detail.Modifier,
                    ModifyDate = detail.ModifyDate,
                    PalletCode= stocks
                        .FirstOrDefault(stock => stock.Id == detail.StockId)?
                        .PalletCode ?? "无托盘编号"
                    PalletCode = stockDict.TryGetValue(detail.StockId, out var palletCode) ? palletCode : "无托盘编号"
                })
                .ToList();
            return new PageGridData<StockInfoDetailWithPalletDto> { Rows = dtoList, Total = lists.Total, Summary = lists.Summary  };
            return new PageGridData<StockInfoDetailWithPalletDto>
            {
                Rows = dtoList,
                Total = pageData.Total,
                Summary = pageData.Summary
            };
        }
    }
 }
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_StockService/StockViewService.cs
@@ -5,6 +5,7 @@
using WIDESEA_Core;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Core.DB.Models;
using WIDESEA_Core.Enums;
using WIDESEA_Core.Helper;
//using WIDESEA_Core.HostedService;
using WIDESEA_Core.Utilities;
@@ -27,30 +28,71 @@
        public virtual PageGridData<StockViewDTO> GetPageData(PageDataOptions options)
        {
            string where = options.ValidatePageOptions(typeof(StockViewDTO).GetProperties());
            //获取排序字段
            //Dictionary<string, OrderByType> orderbyDic = options.GetPageDataSort(typeof(StockViewDTO).GetProperties());
            //List<OrderByModel> orderByModels = new List<OrderByModel>();
            //foreach (var item in orderbyDic)
            //{
            //    OrderByModel orderByModel = new OrderByModel()
            //    {
            //        FieldName = item.Key,
            //        OrderByType = item.Value
            //    };
            //    orderByModels.Add(orderByModel);
            //}
            int totalCount = 0;
            try
            {
            ISugarQueryable<Dt_StockInfo> sugarQueryable1 = _dbBase.Queryable<Dt_StockInfo>().Includes(x=>x.Details);
                if (!string.IsNullOrEmpty(options.Wheres))
                {
                    try
                    {
                        List<SearchParameters> searchParametersList = options.Wheres.DeserializeObject<List<SearchParameters>>();
                        if (searchParametersList?.Any() == true)
                        {
                            foreach (var param in searchParametersList)
                            {
                                switch (param.Name)
                                {
                                    case var name when name == nameof(Dt_StockInfoDetail.MaterielCode).FirstLetterToLower():
                                        if (!string.IsNullOrEmpty(param.Value?.ToString()))
                                        {
                                            sugarQueryable1 = sugarQueryable1
                                                .Where(x => x.Details.Any(v => v.MaterielCode.Contains(param.Value.ToString())));
                                        }
                                        break;
                                    case var name when name == nameof(Dt_StockInfoDetail.BatchNo).FirstLetterToLower():
                                        if (!string.IsNullOrEmpty(param.Value?.ToString()))
                                        {
                                            sugarQueryable1 = sugarQueryable1
                                                .Where(x => x.Details.Any(v => v.BatchNo.Contains(param.Value.ToString())));
                                        }
                                        break;
                                    case var name when name == nameof(Dt_StockInfo.LocationCode).FirstLetterToLower():
                                        if (!string.IsNullOrEmpty(param.Value?.ToString()))
                                        {
                                            sugarQueryable1 = sugarQueryable1
                                                .Where(x => x.LocationCode == param.Value.ToString());
                                        }
                                        break;
                                    case var name when name == nameof(Dt_StockInfo.CreateDate).FirstLetterToLower():
                                        if (DateTime.TryParse(param.Value?.ToString(), out DateTime minDate))
                                        {
                                            LinqExpressionType expressionType = param.DisplayType.GetLinqCondition();
                                            if (expressionType == LinqExpressionType.ThanOrEqual)
                                            {
                                                sugarQueryable1 = sugarQueryable1.Where(x => x.CreateDate >= minDate);
                                            }
                                            else if (expressionType == LinqExpressionType.LessThanOrEqual)
                                            {
                                                sugarQueryable1 = sugarQueryable1.Where(x => x.CreateDate <= minDate);
                                            }
                                        }
                                        break;
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                    }
                }
                EntityProperties.ValidatePageOptions(options, ref sugarQueryable1);
            ISugarQueryable<Dt_LocationInfo> sugarQueryable = _dbBase.Queryable<Dt_LocationInfo>();
            ISugarQueryable<Dt_StockInfoDetail> sugarQueryable2 = _dbBase.Queryable<Dt_StockInfoDetail>();
            List<StockViewDTO> list = sugarQueryable1.InnerJoin(sugarQueryable, (b, a) => a.LocationCode == b.LocationCode).WhereIF(!string.IsNullOrEmpty(where), where).Select((b, a) => new StockViewDTO
                ISugarQueryable<StockViewDTO> list = sugarQueryable1.InnerJoin(sugarQueryable, (b, a) => a.LocationCode == b.LocationCode).Select((b, a) => new StockViewDTO
            {
                LocationCode = b.LocationCode,
                Column = a.Column,
                    WarehouseId = b.WarehouseId,
                CreateDate = b.CreateDate,
                Creater = b.Creater,
                Depth = a.Depth,
@@ -68,11 +110,16 @@
                StockId = b.Id,
                StockStatus = b.StockStatus,
                Details = b.Details,
            }).ToPageList(options.Page, options.Rows, ref totalCount);
                });
                int totalCount = 0;
                var stockViewDTOs = list.ToPageList(options.Page, options.Rows, ref totalCount);
                return new PageGridData<StockViewDTO>(totalCount, stockViewDTOs);
            return new PageGridData<StockViewDTO>(totalCount, list);
            }
            catch (Exception ex)
            {
            }
            return new PageGridData<StockViewDTO>();
        }
        public virtual object GetDetailPage(PageDataOptions pageData)
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_SystemService/Sys_DictionaryService.cs
@@ -24,6 +24,7 @@
using WIDESEA_DTO.System;
using WIDESEA_ISystemService;
using WIDESEA_Model.Models;
using WIDESEA_Model.Models.Basic;
namespace WIDESEA_SystemService
{
@@ -211,10 +212,67 @@
            {
                switch (key)
                {
                    default:
                        return result;
                    case "warehouses":
                        {
                            List<object> data = new List<object>();
                            {
                                List<Dt_WarehouseArea> warehouses = BaseDal.Db.Queryable<Dt_WarehouseArea>().ToList();
                                int index = 0;
                                foreach (var item in warehouses)
                                {
                                    data.Add(new { key = item.Code, value = item.Name });
                                    index++;
                }
            }
                            result = new VueDictionaryDTO { DicNo = key, Config = "", Data = data };
                        }
                        break;
                    case "locationTypes":
                        {
                            List<object> data = new List<object>();
                            {
                                List<Dt_LocationType> warehouses = BaseDal.Db.Queryable<Dt_LocationType>().ToList();
                                int index = 0;
                                foreach (var item in warehouses)
                                {
                                    data.Add(new { key = item.LocationType, value = item.LocationTypeDesc });
                                    index++;
                                }
                            }
                            result = new VueDictionaryDTO { DicNo = key, Config = "", Data = data };
                        }
                        break;
                    case "stockStatusEmun":
                        {
                            List<object> data = new List<object>();
                            Type type = typeof(StockStatusEmun);
                            List<int> enums = Enum.GetValues(typeof(StockStatusEmun)).Cast<int>().ToList();
                            int index = 0;
                            foreach (var item in enums)
                            {
                                FieldInfo? fieldInfo = typeof(StockStatusEmun).GetField(((StockStatusEmun)item).ToString());
                                DescriptionAttribute? description = fieldInfo.GetCustomAttribute<DescriptionAttribute>();
                                if (description != null)
                                {
                                    data.Add(new { key = item.ToString(), value = description.Description });
                                }
                                else
                                {
                                    data.Add(new { key = item.ToString(), value = item.ToString() });
                                }
                                index++;
                            }
                            result = new VueDictionaryDTO { DicNo = key, Config = "", Data = data };
                        }
                        break;
                }
                return result;
            }
            catch (Exception ex)
            {
                return null;
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_SystemService/Sys_RoleService.cs
@@ -187,12 +187,11 @@
                //当前用户的权限
                List<Permissions> permissions = _MenuService.GetPermissions(App.User.RoleId);
                List<int> menuIds = _MenuService.Repository.QueryData(x => x.MenuId, x => x.MenuType == 1);
                List<int> originalMeunIds = new List<int>();
                //被分配角色的权限
                List<Sys_RoleAuth> roleAuths = _RoleAuthService.Repository.QueryData(x => x.RoleId == roleId && menuIds.Contains(x.MenuId));
                List<Sys_RoleAuth> roleAuths = _RoleAuthService.Repository.QueryData(x => x.RoleId == roleId);
                List<Sys_RoleAuth> updateAuths = new List<Sys_RoleAuth>();
                List<Sys_RoleAuth> addAuths = new List<Sys_RoleAuth>();
                foreach (UserPermissionDTO x in userPermissions)
                {
                    Permissions per = permissions.FirstOrDefault(p => p.MenuId == x.Id);
@@ -208,7 +207,21 @@
                    var auth = roleAuths.Where(r => r.MenuId == x.Id).Select(s => new { s.AuthId, s.AuthValue, s.MenuId }).FirstOrDefault();
                    string newAuthValue = string.Join(",", arr);
                    //权限没有发生变化则不处理
                    if (auth == null || auth.AuthValue != newAuthValue)
                    if (auth == null)
                    {
                        addAuths.Add(new Sys_RoleAuth()
                        {
                            RoleId = roleId,
                            MenuId = x.Id,
                            AuthValue = string.Join(",", arr),
                            AuthId = auth == null ? 0 : auth.AuthId,
                            ModifyDate = DateTime.Now,
                            Modifier = App.User.UserName,
                            CreateDate = DateTime.Now,
                            Creater = App.User.UserName
                        });
                    }
                    else if (auth.AuthValue != newAuthValue)
                    {
                        updateAuths.Add(new Sys_RoleAuth()
                        {
@@ -231,7 +244,7 @@
                //更新权限
                _RoleAuthService.Repository.UpdateData(updateAuths);
                //新增的权限
                _RoleAuthService.Repository.AddData(updateAuths);
                _RoleAuthService.Repository.AddData(addAuths);
                //获取权限取消的权限
                int[] authIds = roleAuths.Where(x => userPermissions.Select(u => u.Id)
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Outbound/OutboundPickingController.cs
@@ -145,5 +145,11 @@
        {
            return await Service.NoStockOutSubmit(noStockOutSubmit);
        }
        [HttpPost, HttpGet, Route("GetPurchaseOrderByBarcode"), AllowAnonymous]
        public WebResponseContent GetPurchaseOrderByBarcode(string barcode)
        {
            return Service.GetPurchaseOrderByBarcode(barcode);
        }
    }
}