feat(wms/receive): 添加收货报表的折线图和柱状图展示

- 在收货报表页面新增双Y轴折线图和柱状图组件
- 默认按厂家汇总展示数据
- 图表支持数据变化自动更新和窗口大小自适应
This commit is contained in:
砂糖
2026-01-14 11:02:39 +08:00
parent 16eaceeba0
commit a64eb08736
4 changed files with 313 additions and 1 deletions

View File

@@ -28,7 +28,7 @@ export default {
data() {
return {
chartInstance: null, // ECharts实例
selectedType: 'itemName', // 默认选中:按物料名称汇总
selectedType: 'manufacturer', // 默认选中:按厂家汇总
// 维度名称映射 - 用于图表标题/提示框展示
typeLabel: {
itemName: '物料名称',

View File

@@ -0,0 +1,142 @@
<template>
<!-- 柱状图容器 必须设置宽高绑定ref获取DOM节点 -->
<div class="bar-chart-container" ref="chartRef"></div>
</template>
<script>
// Vue2 标准echarts引入方式兼容性最优
import * as echarts from 'echarts'
export default {
name: 'BarChart',
props: {
data: {
type: Array,
default: () => []
}
},
data() {
return {
chartInstance: null // 存储echarts实例防止重复渲染/内存泄漏
}
},
mounted() {
// 初始化图表
this.initEcharts()
// 监听窗口大小变化,图表自适应
window.addEventListener('resize', this.resizeChart)
},
beforeDestroy() {
// Vue2 销毁生命周期钩子,清理实例+解绑监听
window.removeEventListener('resize', this.resizeChart)
if (this.chartInstance) {
this.chartInstance.dispose()
this.chartInstance = null
}
},
watch: {
// ✅ 深度监听父组件传的data数据数据更新/新增/删除 自动重绘图表
data: {
deep: true,
immediate: true,
handler() {
this.chartInstance && this.initEcharts()
}
}
},
methods: {
// 初始化柱状图核心方法
initEcharts() {
const chartDom = this.$refs.chartRef
// 防止重复初始化,先销毁旧实例
if (this.chartInstance) {
this.chartInstance.dispose()
}
// 创建新的echarts实例
this.chartInstance = echarts.init(chartDom)
// 从props.data中提取图表数据和你的数据格式完全匹配
const xAxisData = this.data.map(item => item.productName) // X轴收货计划名称
const coilCountData = this.data.map(item => item.coilCount) // 总卷数
const totalWeightData = this.data.map(item => Number(item.totalWeight)) // 总重量(字符串转数字)
// 柱状图核心配置项
const option = {
// 图表内边距,防止内容溢出
grid: { left: '3%', right: '10%', bottom: '3%', containLabel: true },
// 鼠标悬浮提示框,精准展示数据
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' }, // 鼠标悬浮显示阴影指示器,柱状图专属
formatter: '{b}<br/>{a}{c}'
},
// 图例,右上角展示指标名称,可点击切换显隐
legend: {
data: ['总卷数', '总重量(吨)'],
top: 10
},
// X轴 类目轴 - 收货计划名称/日期
xAxis: [
{
type: 'category',
data: xAxisData,
axisLabel: { interval: 0 }, // 强制显示所有X轴标签不隐藏
axisTick: { show: false } // 隐藏X轴刻度线美化
}
],
// ✅ 双Y轴配置核心解决数值量级差异大的问题
yAxis: [
{
// 左侧Y轴 - 对应总卷数
type: 'value',
name: '总卷数',
nameTextStyle: { color: '#1890ff' },
axisLine: { lineStyle: { color: '#1890ff' } }
},
{
// 右侧Y轴 - 对应总重量
type: 'value',
name: '总重量(吨)',
nameTextStyle: { color: '#ff7a45' },
axisLine: { lineStyle: { color: '#ff7a45' } },
splitLine: { show: false } // 关闭右侧网格线,避免页面杂乱
}
],
// ✅ 双系列柱状图配置
series: [
{
name: '总卷数',
type: 'bar', // 图表类型:柱状图
yAxisIndex: 0, // 绑定左侧Y轴
barWidth: '35%', // 柱子宽度,美观适配
itemStyle: { color: '#1890ff', borderRadius: [4, 4, 0, 0] }, // 柱子颜色+顶部圆角
data: coilCountData
},
{
name: '总重量(吨)',
type: 'bar', // 图表类型:柱状图
yAxisIndex: 1, // 绑定右侧Y轴
barWidth: '35%',
itemStyle: { color: '#ff7a45', borderRadius: [4, 4, 0, 0] },
data: totalWeightData
}
]
}
// 挂载配置项渲染图表
this.chartInstance.setOption(option)
},
// 图表自适应窗口大小
resizeChart() {
this.chartInstance && this.chartInstance.resize()
}
}
}
</script>
<style scoped>
/* 柱状图容器样式,宽高可按需修改,必须设置! */
.bar-chart-container {
width: 100%;
height: 400px;
}
</style>

View File

@@ -0,0 +1,154 @@
<template>
<!-- Echarts图表容器必须设置宽高绑定ref用于获取DOM -->
<div class="line-chart-container" ref="chartRef"></div>
</template>
<script>
// Vue2 推荐这种引入方式适配绝大多数echarts版本
import * as echarts from 'echarts'
export default {
name: 'LineChart',
props: {
data: {
type: Array,
default: () => []
}
},
data() {
return {
// 存储echarts实例对象防止重复渲染
chartInstance: null
}
},
mounted() {
// 组件挂载完成初始化图表
this.initEcharts()
// 监听浏览器窗口大小变化,图表自适应
window.addEventListener('resize', this.resizeChart)
},
beforeDestroy() {
// Vue2 销毁生命周期【重点】不是beforeUnmount
// 销毁实例+解绑监听,防止内存泄漏
window.removeEventListener('resize', this.resizeChart)
if (this.chartInstance) {
this.chartInstance.dispose()
this.chartInstance = null
}
},
watch: {
// 深度监听父组件传入的data数据数据更新时重新渲染图表【核心】
data: {
deep: true,
immediate: true,
handler() {
// 数据变化时,先判断实例是否存在,存在则重新渲染
this.chartInstance && this.initEcharts()
}
}
},
methods: {
// 初始化echarts图表核心方法
initEcharts() {
const chartDom = this.$refs.chartRef
// 防止重复初始化,先销毁再创建
if (this.chartInstance) {
this.chartInstance.dispose()
}
// 初始化图表实例
this.chartInstance = echarts.init(chartDom)
// 从props接收的data中提取图表所需数据
const xAxisData = this.data.map(item => item.productName)
const coilCountData = this.data.map(item => item.coilCount)
// 总重量是字符串格式,强制转数字,保证图表能正常渲染
const totalWeightData = this.data.map(item => Number(item.totalWeight))
// echarts核心配置项 - 双Y轴折线图
const option = {
// 图表内边距,防止内容溢出
grid: { left: '3%', right: '10%', bottom: '3%', containLabel: true },
// 鼠标悬浮提示框,展示精准数据
tooltip: {
trigger: 'axis',
formatter: '{b}<br/>{a}: {c}',
axisPointer: { type: 'cross' }
},
// 图例,可点击切换显隐对应折线
legend: {
data: ['总卷数', '总重量(吨)'],
top: 10
},
// X轴 - 类目轴,展示收货计划名称
xAxis: [
{
type: 'category',
data: xAxisData,
axisLabel: {
interval: 0, // 强制显示所有X轴标签不隐藏
fontSize: 12
}
}
],
// 双Y轴核心配置
yAxis: [
{
// 左侧Y轴 - 对应【总卷数】
type: 'value',
name: '总卷数',
nameTextStyle: { color: '#2db7f5' },
axisLine: { lineStyle: { color: '#2db7f5' } }
},
{
// 右侧Y轴 - 对应【总重量】
type: 'value',
name: '总重量(吨)',
nameTextStyle: { color: '#ff6600' },
axisLine: { lineStyle: { color: '#ff6600' } },
splitLine: { show: false } // 关闭右侧网格线,避免页面杂乱
}
],
// 折线图系列数据
series: [
{
name: '总卷数',
type: 'line',
yAxisIndex: 0, // 指定绑定左侧Y轴
data: coilCountData,
smooth: true, // 平滑折线,更美观
symbol: 'circle', // 拐点为圆点
symbolSize: 6,
lineStyle: { width: 2, color: '#2db7f5' },
itemStyle: { color: '#2db7f5' }
},
{
name: '总重量(吨)',
type: 'line',
yAxisIndex: 1, // 指定绑定右侧Y轴
data: totalWeightData,
smooth: true,
symbol: 'circle',
symbolSize: 6,
lineStyle: { width: 2, color: '#ff6600' },
itemStyle: { color: '#ff6600' }
}
]
}
// 设置配置项并渲染图表
this.chartInstance.setOption(option)
},
// 图表自适应窗口大小
resizeChart() {
this.chartInstance && this.chartInstance.resize()
}
}
}
</script>
<style scoped>
/* 图表容器必须设置宽高,否则无法渲染,可根据需求修改尺寸 */
.line-chart-container {
width: 100%;
height: 400px;
}
</style>

View File

@@ -35,6 +35,16 @@
</el-descriptions>
</el-card>
<el-row :gutter="20" style="margin-top: 20px; margin-bottom: 20px;">
<el-col :span="12">
<line-chart :data="details" />
</el-col>
<el-col :span="12">
<bar-chart :data="details" />
</el-col>
</el-row>
<!-- 详细数据表格 -->
<el-card class="table-card" v-if="details && details.length > 0">
<template #header>
@@ -92,9 +102,15 @@
<script>
import { getReceiptReport } from '@/api/wms/deliveryPlan'
import lineChart from './charts/line.vue';
import barChart from './charts/bar.vue';
export default {
name: 'DeliveryReport',
components: {
lineChart,
barChart
},
data() {
return {
summary: null,