feat(useThinkFlow): 添加画布状态持久化功能
- 新增本地存储功能,持久化保存节点、边和折叠状态 - 实现配置项的本地存储和恢复 - 添加初始化时从本地存储恢复状态的逻辑 - 在重置功能中增加清除本地存储的操作
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
* - 对外提供:页面与组件可直接调用的状态与动作(expand / deepDive / image / summary 等)
|
* - 对外提供:页面与组件可直接调用的状态与动作(expand / deepDive / image / summary 等)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { computed, reactive, ref, watch, type Ref } from 'vue'
|
import { computed, nextTick, onMounted, reactive, ref, toRaw, watch, type Ref } from 'vue'
|
||||||
import { MarkerType, Position, useVueFlow } from '@vue-flow/core'
|
import { MarkerType, Position, useVueFlow } from '@vue-flow/core'
|
||||||
import { BackgroundVariant } from '@vue-flow/background'
|
import { BackgroundVariant } from '@vue-flow/background'
|
||||||
|
|
||||||
@@ -380,7 +380,11 @@ export function useThinkFlow({ t, locale }: { t: Translate; locale: Ref<string>
|
|||||||
return { nodeIds, edgeIds }
|
return { nodeIds, edgeIds }
|
||||||
})
|
})
|
||||||
|
|
||||||
const config = reactive({
|
const savedConfig = localStorage.getItem('thinkflow_config')
|
||||||
|
const config = reactive(
|
||||||
|
savedConfig
|
||||||
|
? JSON.parse(savedConfig)
|
||||||
|
: {
|
||||||
edgeColor: '#fed7aa',
|
edgeColor: '#fed7aa',
|
||||||
edgeType: 'default',
|
edgeType: 'default',
|
||||||
backgroundVariant: BackgroundVariant.Lines,
|
backgroundVariant: BackgroundVariant.Lines,
|
||||||
@@ -391,9 +395,82 @@ export function useThinkFlow({ t, locale }: { t: Translate; locale: Ref<string>
|
|||||||
snapGrid: [16, 16] as [number, number],
|
snapGrid: [16, 16] as [number, number],
|
||||||
snapToAlignment: true,
|
snapToAlignment: true,
|
||||||
showAlignmentGuides: true
|
showAlignmentGuides: true
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const collapsedNodeIds = ref<string[]>([])
|
const collapsedNodeIds = ref<string[]>([])
|
||||||
|
const isInitialLoad = ref(true)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 画布状态持久化 (Nodes, Edges, Collapsed)
|
||||||
|
*/
|
||||||
|
watch(
|
||||||
|
[() => flowNodes.value, () => flowEdges.value, () => collapsedNodeIds.value],
|
||||||
|
() => {
|
||||||
|
if (isInitialLoad.value) return
|
||||||
|
|
||||||
|
// 使用 toRaw 确保保存的是纯 JS 对象,避免响应式代理带来的序列化问题
|
||||||
|
localStorage.setItem('thinkflow_nodes', JSON.stringify(toRaw(flowNodes.value)))
|
||||||
|
localStorage.setItem('thinkflow_edges', JSON.stringify(toRaw(flowEdges.value)))
|
||||||
|
localStorage.setItem('thinkflow_collapsed', JSON.stringify(toRaw(collapsedNodeIds.value)))
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UI 配置持久化
|
||||||
|
*/
|
||||||
|
watch(
|
||||||
|
() => config,
|
||||||
|
newConfig => {
|
||||||
|
if (isInitialLoad.value) return
|
||||||
|
localStorage.setItem('thinkflow_config', JSON.stringify(toRaw(newConfig)))
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化时从本地存储恢复状态
|
||||||
|
*/
|
||||||
|
onMounted(async () => {
|
||||||
|
const savedNodes = localStorage.getItem('thinkflow_nodes')
|
||||||
|
const savedEdges = localStorage.getItem('thinkflow_edges')
|
||||||
|
const savedCollapsed = localStorage.getItem('thinkflow_collapsed')
|
||||||
|
|
||||||
|
if (savedNodes && savedEdges) {
|
||||||
|
try {
|
||||||
|
const nodes = JSON.parse(savedNodes)
|
||||||
|
const edges = JSON.parse(savedEdges)
|
||||||
|
const collapsed = savedCollapsed ? JSON.parse(savedCollapsed) : []
|
||||||
|
|
||||||
|
if (nodes.length > 0) {
|
||||||
|
// 必须先清空当前可能存在的默认节点(如果有)
|
||||||
|
setNodes([])
|
||||||
|
setEdges([])
|
||||||
|
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
// 恢复节点和边
|
||||||
|
setNodes(nodes)
|
||||||
|
setEdges(edges)
|
||||||
|
collapsedNodeIds.value = collapsed
|
||||||
|
|
||||||
|
// 恢复后自适应一次视图
|
||||||
|
setTimeout(() => {
|
||||||
|
fitView({ padding: 0.2, duration: 800 })
|
||||||
|
}, 150)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to restore ThinkFlow state:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保在所有初始化操作(包括可能的 setNodes)完成后再开启保存
|
||||||
|
await nextTick()
|
||||||
|
setTimeout(() => {
|
||||||
|
isInitialLoad.value = false
|
||||||
|
}, 300)
|
||||||
|
})
|
||||||
|
|
||||||
const isSubtreeCollapsed = (nodeId: string) => collapsedNodeIds.value.includes(nodeId)
|
const isSubtreeCollapsed = (nodeId: string) => collapsedNodeIds.value.includes(nodeId)
|
||||||
|
|
||||||
@@ -1027,6 +1104,13 @@ export function useThinkFlow({ t, locale }: { t: Translate; locale: Ref<string>
|
|||||||
ideaInput.value = ''
|
ideaInput.value = ''
|
||||||
setNodes([])
|
setNodes([])
|
||||||
setEdges([])
|
setEdges([])
|
||||||
|
collapsedNodeIds.value = []
|
||||||
|
|
||||||
|
// 清除本地存储
|
||||||
|
localStorage.removeItem('thinkflow_nodes')
|
||||||
|
localStorage.removeItem('thinkflow_edges')
|
||||||
|
localStorage.removeItem('thinkflow_collapsed')
|
||||||
|
|
||||||
showResetConfirm.value = false
|
showResetConfirm.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user