feat(cost/comprehensive): 添加汇总行功能,支持表格数据求和与平均计算
1. 新增summaryRows计算属性,生成总和与平均两行汇总数据 2. 新增tableData计算属性,将汇总行合并到原表格数据中 3. 为汇总行添加专属样式,高亮显示并加粗文字 4. 优化表格列渲染逻辑,普通行保留编辑能力,汇总行仅展示静态数据 5. 调整删除按钮逻辑,仅对非汇总行生效并修复删除索引问题 6. 新增rowClassName方法为汇总行添加专属CSS类
This commit is contained in:
@@ -30,9 +30,9 @@
|
||||
</span>
|
||||
</div>
|
||||
<!-- <el-alert :title="'已配置'+allCols.length+'个列'" type="info" :closable="false" show-icon style="margin-bottom:8px" /> -->
|
||||
<el-table v-loading="gridLoading" height="calc(100vh - 260px)" :data="gridRows" border stripe size="mini" style="width:100%" :header-cell-style="headerStyle" :key="'tbl-'+inputMode">
|
||||
<el-table v-loading="gridLoading" height="calc(100vh - 260px)" :data="tableData" border stripe size="mini" style="width:100%" :header-cell-style="headerStyle" :row-class-name="rowClassName" :key="'tbl-'+inputMode">
|
||||
<el-table-column label="日期" width="135" fixed>
|
||||
<template slot-scope="s"><el-date-picker v-model="s.row.detailDate" type="date" value-format="yyyy-MM-dd" size="mini" style="width:124px" @change="sortGrid" /></template>
|
||||
<template slot-scope="s"><span v-if="s.row.$isSummary" class="summary-label">{{ s.row.detailDate }}</span><el-date-picker v-else v-model="s.row.detailDate" type="date" value-format="yyyy-MM-dd" size="mini" style="width:124px" @change="sortGrid" /></template>
|
||||
</el-table-column>
|
||||
<template v-for="col in displayCols">
|
||||
<el-table-column v-if="col.$type==='detail' && !col.isShift" :key="'d'+col.itemId" align="center" width="130">
|
||||
@@ -40,7 +40,8 @@
|
||||
<div class="col-hd">{{ col.itemName }}{{ col.unit ? '('+col.unit+')' : '' }}</div>
|
||||
</template>
|
||||
<template slot-scope="s">
|
||||
<el-input v-model="s.row['q'+col.itemId]" size="mini" @input="recalcAll" :class="{'anomaly-input': isAnomalyCell(s.row.detailDate, col.itemId)}">
|
||||
<span v-if="s.row.$isSummary" class="summary-val">{{ s.row['q'+col.itemId] || '-' }}</span>
|
||||
<el-input v-else v-model="s.row['q'+col.itemId]" size="mini" @input="recalcAll" :class="{'anomaly-input': isAnomalyCell(s.row.detailDate, col.itemId)}">
|
||||
<!-- <span slot="suffix" v-if="col.queryCondition" class="input-suffix-actions">
|
||||
<i title="反填" v-if="col.category==='辅料'||col.category==='能耗'" :class="backfillLoading[col.itemId]?'el-icon-loading':'el-icon-upload2'" class="ica ica-backfill" @click.stop="backfillCell(col, s.row)" />
|
||||
<i title="自动获取" :class="autoLoading[col.itemId]?'el-icon-loading':'el-icon-refresh'" class="ica ica-fetch" @click.stop="fetchAutoData(col, s.row)" />
|
||||
@@ -53,27 +54,39 @@
|
||||
<div class="col-hd">{{ col.itemName }}{{ col.unit ? '('+col.unit+')' : '' }}</div>
|
||||
</template>
|
||||
<template slot-scope="s">
|
||||
<div class="shift-cell">
|
||||
<span class="shift-tag">甲</span>
|
||||
<el-input v-model="s.row['q'+col.itemId+'_1']" size="mini" class="shift-input" @input="recalcAll" :class="{'anomaly-input': isAnomalyCell(s.row.detailDate, col.itemId)}">
|
||||
<!-- <span slot="suffix" v-if="col.queryCondition" class="input-suffix-actions"><i v-if="col.category==='辅料'||col.category==='能耗'" :class="backfillLoading[col.itemId]?'el-icon-loading':'el-icon-upload2'" class="ica ica-backfill" @click.stop="backfillCell(col, s.row, '1')" /><i :class="autoLoading[col.itemId]?'el-icon-loading':'el-icon-refresh'" class="ica ica-fetch" @click.stop="fetchAutoData(col, s.row, '1')" /></span> -->
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="shift-cell">
|
||||
<span class="shift-tag">乙</span>
|
||||
<el-input v-model="s.row['q'+col.itemId+'_2']" size="mini" class="shift-input" @input="recalcAll" :class="{'anomaly-input': isAnomalyCell(s.row.detailDate, col.itemId)}">
|
||||
<!-- <span slot="suffix" v-if="col.queryCondition" class="input-suffix-actions"><i v-if="col.category==='辅料'||col.category==='能耗'" :class="backfillLoading[col.itemId]?'el-icon-loading':'el-icon-upload2'" class="ica ica-backfill" @click.stop="backfillCell(col, s.row, '2')" /><i :class="autoLoading[col.itemId]?'el-icon-loading':'el-icon-refresh'" class="ica ica-fetch" @click.stop="fetchAutoData(col, s.row, '2')" /></span> -->
|
||||
</el-input>
|
||||
</div>
|
||||
<template v-if="s.row.$isSummary">
|
||||
<div class="shift-cell"><span class="shift-tag">甲</span><span class="summary-val">{{ s.row['q'+col.itemId+'_1'] || '-' }}</span></div>
|
||||
<div class="shift-cell"><span class="shift-tag">乙</span><span class="summary-val">{{ s.row['q'+col.itemId+'_2'] || '-' }}</span></div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="shift-cell">
|
||||
<span class="shift-tag">甲</span>
|
||||
<el-input v-model="s.row['q'+col.itemId+'_1']" size="mini" class="shift-input" @input="recalcAll" :class="{'anomaly-input': isAnomalyCell(s.row.detailDate, col.itemId)}">
|
||||
<!-- <span slot="suffix" v-if="col.queryCondition" class="input-suffix-actions"><i v-if="col.category==='辅料'||col.category==='能耗'" :class="backfillLoading[col.itemId]?'el-icon-loading':'el-icon-upload2'" class="ica ica-backfill" @click.stop="backfillCell(col, s.row, '1')" /><i :class="autoLoading[col.itemId]?'el-icon-loading':'el-icon-refresh'" class="ica ica-fetch" @click.stop="fetchAutoData(col, s.row, '1')" /></span> -->
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="shift-cell">
|
||||
<span class="shift-tag">乙</span>
|
||||
<el-input v-model="s.row['q'+col.itemId+'_2']" size="mini" class="shift-input" @input="recalcAll" :class="{'anomaly-input': isAnomalyCell(s.row.detailDate, col.itemId)}">
|
||||
<!-- <span slot="suffix" v-if="col.queryCondition" class="input-suffix-actions"><i v-if="col.category==='辅料'||col.category==='能耗'" :class="backfillLoading[col.itemId]?'el-icon-loading':'el-icon-upload2'" class="ica ica-backfill" @click.stop="backfillCell(col, s.row, '2')" /><i :class="autoLoading[col.itemId]?'el-icon-loading':'el-icon-refresh'" class="ica ica-fetch" @click.stop="fetchAutoData(col, s.row, '2')" /></span> -->
|
||||
</el-input>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-else-if="col.$type==='metric' && !col.isShift" :key="'m'+col.mIdx" :label="col.metricName+(col.unit?'('+col.unit+')':'')" width="85" align="center">
|
||||
<template slot-scope="s">{{ s.row['mv'+col.mIdx]!=null ? s.row['mv'+col.mIdx] : '-' }}</template>
|
||||
<template slot-scope="s"><span v-if="s.row.$isSummary" class="summary-val">{{ s.row['mv'+col.mIdx]!=null ? s.row['mv'+col.mIdx] : '-' }}</span><template v-else>{{ s.row['mv'+col.mIdx]!=null ? s.row['mv'+col.mIdx] : '-' }}</template></template>
|
||||
</el-table-column>
|
||||
<el-table-column v-else-if="col.$type==='metric' && col.isShift" :key="'ms'+col.mIdx" :label="col.metricName+(col.unit?'('+col.unit+')':'')" width="95" align="center">
|
||||
<template slot-scope="s">
|
||||
<div class="shift-metric">甲 {{ s.row['mv'+col.mIdx+'_1']!=null ? s.row['mv'+col.mIdx+'_1'] : '-' }}</div>
|
||||
<div class="shift-metric">乙 {{ s.row['mv'+col.mIdx+'_2']!=null ? s.row['mv'+col.mIdx+'_2'] : '-' }}</div>
|
||||
<template v-if="s.row.$isSummary">
|
||||
<div class="shift-metric"><span class="summary-val">甲 {{ s.row['mv'+col.mIdx+'_1']!=null ? s.row['mv'+col.mIdx+'_1'] : '-' }}</span></div>
|
||||
<div class="shift-metric"><span class="summary-val">乙 {{ s.row['mv'+col.mIdx+'_2']!=null ? s.row['mv'+col.mIdx+'_2'] : '-' }}</span></div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="shift-metric">甲 {{ s.row['mv'+col.mIdx+'_1']!=null ? s.row['mv'+col.mIdx+'_1'] : '-' }}</div>
|
||||
<div class="shift-metric">乙 {{ s.row['mv'+col.mIdx+'_2']!=null ? s.row['mv'+col.mIdx+'_2'] : '-' }}</div>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
@@ -82,7 +95,7 @@
|
||||
<!-- <el-button size="mini" type="text" style="padding:0 2px;font-size:11px" @click="saveRow(s.row)">保存</el-button>
|
||||
<el-button size="mini" type="text" style="padding:0 2px;font-size:11px" @click="fetchRow(s.row)">抓取</el-button>
|
||||
<el-button size="mini" type="text" style="padding:0 2px;font-size:11px;color:#67c23a" @click="backfillRow(s.row)">反填</el-button> -->
|
||||
<el-button size="mini" type="text" style="padding:0 2px;font-size:11px;color:#f56c6c" @click="gridRows.splice(s.$index,1)">删除</el-button>
|
||||
<el-button v-if="!s.row.$isSummary" size="mini" type="text" style="padding:0 2px;font-size:11px;color:#f56c6c" @click="gridRows.splice(s.$index - summaryRows.length,1)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -494,6 +507,45 @@ export default {
|
||||
})
|
||||
return col && col.color ? { background: col.color, color: '#fff' } : {}
|
||||
}
|
||||
},
|
||||
summaryRows() {
|
||||
const rows = this.gridRows.filter(r => r.detailDate)
|
||||
if (!rows.length) return []
|
||||
const detailCols = this.allCols.filter(c => c.$type === 'detail')
|
||||
const metricCols = this.allCols.filter(c => c.$type === 'metric')
|
||||
const calc = (type) => {
|
||||
const row = { detailDate: type === 'sum' ? '总和' : '平均', $isSummary: true, $summaryType: type }
|
||||
const n = rows.length || 1
|
||||
detailCols.forEach(col => {
|
||||
if (col.isShift) {
|
||||
const s1 = rows.reduce((a, r) => a + (parseFloat(r['q' + col.itemId + '_1']) || 0), 0)
|
||||
const s2 = rows.reduce((a, r) => a + (parseFloat(r['q' + col.itemId + '_2']) || 0), 0)
|
||||
row['q' + col.itemId + '_1'] = type === 'sum' ? s1.toFixed(2) : (s1 / n).toFixed(2)
|
||||
row['q' + col.itemId + '_2'] = type === 'sum' ? s2.toFixed(2) : (s2 / n).toFixed(2)
|
||||
row['q' + col.itemId] = type === 'sum' ? (s1 + s2).toFixed(2) : ((s1 + s2) / n).toFixed(2)
|
||||
} else {
|
||||
const s = rows.reduce((a, r) => a + (parseFloat(r['q' + col.itemId]) || 0), 0)
|
||||
row['q' + col.itemId] = type === 'sum' ? s.toFixed(2) : (s / n).toFixed(2)
|
||||
}
|
||||
})
|
||||
metricCols.forEach(col => {
|
||||
if (col.isShift) {
|
||||
const s1 = rows.reduce((a, r) => a + (parseFloat(r['mv' + col.mIdx + '_1']) || 0), 0)
|
||||
const s2 = rows.reduce((a, r) => a + (parseFloat(r['mv' + col.mIdx + '_2']) || 0), 0)
|
||||
row['mv' + col.mIdx + '_1'] = type === 'sum' ? s1.toFixed(2) : (s1 / n).toFixed(2)
|
||||
row['mv' + col.mIdx + '_2'] = type === 'sum' ? s2.toFixed(2) : (s2 / n).toFixed(2)
|
||||
row['mv' + col.mIdx] = type === 'sum' ? (s1 + s2).toFixed(2) : ((s1 + s2) / n).toFixed(2)
|
||||
} else {
|
||||
const s = rows.reduce((a, r) => a + (parseFloat(r['mv' + col.mIdx]) || 0), 0)
|
||||
row['mv' + col.mIdx] = type === 'sum' ? s.toFixed(2) : (s / n).toFixed(2)
|
||||
}
|
||||
})
|
||||
return row
|
||||
}
|
||||
return [calc('sum'), calc('avg')]
|
||||
},
|
||||
tableData() {
|
||||
return [...this.summaryRows, ...this.gridRows]
|
||||
}
|
||||
},
|
||||
watch: { configOpen(v) { if (!v) this.rpOpen = false } },
|
||||
@@ -509,6 +561,10 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
rowClassName({ row }) {
|
||||
if (row.$isSummary) return 'summary-row'
|
||||
return ''
|
||||
},
|
||||
/* report */
|
||||
getList() {
|
||||
this.loading = true
|
||||
@@ -1142,4 +1198,8 @@ export default {
|
||||
.ica-backfill { color: #67c23a; margin-right: 1px; }
|
||||
.ica:hover { opacity: 0.7; }
|
||||
/deep/ .anomaly-input .el-input__inner { background: #fef0f0 !important; border-color: #f56c6c !important; }
|
||||
/deep/ .summary-row td { background: #f0f7ff !important; font-weight: bold; }
|
||||
/deep/ .summary-row td .cell { color: #303133; }
|
||||
.summary-label { font-weight: bold; color: #303133; padding: 0 5px; }
|
||||
.summary-val { font-weight: bold; color: #303133; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user