xiazhengtongxue
2026-03-16 acf83c45ef4f342380d1ef1f146a1d575de7c9eb
ÏîÄ¿´úÂë/WMS/WIDESEA_WMSClient/src/views/stock/stockView.vue
@@ -1,392 +1,948 @@
<template>
  <view-grid
    ref="grid"
    :columns="columns"
    :detail="detail"
    :editFormFields="editFormFields"
    :editFormOptions="editFormOptions"
    :searchFormFields="searchFormFields"
    :searchFormOptions="searchFormOptions"
    :table="table"
    :extend="extend"
  >
  </view-grid>
  <div class="container">
    <div class="content-wrapper">
      <!-- æŽ§åˆ¶é¢æ¿åŒºåŸŸ -->
      <div class="control-panel">
        <div class="panel-header">
          <h3>控制面板</h3>
        </div>
        <div class="panel-body">
          <div class="form-group">
            <label class="form-label">仓库:</label>
            <el-select size="mini" filterable v-model="selectedWarehouse" placeholder="请选择仓库" class="full-width"
              @change="handleWarehouseChange">
              <el-option v-for="item in warehouseList" :key="item.warehouseId" :value="item.warehouseId"
                :label="getWarehouseName(item.warehouseId)">
              </el-option>
            </el-select>
          </div>
          <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-option v-for="item in roadwayNoList" :key="item" :value="item" :label="'巷道 ' + item"></el-option>
            </el-select>
          </div>
          <el-button type="success" class="refresh-btn" @click="fetchLocationStatus" :loading="loading">
            åˆ·æ–°
          </el-button>
          <div class="legend-section">
            <div class="legend-header">
              <h4>图例说明</h4>
            </div>
            <div class="legend-list">
              <div class="legend-item" v-for="(item, index) in infoMsg" :key="index">
                <span class="color-box" :style="{ 'background-color': item.bgcolor }"></span>
                <span class="legend-label">{{ item.msg }}</span>
              </div>
            </div>
          </div>
        </div>
      </div>
      <!-- è´§ä½å±•示区域 -->
      <div class="main-content">
        <div v-if="loading" class="loading-container">
          <el-skeleton :rows="6" animated />
        </div>
        <div v-else-if="locationData.length > 0" class="location-container">
          <div class="location-header">
            <div class="location-info">
              <span>当前查看:{{ getWarehouseName(selectedWarehouse) }} - å··é“ {{ selectedRoadwayNo }}</span>
              <span class="total-count">共 {{ totalLocations }} ä¸ªè´§ä½</span>
            </div>
          </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>
                <span class="layer-count">{{ getLayerLocations(layer) }} ä¸ªè´§ä½</span>
              </div>
              <div class="layer-content-wrap">
                <!-- æŒ‰è¡Œåˆ†ç»„显示 -->
                <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>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
        <!-- æ— æ•°æ®æç¤º -->
        <div v-else class="empty-container">
          <el-empty description="暂无货位数据">
            <template #description>
              <p>请选择仓库和巷道来查看货位信息</p>
            </template>
          </el-empty>
        </div>
        <!-- æ‚¬æµ®æç¤ºæ¡† -->
        <div v-if="showTooltipFlag" class="location-tooltip" :style="{
          left: tooltipPosition.x + 'px',
          top: tooltipPosition.y + 'px',
        }">
          <div class="tooltip-content">
            <div class="tooltip-header">
              <h4>货位详情</h4>
            </div>
            <div class="tooltip-body">
              <div class="tooltip-row">
                <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?.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>
              </div>
              <div class="tooltip-row">
                <span class="tooltip-label">状态:</span>
                <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>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
    <script>
import extend from "@/extension/stock/stockView.js";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
    const table = ref({
      key: "stockId",
      footer: "Foots",
      cnName: "库存视图",
      name: "stockView",
      url: "/stockView/",
      sortName: "stockId",
    });
    const editFormFields = ref({
      palletCode: "",
      locationCode: "",
      locationName: "",
    });
    const editFormOptions = ref([
    ]);
    const searchFormFields = ref({
      palletCode: "",
      // locationCode: "",
      materielCode:"",
      batchNo:""
    });
    const searchFormOptions = ref([
      [
        { title: "托盘编号", field: "palletCode",type: "like" },
        // { title: "货位编号", field: "locationCode",type: "like" },
        { title: "货位状态", field: "locationStatus" ,type: "selectList",dataKey: "locationStatusEnum",data: [],},
        { title: "库存状态", field: "stockStatus",type: "selectList",dataKey: "stockStatusEmun",data: [],},
      ],
      [
        { title: "物料编号", field: "materielCode",type: "like"},
        { title: "批次号", field: "batchNo",type: "like"},
        { title: "所属仓库", field: "warehouseId",type: "selectList",dataKey: "warehouses",data: [],},
      ],
    ]);
    const columns = ref([
      {
        field: "stockId",
        title: "Id",
        type: "int",
        width: 90,
        hidden: true,
        readonly: true,
        require: true,
        align: "left",
      },
      {
        field: "palletCode",
        title: "托盘编号",
        type: "string",
        width: 150,
        link: true,
        align: "left",
      },
      {
        field: "locationCode",
        title: "货位编号",
        type: "string",
        width: 200,
        align: "left",
      },
      {
        field: "locationName",
        title: "货位名称",
        type: "string",
        width: 270,
        align: "left",
      },
      {
        field: "warehouseId",
        title: "所属仓库",
        type: "string",
        width: 80,
        align: "left",
        bind: { key: "warehouses", data: [] },
      },
      {
        field: "roadwayNo",
        title: "巷道编号",
        type: "decimal",
        width: 100,
        align: "left",
        hidden:true
      },
      {
        field: "materielCode",
        title: "所含物料编号",
        type: "string",
        width: 120,
        align: "left",
      },
      {
        field: "batchNo",
        title: "所含物料批次",
        type: "string",
        width: 200,
        align: "left"
      },
      {
        field: "materielInfo",
        title: "所含物料最早临期",
        type: "string",
        width: 140,
        align: "left",
      },
      {
        field: "sumStock",
        title: "总库存",
        type: "string",
        width: 140,
        align: "left",
      },
      {
        field: "row",
        title: "货位行",
        type: "string",
        width: 90,
        align: "left",
        hidden: true,
      },
      {
        field: "column",
        title: "货位列",
        type: "int",
        width: 120,
        align: "left",
        hidden: true,
      },
      {
        field: "layer",
        title: "货位层",
        type: "string",
        width: 200,
        align: "left",
        hidden: true,
      },
      {
        field: "depth",
        title: "货位深度",
        type: "string",
        width: 180,
        align: "left",
        hidden: true,
      },
      {
        field: "stockStatus",
        title: "库存状态",
        type: "string",
        width: 200,
        align: "left",
        bind: { key: "stockStatusEmun", data: [] },
      },
      {
        field: "locationType",
        title: "货位类型",
        type: "string",
        width: 100,
        align: "left",
        bind:{key: "locationTypeEnum", data: []}
      },
      {
        field: "locationStatus",
        title: "货位状态",
        type: "string",
        width: 120,
        align: "left",
        bind: { key: "locationStatusEnum", data: [] },
      },
      {
        field: "enalbeStatus",
        title: "禁用状态",
        type: "string",
        width: 80,
        align: "left",
        bind: { key: "enableStatusEnum", data: [] },
      },
      {
        field: "creater",
        title: "创建人",
        type: "string",
        width: 90,
        align: "left",
      },
      {
        field: "createDate",
        title: "创建时间",
        type: "datetime",
        width: 160,
        align: "left",
      },
      {
        field: "modifier",
        title: "修改人",
        type: "string",
        width: 100,
        align: "left",
      },
      {
        field: "modifyDate",
        title: "修改时间",
        type: "datetime",
        width: 160,
        align: "left",
      },
      {
        field: "remark",
        title: "备注",
        type: "string",
        width: 100,
        align: "left",
        hidden:true
      },
    ]);
    const detail = ref({
      cnName: "库存明细信息",
      table: "StockInfoDetail",
      columns: [
        {
          field: "id",
          title: "Id",
          type: "int",
          width: 90,
          hidden: true,
          readonly: true,
          require: true,
          align: "left",
        },
        {
          field: "stockId",
          title: "库存信息主键",
          type: "string",
          width: 90,
          align: "left",
          hidden: true
        },
        {
          field: "materielCode",
          title: "物料编号",
          type: "string",
          width: 110,
          align: "left",
        },
        {
          field: "materielName",
          title: "物料名称",
          type: "string",
          width: 130,
          align: "left",
        },
        {
          field: "orderNo",
          title: "单据编号",
          type: "decimal",
          width: 130,
          align: "left",
        },
        {
          field: "batchNo",
          title: "批次号",
          type: "string",
          width: 180,
          align: "left",
        },
        {
          field: "serialNumber",
          title: "序列号",
          type: "int",
          width: 120,
          align: "left",
          hidden: true,
        },
        {
          field: "stockQuantity",
          title: "库存数量",
          type: "string",
          width: 80,
          align: "left",
        },
        {
          field: "outboundQuantity",
          title: "出库数量",
          type: "string",
          width: 80,
          align: "left",
        },
        {
          field: "unit",
          title: "单位",
          type: "string",
          width: 50,
          align: "left",
        },
        {
          field: "productionDate",
          title: "生产日期",
          type: "string",
          width: 80,
          align: "left",
        },
        {
          field: "effectiveDate",
          title: "有效日期",
          type: "string",
          width: 80,
          align: "left",
        },
        {
          field: "status",
          title: "库存明细状态",
          type: "string",
          width: 120,
          align: "left",
          bind: { key: "stockStatusEmun", data: [] }
        },
        {
          field: "creater",
          title: "创建人",
          type: "string",
          width: 90,
          align: "left",
          hidden: true
        },
        {
          field: "createDate",
          title: "创建时间",
          type: "datetime",
          width: 160,
          align: "left",
          hidden: true
        },
        {
          field: "modifier",
          title: "修改人",
          type: "string",
          width: 100,
          align: "left",
          hidden: true
        },
        {
          field: "modifyDate",
          title: "修改时间",
          type: "datetime",
          width: 160,
          align: "left",
          hidden: true
        },
        {
          field: "remark",
          title: "备注",
          type: "string",
          width: 100,
          align: "left",
          hidden: true
        },
      ],
      sortName: "id",
      key: "id",
    });
<script>
import { ElButton, ElMessage, ElSelect, ElOption, ElEmpty, ElSkeleton } from "element-plus";
export default {
  data() {
    return {
      table,
      extend,
      editFormFields,
      editFormOptions,
      searchFormFields,
      searchFormOptions,
      columns,
      detail,
      warehouseList: [],
      roadwayNoList: [],
      selectedWarehouse: null,
      selectedRoadwayNo: null,
      selectedLocationCode: null,
      infoMsg: [
        { bgcolor: "#2BB3D5", msg: "空货位" },
        { bgcolor: "orange", msg: "有货" },
        { bgcolor: "lightgreen", msg: "锁定" },
        { bgcolor: "#ccc", msg: "禁用" }
      ],
      locationData: [],
      showTooltipFlag: false,
      currentLocation: null,
      tooltipPosition: { x: 0, y: 0 },
      currentColumn: null,
      currentLayer: null,
      loading: false
    };
  },
});
  computed: {
    sortedLayerData() {
      const layerMap = {};
      this.locationData.forEach(layer => {
        if (!layerMap[layer.layer]) {
          layerMap[layer.layer] = {
            layer: layer.layer,
            columns: []
          };
        }
        layer.columns?.forEach(column => {
          layerMap[layer.layer].columns.push({
            column: column.column,
            depths: column.depths || []
          });
        });
      });
      return Object.values(layerMap).sort((a, b) => a.layer - b.layer);
    },
    totalLocations() {
      let count = 0;
      this.locationData.forEach(layer => {
        layer.columns?.forEach(column => {
          count += column.depths?.length || 0;
        });
      });
      return count;
    }
  },
  methods: {
    // æŒ‰è¡Œåˆ†ç»„数据
    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));
    },
    sortedColumns(columnsObj) {
      // å°†å¯¹è±¡è½¬æ¢ä¸ºæ•°ç»„并按列号排序
      const columns = Object.values(columnsObj || {});
      return columns.sort((a, b) => a.column - b.column);
    },
    // æ¯”较行号的辅助方法
    compareRows(rowA, rowB) {
      // å¦‚果都是数字,按数字比较
      if (!isNaN(rowA) && !isNaN(rowB)) {
        return Number(rowA) - Number(rowB);
      }
      // å¦åˆ™æŒ‰å­—符串比较
      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() {
      try {
        this.loading = true;
        const response = await this.http.get("/api/LocationInfo/GetArea");
        this.warehouseList = response.data || [];
        if (this.warehouseList.length > 0) {
          this.selectedWarehouse = this.warehouseList[0].warehouseId;
          this.roadwayNoList = this.warehouseList[0].roadwayNo || [];
          if (this.roadwayNoList.length > 0) {
            this.selectedRoadwayNo = this.roadwayNoList[0];
            await this.fetchLocationStatus();
          }
        }
      } catch (error) {
        console.error("获取仓库数据失败:", error);
        this.warehouseList = [];
        this.roadwayNoList = [];
        ElMessage.error("获取仓库数据失败,请稍后重试");
      } finally {
        this.loading = false;
      }
    },
    async fetchLocationStatus() {
      if (!this.selectedWarehouse || !this.selectedRoadwayNo) {
        ElMessage.warning("请选择仓库和巷道");
        return;
      }
      try {
        this.loading = true;
        const response = await this.http.get(
          `/api/LocationInfo/GetLocationStatus?WarehouseId=${this.selectedWarehouse}&RoadwayNo=${this.selectedRoadwayNo}`
        );
        if (response.data && response.status) {
          this.locationData = response.data || [];
          // éªŒè¯æ•°æ®
          console.log("货位数据:", this.locationData);
          if (this.locationData.length > 0 && this.locationData[0].columns) {
            console.log("示例货位:", this.locationData[0].columns[0]?.depths?.[0]);
          }
          if (this.locationData.length === 0) {
            ElMessage.info("该巷道没有货位数据");
          }
        } else {
          this.locationData = [];
          ElMessage.error(response.data?.message || "获取货位状态失败");
        }
      } catch (error) {
        console.error("获取货位状态失败:", error);
        this.locationData = [];
        ElMessage.error("获取货位状态失败,请稍后重试");
      } finally {
        this.loading = false;
      }
    },
    handleWarehouseChange() {
      const selectedWarehouse = this.warehouseList.find(
        w => w.warehouseId === this.selectedWarehouse
      );
      this.roadwayNoList = selectedWarehouse ? selectedWarehouse.roadwayNo : [];
      this.selectedRoadwayNo = this.roadwayNoList.length > 0 ? this.roadwayNoList[0] : null;
      this.fetchLocationStatus();
    },
    getWarehouseName(warehouseId) {
      const warehouseMap = {
        1: '原材料仓',
        2: '成品仓'
      };
      return warehouseMap[warehouseId] || `仓库 ${warehouseId}`;
    },
    handleRoadwayNoChange() {
      this.fetchLocationStatus();
    },
    getLocationStatusClass(depth) {
      if (depth.enableStatus !== 0) {
        return 'location-disabled';
      }
      switch (depth.locationStatus) {
        case 0: return 'location-empty';     // ç©ºè´§ä½
        case 1: return 'location-locked';    // é”å®š
        case 100: return 'location-occupied'; // æœ‰è´§
        default: return 'location-other';
      }
    },
    getLayerLocations(layer) {
      let count = 0;
      layer.columns?.forEach(column => {
        count += column.depths?.length || 0;
      });
      return count;
    },
    showTooltip(depth, column, layer, event) {
      this.currentLocation = depth;
      this.currentColumn = column;
      this.currentLayer = layer;
      this.showTooltipFlag = true;
      const offsetX = 15;
      const offsetY = 15;
      this.tooltipPosition = {
        x: event.clientX + offsetX,
        y: event.clientY + offsetY,
      };
    },
    hideTooltip() {
      this.showTooltipFlag = false;
      this.currentLocation = null;
      this.currentColumn = null;
      this.currentLayer = null;
    },
    handleLocationClick(depth) {
      console.log('点击货位:', depth);
      // è¿™é‡Œå¯ä»¥æ·»åŠ ç‚¹å‡»è´§ä½çš„å¤„ç†é€»è¾‘
      ElMessage.info(`选中货位: ${depth.locationCode || '未知货位'}`);
    },
    getStatusText(status) {
      const statusMap = {
        0: "空货位",
        1: "锁定",
        100: "有货",
      };
      return statusMap[status] || "未知状态";
    },
    getStatusClass(status) {
      const classMap = {
        0: 'status-empty',
        1: 'status-locked',
        100: 'status-occupied'
      };
      return classMap[status] || '';
    },
    getLocationTypeText(type) {
      const typeMap = {
        1: "布卷",
        2: "松布卷",
        3: "成品货位"
      };
      return typeMap[type] || "未知类型";
    },
    getEnableStatusText(status) {
      return status === 0 ? '启用' : '禁用';
    }
  },
  mounted() {
    this.fetchWarehouseData();
  },
  components: {
    ElButton,
    ElSelect,
    ElOption,
    ElEmpty,
    ElSkeleton
  },
};
</script>
<style scoped>
/* æ ·å¼éƒ¨åˆ† */
.container {
  display: flex;
  flex-direction: column;
  height: 100vh;
  padding: 16px;
  box-sizing: border-box;
  background-color: #f0f2f5;
}
.content-wrapper {
  display: flex;
  flex: 1;
  min-height: 0;
  gap: 16px;
}
.control-panel {
  width: 260px;
  background-color: white;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  overflow: hidden;
}
.panel-header {
  padding: 16px;
  border-bottom: 1px solid #e8e8e8;
  background-color: #fafafa;
}
.panel-header h3 {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  color: #333;
}
.panel-body {
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.form-group {
  margin-bottom: 0;
}
.form-label {
  display: block;
  margin-bottom: 6px;
  font-size: 14px;
  color: #666;
  font-weight: 500;
}
.full-width {
  width: 100%;
}
.refresh-btn {
  margin-top: 8px;
  width: 100%;
  height: 32px;
}
.legend-section {
  margin-top: 8px;
  padding-top: 16px;
  border-top: 1px solid #e8e8e8;
}
.legend-header h4 {
  margin: 0 0 12px 0;
  font-size: 15px;
  font-weight: 600;
  color: #333;
}
.legend-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.legend-item {
  display: flex;
  align-items: center;
  gap: 10px;
}
.color-box {
  width: 20px;
  height: 20px;
  border-radius: 4px;
  border: 1px solid rgba(0, 0, 0, 0.1);
  flex-shrink: 0;
}
.legend-label {
  font-size: 13px;
  color: #666;
}
.main-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.loading-container {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: white;
  border-radius: 8px;
  padding: 24px;
}
.location-container {
  flex: 1;
  display: flex;
  flex-direction: column;
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  overflow: hidden;
  min-height: 0;
}
.location-header {
  padding: 16px 20px;
  background: #fafafa;
  border-bottom: 1px solid #e8e8e8;
  flex-shrink: 0;
}
.location-info {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 14px;
}
.total-count {
  font-weight: 600;
  color: #1890ff;
}
.layers-container {
  width: 100%;
  height: 100%;
  flex: 1;
  overflow-y: auto;
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 24px;
}
.layer-row {
  background: #f9f9f9;
  border-radius: 8px;
  border: 1px solid #e8e8e8;
  display: flex;
  flex-direction: column;
  min-height: auto;
}
.layer-title-area {
  padding: 12px 16px;
  background: #f0f0f0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-shrink: 0;
}
.layer-title {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  color: #333;
}
.layer-count {
  font-size: 12px;
  color: #666;
  background: white;
  padding: 4px 10px;
  border-radius: 12px;
  border: 1px solid #ddd;
}
.layer-content-wrap {
  width: 100%;
  overflow: hidden;
  flex: 1;
  padding: 16px;
}
/* è¡Œç»„样式 */
.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: 8px; /* å‡å°é—´è· */
}
/* ç¼©å°è´§ä½æ¡†å°ºå¯¸ */
.location-column {
  flex: 0 0 auto;
  width: 70px; /* å‡å°å®½åº¦ */
  min-height: 30px; /* å‡å°é«˜åº¦ */
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
}
.locations-wrapper {
  display: flex;
  flex-direction: column;
  gap: 4px; /* å‡å°é—´è· */
  flex: 1;
  min-height: 0;
}
/* ç¼©å°è´§ä½é¡¹ */
.location-item {
  min-height: 32px; /* å‡å°é«˜åº¦ */
  padding: 4px 2px; /* å‡å°å†…边距 */
  border-radius: 4px;
  text-align: center;
  cursor: pointer;
  border: 1px solid rgba(0, 0, 0, 0.1);
  transition: all 0.2s;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  flex: 1;
}
.location-item:hover {
  transform: scale(1.05);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
  z-index: 10;
}
/* ç¼©å°è´§ä½ç¼–号字体 */
.location-code {
  font-size: 9px; /* å‡å°å­—体 */
  font-weight: 500;
  line-height: 1.2;
  word-break: break-all;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.location-status {
  font-size: 8px; /* å‡å°å­—体 */
  padding: 1px 3px;
  border-radius: 2px;
  background: rgba(0, 0, 0, 0.1);
}
/* è´§ä½çŠ¶æ€é¢œè‰² */
.location-empty {
  background-color: #2BB3D5;
  color: white;
}
.location-occupied {
  background-color: orange;
  color: white;
}
.location-locked {
  background-color: lightgreen;
  color: #333;
}
.location-disabled {
  background-color: #ccc;
  color: #666;
  cursor: not-allowed;
}
.location-other {
  background-color: #b7ba6b;
  color: white;
}
.empty-container {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: white;
  border-radius: 8px;
}
.location-tooltip {
  position: fixed;
  z-index: 9999;
  background-color: white;
  border: 1px solid #ddd;
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  pointer-events: none;
  min-width: 220px;
  max-width: 300px;
}
.tooltip-content {
  overflow: hidden;
}
.tooltip-header {
  padding: 12px 16px;
  background-color: #1890ff;
  color: white;
}
.tooltip-header h4 {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
}
.tooltip-body {
  padding: 12px 16px;
}
.tooltip-row {
  display: flex;
  margin-bottom: 8px;
  font-size: 13px;
  line-height: 1.4;
}
.tooltip-row:last-child {
  margin-bottom: 0;
}
.tooltip-label {
  flex: 0 0 80px;
  color: #666;
  font-weight: 500;
}
.tooltip-value {
  flex: 1;
  color: #333;
  word-break: break-all;
}
.status-disabled {
  color: #f5222d;
}
/* æ»šåŠ¨æ¡æ ·å¼ */
.layers-container::-webkit-scrollbar {
  width: 8px;
}
.layers-container::-webkit-scrollbar-track {
  background: #f1f1f1;
  border-radius: 4px;
}
.layers-container::-webkit-scrollbar-thumb {
  background: #c1c1c1;
  border-radius: 4px;
}
.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>