完善菜单功能
This commit is contained in:
@@ -5,8 +5,8 @@
|
|||||||
<span
|
<span
|
||||||
v-else
|
v-else
|
||||||
class="icon"
|
class="icon"
|
||||||
@click="onClickTool(v.name)"
|
@click="onClickTool(v)"
|
||||||
:class="{ active: v.name === tool }"
|
:class="{ active: v.name === tool, disabled: v.disabled }"
|
||||||
>
|
>
|
||||||
<svg-icon :name="v.icon" :size="v.iconSize" />
|
<svg-icon :name="v.icon" :size="v.iconSize" />
|
||||||
</span>
|
</span>
|
||||||
@@ -32,16 +32,37 @@
|
|||||||
const stateManager = inject('stateManager') as any
|
const stateManager = inject('stateManager') as any
|
||||||
const toolManager = inject('toolManager') as any
|
const toolManager = inject('toolManager') as any
|
||||||
const tool = computed(() => stateManager.tool.value)
|
const tool = computed(() => stateManager.tool.value)
|
||||||
|
const historyIndex = computed(() => stateManager.historyIndex.value)
|
||||||
|
const historyList = computed(() => stateManager.historyList.value)
|
||||||
|
const isUndo = computed(() => !historyList.value[historyIndex.value - 1])
|
||||||
|
const isRedo = computed(() => !historyList.value[historyIndex.value + 1])
|
||||||
const tools = ref([
|
const tools = ref([
|
||||||
{ name: TOOLS.SELECT, icon: 'c-select', iconSize: 16 },
|
{ name: TOOLS.SELECT, icon: 'c-select', iconSize: 16, disabled: ref(false) },
|
||||||
{ name: TOOLS.MOVE, icon: 'c-move', iconSize: 18 },
|
{ name: TOOLS.MOVE, icon: 'c-move', iconSize: 18, disabled: ref(false) },
|
||||||
{ name: TOOLS.TEXT, icon: 'c-text', iconSize: 18 },
|
{ name: TOOLS.TEXT, icon: 'c-text', iconSize: 18, disabled: ref(false) },
|
||||||
{ type: 'line' },
|
{ type: 'line' },
|
||||||
{ name: TOOLS.UNDO, icon: 'c-undo', iconSize: 18 },
|
{
|
||||||
{ name: TOOLS.REDO, icon: 'c-redo', iconSize: 18 }
|
name: TOOLS.UNDO,
|
||||||
|
icon: 'c-undo',
|
||||||
|
iconSize: 18,
|
||||||
|
disabled: isUndo,
|
||||||
|
on: () => stateManager.undoState()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: TOOLS.REDO,
|
||||||
|
icon: 'c-redo',
|
||||||
|
iconSize: 18,
|
||||||
|
disabled: isRedo,
|
||||||
|
on: () => stateManager.redoState()
|
||||||
|
}
|
||||||
])
|
])
|
||||||
const onClickTool = (tool: any) => {
|
const onClickTool = (tool: any) => {
|
||||||
toolManager.setTool(tool)
|
if (tool.disabled?.value) return
|
||||||
|
if (tool.on) {
|
||||||
|
tool.on()
|
||||||
|
} else {
|
||||||
|
toolManager.setTool(tool.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -79,10 +100,14 @@
|
|||||||
height: 3rem;
|
height: 3rem;
|
||||||
--svg-icon-color: #000;
|
--svg-icon-color: #000;
|
||||||
border-radius: 0.4rem;
|
border-radius: 0.4rem;
|
||||||
&.active,
|
&:not(.disabled).active,
|
||||||
&:hover {
|
&:not(.disabled):hover {
|
||||||
background-color: #dfdfdf;
|
background-color: #dfdfdf;
|
||||||
}
|
}
|
||||||
|
&.disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
> button {
|
> button {
|
||||||
width: 10rem;
|
width: 10rem;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<span class="text">Edit</span>
|
<span class="text">Edit</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<img class="image" :src="data.url" />
|
<img class="image" :src="data.url" :style="{transform: `scale(${data?.scale?.x || 1}, ${data?.scale?.y || 1})`}" />
|
||||||
<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>
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
<div
|
<div
|
||||||
v-for="(v, i) in menus"
|
v-for="(v, i) in menus"
|
||||||
:key="i"
|
:key="i"
|
||||||
:class="[v.isDivide ? 'divide' : 'item']"
|
:class="[v.isDivide ? 'divide' : 'item', { disabled: v.disabled }]"
|
||||||
@click="onMenuItem(v)"
|
@click="onMenuItem(v)"
|
||||||
>
|
>
|
||||||
<template v-if="!v.isDivide">
|
<template v-if="!v.isDivide">
|
||||||
@@ -39,17 +39,22 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, ref, onBeforeUnmount, useAttrs, inject, watch } from 'vue'
|
import { reactive, ref, onBeforeUnmount, useAttrs, inject, watch } from 'vue'
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
config: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
data: {
|
data: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const emit = defineEmits(['delete-node', 'copy-node', 'bring-to-font', 'send-to-back', 'flip-horizontal', 'flip-vertical'])
|
const emit = defineEmits(['delete-node', 'copy-node', 'bring-to-font', 'send-to-back', 'update-data'])
|
||||||
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)
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
url: props.data?.url || ''
|
url: props.data?.url || '',
|
||||||
|
scale: props.data?.scale || { x:1,y:1 }
|
||||||
})
|
})
|
||||||
watch(
|
watch(
|
||||||
() => props.data.url,
|
() => props.data.url,
|
||||||
@@ -59,17 +64,21 @@
|
|||||||
)
|
)
|
||||||
const menus = ref([
|
const menus = ref([
|
||||||
{ label: 'Copy', tip: 'Ctrl+C', on: () => emit('copy-node') },
|
{ label: 'Copy', tip: 'Ctrl+C', on: () => emit('copy-node') },
|
||||||
// { label: 'Paste', tip: 'Ctrl+V', on: () => {} },
|
{
|
||||||
// { label: 'Duplicate', tip: 'Ctrl+D', on: () => {} },
|
label: 'Delete',
|
||||||
{ label: 'Delete', tip: 'Del', on: () => emit('delete-node') },
|
tip: 'Del',
|
||||||
|
on: () => emit('delete-node'),
|
||||||
|
disabled: !!props.config?.disableDelete
|
||||||
|
},
|
||||||
{ isDivide: true },
|
{ isDivide: true },
|
||||||
{ label: 'Bring to font', tip: '', on: () => {emit('bring-to-font')} },
|
{ label: 'Bring to font', tip: '', on: () => {} },
|
||||||
{ label: 'Send to back', tip: '', on: () => {emit('send-to-back')} },
|
{ label: 'Send to back', tip: '', on: () => {} },
|
||||||
{ isDivide: true },
|
{ isDivide: true },
|
||||||
{ label: 'Flip horizontal', tip: '', on: () => {emit('flip-horizontal')} },
|
{ label: 'Flip horizontal', tip: '', on: () => {data.scale.x = -data.scale.x; emit('update-data',data)} },
|
||||||
{ label: 'Flip vertical', tip: '', on: () => {emit('flip-vertical')} }
|
{ label: 'Flip vertical', tip: '', on: () => {data.scale.y = -data.scale.y; emit('update-data',data)} }
|
||||||
])
|
])
|
||||||
const onMenuItem = (v) => {
|
const onMenuItem = (v) => {
|
||||||
|
if (v.disabled) return
|
||||||
v.on && v.on()
|
v.on && v.on()
|
||||||
hideMenu()
|
hideMenu()
|
||||||
}
|
}
|
||||||
@@ -181,7 +190,14 @@
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
&:hover {
|
&.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
> .tip,
|
||||||
|
> .label {
|
||||||
|
color: rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:not(.disabled):hover {
|
||||||
background: #f3f3f3;
|
background: #f3f3f3;
|
||||||
}
|
}
|
||||||
> .label {
|
> .label {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
tabindex="0"
|
tabindex="0"
|
||||||
class="input"
|
class="input"
|
||||||
ref="inputRef"
|
ref="inputRef"
|
||||||
contenteditable="true"
|
:contenteditable="edit"
|
||||||
@input="onInput"
|
@input="onInput"
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
@paste.prevent
|
@paste.prevent
|
||||||
@@ -66,6 +66,7 @@
|
|||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.text {
|
.text {
|
||||||
|
user-select: none;
|
||||||
&.edit {
|
&.edit {
|
||||||
> .input {
|
> .input {
|
||||||
cursor: text;
|
cursor: text;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
<component
|
<component
|
||||||
:is="components[node.data.component]"
|
:is="components[node.data.component]"
|
||||||
:node="node"
|
:node="node"
|
||||||
|
:config="node.data"
|
||||||
:data="node.data.data"
|
:data="node.data.data"
|
||||||
v-bind="node.data"
|
v-bind="node.data"
|
||||||
@delete-node="deleteNode(node.id)"
|
@delete-node="deleteNode(node.id)"
|
||||||
@@ -35,8 +36,6 @@
|
|||||||
@update-data="(v) => (node.data.data = v)"
|
@update-data="(v) => (node.data.data = v)"
|
||||||
@bring-to-font="bringToFont(node.id)"
|
@bring-to-font="bringToFont(node.id)"
|
||||||
@send-to-back="sendToBack(node.id)"
|
@send-to-back="sendToBack(node.id)"
|
||||||
@flip-horizontal="flipHorizontal(node.id)"
|
|
||||||
@flip-vertical="flipVertical(node.id)"
|
|
||||||
/>
|
/>
|
||||||
</node>
|
</node>
|
||||||
</template>
|
</template>
|
||||||
@@ -152,15 +151,6 @@
|
|||||||
const sendToBack = (id) => {
|
const sendToBack = (id) => {
|
||||||
stateManager.sendToBack(id)
|
stateManager.sendToBack(id)
|
||||||
}
|
}
|
||||||
/** 水平翻转 */
|
|
||||||
const flipHorizontal = (id) => {
|
|
||||||
stateManager.setNodeFlip(id,'X')
|
|
||||||
}
|
|
||||||
/** 垂直翻转 */
|
|
||||||
const flipVertical = (id) => {
|
|
||||||
stateManager.setNodeFlip(id,'Y')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 导出流程
|
// 导出流程
|
||||||
const exportFlow = () => {
|
const exportFlow = () => {
|
||||||
// flowManager.exportFlow()
|
// flowManager.exportFlow()
|
||||||
@@ -188,9 +178,9 @@
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// window['vueFlow'] = vueFlow
|
// window['vueFlow'] = vueFlow
|
||||||
// window['nodes'] = nodes
|
// window['nodes'] = nodes
|
||||||
console.log(props.config)
|
|
||||||
nodeManager.createResultNode({
|
nodeManager.createResultNode({
|
||||||
data: {
|
data: {
|
||||||
|
disableDelete: true,
|
||||||
isHeader: false,
|
isHeader: false,
|
||||||
data: {
|
data: {
|
||||||
url: props.config.url
|
url: props.config.url
|
||||||
|
|||||||
@@ -16,9 +16,16 @@ export class StateManager {
|
|||||||
zoom: any
|
zoom: any
|
||||||
tool: any
|
tool: any
|
||||||
cursor: any
|
cursor: any
|
||||||
|
// 节点是否可拖动
|
||||||
nodesDraggable: any
|
nodesDraggable: any
|
||||||
|
// 拖动时是否可以平移画布
|
||||||
panOnDrag: any
|
panOnDrag: any
|
||||||
|
|
||||||
|
// 历史记录-撤回/重做
|
||||||
|
mxHistory: any
|
||||||
|
historyList: any
|
||||||
|
historyIndex: any
|
||||||
|
|
||||||
// 管理器
|
// 管理器
|
||||||
eventManager: any
|
eventManager: any
|
||||||
flowManager: any
|
flowManager: any
|
||||||
@@ -38,17 +45,14 @@ export class StateManager {
|
|||||||
this.cursor = ref("")
|
this.cursor = ref("")
|
||||||
this.nodesDraggable = ref(false)
|
this.nodesDraggable = ref(false)
|
||||||
this.panOnDrag = ref(false)
|
this.panOnDrag = ref(false)
|
||||||
|
this.mxHistory = ref(50)
|
||||||
|
this.historyList = ref([])
|
||||||
|
this.historyIndex = ref(0)
|
||||||
|
|
||||||
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) => {
|
||||||
const obj = node;
|
const obj = node;
|
||||||
// if (index === 0) {
|
|
||||||
// obj.type = NODE_TYPE.INPUT;
|
|
||||||
// } else if (index === this.nodes.value.length - 1) {
|
|
||||||
// obj.type = NODE_TYPE.OUTPUT;
|
|
||||||
// } else {
|
|
||||||
// obj.type = NODE_TYPE.SECONDARY;
|
|
||||||
// }
|
|
||||||
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)
|
||||||
@@ -68,17 +72,6 @@ export class StateManager {
|
|||||||
this.edges = computed(() => {
|
this.edges = computed(() => {
|
||||||
const arr = []
|
const arr = []
|
||||||
this.nodes.value.forEach((node, index) => {
|
this.nodes.value.forEach((node, index) => {
|
||||||
// if (index < this.nodes.value.length - 1) {
|
|
||||||
// const source = node.id
|
|
||||||
// const target = this.nodes.value[index + 1].id
|
|
||||||
// arr.push({
|
|
||||||
// id: `el-${source}-${target}`,
|
|
||||||
// source: source,
|
|
||||||
// target: target,
|
|
||||||
// selectable: false,
|
|
||||||
// type: 'default'
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
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)
|
||||||
if (superiorID && isSuperior) {
|
if (superiorID && isSuperior) {
|
||||||
@@ -99,28 +92,21 @@ export class StateManager {
|
|||||||
}
|
}
|
||||||
/** 添加节点 */
|
/** 添加节点 */
|
||||||
addNode(node: NodesItem) {
|
addNode(node: NodesItem) {
|
||||||
this.nodes.value.push(node)
|
this.nodes.value.push(node);
|
||||||
|
this.recordState()
|
||||||
}
|
}
|
||||||
/** 删除节点 */
|
/** 删除节点 */
|
||||||
deleteNode(id: string) {
|
deleteNode(id: string) {
|
||||||
this.nodes.value = this.nodes.value.filter((node: NodesItem) => node.id !== id)
|
this.nodes.value = this.nodes.value.filter((node: NodesItem) => node.id !== id)
|
||||||
|
this.recordState()
|
||||||
}
|
}
|
||||||
/** 获取节点 */
|
/** 获取节点 */
|
||||||
getNodeById(id: string) {
|
getNodeById(id: string) { return this.nodes.value.find((node: NodesItem) => node.id === id) }
|
||||||
return this.nodes.value.find((node: NodesItem) => node.id === id)
|
|
||||||
}
|
|
||||||
/** 获取下级节点 */
|
/** 获取下级节点 */
|
||||||
getSubordNodeByID(id: string) {
|
getSubordNodeByID(id: string) { return this.nodes.value.find((node: NodesItem) => node.data.superiorID === id) }
|
||||||
return this.nodes.value.find((node: NodesItem) => node.data.superiorID === id)
|
getLastNode() { return this.nodes.value[this.nodes.value.length - 1] }
|
||||||
}
|
|
||||||
getLastNode() {
|
|
||||||
return this.nodes.value[this.nodes.value.length - 1]
|
|
||||||
}
|
|
||||||
/** 设置工具 */
|
/** 设置工具 */
|
||||||
setTool(tool: string) {
|
setTool(tool: string) { this.tool.value = tool }
|
||||||
this.tool.value = tool
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 设置光标 */
|
/** 设置光标 */
|
||||||
setCursor(v: string) { this.cursor.value = v }
|
setCursor(v: string) { this.cursor.value = v }
|
||||||
/** 设置节点是否可拖动 */
|
/** 设置节点是否可拖动 */
|
||||||
@@ -139,15 +125,34 @@ export class StateManager {
|
|||||||
if (fromIndex === -1) return console.warn(`没有找到指定id:${id}`)
|
if (fromIndex === -1) return console.warn(`没有找到指定id:${id}`)
|
||||||
this.nodes.value.splice(0, 0, ...this.nodes.value.splice(fromIndex, 1))
|
this.nodes.value.splice(0, 0, ...this.nodes.value.splice(fromIndex, 1))
|
||||||
}
|
}
|
||||||
/** 设置水平或者垂直翻转 */
|
/** 记录状态 */
|
||||||
setNodeFlip(id,direction) {
|
recordState() {
|
||||||
const node = this.getNodeById(id)
|
if (this.historyIndex.value < this.historyList.value.length - 1) {
|
||||||
if (!node) return console.warn(`没有找到指定id:${id}`)
|
this.historyList.value.splice(this.historyIndex.value)
|
||||||
if (direction === 'X') {
|
|
||||||
node.data.scale.x = -node.data.scale.x
|
|
||||||
} else if (direction === 'Y') {
|
|
||||||
node.data.scale.y = -node.data.scale.y
|
|
||||||
}
|
}
|
||||||
console.log(node,direction)
|
const state = {
|
||||||
|
nodes: JSON.stringify(this.nodes.value)
|
||||||
}
|
}
|
||||||
|
this.historyList.value.push(state)
|
||||||
|
const size = this.historyList.value.length - this.mxHistory.value
|
||||||
|
if (size > 0) this.historyList.value.splice(0, size)
|
||||||
|
this.historyIndex.value = this.historyList.value.length - 1
|
||||||
|
}
|
||||||
|
/** 撤回状态 */
|
||||||
|
undoState() {
|
||||||
|
var index = this.historyIndex.value - 1
|
||||||
|
const state = this.historyList.value[index]
|
||||||
|
if (!state) return
|
||||||
|
this.historyIndex.value = index
|
||||||
|
this.nodes.value = JSON.parse(state.nodes)
|
||||||
|
}
|
||||||
|
/** 重做状态 */
|
||||||
|
redoState() {
|
||||||
|
var index = this.historyIndex.value + 1
|
||||||
|
const state = this.historyList.value[index]
|
||||||
|
if (!state) return
|
||||||
|
this.historyIndex.value = index
|
||||||
|
this.nodes.value = JSON.parse(state.nodes)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user