diff --git a/klp-ui/src/views/cost/comprehensive.vue b/klp-ui/src/views/cost/comprehensive.vue
index 53073a236..1c08f505e 100644
--- a/klp-ui/src/views/cost/comprehensive.vue
+++ b/klp-ui/src/views/cost/comprehensive.vue
@@ -23,6 +23,7 @@
列配置
价格管理
异常分析
+ 折线图统计
{{ inputMode ? '录入' : '查看' }}
@@ -334,6 +335,16 @@
+
+
+
+
+
+
@@ -352,6 +363,7 @@ import { listProductionLine } from "@/api/wms/productionLine"
import { listRollGrindAll } from "@/api/mes/roll/rollGrind"
import { listEnergyRecord, addEnergyRecord, updateEnergyRecord } from "@/api/ems/energyRecord"
import AnomalyAnalysis from "./components/AnomalyAnalysis"
+import * as echarts from 'echarts'
function parseDateRange(detailDate) {
const d = (detailDate || '').slice(0, 10)
@@ -485,7 +497,8 @@ export default {
priceOpen: false, priceList: [], priceSaving: false,
backfilling: false,
progressOpen: false, progressTitle: '', progressTasks: [],
- anomalyOpen: false, anomalyMap: {}
+ anomalyOpen: false, anomalyMap: {},
+ chartOpen: false, _chartInstances: []
}
},
computed: {
@@ -575,6 +588,17 @@ export default {
},
tableData() {
return [...this.summaryRows, ...this.gridRows]
+ },
+ chartDataCols() {
+ if (!this.gridRows.length) return []
+ return this.displayCols.filter(col => {
+ if (col.$type === 'detail') {
+ const key = 'q' + col.itemId
+ return this.gridRows.some(r => r[key] != null)
+ }
+ const key = 'mv' + col.mIdx
+ return this.gridRows.some(r => r[key] != null)
+ })
}
},
watch: { configOpen(v) { if (!v) this.rpOpen = false } },
@@ -1144,7 +1168,7 @@ export default {
},
/* helpers */
- async loadLines() { const r = await listProductionLine({ pageSize: 999 }); this.lineOptions = r.rows || [] },
+ async loadLines() {const r = await listProductionLine({ pageSize: 999 }); this.lineOptions = r.rows || [] },
lineName(row) {
if (row.lineType) {
const found = this.lineOptions.find(l => l.lineId == row.lineType);
@@ -1173,6 +1197,60 @@ export default {
if (!set) return false
return set.has(String(itemId))
},
+
+ /* charts */
+ buildChartOption(col) {
+ const rows = this.gridRows.filter(r => r.detailDate).sort((a,b) => a.detailDate.localeCompare(b.detailDate))
+ const dates = rows.map(r => r.detailDate.slice(0,10))
+ const makeOption = (title, series) => ({
+ title: { text: title, left: 'center', textStyle: { fontSize: 12 } },
+ tooltip: { trigger: 'axis' },
+ legend: series.length > 1 ? { top: 'bottom', textStyle: { fontSize: 11 } } : undefined,
+ xAxis: { type: 'category', data: dates, axisLabel: { rotate: 30, fontSize: 10 } },
+ yAxis: { type: 'value', axisLabel: { fontSize: 10 } },
+ grid: { left: 50, right: 16, top: 36, bottom: 40 },
+ series
+ })
+ if (col.$type === 'detail') {
+ const label = (col.itemName || col.itemCode) + (col.unit ? '('+col.unit+')' : '')
+ if (col.isShift) {
+ return makeOption(label, [
+ { name: '甲班', type: 'line', data: rows.map(r => { const v = parseFloat(r['q'+col.itemId+'_1']); return isNaN(v) ? 0 : v }), smooth: true },
+ { name: '乙班', type: 'line', data: rows.map(r => { const v = parseFloat(r['q'+col.itemId+'_2']); return isNaN(v) ? 0 : v }), smooth: true }
+ ])
+ }
+ return makeOption(label, [{ type: 'line', data: rows.map(r => { const v = parseFloat(r['q'+col.itemId]); return isNaN(v) ? 0 : v }), smooth: true }])
+ }
+ // metric
+ const label = (col.metricName || '') + (col.unit ? '('+col.unit+')' : '')
+ if (col.isShift) {
+ return makeOption(label, [
+ { name: '甲班', type: 'line', data: rows.map(r => { const v = parseFloat(r['mv'+col.mIdx+'_1']); return isNaN(v) ? 0 : v }), smooth: true },
+ { name: '乙班', type: 'line', data: rows.map(r => { const v = parseFloat(r['mv'+col.mIdx+'_2']); return isNaN(v) ? 0 : v }), smooth: true }
+ ])
+ }
+ return makeOption(label, [{ type: 'line', data: rows.map(r => { const v = parseFloat(r['mv'+col.mIdx]); return isNaN(v) ? 0 : v }), smooth: true }])
+ },
+ renderCharts() {
+ this.disposeCharts()
+ this.$nextTick(() => {
+ this._chartInstances = []
+ this.chartDataCols.forEach((col, idx) => {
+ const el = this.$refs['chart_'+idx]
+ if (!el || !el[0]) return
+ const chart = echarts.init(el[0])
+ chart.setOption(this.buildChartOption(col))
+ this._chartInstances.push(chart)
+ })
+ })
+ },
+ disposeCharts() {
+ if (this._chartInstances) {
+ this._chartInstances.forEach(c => c.dispose())
+ this._chartInstances = []
+ }
+ },
+
async loadItems() { if (!this.allItems.length) { const r = await listItem({ pageNum:1, pageSize:999 }); this.allItems = r.rows || [] } },
async loadAllMetrics(rid) {
const q = { pageNum:1, pageSize:99999 }; if (rid) q.reportId = rid
@@ -1231,4 +1309,9 @@ export default {
/deep/ .summary-row td .cell { color: #303133; }
.summary-label { font-weight: bold; color: #303133; padding: 0 5px; }
.summary-val { font-weight: bold; color: #303133; }
+/deep/ .chart-fullscreen { width: 100% !important; max-width: 100% !important; height: 100vh !important; margin: 0 !important; }
+/deep/ .chart-fullscreen .el-dialog__body { height: calc(100vh - 90px); overflow: auto; padding: 16px 20px; }
+/deep/ .chart-fullscreen .el-dialog__header { padding: 12px 20px; }
+.chart-grid { display: flex; flex-wrap: wrap; }
+.chart-item { width: 33.33%; padding: 6px; box-sizing: border-box; }