-
-
-
-
-
-
- {{ scope.row['type_' + type.energyTypeId] ? Number(scope.row['type_' + type.energyTypeId]).toFixed(2) : 0 }}
-
-
-
-
- {{ scope.row.total ? Number(scope.row.total).toFixed(2) : 0 }}
-
-
-
+
+
@@ -140,6 +143,11 @@ export default {
productionLines: []
},
energyTypeList: [],
+ productionLines: [],
+ selectedEnergyTypeId: null,
+ selectedLineAnalysisEnergyTypeId: null,
+ selectedSankeyEnergyTypeId: null,
+ selectedLines: [],
meterList: [],
energyRecords: [],
statistics: [],
@@ -147,8 +155,8 @@ export default {
trendChartType: 'day',
trendChart: null,
energyTypeChart: null,
- productionLineChart: null,
- deviceRankingChart: null,
+ lineEnergyTrendChart: null,
+ sankeyChart: null,
loading: false,
}
},
@@ -161,31 +169,44 @@ export default {
window.removeEventListener('resize', this.handleResize)
if (this.trendChart) this.trendChart.dispose()
if (this.energyTypeChart) this.energyTypeChart.dispose()
- if (this.productionLineChart) this.productionLineChart.dispose()
- if (this.deviceRankingChart) this.deviceRankingChart.dispose()
+ if (this.lineEnergyTrendChart) this.lineEnergyTrendChart.dispose()
+ if (this.sankeyChart) this.sankeyChart.dispose()
},
methods: {
initCharts() {
this.trendChart = echarts.init(this.$refs.trendChart)
this.energyTypeChart = echarts.init(this.$refs.energyTypeChart)
- this.productionLineChart = echarts.init(this.$refs.productionLineChart)
- this.deviceRankingChart = echarts.init(this.$refs.deviceRankingChart)
+ this.lineEnergyTrendChart = echarts.init(this.$refs.lineEnergyTrendChart)
+ this.sankeyChart = echarts.init(this.$refs.sankeyChart)
},
handleResize() {
this.trendChart && this.trendChart.resize()
this.energyTypeChart && this.energyTypeChart.resize()
- this.productionLineChart && this.productionLineChart.resize()
- this.deviceRankingChart && this.deviceRankingChart.resize()
+ this.lineEnergyTrendChart && this.lineEnergyTrendChart.resize()
+ this.sankeyChart && this.sankeyChart.resize()
},
async loadBasicData() {
try {
this.loading = true;
const [energyTypeRes, meterRes] = await Promise.all([
listEnergyType({ pageSize: 999 }),
- listMeter({ pageSize: 999, isTotalMeter: 0 })
+ listMeter({ pageSize: 999 })
])
this.energyTypeList = energyTypeRes.rows || []
+ if (this.energyTypeList.length > 0) {
+ this.selectedEnergyTypeId = this.energyTypeList[0].energyTypeId
+ this.selectedLineAnalysisEnergyTypeId = this.energyTypeList[0].energyTypeId
+ }
this.meterList = meterRes.rows || []
+ this.productionLines = this.dict.type.sys_lines || []
+ if (this.productionLines.length > 0) {
+ this.selectedLines = this.productionLines.slice(0, 3).map(l => l.value)
+ }
+ // 设置默认的桑基图能源类型(筛选有且只有一个主表的能源)
+ const validSankeyEnergyTypes = this.getValidSankeyEnergyTypes()
+ if (validSankeyEnergyTypes.length > 0) {
+ this.selectedSankeyEnergyTypeId = validSankeyEnergyTypes[0].energyTypeId
+ }
await this.loadData()
} catch (error) {
console.error('加载基础数据失败', error)
@@ -216,8 +237,8 @@ export default {
this.calculateStatistics()
this.updateTrendChart()
this.updateEnergyTypeChart()
- this.updateProductionLineChart()
- this.updateDeviceRankingChart()
+ this.updateLineEnergyTrendChart()
+ this.updateSankeyChart()
this.updateTableData()
},
calculateStatistics() {
@@ -272,13 +293,13 @@ export default {
},
updateTrendChart() {
const dateFormat = this.trendChartType === 'day' ? 'YYYY-MM-DD' :
- this.trendChartType === 'week' ? 'YYYY-WW' : 'YYYY-MM'
-
+ this.trendChartType === 'week' ? 'YYYY-WW' : 'YYYY-MM'
+
const groupedData = new Map()
this.energyRecords.forEach(record => {
const dateKey = dayjs(record.recordDate).format(dateFormat)
const energyType = this.getEnergyTypeName(record.energyId)
-
+
if (!groupedData.has(dateKey)) {
groupedData.set(dateKey, {})
}
@@ -290,7 +311,7 @@ export default {
const dates = Array.from(groupedData.keys()).sort()
const energyTypes = Array.from(new Set(this.energyRecords.map(r => this.getEnergyTypeName(r.energyId))))
-
+
const colors = ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C', '#909399', '#00CED1']
const series = energyTypes.map((type, index) => ({
name: type,
@@ -333,14 +354,26 @@ export default {
this.trendChart.setOption(option)
},
updateEnergyTypeChart() {
- const typeMap = new Map()
+ const lineMap = new Map()
+
this.energyRecords.forEach(record => {
- const typeName = this.getEnergyTypeName(record.energyId)
- typeMap.set(typeName, (typeMap.get(typeName) || 0) + (Number(record.consumption) || 0))
+ // 只统计选中的能源类型
+ if (this.selectedEnergyTypeId && record.energyId !== this.selectedEnergyTypeId) {
+ return
+ }
+
+ const meter = this.meterList.find(m => m.meterId === record.meterId)
+ if (meter && meter.productionLine) {
+ const lines = Array.isArray(meter.productionLine) ? meter.productionLine : [meter.productionLine]
+ lines.forEach(lineId => {
+ const lineName = this.getLineName(lineId)
+ lineMap.set(lineName, (lineMap.get(lineName) || 0) + (Number(record.consumption) || 0))
+ })
+ }
})
- const data = Array.from(typeMap.entries()).map(([name, value]) => ({ name, value }))
-
+ const data = Array.from(lineMap.entries()).map(([name, value]) => ({ name, value }))
+
const option = {
tooltip: {
trigger: 'item',
@@ -353,7 +386,7 @@ export default {
},
series: [
{
- name: '能源类型',
+ name: '产线消耗',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
@@ -382,112 +415,8 @@ export default {
}
this.energyTypeChart.setOption(option)
},
- updateProductionLineChart() {
- const lineMap = new Map()
-
- this.energyRecords.forEach(record => {
- const meter = this.meterList.find(m => m.meterId === record.meterId)
- if (meter && meter.productionLine) {
- const lines = Array.isArray(meter.productionLine) ? meter.productionLine : [meter.productionLine]
- lines.forEach(lineId => {
- const lineName = this.getLineName(lineId)
- lineMap.set(lineName, (lineMap.get(lineName) || 0) + (Number(record.consumption) || 0))
- })
- }
- })
- const sortedLines = Array.from(lineMap.entries())
- .sort((a, b) => b[1] - a[1])
-
- const option = {
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'shadow'
- }
- },
- grid: {
- left: '3%',
- right: '4%',
- bottom: '3%',
- containLabel: true
- },
- xAxis: {
- type: 'value',
- name: '能耗'
- },
- yAxis: {
- type: 'category',
- data: sortedLines.map(item => item[0])
- },
- series: [
- {
- name: '能耗',
- type: 'bar',
- data: sortedLines.map(item => item[1]),
- itemStyle: {
- color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
- { offset: 0, color: '#409EFF' },
- { offset: 1, color: '#00CED1' }
- ]),
- borderRadius: [0, 4, 4, 0]
- }
- }
- ]
- }
- this.productionLineChart.setOption(option)
- },
- updateDeviceRankingChart() {
- const deviceMap = new Map()
-
- this.energyRecords.forEach(record => {
- const meter = this.meterList.find(m => m.meterId === record.meterId)
- const deviceName = meter ? meter.meterCode : `设备${record.meterId}`
- deviceMap.set(deviceName, (deviceMap.get(deviceName) || 0) + (Number(record.consumption) || 0))
- })
- const sortedDevices = Array.from(deviceMap.entries())
- .sort((a, b) => b[1] - a[1])
- .slice(0, 10)
-
- const option = {
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'shadow'
- }
- },
- grid: {
- left: '3%',
- right: '4%',
- bottom: '3%',
- containLabel: true
- },
- xAxis: {
- type: 'value',
- name: '能耗'
- },
- yAxis: {
- type: 'category',
- data: sortedDevices.map(item => item[0]).reverse()
- },
- series: [
- {
- name: '能耗',
- type: 'bar',
- data: sortedDevices.map(item => item[1]).reverse(),
- itemStyle: {
- color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
- { offset: 0, color: '#67C23A' },
- { offset: 1, color: '#95E67D' }
- ]),
- borderRadius: [0, 4, 4, 0]
- }
- }
- ]
- }
- this.deviceRankingChart.setOption(option)
- },
getEnergyTypeName(energyTypeId) {
const type = this.energyTypeList.find(t => t.energyTypeId === energyTypeId)
return type ? type.name : '未知'
@@ -496,9 +425,257 @@ export default {
const line = this.dict.type.sys_lines?.find(l => l.value === lineId)
return line ? line.label : lineId
},
+ // 获取有且仅有一个主表的能源类型列表
+ getValidSankeyEnergyTypes() {
+ const energyTypeMeterMap = {}
+ // 按能源类型分组所有表计
+ this.meterList.forEach(meter => {
+ if (!energyTypeMeterMap[meter.energyTypeId]) {
+ energyTypeMeterMap[meter.energyTypeId] = []
+ }
+ energyTypeMeterMap[meter.energyTypeId].push(meter)
+ })
+ // 筛选出有且仅有一个主表的能源类型
+ return this.energyTypeList.filter(type => {
+ const meters = energyTypeMeterMap[type.energyTypeId] || []
+ const totalMeters = meters.filter(m => m.isTotalMeter === 1)
+ return totalMeters.length === 1
+ })
+ },
+ // 更新桑基图
+ updateSankeyChart() {
+ if (!this.selectedSankeyEnergyTypeId) {
+ // 如果没有选择能源类型,显示空图表
+ this.sankeyChart.setOption({
+ title: { text: '请选择能源类型' },
+ series: []
+ })
+ return
+ }
+
+ // 获取当前能源类型的所有表计
+ const currentEnergyMeters = this.meterList.filter(m => m.energyTypeId === this.selectedSankeyEnergyTypeId)
+ const totalMeter = currentEnergyMeters.find(m => m.isTotalMeter === 1)
+ const subMeters = currentEnergyMeters.filter(m => m.isTotalMeter !== 1)
+
+ if (!totalMeter) {
+ this.sankeyChart.setOption({
+ title: { text: '当前能源类型没有主表' },
+ series: []
+ })
+ return
+ }
+
+ // 计算各节点的能耗
+ const nodeMap = {}
+ const links = []
+ const nodes = []
+
+ // 获取能源类型名称
+ const energyType = this.energyTypeList.find(t => t.energyTypeId === this.selectedSankeyEnergyTypeId)
+ const energyTypeName = energyType ? energyType.name : '能源'
+
+ // 1. 能源节点 -> 总表节点
+ let totalConsumption = 0
+ // 先计算总表的总消耗
+ this.energyRecords.forEach(record => {
+ if (record.meterId === totalMeter.meterId) {
+ totalConsumption += Number(record.consumption) || 0
+ }
+ })
+
+ // 添加能源节点
+ const energyNodeName = energyTypeName
+ nodes.push({ name: energyNodeName })
+ nodeMap[energyNodeName] = true
+
+ // 添加总表节点
+ const totalMeterName = `总表:${totalMeter.meterCode}`
+ nodes.push({ name: totalMeterName })
+ nodeMap[totalMeterName] = true
+
+ // 能源 -> 总表 连接
+ links.push({
+ source: energyNodeName,
+ target: totalMeterName,
+ value: totalConsumption
+ })
+
+ // 2. 总表 -> 分表/产线节点
+ // 按产线分组分表
+ const lineSubMeterMap = {}
+ subMeters.forEach(meter => {
+ const lineName = meter.productionLine ? this.getLineName(meter.productionLine) : '未分配产线'
+ if (!lineSubMeterMap[lineName]) {
+ lineSubMeterMap[lineName] = []
+ }
+ lineSubMeterMap[lineName].push(meter)
+ })
+
+ // 为每条产线创建节点
+ Object.keys(lineSubMeterMap).forEach(lineName => {
+ const lineMeters = lineSubMeterMap[lineName]
+ let lineConsumption = 0
+
+ // 计算产线总消耗
+ lineMeters.forEach(meter => {
+ this.energyRecords.forEach(record => {
+ if (record.meterId === meter.meterId) {
+ lineConsumption += Number(record.consumption) || 0
+ }
+ })
+ })
+
+ if (lineConsumption > 0) {
+ // 产线节点
+ nodes.push({ name: `产线:${lineName}` })
+ nodeMap[`产线:${lineName}`] = true
+ links.push({
+ source: totalMeterName,
+ target: `产线:${lineName}`,
+ value: lineConsumption
+ })
+
+ // 为产线下的每个分表创建节点
+ lineMeters.forEach(meter => {
+ let meterConsumption = 0
+ this.energyRecords.forEach(record => {
+ if (record.meterId === meter.meterId) {
+ meterConsumption += Number(record.consumption) || 0
+ }
+ })
+ if (meterConsumption > 0) {
+ const meterNodeName = `设备:${meter.meterCode}`
+ if (!nodeMap[meterNodeName]) {
+ nodes.push({ name: meterNodeName })
+ nodeMap[meterNodeName] = true
+ }
+ links.push({
+ source: `产线:${lineName}`,
+ target: meterNodeName,
+ value: meterConsumption
+ })
+ }
+ })
+ }
+ })
+
+ // 渲染桑基图
+ const option = {
+ tooltip: {
+ trigger: 'item',
+ formatter: function (params) {
+ if (params.dataType === 'edge') {
+ return `${params.data.source} → ${params.data.target}
能耗: ${params.data.value.toFixed(2)}`
+ } else {
+ return `${params.name}
能耗: ${params.value ? params.value.toFixed(2) : 0}`
+ }
+ }
+ },
+ series: [{
+ type: 'sankey',
+ layout: 'none',
+ emphasis: {
+ focus: 'adjacency'
+ },
+ data: nodes,
+ links: links,
+ lineStyle: {
+ color: 'gradient',
+ curveness: 0.5
+ },
+ label: {
+ color: '#333'
+ }
+ }]
+ }
+
+ this.sankeyChart.setOption(option)
+ },
+ updateLineEnergyTrendChart() {
+ const dateFormat = 'YYYY-MM-DD'
+ const groupedData = new Map()
+
+ this.energyRecords.forEach(record => {
+ if (this.selectedLineAnalysisEnergyTypeId && record.energyId !== this.selectedLineAnalysisEnergyTypeId) {
+ return
+ }
+
+ const meter = this.meterList.find(m => m.meterId === record.meterId)
+ if (!meter || !meter.productionLine) return
+
+ const lines = Array.isArray(meter.productionLine) ? meter.productionLine : [meter.productionLine]
+ const dateKey = dayjs(record.recordDate).format(dateFormat)
+
+ lines.forEach(lineId => {
+ if (this.selectedLines.length > 0 && !this.selectedLines.includes(lineId)) {
+ return
+ }
+
+ const lineName = this.getLineName(lineId)
+ if (!groupedData.has(dateKey)) {
+ groupedData.set(dateKey, {})
+ }
+ if (!groupedData.get(dateKey)[lineName]) {
+ groupedData.get(dateKey)[lineName] = 0
+ }
+ groupedData.get(dateKey)[lineName] += Number(record.consumption) || 0
+ })
+ })
+
+ const dates = Array.from(groupedData.keys()).sort()
+ const lineNames = Array.from(new Set(
+ Array.from(groupedData.values()).flatMap(obj => Object.keys(obj))
+ )).sort()
+
+ const colors = ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C', '#909399', '#00CED1', '#00BFFF', '#32CD32']
+ const series = lineNames.map((lineName, index) => ({
+ name: lineName,
+ type: 'line',
+ smooth: true,
+ data: dates.map(date => groupedData.get(date)[lineName] || 0),
+ itemStyle: { color: colors[index % colors.length] },
+ areaStyle: {
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+ { offset: 0, color: colors[index % colors.length] + '80' },
+ { offset: 1, color: colors[index % colors.length] + '10' }
+ ])
+ }
+ }))
+
+ const selectedEnergyType = this.energyTypeList.find(t => t.energyTypeId === this.selectedLineAnalysisEnergyTypeId)
+
+ const option = {
+ tooltip: {
+ trigger: 'axis'
+ },
+ legend: {
+ data: lineNames,
+ top: 10
+ },
+ grid: {
+ left: '3%',
+ right: '4%',
+ bottom: '3%',
+ top: 80,
+ containLabel: true
+ },
+ xAxis: {
+ type: 'category',
+ boundaryGap: false,
+ data: dates
+ },
+ yAxis: {
+ type: 'value',
+ name: selectedEnergyType ? selectedEnergyType.name : '能耗'
+ },
+ series
+ }
+ this.lineEnergyTrendChart.setOption(option)
+ },
updateTableData() {
const lineEnergyMap = new Map()
-
+
this.energyRecords.forEach(record => {
const meter = this.meterList.find(m => m.meterId === record.meterId)
if (meter && meter.productionLine) {
@@ -506,7 +683,7 @@ export default {
lines.forEach(lineId => {
const lineName = this.getLineName(lineId)
const energyTypeId = record.energyId
-
+
if (!lineEnergyMap.has(lineName)) {
lineEnergyMap.set(lineName, {})
}
@@ -596,12 +773,15 @@ export default {
&.stat-0 {
border-left: 4px solid #409EFF;
}
+
&.stat-1 {
border-left: 4px solid #67C23A;
}
+
&.stat-2 {
border-left: 4px solid #E6A23C;
}
+
&.stat-3 {
border-left: 4px solid #F56C6C;
}
@@ -620,14 +800,17 @@ export default {
background: linear-gradient(135deg, #409EFF, #00CED1);
color: #ffffff;
}
+
.stat-1 & {
background: linear-gradient(135deg, #67C23A, #95E67D);
color: #ffffff;
}
+
.stat-2 & {
background: linear-gradient(135deg, #E6A23C, #F7D94C);
color: #ffffff;
}
+
.stat-3 & {
background: linear-gradient(135deg, #F56C6C, #FC9494);
color: #ffffff;
@@ -663,6 +846,8 @@ export default {
}
.charts-section {
+ margin-bottom: 20px;
+
.chart-card {
background: #ffffff;
border-radius: 8px;
From b129dd8f13c5da2339b1ac8ddd0234e1c7d66801 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=A0=82=E7=B3=96?= <2178503051@qq.com>
Date: Thu, 7 May 2026 11:08:05 +0800
Subject: [PATCH 2/2] =?UTF-8?q?refactor(CoilSelector):=20=E9=87=8D?=
=?UTF-8?q?=E6=9E=84=E5=A4=9A=E9=80=89=E6=A8=A1=E5=BC=8F=E7=95=8C=E9=9D=A2?=
=?UTF-8?q?=E5=B9=B6=E7=A7=BB=E9=99=A4=E9=94=80=E5=94=AE=E5=8F=97=E9=99=90?=
=?UTF-8?q?=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
重构多选模式界面,使用拖拽面板展示已选钢卷列表
移除销售视角受限逻辑及相关样式
调整默认分页大小为50条
---
klp-ui/src/components/CoilSelector/index.vue | 261 ++++++++++--------
.../wms/delivery/components/detailTable.vue | 2 +-
2 files changed, 146 insertions(+), 117 deletions(-)
diff --git a/klp-ui/src/components/CoilSelector/index.vue b/klp-ui/src/components/CoilSelector/index.vue
index fcfb86d1..c072d6b6 100644
--- a/klp-ui/src/components/CoilSelector/index.vue
+++ b/klp-ui/src/components/CoilSelector/index.vue
@@ -85,26 +85,90 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 总净重:{{ coilTrimStatistics.total_net_weight || 0 }}t
+
+
+
+
+
+
+
+
+
+
+
+
+ 总卷数:
+ {{ totalCoils }}
+
+
+ 总净重:
+ {{ totalNetWeight }}t
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+ 请从上方表格中选择钢卷
+
+
+ 点击上方表格行进行选择,再次点击可取消选择
+
+
+ {{ totalCoils }} 卷
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
@@ -116,48 +180,18 @@
-
-
-
-
- 总净重:{{ coilTrimStatistics.total_net_weight || 0 }}t
-
+
+
+
+ 总净重:{{ coilTrimStatistics.total_net_weight || 0 }}t
+
-
-
-
-
-
-
-
- 总卷数:
- {{ totalCoils }}
-
-
- 总净重:
- {{ totalNetWeight }}t
-
+
-
-
-
-
-
-
-
-
-
-
- 删除
-
-
-
-