feat(wms): 新增发货报表页面及图表组件
新增发货报表页面,包含时间趋势折线图和维度汇总柱状图 添加报表数据统计展示和明细表格功能 优化库存汇总页面,增加冷硬卷、冷轧卷等分类统计 重构发货报表页面布局和交互逻辑
This commit is contained in:
219
klp-ui/src/views/wms/delivery/report/charts/bar.vue
Normal file
219
klp-ui/src/views/wms/delivery/report/charts/bar.vue
Normal 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>
|
||||
Reference in New Issue
Block a user