a
This commit is contained in:
@@ -9,9 +9,6 @@
|
|||||||
size-unit="px"
|
size-unit="px"
|
||||||
/>
|
/>
|
||||||
<span>{{ currentComponent?.title }}</span>
|
<span>{{ currentComponent?.title }}</span>
|
||||||
<!-- <div class="add" @click="emit('add')" @mousedown.stop>
|
|
||||||
<svg-icon name="add" size="14" size-unit="px" />
|
|
||||||
</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="body" @mousedown.stop>
|
<div class="body" @mousedown.stop>
|
||||||
<component :is="currentComponent?.component" ref="componentRef" />
|
<component :is="currentComponent?.component" ref="componentRef" />
|
||||||
@@ -146,24 +143,6 @@
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
> .add {
|
|
||||||
position: absolute;
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
border: 2px solid #fff;
|
|
||||||
bottom: -16px;
|
|
||||||
right: -16px;
|
|
||||||
background-color: #ed8936;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 25px;
|
|
||||||
box-shadow: 0 8px 20px 0 #71809633;
|
|
||||||
cursor: pointer;
|
|
||||||
z-index: 20;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
> .body {
|
> .body {
|
||||||
cursor: initial;
|
cursor: initial;
|
||||||
|
|||||||
@@ -21,16 +21,43 @@
|
|||||||
<div class="item">
|
<div class="item">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="add" @mousedown.stop>
|
||||||
|
<svg-icon name="add" size="14" size-unit="px" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.node {
|
.node {
|
||||||
|
position: relative;
|
||||||
.vue-flow__handle {
|
.vue-flow__handle {
|
||||||
width: 5px;
|
width: 5px;
|
||||||
height: 5px;
|
height: 5px;
|
||||||
top: 50px;
|
top: 50px;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
> .item {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
> .add {
|
||||||
|
position: absolute;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border: 2px solid #fff;
|
||||||
|
// bottom: -16px;
|
||||||
|
right: -16px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
background-color: #ed8936;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 25px;
|
||||||
|
box-shadow: 0 8px 20px 0 #71809633;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 20;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -20,9 +20,6 @@
|
|||||||
class="image"
|
class="image"
|
||||||
src="https://s3-alpha-sig.figma.com/img/ea2f/590e/9638f62a2fc91e31f33db0022db1642c?Expires=1773014400&Key-Pair-Id=APKAQ4GOSFWCW27IBOMQ&Signature=M0B8oJJOk~dGG0aZAqOIocAp7T0LFdJ9FYmCrEZVTCRzYxM6SJRNtYMTX-rTO3Z~s14QINh~o-S41XiZnBv-0zcKjuWot~VVaNHfd0~1LesfNe2KwvCinT~72btFut1pheLnKE-wWCX5ewtonxU77bnw386YPMTqv7DBZzksf2udsJA7NmOYD6~TUG3Q2dWSt~zPH~lkaidscPqpCnCbqzljCEi4RiHY4U3A45l5XypcX2umqn1UaYUFCTqV9471J4qdB6Dg2pcKocdp-7-3s1De6Q~2SmBOrSgDQ~KEADCB2lhKfhxgWmy0lwMvhTd4l90ygVZDWZRABgjHNrGUvg__"
|
src="https://s3-alpha-sig.figma.com/img/ea2f/590e/9638f62a2fc91e31f33db0022db1642c?Expires=1773014400&Key-Pair-Id=APKAQ4GOSFWCW27IBOMQ&Signature=M0B8oJJOk~dGG0aZAqOIocAp7T0LFdJ9FYmCrEZVTCRzYxM6SJRNtYMTX-rTO3Z~s14QINh~o-S41XiZnBv-0zcKjuWot~VVaNHfd0~1LesfNe2KwvCinT~72btFut1pheLnKE-wWCX5ewtonxU77bnw386YPMTqv7DBZzksf2udsJA7NmOYD6~TUG3Q2dWSt~zPH~lkaidscPqpCnCbqzljCEi4RiHY4U3A45l5XypcX2umqn1UaYUFCTqV9471J4qdB6Dg2pcKocdp-7-3s1De6Q~2SmBOrSgDQ~KEADCB2lhKfhxgWmy0lwMvhTd4l90ygVZDWZRABgjHNrGUvg__"
|
||||||
/>
|
/>
|
||||||
<div class="add" @mousedown.stop>
|
|
||||||
<svg-icon name="add" size="14" size-unit="px" />
|
|
||||||
</div>
|
|
||||||
<div class="more" @click="showMenu = !showMenu" @mousedown.stop>
|
<div class="more" @click="showMenu = !showMenu" @mousedown.stop>
|
||||||
<svg-icon name="more" size="24" size-unit="px" color="#C9C9C9" />
|
<svg-icon name="more" size="24" size-unit="px" color="#C9C9C9" />
|
||||||
</div>
|
</div>
|
||||||
@@ -81,6 +78,7 @@
|
|||||||
box-shadow: 0px 15px 21px 0px #0000000d;
|
box-shadow: 0px 15px 21px 0px #0000000d;
|
||||||
padding: 25px 6px;
|
padding: 25px 6px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
background-color: #fff;
|
||||||
> .header {
|
> .header {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -20px;
|
top: -20px;
|
||||||
@@ -97,6 +95,7 @@
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
padding: 0 19px;
|
padding: 0 19px;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
background-color: #fff;
|
||||||
> .icon {
|
> .icon {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -133,26 +132,7 @@
|
|||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
position: relative;
|
position: relative;
|
||||||
> .add {
|
|
||||||
position: absolute;
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
border: 2px solid #fff;
|
|
||||||
// bottom: -16px;
|
|
||||||
right: -16px;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
background-color: #ed8936;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 25px;
|
|
||||||
box-shadow: 0 8px 20px 0 #71809633;
|
|
||||||
cursor: pointer;
|
|
||||||
z-index: 20;
|
|
||||||
}
|
|
||||||
> .more {
|
> .more {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 5px;
|
top: 5px;
|
||||||
|
|||||||
@@ -12,12 +12,12 @@
|
|||||||
@viewport-change="(e) => eventManager.handleViewportChange(e)"
|
@viewport-change="(e) => eventManager.handleViewportChange(e)"
|
||||||
>
|
>
|
||||||
<template #node-InputNode="nodeProps">
|
<template #node-InputNode="nodeProps">
|
||||||
<node type="InputNode">
|
<node type="InputNode" :isAdd="lastNode.id === nodeProps.id">
|
||||||
<component :is="nodeProps.data.component" v-bind="nodeProps.data" />
|
<component :is="nodeProps.data.component" v-bind="nodeProps.data" />
|
||||||
</node>
|
</node>
|
||||||
</template>
|
</template>
|
||||||
<template #node-SecondaryNode="nodeProps">
|
<template #node-SecondaryNode="nodeProps">
|
||||||
<node type="SecondaryNode">
|
<node type="SecondaryNode" :isAdd="lastNode.id === nodeProps.id">
|
||||||
<component :is="nodeProps.data.component" v-bind="nodeProps.data" />
|
<component :is="nodeProps.data.component" v-bind="nodeProps.data" />
|
||||||
</node>
|
</node>
|
||||||
</template>
|
</template>
|
||||||
@@ -40,25 +40,39 @@
|
|||||||
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 card from './components/cards/index.vue'
|
||||||
|
import resultImage from './components/result/result-image.vue'
|
||||||
import { computed, ref, markRaw, onMounted, reactive, nextTick } from 'vue'
|
import { computed, ref, markRaw, onMounted, reactive, nextTick } from 'vue'
|
||||||
|
|
||||||
// 管理器
|
// 管理器
|
||||||
import { StateManager } from './manager/StateManager'
|
import { StateManager } from './manager/StateManager'
|
||||||
import { EventManager } from './manager/EventManager'
|
import { EventManager } from './manager/EventManager'
|
||||||
import { FlowManager } from './manager/FlowManager'
|
import { FlowManager } from './manager/FlowManager'
|
||||||
|
import { NodeManager } from './manager/NodeManager'
|
||||||
|
|
||||||
const vueFlow = ref<any>()
|
const vueFlow = ref<any>()
|
||||||
|
|
||||||
|
// 状态管理器
|
||||||
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 eventManager = new EventManager({
|
const lastNode = computed(() => nodes.value[nodes.value.length - 1])
|
||||||
stateManager,
|
// 事件管理器
|
||||||
vueFlow
|
const eventManager = new EventManager({ stateManager, vueFlow })
|
||||||
|
// 流程管理器
|
||||||
|
const flowManager = new FlowManager({ stateManager, vueFlow })
|
||||||
|
// 节点管理器
|
||||||
|
const nodeManager = new NodeManager({ stateManager, vueFlow })
|
||||||
|
stateManager.setManager({
|
||||||
|
eventManager,
|
||||||
|
flowManager,
|
||||||
|
nodeManager
|
||||||
})
|
})
|
||||||
const flowManager = new FlowManager({
|
|
||||||
stateManager,
|
nodeManager.createNode({
|
||||||
vueFlow
|
type: 'InputNode',
|
||||||
|
class: 'custom-node start',
|
||||||
|
component: resultImage,
|
||||||
|
data: {}
|
||||||
})
|
})
|
||||||
|
|
||||||
const { fitView } = useVueFlow()
|
const { fitView } = useVueFlow()
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
|
import { createId } from '../../tools/tools'
|
||||||
|
interface NodeOptions {
|
||||||
|
id?: string
|
||||||
|
type: "InputNode" | "SecondaryNode"
|
||||||
|
class?: string
|
||||||
|
position?: { x: number, y: number }
|
||||||
|
component: any
|
||||||
|
data?: object
|
||||||
|
}
|
||||||
export class NodeManager {
|
export class NodeManager {
|
||||||
stateManager: any
|
stateManager: any
|
||||||
vueFlow: any
|
vueFlow: any
|
||||||
@@ -6,6 +15,22 @@ export class NodeManager {
|
|||||||
this.vueFlow = options.vueFlow
|
this.vueFlow = options.vueFlow
|
||||||
}
|
}
|
||||||
nodes: [
|
nodes: [
|
||||||
|
|
||||||
]
|
]
|
||||||
|
createNode(options: NodeOptions) {
|
||||||
|
const id = options.id || createId()
|
||||||
|
const type = options.type || 'InputNode'
|
||||||
|
const class_ = options.class || 'custom-node'
|
||||||
|
const position = options.position || { x: 0, y: 0 }
|
||||||
|
const data = options.data || {}
|
||||||
|
data['component'] = options.component
|
||||||
|
this.stateManager.nodes.value.push({
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
class: class_,
|
||||||
|
position,
|
||||||
|
data
|
||||||
|
})
|
||||||
|
console.log(this.stateManager.nodes.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,16 @@ export class StateManager {
|
|||||||
nodes: any
|
nodes: any
|
||||||
edges: any
|
edges: any
|
||||||
zoom: any
|
zoom: any
|
||||||
|
// 管理器
|
||||||
|
eventManager: any
|
||||||
|
flowManager: any
|
||||||
|
nodeManager: any
|
||||||
|
// 设置管理器
|
||||||
|
setManager(options) {
|
||||||
|
options.eventManager && (this.eventManager = options.eventManager)
|
||||||
|
options.flowManager && (this.flowManager = options.flowManager)
|
||||||
|
options.nodeManager && (this.nodeManager = options.nodeManager)
|
||||||
|
}
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
this.vueFlow = options.vueFlow
|
this.vueFlow = options.vueFlow
|
||||||
this.nodes = ref<any[]>([
|
this.nodes = ref<any[]>([
|
||||||
@@ -87,4 +97,5 @@ export class StateManager {
|
|||||||
this.zoom = ref(1)
|
this.zoom = ref(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/components/Canvas/tools/tools.ts
Normal file
5
src/components/Canvas/tools/tools.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export const createId = (before: string = 'node') => {
|
||||||
|
const time = Date.now().toString(36)
|
||||||
|
const random = Math.random().toString(36).substring(2, 20)
|
||||||
|
return `${before}_${time}${random}`
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useUserInfoStore } from '@/stores'
|
import { useUserInfoStore } from '@/stores'
|
||||||
const userInfoStore = useUserInfoStore()
|
const userInfoStore = useUserInfoStore()
|
||||||
const email = computed(() => userInfoStore.state.userInfo.email || '------')
|
const email = computed(() => userInfoStore.state.userInfo?.email || '------')
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const onShop = () => {
|
const onShop = () => {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="label">User Name</div>
|
<div class="label">User Name</div>
|
||||||
<div class="value">张三</div>
|
<div class="value">{{ userInfo?.username || '------' }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="label">Email</div>
|
<div class="label">Email</div>
|
||||||
<div class="value">zhangsan@example.com</div>
|
<div class="value">{{ userInfo?.email || '------' }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="label">Language</div>
|
<div class="label">Language</div>
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { locale } = useI18n()
|
const { locale } = useI18n()
|
||||||
const userInfoStore = useUserInfoStore()
|
const userInfoStore = useUserInfoStore()
|
||||||
|
const userInfo = computed(() => userInfoStore.state.userInfo)
|
||||||
const langs = ref([
|
const langs = ref([
|
||||||
{ label: 'English', value: 'ENGLISH' },
|
{ label: 'English', value: 'ENGLISH' },
|
||||||
{ label: '中文', value: 'CHINESE_SIMPLIFIED' }
|
{ label: '中文', value: 'CHINESE_SIMPLIFIED' }
|
||||||
|
|||||||
Reference in New Issue
Block a user