库存数据看板
This commit is contained in:
218
klp-ui/src/views/wms/stock/panels/bar.vue
Normal file
218
klp-ui/src/views/wms/stock/panels/bar.vue
Normal file
@@ -0,0 +1,218 @@
|
||||
<template>
|
||||
<div ref="chartContainer" style="height: 100%;"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
name: "MaterialBar",
|
||||
props: {
|
||||
// 图表高度
|
||||
height: {
|
||||
type: String,
|
||||
default: '300px'
|
||||
},
|
||||
// 库存数据
|
||||
stockData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// 选中的仓库ID
|
||||
selectedWarehouseId: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
},
|
||||
// 仓库树数据
|
||||
warehouseTreeData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
stockData: {
|
||||
handler: 'updateChart',
|
||||
deep: true
|
||||
},
|
||||
selectedWarehouseId: {
|
||||
handler: 'updateChart',
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart();
|
||||
this.updateChart();
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
if (this.chart) {
|
||||
this.chart.dispose();
|
||||
}
|
||||
this.chart = echarts.init(this.$refs.chartContainer);
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
},
|
||||
handleResize() {
|
||||
this.chart && this.chart.resize();
|
||||
},
|
||||
// 获取所有子仓库ID(包括自身)
|
||||
getAllWarehouseIds(warehouseId) {
|
||||
const ids = new Set();
|
||||
|
||||
const findChildren = (treeData, targetId) => {
|
||||
for (const node of treeData) {
|
||||
if (String(node.warehouseId) === String(targetId)) {
|
||||
// 找到目标节点,收集它和它的所有子节点的ID
|
||||
const collectIds = (node) => {
|
||||
ids.add(String(node.warehouseId));
|
||||
if (node.children) {
|
||||
node.children.forEach(child => collectIds(child));
|
||||
}
|
||||
};
|
||||
collectIds(node);
|
||||
return true;
|
||||
}
|
||||
if (node.children && findChildren(node.children, targetId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (warehouseId) {
|
||||
findChildren(this.warehouseTreeData, warehouseId);
|
||||
}
|
||||
|
||||
return ids;
|
||||
},
|
||||
|
||||
// 处理数据,获取前10的物料
|
||||
processData() {
|
||||
let filteredData;
|
||||
|
||||
if (this.selectedWarehouseId) {
|
||||
// 获取选中仓库及其所有子仓库的ID
|
||||
const warehouseIds = this.getAllWarehouseIds(this.selectedWarehouseId);
|
||||
// 过滤属于这些仓库的数据
|
||||
filteredData = this.stockData.filter(item => warehouseIds.has(String(item.warehouseId)));
|
||||
} else {
|
||||
filteredData = this.stockData;
|
||||
}
|
||||
|
||||
// 按物料分组并汇总数量
|
||||
const materialMap = new Map();
|
||||
filteredData.forEach(item => {
|
||||
const key = item.itemName;
|
||||
const quantity = Number(item.quantity) || 0;
|
||||
const currentQuantity = materialMap.get(key) || 0;
|
||||
materialMap.set(key, currentQuantity + quantity);
|
||||
});
|
||||
|
||||
// 转换为数组并排序
|
||||
const sortedData = Array.from(materialMap.entries())
|
||||
.map(([name, value]) => ({ name, value }))
|
||||
.sort((a, b) => b.value - a.value)
|
||||
.slice(0, 10); // 只取前10名
|
||||
|
||||
return sortedData;
|
||||
},
|
||||
updateChart() {
|
||||
if (!this.chart) return;
|
||||
|
||||
const data = this.processData();
|
||||
const names = data.map(item => item.name);
|
||||
const values = data.map(item => item.value);
|
||||
|
||||
// 计算最大值,用于设置图表的最大刻度
|
||||
const maxValue = Math.max(...values);
|
||||
const interval = Math.ceil(maxValue / 5); // 将刻度分为5份
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: this.selectedWarehouseId ? '当前仓库及子仓库物料TOP10' : '全部仓库物料TOP10',
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
},
|
||||
formatter: (params) => {
|
||||
const data = params[0];
|
||||
return `${data.name}<br/>数量: ${data.value}`;
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, 0.01],
|
||||
max: Math.ceil(maxValue / interval) * interval,
|
||||
interval: interval
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: names,
|
||||
axisTick: {
|
||||
alignWithLabel: true
|
||||
},
|
||||
axisLabel: {
|
||||
formatter: (value) => {
|
||||
// 如果名称太长,截断并添加省略号
|
||||
if (value.length > 10) {
|
||||
return value.substring(0, 10) + '...';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '数量',
|
||||
type: 'bar',
|
||||
data: values,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{ offset: 0, color: '#83bff6' },
|
||||
{ offset: 0.5, color: '#188df0' },
|
||||
{ offset: 1, color: '#188df0' }
|
||||
])
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{ offset: 0, color: '#2378f7' },
|
||||
{ offset: 0.7, color: '#2378f7' },
|
||||
{ offset: 1, color: '#83bff6' }
|
||||
])
|
||||
}
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'right',
|
||||
formatter: '{c}'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
this.chart.setOption(option, true);
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
if (this.chart) {
|
||||
this.chart.dispose();
|
||||
this.chart = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user