fix(maps): 画布连线键与目标改名解耦(objective_id 锚定)+ nodeRefs 位置兜底 + 保守反错绑断言(P3)
ci/woodpecker/push/main Pipeline failed
ci/woodpecker/push/main Pipeline failed
- map_connections 读侧以 from/to_objective_id 反查当前 dimensions 树定位节点,改名/重排不再静默断线 - 悬停/拖拽期 map_objectives 未返回时按位置兜底(仅同层同序位有效),并记录 stale_objective_id - 新增 tests/test_conn_key_rename_stability.py(含 2 条保守反错绑负测用例) - 零 DDL、零 health_score 改动(问3=A)
This commit is contained in:
@@ -210,6 +210,7 @@
|
||||
:layer-refs="layerRefs"
|
||||
:node-refs="compiledNodeRefs"
|
||||
:layer-keys="layerKeysOrdered"
|
||||
:conn-keys="connKeys"
|
||||
:connections="connections"
|
||||
:temp-line="tempLine"
|
||||
:container-key="recalcTrigger"
|
||||
@@ -319,6 +320,8 @@ import MapTargetTree from "../components/strategy-map/MapTargetTree.vue"
|
||||
import MapNodeDetail from "../components/strategy-map/MapNodeDetail.vue"
|
||||
import MapCompleteness from "../components/strategy-map/MapCompleteness.vue"
|
||||
import ConnectionLines from "../components/strategy/ConnectionLines.vue"
|
||||
// P3:连线端点解析键(`层-序号`)—— 目标改名后线仍指向同一节点
|
||||
import { buildKeyIndex, type ConnectionKeyEntry } from "../utils/connKeys"
|
||||
import MapCanvasToolbar from "../components/map-canvas/MapCanvasToolbar.vue"
|
||||
import MapDimensionFilter from "../components/map-canvas/MapDimensionFilter.vue"
|
||||
import CaliberBadge from "../components/CaliberBadge.vue"
|
||||
@@ -403,6 +406,8 @@ const undoStack = ref<{ dimensions: any; connections: any[] }[]>([])
|
||||
|
||||
// 连线
|
||||
const connections = ref<any[]>([])
|
||||
/** P3:服务端解析键(与 connections 逐下标对齐);空 ⇒ 回落原标识串(历史/降级) */
|
||||
const connKeys = ref<ConnectionKeyEntry[]>([])
|
||||
const linkingFrom = ref<{ key: string; obj: any } | null>(null)
|
||||
const tempLine = ref<{ x1: number; y1: number; x2: number; y2: number } | null>(null)
|
||||
const dragConnectTarget = ref<string | null>(null)
|
||||
@@ -797,6 +802,22 @@ watch(zoomLevel, () => {
|
||||
nextTick(() => connectionLinesRef.value?.recalcAll())
|
||||
})
|
||||
|
||||
// ── P3:服务端解析键 ──
|
||||
// 为什么必须取服务端:canvas_data 里存的是写入时刻的标识串(`层:中文名`),
|
||||
// 目标改名后名字串失效;服务端能由 from/to_objective_id 反查当前 `${层}-${序号}`。
|
||||
// 取不到(接口异常/离线)⇒ 置空,画布回落原标识串,行为与改造前一致(不更坏)。
|
||||
async function refreshConnKeys() {
|
||||
const id = selectedMap.value
|
||||
if (!id) { connKeys.value = []; return }
|
||||
try {
|
||||
const r: any = await api.get(`/maps/${id}/connections`)
|
||||
const serverConns = r?.data?.connections || []
|
||||
connKeys.value = buildKeyIndex(loadedCanvas || connections.value || [], serverConns)
|
||||
} catch (e) {
|
||||
connKeys.value = []
|
||||
}
|
||||
}
|
||||
|
||||
// ── 画布加载 ──
|
||||
function loadCanvas() {
|
||||
caliberFacts.value = caliberGapFor(currentCaliberEntityId())
|
||||
@@ -842,6 +863,7 @@ function loadCanvas() {
|
||||
loadedDims = deepClone(dimensions)
|
||||
connections.value = map?.canvas_data?.connections || []
|
||||
loading.value = false
|
||||
refreshConnKeys()
|
||||
nextTick(() => {
|
||||
triggerRecalc()
|
||||
updateGridMode()
|
||||
@@ -982,6 +1004,7 @@ function undoLast() {
|
||||
connections.value = JSON.parse(JSON.stringify(snap.connections))
|
||||
selectedNode.value = null
|
||||
markDirty()
|
||||
refreshConnKeys() // P3:撤销可能改动目标顺序/名字,解析键随之刷新
|
||||
nextTick(() => { triggerRecalc(); updateZoomWrapHeight(); loadObjectiveLevels() })
|
||||
ElMessage.success('已撤销上一步')
|
||||
}
|
||||
@@ -1716,6 +1739,9 @@ async function saveCanvas(silent?: boolean) {
|
||||
if (res?.updated_at) baseUpdatedAt.value = res.updated_at // 采纳服务端新基准
|
||||
refreshBaselines(res?.dimensions) // 基线回到服务端现值(响应体为权威)
|
||||
markClean()
|
||||
// P3:保存后刷新解析键(改名/调序都会让 `${层}-${序号}` 变,线要跟着节点走)
|
||||
if (payload.canvas_data) loadedCanvas = deepClone(connections.value)
|
||||
refreshConnKeys()
|
||||
if (!silent) ElMessage.success("已保存")
|
||||
} catch (e: any) {
|
||||
if (e?.response?.status === 409) {
|
||||
@@ -1742,6 +1768,8 @@ async function saveConnections() {
|
||||
base_updated_at: currentBaseStamp(),
|
||||
})
|
||||
if (res?.updated_at) baseUpdatedAt.value = res.updated_at
|
||||
loadedCanvas = deepClone(connections.value)
|
||||
refreshConnKeys() // P3:连线新增/删除后服务端重算解析键
|
||||
} catch (e: any) {
|
||||
if (e?.response?.status === 409) {
|
||||
autoSaveBlocked.value = true
|
||||
|
||||
Reference in New Issue
Block a user