mirror of
https://github.com/Powerful-517/yys-editor.git
synced 2026-03-05 06:55:26 +00:00
feat: add flow capability levels and plugin injection API
This commit is contained in:
@@ -41,7 +41,6 @@ import { ref, computed, watch, onMounted, onBeforeUnmount, provide } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import LogicFlow from '@logicflow/core'
|
||||
import '@logicflow/core/lib/style/index.css'
|
||||
import { Snapshot, MiniMap, Control } from '@logicflow/extension'
|
||||
import '@logicflow/extension/lib/style/index.css'
|
||||
|
||||
import FlowEditor from './components/flow/FlowEditor.vue'
|
||||
@@ -49,13 +48,13 @@ import Toolbar from './components/Toolbar.vue'
|
||||
import ComponentsPanel from './components/flow/ComponentsPanel.vue'
|
||||
import { useFilesStore } from '@/ts/useStore'
|
||||
import { setLogicFlowInstance, destroyLogicFlowInstance, getLogicFlowInstance } from '@/ts/useLogicFlow'
|
||||
import { register } from '@logicflow/vue-node-registry'
|
||||
import ImageNode from './components/flow/nodes/common/ImageNode.vue'
|
||||
import AssetSelectorNode from './components/flow/nodes/common/AssetSelectorNode.vue'
|
||||
import TextNode from './components/flow/nodes/common/TextNode.vue'
|
||||
import TextNodeModel from './components/flow/nodes/common/TextNodeModel'
|
||||
import VectorNode from './components/flow/nodes/common/VectorNode.vue'
|
||||
import VectorNodeModel from './components/flow/nodes/common/VectorNodeModel'
|
||||
import {
|
||||
registerFlowNodes,
|
||||
resolveFlowPlugins,
|
||||
type FlowCapabilityLevel,
|
||||
type FlowNodeRegistration,
|
||||
type FlowPlugin
|
||||
} from './flowRuntime'
|
||||
|
||||
// 类型定义
|
||||
export interface GraphData {
|
||||
@@ -92,12 +91,15 @@ export interface EditorConfig {
|
||||
const props = withDefaults(defineProps<{
|
||||
data?: GraphData
|
||||
mode?: 'preview' | 'edit'
|
||||
capability?: FlowCapabilityLevel
|
||||
width?: string | number
|
||||
height?: string | number
|
||||
showToolbar?: boolean
|
||||
showPropertyPanel?: boolean
|
||||
showComponentPanel?: boolean
|
||||
config?: EditorConfig
|
||||
plugins?: FlowPlugin[]
|
||||
nodeRegistrations?: FlowNodeRegistration[]
|
||||
}>(), {
|
||||
mode: 'edit',
|
||||
width: '100%',
|
||||
@@ -132,6 +134,13 @@ const previewContainerRef = ref<HTMLElement | null>(null)
|
||||
const previewLf = ref<LogicFlow | null>(null)
|
||||
|
||||
// Computed
|
||||
const effectiveCapability = computed<FlowCapabilityLevel>(() => {
|
||||
if (props.capability) {
|
||||
return props.capability
|
||||
}
|
||||
return props.mode === 'preview' ? 'render-only' : 'interactive'
|
||||
})
|
||||
|
||||
const containerStyle = computed(() => ({
|
||||
width: typeof props.width === 'number' ? `${props.width}px` : props.width,
|
||||
height: typeof props.height === 'number' ? `${props.height}px` : props.height
|
||||
@@ -150,10 +159,20 @@ const contentHeight = computed(() => {
|
||||
return containerHeight.value
|
||||
})
|
||||
|
||||
const destroyPreviewMode = () => {
|
||||
if (previewLf.value) {
|
||||
previewLf.value.destroy()
|
||||
previewLf.value = null
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化预览模式的 LogicFlow
|
||||
const initPreviewMode = () => {
|
||||
if (!previewContainerRef.value) return
|
||||
|
||||
destroyPreviewMode()
|
||||
const isRenderOnly = effectiveCapability.value === 'render-only'
|
||||
|
||||
// 创建 LogicFlow 实例(只读模式)
|
||||
previewLf.value = new LogicFlow({
|
||||
container: previewContainerRef.value,
|
||||
@@ -161,36 +180,19 @@ const initPreviewMode = () => {
|
||||
height: previewContainerRef.value.offsetHeight,
|
||||
grid: false,
|
||||
keyboard: {
|
||||
enabled: false
|
||||
enabled: !isRenderOnly
|
||||
},
|
||||
// 禁用所有交互
|
||||
isSilentMode: true,
|
||||
stopScrollGraph: true,
|
||||
stopZoomGraph: true,
|
||||
stopMoveGraph: true,
|
||||
adjustNodePosition: false,
|
||||
plugins: [Snapshot, MiniMap, Control]
|
||||
// render-only 模式禁用所有交互能力
|
||||
isSilentMode: isRenderOnly,
|
||||
stopScrollGraph: isRenderOnly,
|
||||
stopZoomGraph: isRenderOnly,
|
||||
stopMoveGraph: isRenderOnly,
|
||||
adjustNodePosition: !isRenderOnly,
|
||||
plugins: resolveFlowPlugins(effectiveCapability.value, props.plugins)
|
||||
})
|
||||
|
||||
// 注册自定义节点(必须在 LogicFlow 实例创建后)
|
||||
register({
|
||||
type: 'imageNode',
|
||||
component: ImageNode
|
||||
}, previewLf.value)
|
||||
register({
|
||||
type: 'assetSelector',
|
||||
component: AssetSelectorNode
|
||||
}, previewLf.value)
|
||||
register({
|
||||
type: 'textNode',
|
||||
component: TextNode,
|
||||
model: TextNodeModel
|
||||
}, previewLf.value)
|
||||
register({
|
||||
type: 'vectorNode',
|
||||
component: VectorNode,
|
||||
model: VectorNodeModel
|
||||
}, previewLf.value)
|
||||
// 注册节点(支持外部注入)
|
||||
registerFlowNodes(previewLf.value, props.nodeRegistrations)
|
||||
|
||||
// 渲染数据
|
||||
if (props.data) {
|
||||
@@ -257,9 +259,23 @@ watch(() => props.mode, (newMode) => {
|
||||
setTimeout(() => {
|
||||
initPreviewMode()
|
||||
}, 100)
|
||||
} else {
|
||||
destroyPreviewMode()
|
||||
}
|
||||
})
|
||||
|
||||
watch(
|
||||
[() => props.capability, () => props.plugins, () => props.nodeRegistrations],
|
||||
() => {
|
||||
if (props.mode === 'preview') {
|
||||
setTimeout(() => {
|
||||
initPreviewMode()
|
||||
}, 0)
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
if (props.mode === 'preview') {
|
||||
@@ -277,10 +293,7 @@ onMounted(() => {
|
||||
|
||||
// 清理
|
||||
onBeforeUnmount(() => {
|
||||
if (previewLf.value) {
|
||||
previewLf.value.destroy()
|
||||
previewLf.value = null
|
||||
}
|
||||
destroyPreviewMode()
|
||||
destroyLogicFlowInstance()
|
||||
})
|
||||
</script>
|
||||
|
||||
63
src/flowRuntime.ts
Normal file
63
src/flowRuntime.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import type LogicFlow from '@logicflow/core'
|
||||
import { Menu, Label, Snapshot, SelectionSelect, MiniMap, Control } from '@logicflow/extension'
|
||||
import { register } from '@logicflow/vue-node-registry'
|
||||
|
||||
import ImageNode from './components/flow/nodes/common/ImageNode.vue'
|
||||
import AssetSelectorNode from './components/flow/nodes/common/AssetSelectorNode.vue'
|
||||
import TextNode from './components/flow/nodes/common/TextNode.vue'
|
||||
import TextNodeModel from './components/flow/nodes/common/TextNodeModel'
|
||||
import VectorNode from './components/flow/nodes/common/VectorNode.vue'
|
||||
import VectorNodeModel from './components/flow/nodes/common/VectorNodeModel'
|
||||
|
||||
export type FlowCapabilityLevel = 'render-only' | 'interactive'
|
||||
|
||||
export interface FlowNodeRegistration {
|
||||
type: string
|
||||
component?: any
|
||||
model?: any
|
||||
}
|
||||
|
||||
export type FlowPlugin = any
|
||||
|
||||
const DEFAULT_FLOW_NODES: FlowNodeRegistration[] = [
|
||||
{ type: 'imageNode', component: ImageNode },
|
||||
{ type: 'assetSelector', component: AssetSelectorNode },
|
||||
{ type: 'textNode', component: TextNode, model: TextNodeModel },
|
||||
{ type: 'vectorNode', component: VectorNode, model: VectorNodeModel }
|
||||
]
|
||||
|
||||
const FLOW_PLUGIN_PRESETS: Record<FlowCapabilityLevel, FlowPlugin[]> = {
|
||||
'render-only': [Snapshot],
|
||||
interactive: [Menu, Label, Snapshot, SelectionSelect, MiniMap, Control]
|
||||
}
|
||||
|
||||
export function getFlowPluginsByCapability(capability: FlowCapabilityLevel): FlowPlugin[] {
|
||||
return [...FLOW_PLUGIN_PRESETS[capability]]
|
||||
}
|
||||
|
||||
export function resolveFlowPlugins(
|
||||
capability: FlowCapabilityLevel,
|
||||
plugins?: FlowPlugin[]
|
||||
): FlowPlugin[] {
|
||||
if (Array.isArray(plugins) && plugins.length > 0) {
|
||||
return plugins
|
||||
}
|
||||
return getFlowPluginsByCapability(capability)
|
||||
}
|
||||
|
||||
export function getDefaultFlowNodes(): FlowNodeRegistration[] {
|
||||
return [...DEFAULT_FLOW_NODES]
|
||||
}
|
||||
|
||||
export function resolveFlowNodes(nodes?: FlowNodeRegistration[]): FlowNodeRegistration[] {
|
||||
if (Array.isArray(nodes) && nodes.length > 0) {
|
||||
return nodes
|
||||
}
|
||||
return getDefaultFlowNodes()
|
||||
}
|
||||
|
||||
export function registerFlowNodes(lfInstance: LogicFlow, nodes?: FlowNodeRegistration[]) {
|
||||
const registrations = resolveFlowNodes(nodes)
|
||||
registrations.forEach((registration) => register(registration, lfInstance))
|
||||
}
|
||||
|
||||
@@ -10,3 +10,4 @@ export default YysEditorEmbed
|
||||
|
||||
// 类型导出
|
||||
export * from './YysEditorEmbed.vue'
|
||||
export * from './flowRuntime'
|
||||
|
||||
Reference in New Issue
Block a user