Admin
2026-01-22 bde09c027eee16a7b05b82e71dea4d3405d6d52d
ÏîÄ¿´úÂë/WMS/WIDESEA_WMSClient/src/views/stock/stockView.vue
@@ -1,392 +1,896 @@
<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="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>
                      </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">{{ getLocationCode(currentLocation) }}</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>
              </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,
      infoMsg: [
        { bgcolor: "lightgreen", msg: "空货位" },
        { bgcolor: "orange", msg: "有货" },
        { bgcolor: "#2BB3D5", msg: "锁定" },
        { bgcolor: "#ccc", msg: "禁用" },
        { bgcolor: "#b7ba6b", 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: {
    sortedColumns(columns) {
      // æŒ‰åˆ—号排序
      return columns?.sort((a, b) => a.column - b.column) || [];
    },
    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);
      }) || [];
    },
    // æ¯”较行号的辅助方法
    compareRows(rowA, rowB) {
      // å¦‚果都是数字,按数字比较
      if (!isNaN(rowA) && !isNaN(rowB)) {
        return Number(rowA) - Number(rowB);
      }
      // å¦åˆ™æŒ‰å­—符串比较
      return String(rowA).localeCompare(String(rowB));
    },
    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 || [];
          // éªŒè¯æ•°æ®ä¸­æ˜¯å¦æœ‰row字段
          console.log("货位数据示例:", this.locationData[0]);
          if (this.locationData.length > 0 && this.locationData[0].columns) {
            console.log("第一个货位的row字段:", this.locationData[0].columns[0]?.depths?.[0]?.row);
          }
          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);
    },
    getLocationCode(location) {
      if (!location) return '';
      return `${location.row || '?'}排-${this.currentColumn}列-${this.currentLayer}层-${location.depth === 1 ? '浅' : 'æ·±'}`;
    },
    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;
}
.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 {
  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;
}
.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 {
  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;
}
.layer-content {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  padding: 16px;
  min-width: 0;
  box-sizing: border-box;
}
.location-column {
  flex: 0 0 auto;
  width: 140px;
  min-height: 120px;
  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;
  flex: 1;
  min-height: 0;
}
.location-item {
  min-height: 40px;
  padding: 6px 4px;
  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: 2px;
  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: 11px;
  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);
}
.location-status {
  font-size: 10px;
  padding: 1px 4px;
  border-radius: 2px;
  background: rgba(0, 0, 0, 0.1);
}
.location-empty {
  background-color: lightgreen;
  color: #333;
}
.location-occupied {
  background-color: orange;
  color: white;
}
.location-locked {
  background-color: #2BB3D5;
  color: white;
}
.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;
}
@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;
}
.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;
}
</style>