Files
klp-oa/klp-ui/src/views/wms/delivery/report/charts/bar.vue
砂糖 a64eb08736 feat(wms/receive): 添加收货报表的折线图和柱状图展示
- 在收货报表页面新增双Y轴折线图和柱状图组件
- 默认按厂家汇总展示数据
- 图表支持数据变化自动更新和窗口大小自适应
2026-01-14 11:02:39 +08:00

219 lines
6.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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