Merge branch 'main' of ssh://18.167.251.121:10002/aidlab/FiDA_Front
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>
|
<template>
|
||||||
<div class="header-tools">
|
<div class="header-tools">
|
||||||
<span class="icon" @click="emit('mouse')"><svg-icon name="c-mouse" size="16" /></span>
|
<template v-for="(v, i) in tools" :key="i">
|
||||||
<span class="icon" @click="emit('hand')"><svg-icon name="c-hand" size="18" /></span>
|
<span class="line" v-if="v.type === 'line'"></span>
|
||||||
<span class="icon" @click="emit('t')"><svg-icon name="c-t" size="18" /></span>
|
<span
|
||||||
<span class="line"></span>
|
v-else
|
||||||
<span class="icon" @click="emit('undo')"><svg-icon name="c-undo" size="18" /></span>
|
class="icon"
|
||||||
<span class="icon" @click="emit('redo')"><svg-icon name="c-redo" size="18" /></span>
|
@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')">
|
<button class="export" @click="emit('export')">
|
||||||
<span class="icon"><svg-icon name="export" size="11" /></span>
|
<span class="icon"><svg-icon name="export" size="11" /></span>
|
||||||
<span class="text">Export</span>
|
<span class="text">Export</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="import" @click="emit('import')">
|
<button class="import" @click="emit('import')">
|
||||||
<span class="icon"><svg-icon name="import" size="11" /></span>
|
|
||||||
<span class="text">Import</span>
|
<span class="text">Import</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch } from 'vue'
|
import { ref, inject, computed } from 'vue'
|
||||||
|
import { TOOLS } from '../manager/ToolManager'
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
zoom: { default: 1, type: Number },
|
zoom: { default: 1, type: Number },
|
||||||
step: { default: 0.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>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
@@ -60,6 +79,7 @@
|
|||||||
height: 3rem;
|
height: 3rem;
|
||||||
--svg-icon-color: #000;
|
--svg-icon-color: #000;
|
||||||
border-radius: 0.4rem;
|
border-radius: 0.4rem;
|
||||||
|
&.active,
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #dfdfdf;
|
background-color: #dfdfdf;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="node" :class="{ center: posCenter }">
|
<div class="node" :class="{ center: posCenter, mask: mask }">
|
||||||
<Handle
|
<Handle
|
||||||
v-for="handle in handles[type]"
|
v-for="handle in handles[type] || []"
|
||||||
:key="handle.id"
|
:key="handle.id"
|
||||||
:type="handle.type"
|
:type="handle.type"
|
||||||
:id="handle.id"
|
:id="handle.id"
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
<div class="add" @mousedown.stop v-if="isAdd" @click="onAdd">
|
<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 class="mask" v-show="mask"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@@ -27,7 +28,8 @@
|
|||||||
{ id: 'Left', type: 'target', position: Position.Left },
|
{ id: 'Left', type: 'target', position: Position.Left },
|
||||||
{ id: 'Right', type: 'source', position: Position.Right }
|
{ 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({
|
const props = defineProps({
|
||||||
type: {
|
type: {
|
||||||
@@ -41,6 +43,10 @@
|
|||||||
stateManager: {
|
stateManager: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
mask: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const nodes = computed(() => props.stateManager.nodes.value)
|
const nodes = computed(() => props.stateManager.nodes.value)
|
||||||
@@ -68,6 +74,10 @@
|
|||||||
.node {
|
.node {
|
||||||
position: relative;
|
position: relative;
|
||||||
--top: 50px;
|
--top: 50px;
|
||||||
|
&.mask *,
|
||||||
|
&.mask {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
.vue-flow__handle {
|
.vue-flow__handle {
|
||||||
width: 12px;
|
width: 12px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
@@ -101,5 +111,13 @@
|
|||||||
&.center {
|
&.center {
|
||||||
--top: 50%;
|
--top: 50%;
|
||||||
}
|
}
|
||||||
|
> .mask {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -59,11 +59,11 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, onMounted } from 'vue'
|
import { reactive, onMounted } from 'vue'
|
||||||
import myTextarea from '../tools/my-textarea.vue'
|
import myTextarea from '../../tools/my-textarea.vue'
|
||||||
import uploadFile from '../tools/upload-file.vue'
|
import uploadFile from '../../tools/upload-file.vue'
|
||||||
import myInput from '../tools/my-input.vue'
|
import myInput from '../../tools/my-input.vue'
|
||||||
import offsetTool from '../tools/offset-tool.vue'
|
import offsetTool from '../../tools/offset-tool.vue'
|
||||||
import slider from '../tools/slider.vue'
|
import slider from '../../tools/slider.vue'
|
||||||
|
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
prompt: '',
|
prompt: '',
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, inject, computed } from 'vue'
|
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 nodeManager = inject('nodeManager') as any
|
||||||
const stateManager = inject('stateManager') as any
|
const stateManager = inject('stateManager') as any
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -56,7 +56,13 @@
|
|||||||
if (!id) return
|
if (!id) return
|
||||||
stateManager.deleteNode(id)
|
stateManager.deleteNode(id)
|
||||||
const superiorID = props.node.data.superiorID
|
const superiorID = props.node.data.superiorID
|
||||||
nodeManager.createCardNode({ data: { type: v.type, superiorID } })
|
nodeManager.createCardNode({
|
||||||
|
data: {
|
||||||
|
tier: v.tier,
|
||||||
|
type: v.type,
|
||||||
|
superiorID
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({})
|
defineExpose({})
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<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 { computed, ref, useAttrs, onMounted, inject } 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'
|
||||||
@@ -122,7 +122,7 @@
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
subordNode.data.data.url =
|
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(() => {
|
onMounted(() => {
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, reactive, onMounted } from 'vue'
|
import { computed, ref, reactive, onMounted } from 'vue'
|
||||||
import myTextarea from '../tools/my-textarea.vue'
|
import myTextarea from '../../tools/my-textarea.vue'
|
||||||
const styleList = ref([
|
const styleList = ref([
|
||||||
{ label: 'Colorful', value: 'Colorful' },
|
{ label: 'Colorful', value: 'Colorful' },
|
||||||
{ label: 'Minimalist', value: 'Minimalist' },
|
{ label: 'Minimalist', value: 'Minimalist' },
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, onMounted } from 'vue'
|
import { reactive, onMounted } from 'vue'
|
||||||
import myTextarea from '../tools/my-textarea.vue'
|
import myTextarea from '../../tools/my-textarea.vue'
|
||||||
import uploadFile from '../tools/upload-file.vue'
|
import uploadFile from '../../tools/upload-file.vue'
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
prompt: '',
|
prompt: '',
|
||||||
file: null
|
file: null
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, onMounted } from 'vue'
|
import { reactive, onMounted } from 'vue'
|
||||||
import uploadFile from '../tools/upload-file.vue'
|
import uploadFile from '../../tools/upload-file.vue'
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
file: null
|
file: null
|
||||||
})
|
})
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, onMounted } from 'vue'
|
import { reactive, onMounted } from 'vue'
|
||||||
import myTextarea from '../tools/my-textarea.vue'
|
import myTextarea from '../../tools/my-textarea.vue'
|
||||||
import uploadFile from '../tools/upload-file.vue'
|
import uploadFile from '../../tools/upload-file.vue'
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
prompt: '',
|
prompt: '',
|
||||||
file: null
|
file: null
|
||||||
@@ -22,9 +22,9 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, reactive, onMounted } from 'vue'
|
import { computed, ref, reactive, onMounted } from 'vue'
|
||||||
import myTextarea from '../tools/my-textarea.vue'
|
import myTextarea from '../../tools/my-textarea.vue'
|
||||||
import mySelect from '../tools/my-select.vue'
|
import mySelect from '../../tools/my-select.vue'
|
||||||
import pixelRatioSelection from '../tools/pixel-ratio-selection.vue'
|
import pixelRatioSelection from '../../tools/pixel-ratio-selection.vue'
|
||||||
const shortcutList = ref([
|
const shortcutList = ref([
|
||||||
{
|
{
|
||||||
label: 'Change the...',
|
label: 'Change the...',
|
||||||
@@ -27,9 +27,9 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, ref } from 'vue'
|
import { reactive, ref } from 'vue'
|
||||||
import myTextarea from '../tools/my-textarea.vue'
|
import myTextarea from '../../tools/my-textarea.vue'
|
||||||
import uploadFile from '../tools/upload-file.vue'
|
import uploadFile from '../../tools/upload-file.vue'
|
||||||
import pixelRatioSelection from '../tools/pixel-ratio-selection.vue'
|
import pixelRatioSelection from '../../tools/pixel-ratio-selection.vue'
|
||||||
const aspectRatioList = ref(['720p', '1080p', '1440p', '2160p', '1k', '2k'])
|
const aspectRatioList = ref(['720p', '1080p', '1440p', '2160p', '1k', '2k'])
|
||||||
const timeList = ref(['5s', '10s', '15s', '20s', '30s', '60s'])
|
const timeList = ref(['5s', '10s', '15s', '20s', '30s', '60s'])
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
@@ -78,9 +78,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.el-popper{
|
.el-popper {
|
||||||
.el-select-dropdown{
|
.el-select-dropdown {
|
||||||
li{
|
li {
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const nodeManager = inject('nodeManager') as any
|
const emit = defineEmits(['delete-node', 'copy-node'])
|
||||||
const attrs = useAttrs()
|
const attrs = useAttrs()
|
||||||
const showHeader = ref(!!attrs.node?.data?.isHeader)
|
const showHeader = ref(!!attrs.node?.data?.isHeader)
|
||||||
const showMenu = ref(false)
|
const showMenu = ref(false)
|
||||||
@@ -58,10 +58,10 @@
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
const menus = ref([
|
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: 'Paste', tip: 'Ctrl+V', on: () => {} },
|
||||||
{ label: 'Duplicate', tip: 'Ctrl+D', 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 },
|
{ isDivide: true },
|
||||||
{ label: 'Bring to font', tip: 'Del', on: () => {} },
|
{ label: 'Bring to font', tip: 'Del', on: () => {} },
|
||||||
{ label: 'Send to back', tip: 'Del', on: () => {} },
|
{ label: 'Send to back', tip: 'Del', on: () => {} },
|
||||||
80
src/components/Canvas/FlowCanvas/components/nodes/text.vue
Normal file
80
src/components/Canvas/FlowCanvas/components/nodes/text.vue
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<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, onMounted } from 'vue'
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['update-data'])
|
||||||
|
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
|
||||||
|
emit('update-data', data)
|
||||||
|
}
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
inputRef.value.innerHTML = data.text
|
||||||
|
emit('update-data', data)
|
||||||
|
})
|
||||||
|
defineExpose({ data })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.text {
|
||||||
|
&.edit {
|
||||||
|
> .input {
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> .input {
|
||||||
|
outline: none;
|
||||||
|
min-width: 1px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -6,18 +6,33 @@
|
|||||||
:edges="edges"
|
:edges="edges"
|
||||||
:min-zoom="0.1"
|
:min-zoom="0.1"
|
||||||
:max-zoom="10"
|
:max-zoom="10"
|
||||||
:nodes-draggable="true"
|
:nodes-draggable="nodesDraggable"
|
||||||
|
:pan-on-drag="panOnDrag"
|
||||||
@nodes-initialized="layoutGraph('LR')"
|
@nodes-initialized="layoutGraph('LR')"
|
||||||
@node-drag-stop="(e) => eventManager.handleNodeDragStop(e)"
|
@node-drag-stop="(e) => eventManager.handleNodeDragStop(e)"
|
||||||
@viewport-change="(e) => eventManager.handleViewportChange(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">
|
<template v-for="v in nodeTypes" :key="v" #[`node-${v}`]="node">
|
||||||
<node :type="v" :stateManager="stateManager" :node="nodeProps">
|
<node
|
||||||
|
:type="v"
|
||||||
|
:stateManager="stateManager"
|
||||||
|
:node="node"
|
||||||
|
:mask="
|
||||||
|
stateManager.tool.value === TOOLS.MOVE ||
|
||||||
|
stateManager.tool.value === TOOLS.TEXT
|
||||||
|
"
|
||||||
|
>
|
||||||
<component
|
<component
|
||||||
:is="components[nodeProps.data.component]"
|
:is="components[node.data.component]"
|
||||||
:node="nodeProps"
|
:node="node"
|
||||||
:data="nodeProps.data.data"
|
:data="node.data.data"
|
||||||
v-bind="nodeProps.data"
|
v-bind="node.data"
|
||||||
|
@delete-node="deleteNode(node.id)"
|
||||||
|
@copy-node="copyNode(node.id)"
|
||||||
|
@update-data="(v) => (node.data.data = v)"
|
||||||
/>
|
/>
|
||||||
</node>
|
</node>
|
||||||
</template>
|
</template>
|
||||||
@@ -44,11 +59,13 @@
|
|||||||
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 resultImage from './components/result/result-image.vue'
|
import resultImage from './components/nodes/result-image.vue'
|
||||||
import card from './components/cards/index.vue'
|
import card from './components/nodes/cards/index.vue'
|
||||||
|
import text from './components/nodes/text.vue'
|
||||||
const components = {
|
const components = {
|
||||||
[NODE_COMPONENT.RESULT_IMAGE]: resultImage,
|
[NODE_COMPONENT.RESULT_IMAGE]: resultImage,
|
||||||
[NODE_COMPONENT.CARD]: card
|
[NODE_COMPONENT.CARD]: card,
|
||||||
|
[NODE_COMPONENT.TEXT]: text
|
||||||
}
|
}
|
||||||
|
|
||||||
// 管理器
|
// 管理器
|
||||||
@@ -56,6 +73,7 @@
|
|||||||
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'
|
import { NodeManager } from './manager/NodeManager'
|
||||||
|
import { ToolManager, TOOLS } from './manager/ToolManager'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
config: {
|
config: {
|
||||||
@@ -65,29 +83,36 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
const vueFlow = ref<any>()
|
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 stateManager = new StateManager({ vueFlow })
|
||||||
const nodes = computed(() => stateManager.nodes_.value)
|
provide('stateManager', stateManager)
|
||||||
const edges = computed(() => stateManager.edges.value)
|
|
||||||
// 事件管理器
|
// 事件管理器
|
||||||
const eventManager = new EventManager({ stateManager, vueFlow })
|
const eventManager = new EventManager({ stateManager, vueFlow })
|
||||||
|
stateManager.setManager({ eventManager })
|
||||||
|
provide('eventManager', eventManager)
|
||||||
|
|
||||||
// 流程管理器
|
// 流程管理器
|
||||||
const flowManager = new FlowManager({ stateManager, vueFlow })
|
const flowManager = new FlowManager({ stateManager, vueFlow })
|
||||||
|
stateManager.setManager({ flowManager })
|
||||||
|
provide('flowManager', flowManager)
|
||||||
|
|
||||||
// 节点管理器
|
// 节点管理器
|
||||||
const nodeManager = new NodeManager({ stateManager, vueFlow })
|
const nodeManager = new NodeManager({ stateManager, vueFlow })
|
||||||
stateManager.setManager({
|
stateManager.setManager({ nodeManager })
|
||||||
eventManager,
|
provide('nodeManager', nodeManager)
|
||||||
flowManager,
|
|
||||||
nodeManager
|
|
||||||
})
|
|
||||||
|
|
||||||
provide('stateManager', stateManager)
|
// 工具管理器
|
||||||
provide('eventManager', eventManager)
|
const toolManager = new ToolManager({ stateManager, vueFlow })
|
||||||
provide('flowManager', flowManager)
|
stateManager.setManager({ toolManager })
|
||||||
provide('nodeManager', nodeManager)
|
provide('toolManager', toolManager)
|
||||||
provide('nodeManager', nodeManager)
|
|
||||||
|
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 { fitView } = useVueFlow()
|
||||||
const { layout } = useLayout()
|
const { layout } = useLayout()
|
||||||
@@ -107,6 +132,15 @@
|
|||||||
}, 0)
|
}, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 删除节点 */
|
||||||
|
const deleteNode = (id) => {
|
||||||
|
nodeManager.deleteNode(id)
|
||||||
|
}
|
||||||
|
/** 复制节点 */
|
||||||
|
const copyNode = (id) => {
|
||||||
|
console.log('复制:', id)
|
||||||
|
}
|
||||||
|
|
||||||
// 导出流程
|
// 导出流程
|
||||||
const exportFlow = () => {
|
const exportFlow = () => {
|
||||||
// flowManager.exportFlow()
|
// flowManager.exportFlow()
|
||||||
@@ -156,6 +190,9 @@
|
|||||||
> .vue-flow {
|
> .vue-flow {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
&.custom-cursor {
|
||||||
|
cursor: var(--custom-cursor, pointer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { TOOLS } from "./ToolManager"
|
||||||
export class EventManager {
|
export class EventManager {
|
||||||
stateManager: any
|
stateManager: any
|
||||||
vueFlow: any
|
vueFlow: any
|
||||||
@@ -7,10 +8,12 @@ export class EventManager {
|
|||||||
this.vueFlow = options.vueFlow
|
this.vueFlow = options.vueFlow
|
||||||
this.zoom = this.stateManager.zoom
|
this.zoom = this.stateManager.zoom
|
||||||
}
|
}
|
||||||
|
/** 处理视口变化 */
|
||||||
handleViewportChange(e: any) {
|
handleViewportChange(e: any) {
|
||||||
const { zoom } = e
|
const { zoom } = e
|
||||||
this.zoom.value = zoom
|
this.zoom.value = zoom
|
||||||
}
|
}
|
||||||
|
/** 处理节点拖动停止 */
|
||||||
handleNodeDragStop(e: any) {
|
handleNodeDragStop(e: any) {
|
||||||
const { node } = e
|
const { node } = e
|
||||||
const { id, position } = node
|
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 { 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 {
|
interface NodeOptions {
|
||||||
id?: string
|
id?: string
|
||||||
position?: { x: number, y: number }
|
position?: { x: number, y: number }
|
||||||
@@ -34,11 +34,12 @@ export class NodeManager {
|
|||||||
const positionX = options.positionX || 0
|
const positionX = options.positionX || 0
|
||||||
const positionY = options.positionY || 0
|
const positionY = options.positionY || 0
|
||||||
const position = options.position ||
|
const position = options.position ||
|
||||||
!snode ? { x: positionX, y: positionY } :
|
(!snode ?
|
||||||
{
|
{ x: positionX, y: positionY } :
|
||||||
x: snode.position.x + snode.dimensions.width + 50 + positionX,
|
{
|
||||||
y: snode.position.y + positionY
|
x: snode.position.x + snode.dimensions.width + 50 + positionX,
|
||||||
}
|
y: snode.position.y + positionY
|
||||||
|
})
|
||||||
const data = options?.data || {}
|
const data = options?.data || {}
|
||||||
data['component'] = options.component
|
data['component'] = options.component
|
||||||
const options_ = {
|
const options_ = {
|
||||||
@@ -55,7 +56,7 @@ export class NodeManager {
|
|||||||
...(options ? options : {}),
|
...(options ? options : {}),
|
||||||
component: NODE_COMPONENT.RESULT_IMAGE,
|
component: NODE_COMPONENT.RESULT_IMAGE,
|
||||||
data: {
|
data: {
|
||||||
tier: 0,
|
tier: NODE_DATATIER.RESULT_IMAGE,
|
||||||
type: NODE_DATATYPE.RESULT_IMAGE,
|
type: NODE_DATATYPE.RESULT_IMAGE,
|
||||||
isHeader: true,
|
isHeader: true,
|
||||||
...(options?.data || {}),
|
...(options?.data || {}),
|
||||||
@@ -70,7 +71,7 @@ export class NodeManager {
|
|||||||
component: NODE_COMPONENT.CARD,
|
component: NODE_COMPONENT.CARD,
|
||||||
positionY: 50,
|
positionY: 50,
|
||||||
data: {
|
data: {
|
||||||
tier: 1,
|
tier: NODE_DATATIER.CARDS_SELECT,
|
||||||
type: NODE_DATATYPE.CARDS_SELECT,
|
type: NODE_DATATYPE.CARDS_SELECT,
|
||||||
...(options?.data || {}),
|
...(options?.data || {}),
|
||||||
},
|
},
|
||||||
@@ -82,6 +83,20 @@ export class NodeManager {
|
|||||||
const options_ = {
|
const options_ = {
|
||||||
...(options ? options : {}),
|
...(options ? options : {}),
|
||||||
component: NODE_COMPONENT.CARD,
|
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_)
|
return this.createNode(options_)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,19 +14,30 @@ export class StateManager {
|
|||||||
nodes_: any
|
nodes_: any
|
||||||
edges: any
|
edges: any
|
||||||
zoom: any
|
zoom: any
|
||||||
// 管理器
|
tool: any
|
||||||
|
cursor: any
|
||||||
|
nodesDraggable: any
|
||||||
|
panOnDrag: any
|
||||||
|
|
||||||
|
// 管理器
|
||||||
eventManager: any
|
eventManager: any
|
||||||
flowManager: any
|
flowManager: any
|
||||||
nodeManager: any
|
nodeManager: any
|
||||||
|
toolManager: any
|
||||||
// 设置管理器
|
// 设置管理器
|
||||||
setManager(options) {
|
setManager(options) {
|
||||||
options.eventManager && (this.eventManager = options.eventManager)
|
options.eventManager && (this.eventManager = options.eventManager)
|
||||||
options.flowManager && (this.flowManager = options.flowManager)
|
options.flowManager && (this.flowManager = options.flowManager)
|
||||||
options.nodeManager && (this.nodeManager = options.nodeManager)
|
options.nodeManager && (this.nodeManager = options.nodeManager)
|
||||||
|
options.toolManager && (this.toolManager = options.toolManager)
|
||||||
}
|
}
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
this.vueFlow = options.vueFlow
|
this.vueFlow = options.vueFlow
|
||||||
this.zoom = ref(1)
|
this.zoom = ref(1)
|
||||||
|
this.tool = ref("")
|
||||||
|
this.cursor = ref("")
|
||||||
|
this.nodesDraggable = ref(false)
|
||||||
|
this.panOnDrag = ref(false)
|
||||||
this.nodes = ref<NodesItem[]>([]);
|
this.nodes = ref<NodesItem[]>([]);
|
||||||
this.nodes_ = computed(() => {
|
this.nodes_ = computed(() => {
|
||||||
return this.nodes.value.map((node, index) => {
|
return this.nodes.value.map((node, index) => {
|
||||||
@@ -41,14 +52,14 @@ export class StateManager {
|
|||||||
const superiorID = node.data.superiorID;
|
const superiorID = node.data.superiorID;
|
||||||
const isSuperior = this.nodes.value.some((v) => v.id === superiorID)
|
const isSuperior = this.nodes.value.some((v) => v.id === superiorID)
|
||||||
const isSubord = this.nodes.value.some((v) => v.data.superiorID === node.id)
|
const isSubord = this.nodes.value.some((v) => v.data.superiorID === node.id)
|
||||||
if (!superiorID) {// 没有上级ID
|
if (!isSuperior && isSubord) {// 没有上级 有下级
|
||||||
obj.type = NODE_TYPE.INPUT;
|
obj.type = NODE_TYPE.INPUT;
|
||||||
} else if (isSuperior && isSubord) {// 有上级ID并找到下级
|
} else if (isSuperior && isSubord) {// 有上级 有下级
|
||||||
obj.type = NODE_TYPE.SECONDARY;
|
obj.type = NODE_TYPE.SECONDARY;
|
||||||
} else if (isSuperior && !isSubord) {// 有上级ID但未找到下级
|
} else if (isSuperior && !isSubord) {// 有上级 没有下级
|
||||||
obj.type = NODE_TYPE.OUTPUT;
|
obj.type = NODE_TYPE.OUTPUT;
|
||||||
} else {// 其他情况-有上级ID未找到上级
|
} else {// 其他情况-没有上级 没有下级
|
||||||
obj.type = NODE_TYPE.INPUT;
|
obj.type = NODE_TYPE.ALONE;
|
||||||
}
|
}
|
||||||
return obj
|
return obj
|
||||||
})
|
})
|
||||||
@@ -105,4 +116,15 @@ export class StateManager {
|
|||||||
getLastNode() {
|
getLastNode() {
|
||||||
return this.nodes.value[this.nodes.value.length - 1]
|
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 = {
|
export const NODE_COMPONENT = {
|
||||||
RESULT_IMAGE: 'result-image',
|
RESULT_IMAGE: 'result-image',
|
||||||
CARD: 'card',
|
CARD: 'card',
|
||||||
|
TEXT: 'text',
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,6 +14,7 @@ export const NODE_TYPE = {
|
|||||||
INPUT: 'InputNode',
|
INPUT: 'InputNode',
|
||||||
SECONDARY: 'SecondaryNode',
|
SECONDARY: 'SecondaryNode',
|
||||||
OUTPUT: 'OutputNode',
|
OUTPUT: 'OutputNode',
|
||||||
|
ALONE: 'AloneNode',
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export default {
|
|||||||
rename: 'Rename',
|
rename: 'Rename',
|
||||||
delete: 'Delete',
|
delete: 'Delete',
|
||||||
setting: 'Setting',
|
setting: 'Setting',
|
||||||
logout: 'Logout',
|
logout: 'Log out',
|
||||||
general: 'General',
|
general: 'General',
|
||||||
profile: 'Profile',
|
profile: 'Profile',
|
||||||
learnMore: 'Learn More',
|
learnMore: 'Learn More',
|
||||||
|
|||||||
@@ -46,12 +46,12 @@
|
|||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.logout-btn {
|
.logout-btn {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
height: 3.7rem;
|
height: 3rem;
|
||||||
width: 9.6rem;
|
width: 7.7rem;
|
||||||
border-radius: 3.7rem;
|
border-radius: 3.7rem;
|
||||||
border: none;
|
border: none;
|
||||||
background-color: #ff7a51;
|
background-color: #ff7a51;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 1.4rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -22,12 +22,12 @@
|
|||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
button {
|
button {
|
||||||
width: 10rem;
|
width: 7.7rem;
|
||||||
height: 3.73rem;
|
height: 3rem;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: 0.01rem solid #b5b5b5;
|
border: 0.01rem solid #b5b5b5;
|
||||||
color: #000;
|
color: #000;
|
||||||
font-size: 1.4rem;
|
font-size: 1.2rem;
|
||||||
border-radius: 3rem;
|
border-radius: 3rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<div class="setting-header">
|
<div class="setting-header">
|
||||||
<div class="title">{{ $t('Home.setting') }}</div>
|
<div class="title">{{ $t('Home.setting') }}</div>
|
||||||
<span class="close" @click="close">
|
<span class="close" @click="close">
|
||||||
<svg-icon name="close" size="10" color="#000" />
|
<svg-icon name="close" size="12" color="#000" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user