Feat:修改复制、剪切、粘贴逻辑;支持粘贴剪切板中的数据
This commit is contained in:
parent
f2b247e85c
commit
a76ec0dad8
@ -213,6 +213,10 @@ export const CONSTANTS = {
|
|||||||
TOP: 'top',
|
TOP: 'top',
|
||||||
RIGHT: 'right',
|
RIGHT: 'right',
|
||||||
BOTTOM: 'bottom'
|
BOTTOM: 'bottom'
|
||||||
|
},
|
||||||
|
PASTE_TYPE: {
|
||||||
|
CLIP_BOARD: 'clipBoard',
|
||||||
|
CANVAS: 'canvas'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -57,8 +57,11 @@ export default class KeyCommand {
|
|||||||
}
|
}
|
||||||
Object.keys(this.shortcutMap).forEach(key => {
|
Object.keys(this.shortcutMap).forEach(key => {
|
||||||
if (this.checkKey(e, key)) {
|
if (this.checkKey(e, key)) {
|
||||||
e.stopPropagation()
|
// 粘贴事件不组织,因为要监听paste事件
|
||||||
e.preventDefault()
|
if (!this.checkKey(e, 'Control+v')) {
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
this.shortcutMap[key].forEach(fn => {
|
this.shortcutMap[key].forEach(fn => {
|
||||||
fn()
|
fn()
|
||||||
})
|
})
|
||||||
|
|||||||
@ -7,7 +7,13 @@ import Timeline from '../../layouts/Timeline'
|
|||||||
import VerticalTimeline from '../../layouts/VerticalTimeline'
|
import VerticalTimeline from '../../layouts/VerticalTimeline'
|
||||||
import Fishbone from '../../layouts/Fishbone'
|
import Fishbone from '../../layouts/Fishbone'
|
||||||
import TextEdit from './TextEdit'
|
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 { shapeList } from './node/Shape'
|
||||||
import { lineStyleProps } from '../../themes/default'
|
import { lineStyleProps } from '../../themes/default'
|
||||||
import { CONSTANTS } from '../../constants/constant'
|
import { CONSTANTS } from '../../constants/constant'
|
||||||
@ -29,7 +35,7 @@ const layouts = {
|
|||||||
// 竖向时间轴
|
// 竖向时间轴
|
||||||
[CONSTANTS.LAYOUT.VERTICAL_TIMELINE]: VerticalTimeline,
|
[CONSTANTS.LAYOUT.VERTICAL_TIMELINE]: VerticalTimeline,
|
||||||
// 鱼骨图
|
// 鱼骨图
|
||||||
[CONSTANTS.LAYOUT.FISHBONE]: Fishbone,
|
[CONSTANTS.LAYOUT.FISHBONE]: Fishbone
|
||||||
}
|
}
|
||||||
|
|
||||||
// 渲染
|
// 渲染
|
||||||
@ -60,6 +66,12 @@ class Render {
|
|||||||
this.root = null
|
this.root = null
|
||||||
// 文本编辑框,需要再bindEvent之前实例化,否则单击事件只能触发隐藏文本编辑框,而无法保存文本修改
|
// 文本编辑框,需要再bindEvent之前实例化,否则单击事件只能触发隐藏文本编辑框,而无法保存文本修改
|
||||||
this.textEdit = new TextEdit(this)
|
this.textEdit = new TextEdit(this)
|
||||||
|
// 当前复制的数据
|
||||||
|
this.lastBeingCopyData = null
|
||||||
|
this.beingCopyData = null
|
||||||
|
this.beingPasteText = ''
|
||||||
|
this.beingPasteImgSize = 0
|
||||||
|
this.currentBeingPasteType = ''
|
||||||
// 布局
|
// 布局
|
||||||
this.setLayout()
|
this.setLayout()
|
||||||
// 绑定事件
|
// 绑定事件
|
||||||
@ -82,18 +94,24 @@ class Render {
|
|||||||
// 绑定事件
|
// 绑定事件
|
||||||
bindEvent() {
|
bindEvent() {
|
||||||
// 点击事件
|
// 点击事件
|
||||||
this.mindMap.on('draw_click', (e) => {
|
this.mindMap.on('draw_click', e => {
|
||||||
// 清除激活状态
|
// 清除激活状态
|
||||||
let isTrueClick = true
|
let isTrueClick = true
|
||||||
let { useLeftKeySelectionRightKeyDrag } = this.mindMap.opt
|
let { useLeftKeySelectionRightKeyDrag } = this.mindMap.opt
|
||||||
if (useLeftKeySelectionRightKeyDrag) {
|
if (useLeftKeySelectionRightKeyDrag) {
|
||||||
let mousedownPos = this.mindMap.event.mousedownPos
|
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) {
|
if (isTrueClick && this.activeNodeList.length > 0) {
|
||||||
this.mindMap.execCommand('CLEAR_ACTIVE_NODE')
|
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)
|
this.mindMap.keyCommand.addShortcut('Control+Down', this.downNode)
|
||||||
// 复制节点、剪切节点、粘贴节点的快捷键需开发者自行注册实现,可参考demo
|
// 复制节点、剪切节点、粘贴节点的快捷键需开发者自行注册实现,可参考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 => {
|
this.layout.doLayout(root => {
|
||||||
// 删除本次渲染时不再需要的节点
|
// 删除本次渲染时不再需要的节点
|
||||||
Object.keys(this.lastNodeCache).forEach((uid) => {
|
Object.keys(this.lastNodeCache).forEach(uid => {
|
||||||
if (!this.nodeCache[uid]) {
|
if (!this.nodeCache[uid]) {
|
||||||
this.lastNodeCache[uid].destroy()
|
this.lastNodeCache[uid].destroy()
|
||||||
if (this.lastNodeCache[uid].parent) {
|
if (this.lastNodeCache[uid].parent) {
|
||||||
@ -301,7 +327,10 @@ class Render {
|
|||||||
this.render(callback, source)
|
this.render(callback, source)
|
||||||
} else {
|
} 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()
|
this.mindMap.command.addHistory()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -406,7 +435,7 @@ class Render {
|
|||||||
// 规范指定节点数据
|
// 规范指定节点数据
|
||||||
formatAppointNodes(appointNodes) {
|
formatAppointNodes(appointNodes) {
|
||||||
if (!appointNodes) return []
|
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) {
|
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let { defaultInsertSecondLevelNodeText, defaultInsertBelowSecondLevelNodeText } = this.mindMap.opt
|
let {
|
||||||
|
defaultInsertSecondLevelNodeText,
|
||||||
|
defaultInsertBelowSecondLevelNodeText
|
||||||
|
} = this.mindMap.opt
|
||||||
let list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
let list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||||||
let first = list[0]
|
let first = list[0]
|
||||||
if (first.isGeneralization) {
|
if (first.isGeneralization) {
|
||||||
@ -424,7 +456,10 @@ class Render {
|
|||||||
if (first.isRoot) {
|
if (first.isRoot) {
|
||||||
this.insertChildNode(openEdit, appointNodes, appointData)
|
this.insertChildNode(openEdit, appointNodes, appointData)
|
||||||
} else {
|
} else {
|
||||||
let text = first.layerIndex === 1 ? defaultInsertSecondLevelNodeText : defaultInsertBelowSecondLevelNodeText
|
let text =
|
||||||
|
first.layerIndex === 1
|
||||||
|
? defaultInsertSecondLevelNodeText
|
||||||
|
: defaultInsertBelowSecondLevelNodeText
|
||||||
if (first.layerIndex === 1) {
|
if (first.layerIndex === 1) {
|
||||||
first.parent.destroy()
|
first.parent.destroy()
|
||||||
}
|
}
|
||||||
@ -451,7 +486,10 @@ class Render {
|
|||||||
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
|
if (this.activeNodeList.length <= 0 && appointNodes.length <= 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let { defaultInsertSecondLevelNodeText, defaultInsertBelowSecondLevelNodeText } = this.mindMap.opt
|
let {
|
||||||
|
defaultInsertSecondLevelNodeText,
|
||||||
|
defaultInsertBelowSecondLevelNodeText
|
||||||
|
} = this.mindMap.opt
|
||||||
let list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
let list = appointNodes.length > 0 ? appointNodes : this.activeNodeList
|
||||||
list.forEach(node => {
|
list.forEach(node => {
|
||||||
if (node.isGeneralization) {
|
if (node.isGeneralization) {
|
||||||
@ -460,7 +498,9 @@ class Render {
|
|||||||
if (!node.nodeData.children) {
|
if (!node.nodeData.children) {
|
||||||
node.nodeData.children = []
|
node.nodeData.children = []
|
||||||
}
|
}
|
||||||
let text = node.isRoot ? defaultInsertSecondLevelNodeText : defaultInsertBelowSecondLevelNodeText
|
let text = node.isRoot
|
||||||
|
? defaultInsertSecondLevelNodeText
|
||||||
|
: defaultInsertBelowSecondLevelNodeText
|
||||||
let isRichText = !!this.mindMap.richText
|
let isRichText = !!this.mindMap.richText
|
||||||
node.nodeData.children.push({
|
node.nodeData.children.push({
|
||||||
inserting: openEdit,
|
inserting: openEdit,
|
||||||
@ -536,13 +576,81 @@ class Render {
|
|||||||
this.mindMap.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) {
|
insertBefore(node, exist) {
|
||||||
if (node.isRoot) {
|
if (node.isRoot) {
|
||||||
return
|
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 nodeParent = node.parent
|
||||||
let nodeBorthers = nodeParent.children
|
let nodeBorthers = nodeParent.children
|
||||||
@ -579,7 +687,9 @@ class Render {
|
|||||||
return
|
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 nodeParent = node.parent
|
||||||
let nodeBorthers = nodeParent.children
|
let nodeBorthers = nodeParent.children
|
||||||
@ -619,7 +729,7 @@ class Render {
|
|||||||
}
|
}
|
||||||
let isAppointNodes = appointNodes.length > 0
|
let isAppointNodes = appointNodes.length > 0
|
||||||
let list = isAppointNodes ? appointNodes : this.activeNodeList
|
let list = isAppointNodes ? appointNodes : this.activeNodeList
|
||||||
let root = list.find((node) => {
|
let root = list.find(node => {
|
||||||
return node.isRoot
|
return node.isRoot
|
||||||
})
|
})
|
||||||
if (root) {
|
if (root) {
|
||||||
@ -705,7 +815,7 @@ class Render {
|
|||||||
|
|
||||||
// 粘贴节点到节点
|
// 粘贴节点到节点
|
||||||
pasteNode(data) {
|
pasteNode(data) {
|
||||||
if (this.activeNodeList.length <= 0) {
|
if (this.activeNodeList.length <= 0 || !data) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.activeNodeList.forEach(item => {
|
this.activeNodeList.forEach(item => {
|
||||||
@ -997,7 +1107,7 @@ class Render {
|
|||||||
if (targetNode) {
|
if (targetNode) {
|
||||||
targetNode.active()
|
targetNode.active()
|
||||||
this.moveNodeToCenter(targetNode)
|
this.moveNodeToCenter(targetNode)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1038,7 +1148,7 @@ class Render {
|
|||||||
this.mindMap.view.setScale(1)
|
this.mindMap.view.setScale(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 展开到指定uid的节点
|
// 展开到指定uid的节点
|
||||||
expandToNodeUid(uid, callback = () => {}) {
|
expandToNodeUid(uid, callback = () => {}) {
|
||||||
let parentsList = []
|
let parentsList = []
|
||||||
const cache = {}
|
const cache = {}
|
||||||
@ -1047,11 +1157,11 @@ class Render {
|
|||||||
parentsList = parent ? [...cache[parent.data.uid], parent] : []
|
parentsList = parent ? [...cache[parent.data.uid], parent] : []
|
||||||
return 'stop'
|
return 'stop'
|
||||||
} else {
|
} else {
|
||||||
cache[node.data.uid] = parent ? [...cache[parent.data.uid], parent]: []
|
cache[node.data.uid] = parent ? [...cache[parent.data.uid], parent] : []
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
let needRender = false
|
let needRender = false
|
||||||
parentsList.forEach((node) => {
|
parentsList.forEach(node => {
|
||||||
if (!node.data.expand) {
|
if (!node.data.expand) {
|
||||||
needRender = true
|
needRender = true
|
||||||
node.data.expand = true
|
node.data.expand = true
|
||||||
@ -1067,11 +1177,11 @@ class Render {
|
|||||||
// 根据uid找到对应的节点实例
|
// 根据uid找到对应的节点实例
|
||||||
findNodeByUid(uid) {
|
findNodeByUid(uid) {
|
||||||
let res = null
|
let res = null
|
||||||
walk(this.root, null, (node) => {
|
walk(this.root, null, node => {
|
||||||
if (node.nodeData.data.uid === uid) {
|
if (node.nodeData.data.uid === uid) {
|
||||||
res = node
|
res = node
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,11 +10,14 @@ export default class TextEdit {
|
|||||||
this.currentNode = null
|
this.currentNode = null
|
||||||
// 文本编辑框
|
// 文本编辑框
|
||||||
this.textEditNode = null
|
this.textEditNode = null
|
||||||
|
// 隐藏的文本输入框
|
||||||
|
this.hiddenInputEl = null
|
||||||
// 文本编辑框是否显示
|
// 文本编辑框是否显示
|
||||||
this.showTextEdit = false
|
this.showTextEdit = false
|
||||||
// 如果编辑过程中缩放画布了,那么缓存当前编辑的内容
|
// 如果编辑过程中缩放画布了,那么缓存当前编辑的内容
|
||||||
this.cacheEditingText = ''
|
this.cacheEditingText = ''
|
||||||
this.bindEvent()
|
this.bindEvent()
|
||||||
|
this.createHiddenInput()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 事件
|
// 事件
|
||||||
@ -46,6 +49,10 @@ export default class TextEdit {
|
|||||||
this.mindMap.on('before_node_active', () => {
|
this.mindMap.on('before_node_active', () => {
|
||||||
this.hideEditTextBox()
|
this.hideEditTextBox()
|
||||||
})
|
})
|
||||||
|
// 节点激活事件
|
||||||
|
this.mindMap.on('node_active', () => {
|
||||||
|
this.focusHiddenInput()
|
||||||
|
})
|
||||||
// 注册编辑快捷键
|
// 注册编辑快捷键
|
||||||
this.mindMap.keyCommand.addShortcut('F2', () => {
|
this.mindMap.keyCommand.addShortcut('F2', () => {
|
||||||
if (this.renderer.activeNodeList.length <= 0) {
|
if (this.renderer.activeNodeList.length <= 0) {
|
||||||
@ -56,6 +63,43 @@ export default class TextEdit {
|
|||||||
this.mindMap.on('scale', this.onScale)
|
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() {
|
registerTmpShortcut() {
|
||||||
// 注册回车快捷键
|
// 注册回车快捷键
|
||||||
@ -96,7 +140,8 @@ export default class TextEdit {
|
|||||||
onScale() {
|
onScale() {
|
||||||
if (!this.currentNode) return
|
if (!this.currentNode) return
|
||||||
if (this.mindMap.richText) {
|
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
|
this.mindMap.richText.showTextEdit = false
|
||||||
} else {
|
} else {
|
||||||
this.cacheEditingText = this.getEditText()
|
this.cacheEditingText = this.getEditText()
|
||||||
@ -124,7 +169,9 @@ export default class TextEdit {
|
|||||||
let scale = this.mindMap.view.scale
|
let scale = this.mindMap.view.scale
|
||||||
let lineHeight = node.style.merge('lineHeight')
|
let lineHeight = node.style.merge('lineHeight')
|
||||||
let fontSize = node.style.merge('fontSize')
|
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'
|
let isMultiLine = node._textData.node.attr('data-ismultiLine') === 'true'
|
||||||
node.style.domText(this.textEditNode, scale, isMultiLine)
|
node.style.domText(this.textEditNode, scale, isMultiLine)
|
||||||
this.textEditNode.style.zIndex = this.mindMap.opt.nodeTextEditZIndex
|
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.left = rect.left + 'px'
|
||||||
this.textEditNode.style.top = rect.top + 'px'
|
this.textEditNode.style.top = rect.top + 'px'
|
||||||
this.textEditNode.style.display = 'block'
|
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) {
|
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
|
this.showTextEdit = true
|
||||||
// 选中文本
|
// 选中文本
|
||||||
|
|||||||
@ -433,4 +433,23 @@ export const getImageSize = src => {
|
|||||||
// 创建节点唯一的id
|
// 创建节点唯一的id
|
||||||
export const createUid = () => {
|
export const createUid = () => {
|
||||||
return uuidv4()
|
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>
|
||||||
<div
|
<div
|
||||||
class="item"
|
class="item"
|
||||||
:class="{ disabled: copyData === null }"
|
|
||||||
@click="exec('PASTE_NODE')"
|
@click="exec('PASTE_NODE')"
|
||||||
>
|
>
|
||||||
{{ $t('contextmenu.pasteNode') }}
|
{{ $t('contextmenu.pasteNode') }}
|
||||||
@ -123,7 +122,6 @@ export default {
|
|||||||
left: 0,
|
left: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
node: null,
|
node: null,
|
||||||
copyData: null,
|
|
||||||
type: '',
|
type: '',
|
||||||
isMousedown: false,
|
isMousedown: false,
|
||||||
mosuedownX: 0,
|
mosuedownX: 0,
|
||||||
@ -180,10 +178,6 @@ export default {
|
|||||||
this.$bus.$on('expand_btn_click', this.hide)
|
this.$bus.$on('expand_btn_click', this.hide)
|
||||||
this.$bus.$on('svg_mousedown', this.onMousedown)
|
this.$bus.$on('svg_mousedown', this.onMousedown)
|
||||||
this.$bus.$on('mouseup', this.onMouseup)
|
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() {
|
beforeDestroy() {
|
||||||
this.$bus.$off('node_contextmenu', this.show)
|
this.$bus.$off('node_contextmenu', this.show)
|
||||||
@ -192,10 +186,6 @@ export default {
|
|||||||
this.$bus.$off('expand_btn_click', this.hide)
|
this.$bus.$off('expand_btn_click', this.hide)
|
||||||
this.$bus.$on('svg_mousedown', this.onMousedown)
|
this.$bus.$on('svg_mousedown', this.onMousedown)
|
||||||
this.$bus.$on('mouseup', this.onMouseup)
|
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: {
|
methods: {
|
||||||
...mapMutations(['setLocalConfig']),
|
...mapMutations(['setLocalConfig']),
|
||||||
@ -282,15 +272,13 @@ export default {
|
|||||||
}
|
}
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'COPY_NODE':
|
case 'COPY_NODE':
|
||||||
this.copyData = this.mindMap.renderer.copyNode()
|
this.mindMap.renderer.copy()
|
||||||
break
|
break
|
||||||
case 'CUT_NODE':
|
case 'CUT_NODE':
|
||||||
this.$bus.$emit('execCommand', key, copyData => {
|
this.mindMap.renderer.cut()
|
||||||
this.copyData = copyData
|
|
||||||
})
|
|
||||||
break
|
break
|
||||||
case 'PASTE_NODE':
|
case 'PASTE_NODE':
|
||||||
this.$bus.$emit('execCommand', key, this.copyData)
|
this.mindMap.renderer.paste()
|
||||||
break
|
break
|
||||||
case 'RETURN_CENTER':
|
case 'RETURN_CENTER':
|
||||||
this.mindMap.view.reset()
|
this.mindMap.view.reset()
|
||||||
@ -308,33 +296,6 @@ export default {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
this.hide()
|
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…
x
Reference in New Issue
Block a user