From b44d9d9daff2ebb15b64034339ecf311dabc20c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A0=82=E7=B3=96?= <2178503051@qq.com> Date: Tue, 12 May 2026 14:18:01 +0800 Subject: [PATCH] =?UTF-8?q?refactor(wms/coil):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E7=94=9F=E4=BA=A7=E5=B7=A5=E8=89=BA=E6=95=B0=E6=8D=AE=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 将接口从get请求改为post请求,适配后端参数变更 2. 重新设计生产工艺数据页面,拆分为趋势参数、厚度曲线、带钢板形、板形曲线四个标签页 3. 新增趋势参数树形侧边栏,支持分类展开选择查看不同参数曲线 4. 新增厚度、板形3D热力图、板形曲线图表展示 5. 优化图表渲染逻辑和样式,统一数据处理与加载状态 --- klp-ui/src/api/wms/coil.js | 6 +- klp-ui/src/views/wms/coil/info.vue | 816 ++++++++++++++++++++++------- 2 files changed, 626 insertions(+), 196 deletions(-) diff --git a/klp-ui/src/api/wms/coil.js b/klp-ui/src/api/wms/coil.js index d1e24225..fe86c1df 100644 --- a/klp-ui/src/api/wms/coil.js +++ b/klp-ui/src/api/wms/coil.js @@ -392,11 +392,11 @@ export function listTypeErrorCoil() { }) } -export function getCoilStatisticsList(params) { +export function getCoilStatisticsList(data) { return request({ url: '/wms/materialCoil/statisticsList', - method: 'get', - params, + method: 'post', + data, }) } diff --git a/klp-ui/src/views/wms/coil/info.vue b/klp-ui/src/views/wms/coil/info.vue index c9c297da..bc73760f 100644 --- a/klp-ui/src/views/wms/coil/info.vue +++ b/klp-ui/src/views/wms/coil/info.vue @@ -734,22 +734,71 @@
📈 生产工艺数据 - 加载中… + 加载中… ({{ perfSegCount }} 段)
-
-
-
-
-
-
-
-
-
-
-
- + + + + +
暂无生产数据
+
加载中…
+
+
+
+
+ + {{ group.label }} +
+
+
{{ item.label }}
+
+
+
+
+
← 点击左侧参数查看曲线
+
+
+
+ + + + +
暂无厚度数据
+
+
+
+
+
+
+ + + + +
暂无板形数据
+
+
+
+ + + + +
暂无板形数据
+
+
+
+
+
+
+ +
@@ -766,11 +815,123 @@ import { listTransferOrderItem } from '@/api/wms/transferOrderItem' import { listCoilQualityRejudge } from '@/api/wms/coilQualityRejudge' // 引入 ECharts 和 L2 时序数据 API import * as echarts from 'echarts' -import { getTimingSegByEncoilId, getTimingPlanDetailByHotcoilId } from '@/api/l2/timing' +import 'echarts-gl' +import { getTimingSegByEncoilId, getTimingPlanDetailByHotcoilId, getTimingRealtimeData } from '@/api/l2/timing' import AbnormalTable from '@/views/wms/coil/components/AbnormalTable.vue'; import FileList from "@/components/FileList"; +const TREND_GROUPS = [ + { + label: '张力', + children: [ + { label: '开卷张力', col: 'PORTENS' }, + { label: '入口活套张力', col: 'ENLTENS' }, + { label: '拉矫张力', col: 'TLTENS' }, + { label: '酸洗张力', col: 'PLTENS' }, + { label: '出口活套张力', col: 'CXLTENS' }, + { label: '圆盘剪张力', col: 'TRIMTENS' } + ] + }, + { + label: '速度', + children: [ + { label: '开卷速度', col: 'PORSPEED' }, + { label: '酸洗速度', col: 'PLSPEED' }, + { label: '圆盘剪速度', col: 'TRIMSPEED' }, + { label: '轧机入口速度', col: 'MILLENTRYSPEED' }, + { label: '轧机出口速度', col: 'MILLEXITSPEED' } + ] + }, + { + label: '拉矫机', + children: [ + { label: '1#插入量', col: 'TLMESH1' }, + { label: '2#插入量', col: 'TLMESH2' }, + { label: '3#插入量', col: 'TLMESH3' }, + { label: '延伸率', col: 'TLELONG' } + ] + }, + { + label: '酸洗段', + children: [ + { label: '1#温度', col: 'TK1TEMP' }, + { label: '2#温度', col: 'TK2TEMP' }, + { label: '3#温度', col: 'TK3TEMP' }, + { label: '漂洗温度', col: 'RINSETEMP' } + ] + } +] + +const GAUGE_COLS = [ + { col: 'THICK0', title: '入口测厚仪 [mm]' }, + { col: 'THICK1', title: '1架出口厚度 [mm]' }, + { col: 'THICK4', title: '末架出口厚度 [mm]' }, + { col: 'EXIT_SPEED', title: '轧制速度 [m/min]' } +] + +const SHAPE_SCALAR_COLS = [ + { col: 'ABSDEVIATION', title: '总板形偏差 [IU]' }, + { col: 'TILT', title: '末架倾斜量 [mm]' }, + { col: 'WRBEND', title: '工作辊弯辊力 [kN]' }, + { col: 'IRBEND', title: '中间辊弯辊力 [kN]' } +] + +function calcYRange(vals) { + const nums = vals.filter(v => v != null && isFinite(Number(v))).map(Number) + if (!nums.length) return {} + const min = Math.min(...nums) + const max = Math.max(...nums) + if (min === max) { + const base = Math.abs(min) || 1 + return { min: parseFloat((min - base * 0.2).toFixed(4)), max: parseFloat((max + base * 0.2).toFixed(4)) } + } + const pad = (max - min) * 0.15 + return { + min: parseFloat((min - pad).toFixed(4)), + max: parseFloat((max + pad).toFixed(4)) + } +} + +function makeLine(title, xData, yData) { + const range = calcYRange(yData) + return { + title: { text: title, textStyle: { fontSize: 12, fontWeight: 'normal' }, top: 4, left: 8 }, + tooltip: { trigger: 'axis' }, + grid: { top: 36, bottom: 28, left: 8, right: 16, containLabel: true }, + xAxis: { + type: 'category', data: xData, + name: 'pos(m)', nameTextStyle: { fontSize: 10 }, axisLabel: { fontSize: 10 } + }, + yAxis: { + type: 'value', + min: range.min, + max: range.max, + nameTextStyle: { fontSize: 10 }, + axisLabel: { fontSize: 10 } + }, + dataZoom: [ + { type: 'inside', xAxisIndex: 0, zoomOnMouseWheel: true, moveOnMouseMove: true }, + { type: 'inside', yAxisIndex: 0, zoomOnMouseWheel: false, moveOnMouseMove: true } + ], + series: [{ + name: title, type: 'line', smooth: false, symbol: 'none', + lineStyle: { width: 1 }, data: yData + }] + } +} + +function getRowVal(row, col) { + const v = row[col] !== undefined ? row[col] : row[col.toLowerCase()] + return v == null ? null : Number(v) +} + +function xLocData(rows) { + return rows.map(r => { + const v = r.XLOCATION !== undefined ? r.XLOCATION : r.xlocation + return v == null ? '' : Number(v).toFixed(1) + }) +} export default { name: 'CoilInfo', @@ -779,30 +940,45 @@ export default { FileList }, data() { - return { - loading: false, - coilInfoLoading: false, - traceLoading: false, - traceResult: null, - coilDetails: {}, - loadingCoilDetails: false, - coilInfo: {}, - coilList: [], - wipList: [], - annealList: [], - standardSteps: [], - coilId: '', - warehouseTranferList: [], - abmornalList: [], - transferOrderItemList: [], // 批量调拨记录 - coilQualityRejudgeList: [], // 技术部改判记录 - tranferList: [], // 合并后的调拨记录 - // 生产数据相关 - perfLoading: false, - perfSeries: null, - perfSegCount: 0 - } - }, + return { + loading: false, + coilInfoLoading: false, + traceLoading: false, + traceResult: null, + coilDetails: {}, + loadingCoilDetails: false, + coilInfo: {}, + coilList: [], + wipList: [], + annealList: [], + standardSteps: [], + coilId: '', + warehouseTranferList: [], + abmornalList: [], + transferOrderItemList: [], // 批量调拨记录 + coilQualityRejudgeList: [], // 技术部改判记录 + tranferList: [], // 合并后的调拨记录 + // 生产数据相关 + perfLoading: false, + perfSeries: null, + perfSegCount: 0, + // 新增图表数据状态 + segLoading: false, + realtimeLoading: false, + segData: null, + gaugeRows: null, + shapeRows: null, + perfActiveTab: 'trend', + // 趋势参数树状态 + trendGroups: TREND_GROUPS, + expandedGroups: { '张力': true, '速度': true, '拉矫机': true, '酸洗段': true }, + selectedTrendParam: null, + trendChartInst: null, + _trendResizeFn: null, + chartInstances: [], + resizeHandler: null + } + }, computed: { salesInfo() { return this.coilInfo.orderList?.[0] || {}; @@ -1153,27 +1329,6 @@ export default { } return step.newCoilInfoList; }, - // ECharts 辅助函数 - makeLine(name, data) { - return { name, type: 'line', smooth: true, symbol: 'none', data }; - }, - baseOption(title, xData, series, yName) { - return { - title: { text: title, textStyle: { fontSize: 12, fontWeight: 'normal' }, top: 4, left: 8 }, - tooltip: { trigger: 'axis' }, - legend: { top: 4, right: 8, textStyle: { fontSize: 11 } }, - grid: { top: 36, bottom: 28, left: 8, right: 8, containLabel: true }, - xAxis: { - type: 'category', - data: xData, - name: 'pos(m)', - nameTextStyle: { fontSize: 10 }, - axisLabel: { fontSize: 10 } - }, - yAxis: { type: 'value', name: yName, nameTextStyle: { fontSize: 10 }, axisLabel: { fontSize: 10 } }, - series - }; - }, // 加载生产数据 async loadProductionData() { const hotCoilId = this.coilInfo.enterCoilNo; @@ -1181,166 +1336,302 @@ export default { this.perfLoading = true; try { - // 先查询详情 const detail = await getTimingPlanDetailByHotcoilId(hotCoilId); const encoilId = detail?.data?.firstRow?.coilid || ''; - if (!encoilId) return; + // V_VBDA_GAUGE / V_VBDA_SHAPE 使用 EXCOILID 作为 MATID,对应入场卷号 + const excoilId = this.coilInfo.enterCoilNo; - const res = await getTimingSegByEncoilId(encoilId); - const series = res?.data?.series || null; - const rows = res?.data?.rows || []; + const [segRes, realtimeRes] = await Promise.all([ + encoilId ? getTimingSegByEncoilId(encoilId) : Promise.resolve({ data: null }), + encoilId ? getTimingRealtimeData(encoilId) : Promise.resolve({ data: null }) + ]); + + const series = segRes?.data?.series || null; + const rows = segRes?.data?.rows || []; this.perfSegCount = rows.length; this.perfSeries = series; + this.segData = series; + + const g = realtimeRes?.data?.gauge?.result; + const s = realtimeRes?.data?.shape?.result; + this.gaugeRows = Array.isArray(g) ? g : null; + this.shapeRows = Array.isArray(s) ? s : null; if (series && rows.length) { await this.$nextTick(); - this.renderCharts(series); + this.selectTrendParam(TREND_GROUPS[0].children[0]); + // 数据加载完成后,根据当前选中的标签页渲染对应图表 + this.renderCurrentTab(); + } else if (this.gaugeRows?.length || this.shapeRows?.length) { + await this.$nextTick(); + this.renderCurrentTab(); } } catch (error) { console.error('获取生产数据异常:', error); this.perfSeries = null; this.perfSegCount = 0; + this.segData = null; + this.gaugeRows = null; + this.shapeRows = null; } finally { this.perfLoading = false; } }, // 销毁图表 disposeCharts() { + this.disposeSideCharts(); + if (this._trendResizeFn) { + window.removeEventListener('resize', this._trendResizeFn); + this._trendResizeFn = null; + } + if (this.trendChartInst && !this.trendChartInst.isDisposed()) { + this.trendChartInst.dispose(); + this.trendChartInst = null; + } + }, + disposeSideCharts() { if (this.resizeHandler) { window.removeEventListener('resize', this.resizeHandler); this.resizeHandler = null; } - if (this.chartInstances && this.chartInstances.length) { - this.chartInstances.forEach(c => { if (c) c.dispose(); }); - this.chartInstances = []; - } + this.chartInstances.forEach(c => { if (c && !c.isDisposed()) c.dispose(); }); + this.chartInstances = []; }, - // 渲染图表 - renderCharts(series) { - this.disposeCharts(); - if (!this.$refs.chartSpeed || !this.$refs.chartMillSpeed || !this.$refs.chartTension) { - return; - } + // 趋势参数树操作 + toggleTrendGroup(label) { + this.$set(this.expandedGroups, label, !this.expandedGroups[label]); + }, + selectTrendParam(item) { + this.selectedTrendParam = item; + this.$nextTick(() => this.renderTrendSingleChart()); + }, + renderTrendSingleChart() { + if (!this.selectedTrendParam || !this.segData) return; + const el = this.$refs.trendSingleChart; + if (!el) return; - const pick = key => (series[key] || []).map(v => v == null ? null : Number(v).toFixed(2)); - const xData = (series.startpos || []).map(v => v == null ? '' : Number(v).toFixed(1)); - - const c1 = echarts.init(this.$refs.chartSpeed); - c1.setOption(this.baseOption( - '速度趋势 (m/min)', xData, - [ - this.makeLine('轧制速度 plspeed', pick('plspeed')), - this.makeLine('剪切速度 trimspeed', pick('trimspeed')) - ], - 'm/min' - )); - - const c2 = echarts.init(this.$refs.chartMillSpeed); - c2.setOption(this.baseOption( - '轧机速度 (m/min)', xData, - [ - this.makeLine('入口速度 millentryspeed', pick('millentryspeed')), - this.makeLine('出口速度 millexitspeed', pick('millexitspeed')) - ], - 'm/min' - )); - - const c3 = echarts.init(this.$refs.chartTension); - c3.setOption(this.baseOption( - '张力趋势 (N)', xData, - [ - this.makeLine('出口张力 pltens', pick('pltens')), - this.makeLine('入口张力 enltens', pick('enltens')), - this.makeLine('cxltens', pick('cxltens')) - ], - 'N' - )); - - let c4, c5, c6, c7, c8, c9; - if (this.$refs.chartPorSpeed) { - c4 = echarts.init(this.$refs.chartPorSpeed); - c4.setOption(this.baseOption( - '开卷机速度 (m/min)', xData, - [ - this.makeLine('开卷速度 porspeed', pick('porspeed')), - this.makeLine('最大 porspeedmax', pick('porspeedmax')), - this.makeLine('最小 porspeedmin', pick('porspeedmin')) - ], - 'm/min' - )); + if (!this.trendChartInst || this.trendChartInst.isDisposed()) { + this.trendChartInst = echarts.init(el); + const resizeFn = () => this.trendChartInst && !this.trendChartInst.isDisposed() && this.trendChartInst.resize(); + window.addEventListener('resize', resizeFn); + this._trendResizeFn = resizeFn; } - - if (this.$refs.chartPorTens) { - c5 = echarts.init(this.$refs.chartPorTens); - c5.setOption(this.baseOption( - '开卷机张力 (N)', xData, - [ - this.makeLine('开卷张力 portens', pick('portens')), - this.makeLine('最大 portensmax', pick('portensmax')), - this.makeLine('最小 portensmin', pick('portensmin')) - ], - 'N' - )); - } - - if (this.$refs.chartTrim) { - c6 = echarts.init(this.$refs.chartTrim); - c6.setOption(this.baseOption( - '剪切参数', xData, - [ - this.makeLine('剪切张力 trimtens', pick('trimtens')), - this.makeLine('剪切宽度 trimwidth', pick('trimwidth')), - this.makeLine('trtens', pick('trtens')) - ], - '' - )); - } - - if (this.$refs.chartTemp) { - c7 = echarts.init(this.$refs.chartTemp); - c7.setOption(this.baseOption( - '温度趋势 (℃)', xData, - [ - this.makeLine('1号测温 tk1temp', pick('tk1temp')), - this.makeLine('2号测温 tk2temp', pick('tk2temp')), - this.makeLine('3号测温 tk3temp', pick('tk3temp')), - this.makeLine('4号测温 tk4temp', pick('tk4temp')), - this.makeLine('漂洗温度 rinsetemp', pick('rinsetemp')) - ], - '℃' - )); - } - - if (this.$refs.chartMesh) { - c8 = echarts.init(this.$refs.chartMesh); - c8.setOption(this.baseOption( - '网纹辊参数', xData, - [ - this.makeLine('网纹辊1 tlmesh1', pick('tlmesh1')), - this.makeLine('网纹辊2 tlmesh2', pick('tlmesh2')), - this.makeLine('网纹辊3 tlmesh3', pick('tlmesh3')) - ], - '' - )); - } - - if (this.$refs.chartElong) { - c9 = echarts.init(this.$refs.chartElong); - c9.setOption(this.baseOption( - '延伸率', xData, - [ - this.makeLine('延伸率 tlelong', pick('tlelong')), - this.makeLine('总张力 tltens', pick('tltens')), - this.makeLine('teltens', pick('teltens')) - ], - '' - )); - } - - this.chartInstances = [c1, c2, c3, c4, c5, c6, c7, c8, c9].filter(c => c); - this.resizeHandler = () => this.chartInstances.forEach(c => { if (c) c.resize(); }); + const x = this.segX(); + const yData = this.seg(this.selectedTrendParam.col); + this.trendChartInst.setOption(makeLine(this.selectedTrendParam.label, x, yData), true); + }, + // 标签页切换 + handlePerfTabSwitch() { + this.$nextTick(() => { + if (this.perfActiveTab === 'trend') { + if (this.selectedTrendParam && this.segData) { + this.renderTrendSingleChart(); + } + } else { + this.renderCurrentTab(); + } + }); + }, + renderCurrentTab() { + this.disposeSideCharts(); + if (this.perfActiveTab === 'thickness' && this.gaugeRows?.length) this.renderGaugeCharts(); + if (this.perfActiveTab === 'flatness3d' && this.shapeRows?.length) this.renderFlatness3d(); + if (this.perfActiveTab === 'flatness' && this.shapeRows?.length) this.renderFlatnessCharts(); + }, + // SEG 数据辅助 + seg(col) { + const s = this.segData; + const arr = s[col] !== undefined ? s[col] : (s[col.toLowerCase()] || []); + return arr.map(v => v == null ? null : Number(Number(v).toFixed(3))); + }, + segX() { + const s = this.segData; + const arr = s['STARTPOS'] !== undefined ? s['STARTPOS'] : (s['startpos'] || []); + return arr.map(v => v == null ? '' : Number(v).toFixed(1)); + }, + // 图表初始化 + makeChart(ref, option) { + const el = this.$refs[ref]; + if (!el) return null; + const chart = echarts.init(el); + chart.setOption(option); + return chart; + }, + setupResize() { + this.resizeHandler = () => this.chartInstances.forEach(c => { + if (c && !c.isDisposed()) c.resize(); + }); window.addEventListener('resize', this.resizeHandler); }, + // 厚度曲线 + renderGaugeCharts() { + const rows = this.gaugeRows; + if (!rows || !rows.length) return; + const xData = xLocData(rows); + const refs = ['chartGauge1', 'chartGauge2', 'chartGauge3', 'chartGauge4']; + const charts = refs.map((ref, i) => { + const { col, title } = GAUGE_COLS[i]; + const yData = rows.map(r => { + const v = getRowVal(r, col); + return v == null ? null : parseFloat(v.toFixed(4)); + }); + return this.makeChart(ref, makeLine(title, xData, yData)); + }); + this.chartInstances = charts.filter(Boolean); + this.setupResize(); + }, + // 带钢板形 3D 热力图 + renderFlatness3d() { + const rows = this.shapeRows; + if (!rows || !rows.length) return; + const firstRow = rows[0]; + const high = parseInt(getRowVal(firstRow, 'HIGHZONEID')) || 26; + const low = parseInt(getRowVal(firstRow, 'LOWZONEID')) || 1; + const numZones = Math.min(Math.max(high - low + 1, 1), 26); + const zoneCols = Array.from({ length: numZones }, (_, i) => + `VALUES${String(low + i).padStart(2, '0')}` + ); + const step = Math.max(1, Math.floor(rows.length / 200)); + const sampled = rows.filter((_, i) => i % step === 0); + const numX = sampled.length; + + const xLabels = sampled.map(r => { + const v = r.XLOCATION !== undefined ? r.XLOCATION : r.xlocation; + return v == null ? '' : Number(v).toFixed(0); + }); + + let minV = Infinity, maxV = -Infinity; + sampled.forEach(row => { + zoneCols.forEach(col => { + const v = getRowVal(row, col); + if (v != null) { + if (v < minV) minV = v; + if (v > maxV) maxV = v; + } + }); + }); + if (!isFinite(minV)) { minV = -30; maxV = 30; } + const absMax = Math.max(Math.abs(minV), Math.abs(maxV)); + + const channelLines = zoneCols.map((col, yi) => ({ + type: 'line3D', + coordinateSystem: 'cartesian3D', + data: sampled.map((row, xi) => { + const v = getRowVal(row, col); + return v == null ? null : [xi, yi, parseFloat(v.toFixed(2))]; + }).filter(Boolean), + lineStyle: { width: 2, opacity: 1 } + })); + + const xStride = Math.max(1, Math.floor(numX / 60)); + const crossLines = []; + for (let xi = 0; xi < numX; xi += xStride) { + const pts = zoneCols.map((col, yi) => { + const v = getRowVal(sampled[xi], col); + return v == null ? null : [xi, yi, parseFloat(v.toFixed(2))]; + }).filter(Boolean); + if (pts.length > 1) { + crossLines.push({ + type: 'line3D', + coordinateSystem: 'cartesian3D', + data: pts, + lineStyle: { width: 1.5, opacity: 1 } + }); + } + } + + const series = [...channelLines, ...crossLines]; + + const option = { + title: { text: '实测平直度 [IU]', textStyle: { fontSize: 13, fontWeight: 'normal' }, top: 6, left: 10 }, + tooltip: {}, + visualMap: { + show: true, + dimension: 2, + min: -absMax, + max: absMax, + calculable: true, + orient: 'vertical', + right: 10, + top: 'center', + textStyle: { fontSize: 10 }, + inRange: { + color: ['#8B0000','#CC2200','#E84C00','#F46D43', + '#FDAE61','#FEE08B', + '#66BD63','#1A9850','#006837', + '#3288BD','#5E4FA2','#762A83'] + } + }, + grid3D: { + boxWidth: 200, + boxHeight: 60, + boxDepth: 80, + viewControl: { + projection: 'orthographic', + autoRotate: false, + rotateSensitivity: 1, + zoomSensitivity: 1 + }, + light: { + main: { intensity: 1.2, shadow: false }, + ambient: { intensity: 0.3 } + } + }, + xAxis3D: { + type: 'value', + name: '位置', + min: 0, + max: numX - 1, + nameTextStyle: { fontSize: 10 }, + axisLabel: { + fontSize: 9, + formatter: v => xLabels[Math.round(v)] || '' + } + }, + yAxis3D: { + type: 'value', + name: '通道', + min: 0, + max: numZones - 1, + nameTextStyle: { fontSize: 10 }, + axisLabel: { + fontSize: 9, + formatter: v => String(low + Math.round(v)) + } + }, + zAxis3D: { + type: 'value', + name: 'IU', + nameTextStyle: { fontSize: 10 }, + axisLabel: { fontSize: 9 } + }, + series + }; + + const el = this.$refs.chartFlatness3d; + if (!el) return; + const chart = echarts.init(el); + chart.setOption(option); + this.chartInstances = [chart]; + this.setupResize(); + }, + // 板形曲线 + renderFlatnessCharts() { + const rows = this.shapeRows; + if (!rows || !rows.length) return; + const xData = xLocData(rows); + const refs = ['chartFlatDev', 'chartTilt', 'chartWrBend', 'chartIrBend']; + const charts = refs.map((ref, i) => { + const { col, title } = SHAPE_SCALAR_COLS[i]; + const yData = rows.map(r => { + const v = getRowVal(r, col); + return v == null ? null : parseFloat(v.toFixed(3)); + }); + return this.makeChart(ref, makeLine(title, xData, yData)); + }); + this.chartInstances = charts.filter(Boolean); + this.setupResize(); + }, }, } @@ -2215,14 +2506,153 @@ export default { margin-left: 4px; } -.charts-wrap { +.production-section { + min-height: 600px; + flex: 1; +} + +.production-section .section-body { + padding: 0; + height: calc(100% - 48px); + min-height: 550px; +} + +.perf-tabs { + height: 100%; + min-height: 550px; display: flex; flex-direction: column; + + ::v-deep .el-tabs__header { + flex-shrink: 0; + } + ::v-deep .el-tabs__content { + flex: 1; + overflow: hidden; + min-height: 0; + padding: 0; + } + ::v-deep .el-tab-pane { + height: 100%; + min-height: 500px; + } +} + +.trend-layout { + display: flex; + height: 100%; + gap: 0; +} + +.trend-tree { + width: 140px; + flex-shrink: 0; + overflow-x: hidden; + overflow-y: auto; + border-right: 1px solid #ebeef5; + padding: 4px 0; + max-height: calc(100vh - 400px); + + &::-webkit-scrollbar { + width: 4px; + height: 4px; + } + &::-webkit-scrollbar-thumb { + background: #dcdfe6; + border-radius: 2px; + } + &::-webkit-scrollbar-track { + background: transparent; + } +} + +.tree-group { + user-select: none; +} + +.tree-group-label { + display: flex; + align-items: center; + gap: 4px; + padding: 5px 8px; + font-size: 12px; + font-weight: 600; + color: #303133; + cursor: pointer; + + &:hover { background: #f5f7fa; } + + i { font-size: 10px; color: #909399; } +} + +.tree-children { padding-left: 4px; } + +.tree-item { + padding: 4px 8px 4px 18px; + font-size: 12px; + color: #606266; + cursor: pointer; + border-radius: 3px; + margin: 1px 4px; + + &:hover { background: #ecf5ff; color: #409eff; } + + &.active { + background: #ecf5ff; + color: #409eff; + font-weight: 500; + } +} + +.trend-chart-area { + flex: 1; + min-width: 0; + height: 500px; + padding: 4px 4px 4px 8px; + display: flex; + align-items: stretch; +} + +.charts-scroll { + height: 100%; + overflow-y: auto; + padding: 8px; + box-sizing: border-box; + + &::-webkit-scrollbar { width: 4px; } + &::-webkit-scrollbar-thumb { background: #dcdfe6; border-radius: 2px; } +} + +.charts-grid { + display: grid; + grid-template-columns: 1fr 1fr; gap: 12px; + align-content: start; + min-height: 100%; + padding: 4px; + + .chart-box { margin-bottom: 0; } } .chart-box { width: 100%; - height: 200px; + height: 260px; + margin-bottom: 12px; + min-height: 200px; +} + +.chart-box-tall { + height: 480px; + min-height: 400px; +} + +.no-data-hint { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 120px; + color: #c0c4cc; + font-size: 13px; }