Merge branch 'feature' into main
This commit is contained in:
commit
af9ee04aaf
File diff suppressed because one or more lines are too long
@ -10,7 +10,7 @@ import BatchExecution from './src/BatchExecution'
|
|||||||
import { layoutValueList, CONSTANTS } from './src/utils/constant'
|
import { layoutValueList, CONSTANTS } from './src/utils/constant'
|
||||||
import { SVG } from '@svgdotjs/svg.js'
|
import { SVG } from '@svgdotjs/svg.js'
|
||||||
import { simpleDeepClone } from './src/utils'
|
import { simpleDeepClone } from './src/utils'
|
||||||
import defaultTheme from './src/themes/default'
|
import defaultTheme, { checkIsNodeSizeIndependenceConfig } from './src/themes/default'
|
||||||
|
|
||||||
// 默认选项配置
|
// 默认选项配置
|
||||||
const defaultOpt = {
|
const defaultOpt = {
|
||||||
@ -120,7 +120,9 @@ const defaultOpt = {
|
|||||||
// }
|
// }
|
||||||
],
|
],
|
||||||
// 节点最大缓存数量
|
// 节点最大缓存数量
|
||||||
maxNodeCacheCount: 1000
|
maxNodeCacheCount: 1000,
|
||||||
|
// 关联线默认文字
|
||||||
|
defaultAssociativeLineText: '关联'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 思维导图
|
// 思维导图
|
||||||
@ -265,7 +267,9 @@ class MindMap {
|
|||||||
// 设置主题配置
|
// 设置主题配置
|
||||||
setThemeConfig(config) {
|
setThemeConfig(config) {
|
||||||
this.opt.themeConfig = config
|
this.opt.themeConfig = config
|
||||||
this.render(null, CONSTANTS.CHANGE_THEME)
|
// 检查改变的是否是节点大小无关的主题属性
|
||||||
|
let res = checkIsNodeSizeIndependenceConfig(config)
|
||||||
|
this.render(null, res ? '' : CONSTANTS.CHANGE_THEME)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取自定义主题配置
|
// 获取自定义主题配置
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "simple-mind-map",
|
"name": "simple-mind-map",
|
||||||
"version": "0.5.10-fix.2",
|
"version": "0.5.11",
|
||||||
"description": "一个简单的web在线思维导图",
|
"description": "一个简单的web在线思维导图",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -3,13 +3,13 @@ import { v4 as uuid } from 'uuid'
|
|||||||
import {
|
import {
|
||||||
getAssociativeLineTargetIndex,
|
getAssociativeLineTargetIndex,
|
||||||
computeCubicBezierPathPoints,
|
computeCubicBezierPathPoints,
|
||||||
joinCubicBezierPath,
|
|
||||||
cubicBezierPath,
|
cubicBezierPath,
|
||||||
getNodePoint,
|
getNodePoint,
|
||||||
computeNodePoints,
|
computeNodePoints,
|
||||||
getNodeLinePath,
|
getNodeLinePath
|
||||||
getDefaultControlPointOffsets
|
|
||||||
} from './utils/associativeLineUtils'
|
} from './utils/associativeLineUtils'
|
||||||
|
import associativeLineControlsMethods from './utils/associativeLineControls'
|
||||||
|
import associativeLineTextMethods from './utils/associativeLineText'
|
||||||
|
|
||||||
// 关联线类
|
// 关联线类
|
||||||
class AssociativeLine {
|
class AssociativeLine {
|
||||||
@ -46,6 +46,14 @@ class AssociativeLine {
|
|||||||
}
|
}
|
||||||
// 节流一下,不然很卡
|
// 节流一下,不然很卡
|
||||||
this.checkOverlapNode = throttle(this.checkOverlapNode, 100, this)
|
this.checkOverlapNode = throttle(this.checkOverlapNode, 100, this)
|
||||||
|
// 控制点相关方法
|
||||||
|
Object.keys(associativeLineControlsMethods).forEach(item => {
|
||||||
|
this[item] = associativeLineControlsMethods[item].bind(this)
|
||||||
|
})
|
||||||
|
// 关联线文字相关方法
|
||||||
|
Object.keys(associativeLineTextMethods).forEach(item => {
|
||||||
|
this[item] = associativeLineTextMethods[item].bind(this)
|
||||||
|
})
|
||||||
this.bindEvent()
|
this.bindEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,6 +97,8 @@ class AssociativeLine {
|
|||||||
window.addEventListener('mouseup', e => {
|
window.addEventListener('mouseup', e => {
|
||||||
this.onControlPointMouseup(e)
|
this.onControlPointMouseup(e)
|
||||||
})
|
})
|
||||||
|
// 缩放事件
|
||||||
|
this.mindMap.on('scale', this.onScale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建箭头
|
// 创建箭头
|
||||||
@ -177,36 +187,46 @@ class AssociativeLine {
|
|||||||
.stroke({ width: associativeLineActiveWidth, color: 'transparent' })
|
.stroke({ width: associativeLineActiveWidth, color: 'transparent' })
|
||||||
.fill({ color: 'none' })
|
.fill({ color: 'none' })
|
||||||
clickPath.plot(pathStr)
|
clickPath.plot(pathStr)
|
||||||
|
// 文字
|
||||||
|
let text = this.createText({ path, clickPath, node, toNode, startPoint, endPoint, controlPoints })
|
||||||
// 点击事件
|
// 点击事件
|
||||||
clickPath.click(e => {
|
clickPath.click(e => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
// 如果当前存在激活节点,那么取消激活节点
|
this.setActiveLine({ path, clickPath, text, node, toNode, startPoint, endPoint, controlPoints })
|
||||||
if (this.mindMap.renderer.activeNodeList.length > 0) {
|
|
||||||
this.clearActiveNodes()
|
|
||||||
} else {
|
|
||||||
// 否则清除当前的关联线的激活状态,如果有的话
|
|
||||||
this.clearActiveLine()
|
|
||||||
// 保存当前激活的关联线信息
|
|
||||||
this.activeLine = [path, clickPath, node, toNode]
|
|
||||||
// 让不可见的点击线显示
|
|
||||||
clickPath.stroke({ color: associativeLineActiveColor })
|
|
||||||
// 渲染控制点和连线
|
|
||||||
this.renderControls(
|
|
||||||
startPoint,
|
|
||||||
endPoint,
|
|
||||||
controlPoints[0],
|
|
||||||
controlPoints[1]
|
|
||||||
)
|
|
||||||
this.mindMap.emit(
|
|
||||||
'associative_line_click',
|
|
||||||
path,
|
|
||||||
clickPath,
|
|
||||||
node,
|
|
||||||
toNode
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
this.lineList.push([path, clickPath, node, toNode])
|
// 渲染关联线文字
|
||||||
|
this.renderText(this.getText(node, toNode), path, text)
|
||||||
|
this.lineList.push([path, clickPath, text, node, toNode])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 激活某根关联线
|
||||||
|
setActiveLine({ path, clickPath, text, node, toNode, startPoint, endPoint, controlPoints }) {
|
||||||
|
let {
|
||||||
|
associativeLineActiveColor
|
||||||
|
} = this.mindMap.themeConfig
|
||||||
|
// 如果当前存在激活节点,那么取消激活节点
|
||||||
|
if (this.mindMap.renderer.activeNodeList.length > 0) {
|
||||||
|
this.clearActiveNodes()
|
||||||
|
} else {
|
||||||
|
// 否则清除当前的关联线的激活状态,如果有的话
|
||||||
|
this.clearActiveLine()
|
||||||
|
// 保存当前激活的关联线信息
|
||||||
|
this.activeLine = [path, clickPath, text, node, toNode]
|
||||||
|
// 让不可见的点击线显示
|
||||||
|
clickPath.stroke({ color: associativeLineActiveColor })
|
||||||
|
// 如果没有输入过关联线文字,那么显示默认文字
|
||||||
|
if (!this.getText(node, toNode)) {
|
||||||
|
this.renderText(this.mindMap.opt.defaultAssociativeLineText, path, text)
|
||||||
|
}
|
||||||
|
// 渲染控制点和连线
|
||||||
|
this.renderControls(
|
||||||
|
startPoint,
|
||||||
|
endPoint,
|
||||||
|
controlPoints[0],
|
||||||
|
controlPoints[1]
|
||||||
|
)
|
||||||
|
this.mindMap.emit('associative_line_click', path, clickPath, node, toNode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 移除所有连接线
|
// 移除所有连接线
|
||||||
@ -214,6 +234,7 @@ class AssociativeLine {
|
|||||||
this.lineList.forEach(line => {
|
this.lineList.forEach(line => {
|
||||||
line[0].remove()
|
line[0].remove()
|
||||||
line[1].remove()
|
line[1].remove()
|
||||||
|
line[2].remove()
|
||||||
})
|
})
|
||||||
this.lineList = []
|
this.lineList = []
|
||||||
}
|
}
|
||||||
@ -253,7 +274,8 @@ class AssociativeLine {
|
|||||||
updateCreatingLine(e) {
|
updateCreatingLine(e) {
|
||||||
let { x, y } = this.getTransformedEventPos(e)
|
let { x, y } = this.getTransformedEventPos(e)
|
||||||
let startPoint = getNodePoint(this.creatingStartNode)
|
let startPoint = getNodePoint(this.creatingStartNode)
|
||||||
let pathStr = cubicBezierPath(startPoint.x, startPoint.y, x, y)
|
let offsetX = x > startPoint.x ? -10 : 10
|
||||||
|
let pathStr = cubicBezierPath(startPoint.x, startPoint.y, x + offsetX, y)
|
||||||
this.creatingLine.plot(pathStr)
|
this.creatingLine.plot(pathStr)
|
||||||
this.checkOverlapNode(x, y)
|
this.checkOverlapNode(x, y)
|
||||||
}
|
}
|
||||||
@ -349,20 +371,33 @@ class AssociativeLine {
|
|||||||
// 删除连接线
|
// 删除连接线
|
||||||
removeLine() {
|
removeLine() {
|
||||||
if (!this.activeLine) return
|
if (!this.activeLine) return
|
||||||
let [, , node, toNode] = this.activeLine
|
let [, , , node, toNode] = this.activeLine
|
||||||
this.removeControls()
|
this.removeControls()
|
||||||
let { associativeLineTargets, associativeLineTargetControlOffsets } =
|
let { associativeLineTargets, associativeLineTargetControlOffsets, associativeLineText } =
|
||||||
node.nodeData.data
|
node.nodeData.data
|
||||||
let targetIndex = getAssociativeLineTargetIndex(node, toNode)
|
let targetIndex = getAssociativeLineTargetIndex(node, toNode)
|
||||||
|
// 更新关联线文本数据
|
||||||
|
let newAssociativeLineText = {}
|
||||||
|
if (associativeLineText) {
|
||||||
|
Object.keys(associativeLineText).forEach((item) => {
|
||||||
|
if (item !== toNode.nodeData.data.id) {
|
||||||
|
newAssociativeLineText[item] = associativeLineText[item]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
this.mindMap.execCommand('SET_NODE_DATA', node, {
|
this.mindMap.execCommand('SET_NODE_DATA', node, {
|
||||||
|
// 目标
|
||||||
associativeLineTargets: associativeLineTargets.filter((_, index) => {
|
associativeLineTargets: associativeLineTargets.filter((_, index) => {
|
||||||
return index !== targetIndex
|
return index !== targetIndex
|
||||||
}),
|
}),
|
||||||
|
// 偏移量
|
||||||
associativeLineTargetControlOffsets: associativeLineTargetControlOffsets
|
associativeLineTargetControlOffsets: associativeLineTargetControlOffsets
|
||||||
? associativeLineTargetControlOffsets.filter((_, index) => {
|
? associativeLineTargetControlOffsets.filter((_, index) => {
|
||||||
return index !== targetIndex
|
return index !== targetIndex
|
||||||
})
|
})
|
||||||
: []
|
: [],
|
||||||
|
// 文本
|
||||||
|
associativeLineText: newAssociativeLineText
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,9 +411,16 @@ class AssociativeLine {
|
|||||||
// 清除激活的线
|
// 清除激活的线
|
||||||
clearActiveLine() {
|
clearActiveLine() {
|
||||||
if (this.activeLine) {
|
if (this.activeLine) {
|
||||||
this.activeLine[1].stroke({
|
let [, clickPath, text, node, toNode] = this.activeLine
|
||||||
|
clickPath.stroke({
|
||||||
color: 'transparent'
|
color: 'transparent'
|
||||||
})
|
})
|
||||||
|
// 隐藏关联线文本编辑框
|
||||||
|
this.hideEditTextBox()
|
||||||
|
// 如果当前关联线没有文字,则清空文字节点
|
||||||
|
if (!this.getText(node, toNode)) {
|
||||||
|
text.clear()
|
||||||
|
}
|
||||||
this.activeLine = null
|
this.activeLine = null
|
||||||
this.removeControls()
|
this.removeControls()
|
||||||
}
|
}
|
||||||
@ -391,6 +433,7 @@ class AssociativeLine {
|
|||||||
this.lineList.forEach(line => {
|
this.lineList.forEach(line => {
|
||||||
line[0].hide()
|
line[0].hide()
|
||||||
line[1].hide()
|
line[1].hide()
|
||||||
|
line[2].hide()
|
||||||
})
|
})
|
||||||
this.hideControls()
|
this.hideControls()
|
||||||
}
|
}
|
||||||
@ -401,225 +444,11 @@ class AssociativeLine {
|
|||||||
this.lineList.forEach(line => {
|
this.lineList.forEach(line => {
|
||||||
line[0].show()
|
line[0].show()
|
||||||
line[1].show()
|
line[1].show()
|
||||||
|
line[2].show()
|
||||||
})
|
})
|
||||||
this.showControls()
|
this.showControls()
|
||||||
this.isNodeDragging = false
|
this.isNodeDragging = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建控制点、连线节点
|
|
||||||
createControlNodes() {
|
|
||||||
let { associativeLineActiveColor } = this.mindMap.themeConfig
|
|
||||||
// 连线
|
|
||||||
this.controlLine1 = this.draw
|
|
||||||
.line()
|
|
||||||
.stroke({ color: associativeLineActiveColor, width: 2 })
|
|
||||||
this.controlLine2 = this.draw
|
|
||||||
.line()
|
|
||||||
.stroke({ color: associativeLineActiveColor, width: 2 })
|
|
||||||
// 控制点
|
|
||||||
this.controlPoint1 = this.createOneControlNode('controlPoint1')
|
|
||||||
this.controlPoint2 = this.createOneControlNode('controlPoint2')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建控制点
|
|
||||||
createOneControlNode(pointKey) {
|
|
||||||
let { associativeLineActiveColor } = this.mindMap.themeConfig
|
|
||||||
return this.draw
|
|
||||||
.circle(this.controlPointDiameter)
|
|
||||||
.stroke({ color: associativeLineActiveColor })
|
|
||||||
.fill({ color: '#fff' })
|
|
||||||
.click(e => {
|
|
||||||
e.stopPropagation()
|
|
||||||
})
|
|
||||||
.mousedown(e => {
|
|
||||||
this.onControlPointMousedown(e, pointKey)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 控制点的鼠标按下事件
|
|
||||||
onControlPointMousedown(e, pointKey) {
|
|
||||||
e.stopPropagation()
|
|
||||||
this.isControlPointMousedown = true
|
|
||||||
this.mousedownControlPointKey = pointKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// 控制点的鼠标移动事件
|
|
||||||
onControlPointMousemove(e) {
|
|
||||||
if (
|
|
||||||
!this.isControlPointMousedown ||
|
|
||||||
!this.mousedownControlPointKey ||
|
|
||||||
!this[this.mousedownControlPointKey]
|
|
||||||
)
|
|
||||||
return
|
|
||||||
e.stopPropagation()
|
|
||||||
e.preventDefault()
|
|
||||||
let radius = this.controlPointDiameter / 2
|
|
||||||
// 转换鼠标当前的位置
|
|
||||||
let { x, y } = this.getTransformedEventPos(e)
|
|
||||||
this.controlPointMousemoveState.pos = {
|
|
||||||
x,
|
|
||||||
y
|
|
||||||
}
|
|
||||||
// 更新当前拖拽的控制点的位置
|
|
||||||
this[this.mousedownControlPointKey].x(x - radius).y(y - radius)
|
|
||||||
let [path, clickPath, node, toNode] = this.activeLine
|
|
||||||
let [startPoint, endPoint] = computeNodePoints(node, toNode)
|
|
||||||
this.controlPointMousemoveState.startPoint = startPoint
|
|
||||||
this.controlPointMousemoveState.endPoint = endPoint
|
|
||||||
let targetIndex = getAssociativeLineTargetIndex(node, toNode)
|
|
||||||
this.controlPointMousemoveState.targetIndex = targetIndex
|
|
||||||
let offsets = []
|
|
||||||
let associativeLineTargetControlOffsets = node.nodeData.data.associativeLineTargetControlOffsets
|
|
||||||
if (!associativeLineTargetControlOffsets) {
|
|
||||||
// 兼容0.4.5版本,没有associativeLineTargetControlOffsets的情况
|
|
||||||
offsets = getDefaultControlPointOffsets(startPoint, endPoint)
|
|
||||||
} else {
|
|
||||||
offsets = associativeLineTargetControlOffsets[targetIndex]
|
|
||||||
}
|
|
||||||
let point1 = null
|
|
||||||
let point2 = null
|
|
||||||
// 拖拽的是控制点1
|
|
||||||
if (this.mousedownControlPointKey === 'controlPoint1') {
|
|
||||||
point1 = {
|
|
||||||
x,
|
|
||||||
y
|
|
||||||
}
|
|
||||||
point2 = {
|
|
||||||
x: endPoint.x + offsets[1].x,
|
|
||||||
y: endPoint.y + offsets[1].y
|
|
||||||
}
|
|
||||||
// 更新控制点1的连线
|
|
||||||
this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y)
|
|
||||||
} else {
|
|
||||||
// 拖拽的是控制点2
|
|
||||||
point1 = {
|
|
||||||
x: startPoint.x + offsets[0].x,
|
|
||||||
y: startPoint.y + offsets[0].y
|
|
||||||
}
|
|
||||||
point2 = {
|
|
||||||
x,
|
|
||||||
y
|
|
||||||
}
|
|
||||||
// 更新控制点2的连线
|
|
||||||
this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y)
|
|
||||||
}
|
|
||||||
// 更新关联线
|
|
||||||
let pathStr = joinCubicBezierPath(startPoint, endPoint, point1, point2)
|
|
||||||
path.plot(pathStr)
|
|
||||||
clickPath.plot(pathStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 控制点的鼠标移动事件
|
|
||||||
onControlPointMouseup(e) {
|
|
||||||
if (!this.isControlPointMousedown) return
|
|
||||||
e.stopPropagation()
|
|
||||||
e.preventDefault()
|
|
||||||
let { pos, startPoint, endPoint, targetIndex } =
|
|
||||||
this.controlPointMousemoveState
|
|
||||||
let [, , node] = this.activeLine
|
|
||||||
let offsetList = []
|
|
||||||
let associativeLineTargetControlOffsets = node.nodeData.data.associativeLineTargetControlOffsets
|
|
||||||
if (!associativeLineTargetControlOffsets) {
|
|
||||||
// 兼容0.4.5版本,没有associativeLineTargetControlOffsets的情况
|
|
||||||
offsetList[targetIndex] = getDefaultControlPointOffsets(startPoint, endPoint)
|
|
||||||
} else {
|
|
||||||
offsetList = associativeLineTargetControlOffsets
|
|
||||||
}
|
|
||||||
let offset1 = null
|
|
||||||
let offset2 = null
|
|
||||||
if (this.mousedownControlPointKey === 'controlPoint1') {
|
|
||||||
// 更新控制点1数据
|
|
||||||
offset1 = {
|
|
||||||
x: pos.x - startPoint.x,
|
|
||||||
y: pos.y - startPoint.y
|
|
||||||
}
|
|
||||||
offset2 = offsetList[targetIndex][1]
|
|
||||||
} else {
|
|
||||||
// 更新控制点2数据
|
|
||||||
offset1 = offsetList[targetIndex][0]
|
|
||||||
offset2 = {
|
|
||||||
x: pos.x - endPoint.x,
|
|
||||||
y: pos.y - endPoint.y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
offsetList[targetIndex] = [offset1, offset2]
|
|
||||||
this.mindMap.execCommand('SET_NODE_DATA', node, {
|
|
||||||
associativeLineTargetControlOffsets: offsetList
|
|
||||||
})
|
|
||||||
// 这里要加个setTimeout0是因为draw_click事件比mouseup事件触发的晚,所以重置isControlPointMousedown需要等draw_click事件触发完以后
|
|
||||||
setTimeout(() => {
|
|
||||||
this.resetControlPoint()
|
|
||||||
}, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 复位控制点移动
|
|
||||||
resetControlPoint() {
|
|
||||||
this.isControlPointMousedown = false
|
|
||||||
this.mousedownControlPointKey = ''
|
|
||||||
this.controlPointMousemoveState = {
|
|
||||||
pos: null,
|
|
||||||
startPoint: null,
|
|
||||||
endPoint: null,
|
|
||||||
targetIndex: ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 渲染控制点
|
|
||||||
renderControls(startPoint, endPoint, point1, point2) {
|
|
||||||
if (!this.controlLine1) {
|
|
||||||
this.createControlNodes()
|
|
||||||
}
|
|
||||||
let radius = this.controlPointDiameter / 2
|
|
||||||
// 控制点和起终点的连线
|
|
||||||
this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y)
|
|
||||||
this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y)
|
|
||||||
// 控制点
|
|
||||||
this.controlPoint1.x(point1.x - radius).y(point1.y - radius)
|
|
||||||
this.controlPoint2.x(point2.x - radius).y(point2.y - radius)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除控制点
|
|
||||||
removeControls() {
|
|
||||||
if (!this.controlLine1) return
|
|
||||||
;[
|
|
||||||
this.controlLine1,
|
|
||||||
this.controlLine2,
|
|
||||||
this.controlPoint1,
|
|
||||||
this.controlPoint2
|
|
||||||
].forEach(item => {
|
|
||||||
item.remove()
|
|
||||||
})
|
|
||||||
this.controlLine1 = null
|
|
||||||
this.controlLine2 = null
|
|
||||||
this.controlPoint1 = null
|
|
||||||
this.controlPoint2 = null
|
|
||||||
}
|
|
||||||
|
|
||||||
// 隐藏控制点
|
|
||||||
hideControls() {
|
|
||||||
if (!this.controlLine1) return
|
|
||||||
;[
|
|
||||||
this.controlLine1,
|
|
||||||
this.controlLine2,
|
|
||||||
this.controlPoint1,
|
|
||||||
this.controlPoint2
|
|
||||||
].forEach(item => {
|
|
||||||
item.hide()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示控制点
|
|
||||||
showControls() {
|
|
||||||
if (!this.controlLine1) return
|
|
||||||
;[
|
|
||||||
this.controlLine1,
|
|
||||||
this.controlLine2,
|
|
||||||
this.controlPoint1,
|
|
||||||
this.controlPoint2
|
|
||||||
].forEach(item => {
|
|
||||||
item.show()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AssociativeLine.instanceName = 'associativeLine'
|
AssociativeLine.instanceName = 'associativeLine'
|
||||||
|
|||||||
@ -46,7 +46,7 @@ export default class KeyCommand {
|
|||||||
if (this.mindMap.richText && this.mindMap.richText.showTextEdit) {
|
if (this.mindMap.richText && this.mindMap.richText.showTextEdit) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (this.mindMap.renderer.textEdit.showTextEdit) {
|
if (this.mindMap.renderer.textEdit.showTextEdit || (this.mindMap.associativeLine && this.mindMap.associativeLine.showTextEdit)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.isInSvg = false
|
this.isInSvg = false
|
||||||
|
|||||||
@ -34,6 +34,14 @@ export default {
|
|||||||
associativeLineActiveWidth: 8,
|
associativeLineActiveWidth: 8,
|
||||||
// 关联线激活状态的颜色
|
// 关联线激活状态的颜色
|
||||||
associativeLineActiveColor: 'rgba(2, 167, 240, 1)',
|
associativeLineActiveColor: 'rgba(2, 167, 240, 1)',
|
||||||
|
// 关联线文字颜色
|
||||||
|
associativeLineTextColor: 'rgb(51, 51, 51)',
|
||||||
|
// 关联线文字大小
|
||||||
|
associativeLineTextFontSize: 14,
|
||||||
|
// 关联线文字行高
|
||||||
|
associativeLineTextLineHeight: 1.2,
|
||||||
|
// 关联线文字字体
|
||||||
|
associativeLineTextFontFamily: '微软雅黑, Microsoft YaHei',
|
||||||
// 背景颜色
|
// 背景颜色
|
||||||
backgroundColor: '#fafafa',
|
backgroundColor: '#fafafa',
|
||||||
// 背景图片
|
// 背景图片
|
||||||
@ -148,4 +156,38 @@ export const supportActiveStyle = [
|
|||||||
'borderRadius'
|
'borderRadius'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// 检测主题配置是否是节点大小无关的
|
||||||
|
const nodeSizeIndependenceList = [
|
||||||
|
'lineWidth',
|
||||||
|
'lineColor',
|
||||||
|
'lineDasharray',
|
||||||
|
'lineStyle',
|
||||||
|
'generalizationLineWidth',
|
||||||
|
'generalizationLineColor',
|
||||||
|
'associativeLineWidth',
|
||||||
|
'associativeLineColor',
|
||||||
|
'associativeLineActiveWidth',
|
||||||
|
'associativeLineActiveColor',
|
||||||
|
'associativeLineTextColor',
|
||||||
|
'associativeLineTextFontSize',
|
||||||
|
'associativeLineTextLineHeight',
|
||||||
|
'associativeLineTextFontFamily',
|
||||||
|
'backgroundColor',
|
||||||
|
'backgroundImage',
|
||||||
|
'backgroundRepeat',
|
||||||
|
'backgroundPosition',
|
||||||
|
'backgroundSize'
|
||||||
|
]
|
||||||
|
export const checkIsNodeSizeIndependenceConfig = (config) => {
|
||||||
|
let keys = Object.keys(config)
|
||||||
|
for(let i = 0; i < keys.length; i++) {
|
||||||
|
if (!nodeSizeIndependenceList.find((item) => {
|
||||||
|
return item === keys[i]
|
||||||
|
})) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
export const lineStyleProps = ['lineColor', 'lineDasharray', 'lineWidth']
|
export const lineStyleProps = ['lineColor', 'lineDasharray', 'lineWidth']
|
||||||
|
|||||||
241
simple-mind-map/src/utils/associativeLineControls.js
Normal file
241
simple-mind-map/src/utils/associativeLineControls.js
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
import {
|
||||||
|
getAssociativeLineTargetIndex,
|
||||||
|
joinCubicBezierPath,
|
||||||
|
computeNodePoints,
|
||||||
|
getDefaultControlPointOffsets
|
||||||
|
} from './associativeLineUtils'
|
||||||
|
|
||||||
|
// 创建控制点、连线节点
|
||||||
|
function createControlNodes() {
|
||||||
|
let { associativeLineActiveColor } = this.mindMap.themeConfig
|
||||||
|
// 连线
|
||||||
|
this.controlLine1 = this.draw
|
||||||
|
.line()
|
||||||
|
.stroke({ color: associativeLineActiveColor, width: 2 })
|
||||||
|
this.controlLine2 = this.draw
|
||||||
|
.line()
|
||||||
|
.stroke({ color: associativeLineActiveColor, width: 2 })
|
||||||
|
// 控制点
|
||||||
|
this.controlPoint1 = this.createOneControlNode('controlPoint1')
|
||||||
|
this.controlPoint2 = this.createOneControlNode('controlPoint2')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建控制点
|
||||||
|
function createOneControlNode(pointKey) {
|
||||||
|
let { associativeLineActiveColor } = this.mindMap.themeConfig
|
||||||
|
return this.draw
|
||||||
|
.circle(this.controlPointDiameter)
|
||||||
|
.stroke({ color: associativeLineActiveColor })
|
||||||
|
.fill({ color: '#fff' })
|
||||||
|
.click(e => {
|
||||||
|
e.stopPropagation()
|
||||||
|
})
|
||||||
|
.mousedown(e => {
|
||||||
|
this.onControlPointMousedown(e, pointKey)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 控制点的鼠标按下事件
|
||||||
|
function onControlPointMousedown(e, pointKey) {
|
||||||
|
e.stopPropagation()
|
||||||
|
this.isControlPointMousedown = true
|
||||||
|
this.mousedownControlPointKey = pointKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// 控制点的鼠标移动事件
|
||||||
|
function onControlPointMousemove(e) {
|
||||||
|
if (
|
||||||
|
!this.isControlPointMousedown ||
|
||||||
|
!this.mousedownControlPointKey ||
|
||||||
|
!this[this.mousedownControlPointKey]
|
||||||
|
)
|
||||||
|
return
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
let radius = this.controlPointDiameter / 2
|
||||||
|
// 转换鼠标当前的位置
|
||||||
|
let { x, y } = this.getTransformedEventPos(e)
|
||||||
|
this.controlPointMousemoveState.pos = {
|
||||||
|
x,
|
||||||
|
y
|
||||||
|
}
|
||||||
|
// 更新当前拖拽的控制点的位置
|
||||||
|
this[this.mousedownControlPointKey].x(x - radius).y(y - radius)
|
||||||
|
let [path, clickPath, text, node, toNode] = this.activeLine
|
||||||
|
let [startPoint, endPoint] = computeNodePoints(node, toNode)
|
||||||
|
this.controlPointMousemoveState.startPoint = startPoint
|
||||||
|
this.controlPointMousemoveState.endPoint = endPoint
|
||||||
|
let targetIndex = getAssociativeLineTargetIndex(node, toNode)
|
||||||
|
this.controlPointMousemoveState.targetIndex = targetIndex
|
||||||
|
let offsets = []
|
||||||
|
let associativeLineTargetControlOffsets =
|
||||||
|
node.nodeData.data.associativeLineTargetControlOffsets
|
||||||
|
if (!associativeLineTargetControlOffsets) {
|
||||||
|
// 兼容0.4.5版本,没有associativeLineTargetControlOffsets的情况
|
||||||
|
offsets = getDefaultControlPointOffsets(startPoint, endPoint)
|
||||||
|
} else {
|
||||||
|
offsets = associativeLineTargetControlOffsets[targetIndex]
|
||||||
|
}
|
||||||
|
let point1 = null
|
||||||
|
let point2 = null
|
||||||
|
// 拖拽的是控制点1
|
||||||
|
if (this.mousedownControlPointKey === 'controlPoint1') {
|
||||||
|
point1 = {
|
||||||
|
x,
|
||||||
|
y
|
||||||
|
}
|
||||||
|
point2 = {
|
||||||
|
x: endPoint.x + offsets[1].x,
|
||||||
|
y: endPoint.y + offsets[1].y
|
||||||
|
}
|
||||||
|
// 更新控制点1的连线
|
||||||
|
this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y)
|
||||||
|
} else {
|
||||||
|
// 拖拽的是控制点2
|
||||||
|
point1 = {
|
||||||
|
x: startPoint.x + offsets[0].x,
|
||||||
|
y: startPoint.y + offsets[0].y
|
||||||
|
}
|
||||||
|
point2 = {
|
||||||
|
x,
|
||||||
|
y
|
||||||
|
}
|
||||||
|
// 更新控制点2的连线
|
||||||
|
this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y)
|
||||||
|
}
|
||||||
|
// 更新关联线
|
||||||
|
let pathStr = joinCubicBezierPath(startPoint, endPoint, point1, point2)
|
||||||
|
path.plot(pathStr)
|
||||||
|
clickPath.plot(pathStr)
|
||||||
|
this.updateTextPos(path, text)
|
||||||
|
this.updateTextEditBoxPos(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 控制点的鼠标移动事件
|
||||||
|
function onControlPointMouseup(e) {
|
||||||
|
if (!this.isControlPointMousedown) return
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
let { pos, startPoint, endPoint, targetIndex } =
|
||||||
|
this.controlPointMousemoveState
|
||||||
|
let [, , , node] = this.activeLine
|
||||||
|
let offsetList = []
|
||||||
|
let associativeLineTargetControlOffsets =
|
||||||
|
node.nodeData.data.associativeLineTargetControlOffsets
|
||||||
|
if (!associativeLineTargetControlOffsets) {
|
||||||
|
// 兼容0.4.5版本,没有associativeLineTargetControlOffsets的情况
|
||||||
|
offsetList[targetIndex] = getDefaultControlPointOffsets(
|
||||||
|
startPoint,
|
||||||
|
endPoint
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
offsetList = associativeLineTargetControlOffsets
|
||||||
|
}
|
||||||
|
let offset1 = null
|
||||||
|
let offset2 = null
|
||||||
|
if (this.mousedownControlPointKey === 'controlPoint1') {
|
||||||
|
// 更新控制点1数据
|
||||||
|
offset1 = {
|
||||||
|
x: pos.x - startPoint.x,
|
||||||
|
y: pos.y - startPoint.y
|
||||||
|
}
|
||||||
|
offset2 = offsetList[targetIndex][1]
|
||||||
|
} else {
|
||||||
|
// 更新控制点2数据
|
||||||
|
offset1 = offsetList[targetIndex][0]
|
||||||
|
offset2 = {
|
||||||
|
x: pos.x - endPoint.x,
|
||||||
|
y: pos.y - endPoint.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offsetList[targetIndex] = [offset1, offset2]
|
||||||
|
this.mindMap.execCommand('SET_NODE_DATA', node, {
|
||||||
|
associativeLineTargetControlOffsets: offsetList
|
||||||
|
})
|
||||||
|
// 这里要加个setTimeout0是因为draw_click事件比mouseup事件触发的晚,所以重置isControlPointMousedown需要等draw_click事件触发完以后
|
||||||
|
setTimeout(() => {
|
||||||
|
this.resetControlPoint()
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复位控制点移动
|
||||||
|
function resetControlPoint() {
|
||||||
|
this.isControlPointMousedown = false
|
||||||
|
this.mousedownControlPointKey = ''
|
||||||
|
this.controlPointMousemoveState = {
|
||||||
|
pos: null,
|
||||||
|
startPoint: null,
|
||||||
|
endPoint: null,
|
||||||
|
targetIndex: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 渲染控制点
|
||||||
|
function renderControls(startPoint, endPoint, point1, point2) {
|
||||||
|
if (!this.controlLine1) {
|
||||||
|
this.createControlNodes()
|
||||||
|
}
|
||||||
|
let radius = this.controlPointDiameter / 2
|
||||||
|
// 控制点和起终点的连线
|
||||||
|
this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y)
|
||||||
|
this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y)
|
||||||
|
// 控制点
|
||||||
|
this.controlPoint1.x(point1.x - radius).y(point1.y - radius)
|
||||||
|
this.controlPoint2.x(point2.x - radius).y(point2.y - radius)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除控制点
|
||||||
|
function removeControls() {
|
||||||
|
if (!this.controlLine1) return
|
||||||
|
;[
|
||||||
|
this.controlLine1,
|
||||||
|
this.controlLine2,
|
||||||
|
this.controlPoint1,
|
||||||
|
this.controlPoint2
|
||||||
|
].forEach(item => {
|
||||||
|
item.remove()
|
||||||
|
})
|
||||||
|
this.controlLine1 = null
|
||||||
|
this.controlLine2 = null
|
||||||
|
this.controlPoint1 = null
|
||||||
|
this.controlPoint2 = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏控制点
|
||||||
|
function hideControls() {
|
||||||
|
if (!this.controlLine1) return
|
||||||
|
;[
|
||||||
|
this.controlLine1,
|
||||||
|
this.controlLine2,
|
||||||
|
this.controlPoint1,
|
||||||
|
this.controlPoint2
|
||||||
|
].forEach(item => {
|
||||||
|
item.hide()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示控制点
|
||||||
|
function showControls() {
|
||||||
|
if (!this.controlLine1) return
|
||||||
|
;[
|
||||||
|
this.controlLine1,
|
||||||
|
this.controlLine2,
|
||||||
|
this.controlPoint1,
|
||||||
|
this.controlPoint2
|
||||||
|
].forEach(item => {
|
||||||
|
item.show()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createControlNodes,
|
||||||
|
createOneControlNode,
|
||||||
|
onControlPointMousedown,
|
||||||
|
onControlPointMousemove,
|
||||||
|
onControlPointMouseup,
|
||||||
|
resetControlPoint,
|
||||||
|
renderControls,
|
||||||
|
removeControls,
|
||||||
|
hideControls,
|
||||||
|
showControls
|
||||||
|
}
|
||||||
167
simple-mind-map/src/utils/associativeLineText.js
Normal file
167
simple-mind-map/src/utils/associativeLineText.js
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
import { Text } from '@svgdotjs/svg.js'
|
||||||
|
import { getStrWithBrFromHtml } from './index'
|
||||||
|
|
||||||
|
// 创建文字节点
|
||||||
|
function createText(data) {
|
||||||
|
let g = this.draw.group()
|
||||||
|
const setActive = () => {
|
||||||
|
if (
|
||||||
|
!this.activeLine ||
|
||||||
|
this.activeLine[3] !== data.node ||
|
||||||
|
this.activeLine[4] !== data.toNode
|
||||||
|
) {
|
||||||
|
this.setActiveLine({
|
||||||
|
...data,
|
||||||
|
text: g
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.click(e => {
|
||||||
|
e.stopPropagation()
|
||||||
|
setActive()
|
||||||
|
})
|
||||||
|
g.on('dblclick', e => {
|
||||||
|
e.stopPropagation()
|
||||||
|
setActive()
|
||||||
|
if (!this.activeLine) return
|
||||||
|
this.showEditTextBox(g)
|
||||||
|
})
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示文本编辑框
|
||||||
|
function showEditTextBox(g) {
|
||||||
|
this.mindMap.emit('before_show_text_edit')
|
||||||
|
// 注册回车快捷键
|
||||||
|
this.mindMap.keyCommand.addShortcut('Enter', () => {
|
||||||
|
this.hideEditTextBox()
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!this.textEditNode) {
|
||||||
|
this.textEditNode = document.createElement('div')
|
||||||
|
this.textEditNode.style.cssText = `position:fixed;box-sizing: border-box;background-color:#fff;box-shadow: 0 0 20px rgba(0,0,0,.5);padding: 3px 5px;margin-left: -5px;margin-top: -3px;outline: none; word-break: break-all;`
|
||||||
|
this.textEditNode.setAttribute('contenteditable', true)
|
||||||
|
this.textEditNode.addEventListener('keyup', e => {
|
||||||
|
e.stopPropagation()
|
||||||
|
})
|
||||||
|
this.textEditNode.addEventListener('click', e => {
|
||||||
|
e.stopPropagation()
|
||||||
|
})
|
||||||
|
document.body.appendChild(this.textEditNode)
|
||||||
|
}
|
||||||
|
let {
|
||||||
|
associativeLineTextFontSize,
|
||||||
|
associativeLineTextFontFamily,
|
||||||
|
associativeLineTextLineHeight
|
||||||
|
} = this.mindMap.themeConfig
|
||||||
|
let scale = this.mindMap.view.scale
|
||||||
|
let [, , , node, toNode] = this.activeLine
|
||||||
|
let textLines = (
|
||||||
|
this.getText(node, toNode) || this.mindMap.opt.defaultAssociativeLineText
|
||||||
|
).split(/\n/gim)
|
||||||
|
this.textEditNode.style.fontFamily = associativeLineTextFontFamily
|
||||||
|
this.textEditNode.style.fontSize = associativeLineTextFontSize * scale + 'px'
|
||||||
|
this.textEditNode.style.lineHeight = textLines.length > 1 ? associativeLineTextLineHeight : 'normal'
|
||||||
|
this.textEditNode.style.zIndex = this.mindMap.opt.nodeTextEditZIndex
|
||||||
|
this.textEditNode.innerHTML = textLines.join('<br>')
|
||||||
|
this.textEditNode.style.display = 'block'
|
||||||
|
this.updateTextEditBoxPos(g)
|
||||||
|
this.showTextEdit = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理画布缩放
|
||||||
|
function onScale() {
|
||||||
|
this.hideEditTextBox()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新文本编辑框位置
|
||||||
|
function updateTextEditBoxPos(g) {
|
||||||
|
let rect = g.node.getBoundingClientRect()
|
||||||
|
this.textEditNode.style.minWidth = rect.width + 10 + 'px'
|
||||||
|
this.textEditNode.style.minHeight = rect.height + 6 + 'px'
|
||||||
|
this.textEditNode.style.left = rect.left + 'px'
|
||||||
|
this.textEditNode.style.top = rect.top + 'px'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏文本编辑框
|
||||||
|
function hideEditTextBox() {
|
||||||
|
if (!this.showTextEdit) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let [path, , text, node, toNode] = this.activeLine
|
||||||
|
let str = getStrWithBrFromHtml(this.textEditNode.innerHTML)
|
||||||
|
this.mindMap.execCommand('SET_NODE_DATA', node, {
|
||||||
|
associativeLineText: {
|
||||||
|
...(node.nodeData.data.associativeLineText || {}),
|
||||||
|
[toNode.nodeData.data.id]: str
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.textEditNode.style.display = 'none'
|
||||||
|
this.textEditNode.innerHTML = ''
|
||||||
|
this.showTextEdit = false
|
||||||
|
this.renderText(str, path, text)
|
||||||
|
this.mindMap.emit('hide_text_edit')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取某根关联线的文字
|
||||||
|
function getText(node, toNode) {
|
||||||
|
let obj = node.nodeData.data.associativeLineText
|
||||||
|
if (!obj) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
return obj[toNode.nodeData.data.id] || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 渲染关联线文字
|
||||||
|
function renderText(str, path, text) {
|
||||||
|
if (!str) return
|
||||||
|
let { associativeLineTextFontSize, associativeLineTextLineHeight } =
|
||||||
|
this.mindMap.themeConfig
|
||||||
|
text.clear()
|
||||||
|
let textArr = str.split(/\n/gim)
|
||||||
|
textArr.forEach((item, index) => {
|
||||||
|
let node = new Text().text(item)
|
||||||
|
node.y(associativeLineTextFontSize * associativeLineTextLineHeight * index)
|
||||||
|
this.styleText(node)
|
||||||
|
text.add(node)
|
||||||
|
})
|
||||||
|
updateTextPos(path, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 给文本设置样式
|
||||||
|
function styleText(node) {
|
||||||
|
let {
|
||||||
|
associativeLineTextColor,
|
||||||
|
associativeLineTextFontSize,
|
||||||
|
associativeLineTextFontFamily
|
||||||
|
} = this.mindMap.themeConfig
|
||||||
|
node
|
||||||
|
.fill({
|
||||||
|
color: associativeLineTextColor
|
||||||
|
})
|
||||||
|
.css({
|
||||||
|
'font-family': associativeLineTextFontFamily,
|
||||||
|
'font-size': associativeLineTextFontSize
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新关联线文字位置
|
||||||
|
function updateTextPos(path, text) {
|
||||||
|
let pathLength = path.length()
|
||||||
|
let centerPoint = path.pointAt(pathLength / 2)
|
||||||
|
let { width: textWidth, height: textHeight } = text.bbox()
|
||||||
|
text.x(centerPoint.x - textWidth / 2)
|
||||||
|
text.y(centerPoint.y - textHeight / 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getText,
|
||||||
|
createText,
|
||||||
|
styleText,
|
||||||
|
onScale,
|
||||||
|
showEditTextBox,
|
||||||
|
hideEditTextBox,
|
||||||
|
updateTextEditBoxPos,
|
||||||
|
renderText,
|
||||||
|
updateTextPos
|
||||||
|
}
|
||||||
@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
> The function of adjusting associated line control points is supported from v0.4.6+
|
> The function of adjusting associated line control points is supported from v0.4.6+
|
||||||
|
|
||||||
This plugin is used to support the addition of associative lines.
|
> Relevance support for text editing starting from v0.5.11+
|
||||||
|
|
||||||
The plugin is currently not fully functional, and does not support adding text to association lines.
|
This plugin is used to support the addition of associative lines.
|
||||||
|
|
||||||
## Register
|
## Register
|
||||||
|
|
||||||
|
|||||||
@ -7,8 +7,10 @@
|
|||||||
<blockquote>
|
<blockquote>
|
||||||
<p>The function of adjusting associated line control points is supported from v0.4.6+</p>
|
<p>The function of adjusting associated line control points is supported from v0.4.6+</p>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
|
<blockquote>
|
||||||
|
<p>Relevance support for text editing starting from v0.5.11+</p>
|
||||||
|
</blockquote>
|
||||||
<p>This plugin is used to support the addition of associative lines.</p>
|
<p>This plugin is used to support the addition of associative lines.</p>
|
||||||
<p>The plugin is currently not fully functional, and does not support adding text to association lines.</p>
|
|
||||||
<h2>Register</h2>
|
<h2>Register</h2>
|
||||||
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map'</span>
|
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map'</span>
|
||||||
<span class="hljs-keyword">import</span> AssociativeLine <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map/src/AssociativeLine.js'</span>
|
<span class="hljs-keyword">import</span> AssociativeLine <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map/src/AssociativeLine.js'</span>
|
||||||
|
|||||||
@ -1,5 +1,11 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.5.11
|
||||||
|
|
||||||
|
New: Supports associative text editing.
|
||||||
|
|
||||||
|
optimization: Optimizing theme configuration updates, changing configurations that do not involve node size does not trigger node recalculation.
|
||||||
|
|
||||||
## 0.5.10
|
## 0.5.10
|
||||||
|
|
||||||
New: Optimize node reuse logic using LRU caching algorithm.
|
New: Optimize node reuse logic using LRU caching algorithm.
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h1>Changelog</h1>
|
<h1>Changelog</h1>
|
||||||
|
<h2>0.5.11</h2>
|
||||||
|
<p>New: Supports associative text editing.</p>
|
||||||
|
<p>optimization: Optimizing theme configuration updates, changing configurations that do not involve node size does not trigger node recalculation.</p>
|
||||||
<h2>0.5.10</h2>
|
<h2>0.5.10</h2>
|
||||||
<p>New: Optimize node reuse logic using LRU caching algorithm.</p>
|
<p>New: Optimize node reuse logic using LRU caching algorithm.</p>
|
||||||
<h2>0.5.10-fix.1</h2>
|
<h2>0.5.10-fix.1</h2>
|
||||||
|
|||||||
@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
> 调整关联线控制点的功能从v0.4.6+开始支持
|
> 调整关联线控制点的功能从v0.4.6+开始支持
|
||||||
|
|
||||||
该插件用于支持添加关联线。
|
> 关联性支持文本编辑从v0.5.11+开始支持
|
||||||
|
|
||||||
该插件目前功能还不完善,不支持在关联线上添加文字。
|
该插件用于支持添加关联线。
|
||||||
|
|
||||||
## 注册
|
## 注册
|
||||||
|
|
||||||
|
|||||||
@ -7,8 +7,10 @@
|
|||||||
<blockquote>
|
<blockquote>
|
||||||
<p>调整关联线控制点的功能从v0.4.6+开始支持</p>
|
<p>调整关联线控制点的功能从v0.4.6+开始支持</p>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
|
<blockquote>
|
||||||
|
<p>关联性支持文本编辑从v0.5.11+开始支持</p>
|
||||||
|
</blockquote>
|
||||||
<p>该插件用于支持添加关联线。</p>
|
<p>该插件用于支持添加关联线。</p>
|
||||||
<p>该插件目前功能还不完善,不支持在关联线上添加文字。</p>
|
|
||||||
<h2>注册</h2>
|
<h2>注册</h2>
|
||||||
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map'</span>
|
<pre class="hljs"><code><span class="hljs-keyword">import</span> MindMap <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map'</span>
|
||||||
<span class="hljs-keyword">import</span> AssociativeLine <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map/src/AssociativeLine.js'</span>
|
<span class="hljs-keyword">import</span> AssociativeLine <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map/src/AssociativeLine.js'</span>
|
||||||
|
|||||||
@ -1,5 +1,11 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.5.11
|
||||||
|
|
||||||
|
新增:支持关联性文本编辑。
|
||||||
|
|
||||||
|
优化:优化主题配置更新,改变不涉及节点大小的配置不触发节点重新计算。
|
||||||
|
|
||||||
## 0.5.10
|
## 0.5.10
|
||||||
|
|
||||||
新增:使用LRU缓存算法优化节点复用逻辑。
|
新增:使用LRU缓存算法优化节点复用逻辑。
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h1>Changelog</h1>
|
<h1>Changelog</h1>
|
||||||
|
<h2>0.5.11</h2>
|
||||||
|
<p>新增:支持关联性文本编辑。</p>
|
||||||
|
<p>优化:优化主题配置更新,改变不涉及节点大小的配置不触发节点重新计算。</p>
|
||||||
<h2>0.5.10</h2>
|
<h2>0.5.10</h2>
|
||||||
<p>新增:使用LRU缓存算法优化节点复用逻辑。</p>
|
<p>新增:使用LRU缓存算法优化节点复用逻辑。</p>
|
||||||
<h2>0.5.10-fix.1</h2>
|
<h2>0.5.10-fix.1</h2>
|
||||||
|
|||||||
@ -55,7 +55,7 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`icon`目前只能使用内置的图标,完整图标可以在[icons.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js)文件中查看。
|
`icon`可以使用内置的图标,完整图标可以在[icons.js](https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js)文件中查看。也可以扩展图标,参考[扩展图标](https://wanglin2.github.io/mind-map/#/doc/zh/course19/%E6%89%A9%E5%B1%95%E5%9B%BE%E6%A0%87)。
|
||||||
|
|
||||||
创建实例时还支持传递其他很多选项参数,完整选项列表可以在[实例化选项](https://wanglin2.github.io/mind-map/#/doc/zh/constructor/%E5%AE%9E%E4%BE%8B%E5%8C%96%E9%80%89%E9%A1%B9)查看。
|
创建实例时还支持传递其他很多选项参数,完整选项列表可以在[实例化选项](https://wanglin2.github.io/mind-map/#/doc/zh/constructor/%E5%AE%9E%E4%BE%8B%E5%8C%96%E9%80%89%E9%A1%B9)查看。
|
||||||
|
|
||||||
|
|||||||
@ -46,7 +46,7 @@
|
|||||||
<span class="hljs-attr">children</span>: []<span class="hljs-comment">// 子节点</span>
|
<span class="hljs-attr">children</span>: []<span class="hljs-comment">// 子节点</span>
|
||||||
}
|
}
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p><code>icon</code>目前只能使用内置的图标,完整图标可以在<a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js">icons.js</a>文件中查看。</p>
|
<p><code>icon</code>可以使用内置的图标,完整图标可以在<a href="https://github.com/wanglin2/mind-map/blob/main/simple-mind-map/src/svg/icons.js">icons.js</a>文件中查看。也可以扩展图标,参考<a href="https://wanglin2.github.io/mind-map/#/doc/zh/course19/%E6%89%A9%E5%B1%95%E5%9B%BE%E6%A0%87">扩展图标</a>。</p>
|
||||||
<p>创建实例时还支持传递其他很多选项参数,完整选项列表可以在<a href="https://wanglin2.github.io/mind-map/#/doc/zh/constructor/%E5%AE%9E%E4%BE%8B%E5%8C%96%E9%80%89%E9%A1%B9">实例化选项</a>查看。</p>
|
<p>创建实例时还支持传递其他很多选项参数,完整选项列表可以在<a href="https://wanglin2.github.io/mind-map/#/doc/zh/constructor/%E5%AE%9E%E4%BE%8B%E5%8C%96%E9%80%89%E9%A1%B9">实例化选项</a>查看。</p>
|
||||||
<p>这样得到的思维导图可以通过鼠标和快捷键进行操作,比如单击某个节点可以激活它,双击某个节点可以编辑节点文本,按下<code>Tab</code>键会给当前激活的节点添加一个子节点,按下<code>Enter</code>键会给当前激活的节点添加一个兄弟节点等等,完整的快捷键列表可以参考<a href="https://github.com/wanglin2/mind-map/blob/main/web/src/config/zh.js#L246">快捷键列表</a>。</p>
|
<p>这样得到的思维导图可以通过鼠标和快捷键进行操作,比如单击某个节点可以激活它,双击某个节点可以编辑节点文本,按下<code>Tab</code>键会给当前激活的节点添加一个子节点,按下<code>Enter</code>键会给当前激活的节点添加一个兄弟节点等等,完整的快捷键列表可以参考<a href="https://github.com/wanglin2/mind-map/blob/main/web/src/config/zh.js#L246">快捷键列表</a>。</p>
|
||||||
<p>当然有些功能还是需要UI界面的,比如图标列表、编辑超链接等等,需要注意的是<code>simple-mind-map</code>库并不包含UI界面,所以需要你自己开发,然后通过<code>simple-mind-map</code>提供的相关API来操作,本教程的其他章节会向你介绍如何使用。</p>
|
<p>当然有些功能还是需要UI界面的,比如图标列表、编辑超链接等等,需要注意的是<code>simple-mind-map</code>库并不包含UI界面,所以需要你自己开发,然后通过<code>simple-mind-map</code>提供的相关API来操作,本教程的其他章节会向你介绍如何使用。</p>
|
||||||
|
|||||||
@ -295,6 +295,67 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 关联线文字 -->
|
||||||
|
<div class="title noTop">关联线文字</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="rowItem">
|
||||||
|
<span class="name">字体</span>
|
||||||
|
<el-select
|
||||||
|
size="mini"
|
||||||
|
v-model="style.associativeLineTextFontFamily"
|
||||||
|
placeholder=""
|
||||||
|
@change="update('associativeLineTextFontFamily', $event)"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in fontFamilyList"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.value"
|
||||||
|
:style="{ fontFamily: item.value }"
|
||||||
|
>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="rowItem">
|
||||||
|
<span class="name">颜色</span>
|
||||||
|
<span
|
||||||
|
class="block"
|
||||||
|
v-popover:popover6
|
||||||
|
:style="{ backgroundColor: style.associativeLineTextColor }"
|
||||||
|
></span>
|
||||||
|
<el-popover ref="popover6" placement="bottom" trigger="click">
|
||||||
|
<Color
|
||||||
|
:color="style.associativeLineTextColor"
|
||||||
|
@change="
|
||||||
|
color => {
|
||||||
|
update('associativeLineTextColor', color)
|
||||||
|
}
|
||||||
|
"
|
||||||
|
></Color>
|
||||||
|
</el-popover>
|
||||||
|
</div>
|
||||||
|
<div class="rowItem">
|
||||||
|
<span class="name">字号</span>
|
||||||
|
<el-select
|
||||||
|
size="mini"
|
||||||
|
style="width: 80px"
|
||||||
|
v-model="style.associativeLineTextFontSize"
|
||||||
|
placeholder=""
|
||||||
|
@change="update('associativeLineTextFontSize', $event)"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in fontSizeList"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item"
|
||||||
|
:style="{ fontSize: item + 'px' }"
|
||||||
|
>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!-- 节点边框风格 -->
|
<!-- 节点边框风格 -->
|
||||||
<div class="title noTop">{{ $t('baseStyle.nodeBorderType') }}</div>
|
<div class="title noTop">{{ $t('baseStyle.nodeBorderType') }}</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -546,7 +607,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import Sidebar from './Sidebar'
|
import Sidebar from './Sidebar'
|
||||||
import Color from './Color'
|
import Color from './Color'
|
||||||
import { lineWidthList, lineStyleList, backgroundRepeatList, backgroundPositionList, backgroundSizeList } from '@/config'
|
import { lineWidthList, lineStyleList, backgroundRepeatList, backgroundPositionList, backgroundSizeList, fontFamilyList, fontSizeList } from '@/config'
|
||||||
import ImgUpload from '@/components/ImgUpload'
|
import ImgUpload from '@/components/ImgUpload'
|
||||||
import { storeConfig } from '@/api'
|
import { storeConfig } from '@/api'
|
||||||
import { mapState, mapMutations } from 'vuex'
|
import { mapState, mapMutations } from 'vuex'
|
||||||
@ -575,6 +636,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
lineWidthList,
|
lineWidthList,
|
||||||
|
fontSizeList,
|
||||||
activeTab: 'color',
|
activeTab: 'color',
|
||||||
marginActiveTab: 'second',
|
marginActiveTab: 'second',
|
||||||
style: {
|
style: {
|
||||||
@ -588,6 +650,9 @@ export default {
|
|||||||
associativeLineWidth: 0,
|
associativeLineWidth: 0,
|
||||||
associativeLineActiveWidth: 0,
|
associativeLineActiveWidth: 0,
|
||||||
associativeLineActiveColor: '',
|
associativeLineActiveColor: '',
|
||||||
|
associativeLineTextFontSize: 0,
|
||||||
|
associativeLineTextColor: '',
|
||||||
|
associativeLineTextFontFamily: '',
|
||||||
paddingX: 0,
|
paddingX: 0,
|
||||||
paddingY: 0,
|
paddingY: 0,
|
||||||
imgMaxWidth: 0,
|
imgMaxWidth: 0,
|
||||||
@ -636,6 +701,9 @@ export default {
|
|||||||
backgroundSizeList() {
|
backgroundSizeList() {
|
||||||
return backgroundSizeList[this.$i18n.locale] || backgroundSizeList.zh
|
return backgroundSizeList[this.$i18n.locale] || backgroundSizeList.zh
|
||||||
},
|
},
|
||||||
|
fontFamilyList() {
|
||||||
|
return fontFamilyList[this.$i18n.locale] || fontFamilyList.zh
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
activeSidebar(val) {
|
activeSidebar(val) {
|
||||||
@ -673,6 +741,9 @@ export default {
|
|||||||
'associativeLineWidth',
|
'associativeLineWidth',
|
||||||
'associativeLineActiveWidth',
|
'associativeLineActiveWidth',
|
||||||
'associativeLineActiveColor',
|
'associativeLineActiveColor',
|
||||||
|
'associativeLineTextFontSize',
|
||||||
|
'associativeLineTextColor',
|
||||||
|
'associativeLineTextFontFamily',
|
||||||
'paddingX',
|
'paddingX',
|
||||||
'paddingY',
|
'paddingY',
|
||||||
'imgMaxWidth',
|
'imgMaxWidth',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user