feat: KPI多粒度目标值补提交 — dashboard API + 前端3视图(目标值月/季/年)
已上线未提交的历史功能(2026-08-17): kpi_target_by_frequency + target_monthly/quarterly/yearly 字段透传
This commit is contained in:
@@ -35,7 +35,9 @@
|
||||
<el-tag v-else type="warning" size="small">{{ kpi.data_owner || '缺失' }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="单位">{{ kpi.unit || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="目标值">{{ kpi.target_value ?? '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="目标值(月)">{{ kpi.target_monthly ?? '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="目标值(季)">{{ kpi.target_quarterly ?? '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="目标值(年)">{{ kpi.target_yearly ?? '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="数据源类型">{{ kpi.data_source_type || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="维度">{{ dimLabel(kpi.dimension) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="频率">{{ kpi.frequency || '-' }}</el-descriptions-item>
|
||||
@@ -63,12 +65,16 @@
|
||||
<el-option v-for="c in categoryOptions" :key="c.value" :label="c.label" :value="c.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="目标值"><el-input-number v-model="kpi.target_value" :min="0" style="width:100%" /></el-form-item>
|
||||
<el-form-item label="目标值(月度)"><el-input-number v-model="kpi.target_monthly" :min="0" style="width:100%" /></el-form-item>
|
||||
<el-form-item label="目标值(季度)"><el-input-number v-model="kpi.target_quarterly" :min="0" style="width:100%" /></el-form-item>
|
||||
<el-form-item label="目标值(年度)"><el-input-number v-model="kpi.target_yearly" :min="0" style="width:100%" /></el-form-item>
|
||||
<el-form-item label="单位"><el-input v-model="kpi.unit" /></el-form-item>
|
||||
<el-form-item label="频率">
|
||||
<el-select v-model="kpi.frequency" style="width:100%">
|
||||
<el-option label="月度" value="monthly" />
|
||||
<el-option label="周度" value="weekly" />
|
||||
<el-option label="季度" value="quarterly" />
|
||||
<el-option label="半年" value="half_year" />
|
||||
<el-option label="年度" value="yearly" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -543,6 +549,13 @@ async function save() {
|
||||
if (saving.value) return
|
||||
saving.value = true
|
||||
try {
|
||||
// 多粒度目标同步:按frequency将对应周期目标写入target_value(兼容评分/阈值等旧逻辑)
|
||||
const freq = kpi.value.frequency || 'monthly'
|
||||
let tv: any = null
|
||||
if (freq === 'quarterly' || freq === 'half_year') tv = kpi.value.target_quarterly
|
||||
else if (freq === 'yearly') tv = kpi.value.target_yearly
|
||||
else tv = kpi.value.target_monthly
|
||||
if (tv != null) kpi.value.target_value = tv
|
||||
await kpiApi.update(kpi.value.id, kpi.value)
|
||||
ElMessage.success('保存成功')
|
||||
isDirty.value = false
|
||||
|
||||
@@ -103,8 +103,10 @@
|
||||
<el-table-column label="二级类别" width="90">
|
||||
<template #default="{ row }">{{ catLabel(row.category) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="target_value" label="目标值" width="90">
|
||||
<template #default="{ row }">{{ formatTarget(row.target_value, row.unit) }}</template>
|
||||
<el-table-column prop="target_value" label="目标值" width="110">
|
||||
<template #default="{ row }">
|
||||
<span class="target-cell">{{ targetLabel(row) }} <b>{{ formatTarget(targetForRow(row), row.unit) }}</b></span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="frequency" label="频率" width="70">
|
||||
<template #default="{ row }">{{ freqLabel(row.frequency) }}</template>
|
||||
@@ -198,7 +200,15 @@
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="目标值"><el-input-number v-model="form.target_value" :min="0" style="width:100%" /></el-form-item>
|
||||
<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-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-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="单位"><el-input v-model="form.unit" placeholder="%, 元, 次, 个..." /></el-form-item>
|
||||
@@ -209,7 +219,9 @@
|
||||
<el-form-item label="频率">
|
||||
<el-select v-model="form.frequency" style="width:100%">
|
||||
<el-option label="月度" value="monthly" />
|
||||
<el-option label="周度" value="weekly" />
|
||||
<el-option label="季度" value="quarterly" />
|
||||
<el-option label="半年" value="half_year" />
|
||||
<el-option label="年度" value="yearly" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -430,6 +442,9 @@ function openEdit(row: any) {
|
||||
dimension: row.dimension,
|
||||
category: row.category,
|
||||
target_value: row.target_value,
|
||||
target_monthly: row.target_monthly,
|
||||
target_quarterly: row.target_quarterly,
|
||||
target_yearly: row.target_yearly,
|
||||
unit: row.unit,
|
||||
frequency: row.frequency,
|
||||
data_source_type: row.data_source_type,
|
||||
@@ -505,6 +520,21 @@ function formatTarget(val: number, unit: string) {
|
||||
return val + (unit || '')
|
||||
}
|
||||
|
||||
// 多粒度目标:按frequency取对应周期目标值
|
||||
function targetForRow(row: any) {
|
||||
const freq = row.frequency || 'monthly'
|
||||
if (freq === 'quarterly' || freq === 'half_year') return row.target_quarterly ?? row.target_value
|
||||
if (freq === 'yearly') return row.target_yearly ?? row.target_value
|
||||
return row.target_monthly ?? row.target_value
|
||||
}
|
||||
|
||||
function targetLabel(row: any) {
|
||||
const freq = row.frequency || 'monthly'
|
||||
if (freq === 'quarterly' || freq === 'half_year') return '季'
|
||||
if (freq === 'yearly') return '年'
|
||||
return '月'
|
||||
}
|
||||
|
||||
// ── 加载分类树 ──
|
||||
async function loadCategories() {
|
||||
try {
|
||||
@@ -639,6 +669,13 @@ async function saveKPI() {
|
||||
if (saving.value) return
|
||||
saving.value = true
|
||||
try {
|
||||
// 多粒度目标同步:按frequency将对应周期目标写入target_value(兼容评分/阈值等旧逻辑)
|
||||
const freq = form.value.frequency || 'monthly'
|
||||
let tv: any = null
|
||||
if (freq === 'quarterly' || freq === 'half_year') tv = form.value.target_quarterly
|
||||
else if (freq === 'yearly') tv = form.value.target_yearly
|
||||
else tv = form.value.target_monthly
|
||||
if (tv != null) form.value.target_value = tv
|
||||
if (editMode.value) {
|
||||
await kpiApi.update(form.value.id, form.value)
|
||||
ElMessage.success('保存成功')
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
</div>
|
||||
<div class="kpi-value-row">
|
||||
<div class="kpi-actual">{{ fmtValue(k.actual_value, k.kpi_code) }}</div>
|
||||
<div class="kpi-target">目标 {{ k.target_value ?? '-' }}</div>
|
||||
<div class="kpi-target">{{ targetLabel(k) }} {{ k.target_value ?? '-' }}</div>
|
||||
</div>
|
||||
<el-progress
|
||||
:percentage="k.target_value ? Math.min(100, Math.round((k.actual_value || 0) / k.target_value * 100)) : 0"
|
||||
@@ -140,6 +140,13 @@ function fmtValue(val: any, code: string) {
|
||||
return val
|
||||
}
|
||||
|
||||
function targetLabel(k: any) {
|
||||
const freq = k.frequency || 'monthly'
|
||||
if (freq === 'quarterly' || freq === 'half_year') return '目标(季)'
|
||||
if (freq === 'yearly') return '目标(年)'
|
||||
return '目标(月)'
|
||||
}
|
||||
|
||||
function planIcon(p: any) {
|
||||
if (p.status === 'completed') return '✅'
|
||||
if (p.overdue) return '🔴'
|
||||
|
||||
Reference in New Issue
Block a user