<template> 
 | 
  <div class="app-container indicator-stats"> 
 | 
    <el-card class="box-card"> 
 | 
      <!-- KPI 汇总 --> 
 | 
      <el-row :gutter="20" class="stats-row"> 
 | 
        <el-col :span="6"> 
 | 
          <div class="stat-card"> 
 | 
            <div class="stat-icon" style="background: #ecf5ff;"> 
 | 
              <el-icon :size="30" color="#409eff"><Document /></el-icon> 
 | 
            </div> 
 | 
            <div class="stat-content"> 
 | 
              <div class="stat-value">{{ indicatorKpis.orderCount.toLocaleString() }}</div> 
 | 
              <div class="stat-label">订单数量</div> 
 | 
            </div> 
 | 
          </div> 
 | 
        </el-col> 
 | 
        <el-col :span="6"> 
 | 
          <div class="stat-card"> 
 | 
            <div class="stat-icon" style="background: #f0f9ff;"> 
 | 
              <el-icon :size="30" color="#67c23a"><Tickets /></el-icon> 
 | 
            </div> 
 | 
            <div class="stat-content"> 
 | 
              <div class="stat-value">¥{{ indicatorKpis.salesAmount.toLocaleString() }}</div> 
 | 
              <div class="stat-label">销售额</div> 
 | 
            </div> 
 | 
          </div> 
 | 
        </el-col> 
 | 
        <el-col :span="6"> 
 | 
          <div class="stat-card"> 
 | 
            <div class="stat-icon" style="background: #fef0f0;"> 
 | 
              <el-icon :size="30" color="#e6a23c"><Van /></el-icon> 
 | 
            </div> 
 | 
            <div class="stat-content"> 
 | 
              <div class="stat-value">{{ indicatorKpis.shipmentRate }}%</div> 
 | 
              <div class="stat-label">发货率</div> 
 | 
            </div> 
 | 
          </div> 
 | 
        </el-col> 
 | 
        <el-col :span="6"> 
 | 
          <div class="stat-card"> 
 | 
            <div class="stat-icon" style="background: #f4f4f5;"> 
 | 
              <el-icon :size="30" color="#f56c6c"><Wallet /></el-icon> 
 | 
            </div> 
 | 
            <div class="stat-content"> 
 | 
              <div class="stat-value">{{ indicatorKpis.collectionRate }}%</div> 
 | 
              <div class="stat-label">回款率</div> 
 | 
            </div> 
 | 
          </div> 
 | 
        </el-col> 
 | 
      </el-row> 
 | 
  
 | 
      <!-- 维度筛选 --> 
 | 
      <el-row :gutter="20" class="search-row"> 
 | 
        <el-col :span="6"> 
 | 
          <el-select v-model="indicatorFilter.product" placeholder="产品" clearable> 
 | 
            <el-option label="全部产品" value="" /> 
 | 
            <el-option label="P.O 42.5普通硅酸盐水泥" value="P.O 42.5普通硅酸盐水泥" /> 
 | 
            <el-option label="P.S 32.5矿渣硅酸盐水泥" value="P.S 32.5矿渣硅酸盐水泥" /> 
 | 
            <el-option label="P.C 32.5复合硅酸盐水泥" value="P.C 32.5复合硅酸盐水泥" /> 
 | 
          </el-select> 
 | 
        </el-col> 
 | 
        <el-col :span="6"> 
 | 
          <el-select v-model="indicatorFilter.customer" placeholder="客户" clearable> 
 | 
            <el-option label="全部客户" value="" /> 
 | 
            <el-option label="华东建材集团" value="华东建材集团" /> 
 | 
            <el-option label="长江混凝土公司" value="长江混凝土公司" /> 
 | 
            <el-option label="浦江水泥制品厂" value="浦江水泥制品厂" /> 
 | 
          </el-select> 
 | 
        </el-col> 
 | 
        <el-col :span="6"> 
 | 
          <el-select v-model="indicatorFilter.region" placeholder="区域" clearable> 
 | 
            <el-option label="全部区域" value="" /> 
 | 
            <el-option label="华东地区" value="华东地区" /> 
 | 
            <el-option label="华南地区" value="华南地区" /> 
 | 
            <el-option label="华北地区" value="华北地区" /> 
 | 
          </el-select> 
 | 
        </el-col> 
 | 
        <el-col :span="6"> 
 | 
          <el-date-picker v-model="indicatorFilter.dateRange" type="daterange" range-separator="至" 
 | 
                          start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD" style="width: 100%" /> 
 | 
        </el-col> 
 | 
        <el-col :span="24" style="text-align: right; margin-top: 10px;"> 
 | 
          <el-button type="primary" @click="applyIndicatorFilter">查询</el-button> 
 | 
          <el-button @click="resetIndicatorFilter">重置</el-button> 
 | 
          <el-button @click="exportIndicatorTable">导出报表</el-button> 
 | 
          <el-button @click="exportIndicatorChart">导出图表</el-button> 
 | 
        </el-col> 
 | 
      </el-row> 
 | 
  
 | 
      <!-- 图表区 --> 
 | 
      <div class="chart-container"> 
 | 
        <div ref="indicatorChartRef" style="width: 100%; height: 360px;"></div> 
 | 
      </div> 
 | 
  
 | 
      <!-- 业绩统计(团队维度,无个人姓名) --> 
 | 
      <el-table :data="teamPerformanceList" border stripe style="margin-top: 20px;"> 
 | 
        <el-table-column prop="team" label="销售团队"/> 
 | 
        <el-table-column prop="orderCount" label="订单数"/> 
 | 
        <el-table-column prop="salesAmount" label="销售额"> 
 | 
          <template #default="scope">¥{{ scope.row.salesAmount.toLocaleString() }}</template> 
 | 
        </el-table-column> 
 | 
        <el-table-column prop="shipmentRate" label="发货率"> 
 | 
          <template #default="scope">{{ scope.row.shipmentRate }}%</template> 
 | 
        </el-table-column> 
 | 
        <el-table-column prop="collectionRate" label="回款率"> 
 | 
          <template #default="scope">{{ scope.row.collectionRate }}%</template> 
 | 
        </el-table-column> 
 | 
        <el-table-column prop="attainment" label="目标达成率"> 
 | 
          <template #default="scope"> 
 | 
            <el-tag :type="scope.row.attainment >= 100 ? 'success' : scope.row.attainment >= 80 ? 'warning' : 'danger'"> 
 | 
              {{ scope.row.attainment }}% 
 | 
            </el-tag> 
 | 
          </template> 
 | 
        </el-table-column> 
 | 
      </el-table> 
 | 
    </el-card> 
 | 
  </div> 
 | 
</template> 
 | 
  
 | 
<script setup> 
 | 
import { ref, reactive, onMounted, nextTick } from 'vue' 
 | 
import { Document, Van, Tickets, Wallet } from '@element-plus/icons-vue' 
 | 
import * as echarts from 'echarts' 
 | 
  
 | 
const indicatorKpis = reactive({ 
 | 
  orderCount: 1280, 
 | 
  salesAmount: 9650000, 
 | 
  shipmentRate: 89.2, 
 | 
  collectionRate: 76.4 
 | 
}) 
 | 
  
 | 
const indicatorFilter = reactive({ 
 | 
  product: '', 
 | 
  customer: '', 
 | 
  region: '', 
 | 
  dateRange: [] 
 | 
}) 
 | 
  
 | 
const indicatorChartRef = ref(null) 
 | 
let indicatorChart = null 
 | 
  
 | 
const teamPerformanceList = ref([ 
 | 
  { team: '华东大区', orderCount: 320, salesAmount: 2850000, shipmentRate: 90, collectionRate: 80, attainment: 105 }, 
 | 
  { team: '华北大区', orderCount: 280, salesAmount: 2150000, shipmentRate: 86, collectionRate: 73, attainment: 92 }, 
 | 
  { team: '华南大区', orderCount: 210, salesAmount: 1850000, shipmentRate: 88, collectionRate: 70, attainment: 78 }, 
 | 
  { team: '西南大区', orderCount: 180, salesAmount: 1500000, shipmentRate: 83, collectionRate: 68, attainment: 74 } 
 | 
]) 
 | 
  
 | 
const initIndicatorChart = () => { 
 | 
  if (!indicatorChartRef.value) return 
 | 
  if (indicatorChart) indicatorChart.dispose() 
 | 
  indicatorChart = echarts.init(indicatorChartRef.value) 
 | 
  const option = { 
 | 
    title: { text: '多维度销售指标趋势', left: 'center' }, 
 | 
    tooltip: { trigger: 'axis' }, 
 | 
    legend: { data: ['订单数', '销售额', '发货率', '回款率'], top: 30 }, 
 | 
    grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true }, 
 | 
    xAxis: { type: 'category', data: ['2024-12', '2025-01', '2025-02', '2025-03', '2025-04', '2025-05'] }, 
 | 
    yAxis: [ 
 | 
      { type: 'value', name: '数量/金额', axisLabel: { formatter: '{value}' } }, 
 | 
      { type: 'value', name: '比例(%)', min: 0, max: 100, axisLabel: { formatter: '{value}%' } } 
 | 
    ], 
 | 
    series: [ 
 | 
      { name: '订单数', type: 'bar', data: [180, 220, 210, 260, 205, 225], itemStyle: { color: '#409eff' } }, 
 | 
      { name: '销售额', type: 'bar', data: [820, 950, 910, 1080, 980, 1020], itemStyle: { color: '#67c23a' } }, 
 | 
      { name: '发货率', type: 'line', yAxisIndex: 1, data: [86, 89, 88, 91, 87, 90], itemStyle: { color: '#e6a23c' } }, 
 | 
      { name: '回款率', type: 'line', yAxisIndex: 1, data: [72, 76, 74, 79, 75, 78], itemStyle: { color: '#f56c6c' } } 
 | 
    ] 
 | 
  } 
 | 
  indicatorChart.setOption(option) 
 | 
} 
 | 
  
 | 
const applyIndicatorFilter = () => { 
 | 
  const random = (base, delta) => { 
 | 
    const v = base + Math.round((Math.random() - 0.5) * delta) 
 | 
    return v < 0 ? 0 : v 
 | 
  } 
 | 
  indicatorKpis.orderCount = random(1280, 120) 
 | 
  indicatorKpis.salesAmount = random(9650000, 350000) 
 | 
  indicatorKpis.shipmentRate = (85 + Math.random() * 10).toFixed(1) * 1 
 | 
  indicatorKpis.collectionRate = (70 + Math.random() * 12).toFixed(1) * 1 
 | 
  setTimeout(() => initIndicatorChart(), 200) 
 | 
} 
 | 
  
 | 
const resetIndicatorFilter = () => { 
 | 
  indicatorFilter.product = '' 
 | 
  indicatorFilter.customer = '' 
 | 
  indicatorFilter.region = '' 
 | 
  indicatorFilter.dateRange = [] 
 | 
  applyIndicatorFilter() 
 | 
} 
 | 
  
 | 
const exportIndicatorTable = () => { 
 | 
  const header = ['销售团队', '订单数', '销售额', '发货率(%)', '回款率(%)', '目标达成率(%)'] 
 | 
  const rows = teamPerformanceList.value.map(r => [ 
 | 
    r.team, 
 | 
    r.orderCount, 
 | 
    r.salesAmount, 
 | 
    r.shipmentRate, 
 | 
    r.collectionRate, 
 | 
    r.attainment 
 | 
  ]) 
 | 
  const csv = [header, ...rows].map(r => r.join(',')).join('\n') 
 | 
  const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }) 
 | 
  const url = URL.createObjectURL(blob) 
 | 
  const link = document.createElement('a') 
 | 
  link.href = url 
 | 
  link.download = '指标统计-团队业绩.csv' 
 | 
  document.body.appendChild(link) 
 | 
  link.click() 
 | 
  document.body.removeChild(link) 
 | 
  URL.revokeObjectURL(url) 
 | 
} 
 | 
  
 | 
const exportIndicatorChart = () => { 
 | 
  if (!indicatorChart) return 
 | 
  const url = indicatorChart.getDataURL({ type: 'png', pixelRatio: 2, backgroundColor: '#fff' }) 
 | 
  const link = document.createElement('a') 
 | 
  link.href = url 
 | 
  link.download = '指标统计-图表.png' 
 | 
  document.body.appendChild(link) 
 | 
  link.click() 
 | 
  document.body.removeChild(link) 
 | 
} 
 | 
  
 | 
onMounted(() => { 
 | 
  nextTick(() => initIndicatorChart()) 
 | 
}) 
 | 
</script> 
 | 
  
 | 
<style scoped> 
 | 
.indicator-stats { 
 | 
  padding: 0; 
 | 
} 
 | 
.box-card { border: none; box-shadow: none; } 
 | 
.search-row { margin-bottom: 20px; } 
 | 
.stats-row { margin-bottom: 24px; } 
 | 
.stat-card { display: flex; align-items: center; padding: 20px; background: #fff; border-radius: 8px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } 
 | 
.stat-icon { width: 60px; height: 60px; display: flex; align-items: center; justify-content: center; border-radius: 8px; margin-right: 16px; } 
 | 
.stat-content { flex: 1; } 
 | 
.stat-value { font-size: 28px; font-weight: bold; color: #303133; margin-bottom: 4px; } 
 | 
.stat-label { font-size: 14px; color: #909399; } 
 | 
.chart-container { margin: 20px 0; padding: 20px; background: #fff; border-radius: 8px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } 
 | 
</style> 
 |