804 lines
21 KiB
Vue
804 lines
21 KiB
Vue
|
|
<template>
|
|||
|
|
<div class="erp-dashboard-page">
|
|||
|
|
<section class="stats-section">
|
|||
|
|
<div class="stat-card">
|
|||
|
|
<div class="stat-icon supplier-icon">
|
|||
|
|
<i class="el-icon-office-building"></i>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat-content">
|
|||
|
|
<div class="stat-value">{{ supplierCount }}</div>
|
|||
|
|
<div class="stat-label">供应商总数</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat-card">
|
|||
|
|
<div class="stat-icon order-icon">
|
|||
|
|
<i class="el-icon-document"></i>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat-content">
|
|||
|
|
<div class="stat-value">{{ orderCount }}</div>
|
|||
|
|
<div class="stat-label">采购订单总数</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat-card">
|
|||
|
|
<div class="stat-icon doing-icon">
|
|||
|
|
<i class="el-icon-loading"></i>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat-content">
|
|||
|
|
<div class="stat-value">{{ doingOrderCount }}</div>
|
|||
|
|
<div class="stat-label">执行中订单</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat-card">
|
|||
|
|
<div class="stat-icon complete-icon">
|
|||
|
|
<i class="el-icon-circle-check"></i>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat-content">
|
|||
|
|
<div class="stat-value">{{ completeOrderCount }}</div>
|
|||
|
|
<div class="stat-label">已完成订单</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
<div class="charts-section">
|
|||
|
|
<div class="chart-card">
|
|||
|
|
<div class="chart-header">
|
|||
|
|
<span>订单状态分布</span>
|
|||
|
|
</div>
|
|||
|
|
<div ref="statusChart" class="chart-container"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="chart-card">
|
|||
|
|
<div class="chart-header">
|
|||
|
|
<span>供应商订单占比</span>
|
|||
|
|
</div>
|
|||
|
|
<div ref="supplierChart" class="chart-container"></div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="charts-section">
|
|||
|
|
<div class="chart-card full-width">
|
|||
|
|
<div class="chart-header">
|
|||
|
|
<span>订单趋势(按天)</span>
|
|||
|
|
</div>
|
|||
|
|
<div ref="trendChart" class="chart-container"></div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="charts-section">
|
|||
|
|
<div class="chart-card">
|
|||
|
|
<div class="chart-header">
|
|||
|
|
<span>业务员订单分布</span>
|
|||
|
|
</div>
|
|||
|
|
<div ref="salesmanBarChart" class="chart-container"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="chart-card">
|
|||
|
|
<div class="chart-header">
|
|||
|
|
<span>部门订单分布</span>
|
|||
|
|
</div>
|
|||
|
|
<div ref="deptBarChart" class="chart-container"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="chart-card">
|
|||
|
|
<div class="chart-header">
|
|||
|
|
<span>按业务员订单占比(饼图)</span>
|
|||
|
|
</div>
|
|||
|
|
<div ref="salesmanPieChart" class="chart-container"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="chart-card">
|
|||
|
|
<div class="chart-header">
|
|||
|
|
<span>按部门订单占比(饼图)</span>
|
|||
|
|
</div>
|
|||
|
|
<div ref="deptPieChart" class="chart-container"></div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="charts-section">
|
|||
|
|
<div class="chart-card">
|
|||
|
|
<div class="chart-header">
|
|||
|
|
<span>供应商信用等级分布</span>
|
|||
|
|
</div>
|
|||
|
|
<div ref="creditChart" class="chart-container"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="chart-card">
|
|||
|
|
<div class="chart-header">
|
|||
|
|
<span>供应商类型分布</span>
|
|||
|
|
</div>
|
|||
|
|
<div ref="typeChart" class="chart-container"></div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
import { listSupplier, listPurchaseOrder } from '@/api/erp/purchase'
|
|||
|
|
import * as echarts from 'echarts'
|
|||
|
|
|
|||
|
|
export default {
|
|||
|
|
name: 'ErpDashboard',
|
|||
|
|
data() {
|
|||
|
|
return {
|
|||
|
|
supplierCount: 0,
|
|||
|
|
orderCount: 0,
|
|||
|
|
doingOrderCount: 0,
|
|||
|
|
completeOrderCount: 0,
|
|||
|
|
supplierList: [],
|
|||
|
|
orderList: [],
|
|||
|
|
loading: false
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
mounted() {
|
|||
|
|
this.loadData()
|
|||
|
|
},
|
|||
|
|
beforeDestroy() {
|
|||
|
|
if (this.statusChart) this.statusChart.dispose()
|
|||
|
|
if (this.supplierChart) this.supplierChart.dispose()
|
|||
|
|
if (this.trendChart) this.trendChart.dispose()
|
|||
|
|
if (this.creditChart) this.creditChart.dispose()
|
|||
|
|
if (this.typeChart) this.typeChart.dispose()
|
|||
|
|
if (this.salesmanBarChart) this.salesmanBarChart.dispose()
|
|||
|
|
if (this.salesmanPieChart) this.salesmanPieChart.dispose()
|
|||
|
|
if (this.deptBarChart) this.deptBarChart.dispose()
|
|||
|
|
if (this.deptPieChart) this.deptPieChart.dispose()
|
|||
|
|
if (this.supplierBarChart) this.supplierBarChart.dispose()
|
|||
|
|
if (this.supplierPieChart) this.supplierPieChart.dispose()
|
|||
|
|
window.removeEventListener('resize', this.handleResize)
|
|||
|
|
},
|
|||
|
|
methods: {
|
|||
|
|
async loadData() {
|
|||
|
|
this.loading = true
|
|||
|
|
try {
|
|||
|
|
const [supplierRes, orderRes] = await Promise.all([
|
|||
|
|
listSupplier({ pageNum: 1, pageSize: 100000 }),
|
|||
|
|
listPurchaseOrder({ pageNum: 1, pageSize: 100000 })
|
|||
|
|
])
|
|||
|
|
this.supplierList = supplierRes.rows || []
|
|||
|
|
this.orderList = orderRes.rows || []
|
|||
|
|
this.supplierCount = this.supplierList.length
|
|||
|
|
this.orderCount = this.orderList.length
|
|||
|
|
this.doingOrderCount = this.orderList.filter(o => o.orderStatus === 1).length
|
|||
|
|
this.completeOrderCount = this.orderList.filter(o => o.orderStatus === 3).length
|
|||
|
|
|
|||
|
|
this.$nextTick(() => {
|
|||
|
|
this.initCharts()
|
|||
|
|
})
|
|||
|
|
} catch (error) {
|
|||
|
|
this.$message.error('加载数据失败')
|
|||
|
|
console.error(error)
|
|||
|
|
} finally {
|
|||
|
|
this.loading = false
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
initCharts() {
|
|||
|
|
this.initStatusChart()
|
|||
|
|
this.initSupplierChart()
|
|||
|
|
this.initTrendChart()
|
|||
|
|
this.initCreditChart()
|
|||
|
|
this.initTypeChart()
|
|||
|
|
this.initSalesmanCharts()
|
|||
|
|
this.initDeptCharts()
|
|||
|
|
this.initSupplierNameCharts()
|
|||
|
|
window.addEventListener('resize', this.handleResize)
|
|||
|
|
},
|
|||
|
|
handleResize() {
|
|||
|
|
this.statusChart && this.statusChart.resize()
|
|||
|
|
this.supplierChart && this.supplierChart.resize()
|
|||
|
|
this.trendChart && this.trendChart.resize()
|
|||
|
|
this.creditChart && this.creditChart.resize()
|
|||
|
|
this.typeChart && this.typeChart.resize()
|
|||
|
|
this.salesmanBarChart && this.salesmanBarChart.resize()
|
|||
|
|
this.salesmanPieChart && this.salesmanPieChart.resize()
|
|||
|
|
this.deptBarChart && this.deptBarChart.resize()
|
|||
|
|
this.deptPieChart && this.deptPieChart.resize()
|
|||
|
|
this.supplierBarChart && this.supplierBarChart.resize()
|
|||
|
|
this.supplierPieChart && this.supplierPieChart.resize()
|
|||
|
|
},
|
|||
|
|
initStatusChart() {
|
|||
|
|
this.statusChart = echarts.init(this.$refs.statusChart)
|
|||
|
|
const statusMap = {
|
|||
|
|
0: '草稿',
|
|||
|
|
1: '执行中',
|
|||
|
|
2: '部分到货',
|
|||
|
|
3: '已完成',
|
|||
|
|
4: '已取消'
|
|||
|
|
}
|
|||
|
|
const statusData = {}
|
|||
|
|
this.orderList.forEach(order => {
|
|||
|
|
const status = order.orderStatus || 0
|
|||
|
|
statusData[status] = (statusData[status] || 0) + 1
|
|||
|
|
})
|
|||
|
|
const data = Object.keys(statusData).map(key => ({
|
|||
|
|
name: statusMap[key],
|
|||
|
|
value: statusData[key]
|
|||
|
|
}))
|
|||
|
|
const option = {
|
|||
|
|
tooltip: {
|
|||
|
|
trigger: 'item',
|
|||
|
|
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
|||
|
|
},
|
|||
|
|
legend: {
|
|||
|
|
orient: 'vertical',
|
|||
|
|
left: 'left'
|
|||
|
|
},
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
name: '订单状态',
|
|||
|
|
type: 'pie',
|
|||
|
|
radius: ['40%', '70%'],
|
|||
|
|
avoidLabelOverlap: false,
|
|||
|
|
itemStyle: {
|
|||
|
|
borderRadius: 10,
|
|||
|
|
borderColor: '#fff',
|
|||
|
|
borderWidth: 2
|
|||
|
|
},
|
|||
|
|
label: {
|
|||
|
|
show: true,
|
|||
|
|
formatter: '{b}: {c}'
|
|||
|
|
},
|
|||
|
|
data: data
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
this.statusChart.setOption(option)
|
|||
|
|
},
|
|||
|
|
initSupplierChart() {
|
|||
|
|
this.supplierChart = echarts.init(this.$refs.supplierChart)
|
|||
|
|
const supplierOrderCount = {}
|
|||
|
|
this.orderList.forEach(order => {
|
|||
|
|
const supplierName = order.supplierName || '未知'
|
|||
|
|
supplierOrderCount[supplierName] = (supplierOrderCount[supplierName] || 0) + 1
|
|||
|
|
})
|
|||
|
|
let data = Object.keys(supplierOrderCount).map(name => ({
|
|||
|
|
name,
|
|||
|
|
value: supplierOrderCount[name]
|
|||
|
|
}))
|
|||
|
|
data = data.sort((a, b) => b.value - a.value).slice(0, 10)
|
|||
|
|
|
|||
|
|
const option = {
|
|||
|
|
tooltip: {
|
|||
|
|
trigger: 'item',
|
|||
|
|
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
|||
|
|
},
|
|||
|
|
legend: {
|
|||
|
|
orient: 'vertical',
|
|||
|
|
left: 'left'
|
|||
|
|
},
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
name: '供应商订单',
|
|||
|
|
type: 'pie',
|
|||
|
|
radius: ['40%', '70%'],
|
|||
|
|
avoidLabelOverlap: false,
|
|||
|
|
itemStyle: {
|
|||
|
|
borderRadius: 10,
|
|||
|
|
borderColor: '#fff',
|
|||
|
|
borderWidth: 2
|
|||
|
|
},
|
|||
|
|
label: {
|
|||
|
|
show: true,
|
|||
|
|
formatter: '{b}: {c}'
|
|||
|
|
},
|
|||
|
|
data: data
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
this.supplierChart.setOption(option)
|
|||
|
|
},
|
|||
|
|
initTrendChart() {
|
|||
|
|
this.trendChart = echarts.init(this.$refs.trendChart)
|
|||
|
|
const dayData = {}
|
|||
|
|
this.orderList.forEach(order => {
|
|||
|
|
if (order.orderDate) {
|
|||
|
|
const day = order.orderDate
|
|||
|
|
dayData[day] = (dayData[day] || 0) + 1
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
const days = Object.keys(dayData).sort()
|
|||
|
|
const counts = days.map(d => dayData[d])
|
|||
|
|
|
|||
|
|
const option = {
|
|||
|
|
tooltip: {
|
|||
|
|
trigger: 'axis'
|
|||
|
|
},
|
|||
|
|
legend: {
|
|||
|
|
data: ['订单数量']
|
|||
|
|
},
|
|||
|
|
grid: {
|
|||
|
|
left: '3%',
|
|||
|
|
right: '4%',
|
|||
|
|
bottom: '3%',
|
|||
|
|
containLabel: true
|
|||
|
|
},
|
|||
|
|
xAxis: {
|
|||
|
|
type: 'category',
|
|||
|
|
boundaryGap: false,
|
|||
|
|
data: days,
|
|||
|
|
axisLabel: {
|
|||
|
|
rotate: 45
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
yAxis: {
|
|||
|
|
type: 'value'
|
|||
|
|
},
|
|||
|
|
dataZoom: [
|
|||
|
|
{
|
|||
|
|
type: 'inside',
|
|||
|
|
start: 0,
|
|||
|
|
end: 100
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
start: 0,
|
|||
|
|
end: 100
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
name: '订单数量',
|
|||
|
|
type: 'line',
|
|||
|
|
smooth: true,
|
|||
|
|
data: counts,
|
|||
|
|
areaStyle: {
|
|||
|
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|||
|
|
{ offset: 0, color: 'rgba(80, 141, 255, 0.5)' },
|
|||
|
|
{ offset: 1, color: 'rgba(80, 141, 255, 0.1)' }
|
|||
|
|
])
|
|||
|
|
},
|
|||
|
|
lineStyle: {
|
|||
|
|
color: '#508dff'
|
|||
|
|
},
|
|||
|
|
itemStyle: {
|
|||
|
|
color: '#508dff'
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
this.trendChart.setOption(option)
|
|||
|
|
},
|
|||
|
|
initCreditChart() {
|
|||
|
|
this.creditChart = echarts.init(this.$refs.creditChart)
|
|||
|
|
const creditData = {}
|
|||
|
|
this.supplierList.forEach(supplier => {
|
|||
|
|
const credit = supplier.creditRating || '-'
|
|||
|
|
creditData[credit] = (creditData[credit] || 0) + 1
|
|||
|
|
})
|
|||
|
|
const data = Object.keys(creditData).map(key => ({
|
|||
|
|
name: key,
|
|||
|
|
value: creditData[key]
|
|||
|
|
}))
|
|||
|
|
|
|||
|
|
const option = {
|
|||
|
|
tooltip: {
|
|||
|
|
trigger: 'axis',
|
|||
|
|
axisPointer: {
|
|||
|
|
type: 'shadow'
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
xAxis: {
|
|||
|
|
type: 'category',
|
|||
|
|
data: Object.keys(creditData)
|
|||
|
|
},
|
|||
|
|
yAxis: {
|
|||
|
|
type: 'value'
|
|||
|
|
},
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
name: '供应商数量',
|
|||
|
|
type: 'bar',
|
|||
|
|
data: Object.values(creditData),
|
|||
|
|
itemStyle: {
|
|||
|
|
color: function (params) {
|
|||
|
|
const colorMap = {
|
|||
|
|
'A': '#67c23a',
|
|||
|
|
'B': '#e6a23c',
|
|||
|
|
'C': '#f56c6c',
|
|||
|
|
'D': '#909399',
|
|||
|
|
'-': '#409eff'
|
|||
|
|
}
|
|||
|
|
return colorMap[params.name] || '#409eff'
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
this.creditChart.setOption(option)
|
|||
|
|
},
|
|||
|
|
initTypeChart() {
|
|||
|
|
this.typeChart = echarts.init(this.$refs.typeChart)
|
|||
|
|
const typeData = {}
|
|||
|
|
this.supplierList.forEach(supplier => {
|
|||
|
|
const type = supplier.type === 'RAW' ? '原料供应商' : '其他供应商'
|
|||
|
|
typeData[type] = (typeData[type] || 0) + 1
|
|||
|
|
})
|
|||
|
|
const data = Object.keys(typeData).map(key => ({
|
|||
|
|
name: key,
|
|||
|
|
value: typeData[key]
|
|||
|
|
}))
|
|||
|
|
|
|||
|
|
const option = {
|
|||
|
|
tooltip: {
|
|||
|
|
trigger: 'item',
|
|||
|
|
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
|||
|
|
},
|
|||
|
|
legend: {
|
|||
|
|
orient: 'vertical',
|
|||
|
|
left: 'left'
|
|||
|
|
},
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
name: '供应商类型',
|
|||
|
|
type: 'pie',
|
|||
|
|
radius: ['40%', '70%'],
|
|||
|
|
avoidLabelOverlap: false,
|
|||
|
|
itemStyle: {
|
|||
|
|
borderRadius: 10,
|
|||
|
|
borderColor: '#fff',
|
|||
|
|
borderWidth: 2
|
|||
|
|
},
|
|||
|
|
label: {
|
|||
|
|
show: true,
|
|||
|
|
formatter: '{b}: {c}'
|
|||
|
|
},
|
|||
|
|
data: data
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
this.typeChart.setOption(option)
|
|||
|
|
},
|
|||
|
|
initSalesmanCharts() {
|
|||
|
|
// 柱状图
|
|||
|
|
this.salesmanBarChart = echarts.init(this.$refs.salesmanBarChart)
|
|||
|
|
const salesmanData = {}
|
|||
|
|
this.orderList.forEach(order => {
|
|||
|
|
const salesman = order.salesman || '未知'
|
|||
|
|
salesmanData[salesman] = (salesmanData[salesman] || 0) + 1
|
|||
|
|
})
|
|||
|
|
const salesmanList = Object.keys(salesmanData).sort((a, b) => salesmanData[b] - salesmanData[a])
|
|||
|
|
const salesmanCounts = salesmanList.map(s => salesmanData[s])
|
|||
|
|
|
|||
|
|
const barOption = {
|
|||
|
|
tooltip: {
|
|||
|
|
trigger: 'axis',
|
|||
|
|
axisPointer: {
|
|||
|
|
type: 'shadow'
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
grid: {
|
|||
|
|
left: '3%',
|
|||
|
|
right: '4%',
|
|||
|
|
bottom: '3%',
|
|||
|
|
containLabel: true
|
|||
|
|
},
|
|||
|
|
xAxis: {
|
|||
|
|
type: 'category',
|
|||
|
|
data: salesmanList,
|
|||
|
|
axisLabel: {
|
|||
|
|
rotate: 45
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
yAxis: {
|
|||
|
|
type: 'value'
|
|||
|
|
},
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
name: '订单数量',
|
|||
|
|
type: 'bar',
|
|||
|
|
data: salesmanCounts,
|
|||
|
|
itemStyle: {
|
|||
|
|
color: '#67c23a'
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
this.salesmanBarChart.setOption(barOption)
|
|||
|
|
|
|||
|
|
// 饼图
|
|||
|
|
this.salesmanPieChart = echarts.init(this.$refs.salesmanPieChart)
|
|||
|
|
let pieData = salesmanList.map(name => ({
|
|||
|
|
name,
|
|||
|
|
value: salesmanData[name]
|
|||
|
|
}))
|
|||
|
|
pieData = pieData.slice(0, 10)
|
|||
|
|
|
|||
|
|
const pieOption = {
|
|||
|
|
tooltip: {
|
|||
|
|
trigger: 'item',
|
|||
|
|
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
|||
|
|
},
|
|||
|
|
legend: {
|
|||
|
|
orient: 'vertical',
|
|||
|
|
left: 'left'
|
|||
|
|
},
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
name: '业务员订单',
|
|||
|
|
type: 'pie',
|
|||
|
|
radius: ['40%', '70%'],
|
|||
|
|
avoidLabelOverlap: false,
|
|||
|
|
itemStyle: {
|
|||
|
|
borderRadius: 10,
|
|||
|
|
borderColor: '#fff',
|
|||
|
|
borderWidth: 2
|
|||
|
|
},
|
|||
|
|
label: {
|
|||
|
|
show: true,
|
|||
|
|
formatter: '{b}: {c}'
|
|||
|
|
},
|
|||
|
|
data: pieData
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
this.salesmanPieChart.setOption(pieOption)
|
|||
|
|
},
|
|||
|
|
initDeptCharts() {
|
|||
|
|
// 柱状图
|
|||
|
|
this.deptBarChart = echarts.init(this.$refs.deptBarChart)
|
|||
|
|
const deptData = {}
|
|||
|
|
this.orderList.forEach(order => {
|
|||
|
|
const dept = order.deptName || '未知'
|
|||
|
|
deptData[dept] = (deptData[dept] || 0) + 1
|
|||
|
|
})
|
|||
|
|
const deptList = Object.keys(deptData).sort((a, b) => deptData[b] - deptData[a])
|
|||
|
|
const deptCounts = deptList.map(d => deptData[d])
|
|||
|
|
|
|||
|
|
const barOption = {
|
|||
|
|
tooltip: {
|
|||
|
|
trigger: 'axis',
|
|||
|
|
axisPointer: {
|
|||
|
|
type: 'shadow'
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
grid: {
|
|||
|
|
left: '3%',
|
|||
|
|
right: '4%',
|
|||
|
|
bottom: '3%',
|
|||
|
|
containLabel: true
|
|||
|
|
},
|
|||
|
|
xAxis: {
|
|||
|
|
type: 'category',
|
|||
|
|
data: deptList,
|
|||
|
|
axisLabel: {
|
|||
|
|
rotate: 45
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
yAxis: {
|
|||
|
|
type: 'value'
|
|||
|
|
},
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
name: '订单数量',
|
|||
|
|
type: 'bar',
|
|||
|
|
data: deptCounts,
|
|||
|
|
itemStyle: {
|
|||
|
|
color: '#e6a23c'
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
this.deptBarChart.setOption(barOption)
|
|||
|
|
|
|||
|
|
// 饼图
|
|||
|
|
this.deptPieChart = echarts.init(this.$refs.deptPieChart)
|
|||
|
|
let pieData = deptList.map(name => ({
|
|||
|
|
name,
|
|||
|
|
value: deptData[name]
|
|||
|
|
}))
|
|||
|
|
pieData = pieData.slice(0, 10)
|
|||
|
|
|
|||
|
|
const pieOption = {
|
|||
|
|
tooltip: {
|
|||
|
|
trigger: 'item',
|
|||
|
|
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
|||
|
|
},
|
|||
|
|
legend: {
|
|||
|
|
orient: 'vertical',
|
|||
|
|
left: 'left'
|
|||
|
|
},
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
name: '部门订单',
|
|||
|
|
type: 'pie',
|
|||
|
|
radius: ['40%', '70%'],
|
|||
|
|
avoidLabelOverlap: false,
|
|||
|
|
itemStyle: {
|
|||
|
|
borderRadius: 10,
|
|||
|
|
borderColor: '#fff',
|
|||
|
|
borderWidth: 2
|
|||
|
|
},
|
|||
|
|
label: {
|
|||
|
|
show: true,
|
|||
|
|
formatter: '{b}: {c}'
|
|||
|
|
},
|
|||
|
|
data: pieData
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
this.deptPieChart.setOption(pieOption)
|
|||
|
|
},
|
|||
|
|
initSupplierNameCharts() {
|
|||
|
|
// 柱状图
|
|||
|
|
this.supplierBarChart = echarts.init(this.$refs.supplierBarChart)
|
|||
|
|
const supplierData = {}
|
|||
|
|
this.orderList.forEach(order => {
|
|||
|
|
const supplier = order.supplierName || '未知'
|
|||
|
|
supplierData[supplier] = (supplierData[supplier] || 0) + 1
|
|||
|
|
})
|
|||
|
|
const supplierList = Object.keys(supplierData).sort((a, b) => supplierData[b] - supplierData[a])
|
|||
|
|
const supplierCounts = supplierList.map(s => supplierData[s])
|
|||
|
|
|
|||
|
|
const barOption = {
|
|||
|
|
tooltip: {
|
|||
|
|
trigger: 'axis',
|
|||
|
|
axisPointer: {
|
|||
|
|
type: 'shadow'
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
grid: {
|
|||
|
|
left: '3%',
|
|||
|
|
right: '4%',
|
|||
|
|
bottom: '3%',
|
|||
|
|
containLabel: true
|
|||
|
|
},
|
|||
|
|
xAxis: {
|
|||
|
|
type: 'category',
|
|||
|
|
data: supplierList.slice(0, 15),
|
|||
|
|
axisLabel: {
|
|||
|
|
rotate: 45
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
yAxis: {
|
|||
|
|
type: 'value'
|
|||
|
|
},
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
name: '订单数量',
|
|||
|
|
type: 'bar',
|
|||
|
|
data: supplierCounts.slice(0, 15),
|
|||
|
|
itemStyle: {
|
|||
|
|
color: '#f56c6c'
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
this.supplierBarChart.setOption(barOption)
|
|||
|
|
|
|||
|
|
// 饼图
|
|||
|
|
this.supplierPieChart = echarts.init(this.$refs.supplierPieChart)
|
|||
|
|
let pieData = supplierList.map(name => ({
|
|||
|
|
name,
|
|||
|
|
value: supplierData[name]
|
|||
|
|
}))
|
|||
|
|
pieData = pieData.slice(0, 10)
|
|||
|
|
|
|||
|
|
const pieOption = {
|
|||
|
|
tooltip: {
|
|||
|
|
trigger: 'item',
|
|||
|
|
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
|||
|
|
},
|
|||
|
|
legend: {
|
|||
|
|
orient: 'vertical',
|
|||
|
|
left: 'left'
|
|||
|
|
},
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
name: '供应商订单',
|
|||
|
|
type: 'pie',
|
|||
|
|
radius: ['40%', '70%'],
|
|||
|
|
avoidLabelOverlap: false,
|
|||
|
|
itemStyle: {
|
|||
|
|
borderRadius: 10,
|
|||
|
|
borderColor: '#fff',
|
|||
|
|
borderWidth: 2
|
|||
|
|
},
|
|||
|
|
label: {
|
|||
|
|
show: true,
|
|||
|
|
formatter: '{b}: {c}'
|
|||
|
|
},
|
|||
|
|
data: pieData
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
this.supplierPieChart.setOption(pieOption)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style lang="scss" scoped>
|
|||
|
|
.erp-dashboard-page {
|
|||
|
|
padding: 16px;
|
|||
|
|
min-height: 100%;
|
|||
|
|
background: #f0f2f5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stats-section {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
|||
|
|
gap: 16px;
|
|||
|
|
margin-bottom: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stat-card {
|
|||
|
|
background: #fff;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
padding: 20px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 16px;
|
|||
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stat-icon {
|
|||
|
|
width: 60px;
|
|||
|
|
height: 60px;
|
|||
|
|
border-radius: 12px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
font-size: 28px;
|
|||
|
|
|
|||
|
|
&.supplier-icon {
|
|||
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|||
|
|
color: #fff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&.order-icon {
|
|||
|
|
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
|||
|
|
color: #fff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&.doing-icon {
|
|||
|
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
|||
|
|
color: #fff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&.complete-icon {
|
|||
|
|
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
|||
|
|
color: #fff;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stat-content {
|
|||
|
|
flex: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stat-value {
|
|||
|
|
font-size: 28px;
|
|||
|
|
font-weight: 700;
|
|||
|
|
color: #1f2a37;
|
|||
|
|
margin-bottom: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.stat-label {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #7a8694;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.charts-section {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: repeat(2, 1fr);
|
|||
|
|
gap: 16px;
|
|||
|
|
margin-bottom: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.chart-card {
|
|||
|
|
background: #fff;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
padding: 16px;
|
|||
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
|||
|
|
|
|||
|
|
&.full-width {
|
|||
|
|
grid-column: span 2;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.chart-header {
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #1f2a37;
|
|||
|
|
margin-bottom: 12px;
|
|||
|
|
padding-bottom: 12px;
|
|||
|
|
border-bottom: 1px solid #e4e7ed;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.chart-container {
|
|||
|
|
height: 300px;
|
|||
|
|
width: 100%;
|
|||
|
|
}
|
|||
|
|
</style>
|