xiazhengtongxue
2026-01-25 a7bd6e538027d876b3122f21c3b1d34663fb2f07
ÏîÄ¿´úÂë/WMS/WIDESEA_WMSClient/src/views/stock/stockView.vue
@@ -20,8 +20,8 @@
          <div class="form-group">
            <label class="form-label">巷道:</label>
            <el-select size="mini" clearable filterable v-model="selectedRoadwayNo" placeholder="请选择巷道" class="full-width"
              @change="handleRoadwayNoChange">
            <el-select size="mini" clearable filterable v-model="selectedRoadwayNo" placeholder="请选择巷道"
              class="full-width" @change="handleRoadwayNoChange">
              <el-option v-for="item in roadwayNoList" :key="item" :value="item" :label="'巷道 ' + item"></el-option>
            </el-select>
          </div>
@@ -59,6 +59,7 @@
          </div>
          <div class="layers-container">
            <!-- éåŽ†æ¯ä¸€å±‚ -->
            <div class="layer-row" v-for="layer in sortedLayerData" :key="layer.layer">
              <div class="layer-title-area">
                <h3 class="layer-title">层 {{ layer.layer }}</h3>
@@ -66,19 +67,29 @@
              </div>
              <div class="layer-content-wrap">
                <div class="layer-content">
                  <div class="location-column" v-for="column in sortedColumns(layer.columns)" :key="column.column">
                    <div class="column-label">列 {{ column.column }}</div>
                    <div class="locations-wrapper">
                      <div class="location-item" v-for="depth in sortedDepthsByRow(column.depths)" :key="depth.depth"
                        :class="getLocationStatusClass(depth)"
                        @mouseenter="showTooltip(depth, column.column, layer.layer, $event)" @mouseleave="hideTooltip"
                        @click="handleLocationClick(depth)">
                        <div class="location-code">
                          {{ depth.row || '?' }}排-{{ column.column }}列-{{ layer.layer }}层-{{ depth.depth == 1 ? '浅' : 'æ·±' }}
                        </div>
                        <div class="location-status" v-if="depth.enableStatus !== 0">
                          ç¦ç”¨
                <!-- æŒ‰è¡Œåˆ†ç»„显示 -->
                <div class="row-group" v-for="rowGroup in getRowGroups(layer)" :key="rowGroup.row">
                  <div class="row-title">
                    <span class="row-label">{{ rowGroup.row }}排</span>
                    <span class="row-count">{{ rowGroup.locations.length }} ä¸ªè´§ä½</span>
                  </div>
                  <div class="row-content">
                    <!-- åœ¨æ¯æŽ’内显示列(不显示列标签) -->
                    <div class="location-column" v-for="column in sortedColumns(rowGroup.columns)" :key="column.column">
                      <div class="locations-wrapper">
                        <!-- æ¯åˆ—显示深度(浅/深) -->
                        <div class="location-item" v-for="depth in column.depths" :key="depth.depth"
                          :class="getLocationStatusClass(depth)"
                          @mouseenter="showTooltip(depth, column.column, layer.layer, $event)"
                          @mouseleave="hideTooltip"
                          @click="handleLocationClick(depth)">
                          <div class="location-code">
                            {{ getLocationPositionForDisplay(depth, column.column, layer.layer) }}
                          </div>
                          <div class="location-status" v-if="depth.enableStatus !== 0">
                            ç¦ç”¨
                          </div>
                        </div>
                      </div>
                    </div>
@@ -109,31 +120,35 @@
            </div>
            <div class="tooltip-body">
              <div class="tooltip-row">
                <span class="tooltip-label">位置:</span>
                <span class="tooltip-value">{{ getLocationCode(currentLocation) }}</span>
                <span class="tooltip-label">仓库:</span>
                <span class="tooltip-value">{{ getWarehouseName(selectedWarehouse) }}</span>
              </div>
              <div class="tooltip-row">
                <span class="tooltip-label">巷道:</span>
                <span class="tooltip-value">{{ selectedRoadwayNo }}</span>
              </div>
              <div class="tooltip-row">
                <span class="tooltip-label">深度:</span>
                <span class="tooltip-value">{{ currentLocation.depth === 1 ? '浅' : 'æ·±' }}</span>
                <span class="tooltip-label">货位编号:</span>
                <span class="tooltip-value">{{ currentLocation?.locationCode || '--' }}</span>
              </div>
              <div class="tooltip-row">
                <span class="tooltip-label">位置:</span>
                <span class="tooltip-value">{{ getLocationPosition(currentLocation) }}</span>
              </div>
              <div class="tooltip-row">
                <span class="tooltip-label">类型:</span>
                <span class="tooltip-value">{{ getLocationTypeText(currentLocation.locationType) }}</span>
                <span class="tooltip-value">{{ getLocationTypeText(currentLocation?.locationType) }}</span>
              </div>
              <div class="tooltip-row">
                <span class="tooltip-label">状态:</span>
                <span class="tooltip-value" :class="getStatusClass(currentLocation.locationStatus)">
                  {{ getStatusText(currentLocation.locationStatus) }}
                <span class="tooltip-value" :class="getStatusClass(currentLocation?.locationStatus)">
                  {{ getStatusText(currentLocation?.locationStatus) }}
                </span>
              </div>
              <div class="tooltip-row">
                <span class="tooltip-label">启用状态:</span>
                <span class="tooltip-value" :class="{ 'status-disabled': currentLocation.enableStatus !== 0 }">
                  {{ getEnableStatusText(currentLocation.enableStatus) }}
                <span class="tooltip-value" :class="{ 'status-disabled': currentLocation?.enableStatus !== 0 }">
                  {{ getEnableStatusText(currentLocation?.enableStatus) }}
                </span>
              </div>
            </div>
@@ -154,12 +169,12 @@
      roadwayNoList: [],
      selectedWarehouse: null,
      selectedRoadwayNo: null,
      selectedLocationCode: null,
      infoMsg: [
        { bgcolor: "lightgreen", msg: "空货位" },
        { bgcolor: "#2BB3D5", msg: "空货位" },
        { bgcolor: "orange", msg: "有货" },
        { bgcolor: "#2BB3D5", msg: "锁定" },
        { bgcolor: "#ccc", msg: "禁用" },
        { bgcolor: "#b7ba6b", msg: "其它" },
        { bgcolor: "lightgreen", msg: "锁定" },
        { bgcolor: "#ccc", msg: "禁用" }
      ],
      locationData: [],
      showTooltipFlag: false,
@@ -204,21 +219,51 @@
    }
  },
  methods: {
    sortedColumns(columns) {
      // æŒ‰åˆ—号排序
      return columns?.sort((a, b) => a.column - b.column) || [];
    // æŒ‰è¡Œåˆ†ç»„数据
    getRowGroups(layer) {
      const rowMap = {};
      // éåŽ†æ‰€æœ‰åˆ—å’Œæ·±åº¦ï¼ŒæŒ‰è¡Œåˆ†ç»„
      layer.columns?.forEach(column => {
        column.depths?.forEach(depth => {
          const row = depth.row || '?';
          if (!rowMap[row]) {
            rowMap[row] = {
              row: row,
              locations: [],
              columns: {}
            };
          }
          // æ·»åŠ åˆ°è¯¥è¡Œçš„ä½ç½®åˆ—è¡¨
          rowMap[row].locations.push({
            ...depth,
            column: column.column,
            layer: layer.layer
          });
          // æŒ‰åˆ—组织数据
          if (!rowMap[row].columns[column.column]) {
            rowMap[row].columns[column.column] = {
              column: column.column,
              depths: []
            };
          }
          rowMap[row].columns[column.column].depths.push(depth);
        });
      });
      // è½¬æ¢ä¸ºæ•°ç»„并按行排序
      return Object.values(rowMap)
        .sort((a, b) => this.compareRows(a.row, b.row));
    },
    sortedDepthsByRow(depths) {
      // é¦–先按行(row)排序,然后按深度(depth)排序
      return depths?.sort((a, b) => {
        // å¦‚果行号相同,按深度排序
        if (a.row === b.row) {
          return a.depth - b.depth;
        }
        // æŒ‰è¡Œå·æŽ’序(这里假设行号是数字或可比较的字符串)
        return this.compareRows(a.row, b.row);
      }) || [];
    sortedColumns(columnsObj) {
      // å°†å¯¹è±¡è½¬æ¢ä¸ºæ•°ç»„并按列号排序
      const columns = Object.values(columnsObj || {});
      return columns.sort((a, b) => a.column - b.column);
    },
    // æ¯”较行号的辅助方法
@@ -229,6 +274,22 @@
      }
      // å¦åˆ™æŒ‰å­—符串比较
      return String(rowA).localeCompare(String(rowB));
    },
    // èŽ·å–ä½ç½®ä¿¡æ¯ç”¨äºŽæ˜¾ç¤º
    getLocationPositionForDisplay(location, column, layer) {
      if (!location) return '';
      const row = location.row || '?';
      const depthText = location.depth === 1 ? '浅' : 'æ·±';
      return `${row}排${column}列${layer}层${depthText}`;
    },
    // èŽ·å–ä½ç½®ä¿¡æ¯ï¼ˆç”¨äºŽæ‚¬æµ®æç¤ºæ¡†ï¼‰
    getLocationPosition(location) {
      if (!location || !this.currentColumn || !this.currentLayer) return '--';
      const row = location.row || '?';
      const depthText = location.depth === 1 ? '浅' : 'æ·±';
      return `${row}排-${this.currentColumn}列-${this.currentLayer}层-${depthText}`;
    },
    async fetchWarehouseData() {
@@ -270,13 +331,13 @@
        if (response.data && response.status) {
          this.locationData = response.data || [];
          // éªŒè¯æ•°æ®ä¸­æ˜¯å¦æœ‰row字段
          console.log("货位数据示例:", this.locationData[0]);
          // éªŒè¯æ•°æ®
          console.log("货位数据:", this.locationData);
          if (this.locationData.length > 0 && this.locationData[0].columns) {
            console.log("第一个货位的row字段:", this.locationData[0].columns[0]?.depths?.[0]?.row);
            console.log("示例货位:", this.locationData[0].columns[0]?.depths?.[0]);
          }
          if (this.locationData.length === 0) {
            ElMessage.info("该巷道没有货位数据");
          }
@@ -320,10 +381,10 @@
      }
      switch (depth.locationStatus) {
        case 0: return 'location-empty';    // ç©ºè´§ä½
        case 1: return 'location-locked';   // é”å®š
        case 0: return 'location-empty';     // ç©ºè´§ä½
        case 1: return 'location-locked';    // é”å®š
        case 100: return 'location-occupied'; // æœ‰è´§
        default: return 'location-other';   // å…¶ä»–状态
        default: return 'location-other';
      }
    },
@@ -358,11 +419,8 @@
    handleLocationClick(depth) {
      console.log('点击货位:', depth);
    },
    getLocationCode(location) {
      if (!location) return '';
      return `${location.row || '?'}排-${this.currentColumn}列-${this.currentLayer}层-${location.depth === 1 ? '浅' : 'æ·±'}`;
      // è¿™é‡Œå¯ä»¥æ·»åŠ ç‚¹å‡»è´§ä½çš„å¤„ç†é€»è¾‘
      ElMessage.info(`选中货位: ${depth.locationCode || '未知货位'}`);
    },
    getStatusText(status) {
@@ -410,7 +468,7 @@
</script>
<style scoped>
/* æ ·å¼éƒ¨åˆ†ä¿æŒä¸å˜ï¼Œä¸Žä¹‹å‰ç›¸åŒ */
/* æ ·å¼éƒ¨åˆ† */
.container {
  display: flex;
  flex-direction: column;
@@ -418,20 +476,6 @@
  padding: 16px;
  box-sizing: border-box;
  background-color: #f0f2f5;
}
.header {
  text-align: center;
  margin-bottom: 20px;
  padding-bottom: 16px;
  border-bottom: 1px solid #e8e8e8;
}
.title {
  font-size: 22px;
  font-weight: 600;
  margin: 0;
  color: #1890ff;
}
.content-wrapper {
@@ -525,16 +569,6 @@
  border-radius: 4px;
  border: 1px solid rgba(0, 0, 0, 0.1);
  flex-shrink: 0;
}
.depth-shallow-legend {
  background-color: rgba(255, 255, 255, 0.7);
  border: 1px solid #2c3e50;
}
.depth-deep-legend {
  background-color: rgba(0, 0, 0, 0.7);
  border: 1px solid #000;
}
.legend-label {
@@ -638,48 +672,74 @@
  width: 100%;
  overflow: hidden;
  flex: 1;
  padding: 16px;
}
.layer-content {
/* è¡Œç»„样式 */
.row-group {
  margin-bottom: 10px;
  padding: 12px;
  background: white;
  border-radius: 6px;
  border: 1px solid #e8e8e8;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
}
.row-group:last-child {
  margin-bottom: 0;
}
.row-title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
  padding-bottom: 8px;
  border-bottom: 1px dashed #e8e8e8;
}
.row-label {
  font-size: 14px;
  font-weight: 600;
  color: #333;
}
.row-count {
  font-size: 12px;
  color: #666;
  background: #f0f0f0;
  padding: 2px 8px;
  border-radius: 10px;
}
.row-content {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  padding: 16px;
  min-width: 0;
  box-sizing: border-box;
  gap: 8px; /* å‡å°é—´è· */
}
/* ç¼©å°è´§ä½æ¡†å°ºå¯¸ */
.location-column {
  flex: 0 0 auto;
  width: 140px;
  min-height: 120px;
  width: 70px; /* å‡å°å®½åº¦ */
  min-height: 30px; /* å‡å°é«˜åº¦ */
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
}
.column-label {
  font-size: 13px;
  font-weight: 600;
  color: #666;
  text-align: center;
  margin-bottom: 8px;
  padding-bottom: 4px;
  border-bottom: 1px dashed #ddd;
  flex-shrink: 0;
}
.locations-wrapper {
  display: flex;
  flex-direction: column;
  gap: 6px;
  gap: 4px; /* å‡å°é—´è· */
  flex: 1;
  min-height: 0;
}
/* ç¼©å°è´§ä½é¡¹ */
.location-item {
  min-height: 40px;
  padding: 6px 4px;
  min-height: 32px; /* å‡å°é«˜åº¦ */
  padding: 4px 2px; /* å‡å°å†…边距 */
  border-radius: 4px;
  text-align: center;
  cursor: pointer;
@@ -690,7 +750,7 @@
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  gap: 1px;
  flex: 1;
}
@@ -700,46 +760,29 @@
  z-index: 10;
}
/* ç¼©å°è´§ä½ç¼–号字体 */
.location-code {
  font-size: 11px;
  font-size: 9px; /* å‡å°å­—体 */
  font-weight: 500;
  line-height: 1.2;
  word-break: break-all;
}
.depth-indicator {
  font-size: 10px;
  padding: 2px 4px;
  border-radius: 3px;
  margin-top: 2px;
  font-weight: bold;
  transition: all 0.2s;
}
.depth-shallow {
  background-color: rgba(255, 255, 255, 0.7);
  color: #2c3e50;
}
.depth-deep {
  background-color: rgba(0, 0, 0, 0.7);
  color: white;
}
.location-item:hover .depth-indicator {
  transform: scale(1.1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.location-status {
  font-size: 10px;
  padding: 1px 4px;
  font-size: 8px; /* å‡å°å­—体 */
  padding: 1px 3px;
  border-radius: 2px;
  background: rgba(0, 0, 0, 0.1);
}
/* è´§ä½çŠ¶æ€é¢œè‰² */
.location-empty {
  background-color: lightgreen;
  color: #333;
  background-color: #2BB3D5;
  color: white;
}
.location-occupied {
@@ -748,8 +791,8 @@
}
.location-locked {
  background-color: #2BB3D5;
  color: white;
  background-color: lightgreen;
  color: #333;
}
.location-disabled {
@@ -831,51 +874,7 @@
  color: #f5222d;
}
@media (max-width: 1200px) {
  .location-column {
    width: 130px;
  }
}
@media (max-width: 768px) {
  .content-wrapper {
    flex-direction: column;
  }
  .control-panel {
    width: 100%;
  }
  .location-column {
    width: 120px;
  }
  .layer-content {
    gap: 12px;
  }
}
@media (max-width: 480px) {
  .location-column {
    width: 110px;
  }
  .location-code {
    font-size: 10px;
  }
  .layer-content {
    gap: 10px;
    padding: 12px;
  }
  @media (max-width: 380px) {
    .location-column {
      width: 100px;
    }
  }
}
/* æ»šåŠ¨æ¡æ ·å¼ */
.layers-container::-webkit-scrollbar {
  width: 8px;
}
@@ -893,4 +892,57 @@
.layers-container::-webkit-scrollbar-thumb:hover {
  background: #a8a8a8;
}
/* å“åº”式设计 */
@media (max-width: 1200px) {
  .location-column {
    width: 95px;
  }
}
@media (max-width: 768px) {
  .content-wrapper {
    flex-direction: column;
  }
  .control-panel {
    width: 100%;
  }
  .location-column {
    width: 90px;
  }
  .row-content {
    gap: 6px;
  }
}
@media (max-width: 480px) {
  .location-column {
    width: 80px;
  }
  .location-code {
    font-size: 8px;
  }
  .row-content {
    gap: 4px;
  }
  .row-group {
    padding: 8px;
  }
}
@media (max-width: 380px) {
  .location-column {
    width: 70px;
  }
  .location-code {
    font-size: 7px;
  }
}
</style>