画布节点创建删除
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="canvas-test">
|
<div class="canvas-test">
|
||||||
<div class="canvas-main" ref="canvasMain" @mousedown="onMouseDown" @wheel="onMouseWheel">
|
<!-- <div class="canvas-main" ref="canvasMain" @mousedown="onMouseDown" @wheel="onMouseWheel">
|
||||||
<div
|
<div
|
||||||
class="canvas-content"
|
class="canvas-content"
|
||||||
:style="{
|
:style="{
|
||||||
@@ -23,11 +23,13 @@
|
|||||||
|
|
||||||
<card type="to-3view" />
|
<card type="to-3view" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
|
<flow-canvas />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import flowCanvas from './FlowCanvas/flow-canvas.vue'
|
||||||
import card from './FlowCanvas/components/cards/index.vue'
|
import card from './FlowCanvas/components/cards/index.vue'
|
||||||
import { computed, ref, markRaw, onMounted, reactive, nextTick } from 'vue'
|
import { computed, ref, markRaw, onMounted, reactive, nextTick } from 'vue'
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
|
|||||||
@@ -11,26 +11,31 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref, inject } from 'vue'
|
||||||
|
import { NODE_DATATYPE } from '../../tools/index.d'
|
||||||
|
const nodeManager = inject('nodeManager') as any
|
||||||
|
const props = defineProps({
|
||||||
|
node: { required: true, type: Object }
|
||||||
|
})
|
||||||
const list = ref([
|
const list = ref([
|
||||||
{
|
{
|
||||||
type: 'to-real-style',
|
type: NODE_DATATYPE.TO_REAL_STYLE,
|
||||||
title: 'To Real Style'
|
title: 'To Real Style'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'surface-edit',
|
type: NODE_DATATYPE.SURFACE_EDIT,
|
||||||
title: 'Surface Edit'
|
title: 'Surface Edit'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'scene-composition',
|
type: NODE_DATATYPE.SCENE_COMPOSITION,
|
||||||
title: 'Scene Composition'
|
title: 'Scene Composition'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'color-palette',
|
type: NODE_DATATYPE.COLOR_PALETTE,
|
||||||
title: 'Color Palette'
|
title: 'Color Palette'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'to-3view',
|
type: NODE_DATATYPE.TO_3VIEW,
|
||||||
title: 'To 3-View'
|
title: 'To 3-View'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -39,7 +44,10 @@
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
const onClickItem = (v) => {
|
const onClickItem = (v) => {
|
||||||
console.log(v.type)
|
const id = props.node.id
|
||||||
|
if (!id) return
|
||||||
|
nodeManager.deleteNode(id)
|
||||||
|
nodeManager.createCardNode({ data: { type: v.type } })
|
||||||
}
|
}
|
||||||
defineExpose({})
|
defineExpose({})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<span>{{ currentComponent?.title }}</span>
|
<span>{{ currentComponent?.title }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="body" @mousedown.stop>
|
<div class="body" @mousedown.stop>
|
||||||
<component :is="currentComponent?.component" ref="componentRef" />
|
<component :is="currentComponent?.component" ref="componentRef" v-bind="attrs" />
|
||||||
</div>
|
</div>
|
||||||
<div class="footer" @mousedown.stop v-if="!currentComponent?.hideFooter">
|
<div class="footer" @mousedown.stop v-if="!currentComponent?.hideFooter">
|
||||||
<button @click="onGenerateClick">
|
<button @click="onGenerateClick">
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, markRaw, onMounted } from 'vue'
|
import { computed, ref, useAttrs } from 'vue'
|
||||||
import CardsSelect from './cards-select.vue'
|
import CardsSelect from './cards-select.vue'
|
||||||
import ToRealStyle from './to-real-style.vue'
|
import ToRealStyle from './to-real-style.vue'
|
||||||
import SurfaceEdit from './surface-edit.vue'
|
import SurfaceEdit from './surface-edit.vue'
|
||||||
@@ -103,6 +103,7 @@
|
|||||||
default: 'to-real-style'
|
default: 'to-real-style'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const attrs = useAttrs()
|
||||||
const currentComponent = computed(() => {
|
const currentComponent = computed(() => {
|
||||||
return components.find((item) => item.type === props.type)
|
return components.find((item) => item.type === props.type)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,12 +1,31 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Handle, Position } from '@vue-flow/core'
|
import { Handle, Position } from '@vue-flow/core'
|
||||||
import { ref } from 'vue'
|
import { NODE_DATATYPE } from '../tools/index.d'
|
||||||
|
import { computed } from 'vue'
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
type: {
|
type: {
|
||||||
type: String as () => 'InputNode' | 'SecondaryNode',
|
type: String as () => 'InputNode' | 'SecondaryNode',
|
||||||
default: 'InputNode'
|
default: 'InputNode'
|
||||||
|
},
|
||||||
|
node: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
stateManager: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const nodes = computed(() => props.stateManager.nodes.value)
|
||||||
|
const firstNode = computed(() => nodes.value[0])
|
||||||
|
const lastNode = computed(() => nodes.value[nodes.value.length - 1])
|
||||||
|
const notAddNodeTypes = [NODE_DATATYPE.CARDS_SELECT]
|
||||||
|
const isAdd = computed(
|
||||||
|
() => props.node.id === lastNode.value.id && !notAddNodeTypes.includes(props.node.data.type)
|
||||||
|
)
|
||||||
|
const onAdd = () => {
|
||||||
|
props.stateManager.nodeManager.createCardsSelect()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -21,7 +40,7 @@
|
|||||||
<div class="item">
|
<div class="item">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="add" @mousedown.stop>
|
<div class="add" @mousedown.stop v-if="isAdd" @click="onAdd">
|
||||||
<svg-icon name="add" size="14" size-unit="px" />
|
<svg-icon name="add" size="14" size-unit="px" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,13 +12,21 @@
|
|||||||
@viewport-change="(e) => eventManager.handleViewportChange(e)"
|
@viewport-change="(e) => eventManager.handleViewportChange(e)"
|
||||||
>
|
>
|
||||||
<template #node-InputNode="nodeProps">
|
<template #node-InputNode="nodeProps">
|
||||||
<node type="InputNode" :isAdd="lastNode.id === nodeProps.id">
|
<node type="InputNode" :stateManager="stateManager" :node="nodeProps">
|
||||||
<component :is="nodeProps.data.component" v-bind="nodeProps.data" />
|
<component
|
||||||
|
:is="nodeProps.data.component"
|
||||||
|
:node="nodeProps"
|
||||||
|
v-bind="nodeProps.data"
|
||||||
|
/>
|
||||||
</node>
|
</node>
|
||||||
</template>
|
</template>
|
||||||
<template #node-SecondaryNode="nodeProps">
|
<template #node-SecondaryNode="nodeProps">
|
||||||
<node type="SecondaryNode" :isAdd="lastNode.id === nodeProps.id">
|
<node type="SecondaryNode" :stateManager="stateManager" :node="nodeProps">
|
||||||
<component :is="nodeProps.data.component" v-bind="nodeProps.data" />
|
<component
|
||||||
|
:is="nodeProps.data.component"
|
||||||
|
:node="nodeProps"
|
||||||
|
v-bind="nodeProps.data"
|
||||||
|
/>
|
||||||
</node>
|
</node>
|
||||||
</template>
|
</template>
|
||||||
</VueFlow>
|
</VueFlow>
|
||||||
@@ -39,9 +47,7 @@
|
|||||||
import headerTools from './components/header-tools.vue'
|
import headerTools from './components/header-tools.vue'
|
||||||
import zoom from '../components/zoom.vue'
|
import zoom from '../components/zoom.vue'
|
||||||
import node from './components/node.vue'
|
import node from './components/node.vue'
|
||||||
import card from './components/cards/index.vue'
|
import { computed, ref, markRaw, onMounted, reactive, nextTick, provide } from 'vue'
|
||||||
import resultImage from './components/result/result-image.vue'
|
|
||||||
import { computed, ref, markRaw, onMounted, reactive, nextTick } from 'vue'
|
|
||||||
|
|
||||||
// 管理器
|
// 管理器
|
||||||
import { StateManager } from './manager/StateManager'
|
import { StateManager } from './manager/StateManager'
|
||||||
@@ -53,9 +59,8 @@
|
|||||||
|
|
||||||
// 状态管理器
|
// 状态管理器
|
||||||
const stateManager = new StateManager({ vueFlow })
|
const stateManager = new StateManager({ vueFlow })
|
||||||
const nodes = computed(() => stateManager.nodes.value)
|
const nodes = computed(() => stateManager.nodes_.value)
|
||||||
const edges = computed(() => stateManager.edges.value)
|
const edges = computed(() => stateManager.edges.value)
|
||||||
const lastNode = computed(() => nodes.value[nodes.value.length - 1])
|
|
||||||
// 事件管理器
|
// 事件管理器
|
||||||
const eventManager = new EventManager({ stateManager, vueFlow })
|
const eventManager = new EventManager({ stateManager, vueFlow })
|
||||||
// 流程管理器
|
// 流程管理器
|
||||||
@@ -68,13 +73,11 @@
|
|||||||
nodeManager
|
nodeManager
|
||||||
})
|
})
|
||||||
|
|
||||||
nodeManager.createNode({
|
provide('stateManager', stateManager)
|
||||||
type: 'InputNode',
|
provide('eventManager', eventManager)
|
||||||
class: 'custom-node start',
|
provide('flowManager', flowManager)
|
||||||
component: resultImage,
|
provide('nodeManager', nodeManager)
|
||||||
data: {}
|
provide('nodeManager', nodeManager)
|
||||||
})
|
|
||||||
|
|
||||||
const { fitView } = useVueFlow()
|
const { fitView } = useVueFlow()
|
||||||
const { layout } = useLayout()
|
const { layout } = useLayout()
|
||||||
const index = ref(0)
|
const index = ref(0)
|
||||||
@@ -88,7 +91,7 @@
|
|||||||
direction
|
direction
|
||||||
)
|
)
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
fitView()
|
fitView({ maxZoom: 1 })
|
||||||
})
|
})
|
||||||
}, 0)
|
}, 0)
|
||||||
}
|
}
|
||||||
@@ -96,22 +99,9 @@
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.vueFlow = vueFlow
|
window.vueFlow = vueFlow
|
||||||
window.nodes = nodes
|
window.nodes = nodes
|
||||||
// window.addaaaaa = () => {
|
nodeManager.createResultNode()
|
||||||
// const lastNode = vueFlow.value.getNode(nodes.value[nodes.value.length - 1].id)
|
// nodeManager.createCardsSelect()
|
||||||
// const width = lastNode.dimensions.width
|
// nodeManager.createResultNode()
|
||||||
// const x = lastNode.position.x
|
|
||||||
// const y = lastNode.position.y
|
|
||||||
// nodes.value.push({
|
|
||||||
// id: nodes.value.length + 1 + '',
|
|
||||||
// type: 'SecondaryNode',
|
|
||||||
// class: 'custom-node',
|
|
||||||
// data: { component: card, type_: 'to-3d-model' },
|
|
||||||
// position: {
|
|
||||||
// x: width + x + 50,
|
|
||||||
// y: y
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
|
|||||||
@@ -9,14 +9,12 @@ export class FlowManager {
|
|||||||
this.stateManager.zoom.value = zoom
|
this.stateManager.zoom.value = zoom
|
||||||
this.vueFlow.value.zoomTo(zoom)
|
this.vueFlow.value.zoomTo(zoom)
|
||||||
}
|
}
|
||||||
handleNodeDragStop(e: any) {
|
getLastNode() {
|
||||||
const { node } = e
|
const lastNode = this.stateManager.getLastNode()
|
||||||
const { id, position } = node
|
if (lastNode?.id) {
|
||||||
this.stateManager.nodes.value.forEach((item) => {
|
return this.vueFlow.value.getNode(lastNode.id)
|
||||||
if (item.id === id) {
|
}
|
||||||
item.position.x = position.x
|
return null;
|
||||||
item.position.y = position.y
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
import { createId } from '../../tools/tools'
|
import { createId } from '../../tools/tools'
|
||||||
|
import card from '../components/cards/index.vue'
|
||||||
|
import resultImage from '../components/result/result-image.vue'
|
||||||
|
import { NODE_DATATYPE } from '../tools/index.d'
|
||||||
interface NodeOptions {
|
interface NodeOptions {
|
||||||
id?: string
|
id?: string
|
||||||
type: "InputNode" | "SecondaryNode"
|
|
||||||
class?: string
|
|
||||||
position?: { x: number, y: number }
|
position?: { x: number, y: number }
|
||||||
component: any
|
positionX?: number
|
||||||
|
positionY?: number
|
||||||
|
component?: any
|
||||||
data?: object
|
data?: object
|
||||||
}
|
}// 不可传入type class (内部使用)
|
||||||
|
|
||||||
export class NodeManager {
|
export class NodeManager {
|
||||||
stateManager: any
|
stateManager: any
|
||||||
vueFlow: any
|
vueFlow: any
|
||||||
@@ -14,23 +18,60 @@ export class NodeManager {
|
|||||||
this.stateManager = options.stateManager;
|
this.stateManager = options.stateManager;
|
||||||
this.vueFlow = options.vueFlow
|
this.vueFlow = options.vueFlow
|
||||||
}
|
}
|
||||||
nodes: [
|
|
||||||
|
|
||||||
]
|
/** 删除节点 */
|
||||||
|
deleteNode(id: string) {
|
||||||
|
this.stateManager.nodes.value = this.stateManager.nodes.value.filter((node: any) => node.id !== id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 创建节点 */
|
||||||
createNode(options: NodeOptions) {
|
createNode(options: NodeOptions) {
|
||||||
|
const lastNode = this.stateManager.flowManager.getLastNode();
|
||||||
const id = options.id || createId()
|
const id = options.id || createId()
|
||||||
const type = options.type || 'InputNode'
|
const positionX = options.positionX || 0
|
||||||
const class_ = options.class || 'custom-node'
|
const positionY = options.positionY || 0
|
||||||
const position = options.position || { x: 0, y: 0 }
|
const position = options.position ||
|
||||||
|
!lastNode ? { x: positionX, y: positionY } :
|
||||||
|
{ x: lastNode.position.x + lastNode.dimensions.width + 50 + positionX, y: lastNode.position.y + positionY }
|
||||||
const data = options.data || {}
|
const data = options.data || {}
|
||||||
data['component'] = options.component
|
data['component'] = options.component
|
||||||
this.stateManager.nodes.value.push({
|
const options_ = {
|
||||||
id,
|
id,
|
||||||
type,
|
|
||||||
class: class_,
|
|
||||||
position,
|
position,
|
||||||
data
|
data
|
||||||
})
|
}
|
||||||
console.log(this.stateManager.nodes.value)
|
this.stateManager.nodes.value.push(options_)
|
||||||
|
return options_;
|
||||||
|
}
|
||||||
|
/** 创建结果节点 */
|
||||||
|
createResultNode(options?: NodeOptions) {
|
||||||
|
const options_ = {
|
||||||
|
component: resultImage,
|
||||||
|
data: {
|
||||||
|
type: NODE_DATATYPE.RESULT_IMAGE,
|
||||||
|
},
|
||||||
|
...(options ? options : {}),
|
||||||
|
}
|
||||||
|
return this.createNode(options_)
|
||||||
|
}
|
||||||
|
/** 创建卡片选择节点 */
|
||||||
|
createCardsSelect(options?: NodeOptions) {
|
||||||
|
const options_ = {
|
||||||
|
component: card,
|
||||||
|
positionY: 50,
|
||||||
|
data: {
|
||||||
|
type: NODE_DATATYPE.CARDS_SELECT,
|
||||||
|
},
|
||||||
|
...(options ? options : {}),
|
||||||
|
}
|
||||||
|
return this.createNode(options_)
|
||||||
|
}
|
||||||
|
/** 创建卡片节点 */
|
||||||
|
createCardNode(options?: NodeOptions) {
|
||||||
|
const options_ = {
|
||||||
|
component: card,
|
||||||
|
...(options ? options : {}),
|
||||||
|
}
|
||||||
|
return this.createNode(options_)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { ref, computed } from "vue";
|
import { ref, computed } from "vue";
|
||||||
import card from '../components/cards/index.vue'
|
|
||||||
|
|
||||||
export class StateManager {
|
export class StateManager {
|
||||||
vueFlow: any
|
vueFlow: any
|
||||||
nodes: any
|
nodes: any
|
||||||
|
nodes_: any
|
||||||
edges: any
|
edges: any
|
||||||
zoom: any
|
zoom: any
|
||||||
// 管理器
|
// 管理器
|
||||||
@@ -19,55 +19,6 @@ export class StateManager {
|
|||||||
constructor(options) {
|
constructor(options) {
|
||||||
this.vueFlow = options.vueFlow
|
this.vueFlow = options.vueFlow
|
||||||
this.nodes = ref<any[]>([
|
this.nodes = ref<any[]>([
|
||||||
// {
|
|
||||||
// id: '1',
|
|
||||||
// type: 'InputNode',
|
|
||||||
// class: 'custom-node start',
|
|
||||||
// position: { x: 0, y: 0 },
|
|
||||||
// data: { component: card, type: 'to-real-style' }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '2',
|
|
||||||
// type: 'SecondaryNode',
|
|
||||||
// class: 'custom-node',
|
|
||||||
// position: { x: 0, y: 0 },
|
|
||||||
// data: { component: card, type: 'scene-composition' }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '3',
|
|
||||||
// type: 'SecondaryNode',
|
|
||||||
// class: 'custom-node',
|
|
||||||
// position: { x: 0, y: 0 },
|
|
||||||
// data: { component: card, type: 'color-palette' }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '4',
|
|
||||||
// type: 'SecondaryNode',
|
|
||||||
// class: 'custom-node',
|
|
||||||
// position: { x: 0, y: 0 },
|
|
||||||
// data: { component: card, type: 'to-video' }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '5',
|
|
||||||
// type: 'SecondaryNode',
|
|
||||||
// class: 'custom-node',
|
|
||||||
// position: { x: 0, y: 0 },
|
|
||||||
// data: { component: card, type: 'to-3d-model' }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '6',
|
|
||||||
// type: 'SecondaryNode',
|
|
||||||
// class: 'custom-node',
|
|
||||||
// position: { x: 0, y: 0 },
|
|
||||||
// data: { component: card, type: 'to-cad' }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '7',
|
|
||||||
// type: 'SecondaryNode',
|
|
||||||
// class: 'custom-node',
|
|
||||||
// position: { x: 0, y: 0 },
|
|
||||||
// data: { component: card, type: 'add-print' }
|
|
||||||
// },
|
|
||||||
// {
|
// {
|
||||||
// id: '8',
|
// id: '8',
|
||||||
// type: 'SecondaryNode',
|
// type: 'SecondaryNode',
|
||||||
@@ -76,6 +27,21 @@ export class StateManager {
|
|||||||
// data: { component: card, type: 'edit-material' }
|
// data: { component: card, type: 'edit-material' }
|
||||||
// }
|
// }
|
||||||
]);
|
]);
|
||||||
|
this.nodes_ = computed(() => {
|
||||||
|
return this.nodes.value.map((node, index) => {
|
||||||
|
const obj = {
|
||||||
|
...node,
|
||||||
|
}
|
||||||
|
if (index === 0) {
|
||||||
|
obj.class = 'custom-node start';
|
||||||
|
obj.type = 'InputNode';
|
||||||
|
} else {
|
||||||
|
obj.class = 'custom-node';
|
||||||
|
obj.type = 'SecondaryNode';
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
this.edges = computed(() => {
|
this.edges = computed(() => {
|
||||||
const arr = []
|
const arr = []
|
||||||
@@ -96,6 +62,7 @@ export class StateManager {
|
|||||||
|
|
||||||
this.zoom = ref(1)
|
this.zoom = ref(1)
|
||||||
}
|
}
|
||||||
|
getLastNode() {
|
||||||
|
return this.nodes.value[this.nodes.value.length - 1]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/components/Canvas/FlowCanvas/tools/index.d.ts
vendored
Normal file
13
src/components/Canvas/FlowCanvas/tools/index.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* 节点数据类型
|
||||||
|
*/
|
||||||
|
export const NODE_DATATYPE = {
|
||||||
|
RESULT_IMAGE: 'result-image',
|
||||||
|
CARDS_SELECT: 'cards-select',
|
||||||
|
TO_REAL_STYLE: 'to-real-style',
|
||||||
|
SURFACE_EDIT: 'surface-edit',
|
||||||
|
SCENE_COMPOSITION: 'scene-composition',
|
||||||
|
COLOR_PALETTE: 'color-palette',
|
||||||
|
TO_3VIEW: 'to-3view',
|
||||||
|
TO_3D_MODEL: 'to-3d-model',
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user