zhangwencui
2 天以前 8ab349b4e271a068b67ad767587e23685760bd12
销售统计看板和生产统计看板页面设计
已添加2个文件
1789 ■■■■■ 文件已修改
src/views/reportAnalysis/productionStatistics/index.vue 866 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/reportAnalysis/salesStatistics/index.vue 923 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/reportAnalysis/productionStatistics/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,866 @@
<template>
  <div class="dashboard-container">
    <div class="data-dashboard">
    <!-- é¡¶éƒ¨æ ‡é¢˜æ  -->
    <!-- <div class="dashboard-header">
      <div class="factory-name">生产统计看板</div>
    </div> -->
    <!-- ç­›é€‰åŒºåŸŸ -->
    <div class="filter-area">
      <div class="filter-section">
        <span class="filter-label">时间维度:</span>
        <el-radio-group v-model="dateType" @change="handleDateTypeChange" class="radio-group">
          <el-radio-button label="month">月度</el-radio-button>
          <el-radio-button label="year">年度</el-radio-button>
        </el-radio-group>
      </div>
      <div class="filter-section">
        <span class="filter-label">产品类型:</span>
        <el-radio-group v-model="productType" @change="handleProductTypeChange" class="radio-group">
          <el-radio-button label="block">砌块</el-radio-button>
          <el-radio-button label="plate">板材</el-radio-button>
        </el-radio-group>
      </div>
    </div>
    <!-- ä¸»è¦å†…容区域 -->
    <div class="dashboard-content">
      <!-- ç¬¬ä¸€è¡Œ -->
      <div class="row row-1">
        <div class="panel-card card-1">
          <div class="panel-title">产量指标</div>
          <div class="chart-container">
            <div ref="productionChart" style="width: 100%; height: 100%"></div>
          </div>
        </div>
        <div class="panel-card card-2">
          <div class="panel-title">固废处理量</div>
          <div class="chart-container">
            <div ref="solidWasteChart" style="width: 100%; height: 100%"></div>
          </div>
        </div>
        <div class="panel-card card-3">
          <div class="panel-title">综合统计</div>
          <div class="stats-grid">
            <div class="stat-item">
              <div class="stat-label">总产能</div>
              <div class="stat-value">{{ totalProduction }}</div>
              <div class="stat-unit">立方米</div>
            </div>
            <div class="stat-item">
              <div class="stat-label">总固废处理</div>
              <div class="stat-value">{{ totalSolidWaste }}</div>
              <div class="stat-unit">吨</div>
            </div>
            <div class="stat-item">
              <div class="stat-label">平均单耗</div>
              <div class="stat-value">{{ averageUnitConsumption }}</div>
              <div class="stat-unit">吨/立方米</div>
            </div>
            <div class="stat-item">
              <div class="stat-label">总能耗</div>
              <div class="stat-value">{{ totalEnergy }}</div>
              <div class="stat-unit">kWh</div>
            </div>
          </div>
        </div>
      </div>
      <!-- ç¬¬äºŒè¡Œ -->
      <div class="row row-2">
        <div class="panel-card card-4">
          <div class="panel-title">生产成本单耗</div>
          <div class="chart-container">
            <div ref="costChart" style="width: 100%; height: 100%"></div>
          </div>
        </div>
        <div class="panel-card card-5">
          <div class="panel-title">生产能耗数据</div>
          <div class="chart-container">
            <div ref="energyChart" style="width: 100%; height: 100%"></div>
          </div>
        </div>
      </div>
      <!-- ç¬¬ä¸‰è¡Œ -->
      <div class="row row-3">
        <div class="panel-card card-6">
          <div class="panel-title">单耗数据明细</div>
          <div class="table-container">
            <el-table :data="costTableData" style="width: 100%">
              <el-table-column prop="material" label="物料类型" width="120" />
              <el-table-column prop="unit" label="单位" width="100" />
              <el-table-column prop="monthlyConsumption" label="月度累计用量" />
              <el-table-column prop="monthlyProduction" label="月度累计产量" />
              <el-table-column prop="monthlyUnitConsumption" label="月度累计单耗" />
              <el-table-column prop="yearlyConsumption" label="年度累计用量" />
              <el-table-column prop="yearlyProduction" label="年度累计产量" />
              <el-table-column prop="yearlyUnitConsumption" label="年度累计单耗" />
            </el-table>
          </div>
        </div>
      </div>
    </div>
    </div>
  </div>
</template>
<script setup>
import { ref, computed, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'
import * as echarts from 'echarts'
// ç­›é€‰æ¡ä»¶
const dateType = ref('month') // month æˆ– year
const productType = ref('block') // block æˆ– plate
// å›¾è¡¨å¼•用
const productionChart = ref(null)
const solidWasteChart = ref(null)
const costChart = ref(null)
const energyChart = ref(null)
// å›¾è¡¨å®žä¾‹
let productionChartInstance = null
let solidWasteChartInstance = null
let costChartInstance = null
let energyChartInstance = null
// æ¨¡æ‹Ÿæ•°æ®
const productionData = ref({
  month: [
    { name: '1月', block: 1200, plate: 800 },
    { name: '2月', block: 1300, plate: 850 },
    { name: '3月', block: 1100, plate: 750 },
    { name: '4月', block: 1400, plate: 900 },
    { name: '5月', block: 1500, plate: 950 },
    { name: '6月', block: 1350, plate: 880 },
    { name: '7月', block: 1450, plate: 920 },
    { name: '8月', block: 1600, plate: 1000 },
    { name: '9月', block: 1550, plate: 980 },
    { name: '10月', block: 1700, plate: 1050 },
    { name: '11月', block: 1650, plate: 1020 },
    { name: '12月', block: 1800, plate: 1100 }
  ],
  year: [
    { name: '2023', block: 15000, plate: 9500 },
    { name: '2024', block: 16500, plate: 10200 },
    { name: '2025', block: 18000, plate: 11000 }
  ]
})
const solidWasteData = ref({
  month: [
    { name: '1月', ç²‰ç…¤ç°: 200, çŸ³è†: 150, çŸ³ç°: 100 },
    { name: '2月', ç²‰ç…¤ç°: 220, çŸ³è†: 160, çŸ³ç°: 110 },
    { name: '3月', ç²‰ç…¤ç°: 190, çŸ³è†: 140, çŸ³ç°: 95 },
    { name: '4月', ç²‰ç…¤ç°: 230, çŸ³è†: 170, çŸ³ç°: 115 },
    { name: '5月', ç²‰ç…¤ç°: 240, çŸ³è†: 180, çŸ³ç°: 120 },
    { name: '6月', ç²‰ç…¤ç°: 225, çŸ³è†: 165, çŸ³ç°: 112 }
  ],
  year: [
    { name: '2023', ç²‰ç…¤ç°: 2500, çŸ³è†: 1800, çŸ³ç°: 1200 },
    { name: '2024', ç²‰ç…¤ç°: 2700, çŸ³è†: 1950, çŸ³ç°: 1300 },
    { name: '2025', ç²‰ç…¤ç°: 2900, çŸ³è†: 2100, çŸ³ç°: 1400 }
  ]
})
const costData = ref({
  materials: ['æ°´æ³¥', '铝粉膏', '脱模剂', '防腐剂', '氯化剂', '冷拔丝'],
  month: {
    consumption: [1200, 50, 80, 30, 40, 60],
    production: [12000, 12000, 12000, 8000, 8000, 8000],
    unitConsumption: [0.1, 0.0042, 0.0067, 0.0038, 0.005, 0.0075]
  },
  year: {
    consumption: [14000, 600, 950, 350, 480, 720],
    production: [140000, 140000, 140000, 95000, 95000, 95000],
    unitConsumption: [0.1, 0.0043, 0.0068, 0.0037, 0.0051, 0.0076]
  }
})
const energyData = ref({
  month: [
    { name: '1月', ç”µé‡: 12000, æ°´é‡: 8000, æ°”量: 5000 },
    { name: '2月', ç”µé‡: 13000, æ°´é‡: 8500, æ°”量: 5500 },
    { name: '3月', ç”µé‡: 11000, æ°´é‡: 7500, æ°”量: 4800 },
    { name: '4月', ç”µé‡: 14000, æ°´é‡: 9000, æ°”量: 6000 },
    { name: '5月', ç”µé‡: 15000, æ°´é‡: 9500, æ°”量: 6500 },
    { name: '6月', ç”µé‡: 13500, æ°´é‡: 8800, æ°”量: 5800 }
  ],
  year: [
    { name: '2023', ç”µé‡: 140000, æ°´é‡: 95000, æ°”量: 65000 },
    { name: '2024', ç”µé‡: 150000, æ°´é‡: 100000, æ°”量: 70000 },
    { name: '2025', ç”µé‡: 160000, æ°´é‡: 105000, æ°”量: 75000 }
  ]
})
// è®¡ç®—属性
const productionChartOption = computed(() => {
  const data = productionData.value[dateType.value]
  return {
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'shadow'
      }
    },
    legend: {
      data: ['砌块', '板材'],
      textStyle: {
        color: '#333'
      }
    },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '3%',
      containLabel: true
    },
    xAxis: {
      type: 'category',
      data: data.map(item => item.name),
      axisLabel: {
        color: '#333'
      }
    },
    yAxis: {
      type: 'value',
      name: '产量 (立方米)',
      axisLabel: {
        color: '#333'
      }
    },
    series: [
      {
        name: '砌块',
        type: 'line',
        data: data.map(item => item.block),
        smooth: true,
        lineStyle: {
          width: 3
        },
        itemStyle: {
          color: '#409EFF'
        }
      },
      {
        name: '板材',
        type: 'line',
        data: data.map(item => item.plate),
        smooth: true,
        lineStyle: {
          width: 3
        },
        itemStyle: {
          color: '#67C23A'
        }
      }
    ]
  }
})
const solidWasteChartOption = computed(() => {
  const data = solidWasteData.value[dateType.value]
  return {
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'shadow'
      }
    },
    legend: {
      data: ['粉煤灰', '石膏', '石灰'],
      textStyle: {
        color: '#333'
      }
    },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '3%',
      containLabel: true
    },
    xAxis: {
      type: 'category',
      data: data.map(item => item.name),
      axisLabel: {
        color: '#333'
      }
    },
    yAxis: {
      type: 'value',
      name: '处理量 (吨)',
      axisLabel: {
        color: '#333'
      }
    },
    series: [
      {
        name: '粉煤灰',
        type: 'bar',
        data: data.map(item => item.粉煤灰),
        itemStyle: {
          color: '#909399'
        }
      },
      {
        name: '石膏',
        type: 'bar',
        data: data.map(item => item.石膏),
        itemStyle: {
          color: '#E6A23C'
        }
      },
      {
        name: '石灰',
        type: 'bar',
        data: data.map(item => item.石灰),
        itemStyle: {
          color: '#F56C6C'
        }
      }
    ]
  }
})
const costChartOption = computed(() => {
  const data = costData.value
  return {
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'shadow'
      }
    },
    legend: {
      data: ['月度单耗', '年度单耗'],
      textStyle: {
        color: '#333'
      }
    },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '3%',
      containLabel: true
    },
    xAxis: {
      type: 'category',
      data: data.materials,
      axisLabel: {
        color: '#333',
        rotate: 45
      }
    },
    yAxis: {
      type: 'value',
      name: '单耗 (吨/立方米)',
      axisLabel: {
        color: '#333'
      }
    },
    series: [
      {
        name: '月度单耗',
        type: 'bar',
        data: data.month.unitConsumption,
        itemStyle: {
          color: '#409EFF'
        }
      },
      {
        name: '年度单耗',
        type: 'bar',
        data: data.year.unitConsumption,
        itemStyle: {
          color: '#67C23A'
        }
      }
    ]
  }
})
const energyChartOption = computed(() => {
  const data = energyData.value[dateType.value]
  return {
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'shadow'
      }
    },
    legend: {
      data: ['电量', '水量', '气量'],
      textStyle: {
        color: '#333'
      }
    },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '3%',
      containLabel: true
    },
    xAxis: {
      type: 'category',
      data: data.map(item => item.name),
      axisLabel: {
        color: '#333'
      }
    },
    yAxis: {
      type: 'value',
      name: '能耗量',
      axisLabel: {
        color: '#333'
      }
    },
    series: [
      {
        name: '电量',
        type: 'line',
        data: data.map(item => item.电量),
        smooth: true,
        lineStyle: {
          width: 3
        },
        itemStyle: {
          color: '#409EFF'
        }
      },
      {
        name: '水量',
        type: 'line',
        data: data.map(item => item.水量),
        smooth: true,
        lineStyle: {
          width: 3
        },
        itemStyle: {
          color: '#67C23A'
        }
      },
      {
        name: '气量',
        type: 'line',
        data: data.map(item => item.气量),
        smooth: true,
        lineStyle: {
          width: 3
        },
        itemStyle: {
          color: '#E6A23C'
        }
      }
    ]
  }
})
const costTableData = computed(() => {
  const data = costData.value
  const materials = data.materials
  const monthData = data.month
  const yearData = data.year
  return materials.map((material, index) => ({
    material,
    unit: '吨/立方米',
    monthlyConsumption: monthData.consumption[index],
    monthlyProduction: monthData.production[index],
    monthlyUnitConsumption: monthData.unitConsumption[index].toFixed(4),
    yearlyConsumption: yearData.consumption[index],
    yearlyProduction: yearData.production[index],
    yearlyUnitConsumption: yearData.unitConsumption[index].toFixed(4)
  }))
})
const totalProduction = computed(() => {
  const data = productionData.value[dateType.value]
  if (dateType.value === 'month') {
    return data.reduce((sum, item) => sum + item[productType.value === 'block' ? 'block' : 'plate'], 0)
  } else {
    return data[data.length - 1][productType.value === 'block' ? 'block' : 'plate']
  }
})
const totalSolidWaste = computed(() => {
  const data = solidWasteData.value[dateType.value]
  if (dateType.value === 'month') {
    return data.reduce((sum, item) => sum + item.粉煤灰 + item.石膏 + item.石灰, 0)
  } else {
    const lastItem = data[data.length - 1]
    return lastItem.粉煤灰 + lastItem.石膏 + lastItem.石灰
  }
})
const averageUnitConsumption = computed(() => {
  const data = costData.value
  const unitConsumption = dateType.value === 'month' ? data.month.unitConsumption : data.year.unitConsumption
  const average = unitConsumption.reduce((sum, value) => sum + value, 0) / unitConsumption.length
  return average.toFixed(4)
})
const totalEnergy = computed(() => {
  const data = energyData.value[dateType.value]
  if (dateType.value === 'month') {
    return data.reduce((sum, item) => sum + item.电量 + item.水量 + item.气量, 0)
  } else {
    const lastItem = data[data.length - 1]
    return lastItem.电量 + lastItem.水量 + lastItem.气量
  }
})
// äº‹ä»¶å¤„理
const handleDateTypeChange = () => {
  updateCharts()
}
const handleProductTypeChange = () => {
  updateCharts()
}
// åˆå§‹åŒ–图表
const initCharts = () => {
  if (productionChart.value) {
    productionChartInstance = echarts.init(productionChart.value)
    productionChartInstance.setOption(productionChartOption.value)
  }
  if (solidWasteChart.value) {
    solidWasteChartInstance = echarts.init(solidWasteChart.value)
    solidWasteChartInstance.setOption(solidWasteChartOption.value)
  }
  if (costChart.value) {
    costChartInstance = echarts.init(costChart.value)
    costChartInstance.setOption(costChartOption.value)
  }
  if (energyChart.value) {
    energyChartInstance = echarts.init(energyChart.value)
    energyChartInstance.setOption(energyChartOption.value)
  }
}
// æ›´æ–°å›¾è¡¨
const updateCharts = () => {
  if (productionChartInstance) {
    productionChartInstance.setOption(productionChartOption.value)
  }
  if (solidWasteChartInstance) {
    solidWasteChartInstance.setOption(solidWasteChartOption.value)
  }
  if (costChartInstance) {
    costChartInstance.setOption(costChartOption.value)
  }
  if (energyChartInstance) {
    energyChartInstance.setOption(energyChartOption.value)
  }
}
// è°ƒæ•´å›¾è¡¨å¤§å°
const resizeCharts = () => {
  productionChartInstance?.resize()
  solidWasteChartInstance?.resize()
  costChartInstance?.resize()
  energyChartInstance?.resize()
}
// çª—口大小变化处理
const handleResize = () => {
  // å»¶è¿Ÿæ‰§è¡Œï¼Œç¡®ä¿DOM更新完成
  setTimeout(() => {
    resizeCharts()
  }, 100)
}
// ç”Ÿå‘½å‘¨æœŸé’©å­
onMounted(() => {
  // ä½¿ç”¨nextTick确保DOM完全渲染后再初始化
  nextTick(() => {
    // åˆå§‹åŒ–图表
    initCharts()
  })
  window.addEventListener('resize', handleResize)
})
onBeforeUnmount(() => {
  window.removeEventListener('resize', handleResize)
  // é”€æ¯å›¾è¡¨å®žä¾‹
  productionChartInstance?.dispose()
  solidWasteChartInstance?.dispose()
  costChartInstance?.dispose()
  energyChartInstance?.dispose()
})
</script>
<style scoped>
/* å¤–部容器 - å æ®æ•´ä¸ªè§†å£ */
.dashboard-container {
  position: relative;
  width: 100%;
  /* é¡µé¢åœ¨å¸¸è§„布局下(有顶栏)默认减去 84px,避免内容被裁切 */
  min-height: calc(100vh - 84px);
  background-color: #f5f7fa;
  overflow: hidden;
}
/* å†…部内容区域 - è‡ªé€‚应宽度 */
.data-dashboard {
  position: relative;
  width: 100%;
  min-height: 100%;
  background-color: #ffffff;
  box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
}
.dashboard-header {
  position: relative;
  z-index: 1;
  height: 86px;
  background-color: #ffffff;
  border-bottom: 1px solid #e4e7ed;
  display: flex;
  align-items: center;
  justify-content: center;
}
.factory-name {
  font-weight: 600;
  font-size: 32px;
  color: #303133;
}
.filter-area {
  padding: 20px;
  background-color: #ffffff;
  border-bottom: 1px solid #e4e7ed;
  display: flex;
  gap: 40px;
  align-items: center;
  flex-wrap: wrap;
}
.filter-section {
  display: flex;
  align-items: center;
  gap: 10px;
}
.filter-label {
  font-size: 14px;
  font-weight: 500;
  color: #303133;
  white-space: nowrap;
}
.radio-group {
  display: flex;
  align-items: center;
}
/* æŒ‰é’®æ ·å¼ */
:deep(.el-radio-button__inner) {
  border-radius: 4px;
  padding: 8px 20px;
  font-size: 14px;
  transition: all 0.3s ease;
}
:deep(.el-radio-button__orig-radio:checked + .el-radio-button__inner) {
  background-color: #409eff;
  border-color: #409eff;
  color: #ffffff;
  box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3);
}
:deep(.el-radio-button__inner:hover) {
  color: #409eff;
  border-color: #c6e2ff;
}
:deep(.el-radio-button:first-child .el-radio-button__inner) {
  border-radius: 4px 0 0 4px;
}
:deep(.el-radio-button:last-child .el-radio-button__inner) {
  border-radius: 0 4px 4px 0;
}
.dashboard-content {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 20px;
  min-height: 800px;
  overflow: hidden;
}
/* è¡Œå¸ƒå±€ */
.row {
  display: flex;
  gap: 20px;
  align-items: stretch;
}
/* ç¬¬ä¸€è¡Œï¼š3个卡片 */
.row-1 {
  height: 300px;
}
/* ç¬¬äºŒè¡Œï¼š2个卡片 */
.row-2 {
  height: 300px;
}
/* ç¬¬ä¸‰è¡Œï¼š1个卡片 */
.row-3 {
  min-height: 250px;
}
/* å¡ç‰‡æ ·å¼ */
.panel-card {
  background-color: #ffffff;
  border-radius: 8px;
  border: 1px solid #e4e7ed;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  transition: all 0.3s ease;
}
.panel-card:hover {
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  transform: translateY(-2px);
}
/* å¡ç‰‡å¸ƒå±€ */
.card-1 {
  flex: 1;
}
.card-2 {
  flex: 1;
}
.card-3 {
  flex: 0.8;
}
.card-4 {
  flex: 1.2;
}
.card-5 {
  flex: 0.8;
}
.card-6 {
  flex: 1;
}
.panel-card {
  background-color: #ffffff;
  border-radius: 8px;
  border: 1px solid #e4e7ed;
  overflow: hidden;
  flex: 1;
  display: flex;
  flex-direction: column;
}
.panel-title {
  padding: 15px 20px;
  font-size: 16px;
  font-weight: 500;
  color: #303133;
  border-bottom: 1px solid #e4e7ed;
  background-color: #fafafa;
}
.chart-container {
  flex: 1;
  padding: 20px;
}
.table-container {
  flex: 1;
  padding: 20px;
  overflow: auto;
}
.stats-grid {
  flex: 1;
  padding: 15px;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 15px;
}
.stat-item {
  background-color: #fafafa;
  border-radius: 8px;
  padding: 15px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  border: 1px solid #e4e7ed;
  min-height: 80px;
}
.stat-label {
  font-size: 13px;
  color: #606266;
  margin-bottom: 8px;
}
.stat-value {
  font-size: 20px;
  font-weight: 600;
  color: #303133;
  margin-bottom: 3px;
}
.stat-unit {
  font-size: 11px;
  color: #909399;
}
/* è¡¨æ ¼æ ·å¼ */
:deep(.el-table) {
  border-radius: 8px;
  overflow: hidden;
}
:deep(.el-table th) {
  background-color: #fafafa;
  font-weight: 500;
}
:deep(.el-table tr:hover > td) {
  background-color: #ecf5ff;
}
/* æŒ‰é’®æ ·å¼ */
:deep(.el-radio-button__inner) {
  border-radius: 4px;
}
:deep(.el-radio-button__orig-radio:checked + .el-radio-button__inner) {
  background-color: #409eff;
  border-color: #409eff;
  color: #ffffff;
}
</style>
src/views/reportAnalysis/salesStatistics/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,923 @@
<template>
  <div class="sales-statistics-container">
    <div class="data-dashboard">
      <!-- é¡µé¢æ ‡é¢˜ -->
      <!-- <div class="dashboard-header">
        <div class="factory-name">销售统计看板</div>
      </div> -->
      <!-- ç­›é€‰æ¡ä»¶ -->
      <div class="filter-area">
        <div class="filter-section">
          <span class="filter-label">时间范围:</span>
          <el-date-picker
            v-model="dateRange"
            type="daterange"
            range-separator="至"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
            value-format="YYYY-MM-DD"
            @change="handleDateChange"
            style="width: 240px;"
          />
        </div>
        <div class="filter-section">
          <span class="filter-label">产品类型:</span>
          <el-select v-model="productType" placeholder="请选择产品类型" @change="handleFilterChange" style="width: 160px;">
            <el-option label="全部" value="" />
            <el-option label="砌块" value="block" />
            <el-option label="板材" value="board" />
            <el-option label="型材" value="profile" />
          </el-select>
        </div>
        <div class="filter-section">
          <span class="filter-label">销售区域:</span>
          <el-select v-model="salesArea" placeholder="请选择销售区域" @change="handleFilterChange" style="width: 160px;">
            <el-option label="全部" value="" />
            <el-option label="华东" value="east" />
            <el-option label="华北" value="north" />
            <el-option label="华南" value="south" />
            <el-option label="西南" value="southwest" />
            <el-option label="西北" value="northwest" />
          </el-select>
        </div>
        <div class="filter-section">
          <span class="filter-label">统计维度:</span>
          <el-select v-model="statDimension" placeholder="请选择统计维度" @change="handleFilterChange" style="width: 160px;">
            <el-option label="月度" value="month" />
            <el-option label="年度" value="year" />
          </el-select>
        </div>
      </div>
      <div class="dashboard-content">
        <!-- æ ¸å¿ƒæŒ‡æ ‡å¡ç‰‡ -->
        <div class="row row-1">
          <div class="panel-card card-1">
            <div class="panel-title">合计销量</div>
            <div class="stats-grid">
              <div class="stat-item">
                <div class="stat-value">{{ totalSalesVolume }}</div>
                <div class="stat-unit">立方米</div>
                <div class="stat-change">{{ salesVolumeChange }}%</div>
              </div>
            </div>
          </div>
          <div class="panel-card card-2">
            <div class="panel-title">销售金额</div>
            <div class="stats-grid">
              <div class="stat-item">
                <div class="stat-value">{{ totalSalesAmount }}</div>
                <div class="stat-unit">万元</div>
                <div class="stat-change">{{ salesAmountChange }}%</div>
              </div>
            </div>
          </div>
          <div class="panel-card card-3">
            <div class="panel-title">新增客户</div>
            <div class="stats-grid">
              <div class="stat-item">
                <div class="stat-value">{{ newCustomerCount }}</div>
                <div class="stat-unit">个</div>
                <div class="stat-change">{{ customerCountChange }}%</div>
              </div>
            </div>
          </div>
          <div class="panel-card card-4">
            <div class="panel-title">合计客户</div>
            <div class="stats-grid">
              <div class="stat-item">
                <div class="stat-value">{{ totalCustomerCount }}</div>
                <div class="stat-unit">个</div>
                <div class="stat-change">{{ totalCustomerChange }}%</div>
              </div>
            </div>
          </div>
        </div>
        <!-- é”€é‡å’Œé”€å”®é‡‘额趋势 -->
        <div class="row row-2">
          <div class="panel-card card-5">
            <div class="panel-title">销量趋势</div>
            <div class="chart-container">
              <div ref="salesVolumeChart" style="width: 100%; height: 100%;"></div>
            </div>
          </div>
          <div class="panel-card card-6">
            <div class="panel-title">销售金额趋势</div>
            <div class="chart-container">
              <div ref="salesAmountChart" style="width: 100%; height: 100%;"></div>
            </div>
          </div>
        </div>
        <!-- ç´¯è®¡æ•°æ®è¶‹åŠ¿ -->
        <div class="row row-3">
          <div class="panel-card card-10">
            <div class="panel-title">累计销量趋势</div>
            <div class="chart-container">
              <div ref="cumulativeSalesVolumeChart" style="width: 100%; height: 100%;"></div>
            </div>
          </div>
          <div class="panel-card card-11">
            <div class="panel-title">累计销售金额趋势</div>
            <div class="chart-container">
              <div ref="cumulativeSalesAmountChart" style="width: 100%; height: 100%;"></div>
            </div>
          </div>
        </div>
        <!-- å›¾è¡¨åŒºåŸŸå’Œè¡¨æ ¼ -->
        <div class="row row-4">
          <!-- å·¦è¾¹ï¼šè¯¦ç»†æ•°æ®è¡¨æ ¼ -->
          <div class="panel-card card-9" style="flex: 2;">
            <div class="panel-title">销售统计详细数据</div>
            <div class="table-container">
              <el-table :data="tableData" style="width: 100%">
                <el-table-column prop="productType" label="产品类型" width="120" />
                <el-table-column prop="salesArea" label="销售区域" width="120" />
                <el-table-column prop="period" label="统计周期" width="120" />
                <el-table-column prop="salesVolume" label="销量(立方米)" />
                <el-table-column prop="salesAmount" label="销售金额(万元)" />
                <el-table-column prop="newCustomers" label="新增客户(个)" width="150" />
                <el-table-column prop="totalCustomers" label="合计客户(个)" width="150" />
              </el-table>
            </div>
          </div>
          <!-- å³è¾¹ï¼šäº§å“ç±»åž‹åˆ†å¸ƒå’Œé”€å”®åŒºåŸŸåˆ†å¸ƒ -->
          <div class="chart-column" style="flex: 1; display: flex; flex-direction: column; gap: 20px;">
            <div class="panel-card card-7" style="flex: 1;">
              <div class="panel-title">产品类型分布</div>
              <div class="chart-container">
                <div ref="productTypeChart" style="width: 100%; height: 100%;"></div>
              </div>
            </div>
            <div class="panel-card card-8" style="flex: 1;">
              <div class="panel-title">销售区域分布</div>
              <div class="chart-container">
                <div ref="salesAreaChart" style="width: 100%; height: 100%;"></div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script setup>
import { ref, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue';
import { useRouter } from 'vue-router';
import * as echarts from 'echarts';
import dayjs from 'dayjs';
const router = useRouter();
// ç­›é€‰æ¡ä»¶
const dateRange = ref([]);
const productType = ref('');
const salesArea = ref('');
const statDimension = ref('month');
// å›¾è¡¨å¼•用
const salesVolumeChart = ref(null);
const salesAmountChart = ref(null);
const productTypeChart = ref(null);
const salesAreaChart = ref(null);
const cumulativeSalesVolumeChart = ref(null);
const cumulativeSalesAmountChart = ref(null);
// å›¾è¡¨å®žä¾‹
let salesVolumeChartInstance = null;
let salesAmountChartInstance = null;
let productTypeChartInstance = null;
let salesAreaChartInstance = null;
let cumulativeSalesVolumeChartInstance = null;
let cumulativeSalesAmountChartInstance = null;
// æ¨¡æ‹Ÿæ•°æ®
const mockData = [
  // 2026å¹´1月数据
  { productType: '砌块', salesArea: '华东', period: '2026-01', salesVolume: 1200, salesAmount: 180, newCustomers: 5, totalCustomers: 120 },
  { productType: '砌块', salesArea: '华北', period: '2026-01', salesVolume: 800, salesAmount: 120, newCustomers: 3, totalCustomers: 80 },
  { productType: '砌块', salesArea: '华南', period: '2026-01', salesVolume: 600, salesAmount: 90, newCustomers: 2, totalCustomers: 60 },
  { productType: '板材', salesArea: '华东', period: '2026-01', salesVolume: 900, salesAmount: 270, newCustomers: 4, totalCustomers: 100 },
  { productType: '板材', salesArea: '华北', period: '2026-01', salesVolume: 500, salesAmount: 150, newCustomers: 2, totalCustomers: 70 },
  { productType: '型材', salesArea: '华东', period: '2026-01', salesVolume: 400, salesAmount: 200, newCustomers: 3, totalCustomers: 50 },
  // 2026å¹´2月数据
  { productType: '砌块', salesArea: '华东', period: '2026-02', salesVolume: 1300, salesAmount: 195, newCustomers: 4, totalCustomers: 124 },
  { productType: '砌块', salesArea: '华北', period: '2026-02', salesVolume: 850, salesAmount: 127.5, newCustomers: 2, totalCustomers: 82 },
  { productType: '砌块', salesArea: '华南', period: '2026-02', salesVolume: 650, salesAmount: 97.5, newCustomers: 1, totalCustomers: 61 },
  { productType: '板材', salesArea: '华东', period: '2026-02', salesVolume: 950, salesAmount: 285, newCustomers: 3, totalCustomers: 103 },
  { productType: '板材', salesArea: '华北', period: '2026-02', salesVolume: 550, salesAmount: 165, newCustomers: 1, totalCustomers: 71 },
  { productType: '型材', salesArea: '华东', period: '2026-02', salesVolume: 450, salesAmount: 225, newCustomers: 2, totalCustomers: 52 },
  // 2026å¹´3月数据
  { productType: '砌块', salesArea: '华东', period: '2026-03', salesVolume: 1400, salesAmount: 210, newCustomers: 6, totalCustomers: 130 },
  { productType: '砌块', salesArea: '华北', period: '2026-03', salesVolume: 900, salesAmount: 135, newCustomers: 3, totalCustomers: 85 },
  { productType: '砌块', salesArea: '华南', period: '2026-03', salesVolume: 700, salesAmount: 105, newCustomers: 2, totalCustomers: 63 },
  { productType: '板材', salesArea: '华东', period: '2026-03', salesVolume: 1000, salesAmount: 300, newCustomers: 5, totalCustomers: 108 },
  { productType: '板材', salesArea: '华北', period: '2026-03', salesVolume: 600, salesAmount: 180, newCustomers: 2, totalCustomers: 73 },
  { productType: '型材', salesArea: '华东', period: '2026-03', salesVolume: 500, salesAmount: 250, newCustomers: 3, totalCustomers: 55 },
  // è¥¿å—和西北地区数据
  { productType: '砌块', salesArea: '西南', period: '2026-03', salesVolume: 500, salesAmount: 75, newCustomers: 2, totalCustomers: 40 },
  { productType: '板材', salesArea: '西南', period: '2026-03', salesVolume: 300, salesAmount: 90, newCustomers: 1, totalCustomers: 30 },
  { productType: '砌块', salesArea: '西北', period: '2026-03', salesVolume: 400, salesAmount: 60, newCustomers: 1, totalCustomers: 35 },
  { productType: '板材', salesArea: '西北', period: '2026-03', salesVolume: 200, salesAmount: 60, newCustomers: 1, totalCustomers: 25 },
];
// è®¡ç®—属性
const filteredData = computed(() => {
  let result = [...mockData];
  // æŒ‰äº§å“ç±»åž‹ç­›é€‰
  if (productType.value) {
    result = result.filter(item => {
      const typeMap = { block: '砌块', board: '板材', profile: '型材' };
      return item.productType === typeMap[productType.value];
    });
  }
  // æŒ‰é”€å”®åŒºåŸŸç­›é€‰
  if (salesArea.value) {
    result = result.filter(item => {
      const areaMap = { east: '华东', north: '华北', south: '华南', southwest: '西南', northwest: '西北' };
      return item.salesArea === areaMap[salesArea.value];
    });
  }
  // æŒ‰æ—¶é—´èŒƒå›´ç­›é€‰
  if (dateRange.value && dateRange.value.length === 2) {
    const startDate = dayjs(dateRange.value[0]);
    const endDate = dayjs(dateRange.value[1]);
    result = result.filter(item => {
      const itemDate = dayjs(item.period);
      return itemDate.isAfter(startDate.subtract(1, 'day')) && itemDate.isBefore(endDate.add(1, 'day'));
    });
  }
  return result;
});
// æ ¸å¿ƒæŒ‡æ ‡è®¡ç®—
const totalSalesVolume = computed(() => {
  return filteredData.value.reduce((sum, item) => sum + item.salesVolume, 0);
});
const totalSalesAmount = computed(() => {
  return filteredData.value.reduce((sum, item) => sum + item.salesAmount, 0).toFixed(2);
});
const newCustomerCount = computed(() => {
  return filteredData.value.reduce((sum, item) => sum + item.newCustomers, 0);
});
const totalCustomerCount = computed(() => {
  // è®¡ç®—每个区域和产品类型的最大客户数
  const customerMap = {};
  filteredData.value.forEach(item => {
    const key = `${item.productType}-${item.salesArea}`;
    if (!customerMap[key] || item.totalCustomers > customerMap[key]) {
      customerMap[key] = item.totalCustomers;
    }
  });
  return Object.values(customerMap).reduce((sum, count) => sum + count, 0);
});
// å˜åŒ–率计算(模拟)
const salesVolumeChange = ref('+5.2');
const salesAmountChange = ref('+7.8');
const customerCountChange = ref('+3.5');
const totalCustomerChange = ref('+2.1');
// è¡¨æ ¼æ•°æ®
const tableData = computed(() => {
  return filteredData.value.map(item => {
    // è®¡ç®—累计值(模拟)
    const cumulativeSalesVolume = item.salesVolume * 1.5;
    const cumulativeSalesAmount = item.salesAmount * 1.5;
    const cumulativeNewCustomers = item.newCustomers * 2;
    return {
      ...item,
      cumulativeSalesVolume,
      cumulativeSalesAmount,
      cumulativeNewCustomers
    };
  });
});
// é”€é‡è¶‹åŠ¿å›¾è¡¨é…ç½®
const salesVolumeChartOption = computed(() => {
  // æŒ‰å‘¨æœŸåˆ†ç»„
  const periodMap = {};
  filteredData.value.forEach(item => {
    if (!periodMap[item.period]) {
      periodMap[item.period] = 0;
    }
    periodMap[item.period] += item.salesVolume;
  });
  const periods = Object.keys(periodMap).sort();
  const values = periods.map(period => periodMap[period]);
  return {
    tooltip: {
      trigger: 'axis',
      formatter: '{b}: {c} ç«‹æ–¹ç±³'
    },
    xAxis: {
      type: 'category',
      data: periods
    },
    yAxis: {
      type: 'value',
      name: '销量(立方米)'
    },
    series: [{
      data: values,
      type: 'line',
      smooth: true,
      lineStyle: {
        width: 3
      },
      itemStyle: {
        color: '#409EFF'
      }
    }]
  };
});
// é”€å”®é‡‘额趋势图表配置
const salesAmountChartOption = computed(() => {
  // æŒ‰å‘¨æœŸåˆ†ç»„
  const periodMap = {};
  filteredData.value.forEach(item => {
    if (!periodMap[item.period]) {
      periodMap[item.period] = 0;
    }
    periodMap[item.period] += item.salesAmount;
  });
  const periods = Object.keys(periodMap).sort();
  const values = periods.map(period => periodMap[period]);
  return {
    tooltip: {
      trigger: 'axis',
      formatter: '{b}: {c} ä¸‡å…ƒ'
    },
    xAxis: {
      type: 'category',
      data: periods
    },
    yAxis: {
      type: 'value',
      name: '销售金额(万元)'
    },
    series: [{
      data: values,
      type: 'bar',
      itemStyle: {
        color: '#67C23A'
      }
    }]
  };
});
// äº§å“ç±»åž‹åˆ†å¸ƒå›¾è¡¨é…ç½®
const productTypeChartOption = computed(() => {
  // æŒ‰äº§å“ç±»åž‹åˆ†ç»„
  const typeMap = {};
  filteredData.value.forEach(item => {
    if (!typeMap[item.productType]) {
      typeMap[item.productType] = 0;
    }
    typeMap[item.productType] += item.salesVolume;
  });
  const types = Object.keys(typeMap);
  const values = types.map(type => typeMap[type]);
  return {
    tooltip: {
      trigger: 'item',
      formatter: '{b}: {c} ç«‹æ–¹ç±³ ({d}%)'
    },
    series: [{
      type: 'pie',
      radius: '60%',
      data: types.map((type, index) => ({
        name: type,
        value: values[index]
      })),
      emphasis: {
        itemStyle: {
          shadowBlur: 10,
          shadowOffsetX: 0,
          shadowColor: 'rgba(0, 0, 0, 0.5)'
        }
      }
    }]
  };
});
// é”€å”®åŒºåŸŸåˆ†å¸ƒå›¾è¡¨é…ç½®
const salesAreaChartOption = computed(() => {
  // æŒ‰é”€å”®åŒºåŸŸåˆ†ç»„
  const areaMap = {};
  filteredData.value.forEach(item => {
    if (!areaMap[item.salesArea]) {
      areaMap[item.salesArea] = 0;
    }
    areaMap[item.salesArea] += item.salesVolume;
  });
  const areas = Object.keys(areaMap);
  const values = areas.map(area => areaMap[area]);
  return {
    tooltip: {
      trigger: 'item',
      formatter: '{b}: {c} ç«‹æ–¹ç±³ ({d}%)'
    },
    series: [{
      type: 'pie',
      radius: '60%',
      data: areas.map((area, index) => ({
        name: area,
        value: values[index]
      })),
      emphasis: {
        itemStyle: {
          shadowBlur: 10,
          shadowOffsetX: 0,
          shadowColor: 'rgba(0, 0, 0, 0.5)'
        }
      }
    }]
  };
});
// ç´¯è®¡é”€é‡è¶‹åŠ¿å›¾è¡¨é…ç½®
const cumulativeSalesVolumeChartOption = computed(() => {
  // æŒ‰å‘¨æœŸåˆ†ç»„
  const periodMap = {};
  let cumulativeValue = 0;
  // æŒ‰å‘¨æœŸæŽ’序
  const sortedData = [...filteredData.value].sort((a, b) => a.period.localeCompare(b.period));
  sortedData.forEach(item => {
    cumulativeValue += item.salesVolume;
    periodMap[item.period] = cumulativeValue;
  });
  const periods = Object.keys(periodMap).sort();
  const values = periods.map(period => periodMap[period]);
  return {
    tooltip: {
      trigger: 'axis',
      formatter: '{b}: {c} ç«‹æ–¹ç±³'
    },
    xAxis: {
      type: 'category',
      data: periods
    },
    yAxis: {
      type: 'value',
      name: '累计销量(立方米)'
    },
    series: [{
      data: values,
      type: 'line',
      smooth: true,
      areaStyle: {
        opacity: 0.3
      },
      itemStyle: {
        color: '#E6A23C'
      },
      lineStyle: {
        width: 3
      }
    }]
  };
});
// ç´¯è®¡é”€å”®é‡‘额趋势图表配置
const cumulativeSalesAmountChartOption = computed(() => {
  // æŒ‰å‘¨æœŸåˆ†ç»„
  const periodMap = {};
  let cumulativeValue = 0;
  // æŒ‰å‘¨æœŸæŽ’序
  const sortedData = [...filteredData.value].sort((a, b) => a.period.localeCompare(b.period));
  sortedData.forEach(item => {
    cumulativeValue += item.salesAmount;
    periodMap[item.period] = cumulativeValue;
  });
  const periods = Object.keys(periodMap).sort();
  const values = periods.map(period => periodMap[period]);
  return {
    tooltip: {
      trigger: 'axis',
      formatter: '{b}: {c} ä¸‡å…ƒ'
    },
    xAxis: {
      type: 'category',
      data: periods
    },
    yAxis: {
      type: 'value',
      name: '累计销售金额(万元)'
    },
    series: [{
      data: values,
      type: 'bar',
      itemStyle: {
        color: '#F56C6C'
      }
    }]
  };
});
// æ–¹æ³•
const goBack = () => {
  router.back();
};
const handleDateChange = () => {
  // å¤„理日期变化
  updateCharts();
};
const handleFilterChange = () => {
  // å¤„理筛选条件变化
  updateCharts();
};
// åˆå§‹åŒ–图表
const initCharts = () => {
  // åˆå§‹åŒ–销量趋势图表
  if (salesVolumeChart.value && !salesVolumeChartInstance) {
    salesVolumeChartInstance = echarts.init(salesVolumeChart.value);
  }
  // åˆå§‹åŒ–销售金额趋势图表
  if (salesAmountChart.value && !salesAmountChartInstance) {
    salesAmountChartInstance = echarts.init(salesAmountChart.value);
  }
  // åˆå§‹åŒ–产品类型分布图表
  if (productTypeChart.value && !productTypeChartInstance) {
    productTypeChartInstance = echarts.init(productTypeChart.value);
  }
  // åˆå§‹åŒ–销售区域分布图表
  if (salesAreaChart.value && !salesAreaChartInstance) {
    salesAreaChartInstance = echarts.init(salesAreaChart.value);
  }
  // åˆå§‹åŒ–累计销量趋势图表
  if (cumulativeSalesVolumeChart.value && !cumulativeSalesVolumeChartInstance) {
    cumulativeSalesVolumeChartInstance = echarts.init(cumulativeSalesVolumeChart.value);
  }
  // åˆå§‹åŒ–累计销售金额趋势图表
  if (cumulativeSalesAmountChart.value && !cumulativeSalesAmountChartInstance) {
    cumulativeSalesAmountChartInstance = echarts.init(cumulativeSalesAmountChart.value);
  }
  updateCharts();
};
// æ›´æ–°å›¾è¡¨
const updateCharts = () => {
  // æ›´æ–°é”€é‡è¶‹åŠ¿å›¾è¡¨
  if (salesVolumeChartInstance) {
    salesVolumeChartInstance.setOption(salesVolumeChartOption.value);
  }
  // æ›´æ–°é”€å”®é‡‘额趋势图表
  if (salesAmountChartInstance) {
    salesAmountChartInstance.setOption(salesAmountChartOption.value);
  }
  // æ›´æ–°äº§å“ç±»åž‹åˆ†å¸ƒå›¾è¡¨
  if (productTypeChartInstance) {
    productTypeChartInstance.setOption(productTypeChartOption.value);
  }
  // æ›´æ–°é”€å”®åŒºåŸŸåˆ†å¸ƒå›¾è¡¨
  if (salesAreaChartInstance) {
    salesAreaChartInstance.setOption(salesAreaChartOption.value);
  }
  // æ›´æ–°ç´¯è®¡é”€é‡è¶‹åŠ¿å›¾è¡¨
  if (cumulativeSalesVolumeChartInstance) {
    cumulativeSalesVolumeChartInstance.setOption(cumulativeSalesVolumeChartOption.value);
  }
  // æ›´æ–°ç´¯è®¡é”€å”®é‡‘额趋势图表
  if (cumulativeSalesAmountChartInstance) {
    cumulativeSalesAmountChartInstance.setOption(cumulativeSalesAmountChartOption.value);
  }
};
// ç›‘听窗口大小变化
const handleResize = () => {
  if (salesVolumeChartInstance) {
    salesVolumeChartInstance.resize();
  }
  if (salesAmountChartInstance) {
    salesAmountChartInstance.resize();
  }
  if (productTypeChartInstance) {
    productTypeChartInstance.resize();
  }
  if (salesAreaChartInstance) {
    salesAreaChartInstance.resize();
  }
  if (cumulativeSalesVolumeChartInstance) {
    cumulativeSalesVolumeChartInstance.resize();
  }
  if (cumulativeSalesAmountChartInstance) {
    cumulativeSalesAmountChartInstance.resize();
  }
};
// ç”Ÿå‘½å‘¨æœŸ
onMounted(() => {
  // è®¾ç½®é»˜è®¤æ—¥æœŸèŒƒå›´ä¸ºæœ€è¿‘3个月
  const endDate = dayjs();
  const startDate = endDate.subtract(3, 'month');
  dateRange.value = [startDate.format('YYYY-MM-DD'), endDate.format('YYYY-MM-DD')];
  // ç­‰å¾…DOM更新后初始化图表
  nextTick(() => {
    initCharts();
  });
  // æ·»åŠ çª—å£å¤§å°å˜åŒ–ç›‘å¬
  window.addEventListener('resize', handleResize);
});
// ç»„件卸载时销毁图表实例
onBeforeUnmount(() => {
  if (salesVolumeChartInstance) {
    salesVolumeChartInstance.dispose();
  }
  if (salesAmountChartInstance) {
    salesAmountChartInstance.dispose();
  }
  if (productTypeChartInstance) {
    productTypeChartInstance.dispose();
  }
  if (salesAreaChartInstance) {
    salesAreaChartInstance.dispose();
  }
  if (cumulativeSalesVolumeChartInstance) {
    cumulativeSalesVolumeChartInstance.dispose();
  }
  if (cumulativeSalesAmountChartInstance) {
    cumulativeSalesAmountChartInstance.dispose();
  }
  // ç§»é™¤çª—口大小变化监听
  window.removeEventListener('resize', handleResize);
});
</script>
<style scoped>
/* å¤–部容器 - å æ®æ•´ä¸ªè§†å£ */
.sales-statistics-container {
  position: relative;
  width: 100%;
  /* é¡µé¢åœ¨å¸¸è§„布局下(有顶栏)默认减去 84px,避免内容被裁切 */
  min-height: calc(100vh - 84px);
  background-color: #f5f7fa;
  overflow: hidden;
}
/* å†…部内容区域 - è‡ªé€‚应宽度 */
.data-dashboard {
  position: relative;
  width: 100%;
  min-height: 100%;
  background-color: #ffffff;
  box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
}
.filter-area {
  padding: 20px;
  background-color: #ffffff;
  border-bottom: 1px solid #e4e7ed;
  display: flex;
  gap: 40px;
  align-items: center;
  flex-wrap: wrap;
}
.filter-section {
  display: flex;
  align-items: center;
  gap: 10px;
}
.filter-label {
  font-size: 14px;
  font-weight: 500;
  color: #303133;
  white-space: nowrap;
}
.dashboard-content {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 20px;
  min-height: 800px;
  overflow: hidden;
}
/* è¡Œå¸ƒå±€ */
.row {
  display: flex;
  gap: 20px;
  align-items: stretch;
}
/* ç¬¬ä¸€è¡Œï¼š4个指标卡片 */
.row-1 {
  height: 180px;
}
/* ç¬¬äºŒè¡Œï¼š2个趋势图表 */
.row-2 {
  height: 350px;
}
/* ç¬¬ä¸‰è¡Œï¼šç´¯è®¡æ•°æ®è¶‹åŠ¿ */
.row-3 {
  height: 350px;
}
/* ç¬¬å››è¡Œï¼šè¡¨æ ¼å’Œå›¾è¡¨ */
.row-4 {
  height: 600px;
}
/* å¡ç‰‡æ ·å¼ */
.panel-card {
  background-color: #ffffff;
  border-radius: 8px;
  border: 1px solid #e4e7ed;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  transition: all 0.3s ease;
}
.panel-card:hover {
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  transform: translateY(-2px);
}
/* å¡ç‰‡å¸ƒå±€ */
.card-1 {
  flex: 1;
}
.card-2 {
  flex: 1;
}
.card-3 {
  flex: 1;
}
.card-4 {
  flex: 1;
}
.card-5 {
  flex: 1;
}
.card-6 {
  flex: 1;
}
.card-7 {
  flex: 1;
}
.card-8 {
  flex: 1;
}
.card-9 {
  flex: 1;
}
.card-10 {
  flex: 1;
}
.card-11 {
  flex: 1;
}
.panel-title {
  padding: 15px 20px;
  font-size: 16px;
  font-weight: 500;
  color: #303133;
  border-bottom: 1px solid #e4e7ed;
  background-color: #fafafa;
}
.chart-container {
  flex: 1;
  padding: 20px;
}
.table-container {
  flex: 1;
  padding: 20px;
  overflow: auto;
}
.stats-grid {
  flex: 1;
  padding: 15px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.stat-item {
  background-color: #fafafa;
  border-radius: 8px;
  padding: 15px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  border: 1px solid #e4e7ed;
  min-height: 80px;
  width: 100%;
}
.stat-value {
  font-size: 24px;
  font-weight: 600;
  color: #303133;
  margin-bottom: 5px;
}
.stat-unit {
  font-size: 12px;
  color: #909399;
  margin-bottom: 3px;
}
.stat-change {
  font-size: 12px;
  color: #67c23a;
}
/* è¡¨æ ¼æ ·å¼ */
:deep(.el-table) {
  border-radius: 8px;
  overflow: hidden;
}
:deep(.el-table th) {
  background-color: #fafafa;
  font-weight: 500;
}
:deep(.el-table tr:hover > td) {
  background-color: #ecf5ff;
}
/* ä¸‹æ‹‰é€‰æ‹©æ¡†æ ·å¼ */
:deep(.el-select) {
  width: 100%;
}
:deep(.el-date-picker) {
  width: 100%;
}
</style>