fix: 修复保存后刷新网页图层全变成1的问题

问题原因:
1. LogicFlow 的 render() 方法不会自动应用节点的 zIndex 属性
2. 切换标签时,LogicFlow Label 插件对空 _label 数组处理有误导致渲染失败
3. 渲染失败后节点 zIndex 被重置为默认值 1

解决方案:
1. 在 App.vue 中,render() 后立即从保存的数据中恢复每个节点的 zIndex
2. 在 normalizeGraphData() 中清理空的 _label 数组,避免 Label 插件报错
3. 简化 FlowEditor.vue 中的 normalizeAllNodes(),移除不必要的重新分配逻辑
4. 清理调试日志,保持代码整洁

测试:
- 添加节点并调整图层顺序
- 切换标签页
- 刷新浏览器
- 确认图层顺序保持不变
This commit is contained in:
2026-02-13 19:28:21 +08:00
parent 92aa4094f5
commit 9227a61c85
21 changed files with 3175 additions and 62 deletions

View File

@@ -198,23 +198,11 @@ function normalizeAllNodes() {
if (!lfInstance) return;
lfInstance.graphModel?.nodes.forEach((model: BaseNodeModel) => normalizeNodeModel(model));
// 检查是否所有节点的 zIndex 都相同且为默认值(通常是从历史恢复的情况)
// 清除新节点标记
const allNodes = lfInstance.graphModel?.nodes || [];
if (allNodes.length > 1) {
const firstZIndex = allNodes[0]?.zIndex;
const allSameZIndex = allNodes.every(n => n.zIndex === firstZIndex);
// 只有当所有节点的 zIndex 都是默认值 1 时才重新分配
if (allSameZIndex && firstZIndex === 1) {
console.log('[初始化] 检测到所有节点 zIndex 都为默认值 1开始重新分配 zIndex');
// 为所有节点分配递增的 zIndex避免层级操作异常
allNodes.forEach((node, index) => {
const newZIndex = index + 1;
node.setZIndex(newZIndex);
});
console.log('[初始化] zIndex 重新分配完成:', allNodes.map(n => ({ id: n.id, zIndex: n.zIndex })));
}
}
allNodes.forEach(node => {
delete (node as any)._isNewNode;
});
}
function updateNodeMeta(model: BaseNodeModel, updater: (meta: Record<string, any>) => Record<string, any>) {
@@ -298,12 +286,19 @@ function sendToBack(nodeId?: string) {
const targetId = nodeId || selectedNode.value?.id;
if (!targetId) return;
// 诊断日志:查看所有节点的 zIndex
const currentNode = lfInstance.getNodeModelById(targetId);
if (!currentNode) return;
const allNodes = lfInstance.graphModel.nodes;
console.log('[置于底层] 目标节点ID:', targetId);
console.log('[置于底层] 所有节点的 zIndex:', allNodes.map(n => ({ id: n.id, zIndex: n.zIndex })));
lfInstance.setElementZIndex(targetId, 'bottom');
// 修复:找到所有节点中最小的 zIndex然后设置为比它更小
const allZIndexes = allNodes.map(n => n.zIndex).filter(z => z !== undefined);
const minZIndex = allZIndexes.length > 0 ? Math.min(...allZIndexes) : 1;
const newZIndex = minZIndex - 1;
currentNode.setZIndex(newZIndex);
// 操作后再次查看
console.log('[置于底层] 操作后所有节点的 zIndex:', allNodes.map(n => ({ id: n.id, zIndex: n.zIndex })));
@@ -699,7 +694,7 @@ onMounted(() => {
grid: { type: 'dot', size: 10 },
allowResize: true,
allowRotate: true,
overlapMode: 0,
overlapMode: -1,
snapline: snaplineEnabled.value,
keyboard: {
enabled: true
@@ -916,45 +911,33 @@ onMounted(() => {
registerNodes(lfInstance);
setLogicFlowInstance(lfInstance);
lfInstance.render({
nodes: [],
edges: []
});
lfInstance.extension.miniMap.show();
normalizeAllNodes();
lfInstance.updateEditConfig({
multipleSelectKey: 'shift',
snapGrid: snapGridEnabled.value
});
applySelectionSelect(selectionEnabled.value);
updateSelectedCount(lfInstance.graphModel);
// 监听节点点击事件,更新 selectedNode
lfInstance.on(EventType.NODE_CLICK, ({ data }) => {
selectedNode.value = data;
updateSelectedCount();
});
lfInstance.on(EventType.NODE_DRAG, (args) => handleNodeDrag(args as any));
// 监听所有可能的节点添加事件
lfInstance.on(EventType.NODE_ADD, ({ data }) => {
console.log('[NODE_ADD 事件触发] 节点ID:', data.id);
const model = lfInstance.getNodeModelById(data.id);
if (model) {
console.log('[NODE_ADD] 获取到节点模型,当前 zIndex:', model.zIndex);
normalizeNodeModel(model);
// 设置新节点的 zIndex 为 1000
const newZIndex = 1000;
console.log(`[NODE_ADD] 准备设置 zIndex: ${newZIndex}`);
model.setZIndex(newZIndex);
console.log(`[NODE_ADD] 设置后的 zIndex:`, model.zIndex);
} else {
console.log('[NODE_ADD] 未能获取到节点模型');
model.setZIndex(1000);
// 标记这个节点是新创建的,避免被 normalizeAllNodes 重置
(model as any)._isNewNode = true;
}
});
lfInstance.on(EventType.GRAPH_RENDERED, () => normalizeAllNodes());
// 监听 DND 添加节点事件
lfInstance.on('node:dnd-add', ({ data }) => {
const model = lfInstance.getNodeModelById(data.id);
if (model) {
// 设置新节点的 zIndex 为 1000
model.setZIndex(1000);
// 标记这个节点是新创建的
(model as any)._isNewNode = true;
}
});
lfInstance.on(EventType.GRAPH_RENDERED, () => {
normalizeAllNodes();
});
// 监听空白点击事件,取消选中
lfInstance.on(EventType.BLANK_CLICK, () => {

View File

@@ -1,9 +1,34 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import { computed, ref, inject, onMounted, onBeforeUnmount } from 'vue';
import { toTextStyle } from '@/ts/nodeStyle';
import { useNodeAppearance } from '@/ts/useNodeAppearance';
const currentShikigami = ref({ name: '未选择式神', avatar: '', rarity: '' });
const getNode = inject('getNode') as (() => any) | undefined;
const zIndex = ref(1);
let intervalId: number | null = null;
// 使用轮询方式定期更新 zIndex
onMounted(() => {
const node = getNode?.();
if (node) {
zIndex.value = node.zIndex ?? 1;
// 每 100ms 检查一次 zIndex 是否变化
intervalId = window.setInterval(() => {
const currentZIndex = node.zIndex ?? 1;
if (zIndex.value !== currentZIndex) {
zIndex.value = currentZIndex;
}
}, 100);
}
});
onBeforeUnmount(() => {
if (intervalId !== null) {
clearInterval(intervalId);
}
});
const { containerStyle, textStyle } = useNodeAppearance({
onPropsChange(props) {
@@ -18,6 +43,7 @@ const mergedContainerStyle = computed(() => ({ ...containerStyle.value, boxSizin
<template>
<div class="node-content" :style="mergedContainerStyle">
<div class="zindex-badge">{{ zIndex }}</div>
<img
v-if="currentShikigami.avatar"
:src="currentShikigami.avatar"
@@ -36,6 +62,20 @@ const mergedContainerStyle = computed(() => ({ ...containerStyle.value, boxSizin
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
}
.zindex-badge {
position: absolute;
top: 4px;
right: 4px;
background: rgba(64, 158, 255, 0.9);
color: white;
font-size: 12px;
font-weight: bold;
padding: 2px 6px;
border-radius: 10px;
z-index: 10;
pointer-events: none;
}
.shikigami-image {
width: 85%;

View File

@@ -1,8 +1,33 @@
<script setup lang="ts">
import { ref } from 'vue';
import { ref, computed, inject, onMounted, onBeforeUnmount } from 'vue';
import { useNodeAppearance } from '@/ts/useNodeAppearance';
const currentYuhun = ref({ name: '未选择御魂', avatar: '', type: '' });
const getNode = inject('getNode') as (() => any) | undefined;
const zIndex = ref(1);
let intervalId: number | null = null;
// 使用轮询方式定期更新 zIndex
onMounted(() => {
const node = getNode?.();
if (node) {
zIndex.value = node.zIndex ?? 1;
// 每 100ms 检查一次 zIndex 是否变化
intervalId = window.setInterval(() => {
const currentZIndex = node.zIndex ?? 1;
if (zIndex.value !== currentZIndex) {
zIndex.value = currentZIndex;
}
}, 100);
}
});
onBeforeUnmount(() => {
if (intervalId !== null) {
clearInterval(intervalId);
}
});
const { containerStyle, textStyle } = useNodeAppearance({
onPropsChange(props) {
@@ -15,6 +40,7 @@ const { containerStyle, textStyle } = useNodeAppearance({
<template>
<div class="node-content" :style="containerStyle">
<div class="zindex-badge">{{ zIndex }}</div>
<img
v-if="currentYuhun.avatar"
:src="currentYuhun.avatar"
@@ -34,6 +60,20 @@ const { containerStyle, textStyle } = useNodeAppearance({
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
}
.zindex-badge {
position: absolute;
top: 4px;
right: 4px;
background: rgba(64, 158, 255, 0.9);
color: white;
font-size: 12px;
font-weight: bold;
padding: 2px 6px;
border-radius: 10px;
z-index: 10;
pointer-events: none;
}
.yuhun-image {
width: 85%;