feat: 战略地图贝塞尔因果链+节点KPI可视化(P0+P1)
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
:view-direction="viewDirection"
|
||||
:current-map="currentMap"
|
||||
:linking-from="linkingFrom"
|
||||
:chain-mode="chainMode"
|
||||
@select-map="onSelectMap"
|
||||
@update:view-direction="v => viewDirection = v"
|
||||
@zoom-in="zoomIn"
|
||||
@@ -20,8 +21,15 @@
|
||||
@show-versions="showVersions = true; loadVersions()"
|
||||
@go-alignment="goAlignment"
|
||||
@cancel-link="cancelLink"
|
||||
@toggle-chain-mode="toggleChainMode"
|
||||
/>
|
||||
|
||||
<!-- 因果链查看模式提示 -->
|
||||
<div v-if="chainMode" class="chain-mode-bar">
|
||||
<span>🔍 因果链查看模式 — 点击节点聚焦其相关因果链(再次点击取消),其余节点自动变淡</span>
|
||||
<el-button size="small" type="warning" @click="toggleChainMode">退出模式</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 连线模式提示 -->
|
||||
<div v-if="linkingFrom" class="linking-bar">
|
||||
<span>🔗 从「{{ linkingFrom.obj.name }}」连线 — 点击另一个目标完成连线</span>
|
||||
@@ -67,6 +75,7 @@
|
||||
:all-kpis="allKpis"
|
||||
:get-node-level="getNodeLevel"
|
||||
:get-node-progress="getNodeProgress"
|
||||
:chain-node-keys="chainNodeKeys"
|
||||
@add-objective="openAddDialog"
|
||||
@edit-objective="(p: any) => openEditDialog(p.dimKey, p.idx, p.obj)"
|
||||
@delete-objective="(p: any) => deleteNode(p.dimKey, p.idx)"
|
||||
@@ -102,8 +111,13 @@
|
||||
:temp-line="tempLine"
|
||||
:container-key="recalcTrigger"
|
||||
:view-direction="viewDirection"
|
||||
:chain-mode="chainMode"
|
||||
:chain-focus-key="chainFocusKey"
|
||||
@select-connection="onSelectConnection"
|
||||
@delete-connection="onDeleteConnection"
|
||||
@chain-hover="onChainHover"
|
||||
@chain-focus="onChainFocus"
|
||||
@update-connection="onUpdateConnection"
|
||||
/>
|
||||
|
||||
<!-- 所有弹窗统一管理 -->
|
||||
@@ -179,6 +193,38 @@ const tempLine = ref<{ x1: number; y1: number; x2: number; y2: number } | null>(
|
||||
const dragConnectTarget = ref<string | null>(null)
|
||||
const selectedConnIdx = ref<number | null>(null)
|
||||
|
||||
// ── 因果链查看模式 ──
|
||||
const chainMode = ref(false)
|
||||
const chainFocusKey = ref<string | null>(null)
|
||||
const chainHoverNodes = ref<string[] | null>(null)
|
||||
const chainFocusNodes = ref<string[] | null>(null)
|
||||
|
||||
/** 当前因果链涉及的节点 key 集合(悬停链 ∪ 聚焦链),用于节点变淡 */
|
||||
const chainNodeKeys = computed<string[]>(() => {
|
||||
const set = new Set<string>()
|
||||
chainHoverNodes.value?.forEach(k => set.add(k))
|
||||
chainFocusNodes.value?.forEach(k => set.add(k))
|
||||
return [...set]
|
||||
})
|
||||
|
||||
function toggleChainMode() {
|
||||
chainMode.value = !chainMode.value
|
||||
chainFocusKey.value = null
|
||||
chainHoverNodes.value = null
|
||||
chainFocusNodes.value = null
|
||||
if (!chainMode.value) selectedConnIdx.value = null
|
||||
}
|
||||
|
||||
function onChainHover(nodes: string[] | null) { chainHoverNodes.value = nodes }
|
||||
function onChainFocus(nodes: string[] | null) { chainFocusNodes.value = nodes }
|
||||
|
||||
/** 更新连线语义(effect/label),落库 */
|
||||
function onUpdateConnection(idx: number, patch: any) {
|
||||
if (!connections.value[idx]) return
|
||||
Object.assign(connections.value[idx], patch)
|
||||
saveConnections()
|
||||
}
|
||||
|
||||
// 缩放
|
||||
const zoomLevel = ref(1)
|
||||
const scrollWrapRef = ref<HTMLElement | null>(null)
|
||||
@@ -342,6 +388,10 @@ function loadCanvas() {
|
||||
connections.value = []
|
||||
linkingFrom.value = null
|
||||
selectedConnIdx.value = null
|
||||
chainMode.value = false
|
||||
chainFocusKey.value = null
|
||||
chainHoverNodes.value = null
|
||||
chainFocusNodes.value = null
|
||||
loading.value = true
|
||||
|
||||
api.get("/maps").then((r: any) => {
|
||||
@@ -404,6 +454,11 @@ function onNodeClick(dimKey: string, oi: number, obj: any) {
|
||||
completeConnection(linkingFrom.value.key, key)
|
||||
return
|
||||
}
|
||||
// 因果链查看模式:点击节点聚焦其相关因果链,再次点击取消
|
||||
if (chainMode.value) {
|
||||
chainFocusKey.value = chainFocusKey.value === key ? null : key
|
||||
return
|
||||
}
|
||||
// 有KPI则展示详情
|
||||
if (kpiProgressMap[key]?.kpiList?.length > 0) {
|
||||
showKpiDetailModal(dimKey, oi, obj)
|
||||
@@ -433,12 +488,12 @@ function completeConnection(fromKey: string, toKey: string) {
|
||||
from: fromKey, to: toKey,
|
||||
}).then(() => {
|
||||
ElMessage.success("连线已添加")
|
||||
connections.value.push({ from: fromKey, to: toKey, style: "solid" })
|
||||
connections.value.push({ from: fromKey, to: toKey, style: "solid", effect: "positive", label: "" })
|
||||
cancelLink()
|
||||
nextTick(() => { triggerRecalc(); saveConnections() })
|
||||
}).catch((e: any) => {
|
||||
if (e?.response?.status !== 400) {
|
||||
connections.value.push({ from: fromKey, to: toKey, style: "solid" })
|
||||
connections.value.push({ from: fromKey, to: toKey, style: "solid", effect: "positive", label: "" })
|
||||
ElMessage.success("连线已添加(本地)")
|
||||
} else {
|
||||
ElMessage.warning(e?.response?.data?.detail || "连线失败")
|
||||
@@ -976,6 +1031,14 @@ onUnmounted(() => {
|
||||
border-radius: 8px; margin-bottom: 8px; font-size: 14px; flex-shrink: 0;
|
||||
}
|
||||
|
||||
.chain-mode-bar {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
padding: 8px 16px; background: #fdf6ec; border: 1px solid #f5dab1;
|
||||
border-radius: 8px; margin-bottom: 8px; font-size: 13px; flex-shrink: 0;
|
||||
color: #b88230;
|
||||
}
|
||||
.chain-mode-bar span { flex: 1; }
|
||||
|
||||
.empty-state {
|
||||
flex: 1; display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user