feat: KPI多粒度目标智能派生 — 基准值+按类型派生+手动覆盖

- 后端: target_calc_type字段(accumulate累计/ratio比率) + infer_calc_type名称/单位推断
- 派生规则: 累计型 月×3=季×12=年(季×4=年); 比率型 季/年沿用基准不可乘
- 虚拟派生不落库: kpi_to_dict返回derived_targets+derived_flags(自动标记)
- DB: 302个KPI回填类型(226累计/76比率)
- 前端KPIList: 指标类型选择 + 季/年自动派生预览(↳自动=N)
- 前端KPIDetail: 元数据卡片自动标记 + 编辑表单指标类型
- 回归: pytest 451 passed
This commit is contained in:
Hermes CI Fix
2026-08-21 10:59:43 +08:00
parent d5edcefb48
commit b06821a426
4 changed files with 214 additions and 5 deletions
+49 -2
View File
@@ -231,12 +231,18 @@
<el-form-item label="目标值()"><el-input-number v-model="form.target_monthly" :min="0" style="width:100%" /></el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="目标值()"><el-input-number v-model="form.target_quarterly" :min="0" style="width:100%" /></el-form-item>
<el-form-item label="目标值()">
<el-input-number v-model="form.target_quarterly" :min="0" style="width:100%" />
<div v-if="derivedPreview.quarterly !== null" class="derive-hint">↳ 自动={{ derivedPreview.quarterly }}</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="目标值()"><el-input-number v-model="form.target_yearly" :min="0" style="width:100%" /></el-form-item>
<el-form-item label="目标值()">
<el-input-number v-model="form.target_yearly" :min="0" style="width:100%" />
<div v-if="derivedPreview.yearly !== null" class="derive-hint">↳ 自动={{ derivedPreview.yearly }}</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="单位"><el-input v-model="form.unit" placeholder="%, , , ..." /></el-form-item>
@@ -254,6 +260,16 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="指标类型">
<el-select v-model="form.target_calc_type" style="width:100%">
<el-option label="累计型×3=可乘" value="accumulate" />
<el-option label="比率型季沿用月不可乘" value="ratio" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="数据源">
<el-select v-model="form.data_source_type" style="width:100%">
@@ -495,6 +511,30 @@ const designChecks = computed(() => {
]
})
// ── 多粒度目标派生预览(填基准值后季/年自动提示,与后端 derive_cycle_targets 一致)──
const derivedPreview = computed(() => {
const f = form.value || {}
const type = f.target_calc_type || 'ratio'
const freq = f.frequency || 'monthly'
let base: number | null = null
if (freq === 'yearly') base = f.target_yearly
else if (freq === 'quarterly' || freq === 'half_year') base = f.target_quarterly
else base = f.target_monthly
if (base == null) base = f.target_value
if (base == null) return { quarterly: null, yearly: null }
let q: number | null = f.target_quarterly
let y: number | null = f.target_yearly
if (type === 'accumulate') {
const m: number = f.target_monthly ?? base
if (q == null) q = m * 3
if (y == null) y = m * 12
} else {
if (q == null) q = base
if (y == null) y = base
}
return { quarterly: f.target_quarterly == null ? q : null, yearly: f.target_yearly == null ? y : null }
})
// ── 编辑入口:列表行 → 弹窗编辑(与新建一致)──
function openEdit(row: any) {
editMode.value = true
@@ -518,6 +558,7 @@ function openEdit(row: any) {
threshold_yellow: row.threshold_yellow,
threshold_red: row.threshold_red,
epic: row.epic,
target_calc_type: row.target_calc_type,
}
showForm.value = true
}
@@ -952,6 +993,12 @@ onMounted(() => {
color: #E6A23C;
font-size: 12px;
}
.derive-hint {
font-size: 12px;
color: #409EFF;
margin-top: 2px;
line-height: 1.4;
}
.page-header {
display: flex;
justify-content: space-between;