Phase 1: OKR模板 — 两步弹窗(O+KR结构) + 14个CMA模板 + okr_templates表 + API

MapCanvasDialogs.vue:
- 节点编辑弹窗改为两步交互(模板选择→O+KR编辑)
- 第一步:按维度展示对应模板(财务4/客户3/流程4/学习3)
- 第二步:编辑O名称+描述+3个KR(名称/目标值/权重)
- 14个CMA标准模板硬编码在前端
- 支持添加/删除KR、自定义目标跳过模板

MapCanvas.vue:
- 传递 editingLayerKey 到弹窗
- onSaveDialog 适配新 O+KR 数据结构
- onNodeSave 存储 krs 和 template_name 字段

Backend:
- OKRTemplate 模型 (okr_templates 表)
- okr_templates API: GET(按维度筛选) + POST(用户自定义) + increment use_count
- seed_okr_templates.py 迁移脚本(建表+14条种子数据)
- 在 main.py 注册新路由
This commit is contained in:
Hermes CI Fix
2026-07-21 17:22:43 +08:00
parent a9cc7c3f1d
commit 9c6c1614fb
8 changed files with 1251 additions and 41 deletions
+19 -1
View File
@@ -120,6 +120,7 @@
:kpi-detail-list="kpiDetailList"
:plan-popup-obj-name="planPopupObjName"
:plan-popup-list="planPopupList"
:editing-layer-key="editingLayerKey"
@update:show-node-dialog="v => showNodeDialog = v"
@update:show-causality-dialog="v => showCausalityDialog = v"
@update:show-versions="v => showVersions = v"
@@ -195,7 +196,20 @@ const editingNodeData = ref<any>(null)
const editingLayerKey = ref('')
const dialogRefreshKey = ref(0)
const dialogForm = reactive({ name: '', description: '', icon: 'target', targetValue: null, currentValue: null, unit: '%', owner: '', isLeading: false, kpis: [] as string[] })
function onSaveDialog() {
function onSaveDialog(data: any) {
// 新格式:O+KR 结构(来自两步弹窗)
if (data.o_name) {
if (!data.o_name?.trim()) { ElMessage.warning('请输入目标名称'); return }
onNodeSave({
...data,
name: data.o_name.trim(),
description: data.o_description?.trim() || '',
layer: editingLayerKey.value,
icon: 'target',
})
return
}
// 旧格式兼容(保留)
if (!dialogForm.name?.trim()) { ElMessage.warning('请输入目标名称'); return }
onNodeSave({ ...dialogForm, name: dialogForm.name.trim(), layer: editingLayerKey.value })
showNodeDialog.value = false
@@ -544,6 +558,9 @@ function onNodeSave(data: any) {
layer: layer,
kpis: data.kpis || [],
icon: data.icon || 'target',
// O+KR 扩展字段
krs: data.krs || [], // 关键结果数组 [{name, target_value, weight, sort_order}]
template_name: data.template_name || null, // 来源模板名
}
if (editingNodeData.value?._index != null && editingNodeData.value._dimKey === layer) {
@@ -569,6 +586,7 @@ function onNodeSave(data: any) {
}
editingNodeData.value = null
showNodeDialog.value = false
debouncedSaveCanvas()
nextTick(triggerRecalc)
}