feat: KPI新建设计引导(五步法·五规则) — KPIList新建弹窗
- 新建KPI时显示设计引导折叠面板: 五步法逐步引导(职责/客户/需求/成果/五因素) - 五规则实时校验清单: 名称/维度/单位/目标值/频率/负责人 + 本维度KPI数量≤6提示 - 提交门禁(仅新建): 名称+维度必填, 单位+至少一个目标值必填 - 对应 docs/kpi-design-rule.md 方法论落地
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
<h3>KPI字典</h3>
|
||||
<div class="header-actions">
|
||||
<el-button @click="openTemplateDialog">📋 从模板创建</el-button>
|
||||
<el-button type="primary" @click="showForm=true; form={}; editMode=false">+ 新建KPI</el-button>
|
||||
<el-button type="primary" @click="openCreate">+ 新建KPI</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -170,6 +170,34 @@
|
||||
|
||||
<!-- 新建/编辑对话框 -->
|
||||
<MyDialog v-model="showForm" :title="editMode ? '编辑KPI' : '新建KPI'" :width="640">
|
||||
<!-- KPI设计引导(五步法·五规则,新建时显示) -->
|
||||
<div v-if="!editMode" class="design-guide">
|
||||
<el-collapse v-model="guideOpen">
|
||||
<el-collapse-item name="guide">
|
||||
<template #title>
|
||||
<span class="guide-title">📐 KPI设计引导(五步法 · 五规则)</span>
|
||||
</template>
|
||||
<div class="guide-steps">
|
||||
<div v-for="(s, i) in guideSteps" :key="i" class="guide-step">
|
||||
<span class="step-no">{{ i + 1 }}</span>
|
||||
<div class="step-body">
|
||||
<div class="step-title">{{ s.title }}</div>
|
||||
<div class="step-desc">{{ s.desc }}</div>
|
||||
<el-input v-model="designDraft[s.field]" :placeholder="s.placeholder" size="small" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rule-checks">
|
||||
<div class="rule-title">五规则实时校验</div>
|
||||
<div v-for="c in designChecks" :key="c.label" class="rule-check">
|
||||
<span :class="c.pass ? 'ok' : 'fail'">{{ c.pass ? '✅' : '❌' }}</span>
|
||||
<span>{{ c.label }}</span>
|
||||
<span v-if="c.note" class="rule-note">{{ c.note }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
<el-form :model="form" label-width="110px" size="small">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
@@ -432,6 +460,41 @@ const editMode = ref(false)
|
||||
const form = ref<any>({})
|
||||
const saving = ref(false)
|
||||
|
||||
// 新建入口:重置表单 + 设计引导草稿
|
||||
function openCreate() {
|
||||
editMode.value = false
|
||||
form.value = {}
|
||||
designDraft.value = {}
|
||||
guideOpen.value = ['guide']
|
||||
showForm.value = true
|
||||
}
|
||||
|
||||
// ── KPI设计引导(五步法·五规则,对应 docs/kpi-design-rule.md)──
|
||||
const guideOpen = ref(['guide'])
|
||||
const guideSteps = [
|
||||
{ field: 'duty', title: '① 明确岗位职责', desc: '这个KPI考核谁的什么岗位?岗位存在的价值是什么?', placeholder: '如:销售岗——负责新客户开拓与回款' },
|
||||
{ field: 'customer', title: '② 分析客户是谁', desc: '服务对象:内部客户(上级/下游部门)+ 外部客户(终端/渠道)', placeholder: '如:外部客户(新签/续约)+ 内部(交付/财务)' },
|
||||
{ field: 'requirement', title: '③ 分析客户需求', desc: '客户对这个岗位的期望标准是什么?', placeholder: '如:快速响应、按时交付、回款可控' },
|
||||
{ field: 'output', title: '④ 确定成果输出', desc: '岗位需要产出什么可交付成果?(即本KPI,填入上方表单)', placeholder: '如:新签客户数、签约金额' },
|
||||
{ field: 'factors', title: '⑤ 五因素设计指标', desc: '时间/数量/质量/成本/风险——对应下方表单字段', placeholder: '如:数量=新签数(目标值)、时间=频率(月度)、质量=满意度(阈值)' },
|
||||
]
|
||||
const designDraft = ref<Record<string, string>>({})
|
||||
|
||||
const designChecks = computed(() => {
|
||||
const f = form.value || {}
|
||||
const dimCount = kpis.value.filter((k: any) => k.dimension === f.dimension).length
|
||||
const hasTarget = f.target_monthly != null || f.target_quarterly != null || f.target_yearly != null || f.target_value != null
|
||||
return [
|
||||
{ label: 'KPI名称已填(SMART·具体)', pass: !!f.kpi_name },
|
||||
{ label: '已选维度(BSC四维平衡)', pass: !!f.dimension },
|
||||
{ label: '已填单位(可衡量)', pass: !!f.unit },
|
||||
{ label: '有目标值(可衡量·五因素数量)', pass: hasTarget },
|
||||
{ label: '已选频率(SMART·有时限)', pass: !!f.frequency },
|
||||
{ label: '已选负责人(责任到人)', pass: !!f.responsible_user },
|
||||
{ label: '本维度KPI数量≤6(规则①)', pass: dimCount < 6, note: dimCount ? `当前${DIM_MAP[f.dimension] || ''}维度已有${dimCount}个,建议聚焦` : '' },
|
||||
]
|
||||
})
|
||||
|
||||
// ── 编辑入口:列表行 → 弹窗编辑(与新建一致)──
|
||||
function openEdit(row: any) {
|
||||
editMode.value = true
|
||||
@@ -667,6 +730,17 @@ function onDimensionChange(val: string) {
|
||||
|
||||
async function saveKPI() {
|
||||
if (saving.value) return
|
||||
// KPI设计规范·基础门禁(仅新建模式;五步法成果输出 + SMART可衡量)
|
||||
if (!editMode.value) {
|
||||
if (!form.value.kpi_name || !form.value.dimension) {
|
||||
ElMessage.warning('请先填写 KPI名称 和 维度(KPI设计规范·五步法)')
|
||||
return
|
||||
}
|
||||
if (!form.value.unit || (form.value.target_monthly == null && form.value.target_quarterly == null && form.value.target_yearly == null && form.value.target_value == null)) {
|
||||
ElMessage.warning('请补充 单位 和 至少一个目标值(KPI设计规范·SMART可衡量)')
|
||||
return
|
||||
}
|
||||
}
|
||||
saving.value = true
|
||||
try {
|
||||
// 多粒度目标同步:按frequency将对应周期目标写入target_value(兼容评分/阈值等旧逻辑)
|
||||
@@ -801,6 +875,83 @@ onMounted(() => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* ── KPI设计引导(五步法·五规则)── */
|
||||
.design-guide {
|
||||
margin-bottom: 12px;
|
||||
border: 1px solid #d9e8ff;
|
||||
border-radius: 8px;
|
||||
background: #f8fbff;
|
||||
overflow: hidden;
|
||||
}
|
||||
.guide-title {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
color: #1f4e9c;
|
||||
}
|
||||
.guide-steps {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 4px 8px 10px;
|
||||
}
|
||||
.guide-step {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.step-no {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 50%;
|
||||
background: #409EFF;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.step-body {
|
||||
flex: 1;
|
||||
}
|
||||
.step-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
.step-desc {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin: 2px 0 4px;
|
||||
}
|
||||
.rule-checks {
|
||||
border-top: 1px dashed #d9e8ff;
|
||||
padding: 10px 8px 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
.rule-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #1f4e9c;
|
||||
}
|
||||
.rule-check {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
color: #303133;
|
||||
}
|
||||
.rule-check .ok { color: #67C23A; }
|
||||
.rule-check .fail { color: #F56C6C; }
|
||||
.rule-note {
|
||||
color: #E6A23C;
|
||||
font-size: 12px;
|
||||
}
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
Reference in New Issue
Block a user