fix
This commit is contained in:
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 481 B After Width: | Height: | Size: 481 B |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
@@ -1,29 +1,48 @@
|
||||
<template>
|
||||
<div class="header-tools">
|
||||
<span class="icon" @click="emit('mouse')"><svg-icon name="c-mouse" size="16" /></span>
|
||||
<span class="icon" @click="emit('hand')"><svg-icon name="c-hand" size="18" /></span>
|
||||
<span class="icon" @click="emit('t')"><svg-icon name="c-t" size="18" /></span>
|
||||
<span class="line"></span>
|
||||
<span class="icon" @click="emit('undo')"><svg-icon name="c-undo" size="18" /></span>
|
||||
<span class="icon" @click="emit('redo')"><svg-icon name="c-redo" size="18" /></span>
|
||||
<template v-for="(v, i) in tools" :key="i">
|
||||
<span class="line" v-if="v.type === 'line'"></span>
|
||||
<span
|
||||
v-else
|
||||
class="icon"
|
||||
@click="onClickTool(v.name)"
|
||||
:class="{ active: v.name === tool }"
|
||||
>
|
||||
<svg-icon :name="v.icon" :size="v.iconSize" />
|
||||
</span>
|
||||
</template>
|
||||
<button class="export" @click="emit('export')">
|
||||
<span class="icon"><svg-icon name="export" size="11" /></span>
|
||||
<span class="text">Export</span>
|
||||
</button>
|
||||
<button class="import" @click="emit('import')">
|
||||
<span class="icon"><svg-icon name="import" size="11" /></span>
|
||||
<span class="text">Import</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import { ref, inject, computed } from 'vue'
|
||||
import { TOOLS } from '../manager/ToolManager'
|
||||
const props = defineProps({
|
||||
zoom: { default: 1, type: Number },
|
||||
step: { default: 0.1, type: Number }
|
||||
})
|
||||
const emit = defineEmits(['mouse', 'hand', 't', 'undo', 'redo', 'export', 'import'])
|
||||
const emit = defineEmits(['export', 'import'])
|
||||
const stateManager = inject('stateManager') as any
|
||||
const toolManager = inject('toolManager') as any
|
||||
const tool = computed(() => stateManager.tool.value)
|
||||
const tools = ref([
|
||||
{ name: TOOLS.SELECT, icon: 'c-select', iconSize: 16 },
|
||||
{ name: TOOLS.MOVE, icon: 'c-move', iconSize: 18 },
|
||||
{ name: TOOLS.TEXT, icon: 'c-text', iconSize: 18 },
|
||||
{ type: 'line' },
|
||||
{ name: TOOLS.UNDO, icon: 'c-undo', iconSize: 18 },
|
||||
{ name: TOOLS.REDO, icon: 'c-redo', iconSize: 18 }
|
||||
])
|
||||
const onClickTool = (tool: any) => {
|
||||
toolManager.setTool(tool)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@@ -60,6 +79,7 @@
|
||||
height: 3rem;
|
||||
--svg-icon-color: #000;
|
||||
border-radius: 0.4rem;
|
||||
&.active,
|
||||
&:hover {
|
||||
background-color: #dfdfdf;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="node" :class="{ center: posCenter }">
|
||||
<div class="node" :class="{ center: posCenter, mask: mask }">
|
||||
<Handle
|
||||
v-for="handle in handles[type]"
|
||||
v-for="handle in handles[type] || []"
|
||||
:key="handle.id"
|
||||
:type="handle.type"
|
||||
:id="handle.id"
|
||||
@@ -14,6 +14,7 @@
|
||||
<div class="add" @mousedown.stop v-if="isAdd" @click="onAdd">
|
||||
<svg-icon name="add" size="14" size-unit="px" />
|
||||
</div>
|
||||
<div class="mask" v-show="mask"></div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
@@ -27,7 +28,8 @@
|
||||
{ id: 'Left', type: 'target', position: Position.Left },
|
||||
{ id: 'Right', type: 'source', position: Position.Right }
|
||||
],
|
||||
[NODE_TYPE.OUTPUT]: [{ id: 'Left', type: 'target', position: Position.Left }]
|
||||
[NODE_TYPE.OUTPUT]: [{ id: 'Left', type: 'target', position: Position.Left }],
|
||||
[NODE_TYPE.ALONE]: []
|
||||
})
|
||||
const props = defineProps({
|
||||
type: {
|
||||
@@ -41,6 +43,10 @@
|
||||
stateManager: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
mask: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
const nodes = computed(() => props.stateManager.nodes.value)
|
||||
@@ -68,6 +74,10 @@
|
||||
.node {
|
||||
position: relative;
|
||||
--top: 50px;
|
||||
&.mask *,
|
||||
&.mask {
|
||||
pointer-events: none;
|
||||
}
|
||||
.vue-flow__handle {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
@@ -101,5 +111,13 @@
|
||||
&.center {
|
||||
--top: 50%;
|
||||
}
|
||||
> .mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -59,11 +59,11 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, onMounted } from 'vue'
|
||||
import myTextarea from '../tools/my-textarea.vue'
|
||||
import uploadFile from '../tools/upload-file.vue'
|
||||
import myInput from '../tools/my-input.vue'
|
||||
import offsetTool from '../tools/offset-tool.vue'
|
||||
import slider from '../tools/slider.vue'
|
||||
import myTextarea from '../../tools/my-textarea.vue'
|
||||
import uploadFile from '../../tools/upload-file.vue'
|
||||
import myInput from '../../tools/my-input.vue'
|
||||
import offsetTool from '../../tools/offset-tool.vue'
|
||||
import slider from '../../tools/slider.vue'
|
||||
|
||||
const data = reactive({
|
||||
prompt: '',
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, inject, computed } from 'vue'
|
||||
import { NODE_DATATYPE, NODE_DATATIER } from '../../tools/index.d'
|
||||
import { NODE_DATATYPE, NODE_DATATIER } from '../../../tools/index.d'
|
||||
const nodeManager = inject('nodeManager') as any
|
||||
const stateManager = inject('stateManager') as any
|
||||
const props = defineProps({
|
||||
@@ -56,7 +56,13 @@
|
||||
if (!id) return
|
||||
stateManager.deleteNode(id)
|
||||
const superiorID = props.node.data.superiorID
|
||||
nodeManager.createCardNode({ data: { type: v.type, superiorID } })
|
||||
nodeManager.createCardNode({
|
||||
data: {
|
||||
tier: v.tier,
|
||||
type: v.type,
|
||||
superiorID
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({})
|
||||
@@ -23,7 +23,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { NODE_DATATYPE, NODE_DATATIER } from '../../tools/index.d'
|
||||
import { NODE_DATATYPE, NODE_DATATIER } from '../../../tools/index.d'
|
||||
import { computed, ref, useAttrs, onMounted, inject } from 'vue'
|
||||
import CardsSelect from './cards-select.vue'
|
||||
import ToRealStyle from './to-real-style.vue'
|
||||
@@ -122,7 +122,7 @@
|
||||
})
|
||||
} else {
|
||||
subordNode.data.data.url =
|
||||
'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__'
|
||||
'https://s3-alpha-sig.figma.com/img/8ce2/f1a4/12b93da90e5f17109e7430f14837fd14?Expires=1773619200&Key-Pair-Id=APKAQ4GOSFWCW27IBOMQ&Signature=kmLsTFtXJqfvuxj6husWlDkRDMOIRDjzUUjb7zh79GkBKihUHc0f59k5OAImHTPdaiEREUCCpn~8sQ-si5lenuauJpApCmAU~NsxjfQhuh9m5O~GiHenr2fKu0DIJ75-oCE3859fyxoSFXQgZ9PRmeb98kikMR6uRX9nI5TPUHgKO8ZgkhDBTW~iyaDT~1ybnoK7elPa6T2VzfO-bpIyY-MZ71VRq3RxwmZRxduqHEb3Dh-jjrHyh2SoQsHmUjSJOf-uYilfvpGUResZAjAq8ZVLEjvhzKC2bmCNZIp3RmhYO8ctU7pd5t91J6Xaa6jBLtGfMxbqIm652EC79K0RoA__'
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, reactive, onMounted } from 'vue'
|
||||
import myTextarea from '../tools/my-textarea.vue'
|
||||
import myTextarea from '../../tools/my-textarea.vue'
|
||||
const styleList = ref([
|
||||
{ label: 'Colorful', value: 'Colorful' },
|
||||
{ label: 'Minimalist', value: 'Minimalist' },
|
||||
@@ -10,8 +10,8 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, onMounted } from 'vue'
|
||||
import myTextarea from '../tools/my-textarea.vue'
|
||||
import uploadFile from '../tools/upload-file.vue'
|
||||
import myTextarea from '../../tools/my-textarea.vue'
|
||||
import uploadFile from '../../tools/upload-file.vue'
|
||||
const data = reactive({
|
||||
prompt: '',
|
||||
file: null
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, onMounted } from 'vue'
|
||||
import uploadFile from '../tools/upload-file.vue'
|
||||
import uploadFile from '../../tools/upload-file.vue'
|
||||
const data = reactive({
|
||||
file: null
|
||||
})
|
||||
@@ -10,8 +10,8 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, onMounted } from 'vue'
|
||||
import myTextarea from '../tools/my-textarea.vue'
|
||||
import uploadFile from '../tools/upload-file.vue'
|
||||
import myTextarea from '../../tools/my-textarea.vue'
|
||||
import uploadFile from '../../tools/upload-file.vue'
|
||||
const data = reactive({
|
||||
prompt: '',
|
||||
file: null
|
||||
@@ -22,9 +22,9 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, reactive, onMounted } from 'vue'
|
||||
import myTextarea from '../tools/my-textarea.vue'
|
||||
import mySelect from '../tools/my-select.vue'
|
||||
import pixelRatioSelection from '../tools/pixel-ratio-selection.vue'
|
||||
import myTextarea from '../../tools/my-textarea.vue'
|
||||
import mySelect from '../../tools/my-select.vue'
|
||||
import pixelRatioSelection from '../../tools/pixel-ratio-selection.vue'
|
||||
const shortcutList = ref([
|
||||
{
|
||||
label: 'Change the...',
|
||||
@@ -27,9 +27,9 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue'
|
||||
import myTextarea from '../tools/my-textarea.vue'
|
||||
import uploadFile from '../tools/upload-file.vue'
|
||||
import pixelRatioSelection from '../tools/pixel-ratio-selection.vue'
|
||||
import myTextarea from '../../tools/my-textarea.vue'
|
||||
import uploadFile from '../../tools/upload-file.vue'
|
||||
import pixelRatioSelection from '../../tools/pixel-ratio-selection.vue'
|
||||
const aspectRatioList = ref(['720p', '1080p', '1440p', '2160p', '1k', '2k'])
|
||||
const timeList = ref(['5s', '10s', '15s', '20s', '30s', '60s'])
|
||||
const data = reactive({
|
||||
@@ -78,9 +78,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-popper{
|
||||
.el-select-dropdown{
|
||||
li{
|
||||
.el-popper {
|
||||
.el-select-dropdown {
|
||||
li {
|
||||
padding-left: 8px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
@@ -44,7 +44,7 @@
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const nodeManager = inject('nodeManager') as any
|
||||
const emit = defineEmits(['delete-node', 'copy-node'])
|
||||
const attrs = useAttrs()
|
||||
const showHeader = ref(!!attrs.node?.data?.isHeader)
|
||||
const showMenu = ref(false)
|
||||
@@ -58,10 +58,10 @@
|
||||
}
|
||||
)
|
||||
const menus = ref([
|
||||
{ label: 'Copy', tip: 'Ctrl+C', on: () => {} },
|
||||
{ label: 'Copy', tip: 'Ctrl+C', on: () => emit('copy-node') },
|
||||
{ label: 'Paste', tip: 'Ctrl+V', on: () => {} },
|
||||
{ label: 'Duplicate', tip: 'Ctrl+D', on: () => {} },
|
||||
{ label: 'Delete', tip: 'Del', on: () => nodeManager.deleteNode(attrs.node.id) },
|
||||
{ label: 'Delete', tip: 'Del', on: () => emit('delete-node') },
|
||||
{ isDivide: true },
|
||||
{ label: 'Bring to font', tip: 'Del', on: () => {} },
|
||||
{ label: 'Send to back', tip: 'Del', on: () => {} },
|
||||
74
src/components/Canvas/FlowCanvas/components/nodes/text.vue
Normal file
74
src/components/Canvas/FlowCanvas/components/nodes/text.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<!-- 结果图片 -->
|
||||
<div
|
||||
class="text"
|
||||
:class="{ edit }"
|
||||
@click="onClick"
|
||||
@mousedown="onMouseDown"
|
||||
@dblclick="onDoubleClick"
|
||||
>
|
||||
<div
|
||||
tabindex="0"
|
||||
class="input"
|
||||
ref="inputRef"
|
||||
contenteditable="true"
|
||||
@input="onInput"
|
||||
@blur="onBlur"
|
||||
@paste.prevent
|
||||
>
|
||||
双击编辑文本
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, nextTick, useAttrs, inject, watch } from 'vue'
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['delete-node', 'copy-node'])
|
||||
const data = reactive({
|
||||
text: props.data?.text || '双击编辑文本'
|
||||
})
|
||||
const edit = ref(false)
|
||||
const time = ref(null)
|
||||
const inputRef = ref<any>()
|
||||
const onInput = () => {
|
||||
const text = inputRef.value.innerText
|
||||
data.text = text
|
||||
}
|
||||
const onBlur = () => {
|
||||
edit.value = false
|
||||
}
|
||||
const onClick = () => {
|
||||
if (edit.value) return
|
||||
time.value = setTimeout(() => {
|
||||
edit.value = false
|
||||
}, 500)
|
||||
edit.value = true
|
||||
}
|
||||
const onDoubleClick = () => {
|
||||
clearTimeout(time.value)
|
||||
}
|
||||
const onMouseDown = (e: MouseEvent) => {
|
||||
if (edit.value) e.stopPropagation()
|
||||
}
|
||||
defineExpose({ data })
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.text {
|
||||
&.edit {
|
||||
> .input {
|
||||
cursor: text;
|
||||
}
|
||||
}
|
||||
> .input {
|
||||
outline: none;
|
||||
min-width: 1px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -6,18 +6,32 @@
|
||||
:edges="edges"
|
||||
:min-zoom="0.1"
|
||||
:max-zoom="10"
|
||||
:nodes-draggable="true"
|
||||
:nodes-draggable="nodesDraggable"
|
||||
:pan-on-drag="panOnDrag"
|
||||
@nodes-initialized="layoutGraph('LR')"
|
||||
@node-drag-stop="(e) => eventManager.handleNodeDragStop(e)"
|
||||
@viewport-change="(e) => eventManager.handleViewportChange(e)"
|
||||
@pane-click="(e) => eventManager.handleClick(e)"
|
||||
:class="{ 'custom-cursor': !!stateManager.cursor.value }"
|
||||
:style="{ '--custom-cursor': stateManager.cursor.value }"
|
||||
>
|
||||
<template v-for="v in nodeTypes" :key="v" #[`node-${v}`]="nodeProps">
|
||||
<node :type="v" :stateManager="stateManager" :node="nodeProps">
|
||||
<template v-for="v in nodeTypes" :key="v" #[`node-${v}`]="node">
|
||||
<node
|
||||
:type="v"
|
||||
:stateManager="stateManager"
|
||||
:node="node"
|
||||
:mask="
|
||||
stateManager.tool.value === TOOLS.MOVE ||
|
||||
stateManager.tool.value === TOOLS.TEXT
|
||||
"
|
||||
>
|
||||
<component
|
||||
:is="components[nodeProps.data.component]"
|
||||
:node="nodeProps"
|
||||
:data="nodeProps.data.data"
|
||||
v-bind="nodeProps.data"
|
||||
:is="components[node.data.component]"
|
||||
:node="node"
|
||||
:data="node.data.data"
|
||||
v-bind="node.data"
|
||||
@delete-node="deleteNode(node.id)"
|
||||
@copy-node="copyNode(node.id)"
|
||||
/>
|
||||
</node>
|
||||
</template>
|
||||
@@ -44,11 +58,13 @@
|
||||
import zoom from '../components/zoom.vue'
|
||||
// 节点
|
||||
import node from './components/node.vue'
|
||||
import resultImage from './components/result/result-image.vue'
|
||||
import card from './components/cards/index.vue'
|
||||
import resultImage from './components/nodes/result-image.vue'
|
||||
import card from './components/nodes/cards/index.vue'
|
||||
import text from './components/nodes/text.vue'
|
||||
const components = {
|
||||
[NODE_COMPONENT.RESULT_IMAGE]: resultImage,
|
||||
[NODE_COMPONENT.CARD]: card
|
||||
[NODE_COMPONENT.CARD]: card,
|
||||
[NODE_COMPONENT.TEXT]: text
|
||||
}
|
||||
|
||||
// 管理器
|
||||
@@ -56,6 +72,7 @@
|
||||
import { EventManager } from './manager/EventManager'
|
||||
import { FlowManager } from './manager/FlowManager'
|
||||
import { NodeManager } from './manager/NodeManager'
|
||||
import { ToolManager, TOOLS } from './manager/ToolManager'
|
||||
|
||||
const props = defineProps({
|
||||
config: {
|
||||
@@ -65,29 +82,36 @@
|
||||
})
|
||||
|
||||
const vueFlow = ref<any>()
|
||||
const nodeTypes = ref([NODE_TYPE.INPUT, NODE_TYPE.SECONDARY, NODE_TYPE.OUTPUT])
|
||||
const nodeTypes = ref([NODE_TYPE.INPUT, NODE_TYPE.SECONDARY, NODE_TYPE.OUTPUT, NODE_TYPE.ALONE])
|
||||
|
||||
// 状态管理器
|
||||
const stateManager = new StateManager({ vueFlow })
|
||||
const nodes = computed(() => stateManager.nodes_.value)
|
||||
const edges = computed(() => stateManager.edges.value)
|
||||
provide('stateManager', stateManager)
|
||||
|
||||
// 事件管理器
|
||||
const eventManager = new EventManager({ stateManager, vueFlow })
|
||||
stateManager.setManager({ eventManager })
|
||||
provide('eventManager', eventManager)
|
||||
|
||||
// 流程管理器
|
||||
const flowManager = new FlowManager({ stateManager, vueFlow })
|
||||
stateManager.setManager({ flowManager })
|
||||
provide('flowManager', flowManager)
|
||||
|
||||
// 节点管理器
|
||||
const nodeManager = new NodeManager({ stateManager, vueFlow })
|
||||
stateManager.setManager({
|
||||
eventManager,
|
||||
flowManager,
|
||||
nodeManager
|
||||
})
|
||||
stateManager.setManager({ nodeManager })
|
||||
provide('nodeManager', nodeManager)
|
||||
|
||||
provide('stateManager', stateManager)
|
||||
provide('eventManager', eventManager)
|
||||
provide('flowManager', flowManager)
|
||||
provide('nodeManager', nodeManager)
|
||||
provide('nodeManager', nodeManager)
|
||||
// 工具管理器
|
||||
const toolManager = new ToolManager({ stateManager, vueFlow })
|
||||
stateManager.setManager({ toolManager })
|
||||
provide('toolManager', toolManager)
|
||||
|
||||
const nodes = computed(() => stateManager.nodes_.value)
|
||||
const edges = computed(() => stateManager.edges.value)
|
||||
const nodesDraggable = computed(() => stateManager.nodesDraggable.value)
|
||||
const panOnDrag = computed(() => stateManager.panOnDrag.value)
|
||||
|
||||
const { fitView } = useVueFlow()
|
||||
const { layout } = useLayout()
|
||||
@@ -107,6 +131,15 @@
|
||||
}, 0)
|
||||
}
|
||||
|
||||
/** 删除节点 */
|
||||
const deleteNode = (id) => {
|
||||
nodeManager.deleteNode(id)
|
||||
}
|
||||
/** 复制节点 */
|
||||
const copyNode = (id) => {
|
||||
console.log('复制:', id)
|
||||
}
|
||||
|
||||
// 导出流程
|
||||
const exportFlow = () => {
|
||||
// flowManager.exportFlow()
|
||||
@@ -156,6 +189,9 @@
|
||||
> .vue-flow {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
&.custom-cursor {
|
||||
cursor: var(--custom-cursor, pointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { TOOLS } from "./ToolManager"
|
||||
export class EventManager {
|
||||
stateManager: any
|
||||
vueFlow: any
|
||||
@@ -7,10 +8,12 @@ export class EventManager {
|
||||
this.vueFlow = options.vueFlow
|
||||
this.zoom = this.stateManager.zoom
|
||||
}
|
||||
/** 处理视口变化 */
|
||||
handleViewportChange(e: any) {
|
||||
const { zoom } = e
|
||||
this.zoom.value = zoom
|
||||
}
|
||||
/** 处理节点拖动停止 */
|
||||
handleNodeDragStop(e: any) {
|
||||
const { node } = e
|
||||
const { id, position } = node
|
||||
@@ -21,4 +24,17 @@ export class EventManager {
|
||||
}
|
||||
})
|
||||
}
|
||||
/** 处理点击 */
|
||||
handleClick(event: any) {
|
||||
const tool = this.stateManager.tool.value
|
||||
if (tool === TOOLS.TEXT) {
|
||||
const { x, y, zoom } = this.vueFlow.value.viewport
|
||||
const position = {
|
||||
x: (event.offsetX - x) / zoom,
|
||||
y: (event.offsetY - y) / zoom
|
||||
}
|
||||
this.stateManager.nodeManager.createTextNode({ position })
|
||||
this.stateManager.toolManager.setTool(TOOLS.SELECT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createId } from '../../tools/tools'
|
||||
import { NODE_DATATYPE, NODE_COMPONENT } from '../tools/index.d'
|
||||
import { NODE_DATATYPE, NODE_COMPONENT, NODE_DATATIER } from '../tools/index.d'
|
||||
interface NodeOptions {
|
||||
id?: string
|
||||
position?: { x: number, y: number }
|
||||
@@ -34,11 +34,12 @@ export class NodeManager {
|
||||
const positionX = options.positionX || 0
|
||||
const positionY = options.positionY || 0
|
||||
const position = options.position ||
|
||||
!snode ? { x: positionX, y: positionY } :
|
||||
{
|
||||
x: snode.position.x + snode.dimensions.width + 50 + positionX,
|
||||
y: snode.position.y + positionY
|
||||
}
|
||||
(!snode ?
|
||||
{ x: positionX, y: positionY } :
|
||||
{
|
||||
x: snode.position.x + snode.dimensions.width + 50 + positionX,
|
||||
y: snode.position.y + positionY
|
||||
})
|
||||
const data = options?.data || {}
|
||||
data['component'] = options.component
|
||||
const options_ = {
|
||||
@@ -55,7 +56,7 @@ export class NodeManager {
|
||||
...(options ? options : {}),
|
||||
component: NODE_COMPONENT.RESULT_IMAGE,
|
||||
data: {
|
||||
tier: 0,
|
||||
tier: NODE_DATATIER.RESULT_IMAGE,
|
||||
type: NODE_DATATYPE.RESULT_IMAGE,
|
||||
isHeader: true,
|
||||
...(options?.data || {}),
|
||||
@@ -70,7 +71,7 @@ export class NodeManager {
|
||||
component: NODE_COMPONENT.CARD,
|
||||
positionY: 50,
|
||||
data: {
|
||||
tier: 1,
|
||||
tier: NODE_DATATIER.CARDS_SELECT,
|
||||
type: NODE_DATATYPE.CARDS_SELECT,
|
||||
...(options?.data || {}),
|
||||
},
|
||||
@@ -82,6 +83,20 @@ export class NodeManager {
|
||||
const options_ = {
|
||||
...(options ? options : {}),
|
||||
component: NODE_COMPONENT.CARD,
|
||||
data: {
|
||||
...(options?.data || {}),
|
||||
}
|
||||
}
|
||||
return this.createNode(options_)
|
||||
}
|
||||
/** 创建文本节点 */
|
||||
createTextNode(options?: NodeOptions) {
|
||||
const options_ = {
|
||||
...(options ? options : {}),
|
||||
component: NODE_COMPONENT.TEXT,
|
||||
data: {
|
||||
...(options?.data || {}),
|
||||
}
|
||||
}
|
||||
return this.createNode(options_)
|
||||
}
|
||||
|
||||
@@ -14,19 +14,30 @@ export class StateManager {
|
||||
nodes_: any
|
||||
edges: any
|
||||
zoom: any
|
||||
// 管理器
|
||||
tool: any
|
||||
cursor: any
|
||||
nodesDraggable: any
|
||||
panOnDrag: any
|
||||
|
||||
// 管理器
|
||||
eventManager: any
|
||||
flowManager: any
|
||||
nodeManager: any
|
||||
toolManager: any
|
||||
// 设置管理器
|
||||
setManager(options) {
|
||||
options.eventManager && (this.eventManager = options.eventManager)
|
||||
options.flowManager && (this.flowManager = options.flowManager)
|
||||
options.nodeManager && (this.nodeManager = options.nodeManager)
|
||||
options.toolManager && (this.toolManager = options.toolManager)
|
||||
}
|
||||
constructor(options) {
|
||||
this.vueFlow = options.vueFlow
|
||||
this.zoom = ref(1)
|
||||
this.tool = ref("")
|
||||
this.cursor = ref("")
|
||||
this.nodesDraggable = ref(false)
|
||||
this.panOnDrag = ref(false)
|
||||
this.nodes = ref<NodesItem[]>([]);
|
||||
this.nodes_ = computed(() => {
|
||||
return this.nodes.value.map((node, index) => {
|
||||
@@ -41,14 +52,14 @@ export class StateManager {
|
||||
const superiorID = node.data.superiorID;
|
||||
const isSuperior = this.nodes.value.some((v) => v.id === superiorID)
|
||||
const isSubord = this.nodes.value.some((v) => v.data.superiorID === node.id)
|
||||
if (!superiorID) {// 没有上级ID
|
||||
if (!isSuperior && isSubord) {// 没有上级 有下级
|
||||
obj.type = NODE_TYPE.INPUT;
|
||||
} else if (isSuperior && isSubord) {// 有上级ID并找到下级
|
||||
} else if (isSuperior && isSubord) {// 有上级 有下级
|
||||
obj.type = NODE_TYPE.SECONDARY;
|
||||
} else if (isSuperior && !isSubord) {// 有上级ID但未找到下级
|
||||
} else if (isSuperior && !isSubord) {// 有上级 没有下级
|
||||
obj.type = NODE_TYPE.OUTPUT;
|
||||
} else {// 其他情况-有上级ID未找到上级
|
||||
obj.type = NODE_TYPE.INPUT;
|
||||
} else {// 其他情况-没有上级 没有下级
|
||||
obj.type = NODE_TYPE.ALONE;
|
||||
}
|
||||
return obj
|
||||
})
|
||||
@@ -105,4 +116,15 @@ export class StateManager {
|
||||
getLastNode() {
|
||||
return this.nodes.value[this.nodes.value.length - 1]
|
||||
}
|
||||
/** 设置工具 */
|
||||
setTool(tool: string) {
|
||||
this.tool.value = tool
|
||||
}
|
||||
|
||||
/** 设置光标 */
|
||||
setCursor(v: string) { this.cursor.value = v }
|
||||
/** 设置节点是否可拖动 */
|
||||
setNodesDraggable(v: boolean) { this.nodesDraggable.value = v }
|
||||
/** 设置是否可以平移画布 */
|
||||
setPanOnDrag(v: boolean) { this.panOnDrag.value = v }
|
||||
}
|
||||
|
||||
47
src/components/Canvas/FlowCanvas/manager/ToolManager.ts
Normal file
47
src/components/Canvas/FlowCanvas/manager/ToolManager.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
export const TOOLS = {
|
||||
SELECT: "SELECT",
|
||||
MOVE: "MOVE",
|
||||
TEXT: "TEXT",
|
||||
UNDO: "UNDO",
|
||||
REDO: "REDO",
|
||||
}
|
||||
export const tools = [
|
||||
/** 选择工具 */
|
||||
{
|
||||
name: TOOLS.SELECT,
|
||||
nodesDraggable: true,
|
||||
panOnDrag: false,
|
||||
},
|
||||
/** 移动工具 */
|
||||
{
|
||||
name: TOOLS.MOVE,
|
||||
nodesDraggable: false,
|
||||
panOnDrag: true,
|
||||
},
|
||||
/** 文本工具 */
|
||||
{
|
||||
name: TOOLS.TEXT,
|
||||
cursor: "text",
|
||||
nodesDraggable: false,
|
||||
panOnDrag: false,
|
||||
},
|
||||
|
||||
]
|
||||
export class ToolManager {
|
||||
stateManager: any
|
||||
vueFlow: any
|
||||
constructor(options) {
|
||||
this.stateManager = options.stateManager;
|
||||
this.vueFlow = options.vueFlow
|
||||
this.setTool(TOOLS.SELECT)
|
||||
}
|
||||
setTool(value: string) {
|
||||
const tool = tools.find((t) => t.name === value)
|
||||
if (!tool) return console.warn(`工具${tool}不存在`)
|
||||
this.stateManager.tool.value = tool.name
|
||||
this.stateManager.setNodesDraggable(!!tool.nodesDraggable)
|
||||
this.stateManager.setPanOnDrag(!!tool.panOnDrag)
|
||||
this.stateManager.setCursor(tool.cursor || "")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
export const NODE_COMPONENT = {
|
||||
RESULT_IMAGE: 'result-image',
|
||||
CARD: 'card',
|
||||
TEXT: 'text',
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -13,6 +14,7 @@ export const NODE_TYPE = {
|
||||
INPUT: 'InputNode',
|
||||
SECONDARY: 'SecondaryNode',
|
||||
OUTPUT: 'OutputNode',
|
||||
ALONE: 'AloneNode',
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -64,7 +64,7 @@ export default {
|
||||
rename: 'Rename',
|
||||
delete: 'Delete',
|
||||
setting: 'Setting',
|
||||
logout: 'Logout',
|
||||
logout: 'Log out',
|
||||
general: 'General',
|
||||
profile: 'Profile',
|
||||
learnMore: 'Learn More',
|
||||
|
||||
@@ -46,12 +46,12 @@
|
||||
<style lang="less" scoped>
|
||||
.logout-btn {
|
||||
cursor: pointer;
|
||||
height: 3.7rem;
|
||||
width: 9.6rem;
|
||||
height: 3rem;
|
||||
width: 7.7rem;
|
||||
border-radius: 3.7rem;
|
||||
border: none;
|
||||
background-color: #ff7a51;
|
||||
color: #fff;
|
||||
font-size: 1.4rem;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -22,12 +22,12 @@
|
||||
|
||||
<style lang="less" scoped>
|
||||
button {
|
||||
width: 10rem;
|
||||
height: 3.73rem;
|
||||
width: 7.7rem;
|
||||
height: 3rem;
|
||||
background-color: transparent;
|
||||
border: 0.01rem solid #b5b5b5;
|
||||
color: #000;
|
||||
font-size: 1.4rem;
|
||||
font-size: 1.2rem;
|
||||
border-radius: 3rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<div class="setting-header">
|
||||
<div class="title">{{ $t('Home.setting') }}</div>
|
||||
<span class="close" @click="close">
|
||||
<svg-icon name="close" size="10" color="#000" />
|
||||
<svg-icon name="close" size="12" color="#000" />
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user