mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-21 18:37:43 +08:00
Feat:修改复制、剪切、粘贴逻辑;支持粘贴剪切板中的数据
This commit is contained in:
parent
f2b247e85c
commit
a76ec0dad8
@ -213,6 +213,10 @@ export const CONSTANTS = {
|
||||
TOP: 'top',
|
||||
RIGHT: 'right',
|
||||
BOTTOM: 'bottom'
|
||||
},
|
||||
PASTE_TYPE: {
|
||||
CLIP_BOARD: 'clipBoard',
|
||||
CANVAS: 'canvas'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -57,8 +57,11 @@ export default class KeyCommand {
|
||||
}
|
||||
Object.keys(this.shortcutMap).forEach(key => {
|
||||
if (this.checkKey(e, key)) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
// 粘贴事件不组织,因为要监听paste事件
|
||||
if (!this.checkKey(e, 'Control+v')) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
}
|
||||
this.shortcutMap[key].forEach(fn => {
|
||||
fn()
|
||||
})
|
||||
|
||||
@ -7,7 +7,13 @@ import Timeline from '../../layouts/Timeline'
|
||||
import VerticalTimeline from '../../layouts/VerticalTimeline'
|
||||
import Fishbone from '../../layouts/Fishbone'
|
||||
import TextEdit from './TextEdit'
|
||||
import { copyNodeTree, simpleDeepClone, walk, bfsWalk } from '../../utils'
|
||||
import {
|
||||
copyNodeTree,
|
||||
simpleDeepClone,
|
||||
walk,
|
||||
bfsWalk,
|
||||
loadImage
|
||||
} from '../../utils'
|
||||
import { shapeList } from './node/Shape'
|
||||
import { lineStyleProps } from '../../themes/default'
|
||||
import { CONSTANTS } from '../../constants/constant'
|
||||
@ -29,7 +35,7 @@ const layouts = {
|
||||
// 竖向时间轴
|
||||
[CONSTANTS.LAYOUT.VERTICAL_TIMELINE]: VerticalTimeline,
|
||||
// 鱼骨图
|
||||
[CONSTANTS.LAYOUT.FISHBONE]: Fishbone,
|
||||
[CONSTANTS.LAYOUT.FISHBONE]: Fishbone
|
||||
}
|
||||
|
||||
// 渲染
|
||||
@ -60,6 +66,12 @@ class Render {
|
||||
this.root = null
|
||||
// 文本编辑框,需要再bindEvent之前实例化,否则单击事件只能触发隐藏文本编辑框,而无法保存文本修改
|
||||
this.textEdit = new TextEdit(this)
|
||||
// 当前复制的数据
|
||||
this.lastBeingCopyData = null
|
||||
this.beingCopyData = null
|
||||
this.beingPasteText = ''
|
||||
this.beingPasteImgSize = 0
|
||||
this.currentBeingPasteType = ''
|
||||
// 布局
|
||||
this.setLayout()
|
||||
// 绑定事件
|
||||
@ -82,18 +94,24 @@ class Render {
|
||||
// 绑定事件
|
||||
bindEvent() {
|
||||
// 点击事件
|
||||
this.mindMap.on('draw_click', (e) => {
|
||||
this.mindMap.on('draw_click', e => {
|
||||
// 清除激活状态
|
||||
let isTrueClick = true
|
||||
let { useLeftKeySelectionRightKeyDrag } = this.mindMap.opt
|
||||
if (useLeftKeySelectionRightKeyDrag) {
|
||||
let mousedownPos = this.mindMap.event.mousedownPos
|
||||
isTrueClick = Math.abs(e.clientX - mousedownPos.x) <= 5 && Math.abs(e.clientY - mousedownPos.y) <= 5
|
||||
isTrueClick =
|
||||
Math.abs(e.clientX - mousedownPos.x) <= 5 &&
|
||||
Math.abs(e.clientY - mousedownPos.y) <= 5
|
||||
}
|
||||
if (isTrueClick && this.activeNodeList.length > 0) {
|
||||
this.mindMap.execCommand('CLEAR_ACTIVE_NODE')
|
||||
}
|
||||
})
|
||||
// 粘贴事件
|
||||
this.mindMap.on('paste', data => {
|
||||
this.onPaste(data)
|
||||
})
|
||||
}
|
||||
|
||||
// 注册命令
|
||||
@ -242,6 +260,14 @@ class Render {
|
||||
// 下移节点
|
||||
this.mindMap.keyCommand.addShortcut('Control+Down', this.downNode)
|
||||
// 复制节点、剪切节点、粘贴节点的快捷键需开发者自行注册实现,可参考demo
|
||||
this.copy = this.copy.bind(this)
|
||||
this.mindMap.keyCommand.addShortcut('Control+c', this.copy)
|
||||
this.mindMap.keyCommand.addShortcut('Control+v', () => {
|
||||
// 隐藏输入框可能会失去焦点,所以要重新聚焦
|
||||
this.textEdit.focusHiddenInput()
|
||||
})
|
||||
this.cut = this.cut.bind(this)
|
||||
this.mindMap.keyCommand.addShortcut('Control+x', this.cut)
|
||||
}
|
||||
|
||||
// 开启文字编辑,会禁用回车键和删除键相关快捷键防止冲突
|
||||
@ -281,7 +307,7 @@ class Render {
|
||||
// 计算布局
|
||||
this.layout.doLayout(root => {
|
||||
// 删除本次渲染时不再需要的节点
|
||||
Object.keys(this.lastNodeCache).forEach((uid) => {
|
||||
Object.keys(this.lastNodeCache).forEach(uid => {
|
||||
if (!this.nodeCache[uid]) {
|
||||
this.lastNodeCache[uid].destroy()
|
||||
if (this.lastNodeCache[uid].parent) {
|
||||
@ -301,7 +327,10 @@ class Render {
|
||||
this.render(callback, source)
|
||||
} else {
|
||||
// 触发一次保存,因为修改了渲染树的数据
|
||||
if (this.mindMap.richText && [CONSTANTS.CHANGE_THEME, CONSTANTS.SET_DATA].includes(source)) {
|
||||
if (
|
||||
this.mindMap.richText &&
|
||||
[CONSTANTS.CHANGE_THEME, CONSTANTS.SET_DATA].includes(source)
|
||||
) {
|
||||
this.mindMap.command.addHistory()
|
||||
}
|
||||
}
|
||||
@ -406,7 +435,7 @@ class Render {
|
||||
// 规范指定节点数据
|
||||
formatAppointNodes(appointNodes) {
|
||||
if (!appointNodes) return []
|
||||
return Array.isArray(appointNodes) ? appointNodes: [appointNodes]
|
||||
return Array.isArray(appointNodes) ? appointNodes : [appointNodes]
|
||||
}
|
||||
|
||||
// 插入同级节点,多个节点只会操作第一个节点
|
||||
@ -415,7 +444,10 @@ class Render {
|
||||
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
|
||||
return
|
||||
}
|
||||
let { defaultInsertSecondLevelNodeText, defaultInsertBelowSecondLevelNodeText } = this.mindMap.opt
|
||||
let {
|
||||
defaultInsertSecondLevelNodeText,
|
||||
defaultInsertBelowSecondLevelNodeText
|
||||
} = this.mindMap.opt
|
||||
let list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||||
let first = list[0]
|
||||
if (first.isGeneralization) {
|
||||
@ -424,7 +456,10 @@ class Render {
|
||||
if (first.isRoot) {
|
||||
this.insertChildNode(openEdit, appointNodes, appointData)
|
||||
} else {
|
||||
let text = first.layerIndex === 1 ? defaultInsertSecondLevelNodeText : defaultInsertBelowSecondLevelNodeText
|
||||
let text =
|
||||
first.layerIndex === 1
|
||||
? defaultInsertSecondLevelNodeText
|
||||
: defaultInsertBelowSecondLevelNodeText
|
||||
if (first.layerIndex === 1) {
|
||||
first.parent.destroy()
|
||||
}
|
||||
@ -451,7 +486,10 @@ class Render {
|
||||
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
|
||||
return
|
||||
}
|
||||
let { defaultInsertSecondLevelNodeText, defaultInsertBelowSecondLevelNodeText } = this.mindMap.opt
|
||||
let {
|
||||
defaultInsertSecondLevelNodeText,
|
||||
defaultInsertBelowSecondLevelNodeText
|
||||
} = this.mindMap.opt
|
||||
let list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||||
list.forEach(node => {
|
||||
if (node.isGeneralization) {
|
||||
@ -460,7 +498,9 @@ class Render {
|
||||
if (!node.nodeData.children) {
|
||||
node.nodeData.children = []
|
||||
}
|
||||
let text = node.isRoot ? defaultInsertSecondLevelNodeText : defaultInsertBelowSecondLevelNodeText
|
||||
let text = node.isRoot
|
||||
? defaultInsertSecondLevelNodeText
|
||||
: defaultInsertBelowSecondLevelNodeText
|
||||
let isRichText = !!this.mindMap.richText
|
||||
node.nodeData.children.push({
|
||||
inserting: openEdit,
|
||||
@ -536,13 +576,81 @@ class Render {
|
||||
this.mindMap.render()
|
||||
}
|
||||
|
||||
// 复制节点
|
||||
copy() {
|
||||
this.beingCopyData = this.copyNode()
|
||||
}
|
||||
|
||||
// 剪切节点
|
||||
cut() {
|
||||
this.mindMap.execCommand('CUT_NODE', copyData => {
|
||||
this.beingCopyData = copyData
|
||||
})
|
||||
}
|
||||
|
||||
// 粘贴节点
|
||||
paste() {
|
||||
if (this.beingCopyData) {
|
||||
this.mindMap.execCommand('PASTE_NODE', this.beingCopyData)
|
||||
}
|
||||
}
|
||||
|
||||
// 粘贴事件
|
||||
async onPaste({ text, img }) {
|
||||
// 检查剪切板数据是否有变化
|
||||
// 通过图片大小来判断图片是否发生变化,可能是不准确的,但是目前没有其他好方法
|
||||
const imgSize = img ? img.size : 0
|
||||
if (this.beingPasteText !== text || this.beingPasteImgSize !== imgSize) {
|
||||
this.currentBeingPasteType = CONSTANTS.PASTE_TYPE.CLIP_BOARD
|
||||
this.beingPasteText = text
|
||||
this.beingPasteImgSize = imgSize
|
||||
}
|
||||
// 检查要粘贴的节点数据是否有变化,节点优先级高于剪切板
|
||||
if (this.lastBeingCopyData !== this.beingCopyData) {
|
||||
this.lastBeingCopyData = this.beingCopyData
|
||||
this.currentBeingPasteType = CONSTANTS.PASTE_TYPE.CANVAS
|
||||
}
|
||||
// 粘贴剪切板的数据
|
||||
if (this.currentBeingPasteType === CONSTANTS.PASTE_TYPE.CLIP_BOARD) {
|
||||
// 存在文本,则创建子节点
|
||||
if (text) {
|
||||
this.mindMap.execCommand('INSERT_CHILD_NODE', false, [], {
|
||||
text
|
||||
})
|
||||
}
|
||||
// 存在图片,则添加到当前激活节点
|
||||
if (img) {
|
||||
try {
|
||||
let imgData = await loadImage(img)
|
||||
if (this.activeNodeList.length > 0) {
|
||||
this.activeNodeList.forEach(node => {
|
||||
this.mindMap.execCommand('SET_NODE_IMAGE', node, {
|
||||
url: imgData.url,
|
||||
title: '',
|
||||
width: imgData.size.width,
|
||||
height: imgData.size.height
|
||||
})
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 粘贴节点数据
|
||||
this.paste()
|
||||
}
|
||||
}
|
||||
|
||||
// 将节点移动到另一个节点的前面
|
||||
insertBefore(node, exist) {
|
||||
if (node.isRoot) {
|
||||
return
|
||||
}
|
||||
// 如果是二级节点变成了下级节点,或是下级节点变成了二级节点,节点样式需要更新
|
||||
let nodeLayerChanged = (node.layerIndex === 1 && exist.layerIndex !== 1) || (node.layerIndex !== 1 && exist.layerIndex === 1)
|
||||
let nodeLayerChanged =
|
||||
(node.layerIndex === 1 && exist.layerIndex !== 1) ||
|
||||
(node.layerIndex !== 1 && exist.layerIndex === 1)
|
||||
// 移动节点
|
||||
let nodeParent = node.parent
|
||||
let nodeBorthers = nodeParent.children
|
||||
@ -579,7 +687,9 @@ class Render {
|
||||
return
|
||||
}
|
||||
// 如果是二级节点变成了下级节点,或是下级节点变成了二级节点,节点样式需要更新
|
||||
let nodeLayerChanged = (node.layerIndex === 1 && exist.layerIndex !== 1) || (node.layerIndex !== 1 && exist.layerIndex === 1)
|
||||
let nodeLayerChanged =
|
||||
(node.layerIndex === 1 && exist.layerIndex !== 1) ||
|
||||
(node.layerIndex !== 1 && exist.layerIndex === 1)
|
||||
// 移动节点
|
||||
let nodeParent = node.parent
|
||||
let nodeBorthers = nodeParent.children
|
||||
@ -619,7 +729,7 @@ class Render {
|
||||
}
|
||||
let isAppointNodes = appointNodes.length > 0
|
||||
let list = isAppointNodes ? appointNodes : this.activeNodeList
|
||||
let root = list.find((node) => {
|
||||
let root = list.find(node => {
|
||||
return node.isRoot
|
||||
})
|
||||
if (root) {
|
||||
@ -705,7 +815,7 @@ class Render {
|
||||
|
||||
// 粘贴节点到节点
|
||||
pasteNode(data) {
|
||||
if (this.activeNodeList.length <= 0) {
|
||||
if (this.activeNodeList.length <= 0 || !data) {
|
||||
return
|
||||
}
|
||||
this.activeNodeList.forEach(item => {
|
||||
@ -997,7 +1107,7 @@ class Render {
|
||||
if (targetNode) {
|
||||
targetNode.active()
|
||||
this.moveNodeToCenter(targetNode)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -1038,7 +1148,7 @@ class Render {
|
||||
this.mindMap.view.setScale(1)
|
||||
}
|
||||
|
||||
// 展开到指定uid的节点
|
||||
// 展开到指定uid的节点
|
||||
expandToNodeUid(uid, callback = () => {}) {
|
||||
let parentsList = []
|
||||
const cache = {}
|
||||
@ -1047,11 +1157,11 @@ class Render {
|
||||
parentsList = parent ? [...cache[parent.data.uid], parent] : []
|
||||
return 'stop'
|
||||
} else {
|
||||
cache[node.data.uid] = parent ? [...cache[parent.data.uid], parent]: []
|
||||
cache[node.data.uid] = parent ? [...cache[parent.data.uid], parent] : []
|
||||
}
|
||||
})
|
||||
let needRender = false
|
||||
parentsList.forEach((node) => {
|
||||
parentsList.forEach(node => {
|
||||
if (!node.data.expand) {
|
||||
needRender = true
|
||||
node.data.expand = true
|
||||
@ -1067,11 +1177,11 @@ class Render {
|
||||
// 根据uid找到对应的节点实例
|
||||
findNodeByUid(uid) {
|
||||
let res = null
|
||||
walk(this.root, null, (node) => {
|
||||
walk(this.root, null, node => {
|
||||
if (node.nodeData.data.uid === uid) {
|
||||
res = node
|
||||
return true
|
||||
}
|
||||
}
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
@ -10,11 +10,14 @@ export default class TextEdit {
|
||||
this.currentNode = null
|
||||
// 文本编辑框
|
||||
this.textEditNode = null
|
||||
// 隐藏的文本输入框
|
||||
this.hiddenInputEl = null
|
||||
// 文本编辑框是否显示
|
||||
this.showTextEdit = false
|
||||
// 如果编辑过程中缩放画布了,那么缓存当前编辑的内容
|
||||
this.cacheEditingText = ''
|
||||
this.bindEvent()
|
||||
this.createHiddenInput()
|
||||
}
|
||||
|
||||
// 事件
|
||||
@ -46,6 +49,10 @@ export default class TextEdit {
|
||||
this.mindMap.on('before_node_active', () => {
|
||||
this.hideEditTextBox()
|
||||
})
|
||||
// 节点激活事件
|
||||
this.mindMap.on('node_active', () => {
|
||||
this.focusHiddenInput()
|
||||
})
|
||||
// 注册编辑快捷键
|
||||
this.mindMap.keyCommand.addShortcut('F2', () => {
|
||||
if (this.renderer.activeNodeList.length <= 0) {
|
||||
@ -56,6 +63,43 @@ export default class TextEdit {
|
||||
this.mindMap.on('scale', this.onScale)
|
||||
}
|
||||
|
||||
// 创建一个隐藏的文本输入框
|
||||
createHiddenInput() {
|
||||
if (this.hiddenInputEl) return
|
||||
this.hiddenInputEl = document.createElement('input')
|
||||
this.hiddenInputEl.type = 'text'
|
||||
this.hiddenInputEl.style.cssText = `
|
||||
position: fixed;
|
||||
left: -99999px;
|
||||
top: -99999px;
|
||||
`
|
||||
// 监听粘贴事件
|
||||
this.hiddenInputEl.addEventListener('paste', async event => {
|
||||
event.preventDefault()
|
||||
const text = (event.clipboardData || window.clipboardData).getData('text')
|
||||
const files = event.clipboardData.files
|
||||
let img = null
|
||||
if (files.length > 0) {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
if (/^image\//.test(files[i].type)) {
|
||||
img = files[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
this.mindMap.emit('paste', {
|
||||
text,
|
||||
img
|
||||
})
|
||||
})
|
||||
document.body.appendChild(this.hiddenInputEl)
|
||||
}
|
||||
|
||||
// 让隐藏的文本输入框聚焦
|
||||
focusHiddenInput() {
|
||||
if (this.hiddenInputEl) this.hiddenInputEl.focus()
|
||||
}
|
||||
|
||||
// 注册临时快捷键
|
||||
registerTmpShortcut() {
|
||||
// 注册回车快捷键
|
||||
@ -96,7 +140,8 @@ export default class TextEdit {
|
||||
onScale() {
|
||||
if (!this.currentNode) return
|
||||
if (this.mindMap.richText) {
|
||||
this.mindMap.richText.cacheEditingText = this.mindMap.richText.getEditText()
|
||||
this.mindMap.richText.cacheEditingText =
|
||||
this.mindMap.richText.getEditText()
|
||||
this.mindMap.richText.showTextEdit = false
|
||||
} else {
|
||||
this.cacheEditingText = this.getEditText()
|
||||
@ -124,7 +169,9 @@ export default class TextEdit {
|
||||
let scale = this.mindMap.view.scale
|
||||
let lineHeight = node.style.merge('lineHeight')
|
||||
let fontSize = node.style.merge('fontSize')
|
||||
let textLines = (this.cacheEditingText || node.nodeData.data.text).split(/\n/gim)
|
||||
let textLines = (this.cacheEditingText || node.nodeData.data.text).split(
|
||||
/\n/gim
|
||||
)
|
||||
let isMultiLine = node._textData.node.attr('data-ismultiLine') === 'true'
|
||||
node.style.domText(this.textEditNode, scale, isMultiLine)
|
||||
this.textEditNode.style.zIndex = this.mindMap.opt.nodeTextEditZIndex
|
||||
@ -134,9 +181,12 @@ export default class TextEdit {
|
||||
this.textEditNode.style.left = rect.left + 'px'
|
||||
this.textEditNode.style.top = rect.top + 'px'
|
||||
this.textEditNode.style.display = 'block'
|
||||
this.textEditNode.style.maxWidth = this.mindMap.opt.textAutoWrapWidth * scale + 'px'
|
||||
this.textEditNode.style.maxWidth =
|
||||
this.mindMap.opt.textAutoWrapWidth * scale + 'px'
|
||||
if (isMultiLine && lineHeight !== 1) {
|
||||
this.textEditNode.style.transform = `translateY(${-((lineHeight * fontSize - fontSize) / 2) * scale}px)`
|
||||
this.textEditNode.style.transform = `translateY(${
|
||||
-((lineHeight * fontSize - fontSize) / 2) * scale
|
||||
}px)`
|
||||
}
|
||||
this.showTextEdit = true
|
||||
// 选中文本
|
||||
|
||||
@ -433,4 +433,23 @@ export const getImageSize = src => {
|
||||
// 创建节点唯一的id
|
||||
export const createUid = () => {
|
||||
return uuidv4()
|
||||
}
|
||||
}
|
||||
|
||||
// 加载图片文件
|
||||
export const loadImage = imgFile => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let fr = new FileReader()
|
||||
fr.readAsDataURL(imgFile)
|
||||
fr.onload = async e => {
|
||||
let url = e.target.result
|
||||
let size = await getImageSize(url)
|
||||
resolve({
|
||||
url,
|
||||
size
|
||||
})
|
||||
}
|
||||
fr.onerror = error => {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -55,7 +55,6 @@
|
||||
</div>
|
||||
<div
|
||||
class="item"
|
||||
:class="{ disabled: copyData === null }"
|
||||
@click="exec('PASTE_NODE')"
|
||||
>
|
||||
{{ $t('contextmenu.pasteNode') }}
|
||||
@ -123,7 +122,6 @@ export default {
|
||||
left: 0,
|
||||
top: 0,
|
||||
node: null,
|
||||
copyData: null,
|
||||
type: '',
|
||||
isMousedown: false,
|
||||
mosuedownX: 0,
|
||||
@ -180,10 +178,6 @@ export default {
|
||||
this.$bus.$on('expand_btn_click', this.hide)
|
||||
this.$bus.$on('svg_mousedown', this.onMousedown)
|
||||
this.$bus.$on('mouseup', this.onMouseup)
|
||||
// 注册快捷键
|
||||
this.mindMap.keyCommand.addShortcut('Control+c', this.copy)
|
||||
this.mindMap.keyCommand.addShortcut('Control+v', this.paste)
|
||||
this.mindMap.keyCommand.addShortcut('Control+x', this.cut)
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$bus.$off('node_contextmenu', this.show)
|
||||
@ -192,10 +186,6 @@ export default {
|
||||
this.$bus.$off('expand_btn_click', this.hide)
|
||||
this.$bus.$on('svg_mousedown', this.onMousedown)
|
||||
this.$bus.$on('mouseup', this.onMouseup)
|
||||
// 移除快捷键
|
||||
this.mindMap.keyCommand.removeShortcut('Control+c', this.copy)
|
||||
this.mindMap.keyCommand.removeShortcut('Control+v', this.paste)
|
||||
this.mindMap.keyCommand.removeShortcut('Control+x', this.cut)
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(['setLocalConfig']),
|
||||
@ -282,15 +272,13 @@ export default {
|
||||
}
|
||||
switch (key) {
|
||||
case 'COPY_NODE':
|
||||
this.copyData = this.mindMap.renderer.copyNode()
|
||||
this.mindMap.renderer.copy()
|
||||
break
|
||||
case 'CUT_NODE':
|
||||
this.$bus.$emit('execCommand', key, copyData => {
|
||||
this.copyData = copyData
|
||||
})
|
||||
this.mindMap.renderer.cut()
|
||||
break
|
||||
case 'PASTE_NODE':
|
||||
this.$bus.$emit('execCommand', key, this.copyData)
|
||||
this.mindMap.renderer.paste()
|
||||
break
|
||||
case 'RETURN_CENTER':
|
||||
this.mindMap.view.reset()
|
||||
@ -308,33 +296,6 @@ export default {
|
||||
break
|
||||
}
|
||||
this.hide()
|
||||
},
|
||||
|
||||
/**
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-08-04 14:25:45
|
||||
* @Desc: 复制
|
||||
*/
|
||||
copy() {
|
||||
this.exec('COPY_NODE')
|
||||
},
|
||||
|
||||
/**
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-08-04 14:26:43
|
||||
* @Desc: 粘贴
|
||||
*/
|
||||
paste() {
|
||||
this.exec('PASTE_NODE')
|
||||
},
|
||||
|
||||
/**
|
||||
* @Author: 王林25
|
||||
* @Date: 2022-08-04 14:27:32
|
||||
* @Desc: 剪切
|
||||
*/
|
||||
cut() {
|
||||
this.exec('CUT_NODE')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user