From 72bc19fab2a291534b350168d8b059a64b36b8dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A0=82=E7=B3=96?= Date: Mon, 6 Jul 2026 15:43:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(mes/roll,cost/trend):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=BD=A7=E8=BE=8A=E6=8A=A5=E8=A1=A8=E5=92=8C=E6=88=90=E6=9C=AC?= =?UTF-8?q?=E8=B6=8B=E5=8A=BF=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 轧辊报表:修复产线关联逻辑,通过轧辊映射获取产线信息,移除冗余的lineId请求参数 2. 成本趋势页:重构为迷你多图表布局,优化筛选栏样式和交互细节 --- klp-ui/src/views/cost/trend.vue | 352 ++++++++++++--------- klp-ui/src/views/mes/roll/report/index.vue | 11 +- 2 files changed, 211 insertions(+), 152 deletions(-) diff --git a/klp-ui/src/views/cost/trend.vue b/klp-ui/src/views/cost/trend.vue index 3562eca4b..c5f8be51e 100644 --- a/klp-ui/src/views/cost/trend.vue +++ b/klp-ui/src/views/cost/trend.vue @@ -1,7 +1,7 @@ @@ -116,7 +123,8 @@ export default { selectedItems: [], allItems: [], loading: false, - chart: null, + chartInstances: [], + seriesList: [], reports: [], cachedDetails: [], summary: { seriesCount: 0, maxLabel: '-' } @@ -128,10 +136,6 @@ export default { if (!this.selectedLine || !this.productionLines.length) return null return this.productionLines.find(l => l.lineId === this.selectedLine) || null }, - lineNameDisplay() { - const line = this.selectedLineObj - return line ? line.lineName : '' - }, filteredItemOptions() { if (!this.selectedCategories.length) return this.allItems return this.allItems.filter(it => this.selectedCategories.includes(it.category)) @@ -143,12 +147,12 @@ export default { this.$nextTick(() => this.fetchData()) }) this.loadItems() - this._resizeHandler = () => { if (this.chart) this.chart.resize() } + this._resizeHandler = () => this.resizeAllCharts() window.addEventListener('resize', this._resizeHandler) }, beforeDestroy() { window.removeEventListener('resize', this._resizeHandler) - if (this.chart) { this.chart.dispose(); this.chart = null } + this.disposeAllCharts() }, methods: { initDefaultLine() { @@ -186,11 +190,9 @@ export default { this.allItems = r.rows || [] } catch (e) { /* ignore */ } }, - + async fetchData() { - if (this.selectedLine == null) { - return - } + if (this.selectedLine == null) return this.loading = true try { @@ -204,13 +206,13 @@ export default { const allReports = (reportRes.rows || []).sort((a, b) => { return (a.reportDate || '').localeCompare(b.reportDate || '') }) - console.log('[CostTrend] reports:', allReports.length, allReports.map(r => ({ id: r.reportId, title: r.reportTitle, date: r.reportDate }))) if (!allReports.length) { this.$modal.msgWarning('该产线暂无报表') this.reports = [] + this.seriesList = [] this.summary = { seriesCount: 0, maxLabel: '-' } - if (this.chart) this.chart.clear() + this.disposeAllCharts() return } this.reports = allReports @@ -224,15 +226,11 @@ export default { } }, - // ==================== 成本项模式 ==================== async fetchDetailsAndCache(reports) { const allDetailResults = await Promise.all( reports.map(r => listProdDetail({ reportId: r.reportId, pageNum: 1, pageSize: 99999 }) - .then(res => { - console.log('[CostTrend] detail for report', r.reportId, 'rows:', (res.rows || []).length) - return { reportId: r.reportId, rows: res.rows || [] } - }) + .then(res => ({ reportId: r.reportId, rows: res.rows || [] })) .catch(e => { console.error('[CostTrend] detail fetch error for report', r.reportId, e) return { reportId: r.reportId, rows: [] } @@ -279,7 +277,7 @@ export default { }) const xLabels = reports.map(r => this.reportLabel(r)) - const series = [] + const list = [] let globalMax = 0 Array.from(allItemIds).forEach((key, idx) => { @@ -288,115 +286,121 @@ export default { const data = reports.map(r => { const sums = reportSums[r.reportId] || {} const val = sums[key] ?? null - if (val != null && val > globalMax) globalMax = val return val }) if (data.every(v => v === null)) return - series.push({ + const maxVal = Math.max(...data.filter(v => v != null)) + if (maxVal > globalMax) globalMax = maxVal + + list.push({ + key, name, - type: 'line', + color: COLORS[idx % COLORS.length], data, - smooth: true, - symbol: 'circle', - symbolSize: 6, - lineStyle: { width: 2, color: COLORS[idx % COLORS.length] }, - itemStyle: { color: COLORS[idx % COLORS.length] }, - emphasis: { focus: 'series' } + xLabels, + maxVal }) }) + this.seriesList = list this.summary = { - seriesCount: series.length, + seriesCount: list.length, maxLabel: this.formatMoney(globalMax) } - this.$nextTick(() => { - setTimeout(() => this.renderChart(xLabels, series, '数量'), 50) + this.$nextTick(() => this.renderMiniCharts()) + }, + + // ==================== 迷你图表渲染 ==================== + disposeAllCharts() { + this.chartInstances.forEach(c => { + try { c.dispose() } catch (e) { /* ignore */ } + }) + this.chartInstances = [] + }, + + resizeAllCharts() { + this.chartInstances.forEach(c => { + try { c.resize() } catch (e) { /* ignore */ } }) }, - // ==================== 图表渲染 ==================== - renderChart(xLabels, series, yAxisName) { - console.log('[CostTrend] renderChart called, ref:', !!this.$refs.trendChart, 'series:', series.length, 'xLabels:', xLabels.length) - if (!this.$refs.trendChart) return - if (this.chart) this.chart.dispose() - this.chart = echarts.init(this.$refs.trendChart) + renderMiniCharts() { + this.disposeAllCharts() - if (!series.length) { - this.chart.setOption({ - title: { text: '暂无数据', left: 'center', top: 'center', textStyle: { color: '#999', fontSize: 14 } }, - xAxis: { show: false }, - yAxis: { show: false }, - series: [] - }) - return - } + const containers = this.$refs.miniCharts + if (!containers || !containers.length) return - this.chart.setOption({ - tooltip: { - trigger: 'axis', - formatter: function (params) { - if (!params || !params.length) return '' - let html = '' + params[0].axisValue + '
' - params.forEach(p => { - if (p.value != null) { - html += '' + - p.seriesName + ': ' + - Number(p.value).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + - '
' - } - }) - return html - } - }, - legend: { - type: 'scroll', - bottom: 0, - textStyle: { fontSize: 11 }, - pageIconSize: 12, - pageTextStyle: { fontSize: 11 } - }, - grid: { - left: '3%', - right: '4%', - top: '3%', - bottom: series.length > 20 ? '18%' : (series.length > 10 ? '14%' : '10%'), - containLabel: true - }, - xAxis: { - type: 'category', - data: xLabels, - boundaryGap: false, - axisLabel: { - fontSize: 11, - rotate: xLabels.length > 8 ? 30 : 0 - } - }, - yAxis: { - type: 'value', - name: yAxisName, - axisLabel: { - fontSize: 11, - formatter: function (v) { - if (v >= 10000) return (v / 10000).toFixed(1) + '万' - return v + this.seriesList.forEach((s, idx) => { + const el = containers[idx] + if (!el) return + + const chart = echarts.init(el) + chart.setOption({ + tooltip: { + trigger: 'axis', + formatter: function(params) { + if (!params || !params.length) return '' + const p = params[0] + return '' + p.axisValue + '
' + + '' + + p.seriesName + ': ' + + Number(p.value).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + + '' } }, - splitLine: { lineStyle: { type: 'dashed', color: '#e8e8e8' } } - }, - dataZoom: xLabels.length > 10 ? [ - { - type: 'slider', - bottom: series.length > 10 ? (series.length > 20 ? 60 : 50) : 35, - height: 16, - textStyle: { fontSize: 10 } + grid: { + left: 8, + right: 12, + top: 8, + bottom: 24, + containLabel: true }, - { type: 'inside' } - ] : [], - series - }, true) + xAxis: { + type: 'category', + data: s.xLabels, + boundaryGap: false, + axisLabel: { + fontSize: 10, + rotate: s.xLabels.length > 8 ? 25 : 0, + interval: s.xLabels.length > 12 ? 'auto' : 0 + }, + axisTick: { show: false }, + axisLine: { lineStyle: { color: '#ddd' }} + }, + yAxis: { + type: 'value', + axisLabel: { + fontSize: 10, + formatter: function(v) { + if (v >= 10000) return (v / 10000).toFixed(1) + '万' + return v + } + }, + splitLine: { lineStyle: { type: 'dashed', color: '#eee' }}, + splitNumber: 3 + }, + series: [{ + name: s.name, + type: 'line', + data: s.data, + smooth: true, + symbol: 'none', + lineStyle: { width: 1.5, color: s.color }, + itemStyle: { color: s.color }, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { offset: 0, color: s.color + '30' }, + { offset: 1, color: s.color + '05' } + ]) + } + }] + }, true) + + this.chartInstances.push(chart) + }) }, reportLabel(r) { @@ -411,78 +415,132 @@ export default { const num = Number(val) if (num >= 10000) return (num / 10000).toFixed(2) + '万' return num.toFixed(2) + }, + + formatMiniMax(val) { + if (val == null || isNaN(val)) return '-' + if (val >= 10000) return (val / 10000).toFixed(1) + '万' + return Number(val).toFixed(1) } } } diff --git a/klp-ui/src/views/mes/roll/report/index.vue b/klp-ui/src/views/mes/roll/report/index.vue index 6b6999cfc..a46ca075d 100644 --- a/klp-ui/src/views/mes/roll/report/index.vue +++ b/klp-ui/src/views/mes/roll/report/index.vue @@ -244,8 +244,8 @@ export default { this.stats = statsRes.data || {} } - // 2. 获取全部轧辊(用于辊型映射) - const rollRes = await listRollInfo({ pageNum: 1, pageSize: 99999, lineId: this.lineId || undefined }) + // 2. 获取全部轧辊(用于辊型映射和产线关联) + const rollRes = await listRollInfo({ pageNum: 1, pageSize: 99999 }) const rolls = rollRes.rows || [] const rollMap = {} rolls.forEach(r => { rollMap[r.rollId] = r }) @@ -270,7 +270,7 @@ export default { if (this._beginTime) allParams.beginTime = this._beginTime if (this._endTime) allParams.endTime = this._endTime + ' 23:59:59' const allGrindRes = await listRollGrindAll(allParams) - this.computeMonthlyPivot(allGrindRes.data || []) + this.computeMonthlyPivot(allGrindRes.data || [], rollMap) this.updateCharts() } catch (e) { @@ -528,13 +528,14 @@ export default { document.body.removeChild(link) URL.revokeObjectURL(url) }, - computeMonthlyPivot(records) { + computeMonthlyPivot(records, rollMap) { const lineMonthMap = {} const monthSet = new Set() records.forEach(r => { if (!r.grindTime) return const month = r.grindTime.substring(0, 7) - const lid = Number(r.lineId) || 0 + const roll = rollMap[r.rollId] + const lid = Number(roll?.lineId) || 0 if (!lineMonthMap[lid]) { const line = this.productionLines.find(l => Number(l.lineId) === lid) lineMonthMap[lid] = { lineId: lid, lineName: line ? line.lineName : (r.lineName || '未知产线'), months: {} }