feat(wms): 新增发货报表页面及图表组件

新增发货报表页面,包含时间趋势折线图和维度汇总柱状图
添加报表数据统计展示和明细表格功能
优化库存汇总页面,增加冷硬卷、冷轧卷等分类统计
重构发货报表页面布局和交互逻辑
This commit is contained in:
砂糖
2026-01-13 17:59:42 +08:00
parent 396b3de4c4
commit 57134e1359
6 changed files with 963 additions and 700 deletions

View File

@@ -0,0 +1,219 @@
<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: 'itemName', // 默认选中:按物料名称汇总
// 维度名称映射 - 用于图表标题/提示框展示
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>