feat(cost): 新增成本综合页的异常分析功能
1. 新增异常分析弹窗组件AnomalyAnalysis,支持基于3σ法则检测异常数据 2. 在综合成本页面右上角添加异常分析入口按钮 3. 为异常单元格添加红色高亮样式,标记异常数据 4. 实现异常数据的接收、存储和表格标记逻辑
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
<el-button type="primary" size="mini" style="float:right;margin-left:8px" @click="saveGrid" :loading="saving">保存</el-button>
|
||||
<el-button size="mini" style="float:right;margin-left:8px" @click="openColCfg">列配置</el-button>
|
||||
<el-button size="mini" style="float:right;margin-left:8px" icon="el-icon-money" @click="openPriceMgr">价格管理</el-button>
|
||||
<el-button size="mini" style="float:right;margin-left:8px" icon="el-icon-warning-outline" @click="anomalyOpen = true">异常分析</el-button>
|
||||
<!-- <el-button size="mini" style="float:right;margin-left:8px" icon="el-icon-upload2" @click="backfillCost" :loading="backfilling">反填</el-button> -->
|
||||
<span style="float:right;margin-right:12px;font-size:12px;color:#606266;display:flex;align-items:center">
|
||||
<span style="margin-right:4px">{{ inputMode ? '录入' : '查看' }}</span>
|
||||
@@ -39,7 +40,7 @@
|
||||
<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">
|
||||
<el-input 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)" />
|
||||
@@ -54,13 +55,13 @@
|
||||
<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">
|
||||
<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">
|
||||
<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>
|
||||
@@ -317,6 +318,9 @@
|
||||
</el-table>
|
||||
<div slot="footer"><el-button @click="progressOpen=false">关 闭</el-button></div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- Anomaly analysis -->
|
||||
<AnomalyAnalysis :visible.sync="anomalyOpen" :reportId="activeReport ? activeReport.reportId : null" :allItems="allItems" :gridRows="gridRows" :allCols="allCols" @anomaly-found="onAnomalyFound" />
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
@@ -334,6 +338,7 @@ import { listAuxiliaryConsume, addAuxiliaryConsume, updateAuxiliaryConsume } fro
|
||||
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"
|
||||
|
||||
function parseDateRange(detailDate) {
|
||||
const d = (detailDate || '').slice(0, 10)
|
||||
@@ -444,6 +449,7 @@ registerBackfillHandler('能耗', async (queryCondition, row, col, report, shift
|
||||
|
||||
export default {
|
||||
name: "CostComprehensive",
|
||||
components: { AnomalyAnalysis },
|
||||
data() {
|
||||
return {
|
||||
loading: false, list: [], tabs: [], total: 0, sel: 0, selIds: [], showSearch: true,
|
||||
@@ -465,7 +471,8 @@ export default {
|
||||
inputMode: false,
|
||||
priceOpen: false, priceList: [], priceSaving: false,
|
||||
backfilling: false,
|
||||
progressOpen: false, progressTitle: '', progressTasks: []
|
||||
progressOpen: false, progressTitle: '', progressTasks: [],
|
||||
anomalyOpen: false, anomalyMap: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -1060,6 +1067,27 @@ export default {
|
||||
else return row.lineType || '-'
|
||||
}
|
||||
},
|
||||
onAnomalyFound(list) {
|
||||
const map = {}
|
||||
list.forEach(a => {
|
||||
// Normalize date to yyyy-MM-dd string
|
||||
const dt = (a.detailDate || '').slice(0, 10)
|
||||
if (!dt) return
|
||||
if (!map[dt]) map[dt] = new Set()
|
||||
map[dt].add(String(a.itemId))
|
||||
})
|
||||
this.anomalyMap = map
|
||||
this.anomalyOpen = false
|
||||
this.$forceUpdate()
|
||||
this.$modal.msgSuccess(`已将 ${list.length} 条异常数据标记到表格`)
|
||||
},
|
||||
isAnomalyCell(detailDate, itemId) {
|
||||
if (!detailDate) return false
|
||||
const dt = (detailDate || '').slice ? detailDate.slice(0, 10) : String(detailDate).slice(0, 10)
|
||||
const set = this.anomalyMap[dt]
|
||||
if (!set) return false
|
||||
return set.has(String(itemId))
|
||||
},
|
||||
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
|
||||
@@ -1113,4 +1141,5 @@ export default {
|
||||
.ica-fetch { color: #409eff; }
|
||||
.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; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user