wangxinhui
2026-03-19 5f53f4af369a1c7a8a09eb1f45c20a8581d107a9
Merge branch 'master' of http://115.159.85.185:8098/r/ZhiHuiQiCe/LongDeLiLiKu
已修改10个文件
749 ■■■■ 文件已修改
项目代码/WMS/WMSClient/src/extension/outbound/extend/outSGOrderDetail.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS/WMSClient/src/views/Home.vue 510 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS/WMSClient/src/views/outbound/outSGOrder.vue 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS/WMSClient/src/views/outbound/outSGOrderDetail.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS/WMSServices/WIDESEA_BasicService/Base/LocationInfoService.cs 41 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS/WMSServices/WIDESEA_Model/Models/Outbound/Dt_OutSGOrder.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS/WMSServices/WIDESEA_OutboundService/OutSGOrderDetailService.cs 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS/WMSServices/WIDESEA_OutboundService/OutSGOrderService.cs 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS/WMSServices/WIDESEA_TaskInfoService/TaskService_Outbound.cs 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS/WMSServices/WIDESEA_WMSServer/Controllers/Basic/LocationInfoController.cs 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ÏîÄ¿´úÂë/WMS/WMSClient/src/extension/outbound/extend/outSGOrderDetail.vue
@@ -438,10 +438,10 @@
      return ''; // é»˜è®¤æ— é¢œè‰²
    },
    isMaterialShortage(row) {
      // åˆ¤æ–­ç‰©æ–™æ˜¯å¦ç¼ºæ–™ï¼Œä¸Žä¸»é¡µé¢é€»è¾‘保持一致
      // æ˜Žç»†çŠ¶æ€ä¸º3表示缺料,或isLackMaterial为true/"是"
      return row.outSGOrderDetailStatus === 3 ||
             (row.isLackMaterial && (typeof row.isLackMaterial === 'boolean' ? row.isLackMaterial : row.isLackMaterial === '是'));
      // åˆ¤æ–­å½“前行物料是否缺料,只根据当前行的明细状态判断
      // æ˜Žç»†çŠ¶æ€ä¸º3表示缺料,支持数字和字符串类型
      const status = parseInt(row.outSGOrderDetailStatus);
      return status === 3;
    },
  },
};
ÏîÄ¿´úÂë/WMS/WMSClient/src/views/Home.vue
@@ -27,9 +27,9 @@
                class="full-width">
                <el-option v-for="item in warehouseList" :value="item" :label="item" :key="item"></el-option>
              </el-select>
              <h4>货位排</h4>
              <el-select size="mini" @change="SCChange" v-model="Area.row" placeholder="请选择排" class="full-width">
                <el-option v-for="item in scList" :value="item" :label="'第' + item + '排'" :key="item"></el-option>
              <h4>巷道</h4>
              <el-select size="mini" @change="SCChange" v-model="Area.roadwayNo" placeholder="请选择巷道" class="full-width">
                <el-option v-for="item in scList" :value="item" :label="item" :key="item"></el-option>
              </el-select>
              <el-button type="success" class="refresh-btn" @click="GetViewData">
                åˆ·æ–°
@@ -50,25 +50,37 @@
        <!-- è´§ä½å±•示区域 -->
        <div v-if="loading" class="loading-container">
          <el-icon class="is-loading">
            <Loading />
          </el-icon>
          <span>加载中...</span>
        </div>
        <div v-else-if="locationData.length === 0" class="empty-container">
        <div
          v-else-if="(!locationData.row1 || locationData.row1.length === 0) && (!locationData.row2 || locationData.row2.length === 0)"
          class="empty-container">
          <el-empty description="暂无数据" />
        </div>
        <div v-else>
          <div class="location-view">
            <div class="layer-container" v-for="(item, index) in locationData" :key="index">
              <h3 class="layer-title">第{{ item.layer }}层</h3>
              <div class="row">
            <!-- ç¬¬1排(东面) -->
            <div class="roadway-section">
              <h2 class="roadway-title">{{ Area.roadwayNo }} ç¬¬1巷道第1排(东面:北>南)</h2>
              <div class="row" v-for="(item, index) in locationData.row1" :key="'row1-' + index">
                <div class="location-cell" :style="{ 'background-color': GetBgColor(column) }"
                  v-for="(column, index) in item.locationObj" :key="index" @mouseenter="showTooltip(column, $event)"
                  @mouseleave="hideTooltip">
                  {{ column.row }}-{{ column.column }}-{{ column.layer }}
                  v-for="(column, colIndex) in item.locationObj" :key="'row1-' + item.layer + '-' + colIndex"
                  @mouseenter="showTooltip(column, $event)" @mouseleave="hideTooltip">
                  {{ getRoadwayNo(column) }}-{{ column.column }}-{{ column.layer }}
                </div>
              </div>
            </div>
            <!-- ç¬¬2排(西面) -->
            <div class="roadway-section">
              <h2 class="roadway-title">{{ Area.roadwayNo }} ç¬¬1巷道第2排(西面:北>南)</h2>
              <div class="row" v-for="(item, index) in locationData.row2" :key="'row2-' + index">
                <div class="location-cell" :style="{ 'background-color': GetBgColor(column) }"
                  v-for="(column, colIndex) in item.locationObj" :key="'row2-' + item.layer + '-' + colIndex"
                  @mouseenter="showTooltip(column, $event)" @mouseleave="hideTooltip">
                  {{ getRoadwayNo(column) }}-{{ column.column }}-{{ column.layer }}
                </div>
              </div>
            </div>
@@ -79,36 +91,30 @@
            top: tooltipPosition.y + 'px',
          }">
            <div v-if="currentLocation">
              <p><strong>仓库:</strong>{{ Area.warehouse || "未选择" }}</p>
              <p><strong>货位号:</strong>{{ currentLocation.locationCode }}</p>
              <!-- <p><strong>料箱号:</strong>{{ currentLocation.barCode ? currentLocation.barCode : "无料箱" }}</p> -->
              <p><strong>仓库:</strong><span>{{ Area.warehouse || "未选择" }}</span></p>
              <p><strong>货位号:</strong><span>{{ currentLocation.locationCode }}</span></p>
              <!-- æ ¹æ®ä»“库类型显示不同的标签和内容 -->
              <p v-if="Area.warehouse === '原料库'">
                <strong>RFID:</strong>{{ this.rfidData[currentLocation.locationCode] || "无" }}
              </p>
              <p v-else-if="Area.warehouse === '成品库'">
                <strong>托盘号:</strong>{{ this.rfidData[currentLocation.locationCode] || "无" }}
              </p>
              <p v-else>
                <strong>标识:</strong>{{ this.rfidData[currentLocation.locationCode] || "无" }}
              </p>
              <!-- åŽŸæ–™åº“æ˜¾ç¤ºå†…å®¹ -->
              <div v-if="Area.warehouse === '原料库'">
                <p><strong>纸卷:</strong><span>{{ currentLocation.paperRoll || "无" }}</span></p>
                <p><strong>门幅:</strong><span>{{ currentLocation.width ? currentLocation.width + "m" : "无" }}</span></p>
                <p><strong>条码:</strong><span>{{ currentLocation.barcode || "无" }}</span></p>
                <p><strong>RFID:</strong><span>{{ currentLocation.rfid || "无" }}</span></p>
              </div>
              <!-- æˆå“åº“显示内容 -->
              <div v-else-if="Area.warehouse === '成品库'">
                <p><strong>成品名称:</strong><span>{{ currentLocation.productName || currentLocation.paperRoll || "无" }}</span></p>
                <p><strong>成品数量:</strong><span>{{ currentLocation.quantity || currentLocation.width || "无" }}</span></p>
                <p><strong>托盘号:</strong><span>{{ currentLocation.rfid || currentLocation.rfidCode ||
                  this.rfidData[currentLocation.locationCode] || "无" }}</span></p>
              </div>
              <p>
                <strong>排列层:</strong> {{ currentLocation.row }}排{{
                  currentLocation.column
                }}列{{ currentLocation.layer }}层
                <strong>排列层:</strong> <span>{{ currentLocation.row }}排{{ currentLocation.column }}列{{ currentLocation.layer
                }}层</span>
              </p>
              <p><strong>状态:</strong> {{ getStatusText(currentLocation) }}</p>
              <!-- <p>
                <strong>禁用:</strong>
                {{ currentLocation.location_lock == 3 ? "是" : "否" }}
              </p> -->
              <!-- <p v-if="currentLocation.location_state > 0">
                <strong>物料编码:</strong>
                {{ currentLocation.material_code || "无" }}
              </p>
              <p v-if="currentLocation.location_state > 0">
                <strong>数量:</strong> {{ currentLocation.quantity || "无" }}
              </p> -->
              <p><strong>状态:</strong> <span>{{ getStatusText(currentLocation) }}</span></p>
              <p><strong>入库日期:</strong><span>{{ currentLocation.inDate ? new Date(currentLocation.inDate).toLocaleString() :
                "无" }}</span></p>
            </div>
          </div>
        </div>
@@ -116,31 +122,37 @@
    </template>
<script>
import { ElButton, Loading } from "element-plus";
import { ElButton } from "element-plus";
export default {
  data() {
      return {
        slectData: [],
        scList: [],
      allRoadways: [], // ä¿å­˜æ‰€æœ‰å··é“列表
        warehouseList: ["原料库", "成品库"],
        warehouseMap: {
          "原料库": 1,
          "成品库": 2
        },
      // å··é“类型映射
      roadwayTypeMap: {
        "原料库": ["SC01_YL", "SC02_YL", "SC03_YLDual", "SC04_YLDual", "SC05_YLDual"],
        "成品库": ["SC01_CP", "SC02_CP", "SC03_CP", "SC04_CP", "SC05_CP"]
      },
        Area: {
          warehouse: "原料库",
          row: "",
        roadwayNo: "",
          shelf_code: ""
        },
        mian_height: "",
        loading: false,
        infoMsg: [
          { bgcolor: "lightgreen", msg: "空闲", state: 0 },
          { bgcolor: "orange", msg: "有货", state: 100 },
          { bgcolor: "#2BB3D5", msg: "锁定", state: 1 },
          { bgcolor: "yellow", msg: "空闲锁定", state: 20 },
          { bgcolor: "purple", msg: "有货锁定", state: 10 },
        { bgcolor: "#f0f0f0", msg: "空闲", state: 0 },
        { bgcolor: "#90ee90", msg: "有货", state: 100 },
        { bgcolor: "#a0a0a0", msg: "空闲锁定", state: 20 },
        { bgcolor: "#228b22", msg: "有货锁定", state: 10 },
        { bgcolor: "#2BB3D5", msg: "专用货位", state: 3 },
        ],
        locationData: [],
        showTooltipFlag: false,
@@ -153,16 +165,25 @@
    GetBgColor() {
      return (col) => {
        var bgColor = "";
        //优先显示禁用状态
        if (col.location_lock > 0) {
          this.infoMsg.forEach((el) => {
            if (el.state === col.location_lock) {
              bgColor = el.bgcolor;
            }
          });
        }
        else {
          return "lightgreen";
        //根据状态设置颜色
        switch (col.location_lock) {
          case 0: //空闲
            bgColor = "#f0f0f0"; //很浅灰
            break;
          case 20: //空闲锁定
            bgColor = "#a0a0a0"; //深灰
            break;
          case 100: //有货
            bgColor = "#90ee90"; //浅绿
            break;
          case 10: //有货锁定
            bgColor = "#228b22"; //深绿
            break;
          case 3: //专用库位
            bgColor = "#2BB3D5"; //蓝色(不变)
            break;
          default:
            bgColor = "#f0f0f0"; //默认很浅灰
        }
        return bgColor;
      };
@@ -187,16 +208,25 @@
          console.log("RFID查询API返回结果:", response);
          if (response.status && response.data) {
            console.log(`成功获取${response.data.length}条RFID记录`);
            // æ›´æ–°rfidData映射
            // å°†RFID数据转换为以locationCode为键的映射
            const rfidDataMap = {};
            response.data.forEach((item, index) => {
              try {
                // æ£€æŸ¥å­—段名,支持大小写两种格式
                const locationCode = item.LocationCode || item.locationCode;
                const rfidCode = item.RfidCode || item.rfidCode;
                if (item && locationCode !== undefined) {
                  console.log(`更新RFID数据: ${locationCode} -> ${rfidCode}`);
                  this.rfidData[locationCode] = rfidCode;
                  rfidDataMap[locationCode] = {
                    paperRoll: item.PaperRoll || item.paperRoll || "",
                    productName: item.ProductName || item.productName || item.PaperRoll || item.paperRoll || "", // ä¼˜å…ˆä½¿ç”¨ProductName字段
                    width: item.Width || item.width || 0,
                    quantity: item.Quantity || item.quantity || item.Width || item.width || 0, // ä¼˜å…ˆä½¿ç”¨Quantity字段
                    barcode: item.Barcode || item.barcode || "",
                    rfid: item.RfidCode || item.rfidCode || "",
                    rfidCode: item.RfidCode || item.rfidCode || "", // ç¡®ä¿rfidCode字段存在
                    status: item.Status || item.status || 0,
                    inDate: item.InDate || item.inDate || null
                  };
                } else {
                  console.warn(`跳过无效的RFID数据项(${index}):`, item);
                }
@@ -204,7 +234,58 @@
                console.error(`处理RFID数据项(${index})时出错:`, error, item);
              }
            });
            console.log("更新后的rfidData:", this.rfidData);
            console.log("RFID数据映射:", rfidDataMap);
            // æ›´æ–°è´§ä½æ•°æ®ï¼Œå°†RFID信息合并到对应的货位对象中
            // å¤„理第1排数据
            if (this.locationData.row1) {
              this.locationData.row1.forEach(layer => {
                layer.locationObj.forEach(location => {
                  const rfidInfo = rfidDataMap[location.locationCode];
                  if (rfidInfo) {
                    // æ›´æ–°è´§ä½å¯¹è±¡çš„字段
                    location.paperRoll = rfidInfo.paperRoll;
                    location.productName = rfidInfo.productName; // æ–°å¢žæˆå“åç§°å­—段
                    location.width = rfidInfo.width;
                    location.quantity = rfidInfo.quantity; // æ–°å¢žæˆå“æ•°é‡å­—段
                    location.barcode = rfidInfo.barcode;
                    location.rfid = rfidInfo.rfid;
                    location.rfidCode = rfidInfo.rfidCode; // æ–°å¢žrfidCode字段
                    location.status = rfidInfo.status;
                    location.inDate = rfidInfo.inDate;
                    // æ›´æ–°rfidData映射(保持向后兼容)
                    this.rfidData[location.locationCode] = rfidInfo.rfid || rfidInfo.rfidCode; // ä½¿ç”¨rfid或rfidCode作为托盘号
                  }
                });
              });
            }
            // å¤„理第2排数据
            if (this.locationData.row2) {
              this.locationData.row2.forEach(layer => {
                layer.locationObj.forEach(location => {
                  const rfidInfo = rfidDataMap[location.locationCode];
                  if (rfidInfo) {
                    // æ›´æ–°è´§ä½å¯¹è±¡çš„字段
                    location.paperRoll = rfidInfo.paperRoll;
                    location.productName = rfidInfo.productName; // æ–°å¢žæˆå“åç§°å­—段
                    location.width = rfidInfo.width;
                    location.quantity = rfidInfo.quantity; // æ–°å¢žæˆå“æ•°é‡å­—段
                    location.barcode = rfidInfo.barcode;
                    location.rfid = rfidInfo.rfid;
                    location.rfidCode = rfidInfo.rfidCode; // æ–°å¢žrfidCode字段
                    location.status = rfidInfo.status;
                    location.inDate = rfidInfo.inDate;
                    // æ›´æ–°rfidData映射(保持向后兼容)
                    this.rfidData[location.locationCode] = rfidInfo.rfid || rfidInfo.rfidCode; // ä½¿ç”¨rfid或rfidCode作为托盘号
                  }
                });
              });
            }
            console.log("更新后的货位数据:", this.locationData);
          } else {
            console.log("RFID查询API返回状态失败或数据为空");
          }
@@ -219,6 +300,7 @@
      this.loading = true;
      this.rfidData = {}; // æ¸…空之前的RFID数据
      this.locationData = { row1: [], row2: [] }; // åˆå§‹åŒ–新的数据结构
      let warehouseId = 0;
      if (this.Area.warehouse) {
@@ -226,21 +308,86 @@
        console.log(`当前选择的仓库: ${this.Area.warehouse}, å¯¹åº”çš„warehouseId: ${warehouseId}`);
      }
      console.log(`调用GetLocationStatus API: row=${_this.Area.row}, warehouseId=${warehouseId}`);
      console.log(`调用GetLocationStatus API: roadwayNo=${_this.Area.roadwayNo}, warehouseId=${warehouseId}`);
      
      this.http
        .post(`/api/LocationInfo/GetLocationStatus?row=${_this.Area.row}&warehouseId=${warehouseId}`, {}, "查询中")
        .post(`/api/LocationInfo/GetLocationStatus?roadwayNo=${_this.Area.roadwayNo}&warehouseId=${warehouseId}`, {}, "查询中")
        .then((x) => {
          console.log("GetLocationStatus API返回结果:", x);
          this.locationData = x.data || [];
          if (x.data) {
            this.locationData = x.data;
            // å¯¹ç¬¬1排数据进行排序
            if (this.locationData.row1) {
              // æŒ‰å±‚号从高到低排序
              this.locationData.row1.sort((a, b) => parseInt(b.layer) - parseInt(a.layer));
              // å¯¹æ¯å±‚内的货位按列号从北到南排序(01-64)
              this.locationData.row1.forEach(layer => {
                layer.locationObj.sort((a, b) => {
                  // ç¡®ä¿åˆ—号按数字顺序排列
                  const colA = parseInt(a.column);
                  const colB = parseInt(b.column);
                  return colA - colB;
                });
              });
            }
            // å¯¹ç¬¬2排数据进行同样的排序
            if (this.locationData.row2) {
              // æŒ‰å±‚号从高到低排序
              this.locationData.row2.sort((a, b) => parseInt(b.layer) - parseInt(a.layer));
              // å¯¹æ¯å±‚内的货位按列号从北到南排序(01-64)
              this.locationData.row2.forEach(layer => {
                layer.locationObj.sort((a, b) => {
                  // ç¡®ä¿åˆ—号按数字顺序排列
                  const colA = parseInt(a.column);
                  const colB = parseInt(b.column);
                  return colA - colB;
                });
              });
            }
          }
          
          // æå–所有货位编号
          let locationCodes = [];
          this.locationData.forEach(layer => {
          // æå–第1排货位编号
          if (this.locationData.row1) {
            this.locationData.row1.forEach(layer => {
            layer.locationObj.forEach(location => {
              locationCodes.push(location.locationCode);
                // åˆå§‹åŒ–每个货位的RFID相关字段
                if (!location.paperRoll) location.paperRoll = "无";
                if (!location.productName) location.productName = "无";
                if (!location.width) location.width = 0;
                if (!location.quantity) location.quantity = 0;
                if (!location.barcode) location.barcode = "无";
                if (!location.rfid) location.rfid = "无";
                if (!location.rfidCode) location.rfidCode = "无";
                if (!location.inDate) location.inDate = null;
            });
          });
          }
          // æå–第2排货位编号
          if (this.locationData.row2) {
            this.locationData.row2.forEach(layer => {
              layer.locationObj.forEach(location => {
                locationCodes.push(location.locationCode);
                // åˆå§‹åŒ–每个货位的RFID相关字段
                if (!location.paperRoll) location.paperRoll = "无";
                if (!location.productName) location.productName = "无";
                if (!location.width) location.width = 0;
                if (!location.quantity) location.quantity = 0;
                if (!location.barcode) location.barcode = "无";
                if (!location.rfid) location.rfid = "无";
                if (!location.rfidCode) location.rfidCode = "无";
                if (!location.inDate) location.inDate = null;
              });
            });
          }
          
          console.log(`从货位数据中提取到${locationCodes.length}个货位编号`);
          console.log("提取的货位编号:", locationCodes);
@@ -261,9 +408,24 @@
    SCChange() {
      this.GetViewData();
    },
    // æ ¹æ®ä»“库类型过滤巷道列表
    filterRoadwaysByWarehouse(warehouse) {
      const allowedRoadways = this.roadwayTypeMap[warehouse] || [];
      this.scList = this.allRoadways.filter(roadway => allowedRoadways.includes(roadway));
      // å¦‚果当前选中的巷道不在过滤后的列表中,重置选择
      if (this.Area.roadwayNo && !this.scList.includes(this.Area.roadwayNo)) {
        this.Area.roadwayNo = this.scList[0] || "";
      } else if (!this.Area.roadwayNo && this.scList.length > 0) {
        this.Area.roadwayNo = this.scList[0];
      }
    },
    // åˆ‡æ¢ä»“库
    onWarehouseChange() {
      // åˆ‡æ¢ä»“库时重置排选择
      // æ ¹æ®ä»“库类型过滤巷道列表
      this.filterRoadwaysByWarehouse(this.Area.warehouse);
      // é‡ç½®æŽ’选择后获取数据
      this.GetViewData();
    },
    showTooltip(location, event) {
@@ -283,21 +445,127 @@
    },
    getStatusText(location) {
      // if (location.location_lock === 3) return "禁用";
      if (location.location_lock === 0) return "空闲";
      if (location.location_lock === 1) return "锁定";
      if (location.location_lock === 100) return "有货";
      if (location.location_lock === 20) return "空闲锁定";
      if (location.location_lock === 10) return "有货锁定";
      // if (location.location_state > 0 && location.location_state < 100)
      //   return "锁定";
      return "其他";
    },
    getRoadwayNo(column) {
      // æ ¹æ®ç”¨æˆ·è¦æ±‚的规则生成标准化巷道号
      // å··é“/排:从东到西 11、12、21、22、31、32、41、42、51、52
      // åˆ—:从北到南 01—64
      // å±‚:01—10
      if (column && column.locationCode && column.row) {
        // æå–原始巷道号,如SC01_YL
        const locationCode = column.locationCode;
        const row = column.row;
        const originalRoadwayNo = locationCode.split('-')[0];
        // æ ¹æ®åŽŸå§‹å··é“å·å’Œè¡Œå·æ˜ å°„åˆ°æ ‡å‡†åŒ–å··é“å·
        const standardizedRoadwayMap = {
          // åŽŸæ–™åº“å··é“æ˜ å°„
          // SC01_YL, ç¬¬1排 -> 11,第2排 -> 12
          'SC01_YL': {
            '01': '11',
            '1': '11',
            '02': '12',
            '2': '12'
          },
          // SC02_YL, ç¬¬1排 -> 21,第2排 -> 22
          'SC02_YL': {
            '01': '21',
            '1': '21',
            '02': '22',
            '2': '22'
          },
          // SC03_YLDual, ç¬¬1排 -> 31,第2排 -> 32
          'SC03_YLDual': {
            '01': '31',
            '1': '31',
            '02': '32',
            '2': '32'
          },
          // SC04_YLDual, ç¬¬1排 -> 41,第2排 -> 42
          'SC04_YLDual': {
            '01': '41',
            '1': '41',
            '02': '42',
            '2': '42'
          },
          // SC05_YLDual, ç¬¬1排 -> 51,第2排 -> 52
          'SC05_YLDual': {
            '01': '51',
            '1': '51',
            '02': '52',
            '2': '52'
          },
          // æˆå“åº“巷道映射
          // SC01_CP, ç¬¬1排 -> 11,第2排 -> 12
          'SC01_CP': {
            '01': '11',
            '1': '11',
            '02': '12',
            '2': '12'
          },
          // SC02_CP, ç¬¬1排 -> 21,第2排 -> 22
          'SC02_CP': {
            '01': '21',
            '1': '21',
            '02': '22',
            '2': '22'
          },
          // SC03_CP, ç¬¬1排 -> 31,第2排 -> 32
          'SC03_CP': {
            '01': '31',
            '1': '31',
            '02': '32',
            '2': '32'
          },
          // SC04_CP, ç¬¬1排 -> 41,第2排 -> 42
          'SC04_CP': {
            '01': '41',
            '1': '41',
            '02': '42',
            '2': '42'
          },
          // SC05_CP, ç¬¬1排 -> 51,第2排 -> 52
          'SC05_CP': {
            '01': '51',
            '1': '51',
            '02': '52',
            '2': '52'
          }
        };
        // ä»Žcolumn.row获取行号,注意column.row是带前导零的字符串
        const rowStr = row;
        // æŸ¥æ‰¾æ˜ å°„
        if (standardizedRoadwayMap[originalRoadwayNo]) {
          const rowMap = standardizedRoadwayMap[originalRoadwayNo];
          if (rowMap[rowStr]) {
            return rowMap[rowStr];
          }
          // å°è¯•不带前导零的情况
          const rowStrWithoutZero = rowStr.replace(/^0/, '');
          if (rowMap[rowStrWithoutZero]) {
            return rowMap[rowStrWithoutZero];
          }
        }
        // å¦‚果没有匹配的映射,返回原始巷道号
        return originalRoadwayNo;
      }
      return '';
    },
    getWarehouseName(location) {
      if (location.warehouseId === 1) return "原料库";
      if (location.warehouseId === 2) return "成品库";
      return "未知仓库";
    },
    }
  },
  mounted() {
    var mainHeight = document.getElementById("vol-main");
@@ -307,16 +575,17 @@
    var _this = this;
    //加载下拉选项
    this.http.post("/api/LocationInfo/GetRow", {}, "查询中").then((x) => {
      //加载第一个区域,第一排
      // _this.Area.shelf_code = _this.slectData[0].shelf_code;
      _this.scList = x.data;
      //保存所有巷道
      _this.allRoadways = x.data;
      //根据当前仓库类型过滤巷道
      _this.filterRoadwaysByWarehouse(_this.Area.warehouse);
      if (_this.scList.length > 0) {
        _this.Area.row = _this.scList[0];
        _this.Area.roadwayNo = _this.scList[0];
      }
      _this.GetViewData();
    });
  },
  components: { ElButton, Loading },
  components: { ElButton }
};
</script>
@@ -385,40 +654,52 @@
.location-view {
      flex: 1;
      width: 100%;
      max-width: 100%;
  width: 470%;
  max-width: 470%;
      overflow: auto;
      padding: 10px;
      background-color: white;
      border-radius: 4px;
    }
.layer-container {
  margin-bottom: 25px;
}
.layer-title {
  margin: 0 0 10px 0;
  font-size: 16px;
  color: #333;
  padding: 20px;
  background-color: #f5f7fa;
  border-radius: 8px;
}
.row {
  display: flex;
  flex-wrap: wrap;
  margin-bottom: 8px;
  cursor: pointer;
  flex-wrap: nowrap;
}
.location-cell {
  width: 85px;
  height: 38px;
  margin: 3px;
  width: 120px;
  height: 50px;
  margin: 5px;
  text-align: center;
  font-size: 14px;
  border-radius: 3px;
  line-height: 38px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  font-size: 16px;
  border-radius: 4px;
  line-height: 50px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
  border: 1px solid #ccc;
  background-color: #f9f9f9;
}
/* å··é“区域样式 */
.roadway-section {
  margin-bottom: 40px;
  padding: 20px;
  background-color: white;
  border-radius: 6px;
  border: 1px solid #eaeaea;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.roadway-title {
  margin: 0 0 20px 0;
  font-size: 24px;
  font-weight: bold;
  color: #2c3e50;
  text-align: left;
  padding-bottom: 15px;
  border-bottom: 3px solid #409EFF;
}
.location-tooltip {
@@ -427,22 +708,37 @@
  background-color: white;
  border: 1px solid #ddd;
  border-radius: 4px;
  padding: 10px;
  padding: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  pointer-events: none;
  max-width: 250px;
  min-width: 280px;
  max-width: 320px;
}
.location-tooltip p {
  margin: 5px 0;
  font-size: 13px;
  line-height: 1.4;
  margin: 6px 0;
  font-size: 14px;
  line-height: 1.5;
  display: flex;
  align-items: center;
}
.location-tooltip strong {
  display: inline-block;
  width: 70px;
  color: #666;
  width: 80px;
  color: #555;
  font-weight: 600;
  text-align: right;
  margin-right: 12px;
  flex-shrink: 0;
}
.location-tooltip span {
  display: inline-block;
  color: #333;
  font-weight: 500;
  flex: 1;
  text-align: left;
}
.form-group {
ÏîÄ¿´úÂë/WMS/WMSClient/src/views/outbound/outSGOrder.vue
@@ -148,29 +148,37 @@
            const materialNos = row.materialNos;
            if (!materialNos) return materialNos;
            
            // æž„建物料缺料状态映射
            const lackStatusMap = new Map();
            // æ³¨æ„ï¼šå­—段名已经被转换为驼峰命名法
            const statusField = row.materialLackStatus;
            if (statusField) {
              const statusPairs = statusField.split(',');
              statusPairs.forEach(pair => {
                const [materialNo, isLack] = pair.split(':');
                lackStatusMap.set(materialNo.trim(), isLack.trim() === 'true');
              });
            }
            // å¤„理物料编号,根据状态设置颜色
          // å¤„理物料编号,根据每个物料的实际状态显示颜色
            const materialList = materialNos.split('/');
            const formattedMaterials = materialList.map(materialNo => {
          let formattedMaterials = [];
          // å¦‚果有物料缺料状态字符串,根据每个物料的状态显示颜色
          if (row.materialLackStatus) {
            // è§£æžç‰©æ–™ç¼ºæ–™çŠ¶æ€å­—ç¬¦ä¸²ï¼šç‰©æ–™ç¼–å·:状态,物料编号:状态
            const statusPairs = row.materialLackStatus.split(',');
            // èŽ·å–æ‰€æœ‰ç‰©æ–™çš„ç¼ºæ–™çŠ¶æ€åˆ—è¡¨ï¼Œä¿æŒåŽŸå§‹é¡ºåº
            const lackStatusList = statusPairs.map(pair => {
              const [, isLack] = pair.split(':');
              return isLack.trim() === 'true';
            });
            // ä¸ºæ¯ä¸ªç‰©æ–™ç¼–号设置颜色,保持原始顺序
            formattedMaterials = materialList.map((materialNo, index) => {
              const trimmedMaterialNo = materialNo.trim();
              const isLack = lackStatusMap.get(trimmedMaterialNo) || false;
              const isLack = lackStatusList[index] || false;
              if (isLack) {
                return `<span style="color: red;">${trimmedMaterialNo}</span>`;
              }
              return trimmedMaterialNo;
            });
          } else {
            // å¦‚果没有物料缺料状态信息,使用订单级别的缺料状态
            const isLack = Boolean(row.isLackMaterial);
            if (isLack) {
              return `<span style="color: red;">${materialNos}</span>`;
            }
            formattedMaterials = materialList;
          }
            
            return formattedMaterials.join('/');
          }
@@ -190,6 +198,13 @@
          }
        },
        {
        field: "boardFluteNos",
        title: "楞别",
        type: "string",
        width: 100,
        align: "left"
      },
      {
          field: "isLackMaterial",
          title: "是否缺料",
          type: "string",
ÏîÄ¿´úÂë/WMS/WMSClient/src/views/outbound/outSGOrderDetail.vue
@@ -1,16 +1,7 @@
<template>
    <view-grid
      ref="grid"
      :columns="columns"
      :detail="detail"
      :editFormFields="editFormFields"
      :editFormOptions="editFormOptions"
      :searchFormFields="searchFormFields"
      :searchFormOptions="searchFormOptions"
      :table="table"
      :extend="extend"
    >
  <view-grid ref="grid" :columns="columns" :detail="detail" :editFormFields="editFormFields"
    :editFormOptions="editFormOptions" :searchFormFields="searchFormFields" :searchFormOptions="searchFormOptions"
    :table="table" :extend="extend">
    </view-grid>
  </template>
    <script>
@@ -218,4 +209,3 @@
    },
  });
  </script>
ÏîÄ¿´úÂë/WMS/WMSServices/WIDESEA_BasicService/Base/LocationInfoService.cs
@@ -1,4 +1,4 @@
using HslCommunication.WebSocket;
using HslCommunication.WebSocket;
using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Text;
using SqlSugar;
@@ -44,7 +44,7 @@
        }
        /// <summary>
        /// æŸ¥è¯¢è´§ä½å¯¹åº”çš„RFID
        /// æŸ¥è¯¢è´§ä½å¯¹åº”çš„RFID及库存信息
        /// </summary>
        /// <param name="locationCodes"></param>
        /// <returns></returns>
@@ -60,28 +60,47 @@
                if (warehouseId == 1)
                {
                    // æŸ¥è¯¢åŽŸæ–™åº“Dt_StockInfo表,获取RFID信息
                    // æŸ¥è¯¢åŽŸæ–™åº“Dt_StockInfo表,获取RFID及库存信息
                    var rawMaterialRfidList = _stockInfoRepository.QueryData()
                        .Where(x => locationCodes.Contains(x.LocationCode))
                        .Select(x => new { x.LocationCode, x.RfidCode })
                        .Select(x => new {
                            locationCode = x.LocationCode,
                            rfidCode = x.RfidCode,
                            paperRoll = x.MaterielName, // çº¸å·åç§°
                            width = x.MaterielWide, // é—¨å¹…(幅宽)
                            barcode = x.PalletCode, // çº¸å·æ¡ç 
                            status = x.StockStatus, // çŠ¶æ€
                            inDate = x.CreateDate // å…¥åº“时间(创建时间)
                        })
                        .ToList();
                    var rfidList = rawMaterialRfidList.ToList();
                    // è¿”回结果
                    return WebResponseContent.Instance.OK(data: rfidList);
                    return WebResponseContent.Instance.OK(data: rawMaterialRfidList);
                }
                if (warehouseId == 2)
                {
                    // æŸ¥è¯¢æˆå“åº“Dt_ProStockInfo表,获取PalletCode信息
                    var finishedProductRfidList =_proStockInfoRepository.QueryData()
                    // æ›¿æ¢åŽŸæœ‰çš„ .Select(x => new { ... Detail = x.proStockInfoDetails?.FirstOrDefault() ... }) ä»£ç å—
                    var finishedProductRfidList = _proStockInfoRepository.Db.Queryable<Dt_ProStockInfo>()
                        .Includes(x => x.proStockInfoDetails)
                        .Where(x => locationCodes.Contains(x.LocationCode))
                        .Select(x => new { x.LocationCode, RfidCode = x.PalletCode })
                        .ToList()
                        .Select(x => new
                        {
                            locationCode = x.LocationCode,
                            rfidCode = x.PalletCode,
                            paperRoll = (x.proStockInfoDetails != null && x.proStockInfoDetails.Count > 0) ? x.proStockInfoDetails[0].ProductName : string.Empty,
                            productName = (x.proStockInfoDetails != null && x.proStockInfoDetails.Count > 0) ? x.proStockInfoDetails[0].ProductName : string.Empty,
                            width = (x.proStockInfoDetails != null && x.proStockInfoDetails.Count > 0) ? x.proStockInfoDetails[0].StockQty : 0,
                            quantity = (x.proStockInfoDetails != null && x.proStockInfoDetails.Count > 0) ? x.proStockInfoDetails[0].StockQty : 0,
                            barcode = x.PalletCode,
                            status = x.StockStatus,
                            inDate = x.CreateDate
                        })
                        .ToList();
                    var rfidList = finishedProductRfidList.ToList();
                    // è¿”回结果
                    return WebResponseContent.Instance.OK(data: rfidList);
                    return WebResponseContent.Instance.OK(data: finishedProductRfidList);
                }
                // å¦‚果没有匹配的仓库ID,返回空列表
                return WebResponseContent.Instance.OK(data: new List<object>());
ÏîÄ¿´úÂë/WMS/WMSServices/WIDESEA_Model/Models/Outbound/Dt_OutSGOrder.cs
@@ -85,6 +85,12 @@
        public string MaterialWides { get; set; }
        /// <summary>
        /// æ¥žåˆ«
        /// </summary>
        [SugarColumn(IsIgnore = true, ColumnDescription = "楞别")]
        public string BoardFluteNos { get; set; }
        /// <summary>
        /// æ˜¯å¦ç¼ºæ–™
        /// </summary>
        [SugarColumn(IsIgnore = true, ColumnDescription = "是否缺料")]
ÏîÄ¿´úÂë/WMS/WMSServices/WIDESEA_OutboundService/OutSGOrderDetailService.cs
@@ -153,7 +153,24 @@
                    
                }
                // åˆ†é…å®ŒæˆåŽï¼Œæ£€æŸ¥æ¯ä¸ªè®¢å•明细的分配情况,设置正确的状态
                foreach (var detail in details)
                {
                    decimal assignedQuantity = outStockLockInfos.Where(x => x.OrderDetailId == detail.Id).Sum(x => x.AssignQuantity);
                    if (assignedQuantity > 0)
                    {
                        // å·²ç»åˆ†é…åˆ°åº“存,标记为非缺料状态(未开始)
                        detail.OutSGOrderDetailStatus = OutOrderStatusEnum.未开始.ObjToInt();
                    }
                    else
                    {
                        // æœªåˆ†é…åˆ°åº“存,标记为缺料
                        detail.OutSGOrderDetailStatus = OutOrderStatusEnum.缺料.ObjToInt();
                    }
                }
                locationInfos.AddRange(_basicRepository.LocationInfoRepository.GetLocationInfos(outStocks.Where(x=>!x.LocationCode.IsNullOrEmpty() && !locationInfos.Select(x=>x.LocationCode).Contains(x.LocationCode)).Select(x => x.LocationCode).ToList()));
            }
            return (outStocks, outboundOrderDetails, outStockLockInfos, locationInfos, message);
ÏîÄ¿´úÂë/WMS/WMSServices/WIDESEA_OutboundService/OutSGOrderService.cs
@@ -59,13 +59,16 @@
                // èŽ·å–æ‰€æœ‰è®¢å•ID
                var orderIds = pageData.Rows.Select(o => o.Id).ToList();
                
                // æ‰¹é‡æŸ¥è¯¢æ‰€æœ‰å…³è”的明细,过滤掉物料编号为空的数据
                // æ‰¹é‡æŸ¥è¯¢æ‰€æœ‰å…³è”的明细,按OutSGOrderId升序、Id降序排序,与订单明细页面保持一致
                var allDetails = BaseDal.Db.Queryable<Dt_OutSGOrderDetail>()
                    .Where(d => orderIds.Contains(d.OutSGOrderId))
                    .OrderBy(d => d.OutSGOrderId)
                    .OrderByDescending(d => d.Id)
                    .ToList();
                
                // æŒ‰è®¢å•ID分组
                var detailsByOrderId = allDetails.GroupBy(d => d.OutSGOrderId).ToDictionary(g => g.Key, g => g.ToList());
                // æŒ‰è®¢å•ID分组,每组明细按Id降序排序,与订单明细页面保持一致
                var detailsByOrderId = allDetails.GroupBy(d => d.OutSGOrderId)
                    .ToDictionary(g => g.Key, g => g.OrderByDescending(d => d.Id).ToList());
                
                // å¤„理每个订单
                foreach (var order in pageData.Rows)
@@ -91,6 +94,9 @@
                            ? uniqueWidths[0] 
                            : string.Join("/", uniqueWidths);
                        
                        // è®¾ç½®æ¥žåˆ«ï¼Œåªæ˜¾ç¤ºä¸€ä¸ªï¼ˆå› ä¸ºæ‰€æœ‰æ¥žåˆ«ç›¸åŒï¼‰
                        order.BoardFluteNos = details.FirstOrDefault()?.BoardFluteNo ?? "";
                        // è®¾ç½®æ˜¯å¦ç¼ºæ–™ï¼šå¦‚果有任何一个明细是缺料状态,就设置为true
                        // å¼•用WIDESEA_Common.OrderEnum命名空间来使用OutOrderStatusEnum枚举
                        order.IsLackMaterial = details.Any(d => d.OutSGOrderDetailStatus == 3); // 3是缺料状态的枚举值
ÏîÄ¿´úÂë/WMS/WMSServices/WIDESEA_TaskInfoService/TaskService_Outbound.cs
@@ -1,4 +1,4 @@

using System;
using System.Collections.Generic;
using System.Linq;
@@ -709,7 +709,7 @@
                }
                content.Message = result.Item6;
                //处理出库数据
                return GenerateOutboundTaskDataUpdate(tasks, stockInfos, outSGOrderDetails, outStockLockInfos, locationInfos);
                return GenerateOutboundTaskDataUpdate(tasks, stockInfos, outSGOrderDetails, outStockLockInfos, locationInfos, result.Item6);
            }
            catch (Exception ex)
            {
@@ -746,6 +746,14 @@
            //分配库存
            (List<Dt_StockInfo>, List<Dt_OutSGOrderDetail>, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>,string) result = _outboundService.OutSGOrderDetailService.AssignStockOutbound(outSGOrderDetails);
            //保存缺料状态,无论是否生成了任务
            var shortageDetails = result.Item2.Where(x => x.OutSGOrderDetailStatus == OutOrderStatusEnum.缺料.ObjToInt()).ToList();
            if (shortageDetails.Any())
            {
                _outboundRepository.OutSGOrderDetailRepository.UpdateData(shortageDetails);
            }
            if (result.Item1 != null && result.Item1.Count > 0)
            {
                //获取任务
@@ -776,7 +784,8 @@
            }
            else
            {
                throw new Exception("无库存");
                //没有库存,但已经在AssignStockOutbound方法中标记了缺料状态,并且已经保存
                orderDetails = result.Item2;
            }
            return (tasks, stockInfos, orderDetails, outStockLockInfos, locationInfos, result.Item5);
@@ -785,7 +794,7 @@
        /// å¤„理出库数据
        /// </summary>
        /// <returns></returns>
        public WebResponseContent GenerateOutboundTaskDataUpdate(List<Dt_Task> tasks, List<Dt_StockInfo>? stockInfos = null, List<Dt_OutSGOrderDetail>? outboundOrderDetails = null, List<Dt_OutStockLockInfo>? outStockLockInfos = null, List<Dt_LocationInfo>? locationInfos = null)
        public WebResponseContent GenerateOutboundTaskDataUpdate(List<Dt_Task> tasks, List<Dt_StockInfo>? stockInfos = null, List<Dt_OutSGOrderDetail>? outboundOrderDetails = null, List<Dt_OutStockLockInfo>? outStockLockInfos = null, List<Dt_LocationInfo>? locationInfos = null, string message = "")
        {
            try
            {
@@ -825,7 +834,7 @@
                }
                _unitOfWorkManage.CommitTran();
                PushTasksToWCS(tasks);
                return WebResponseContent.Instance.OK();
                return WebResponseContent.Instance.OK(message);
            }
            catch (Exception ex)
            {
ÏîÄ¿´úÂë/WMS/WMSServices/WIDESEA_WMSServer/Controllers/Basic/LocationInfoController.cs
@@ -1,4 +1,4 @@
using HslCommunication.WebSocket;
using HslCommunication.WebSocket;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -90,29 +90,33 @@
        /// <summary>
        /// æŸ¥è¯¢æ¡ä»¶è´§ä½
        /// </summary>
        /// <param name="row"></param>
        /// <param name="roadwayNo"></param>
        /// <param name="warehouseId"></param>
        /// <returns></returns>
        [HttpPost, HttpGet, Route("GetLocationStatus")]
        public WebResponseContent GetLocationStatus(int row, int warehouseId = 0)
        public WebResponseContent GetLocationStatus(string roadwayNo, int warehouseId = 0)
        {
            List<int> layers;
            if (warehouseId == 0)
            // å®šä¹‰è¿”回数据结构
            var result = new
            {
                layers = _repository.QueryData(x => x.Row == row).Select(x => x.Layer).Distinct().ToList();
            }
            else
            {
                layers = _repository.QueryData(x => x.Row == row && x.WarehouseId == warehouseId).Select(x => x.Layer).Distinct().ToList();
            }
                row1 = new List<object>(), // å··é“第1排(东面)
                row2 = new List<object>()  // å··é“第2排(西面)
            };
            
            List<object> listObj = new List<object>();
            foreach (var item in layers)
            // æŸ¥è¯¢å··é“下所有货位
            var locations = _repository.QueryData(x => x.RoadwayNo == roadwayNo);
            // èŽ·å–æ‰€æœ‰å±‚ï¼ŒæŒ‰ä»Žé«˜åˆ°ä½ŽæŽ’åº
            var layers = locations.Select(x => x.Layer).Distinct().OrderByDescending(x => x).ToList();
            // å¤„理每一层
            foreach (var layer in layers)
            {
                object locationObj;
                if (warehouseId == 0)
                {
                    locationObj = _repository.QueryData(x => x.Row == row && x.Layer == item)
                // èŽ·å–å½“å‰å±‚çš„è´§ä½
                var layerLocations = locations.Where(x => x.Layer == layer);
                // ç¬¬1排数据(东面)
                var row1Locations = layerLocations.Where(x => x.Row == 1)
                        .OrderBy(x => x.Columns)
                        .Select(x => new
                        {
@@ -122,10 +126,9 @@
                            locationCode = x.LocationCode,
                            location_lock = x.LocationStatus
                        }).ToList();
                }
                else
                {
                    locationObj = _repository.QueryData(x => x.Row == row && x.Layer == item && x.WarehouseId == warehouseId)
                // ç¬¬2排数据(西面)
                var row2Locations = layerLocations.Where(x => x.Row == 2)
                        .OrderBy(x => x.Columns)
                        .Select(x => new
                        {
@@ -135,24 +138,36 @@
                            locationCode = x.LocationCode,
                            location_lock = x.LocationStatus
                        }).ToList();
                // æ·»åŠ åˆ°ç»“æžœä¸­
                result.row1.Add(new { layer, locationObj = row1Locations });
                result.row2.Add(new { layer, locationObj = row2Locations });
                }
                
                object obj = new { layer = item, locationObj };
                listObj.Add(obj);
            }
            return WebResponseContent.Instance.OK("成功", listObj);
            return WebResponseContent.Instance.OK("成功", result);
        }
        /// <summary>
        /// æŸ¥è¯¢å…¨éƒ¨è´§ä½
        /// æŸ¥è¯¢å…¨éƒ¨å··é“
        /// </summary>
        /// <returns></returns>
        [HttpPost, HttpGet, Route("GetRow")]
        public WebResponseContent GetRow()
        {
            List<int> listRow = _repository.QueryData().Select(x => x.Row).Distinct().ToList();
            return WebResponseContent.Instance.OK("成功", listRow);
            // ä¿æŒå…¼å®¹æ—§æŽ¥å£ï¼Œè¿”回巷道列表
            List<string> roadwayList = _repository.QueryData().Select(x => x.RoadwayNo).Distinct().ToList();
            return WebResponseContent.Instance.OK("成功", roadwayList);
        }
        /// <summary>
        /// æŸ¥è¯¢å…¨éƒ¨å··é“
        /// </summary>
        /// <returns></returns>
        [HttpPost, HttpGet, Route("GetRoadway")]
        public WebResponseContent GetRoadway()
        {
            List<string> roadwayList = _repository.QueryData().Select(x => x.RoadwayNo).Distinct().ToList();
            return WebResponseContent.Instance.OK("成功", roadwayList);
        }
        /// <summary>