feat: O→KR→KPI Phase 0 - KR sub-nodes on canvas, KR edit dialog, data migration
- MapNode.vue: Add collapsible KR sub-list with progress bars under each O node - MapCanvasDialogs.vue: Add KPI selector per KR, editing mode (skip template step, pre-fill data), validation (max 5 KR, target value required) - StrategyLayer.vue: Pass through edit-kr/add-kr events - MapCanvas.vue: Data migration (old kpis[]→krs[]), KR add/edit handlers, pass editingObj/allKpis to dialog
This commit is contained in:
@@ -76,6 +76,8 @@
|
||||
@show-plan-list="(p: any) => showPlanList(p.dimKey, p.idx, p.obj)"
|
||||
@node-click="(dk: string, oi: number, obj: any) => onNodeClick(dk, oi, obj)"
|
||||
@reorder="(dk: string) => onLayerReorder(dk)"
|
||||
@edit-kr="(p: any) => onEditKr(p.dimKey, p.idx, p.obj, p.ki, p.kr)"
|
||||
@add-kr="(p: any) => onAddKr(p.dimKey, p.idx, p.obj)"
|
||||
/>
|
||||
|
||||
<!-- 跨层箭头指示器(根据视角切换方向) -->
|
||||
@@ -121,6 +123,8 @@
|
||||
:plan-popup-obj-name="planPopupObjName"
|
||||
:plan-popup-list="planPopupList"
|
||||
:editing-layer-key="editingLayerKey"
|
||||
:editing-obj="editingNodeData"
|
||||
:all-kpis="allKpis"
|
||||
@update:show-node-dialog="v => showNodeDialog = v"
|
||||
@update:show-causality-dialog="v => showCausalityDialog = v"
|
||||
@update:show-versions="v => showVersions = v"
|
||||
@@ -357,6 +361,8 @@ function loadCanvas() {
|
||||
}
|
||||
// 确保四层都存在
|
||||
ensureFourLayers()
|
||||
// 数据迁移:旧格式 kpis[] → krs[]
|
||||
migrateOldKpiFormat()
|
||||
connections.value = map?.canvas_data?.connections || []
|
||||
loading.value = false
|
||||
nextTick(() => {
|
||||
@@ -534,6 +540,11 @@ function openEditDialog(dimKey: string, oi: number, obj: any) {
|
||||
owner: obj.owner || '',
|
||||
isLeading: obj.isLeading ?? obj.is_leading ?? false,
|
||||
description: obj.description || '',
|
||||
// O+KR 扩展字段(传递给弹窗预填)
|
||||
krs: obj.krs || [],
|
||||
o_name: obj.name,
|
||||
o_description: obj.description || '',
|
||||
_rawObjective: obj, // 保留原始引用以便直接修改KR
|
||||
}
|
||||
// 保存编辑索引以便更新
|
||||
editingNodeData.value._dimKey = dimKey
|
||||
@@ -567,11 +578,15 @@ function onNodeSave(data: any) {
|
||||
// 编辑已有节点
|
||||
const idx = editingNodeData.value._index
|
||||
if (dim.objectives[idx]) {
|
||||
// 保留原有kpis等额外字段
|
||||
// 保留原有kpis/krs等额外字段(当新数据未提供时)
|
||||
const extra = {
|
||||
kpis: dim.objectives[idx].kpis || [],
|
||||
icon: dim.objectives[idx].icon || 'target',
|
||||
}
|
||||
// 如果新数据没有提供krs,保留旧krs
|
||||
if (!data.krs || data.krs.length === 0) {
|
||||
node.krs = dim.objectives[idx].krs || []
|
||||
}
|
||||
dim.objectives[idx] = { ...node, ...extra }
|
||||
ElMessage.success("节点已更新")
|
||||
}
|
||||
@@ -604,6 +619,51 @@ async function deleteNode(dimKey: string, oi: number) {
|
||||
} catch { /* cancel */ }
|
||||
}
|
||||
|
||||
// ── KR操作 ──
|
||||
function onEditKr(dimKey: string, oi: number, obj: any, ki: number, kr: any) {
|
||||
// 打开O编辑弹窗并定位到对应KR
|
||||
openEditDialog(dimKey, oi, obj)
|
||||
}
|
||||
|
||||
function onAddKr(dimKey: string, oi: number, obj: any) {
|
||||
const dim = dimensions.find((d: any) => d.key === dimKey)
|
||||
if (!dim || !dim.objectives[oi]) return
|
||||
const target = dim.objectives[oi]
|
||||
if (!target.krs) target.krs = []
|
||||
if (target.krs.length >= 5) {
|
||||
ElMessage.warning('每个目标最多5个关键结果')
|
||||
return
|
||||
}
|
||||
target.krs.push({
|
||||
name: "",
|
||||
target_value: "",
|
||||
weight: 33,
|
||||
kpi_code: null,
|
||||
sort_order: target.krs.length + 1,
|
||||
})
|
||||
ElMessage.success("已添加空白KR,请编辑完善")
|
||||
debouncedSaveCanvas()
|
||||
}
|
||||
|
||||
// ── 数据迁移:旧格式 kpis[] 自动转为 krs[] ──
|
||||
function migrateOldKpiFormat() {
|
||||
for (const dim of dimensions) {
|
||||
for (const obj of dim.objectives || []) {
|
||||
// 仅当有 kpis 数组 且 没有 krs 时执行迁移
|
||||
if (obj.kpis && Array.isArray(obj.kpis) && obj.kpis.length > 0 && (!obj.krs || obj.krs.length === 0)) {
|
||||
obj.krs = obj.kpis.map((kpiCode: string, idx: number) => ({
|
||||
name: kpiNameMap[kpiCode] || kpiCode,
|
||||
target_value: "",
|
||||
weight: Math.round(100 / obj.kpis.length),
|
||||
kpi_code: kpiCode,
|
||||
sort_order: idx + 1,
|
||||
}))
|
||||
console.log(`[迁移] 目标「${obj.name}」: ${obj.kpis.length} 条KPI已转为KR`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── 保存 ──
|
||||
let saveTimer: ReturnType<typeof setTimeout> | null = null
|
||||
let autoSaveTimer: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
Reference in New Issue
Block a user