<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>
|