219 lines
6.4 KiB
Vue
219 lines
6.4 KiB
Vue
<template>
|
||
<div class="category-bar-chart-container" style="position: relative;">
|
||
<!-- 维度筛选下拉框 - 选择要汇总的维度 -->
|
||
<div class="chart-select" style="position: absolute; top: 10px; right: 10px; z-index: 10; text-align: right;">
|
||
<select v-model="selectedType" @change="renderChart">
|
||
<option value="itemName">按物料名称汇总</option>
|
||
<option value="specification">按规格汇总</option>
|
||
<option value="material">按材质汇总</option>
|
||
<option value="manufacturer">按厂家汇总</option>
|
||
</select>
|
||
</div>
|
||
<!-- 柱状图容器 -->
|
||
<div class="chart-content" ref="chartRef"></div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
// 引入ECharts,全局引入的项目可删除该行
|
||
import * as echarts from 'echarts'
|
||
|
||
export default {
|
||
props: {
|
||
data: {
|
||
type: Array,
|
||
default: () => []
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
chartInstance: null, // ECharts实例
|
||
selectedType: 'manufacturer', // 默认选中:按厂家汇总
|
||
// 维度名称映射 - 用于图表标题/提示框展示
|
||
typeLabel: {
|
||
itemName: '物料名称',
|
||
specification: '规格',
|
||
material: '材质',
|
||
manufacturer: '厂家'
|
||
}
|
||
}
|
||
},
|
||
watch: {
|
||
// 监听数据源变化,深度监听,数据更新重新渲染
|
||
data: {
|
||
deep: true,
|
||
handler() {
|
||
this.renderChart()
|
||
}
|
||
}
|
||
},
|
||
mounted() {
|
||
// 挂载后初始化图表
|
||
this.renderChart()
|
||
// 窗口大小变化,图表自适应
|
||
window.addEventListener('resize', this.resizeChart)
|
||
},
|
||
beforeDestroy() {
|
||
// 销毁资源,防止内存泄漏
|
||
window.removeEventListener('resize', this.resizeChart)
|
||
this.chartInstance && this.chartInstance.dispose()
|
||
},
|
||
methods: {
|
||
/**
|
||
* 核心处理方法:按选择的维度 + itemType自动匹配字段,汇总数据
|
||
* ✅ 核心规则:itemType=product → 取嵌套的product对象内的对应字段
|
||
* ✅ 其他itemType → 取根级的对应字段
|
||
*/
|
||
formatChartData() {
|
||
const sourceData = this.data || []
|
||
if (sourceData.length === 0) return { xData: [], yData: [] }
|
||
|
||
// 分组汇总的核心对象
|
||
const countObj = {}
|
||
sourceData.forEach(item => {
|
||
if (!item) return
|
||
let targetValue = ''
|
||
const { itemType } = item
|
||
|
||
// ✅ 核心判断:根据itemType决定取值来源
|
||
switch (this.selectedType) {
|
||
case 'itemName':
|
||
targetValue = itemType === 'product'
|
||
? (item.product?.productName || item.product?.name || '无名称')
|
||
: (item.itemName || '无名称')
|
||
break
|
||
case 'specification':
|
||
targetValue = itemType === 'product'
|
||
? (item.product?.specification || '无规格')
|
||
: (item.specification || '无规格')
|
||
break
|
||
case 'material':
|
||
targetValue = itemType === 'product'
|
||
? (item.product?.material || '无材质')
|
||
: (item.material || '无材质')
|
||
break
|
||
case 'manufacturer':
|
||
targetValue = itemType === 'product'
|
||
? (item.product?.manufacturer || '无厂家')
|
||
: (item.manufacturer || '无厂家')
|
||
break
|
||
}
|
||
|
||
// 空值统一处理为【无数据】,避免图表展示空字符串
|
||
const key = targetValue || '无数据'
|
||
countObj[key] = countObj[key] ? countObj[key] + 1 : 1
|
||
})
|
||
|
||
// 转为数组并按【数量倒序】排列,数量多的柱子在前,更直观
|
||
const sortArr = Object.entries(countObj).sort((a, b) => b[1] - a[1])
|
||
// 分离x轴类目 和 y轴数量
|
||
const xData = sortArr.map(item => item[0])
|
||
const yData = sortArr.map(item => item[1])
|
||
|
||
return { xData, yData }
|
||
},
|
||
|
||
// 初始化渲染柱状图
|
||
renderChart() {
|
||
const { xData, yData } = this.formatChartData()
|
||
const chartDom = this.$refs.chartRef
|
||
if (!chartDom) return
|
||
|
||
// 初始化ECharts实例
|
||
this.chartInstance = echarts.init(chartDom)
|
||
const option = {
|
||
grid: { left: '8%', right: '8%', bottom: '8%', top: '8%' },
|
||
// 图表标题,根据选中维度动态变化
|
||
title: {
|
||
text: `${this.typeLabel[this.selectedType]} 数据汇总`,
|
||
left: 'center',
|
||
textStyle: { fontSize: 16 }
|
||
},
|
||
// 悬浮提示框
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
formatter: `{b}<br/>数量:{c} 条`,
|
||
axisPointer: { type: 'shadow' }
|
||
},
|
||
// x轴:类目轴(名称/规格/材质/厂家)
|
||
xAxis: [
|
||
{
|
||
type: 'category',
|
||
data: xData,
|
||
axisLabel: {
|
||
rotate: 30, // 文字旋转,防止类目名称过长重叠
|
||
fontSize: 12,
|
||
overflow: 'truncate'
|
||
},
|
||
axisTick: { alignWithLabel: true }
|
||
}
|
||
],
|
||
// y轴:数量轴,最小值为0
|
||
yAxis: [
|
||
{
|
||
type: 'value',
|
||
name: '数据数量',
|
||
min: 0,
|
||
axisLabel: { formatter: '{value}' }
|
||
}
|
||
],
|
||
// 柱状图核心配置
|
||
series: [
|
||
{
|
||
name: '数据数量',
|
||
type: 'bar',
|
||
barWidth: '60%', // 柱子宽度
|
||
data: yData,
|
||
itemStyle: {
|
||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||
{ offset: 0, color: '#0088ff' },
|
||
{ offset: 1, color: '#0055bb' }
|
||
])
|
||
},
|
||
// 柱子上显示具体数值
|
||
label: {
|
||
show: true,
|
||
position: 'top',
|
||
fontSize: 12
|
||
}
|
||
}
|
||
]
|
||
}
|
||
|
||
// 设置配置项渲染
|
||
this.chartInstance.setOption(option, true)
|
||
},
|
||
|
||
// 窗口大小变化,图表自适应
|
||
resizeChart() {
|
||
this.chartInstance && this.chartInstance.resize()
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* 外层容器样式 */
|
||
.category-bar-chart-container {
|
||
width: 100%;
|
||
height: 550px;
|
||
}
|
||
/* 筛选下拉框样式 */
|
||
.chart-select {
|
||
width: 100%;
|
||
padding: 8px 0;
|
||
text-align: center;
|
||
}
|
||
.chart-select select {
|
||
padding: 6px 12px;
|
||
font-size: 14px;
|
||
border-radius: 4px;
|
||
border: 1px solid #dcdcdc;
|
||
cursor: pointer;
|
||
}
|
||
/* 图表容器样式 */
|
||
.chart-content {
|
||
width: 100%;
|
||
height: calc(100% - 40px);
|
||
}
|
||
</style> |