切换tab时,恢复viewport

This commit is contained in:
2025-07-03 17:40:07 +08:00
parent ed2050c5c7
commit 44ff25b366
3 changed files with 91 additions and 23 deletions

View File

@@ -2,7 +2,7 @@
import Toolbar from './components/Toolbar.vue'; import Toolbar from './components/Toolbar.vue';
import ProjectExplorer from './components/ProjectExplorer.vue'; import ProjectExplorer from './components/ProjectExplorer.vue';
import ComponentsPanel from './components/flow/ComponentsPanel.vue'; import ComponentsPanel from './components/flow/ComponentsPanel.vue';
import { computed, ref, onMounted, onUnmounted, onBeforeUpdate, reactive, provide, inject } from "vue"; import { computed, ref, onMounted, onUnmounted, onBeforeUpdate, reactive, provide, inject, watch } from "vue";
import { useFilesStore } from "@/ts/useStore"; import { useFilesStore } from "@/ts/useStore";
import Vue3DraggableResizable from 'vue3-draggable-resizable'; import Vue3DraggableResizable from 'vue3-draggable-resizable';
import { TabPaneName, TabsPaneContext } from "element-plus"; import { TabPaneName, TabsPaneContext } from "element-plus";
@@ -24,6 +24,7 @@ const contentHeight = computed(() => `${windowHeight.value - toolbarHeight}px`);
const flowEditorRef = ref(null); const flowEditorRef = ref(null);
const flowEditorRefs = ref({}); const flowEditorRefs = ref({});
const lastActiveFile = ref(filesStore.activeFile);
const handleTabsEdit = ( const handleTabsEdit = (
targetName: String | undefined, targetName: String | undefined,
@@ -85,6 +86,24 @@ const handleAddNode = (nodeData) => {
} }
}; };
const handleSaveViewport = (viewport) => {
filesStore.updateFileViewport(filesStore.activeFile, viewport);
};
const handleRequestViewport = () => {
return filesStore.getFileViewport(filesStore.activeFile);
};
watch(
() => filesStore.activeFile,
(newVal, oldVal) => {
// 切换前保存旧 tab 的 viewport
if (oldVal && flowEditorRef.value && flowEditorRef.value.getViewport) {
const viewport = flowEditorRef.value.getViewport();
filesStore.updateFileViewport(oldVal, viewport);
}
lastActiveFile.value = newVal;
}
);
</script> </script>
@@ -110,20 +129,20 @@ const handleAddNode = (nodeData) => {
:key="`${file.name}-${filesStore.activeFile}`" :key="`${file.name}-${filesStore.activeFile}`"
:label="file.label" :label="file.label"
:name="file.name.toString()" :name="file.name.toString()"
> />
<div id="main-container" :style="{ height: contentHeight, overflow: 'auto' }">
<!-- 流程图编辑器 -->
<FlowEditor
:ref="(el) => { if (el) flowEditorRefs[file.name] = el }"
:height="contentHeight"
/>
</div>
</el-tab-pane>
</el-tabs> </el-tabs>
<div id="main-container" :style="{ height: contentHeight, overflow: 'auto' }">
<FlowEditor
ref="flowEditorRef"
:height="contentHeight"
:nodes="filesStore.activeFileNodes"
:edges="filesStore.activeFileEdges"
:viewport="filesStore.getFileViewport(filesStore.activeFile)"
:key="filesStore.activeFile"
/>
</div>
</div> </div>
</div> </div>
<DialogManager /> <DialogManager />
</div> </div>
</template> </template>

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, shallowRef, markRaw, onUnmounted } from 'vue'; import { ref, onMounted, shallowRef, markRaw, onUnmounted, watch, nextTick } from 'vue';
import { VueFlow, useVueFlow, Panel } from '@vue-flow/core'; import { VueFlow, useVueFlow, Panel } from '@vue-flow/core';
import { Background } from '@vue-flow/background'; import { Background } from '@vue-flow/background';
import { Controls } from '@vue-flow/controls'; import { Controls } from '@vue-flow/controls';
@@ -14,13 +14,15 @@ import ImageNode from './nodes/common/ImageNode.vue';
import TextNode from './nodes/common/TextNode.vue'; import TextNode from './nodes/common/TextNode.vue';
import useDragAndDrop from '@/ts/useDnD'; import useDragAndDrop from '@/ts/useDnD';
import { useFilesStore } from '@/ts/useStore'; import { useFilesStore } from '@/ts/useStore';
import type { Node, Edge, ViewportTransform } from '@vue-flow/core';
const props = defineProps({ const props = defineProps<{
height: { height: string;
type: String, nodes: Node[];
default: '100%' edges: Edge[];
} viewport: ViewportTransform;
}); }>();
const emit = defineEmits(['save-viewport', 'request-viewport']);
// 获取文件 store // 获取文件 store
const filesStore = useFilesStore(); const filesStore = useFilesStore();
@@ -35,12 +37,25 @@ const nodeTypes = shallowRef({
}); });
// 使用VueFlow的API // 使用VueFlow的API
const { onNodesChange, onEdgesChange, onConnect, addNodes, setTransform, getViewport, updateNode } = useVueFlow({ const { nodes, edges, setNodes, setEdges, setTransform, getViewport, onNodesChange, onEdgesChange, onConnect, addNodes, updateNode } = useVueFlow({
nodes: filesStore.activeFileNodes, nodes: props.nodes,
edges: filesStore.activeFileEdges, edges: props.edges,
nodeTypes: nodeTypes.value nodeTypes: nodeTypes.value
}); });
// 监听 viewport 变化,重绘视图
watch(
() => props.viewport,
(newViewport) => {
setTransform(newViewport);
},
{ immediate: true }
);
onMounted(() => {
setTransform(props.viewport);
});
// 监听节点变化 // 监听节点变化
const handleNodesChange = (changes) => { const handleNodesChange = (changes) => {
// 更新 store 中的节点 // 更新 store 中的节点
@@ -170,6 +185,22 @@ onUnmounted(() => {
// 移除事件监听 // 移除事件监听
// document.removeEventListener('click', handleClickOutside); // document.removeEventListener('click', handleClickOutside);
}); });
const lastActiveFile = ref(filesStore.activeFile);
const flowEditorRef = ref();
watch(
() => filesStore.activeFile,
(newVal, oldVal) => {
// 切换前保存旧 tab 的 viewport
if (oldVal && flowEditorRef.value && flowEditorRef.value.getViewport) {
const viewport = flowEditorRef.value.getViewport();
filesStore.updateFileViewport(oldVal, viewport);
}
lastActiveFile.value = newVal;
}
);
</script> </script>
<template> <template>

View File

@@ -1,6 +1,6 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { ref, computed } from 'vue'; import { ref, computed } from 'vue';
import type { Edge, Node } from '@vue-flow/core'; import type { Edge, Node, ViewportTransform } from '@vue-flow/core';
// 文件相关的类型定义 // 文件相关的类型定义
interface FileGroup { interface FileGroup {
@@ -17,6 +17,7 @@ interface FlowFile {
groups: FileGroup[]; groups: FileGroup[];
nodes?: Node[]; nodes?: Node[];
edges?: Edge[]; edges?: Edge[];
viewport?: { x: number; y: number; zoom: number };
} }
export const useFilesStore = defineStore('files', () => { export const useFilesStore = defineStore('files', () => {
@@ -137,6 +138,21 @@ export const useFilesStore = defineStore('files', () => {
file.nodes = nodes; file.nodes = nodes;
}; };
// 更新文件的 viewport
const updateFileViewport = (fileName: string, viewport: { x: number; y: number; zoom: number }) => {
const file = fileList.value.find(f => f.name === fileName);
if (file) file.viewport = viewport;
};
const getFileViewport = (fileName: string): ViewportTransform => {
const file = fileList.value.find(f => f.name === fileName);
const v = file?.viewport;
if (v && typeof v.x === 'number' && typeof v.y === 'number' && typeof v.zoom === 'number') {
return v as ViewportTransform;
}
return { x: 0, y: 0, zoom: 1 };
};
return { return {
fileList, fileList,
activeFile, activeFile,
@@ -152,5 +168,7 @@ export const useFilesStore = defineStore('files', () => {
removeEdge, removeEdge,
updateNodePosition, updateNodePosition,
updateNodesOrder, updateNodesOrder,
updateFileViewport,
getFileViewport,
}; };
}); });