From 3763cd0efca1655a0aeeeb65dac3f5878719748b Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Sat, 26 Aug 2023 21:54:00 +0800 Subject: [PATCH 01/38] =?UTF-8?q?Feat:=E4=BF=AE=E6=94=B9=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E6=96=B9=E6=B3=95=E7=9A=84=E5=8F=82=E6=95=B0?= =?UTF-8?q?,=E5=AF=BC=E5=87=BApdf=E6=97=B6=E5=A6=82=E6=9E=9C=E6=80=9D?= =?UTF-8?q?=E7=BB=B4=E5=AF=BC=E5=9B=BE=E5=B0=BA=E5=AF=B8=E5=B0=8F=E4=BA=8E?= =?UTF-8?q?a4=E7=BA=B8=E9=82=A3=E4=B9=88=E4=B8=8D=E6=97=8B=E8=BD=AC?= =?UTF-8?q?=E6=96=B9=E5=90=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/constants/constant.js | 6 +++++ simple-mind-map/src/plugins/Export.js | 17 ++++++++----- simple-mind-map/src/plugins/ExportPDF.js | 31 ++++++++++------------- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/simple-mind-map/src/constants/constant.js b/simple-mind-map/src/constants/constant.js index f07b162f..13202200 100644 --- a/simple-mind-map/src/constants/constant.js +++ b/simple-mind-map/src/constants/constant.js @@ -342,4 +342,10 @@ export const ERROR_TYPES = { LOAD_CLIPBOARD_IMAGE_ERROR: 'load_clipboard_image_error', BEFORE_TEXT_EDIT_ERROR: 'before_text_edit_error', EXPORT_ERROR: 'export_error' +} + +// a4纸的宽高 +export const a4Size = { + width: 592.28, + height: 841.89 } \ No newline at end of file diff --git a/simple-mind-map/src/plugins/Export.js b/simple-mind-map/src/plugins/Export.js index 08c8ec3c..bb8dd3a6 100644 --- a/simple-mind-map/src/plugins/Export.js +++ b/simple-mind-map/src/plugins/Export.js @@ -7,6 +7,7 @@ import { import { SVG } from '@svgdotjs/svg.js' import drawBackgroundImageToCanvas from '../utils/simulateCSSBackgroundInCanvas' import { transformToMarkdown } from '../parse/toMarkdown' +import { a4Size } from '../constants/constant' // 导出插件 class Export { @@ -57,7 +58,7 @@ class Export { } // svg转png - svgToPng(svgSrc, transparent, rotateWhenWidthLongerThenHeight = false) { + svgToPng(svgSrc, transparent, checkRotate = () => { return false }) { return new Promise((resolve, reject) => { const img = new Image() // 跨域图片需要添加这个属性,否则画布被污染了无法导出图片 @@ -66,8 +67,7 @@ class Export { try { let canvas = document.createElement('canvas') // 如果宽比高长,那么旋转90度 - let needRotate = - rotateWhenWidthLongerThenHeight && img.width / img.height > 1 + let needRotate = checkRotate(img.width, img.height) if (needRotate) { canvas.width = img.height canvas.height = img.width @@ -179,7 +179,7 @@ class Export { * 方法1.把svg的图片都转化成data:url格式,再转换 * 方法2.把svg的图片提取出来再挨个绘制到canvas里,最后一起转换 */ - async png(name, transparent = false, rotateWhenWidthLongerThenHeight) { + async png(name, transparent = false, checkRotate) { let { node, str } = await this.getSvgData() str = removeHTMLEntities(str) // 如果开启了富文本,则使用htmltocanvas转换为图片 @@ -195,7 +195,7 @@ class Export { // let imgDataUrl = await this.svgToPng( // res, // transparent, - // rotateWhenWidthLongerThenHeight + // checkRotate // ) // return imgDataUrl } @@ -209,7 +209,7 @@ class Export { let res = await this.svgToPng( svgUrl, transparent, - rotateWhenWidthLongerThenHeight + checkRotate ) return res } @@ -219,7 +219,10 @@ class Export { if (!this.mindMap.doExportPDF) { throw new Error('请注册ExportPDF插件') } - let img = await this.png('', false, true) + let img = await this.png('', false, (width, height) => { + if (width <= a4Size.width && height && a4Size.height) return false + return (width / height) > 1 + }) this.mindMap.doExportPDF.pdf(name, img, useMultiPageExport) } diff --git a/simple-mind-map/src/plugins/ExportPDF.js b/simple-mind-map/src/plugins/ExportPDF.js index 27a81374..83f7ee88 100644 --- a/simple-mind-map/src/plugins/ExportPDF.js +++ b/simple-mind-map/src/plugins/ExportPDF.js @@ -1,4 +1,5 @@ import JsPDF from 'jspdf' +import { a4Size } from '../constants/constant' // 导出PDF插件,需要通过Export插件使用 class ExportPDF { @@ -19,29 +20,27 @@ class ExportPDF { // 单页导出 onePageExport(name, img) { let pdf = new JsPDF('', 'pt', 'a4') - let a4Width = 595 - let a4Height = 841 - let a4Ratio = a4Width / a4Height + let a4Ratio = a4Size.width / a4Size.height let image = new Image() image.onload = () => { let imageWidth = image.width let imageHeight = image.height let imageRatio = imageWidth / imageHeight let w, h - if (imageWidth <= a4Width && imageHeight <= a4Height) { + if (imageWidth <= a4Size.width && imageHeight <= a4Size.height) { // 使用图片原始宽高 w = imageWidth h = imageHeight } else if (a4Ratio > imageRatio) { // 以a4Height为高度,缩放图片宽度 - w = imageRatio * a4Height - h = a4Height + w = imageRatio * a4Size.height + h = a4Size.height } else { // 以a4Width为宽度,缩放图片高度 - w = a4Width - h = a4Width / imageRatio + w = a4Size.width + h = a4Size.width / imageRatio } - pdf.addImage(img, 'PNG', (a4Width - w) / 2, (a4Height - h) / 2, w, h) + pdf.addImage(img, 'PNG', (a4Size.width - w) / 2, (a4Size.height - h) / 2, w, h) pdf.save(name) } image.src = img @@ -50,20 +49,18 @@ class ExportPDF { // 多页导出 multiPageExport(name, img) { let image = new Image() - const a4Width = 592.28 - const a4Height = 841.89 image.onload = () => { let imageWidth = image.width let imageHeight = image.height // 一页pdf显示高度 - let pageHeight = (imageWidth / a4Width) * a4Height + let pageHeight = (imageWidth / a4Size.width) * a4Size.height // 未生成pdf的高度 let leftHeight = imageHeight // 偏移 let position = 0 // a4纸的尺寸[595.28,841.89],图片在pdf中图片的宽高 - let imgWidth = a4Width - let imgHeight = (a4Width / imageWidth) * imageHeight + let imgWidth = a4Size.width + let imgHeight = (a4Size.width / imageWidth) * imageHeight let pdf = new JsPDF('', 'pt', 'a4') // 有两个高度需要区分,一个是图片的实际高度,和生成pdf的页面高度(841.89) // 当内容未超过pdf一页显示的范围,无需分页 @@ -71,8 +68,8 @@ class ExportPDF { pdf.addImage( img, 'PNG', - (a4Width - imgWidth) / 2, - (a4Height - imgHeight) / 2, + (a4Size.width - imgWidth) / 2, + (a4Size.height - imgHeight) / 2, imgWidth, imgHeight ) @@ -81,7 +78,7 @@ class ExportPDF { while (leftHeight > 0) { pdf.addImage(img, 'PNG', 0, position, imgWidth, imgHeight) leftHeight -= pageHeight - position -= a4Height + position -= a4Size.height // 避免添加空白页 if (leftHeight > 0) { pdf.addPage() From 8c0c2c5bc47bc1efccb4de069d1d17effa976c30 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Sun, 27 Aug 2023 09:40:45 +0800 Subject: [PATCH 02/38] =?UTF-8?q?Feat:=E6=8F=90=E5=8D=87=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E7=9A=84=E5=9B=BE=E7=89=87=E5=92=8Cpdf=E5=9C=A8=E9=AB=98?= =?UTF-8?q?=E6=B8=85=E5=B1=8F=E7=9A=84=E6=B8=85=E6=99=B0=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/constants/defaultOptions.js | 4 +- simple-mind-map/src/plugins/Export.js | 38 +++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/simple-mind-map/src/constants/defaultOptions.js b/simple-mind-map/src/constants/defaultOptions.js index 0c8262e1..8aeb847b 100644 --- a/simple-mind-map/src/constants/defaultOptions.js +++ b/simple-mind-map/src/constants/defaultOptions.js @@ -166,5 +166,7 @@ export const defaultOpt = { } `, // 开启鼠标双击复位思维导图位置及缩放 - enableDblclickReset: true + enableDblclickReset: true, + // 导出图片时canvas的缩放倍数,该配置会和window.devicePixelRatio值取最大值 + minExportImgCanvasScale: 2 } diff --git a/simple-mind-map/src/plugins/Export.js b/simple-mind-map/src/plugins/Export.js index bb8dd3a6..aa447540 100644 --- a/simple-mind-map/src/plugins/Export.js +++ b/simple-mind-map/src/plugins/Export.js @@ -65,37 +65,35 @@ class Export { img.setAttribute('crossOrigin', 'anonymous') img.onload = async () => { try { - let canvas = document.createElement('canvas') + const canvas = document.createElement('canvas') + const dpr = Math.max(window.devicePixelRatio, this.mindMap.opt.minExportImgCanvasScale) + const imgWidth = img.width + const imgHeight = img.height // 如果宽比高长,那么旋转90度 - let needRotate = checkRotate(img.width, img.height) + const needRotate = checkRotate(imgWidth, imgHeight) if (needRotate) { - canvas.width = img.height - canvas.height = img.width + canvas.width = imgHeight * dpr + canvas.height = imgWidth * dpr + canvas.style.width = imgHeight + 'px' + canvas.style.height = imgWidth + 'px' } else { - canvas.width = img.width - canvas.height = img.height + canvas.width = imgWidth * dpr + canvas.height = imgHeight * dpr + canvas.style.width = imgWidth + 'px' + canvas.style.height = imgHeight + 'px' } - let ctx = canvas.getContext('2d') + const ctx = canvas.getContext('2d') + ctx.scale(dpr, dpr) if (needRotate) { ctx.rotate(0.5 * Math.PI) - ctx.translate(0, -img.height) + ctx.translate(0, -imgHeight) } // 绘制背景 if (!transparent) { - await this.drawBackgroundToCanvas(ctx, img.width, img.height) + await this.drawBackgroundToCanvas(ctx, imgWidth, imgHeight) } // 图片绘制到canvas里 - ctx.drawImage( - img, - 0, - 0, - img.width, - img.height, - 0, - 0, - img.width, - img.height - ) + ctx.drawImage(img, 0, 0, imgWidth, imgHeight) resolve(canvas.toDataURL()) } catch (error) { reject(error) From b35dd282ecc5ea1045f7f39a50a1d5b5b660cfc5 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Sun, 27 Aug 2023 10:34:15 +0800 Subject: [PATCH 03/38] =?UTF-8?q?Feat=EF=BC=9A=E6=8F=92=E4=BB=B6=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E9=94=80=E6=AF=81=E5=89=8D=E7=94=9F=E5=91=BD=E5=91=A8?= =?UTF-8?q?=E6=9C=9F=E5=87=BD=E6=95=B0=EF=BC=8C=E8=A7=A3=E5=86=B3=E9=94=80?= =?UTF-8?q?=E6=AF=81=E6=80=9D=E7=BB=B4=E5=AF=BC=E5=9B=BE=E6=97=B6=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E7=9A=84=E4=B8=80=E4=BA=9B=E5=89=AF=E4=BD=9C=E7=94=A8?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E6=B8=85=E9=99=A4=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/index.js | 4 ++++ simple-mind-map/src/plugins/NodeImgAdjust.js | 5 +++++ simple-mind-map/src/plugins/Painter.js | 5 +++++ simple-mind-map/src/plugins/RichText.js | 5 +++++ simple-mind-map/src/plugins/TouchEvent.js | 5 +++++ 5 files changed, 24 insertions(+) diff --git a/simple-mind-map/index.js b/simple-mind-map/index.js index f95a51e4..8767e978 100644 --- a/simple-mind-map/index.js +++ b/simple-mind-map/index.js @@ -406,6 +406,9 @@ class MindMap { destroy() { // 移除插件 ;[...MindMap.pluginList].forEach(plugin => { + if (this[plugin.instanceName].beforePluginDestroy) { + this[plugin.instanceName].beforePluginDestroy() + } this[plugin.instanceName] = null }) // 解绑事件 @@ -414,6 +417,7 @@ class MindMap { this.svg.remove() // 去除给容器元素设置的背景样式 Style.removeBackgroundStyle(this.el) + this.el.innerHTML = '' this.el = null } } diff --git a/simple-mind-map/src/plugins/NodeImgAdjust.js b/simple-mind-map/src/plugins/NodeImgAdjust.js index 4999f4bc..cdde7045 100644 --- a/simple-mind-map/src/plugins/NodeImgAdjust.js +++ b/simple-mind-map/src/plugins/NodeImgAdjust.js @@ -263,6 +263,11 @@ class NodeImgAdjust { beforePluginRemove() { this.unBindEvent() } + + // 插件被卸载前做的事情 + beforePluginDestroy() { + this.unBindEvent() + } } NodeImgAdjust.instanceName = 'nodeImgAdjust' diff --git a/simple-mind-map/src/plugins/Painter.js b/simple-mind-map/src/plugins/Painter.js index 50ee08e5..b9ef5cd3 100644 --- a/simple-mind-map/src/plugins/Painter.js +++ b/simple-mind-map/src/plugins/Painter.js @@ -69,6 +69,11 @@ class Painter { beforePluginRemove() { this.unBindEvent() } + + // 插件被卸载前做的事情 + beforePluginDestroy() { + this.unBindEvent() + } } Painter.instanceName = 'painter' diff --git a/simple-mind-map/src/plugins/RichText.js b/simple-mind-map/src/plugins/RichText.js index 8b393d60..2bd229e2 100644 --- a/simple-mind-map/src/plugins/RichText.js +++ b/simple-mind-map/src/plugins/RichText.js @@ -631,6 +631,11 @@ class RichText { this.transformAllNodesToNormalNode() document.head.removeChild(this.styleEl) } + + // 插件被卸载前做的事情 + beforePluginDestroy() { + document.head.removeChild(this.styleEl) + } } RichText.instanceName = 'richText' diff --git a/simple-mind-map/src/plugins/TouchEvent.js b/simple-mind-map/src/plugins/TouchEvent.js index e2a3a741..7bad4fc3 100644 --- a/simple-mind-map/src/plugins/TouchEvent.js +++ b/simple-mind-map/src/plugins/TouchEvent.js @@ -148,6 +148,11 @@ class TouchEvent { beforePluginRemove() { this.unBindEvent() } + + // 插件被卸载前做的事情 + beforePluginDestroy() { + this.unBindEvent() + } } TouchEvent.instanceName = 'touchEvent' From a24f7a73c897af5bf20b64513066b7757f1966f3 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Sun, 27 Aug 2023 22:10:49 +0800 Subject: [PATCH 04/38] =?UTF-8?q?Feat=EF=BC=9A=E8=8A=82=E7=82=B9=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E9=BC=A0=E6=A0=87=E6=BB=91=E8=BF=87=E6=95=88=E6=9E=9C?= =?UTF-8?q?=E3=80=81=E8=8A=82=E7=82=B9=E6=BF=80=E6=B4=BB=E6=95=88=E6=9E=9C?= =?UTF-8?q?=E9=87=8D=E6=9E=84=E3=80=81=E5=8E=BB=E9=99=A4=E6=BF=80=E6=B4=BB?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E6=94=B9=E5=8F=98=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/index.js | 21 ++++++++++- simple-mind-map/src/constants/constant.js | 22 +++++++++++- .../src/constants/defaultOptions.js | 4 ++- simple-mind-map/src/core/render/Render.js | 4 +-- simple-mind-map/src/core/render/node/Node.js | 35 ++++++++++--------- simple-mind-map/src/core/render/node/Shape.js | 16 ++++++++- simple-mind-map/src/core/render/node/Style.js | 31 ++++++++-------- 7 files changed, 97 insertions(+), 36 deletions(-) diff --git a/simple-mind-map/index.js b/simple-mind-map/index.js index 8767e978..44ad317f 100644 --- a/simple-mind-map/index.js +++ b/simple-mind-map/index.js @@ -11,7 +11,8 @@ import { layoutValueList, CONSTANTS, commonCaches, - ERROR_TYPES + ERROR_TYPES, + cssContent } from './src/constants/constant' import { SVG } from '@svgdotjs/svg.js' import { simpleDeepClone, getType } from './src/utils' @@ -37,6 +38,10 @@ class MindMap { this.height = this.elRect.height if (this.width <= 0 || this.height <= 0) throw new Error('容器元素el的宽高不能为0') + // 添加css + this.cssEl = null + this.addCss() + // 画布 this.svg = SVG().addTo(this.el).size(this.width, this.height) this.draw = this.svg.group() @@ -101,6 +106,19 @@ class MindMap { return opt } + // 添加css到页面 + addCss() { + this.cssEl = document.createElement('style') + this.cssEl.type = 'text/css' + this.cssEl.innerHTML = cssContent + document.head.appendChild(this.cssEl) + } + + // 移除css + removeCss() { + document.head.removeChild(this.cssEl) + } + // 渲染,部分渲染 render(callback, source = '') { this.batchExecution.push('render', () => { @@ -419,6 +437,7 @@ class MindMap { Style.removeBackgroundStyle(this.el) this.el.innerHTML = '' this.el = null + this.removeCss() } } diff --git a/simple-mind-map/src/constants/constant.js b/simple-mind-map/src/constants/constant.js index 13202200..3a90c01c 100644 --- a/simple-mind-map/src/constants/constant.js +++ b/simple-mind-map/src/constants/constant.js @@ -348,4 +348,24 @@ export const ERROR_TYPES = { export const a4Size = { width: 592.28, height: 841.89 -} \ No newline at end of file +} + +// css +export const cssContent = ` + /* 鼠标hover和激活时渲染的矩形 */ + .smm-hover-node{ + display: none; + opacity: 0.6; + stroke-width: 1; + } + + .smm-node:hover .smm-hover-node{ + display: block; + } + + .smm-node.active .smm-hover-node{ + display: block; + opacity: 1; + stroke-width: 2; + } +` \ No newline at end of file diff --git a/simple-mind-map/src/constants/defaultOptions.js b/simple-mind-map/src/constants/defaultOptions.js index 8aeb847b..9cd26ce9 100644 --- a/simple-mind-map/src/constants/defaultOptions.js +++ b/simple-mind-map/src/constants/defaultOptions.js @@ -168,5 +168,7 @@ export const defaultOpt = { // 开启鼠标双击复位思维导图位置及缩放 enableDblclickReset: true, // 导出图片时canvas的缩放倍数,该配置会和window.devicePixelRatio值取最大值 - minExportImgCanvasScale: 2 + minExportImgCanvasScale: 2, + // 节点鼠标hover和激活时显示的矩形边框颜色 + hoverRectColor: 'rgb(94, 200, 248)' } diff --git a/simple-mind-map/src/core/render/Render.js b/simple-mind-map/src/core/render/Render.js index 39506d75..b7bb3ba1 100644 --- a/simple-mind-map/src/core/render/Render.js +++ b/simple-mind-map/src/core/render/Render.js @@ -407,7 +407,7 @@ class Render { // 激活节点需要显示展开收起按钮 node.showExpandBtn() setTimeout(() => { - node.updateNodeShape() + node.updateNodeActive() }, 0) } }, @@ -1000,7 +1000,7 @@ class Render { } else { node.hideExpandBtn() } - node.updateNodeShape() + node.updateNodeActive() } // 设置节点是否展开 diff --git a/simple-mind-map/src/core/render/node/Node.js b/simple-mind-map/src/core/render/node/Node.js index f763b650..11eed5f9 100644 --- a/simple-mind-map/src/core/render/node/Node.js +++ b/simple-mind-map/src/core/render/node/Node.js @@ -1,6 +1,6 @@ import Style from './Style' import Shape from './Shape' -import { G, ForeignObject, SVG } from '@svgdotjs/svg.js' +import { G, ForeignObject, SVG, Rect } from '@svgdotjs/svg.js' import nodeGeneralizationMethods from './nodeGeneralization' import nodeExpandBtnMethods from './nodeExpandBtn' import nodeCommandWrapsMethods from './nodeCommandWraps' @@ -58,6 +58,7 @@ class Node { // 节点内容的容器 this.group = null this.shapeNode = null // 节点形状节点 + this.hoverNode = null // 节点hover和激活的节点 // 节点内容对象 this._customNodeContent = null this._imgData = null @@ -277,8 +278,8 @@ class Node { this.shapeNode = this.shapeInstance.createShape() this.shapeNode.addClass('smm-node-shape') this.shapeNode.translate(halfBorderWidth, halfBorderWidth) + this.style.shape(this.shapeNode) this.group.add(this.shapeNode) - this.updateNodeShape() // 渲染一个隐藏的矩形区域,用来触发展开收起按钮的显示 this.renderExpandBtnPlaceholderRect() // 概要节点添加一个带所属节点id的类名 @@ -365,6 +366,11 @@ class Node { : 0) ) this.group.add(textContentNested) + // 激活hover和激活边框 + this.hoverNode = new Rect() + this.hoverNode.addClass('smm-hover-node') + this.style.hoverNode(this.hoverNode, width, height) + this.group.add(this.hoverNode) } // 给节点绑定事件 @@ -467,10 +473,11 @@ class Node { } // 更新节点 - update(isLayout = false) { + update() { if (!this.group) { return } + this.updateNodeActive() let { alwaysShowExpandBtn } = this.mindMap.opt if (alwaysShowExpandBtn) { // 需要移除展开收缩按钮 @@ -543,13 +550,11 @@ class Node { return sizeChange } - // 更新节点形状样式 - updateNodeShape() { - if (!this.shapeNode) return - const shape = this.getShape() - this.style[shape === CONSTANTS.SHAPE.RECTANGLE ? 'rect' : 'shape']( - this.shapeNode - ) + // 更新节点激活状态 + updateNodeActive() { + if (!this.group) return + const isActive = this.nodeData.data.isActive + this.group[isActive ? 'addClass' : 'removeClass']('active') } // 递归渲染 @@ -557,9 +562,7 @@ class Node { // 节点 // 重新渲染连线 this.renderLine() - let isLayout = false if (!this.group) { - isLayout = true // 创建组 this.group = new G() this.group.addClass('smm-node') @@ -569,7 +572,7 @@ class Node { this.bindGroupEvent() this.draw.add(this.group) this.layout() - this.update(isLayout) + this.update() } else { this.draw.add(this.group) if (this.needLayout) { @@ -803,8 +806,8 @@ class Node { } // 获取某个样式 - getStyle(prop, root, isActive) { - let v = this.style.merge(prop, root, isActive) + getStyle(prop, root) { + let v = this.style.merge(prop, root) return v === undefined ? '' : v } @@ -833,7 +836,7 @@ class Node { // 获取节点非节点状态的边框大小 getBorderWidth() { - return this.style.merge('borderWidth', false, false) || 0 + return this.style.merge('borderWidth', false) || 0 } // 获取数据 diff --git a/simple-mind-map/src/core/render/node/Shape.js b/simple-mind-map/src/core/render/node/Shape.js index edefc6b2..f6d0e0df 100644 --- a/simple-mind-map/src/core/render/node/Shape.js +++ b/simple-mind-map/src/core/render/node/Shape.js @@ -69,7 +69,8 @@ export default class Shape { let node = null // 矩形 if (shape === CONSTANTS.SHAPE.RECTANGLE) { - node = new Rect().size(width, height) + // node = new Rect().size(width, height) + node = this.createRect() } else if (shape === CONSTANTS.SHAPE.DIAMOND) { // 菱形 node = this.createDiamond() @@ -98,6 +99,19 @@ export default class Shape { return node } + // 创建矩形TODO + createRect() { + let { width, height } = this.node + let borderRadius = this.node.style.merge('borderRadius') + return new Path().plot(` + M${0},0 + L${width},0 + L${width},${height} + L${0},${height} + L${0},${0} + `) + } + // 创建菱形 createDiamond() { let { width, height } = this.node diff --git a/simple-mind-map/src/core/render/node/Style.js b/simple-mind-map/src/core/render/node/Style.js index 9cd452d2..325b8989 100644 --- a/simple-mind-map/src/core/render/node/Style.js +++ b/simple-mind-map/src/core/render/node/Style.js @@ -42,7 +42,7 @@ class Style { } // 合并样式 - merge(prop, root, isActive) { + merge(prop, root) { let themeConfig = this.ctx.mindMap.themeConfig // 三级及以下节点 let defaultConfig = themeConfig.node @@ -59,17 +59,6 @@ class Style { // 二级节点 defaultConfig = themeConfig.second } - // 激活状态 - if (isActive !== undefined ? isActive : this.ctx.nodeData.data.isActive) { - if ( - this.ctx.nodeData.data.activeStyle && - this.ctx.nodeData.data.activeStyle[prop] !== undefined - ) { - return this.ctx.nodeData.data.activeStyle[prop] - } else if (defaultConfig.active && defaultConfig.active[prop]) { - return defaultConfig.active[prop] - } - } // 优先使用节点本身的样式 return this.getSelfStyle(prop) !== undefined ? this.getSelfStyle(prop) @@ -77,8 +66,8 @@ class Style { } // 获取某个样式值 - getStyle(prop, root, isActive) { - return this.merge(prop, root, isActive) + getStyle(prop, root) { + return this.merge(prop, root) } // 获取自身自定义样式 @@ -220,6 +209,20 @@ class Style { }) return res } + + // hover和激活节点 + hoverNode(node, width, height) { + const { hoverRectColor } = this.ctx.mindMap.opt + node + .size(width + 0, height + 0) + .x(-0) + .y(-0) + .radius(5) + .fill('none') + .stroke({ + color: hoverRectColor + }) + } } Style.cacheStyle = null From 1b0646af6d7c0d5f9fa76a23f103b81798dfc0b1 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Sun, 27 Aug 2023 22:51:18 +0800 Subject: [PATCH 05/38] =?UTF-8?q?Feat:=E8=8A=82=E7=82=B9=E7=9F=A9=E5=BD=A2?= =?UTF-8?q?=E5=BD=A2=E7=8A=B6=E6=94=B9=E4=B8=BA=E4=BD=BF=E7=94=A8path?= =?UTF-8?q?=E6=B8=B2=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/core/render/node/Shape.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/simple-mind-map/src/core/render/node/Shape.js b/simple-mind-map/src/core/render/node/Shape.js index f6d0e0df..a9ff7816 100644 --- a/simple-mind-map/src/core/render/node/Shape.js +++ b/simple-mind-map/src/core/render/node/Shape.js @@ -104,11 +104,18 @@ export default class Shape { let { width, height } = this.node let borderRadius = this.node.style.merge('borderRadius') return new Path().plot(` - M${0},0 - L${width},0 - L${width},${height} - L${0},${height} - L${0},${0} + M${borderRadius},0 + L${width - borderRadius},0 + C${width - borderRadius},0 ${width},${0} ${width},${borderRadius} + L${width},${height - borderRadius} + C${width},${height - borderRadius} ${width},${height} ${ + width - borderRadius + },${height} + L${borderRadius},${height} + C${borderRadius},${height} ${0},${height} ${0},${height - borderRadius} + L${0},${borderRadius} + C${0},${borderRadius} ${0},${0} ${borderRadius},${0} + Z `) } From e36e238c2f0e81f1c6b6d5a4d6e6c8c1c9550159 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Mon, 28 Aug 2023 08:49:45 +0800 Subject: [PATCH 06/38] =?UTF-8?q?Fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E5=A4=A7?= =?UTF-8?q?=E8=BE=B9=E6=A1=86=E5=B0=BA=E5=AF=B8=E7=9A=84=E6=B8=B2=E6=9F=93?= =?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=8C=E5=8C=85=E6=8B=AC=E9=87=8D=E5=8F=A0?= =?UTF-8?q?=EF=BC=8C=E5=86=85=E5=AE=B9=E4=B8=8D=E5=B1=85=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/core/render/node/Shape.js | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/simple-mind-map/src/core/render/node/Shape.js b/simple-mind-map/src/core/render/node/Shape.js index a9ff7816..217a8ae6 100644 --- a/simple-mind-map/src/core/render/node/Shape.js +++ b/simple-mind-map/src/core/render/node/Shape.js @@ -62,14 +62,9 @@ export default class Shape { // 创建形状节点 createShape() { const shape = this.node.getShape() - const borderWidth = this.node.getBorderWidth() - let { width, height } = this.node - width -= borderWidth - height -= borderWidth let node = null // 矩形 if (shape === CONSTANTS.SHAPE.RECTANGLE) { - // node = new Rect().size(width, height) node = this.createRect() } else if (shape === CONSTANTS.SHAPE.DIAMOND) { // 菱形 @@ -99,9 +94,21 @@ export default class Shape { return node } - // 创建矩形TODO - createRect() { + // 获取节点减去边框宽度的尺寸 + getNodeSize() { + const borderWidth = this.node.getBorderWidth() let { width, height } = this.node + width -= borderWidth + height -= borderWidth + return { + width, + height + } + } + + // 创建矩形 + createRect() { + let { width, height } = this.getNodeSize() let borderRadius = this.node.style.merge('borderRadius') return new Path().plot(` M${borderRadius},0 @@ -121,7 +128,7 @@ export default class Shape { // 创建菱形 createDiamond() { - let { width, height } = this.node + let { width, height } = this.getNodeSize() let halfWidth = width / 2 let halfHeight = height / 2 let topX = halfWidth @@ -144,7 +151,7 @@ export default class Shape { createParallelogram() { let { paddingX } = this.node.getPaddingVale() paddingX = paddingX || this.node.shapePadding.paddingX - let { width, height } = this.node + let { width, height } = this.getNodeSize() return new Polygon().plot([ [paddingX, 0], [width, 0], @@ -155,7 +162,7 @@ export default class Shape { // 创建圆角矩形 createRoundedRectangle() { - let { width, height } = this.node + let { width, height } = this.getNodeSize() let halfHeight = height / 2 return new Path().plot(` M${halfHeight},0 @@ -169,7 +176,7 @@ export default class Shape { // 创建八角矩形 createOctagonalRectangle() { let w = 5 - let { width, height } = this.node + let { width, height } = this.getNodeSize() return new Polygon().plot([ [0, w], [w, 0], @@ -186,7 +193,7 @@ export default class Shape { createOuterTriangularRectangle() { let { paddingX } = this.node.getPaddingVale() paddingX = paddingX || this.node.shapePadding.paddingX - let { width, height } = this.node + let { width, height } = this.getNodeSize() return new Polygon().plot([ [paddingX, 0], [width - paddingX, 0], @@ -201,7 +208,7 @@ export default class Shape { createInnerTriangularRectangle() { let { paddingX } = this.node.getPaddingVale() paddingX = paddingX || this.node.shapePadding.paddingX - let { width, height } = this.node + let { width, height } = this.getNodeSize() return new Polygon().plot([ [0, 0], [width, 0], @@ -214,7 +221,7 @@ export default class Shape { // 创建椭圆 createEllipse() { - let { width, height } = this.node + let { width, height } = this.getNodeSize() let halfWidth = width / 2 let halfHeight = height / 2 return new Path().plot(` @@ -227,7 +234,7 @@ export default class Shape { // 创建圆 createCircle() { - let { width, height } = this.node + let { width, height } = this.getNodeSize() let halfWidth = width / 2 let halfHeight = height / 2 return new Path().plot(` From d14d887c1a4f03b2f78f5248793d22e96e7531c1 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Mon, 28 Aug 2023 09:11:39 +0800 Subject: [PATCH 07/38] =?UTF-8?q?Feat=EF=BC=9A=E4=BC=98=E5=8C=96=E9=BC=A0?= =?UTF-8?q?=E6=A0=87hover=E5=92=8C=E6=BF=80=E6=B4=BB=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E7=9F=A9=E5=BD=A2=EF=BC=8C=E5=A2=9E=E5=8A=A0=E5=92=8C=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E7=9A=84=E8=B7=9D=E7=A6=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/constants/defaultOptions.js | 6 ++++-- simple-mind-map/src/core/render/node/Node.js | 13 ++++++++----- simple-mind-map/src/core/render/node/Shape.js | 7 ++++--- simple-mind-map/src/core/render/node/Style.js | 5 +---- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/simple-mind-map/src/constants/defaultOptions.js b/simple-mind-map/src/constants/defaultOptions.js index 9cd26ce9..111d8ac6 100644 --- a/simple-mind-map/src/constants/defaultOptions.js +++ b/simple-mind-map/src/constants/defaultOptions.js @@ -169,6 +169,8 @@ export const defaultOpt = { enableDblclickReset: true, // 导出图片时canvas的缩放倍数,该配置会和window.devicePixelRatio值取最大值 minExportImgCanvasScale: 2, - // 节点鼠标hover和激活时显示的矩形边框颜色 - hoverRectColor: 'rgb(94, 200, 248)' + // 节点鼠标hover和激活时显示的矩形边框的颜色 + hoverRectColor: 'rgb(94, 200, 248)', + // 节点鼠标hover和激活时显示的矩形边框距节点内容的距离 + hoverRectPadding: 2 } diff --git a/simple-mind-map/src/core/render/node/Node.js b/simple-mind-map/src/core/render/node/Node.js index 11eed5f9..b19b7b51 100644 --- a/simple-mind-map/src/core/render/node/Node.js +++ b/simple-mind-map/src/core/render/node/Node.js @@ -260,9 +260,11 @@ class Node { this.shapePadding.paddingY = shapePaddingY // 边框宽度,因为边框是以中线向两端发散,所以边框会超出节点 const borderWidth = this.getBorderWidth() + const { hoverRectPadding } = this.mindMap.opt return { - width: _width + paddingX * 2 + shapePaddingX * 2 + borderWidth, - height: _height + paddingY * 2 + margin + shapePaddingY * 2 + borderWidth + width: _width + paddingX * 2 + shapePaddingX * 2 + borderWidth + hoverRectPadding * 2, + height: + _height + paddingY * 2 + margin + shapePaddingY * 2 + borderWidth + hoverRectPadding * 2 } } @@ -270,14 +272,15 @@ class Node { layout() { // 清除之前的内容 this.group.clear() + const { hoverRectPadding } = this.mindMap.opt let { width, height, textContentItemMargin } = this let { paddingY } = this.getPaddingVale() const halfBorderWidth = this.getBorderWidth() / 2 - paddingY += this.shapePadding.paddingY + halfBorderWidth + paddingY += this.shapePadding.paddingY + halfBorderWidth + hoverRectPadding // 节点形状 this.shapeNode = this.shapeInstance.createShape() this.shapeNode.addClass('smm-node-shape') - this.shapeNode.translate(halfBorderWidth, halfBorderWidth) + this.shapeNode.translate(halfBorderWidth + hoverRectPadding, halfBorderWidth + hoverRectPadding) this.style.shape(this.shapeNode) this.group.add(this.shapeNode) // 渲染一个隐藏的矩形区域,用来触发展开收起按钮的显示 @@ -367,7 +370,7 @@ class Node { ) this.group.add(textContentNested) // 激活hover和激活边框 - this.hoverNode = new Rect() + this.hoverNode = new Rect().size(width, height).x(0).y(0) this.hoverNode.addClass('smm-hover-node') this.style.hoverNode(this.hoverNode, width, height) this.group.add(this.hoverNode) diff --git a/simple-mind-map/src/core/render/node/Shape.js b/simple-mind-map/src/core/render/node/Shape.js index 217a8ae6..ef85c3b0 100644 --- a/simple-mind-map/src/core/render/node/Shape.js +++ b/simple-mind-map/src/core/render/node/Shape.js @@ -94,12 +94,13 @@ export default class Shape { return node } - // 获取节点减去边框宽度的尺寸 + // 获取节点减去节点边框宽度、hover节点边框宽度后的尺寸 getNodeSize() { const borderWidth = this.node.getBorderWidth() let { width, height } = this.node - width -= borderWidth - height -= borderWidth + const { hoverRectPadding } = this.node.mindMap.opt + width -= borderWidth + hoverRectPadding * 2 + height -= borderWidth + hoverRectPadding * 2 return { width, height diff --git a/simple-mind-map/src/core/render/node/Style.js b/simple-mind-map/src/core/render/node/Style.js index 325b8989..2f8ab7f0 100644 --- a/simple-mind-map/src/core/render/node/Style.js +++ b/simple-mind-map/src/core/render/node/Style.js @@ -211,12 +211,9 @@ class Style { } // hover和激活节点 - hoverNode(node, width, height) { + hoverNode(node) { const { hoverRectColor } = this.ctx.mindMap.opt node - .size(width + 0, height + 0) - .x(-0) - .y(-0) .radius(5) .fill('none') .stroke({ From 217d66f692ded65de289404bb4e68a293550ccb4 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Mon, 28 Aug 2023 09:21:18 +0800 Subject: [PATCH 08/38] =?UTF-8?q?Feat=EF=BC=9A=E5=88=A0=E9=99=A4=E4=B8=BB?= =?UTF-8?q?=E9=A2=98=E6=96=87=E4=BB=B6=E4=B8=AD=E8=8A=82=E7=82=B9=E6=BF=80?= =?UTF-8?q?=E6=B4=BB=E6=A0=B7=E5=BC=8F=E7=9A=84=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/themes/autumn.js | 21 +++----------- simple-mind-map/src/themes/avocado.js | 21 +++----------- simple-mind-map/src/themes/blackGold.js | 21 +++----------- simple-mind-map/src/themes/blackHumour.js | 22 +++------------ simple-mind-map/src/themes/blueSky.js | 20 +++---------- .../src/themes/brainImpairedPink.js | 20 +++---------- simple-mind-map/src/themes/classic.js | 24 +++------------- simple-mind-map/src/themes/classic2.js | 20 +++---------- simple-mind-map/src/themes/classic3.js | 20 +++---------- simple-mind-map/src/themes/classic4.js | 20 +++---------- simple-mind-map/src/themes/classicBlue.js | 20 +++---------- simple-mind-map/src/themes/classicGreen.js | 20 +++---------- simple-mind-map/src/themes/coffee.js | 21 +++----------- simple-mind-map/src/themes/courseGreen.js | 21 +++----------- simple-mind-map/src/themes/dark.js | 20 +++---------- simple-mind-map/src/themes/dark2.js | 22 +++------------ simple-mind-map/src/themes/default.js | 28 +++---------------- simple-mind-map/src/themes/earthYellow.js | 20 +++---------- simple-mind-map/src/themes/freshGreen.js | 7 +---- simple-mind-map/src/themes/freshRed.js | 20 +++---------- simple-mind-map/src/themes/gold.js | 22 +++------------ simple-mind-map/src/themes/greenLeaf.js | 22 +++------------ simple-mind-map/src/themes/lateNightOffice.js | 22 +++------------ simple-mind-map/src/themes/minions.js | 20 +++---------- simple-mind-map/src/themes/mint.js | 21 +++----------- simple-mind-map/src/themes/orangeJuice.js | 21 +++----------- simple-mind-map/src/themes/pinkGrape.js | 23 +++------------ simple-mind-map/src/themes/redSpirit.js | 21 +++----------- simple-mind-map/src/themes/romanticPurple.js | 20 +++---------- simple-mind-map/src/themes/simpleBlack.js | 20 +++---------- simple-mind-map/src/themes/skyGreen.js | 22 +++------------ simple-mind-map/src/themes/vitalityOrange.js | 22 +++------------ web/src/customThemes/darkNightLceBlade.js | 20 +++---------- web/src/customThemes/lemonBubbles.js | 20 +++---------- web/src/customThemes/morandi.js | 20 +++---------- web/src/customThemes/neonLamp.js | 20 +++---------- web/src/customThemes/oreo.js | 20 +++---------- web/src/customThemes/rose.js | 20 +++---------- web/src/customThemes/seaBlueLine.js | 20 +++---------- web/src/customThemes/shallowSea.js | 20 +++---------- 40 files changed, 157 insertions(+), 667 deletions(-) diff --git a/simple-mind-map/src/themes/autumn.js b/simple-mind-map/src/themes/autumn.js index 66c301bb..b7d295b1 100644 --- a/simple-mind-map/src/themes/autumn.js +++ b/simple-mind-map/src/themes/autumn.js @@ -18,11 +18,7 @@ export default merge(defaultTheme, { color: '#fff', borderColor: '#e68112', borderWidth: 0, - fontSize: 24, - active: { - borderColor: '#b0bc47', - borderWidth: 3 - } + fontSize: 24 }, // 二级节点样式 second: { @@ -30,18 +26,12 @@ export default merge(defaultTheme, { color: '#8c5416', borderColor: '#b0bc47', borderWidth: 2, - fontSize: 18, - active: { - borderColor: '#e68112' - } + fontSize: 18 }, // 三级及以下节点样式 node: { fontSize: 14, - color: '#8c5416', - active: { - borderColor: '#b0bc47' - } + color: '#8c5416' }, // 概要节点样式 generalization: { @@ -49,9 +39,6 @@ export default merge(defaultTheme, { fillColor: '#ffd683', borderColor: '#b0bc47', borderWidth: 2, - color: '#8c5416', - active: { - borderColor: '#e68112' - } + color: '#8c5416' } }) diff --git a/simple-mind-map/src/themes/avocado.js b/simple-mind-map/src/themes/avocado.js index d0d4f2bf..d425388b 100644 --- a/simple-mind-map/src/themes/avocado.js +++ b/simple-mind-map/src/themes/avocado.js @@ -18,11 +18,7 @@ export default merge(defaultTheme, { color: '#fff', borderColor: '#94c143', borderWidth: 0, - fontSize: 24, - active: { - borderColor: '#749336', - borderWidth: 3 - } + fontSize: 24 }, // 二级节点样式 second: { @@ -30,18 +26,12 @@ export default merge(defaultTheme, { color: '#749336', borderColor: '#aec668', borderWidth: 2, - fontSize: 18, - active: { - borderColor: '#749336' - } + fontSize: 18 }, // 三级及以下节点样式 node: { fontSize: 14, - color: '#749336', - active: { - borderColor: '#749336' - } + color: '#749336' }, // 概要节点样式 generalization: { @@ -49,9 +39,6 @@ export default merge(defaultTheme, { fillColor: '#cee498', borderColor: '#aec668', borderWidth: 2, - color: '#749336', - active: { - borderColor: '#749336' - } + color: '#749336' } }) diff --git a/simple-mind-map/src/themes/blackGold.js b/simple-mind-map/src/themes/blackGold.js index d694fe15..c11cd4ee 100644 --- a/simple-mind-map/src/themes/blackGold.js +++ b/simple-mind-map/src/themes/blackGold.js @@ -18,11 +18,7 @@ export default merge(defaultTheme, { color: 'rgb(111, 61, 6)', borderColor: '', borderWidth: 0, - fontSize: 24, - active: { - borderColor: '#fff', - borderWidth: 3 - } + fontSize: 24 }, // 二级节点样式 second: { @@ -30,18 +26,12 @@ export default merge(defaultTheme, { color: 'rgb(225, 201, 158)', borderColor: 'rgb(245, 224, 191)', borderWidth: 2, - fontSize: 18, - active: { - borderColor: 'rgb(255, 208, 124)' - } + fontSize: 18 }, // 三级及以下节点样式 node: { fontSize: 14, - color: 'rgb(231, 203, 155)', - active: { - borderColor: 'rgb(255, 208, 124)' - } + color: 'rgb(231, 203, 155)' }, // 概要节点样式 generalization: { @@ -49,9 +39,6 @@ export default merge(defaultTheme, { fillColor: 'rgb(56, 45, 34)', borderColor: 'rgb(104, 84, 61)', borderWidth: 2, - color: 'rgb(242, 216, 176)', - active: { - borderColor: 'rgb(255, 208, 124)' - } + color: 'rgb(242, 216, 176)' } }) diff --git a/simple-mind-map/src/themes/blackHumour.js b/simple-mind-map/src/themes/blackHumour.js index e219ff98..62de9777 100644 --- a/simple-mind-map/src/themes/blackHumour.js +++ b/simple-mind-map/src/themes/blackHumour.js @@ -18,11 +18,7 @@ export default merge(defaultTheme, { color: '#fff', borderColor: '', borderWidth: 0, - fontSize: 24, - active: { - borderColor: 'rgb(254, 199, 13)', - borderWidth: 3 - } + fontSize: 24 }, // 二级节点样式 second: { @@ -30,19 +26,12 @@ export default merge(defaultTheme, { color: 'rgb(0, 0, 0)', borderColor: '', borderWidth: 0, - fontSize: 18, - active: { - borderColor: 'rgb(36, 179, 96)', - borderWidth: 3 - } + fontSize: 18 }, // 三级及以下节点样式 node: { fontSize: 14, - color: 'rgb(204, 204, 204)', - active: { - borderColor: 'rgb(254, 199, 13)' - } + color: 'rgb(204, 204, 204)' }, // 概要节点样式 generalization: { @@ -50,9 +39,6 @@ export default merge(defaultTheme, { fillColor: 'rgb(27, 31, 34)', borderColor: 'rgb(255, 119, 34)', borderWidth: 2, - color: 'rgb(204, 204, 204)', - active: { - borderColor: 'rgb(36, 179, 96)' - } + color: 'rgb(204, 204, 204)' } }) diff --git a/simple-mind-map/src/themes/blueSky.js b/simple-mind-map/src/themes/blueSky.js index 1f464a72..f5343553 100644 --- a/simple-mind-map/src/themes/blueSky.js +++ b/simple-mind-map/src/themes/blueSky.js @@ -13,10 +13,7 @@ export default merge(defaultTheme, { generalizationLineColor: '#333', // 根节点样式 root: { - fillColor: 'rgb(115, 161, 191)', - active: { - borderColor: 'rgb(57, 80, 96)' - } + fillColor: 'rgb(115, 161, 191)' }, // 二级节点样式 second: { @@ -24,26 +21,17 @@ export default merge(defaultTheme, { color: '#333', borderColor: 'rgb(115, 161, 191)', borderWidth: 1, - fontSize: 14, - active: { - borderColor: 'rgb(57, 80, 96)' - } + fontSize: 14 }, // 三级及以下节点样式 node: { fontSize: 12, - color: '#333', - active: { - borderColor: 'rgb(57, 80, 96)' - } + color: '#333' }, // 概要节点样式 generalization: { fillColor: '#fff', borderColor: '#333', - color: '#333', - active: { - borderColor: 'rgb(57, 80, 96)' - } + color: '#333' } }) diff --git a/simple-mind-map/src/themes/brainImpairedPink.js b/simple-mind-map/src/themes/brainImpairedPink.js index e301bdfc..01fd3283 100644 --- a/simple-mind-map/src/themes/brainImpairedPink.js +++ b/simple-mind-map/src/themes/brainImpairedPink.js @@ -13,10 +13,7 @@ export default merge(defaultTheme, { generalizationLineColor: '#333', // 根节点样式 root: { - fillColor: 'rgb(191, 115, 148)', - active: { - borderColor: 'rgb(96, 57, 74)' - } + fillColor: 'rgb(191, 115, 148)' }, // 二级节点样式 second: { @@ -24,26 +21,17 @@ export default merge(defaultTheme, { color: '#333', borderColor: 'rgb(191, 115, 148)', borderWidth: 1, - fontSize: 14, - active: { - borderColor: 'rgb(96, 57, 74)' - } + fontSize: 14 }, // 三级及以下节点样式 node: { fontSize: 12, - color: '#333', - active: { - borderColor: 'rgb(96, 57, 74)' - } + color: '#333' }, // 概要节点样式 generalization: { fillColor: '#fff', borderColor: '#333', - color: '#333', - active: { - borderColor: 'rgb(96, 57, 74)' - } + color: '#333' } }) diff --git a/simple-mind-map/src/themes/classic.js b/simple-mind-map/src/themes/classic.js index eeeff67e..e58a7d3d 100644 --- a/simple-mind-map/src/themes/classic.js +++ b/simple-mind-map/src/themes/classic.js @@ -23,11 +23,7 @@ export default merge(defaultTheme, { fillColor: 'rgb(233, 223, 152)', color: '#333', fontSize: 24, - borderRadius: 21, - active: { - fillColor: 'rgb(254, 219, 0)', - borderColor: 'transparent' - } + borderRadius: 21 }, // 二级节点样式 second: { @@ -35,30 +31,18 @@ export default merge(defaultTheme, { borderColor: 'transparent', color: '#333', fontSize: 16, - borderRadius: 10, - active: { - fillColor: 'rgb(254, 219, 0)', - borderColor: 'transparent' - } + borderRadius: 10 }, // 三级及以下节点样式 node: { fontSize: 12, color: '#fff', - fontWeight: 'bold', - active: { - fillColor: 'rgb(254, 219, 0)', - borderColor: 'transparent' - } + fontWeight: 'bold' }, // 概要节点样式 generalization: { fillColor: '#fff', borderColor: 'transparent', - color: '#333', - active: { - fillColor: 'rgb(254, 219, 0)', - borderColor: 'transparent' - } + color: '#333' } }) diff --git a/simple-mind-map/src/themes/classic2.js b/simple-mind-map/src/themes/classic2.js index 2c40fc70..1fef1fe2 100644 --- a/simple-mind-map/src/themes/classic2.js +++ b/simple-mind-map/src/themes/classic2.js @@ -18,10 +18,7 @@ export default merge(defaultTheme, { fillColor: 'rgb(18, 187, 55)', color: '#fff', fontSize: 24, - borderRadius: 10, - active: { - borderColor: 'rgb(51, 51, 51)' - } + borderRadius: 10 }, // 二级节点样式 second: { @@ -29,27 +26,18 @@ export default merge(defaultTheme, { borderColor: 'transparent', color: '#1a1a1a', fontSize: 18, - borderRadius: 10, - active: { - borderColor: 'rgb(51, 51, 51)' - } + borderRadius: 10 }, // 三级及以下节点样式 node: { fontSize: 14, - color: '#1a1a1a', - active: { - borderColor: 'rgb(51, 51, 51)' - } + color: '#1a1a1a' }, // 概要节点样式 generalization: { fillColor: '#fff', borderColor: 'rgb(51, 51, 51)', borderWidth: 2, - color: '#1a1a1a', - active: { - borderColor: 'rgb(18, 187, 55)' - } + color: '#1a1a1a' } }) diff --git a/simple-mind-map/src/themes/classic3.js b/simple-mind-map/src/themes/classic3.js index 27ce5c6b..21523e2c 100644 --- a/simple-mind-map/src/themes/classic3.js +++ b/simple-mind-map/src/themes/classic3.js @@ -20,10 +20,7 @@ export default merge(defaultTheme, { fontSize: 24, borderRadius: 10, borderColor: 'rgb(249, 199, 84)', - borderWidth: 1, - active: { - borderColor: 'rgb(94, 202, 110)' - } + borderWidth: 1 }, // 二级节点样式 second: { @@ -32,27 +29,18 @@ export default merge(defaultTheme, { borderWidth: 1, color: '#1a1a1a', fontSize: 18, - borderRadius: 10, - active: { - borderColor: 'rgb(94, 202, 110)' - } + borderRadius: 10 }, // 三级及以下节点样式 node: { fontSize: 14, - color: '#1a1a1a', - active: { - borderColor: 'rgb(94, 202, 110)' - } + color: '#1a1a1a' }, // 概要节点样式 generalization: { fillColor: '#fff', borderColor: '#1a1a1a', color: '#1a1a1a', - borderWidth: 2, - active: { - borderColor: 'rgb(94, 202, 110)' - } + borderWidth: 2 } }) diff --git a/simple-mind-map/src/themes/classic4.js b/simple-mind-map/src/themes/classic4.js index a4046e9e..70886afb 100644 --- a/simple-mind-map/src/themes/classic4.js +++ b/simple-mind-map/src/themes/classic4.js @@ -20,10 +20,7 @@ export default merge(defaultTheme, { fontSize: 24, borderRadius: 10, borderColor: 'rgb(189, 197, 201)', - borderWidth: 2, - active: { - borderColor: 'rgb(169, 218, 218)' - } + borderWidth: 2 }, // 二级节点样式 second: { @@ -32,10 +29,7 @@ export default merge(defaultTheme, { borderWidth: 2, color: '#fff', fontSize: 18, - borderRadius: 10, - active: { - borderColor: 'rgb(56, 123, 233)' - } + borderRadius: 10 }, // 三级及以下节点样式 node: { @@ -43,19 +37,13 @@ export default merge(defaultTheme, { color: 'rgb(30, 53, 86)', borderColor: 'rgb(30, 53, 86)', borderWidth: 1, - marginY: 20, - active: { - borderColor: 'rgb(169, 218, 218)' - } + marginY: 20 }, // 概要节点样式 generalization: { fillColor: 'rgb(56, 123, 233)', borderColor: 'rgb(56, 123, 233)', color: '#fff', - borderWidth: 0, - active: { - borderColor: 'rgb(169, 218, 218)' - } + borderWidth: 0 } }) diff --git a/simple-mind-map/src/themes/classicBlue.js b/simple-mind-map/src/themes/classicBlue.js index 59539342..8b7a60b3 100644 --- a/simple-mind-map/src/themes/classicBlue.js +++ b/simple-mind-map/src/themes/classicBlue.js @@ -16,10 +16,7 @@ export default merge(defaultTheme, { // 根节点样式 root: { fillColor: 'rgb(255, 255, 255)', - color: '#222', - active: { - borderColor: 'rgb(94, 199, 248)' - } + color: '#222' }, // 二级节点样式 second: { @@ -27,26 +24,17 @@ export default merge(defaultTheme, { color: '#222', borderColor: 'rgb(255, 255, 255)', borderWidth: 1, - fontSize: 14, - active: { - borderColor: 'rgb(94, 199, 248)' - } + fontSize: 14 }, // 三级及以下节点样式 node: { fontSize: 12, - color: '#333', - active: { - borderColor: 'rgb(94, 199, 248)' - } + color: '#333' }, // 概要节点样式 generalization: { fillColor: '#fff', borderColor: 'rgb(51, 51, 51)', - color: '#333', - active: { - borderColor: 'rgb(94, 199, 248)' - } + color: '#333' } }) diff --git a/simple-mind-map/src/themes/classicGreen.js b/simple-mind-map/src/themes/classicGreen.js index 87725dbf..65b6e81b 100644 --- a/simple-mind-map/src/themes/classicGreen.js +++ b/simple-mind-map/src/themes/classicGreen.js @@ -14,10 +14,7 @@ export default merge(defaultTheme, { // 根节点样式 root: { fillColor: 'rgb(253, 244, 217)', - color: '#222', - active: { - borderColor: 'rgb(94, 199, 248)' - } + color: '#222' }, // 二级节点样式 second: { @@ -25,27 +22,18 @@ export default merge(defaultTheme, { color: '#222', borderColor: 'rgb(242, 200, 104)', borderWidth: 1, - fontSize: 14, - active: { - borderColor: 'rgb(94, 199, 248)' - } + fontSize: 14 }, // 三级及以下节点样式 node: { fontSize: 12, - color: '#333', - active: { - borderColor: 'rgb(94, 199, 248)' - } + color: '#333' }, // 概要节点样式 generalization: { fillColor: 'rgb(123, 199, 120)', borderColor: 'transparent', borderWidth: 2, - color: '#fff', - active: { - borderColor: 'rgb(94, 199, 248)' - } + color: '#fff' } }) diff --git a/simple-mind-map/src/themes/coffee.js b/simple-mind-map/src/themes/coffee.js index 792a3389..b3f73d3c 100644 --- a/simple-mind-map/src/themes/coffee.js +++ b/simple-mind-map/src/themes/coffee.js @@ -16,11 +16,7 @@ export default merge(defaultTheme, { color: '#fff', borderColor: '', borderWidth: 0, - fontSize: 24, - active: { - borderColor: 'rgb(173, 123, 91)', - borderWidth: 3 - } + fontSize: 24 }, // 二级节点样式 second: { @@ -28,18 +24,12 @@ export default merge(defaultTheme, { color: 'rgb(125, 86, 42)', borderColor: '', borderWidth: 0, - fontSize: 18, - active: { - borderColor: 'rgb(173, 123, 91)' - } + fontSize: 18 }, // 三级及以下节点样式 node: { fontSize: 14, - color: 'rgb(96, 71, 47)', - active: { - borderColor: 'rgb(173, 123, 91)' - } + color: 'rgb(96, 71, 47)' }, // 概要节点样式 generalization: { @@ -47,9 +37,6 @@ export default merge(defaultTheme, { fillColor: 'rgb(255, 249, 239)', borderColor: 'rgb(173, 123, 91)', borderWidth: 2, - color: 'rgb(122, 83, 44)', - active: { - borderColor: 'rgb(202, 117, 79)' - } + color: 'rgb(122, 83, 44)' } }) diff --git a/simple-mind-map/src/themes/courseGreen.js b/simple-mind-map/src/themes/courseGreen.js index 78e50978..2d19cd1a 100644 --- a/simple-mind-map/src/themes/courseGreen.js +++ b/simple-mind-map/src/themes/courseGreen.js @@ -16,11 +16,7 @@ export default merge(defaultTheme, { color: '#fff', borderColor: '', borderWidth: 0, - fontSize: 24, - active: { - borderColor: 'rgb(173, 91, 12)', - borderWidth: 3 - } + fontSize: 24 }, // 二级节点样式 second: { @@ -28,18 +24,12 @@ export default merge(defaultTheme, { color: 'rgb(50, 113, 96)', borderColor: 'rgb(113, 195, 169)', borderWidth: 2, - fontSize: 18, - active: { - borderColor: 'rgb(173, 91, 12)' - } + fontSize: 18 }, // 三级及以下节点样式 node: { fontSize: 14, - color: 'rgb(10, 59, 43)', - active: { - borderColor: 'rgb(173, 91, 12)' - } + color: 'rgb(10, 59, 43)' }, // 概要节点样式 generalization: { @@ -47,9 +37,6 @@ export default merge(defaultTheme, { fillColor: 'rgb(246, 238, 211)', borderColor: '', borderWidth: 0, - color: 'rgb(173, 91, 12)', - active: { - borderColor: 'rgb(113, 195, 169)' - } + color: 'rgb(173, 91, 12)' } }) diff --git a/simple-mind-map/src/themes/dark.js b/simple-mind-map/src/themes/dark.js index 08350045..833f93c2 100644 --- a/simple-mind-map/src/themes/dark.js +++ b/simple-mind-map/src/themes/dark.js @@ -18,10 +18,7 @@ export default merge(defaultTheme, { fillColor: 'rgb(28, 178, 43)', color: '#fff', fontSize: 24, - borderRadius: 10, - active: { - borderColor: 'rgb(17, 68, 23)' - } + borderRadius: 10 }, // 二级节点样式 second: { @@ -29,26 +26,17 @@ export default merge(defaultTheme, { color: 'rgb(147,148,149)', fontSize: 18, borderRadius: 10, - borderWidth: 0, - active: { - borderColor: 'rgb(17, 68, 23)' - } + borderWidth: 0 }, // 三级及以下节点样式 node: { fontSize: 14, - color: 'rgb(147, 148, 149)', - active: { - borderColor: 'rgb(17, 68, 23)' - } + color: 'rgb(147, 148, 149)' }, // 概要节点样式 generalization: { fillColor: '#fff', borderColor: 'transparent', - color: '#333', - active: { - borderColor: 'rgb(17, 68, 23)' - } + color: '#333' } }) diff --git a/simple-mind-map/src/themes/dark2.js b/simple-mind-map/src/themes/dark2.js index a81de694..4a4e1c31 100644 --- a/simple-mind-map/src/themes/dark2.js +++ b/simple-mind-map/src/themes/dark2.js @@ -17,11 +17,7 @@ export default merge(defaultTheme, { fillColor: 'rgb(36, 179, 96)', color: '#fff', borderColor: '', - borderWidth: 0, - active: { - borderColor: 'rgb(254, 199, 13)', - borderWidth: 3 - } + borderWidth: 0 }, // 二级节点样式 second: { @@ -29,28 +25,18 @@ export default merge(defaultTheme, { color: 'rgb(0, 0, 0)', borderColor: '', borderWidth: 0, - fontSize: 14, - active: { - borderColor: 'rgb(36, 179, 96)', - borderWidth: 2 - } + fontSize: 14 }, // 三级及以下节点样式 node: { fontSize: 12, - color: 'rgb(204, 204, 204)', - active: { - borderColor: 'rgb(254, 199, 13)' - } + color: 'rgb(204, 204, 204)' }, // 概要节点样式 generalization: { fillColor: 'transparent', borderColor: 'rgb(255, 119, 34)', borderWidth: 2, - color: 'rgb(204, 204, 204)', - active: { - borderColor: 'rgb(254, 199, 13)' - } + color: 'rgb(204, 204, 204)' } }) diff --git a/simple-mind-map/src/themes/default.js b/simple-mind-map/src/themes/default.js index 6b94a3c8..addc497a 100644 --- a/simple-mind-map/src/themes/default.js +++ b/simple-mind-map/src/themes/default.js @@ -70,12 +70,7 @@ export default { borderWidth: 0, borderDasharray: 'none', borderRadius: 5, - textDecoration: 'none', - active: { - borderColor: 'rgb(57, 80, 96)', - borderWidth: 3, - borderDasharray: 'none' - } + textDecoration: 'none' }, // 二级节点样式 second: { @@ -93,12 +88,7 @@ export default { borderWidth: 1, borderDasharray: 'none', borderRadius: 5, - textDecoration: 'none', - active: { - borderColor: 'rgb(57, 80, 96)', - borderWidth: 3, - borderDasharray: 'none' - } + textDecoration: 'none' }, // 三级及以下节点样式 node: { @@ -116,12 +106,7 @@ export default { borderWidth: 0, borderRadius: 5, borderDasharray: 'none', - textDecoration: 'none', - active: { - borderColor: 'rgb(57, 80, 96)', - borderWidth: 3, - borderDasharray: 'none' - } + textDecoration: 'none' }, // 概要节点样式 generalization: { @@ -139,12 +124,7 @@ export default { borderWidth: 1, borderDasharray: 'none', borderRadius: 5, - textDecoration: 'none', - active: { - borderColor: 'rgb(57, 80, 96)', - borderWidth: 3, - borderDasharray: 'none' - } + textDecoration: 'none' } } diff --git a/simple-mind-map/src/themes/earthYellow.js b/simple-mind-map/src/themes/earthYellow.js index 9d375b4a..0196cf8b 100644 --- a/simple-mind-map/src/themes/earthYellow.js +++ b/simple-mind-map/src/themes/earthYellow.js @@ -13,10 +13,7 @@ export default merge(defaultTheme, { generalizationLineColor: '#333', // 根节点样式 root: { - fillColor: 'rgb(191, 147, 115)', - active: { - borderColor: 'rgb(96, 73, 57)' - } + fillColor: 'rgb(191, 147, 115)' }, // 二级节点样式 second: { @@ -24,26 +21,17 @@ export default merge(defaultTheme, { color: '#333', borderColor: 'rgb(191, 147, 115)', borderWidth: 1, - fontSize: 14, - active: { - borderColor: 'rgb(96, 73, 57)' - } + fontSize: 14 }, // 三级及以下节点样式 node: { fontSize: 12, - color: '#333', - active: { - borderColor: 'rgb(96, 73, 57)' - } + color: '#333' }, // 概要节点样式 generalization: { fillColor: '#fff', borderColor: '#333', - color: '#333', - active: { - borderColor: 'rgb(96, 73, 57)' - } + color: '#333' } }) diff --git a/simple-mind-map/src/themes/freshGreen.js b/simple-mind-map/src/themes/freshGreen.js index ab11006e..7c939639 100644 --- a/simple-mind-map/src/themes/freshGreen.js +++ b/simple-mind-map/src/themes/freshGreen.js @@ -26,11 +26,6 @@ export default merge(defaultTheme, { generalization: { fillColor: '#fff', borderColor: '#333', - color: '#333', - active: { - borderColor: 'rgb(57, 80, 96)', - borderWidth: 3, - borderDasharray: 'none' - } + color: '#333' } }) diff --git a/simple-mind-map/src/themes/freshRed.js b/simple-mind-map/src/themes/freshRed.js index 3032f4d0..583b7933 100644 --- a/simple-mind-map/src/themes/freshRed.js +++ b/simple-mind-map/src/themes/freshRed.js @@ -13,10 +13,7 @@ export default merge(defaultTheme, { generalizationLineColor: '#333', // 根节点样式 root: { - fillColor: 'rgb(191, 115, 115)', - active: { - borderColor: 'rgb(96, 57, 57)' - } + fillColor: 'rgb(191, 115, 115)' }, // 二级节点样式 second: { @@ -24,26 +21,17 @@ export default merge(defaultTheme, { color: '#333', borderColor: 'rgb(191, 115, 115)', borderWidth: 1, - fontSize: 14, - active: { - borderColor: 'rgb(96, 57, 57)' - } + fontSize: 14 }, // 三级及以下节点样式 node: { fontSize: 12, - color: '#333', - active: { - borderColor: 'rgb(96, 57, 57)' - } + color: '#333' }, // 概要节点样式 generalization: { fillColor: '#fff', borderColor: '#333', - color: '#333', - active: { - borderColor: 'rgb(96, 57, 57)' - } + color: '#333' } }) diff --git a/simple-mind-map/src/themes/gold.js b/simple-mind-map/src/themes/gold.js index 538c3c67..a524a691 100644 --- a/simple-mind-map/src/themes/gold.js +++ b/simple-mind-map/src/themes/gold.js @@ -17,11 +17,7 @@ export default merge(defaultTheme, { fillColor: 'rgb(51, 56, 62)', color: 'rgb(247, 208, 160)', borderColor: '', - borderWidth: 0, - active: { - borderColor: 'rgb(247, 208, 160)', - borderWidth: 3 - } + borderWidth: 0 }, // 二级节点样式 second: { @@ -29,27 +25,17 @@ export default merge(defaultTheme, { color: 'rgb(81, 58, 42)', borderColor: '', borderWidth: 0, - fontSize: 14, - active: { - borderColor: 'rgb(51, 56, 62)', - borderWidth: 2 - } + fontSize: 14 }, // 三级及以下节点样式 node: { fontSize: 12, - color: '#222', - active: { - borderColor: 'rgb(0, 192, 184)' - } + color: '#222' }, // 概要节点样式 generalization: { fillColor: 'rgb(127, 93, 64)', borderColor: 'transparent', - color: 'rgb(255, 214, 175)', - active: { - borderColor: 'rgb(51, 56, 62)' - } + color: 'rgb(255, 214, 175)' } }) diff --git a/simple-mind-map/src/themes/greenLeaf.js b/simple-mind-map/src/themes/greenLeaf.js index d0c4f642..9fd18a39 100644 --- a/simple-mind-map/src/themes/greenLeaf.js +++ b/simple-mind-map/src/themes/greenLeaf.js @@ -17,11 +17,7 @@ export default merge(defaultTheme, { fillColor: 'rgb(25, 193, 73)', color: '#fff', borderColor: '', - borderWidth: 0, - active: { - borderColor: '#222', - borderWidth: 3 - } + borderWidth: 0 }, // 二级节点样式 second: { @@ -29,28 +25,18 @@ export default merge(defaultTheme, { color: 'rgb(69, 149, 96)', borderColor: '', borderWidth: 0, - fontSize: 14, - active: { - borderColor: 'rgb(25, 193, 73)', - borderWidth: 2 - } + fontSize: 14 }, // 三级及以下节点样式 node: { fontSize: 12, - color: '#222', - active: { - borderColor: 'rgb(25, 193, 73)' - } + color: '#222' }, // 概要节点样式 generalization: { fillColor: '#fff', borderColor: 'rgb(251, 158, 0)', borderWidth: 2, - color: 'rgb(51, 51, 51)', - active: { - borderColor: 'rgb(25, 193, 73)' - } + color: 'rgb(51, 51, 51)' } }) diff --git a/simple-mind-map/src/themes/lateNightOffice.js b/simple-mind-map/src/themes/lateNightOffice.js index 34cec1a3..a80f4b69 100644 --- a/simple-mind-map/src/themes/lateNightOffice.js +++ b/simple-mind-map/src/themes/lateNightOffice.js @@ -18,11 +18,7 @@ export default merge(defaultTheme, { color: 'rgb(255, 255, 255)', borderColor: '', borderWidth: 0, - fontSize: 24, - active: { - borderColor: 'rgb(255, 119, 34)', - borderWidth: 3 - } + fontSize: 24 }, // 二级节点样式 second: { @@ -30,19 +26,12 @@ export default merge(defaultTheme, { color: 'rgb(209, 210, 210)', borderColor: '', borderWidth: 0, - fontSize: 18, - active: { - borderColor: 'rgb(255, 119, 34)', - borderWidth: 3 - } + fontSize: 18 }, // 三级及以下节点样式 node: { fontSize: 14, - color: 'rgb(204, 204, 204)', - active: { - borderColor: 'rgb(255, 119, 34)' - } + color: 'rgb(204, 204, 204)' }, // 概要节点样式 generalization: { @@ -50,9 +39,6 @@ export default merge(defaultTheme, { fillColor: 'rgb(255, 119, 34)', borderColor: '', borderWidth: 2, - color: '#fff', - active: { - borderColor: 'rgb(23, 153, 243)' - } + color: '#fff' } }) diff --git a/simple-mind-map/src/themes/minions.js b/simple-mind-map/src/themes/minions.js index 09ba44fd..b7f0d2f3 100644 --- a/simple-mind-map/src/themes/minions.js +++ b/simple-mind-map/src/themes/minions.js @@ -16,10 +16,7 @@ export default merge(defaultTheme, { root: { fillColor: 'rgb(55, 165, 255)', borderColor: 'rgb(51, 51, 51)', - borderWidth: 3, - active: { - borderColor: 'rgb(255, 160, 36)' - } + borderWidth: 3 }, // 二级节点样式 second: { @@ -27,26 +24,17 @@ export default merge(defaultTheme, { color: '#222', borderColor: 'rgb(51, 51, 51)', borderWidth: 3, - fontSize: 14, - active: { - borderColor: 'rgb(55, 165, 255)' - } + fontSize: 14 }, // 三级及以下节点样式 node: { fontSize: 12, - color: '#222', - active: { - borderColor: 'rgb(55, 165, 255)' - } + color: '#222' }, // 概要节点样式 generalization: { borderColor: '#222', borderWidth: 3, - color: '#222', - active: { - borderColor: 'rgb(55, 165, 255)' - } + color: '#222' } }) diff --git a/simple-mind-map/src/themes/mint.js b/simple-mind-map/src/themes/mint.js index 19e22104..336a67c0 100644 --- a/simple-mind-map/src/themes/mint.js +++ b/simple-mind-map/src/themes/mint.js @@ -16,11 +16,7 @@ export default merge(defaultTheme, { root: { fillColor: 'rgb(0, 192, 184)', borderColor: '', - borderWidth: 0, - active: { - borderColor: 'rgb(255, 160, 36)', - borderWidth: 3 - } + borderWidth: 0 }, // 二级节点样式 second: { @@ -28,26 +24,17 @@ export default merge(defaultTheme, { color: '#222', borderColor: 'rgb(184, 235, 233)', borderWidth: 2, - fontSize: 14, - active: { - borderColor: 'rgb(0, 192, 184)' - } + fontSize: 14 }, // 三级及以下节点样式 node: { fontSize: 12, - color: '#222', - active: { - borderColor: 'rgb(0, 192, 184)' - } + color: '#222' }, // 概要节点样式 generalization: { fillColor: 'rgb(90, 206, 241)', borderColor: 'transparent', - color: '#fff', - active: { - borderColor: 'rgb(0, 192, 184)' - } + color: '#fff' } }) diff --git a/simple-mind-map/src/themes/orangeJuice.js b/simple-mind-map/src/themes/orangeJuice.js index 419684e3..0e2c47f6 100644 --- a/simple-mind-map/src/themes/orangeJuice.js +++ b/simple-mind-map/src/themes/orangeJuice.js @@ -18,11 +18,7 @@ export default merge(defaultTheme, { color: '#110501', borderColor: '#ff6811', borderWidth: 0, - fontSize: 24, - active: { - borderColor: '#a9a4a9', - borderWidth: 3 - } + fontSize: 24 }, // 二级节点样式 second: { @@ -30,18 +26,12 @@ export default merge(defaultTheme, { color: '#a9a4a9', borderColor: '#ff6811', borderWidth: 2, - fontSize: 18, - active: { - borderColor: '#110501' - } + fontSize: 18 }, // 三级及以下节点样式 node: { fontSize: 14, - color: '#a9a4a9', - active: { - borderColor: '#ff6811' - } + color: '#a9a4a9' }, // 概要节点样式 generalization: { @@ -49,9 +39,6 @@ export default merge(defaultTheme, { fillColor: '', borderColor: '#ff6811', borderWidth: 2, - color: '#a9a4a9', - active: { - borderColor: '#110501' - } + color: '#a9a4a9' } }) diff --git a/simple-mind-map/src/themes/pinkGrape.js b/simple-mind-map/src/themes/pinkGrape.js index ca9b1c11..c867c740 100644 --- a/simple-mind-map/src/themes/pinkGrape.js +++ b/simple-mind-map/src/themes/pinkGrape.js @@ -16,11 +16,7 @@ export default merge(defaultTheme, { root: { fillColor: 'rgb(139, 109, 225)', borderColor: '', - borderWidth: 0, - active: { - borderColor: 'rgb(243, 104, 138)', - borderWidth: 2 - } + borderWidth: 0 }, // 二级节点样式 second: { @@ -28,28 +24,17 @@ export default merge(defaultTheme, { color: '#fff', borderColor: '', borderWidth: 0, - fontSize: 14, - active: { - borderColor: 'rgb(139, 109, 225)', - borderWidth: 2 - } + fontSize: 14 }, // 三级及以下节点样式 node: { fontSize: 12, - color: '#222', - active: { - borderColor: 'rgb(139, 109, 225)' - } + color: '#222' }, // 概要节点样式 generalization: { fillColor: '#fff', borderColor: 'transparent', - color: '#222', - active: { - borderColor: 'rgb(139, 109, 225)', - borderWidth: 2 - } + color: '#222' } }) diff --git a/simple-mind-map/src/themes/redSpirit.js b/simple-mind-map/src/themes/redSpirit.js index 0ee103c3..89dbca7c 100644 --- a/simple-mind-map/src/themes/redSpirit.js +++ b/simple-mind-map/src/themes/redSpirit.js @@ -18,11 +18,7 @@ export default merge(defaultTheme, { color: 'rgb(255, 233, 157)', borderColor: '', borderWidth: 0, - fontSize: 24, - active: { - borderColor: 'rgb(255, 233, 157)', - borderWidth: 3 - } + fontSize: 24 }, // 二级节点样式 second: { @@ -30,18 +26,12 @@ export default merge(defaultTheme, { color: 'rgb(211, 58, 21)', borderColor: 'rgb(222, 101, 85)', borderWidth: 2, - fontSize: 18, - active: { - borderColor: 'rgb(255, 233, 157)' - } + fontSize: 18 }, // 三级及以下节点样式 node: { fontSize: 14, - color: 'rgb(144, 71, 43)', - active: { - borderColor: 'rgb(255, 233, 157)' - } + color: 'rgb(144, 71, 43)' }, // 概要节点样式 generalization: { @@ -49,9 +39,6 @@ export default merge(defaultTheme, { fillColor: 'rgb(255, 247, 211)', borderColor: 'rgb(255, 202, 162)', borderWidth: 2, - color: 'rgb(187, 101, 69)', - active: { - borderColor: 'rgb(222, 101, 85)' - } + color: 'rgb(187, 101, 69)' } }) diff --git a/simple-mind-map/src/themes/romanticPurple.js b/simple-mind-map/src/themes/romanticPurple.js index fc27a18e..77fac345 100644 --- a/simple-mind-map/src/themes/romanticPurple.js +++ b/simple-mind-map/src/themes/romanticPurple.js @@ -13,10 +13,7 @@ export default merge(defaultTheme, { generalizationLineColor: '#333', // 根节点样式 root: { - fillColor: 'rgb(123, 115, 191)', - active: { - borderColor: 'rgb(61, 57, 96)' - } + fillColor: 'rgb(123, 115, 191)' }, // 二级节点样式 second: { @@ -24,26 +21,17 @@ export default merge(defaultTheme, { color: '#333', borderColor: 'rgb(123, 115, 191)', borderWidth: 1, - fontSize: 14, - active: { - borderColor: 'rgb(61, 57, 96)' - } + fontSize: 14 }, // 三级及以下节点样式 node: { fontSize: 12, - color: '#333', - active: { - borderColor: 'rgb(61, 57, 96)' - } + color: '#333' }, // 概要节点样式 generalization: { fillColor: '#fff', borderColor: '#333', - color: '#333', - active: { - borderColor: 'rgb(61, 57, 96)' - } + color: '#333' } }) diff --git a/simple-mind-map/src/themes/simpleBlack.js b/simple-mind-map/src/themes/simpleBlack.js index 6a04c827..31360896 100644 --- a/simple-mind-map/src/themes/simpleBlack.js +++ b/simple-mind-map/src/themes/simpleBlack.js @@ -16,10 +16,7 @@ export default merge(defaultTheme, { color: 'rgb(34, 34, 34)', borderColor: 'rgb(34, 34, 34)', borderWidth: 3, - fontSize: 24, - active: { - borderColor: '#a13600' - } + fontSize: 24 }, // 二级节点样式 second: { @@ -27,18 +24,12 @@ export default merge(defaultTheme, { color: 'rgb(34, 34, 34)', borderColor: 'rgb(34, 34, 34)', borderWidth: 3, - fontSize: 18, - active: { - borderColor: '#a13600' - } + fontSize: 18 }, // 三级及以下节点样式 node: { fontSize: 14, - color: 'rgb(34, 34, 34)', - active: { - borderColor: '#a13600' - } + color: 'rgb(34, 34, 34)' }, // 概要节点样式 generalization: { @@ -46,9 +37,6 @@ export default merge(defaultTheme, { fillColor: 'transparent', borderColor: 'rgb(34, 34, 34)', borderWidth: 2, - color: 'rgb(34, 34, 34)', - active: { - borderColor: '#a13600' - } + color: 'rgb(34, 34, 34)' } }) diff --git a/simple-mind-map/src/themes/skyGreen.js b/simple-mind-map/src/themes/skyGreen.js index d0a92f29..1193c397 100644 --- a/simple-mind-map/src/themes/skyGreen.js +++ b/simple-mind-map/src/themes/skyGreen.js @@ -17,11 +17,7 @@ export default merge(defaultTheme, { fillColor: '#fff', borderColor: '', borderWidth: 0, - color: 'rgb(65, 89, 158)', - active: { - borderColor: 'rgb(251, 227, 188)', - borderWidth: 3 - } + color: 'rgb(65, 89, 158)' }, // 二级节点样式 second: { @@ -29,27 +25,17 @@ export default merge(defaultTheme, { color: 'rgb(65, 89, 158)', borderColor: '', borderWidth: 0, - fontSize: 14, - active: { - borderColor: '#fff', - borderWidth: 2 - } + fontSize: 14 }, // 三级及以下节点样式 node: { fontSize: 12, - color: 'rgb(65, 89, 158)', - active: { - borderColor: 'rgb(251, 227, 188)' - } + color: 'rgb(65, 89, 158)' }, // 概要节点样式 generalization: { fillColor: '#fff', borderColor: 'transparent', - color: 'rgb(65, 89, 158)', - active: { - borderColor: 'rgb(251, 227, 188)' - } + color: 'rgb(65, 89, 158)' } }) diff --git a/simple-mind-map/src/themes/vitalityOrange.js b/simple-mind-map/src/themes/vitalityOrange.js index f3f95c0c..a35a32fe 100644 --- a/simple-mind-map/src/themes/vitalityOrange.js +++ b/simple-mind-map/src/themes/vitalityOrange.js @@ -17,11 +17,7 @@ export default merge(defaultTheme, { fillColor: 'rgb(255, 112, 52)', color: '#fff', borderColor: '', - borderWidth: 0, - active: { - borderColor: 'rgb(51, 51, 51)', - borderWidth: 3 - } + borderWidth: 0 }, // 二级节点样式 second: { @@ -29,27 +25,17 @@ export default merge(defaultTheme, { color: 'rgb(51, 51, 51)', borderColor: '', borderWidth: 0, - fontSize: 14, - active: { - borderColor: 'rgb(255, 112, 52)', - borderWidth: 2 - } + fontSize: 14 }, // 三级及以下节点样式 node: { fontSize: 12, - color: '#222', - active: { - borderColor: 'rgb(255, 112, 52)' - } + color: '#222' }, // 概要节点样式 generalization: { fillColor: 'rgb(255, 222, 69)', borderColor: 'transparent', - color: 'rgb(51, 51, 51)', - active: { - borderColor: 'rgb(255, 112, 52)' - } + color: 'rgb(51, 51, 51)' } }) diff --git a/web/src/customThemes/darkNightLceBlade.js b/web/src/customThemes/darkNightLceBlade.js index 5345d914..12c2d2d5 100644 --- a/web/src/customThemes/darkNightLceBlade.js +++ b/web/src/customThemes/darkNightLceBlade.js @@ -19,10 +19,7 @@ export default { borderColor: '#fff', borderWidth: 3, fontSize: 24, - shape: 'parallelogram', - active: { - borderColor: 'rgba(2, 167, 240, 0.5)', - } + shape: 'parallelogram' }, // 二级节点样式 second: { @@ -31,18 +28,12 @@ export default { borderColor: '#fff', borderWidth: 3, fontSize: 18, - shape: 'diamond', - active: { - borderColor: 'rgba(2, 167, 240, 0.5)', - } + shape: 'diamond' }, // 三级及以下节点样式 node: { fontSize: 14, - color: '#fff', - active: { - borderColor: 'rgba(2, 167, 240, 0.5)' - } + color: '#fff' }, // 概要节点样式 generalization: { @@ -50,10 +41,7 @@ export default { fillColor: '#fff', borderColor: 'rgb(0, 117, 255)', borderWidth: 2, - color: 'rgb(0, 21, 21)', - active: { - borderColor: 'rgb(0, 243, 255)' - } + color: 'rgb(0, 21, 21)' } } \ No newline at end of file diff --git a/web/src/customThemes/lemonBubbles.js b/web/src/customThemes/lemonBubbles.js index 15470539..4cfe0604 100644 --- a/web/src/customThemes/lemonBubbles.js +++ b/web/src/customThemes/lemonBubbles.js @@ -15,10 +15,7 @@ export default { borderColor: 'rgb(26, 26, 26)', borderWidth: 3, fontSize: 24, - shape: 'roundedRectangle', - active: { - borderColor: 'rgb(235, 255, 187)', - } + shape: 'roundedRectangle' }, // 二级节点样式 second: { @@ -27,18 +24,12 @@ export default { borderColor: 'rgb(51, 51, 51)', borderWidth: 3, fontSize: 18, - shape: 'roundedRectangle', - active: { - borderColor: 'rgb(39, 222, 232)', - } + shape: 'roundedRectangle' }, // 三级及以下节点样式 node: { fontSize: 14, - color: 'rgb(0, 0, 0)', - active: { - borderColor: 'rgb(39, 222, 232)' - } + color: 'rgb(0, 0, 0)' }, // 概要节点样式 generalization: { @@ -46,10 +37,7 @@ export default { fillColor: '#fff', borderColor: 'rgb(26, 26, 26)', borderWidth: 2, - color: 'rgb(26, 26, 26)', - active: { - borderColor: 'rgb(39, 222, 232)' - } + color: 'rgb(26, 26, 26)' } } \ No newline at end of file diff --git a/web/src/customThemes/morandi.js b/web/src/customThemes/morandi.js index dc106f68..d00fb172 100644 --- a/web/src/customThemes/morandi.js +++ b/web/src/customThemes/morandi.js @@ -19,10 +19,7 @@ export default { borderColor: 'rgb(207, 121, 105)', borderWidth: 3, fontSize: 24, - shape: 'roundedRectangle', - active: { - borderColor: 'rgb(172, 202, 199)', - } + shape: 'roundedRectangle' }, // 二级节点样式 second: { @@ -31,18 +28,12 @@ export default { borderColor: 'rgb(222, 186, 183)', borderWidth: 3, fontSize: 18, - shape: 'roundedRectangle', - active: { - borderColor: 'rgb(172, 202, 199)', - } + shape: 'roundedRectangle' }, // 三级及以下节点样式 node: { fontSize: 14, - color: 'rgb(131, 90, 64)', - active: { - borderColor: 'rgb(172, 202, 199)' - } + color: 'rgb(131, 90, 64)' }, // 概要节点样式 generalization: { @@ -50,10 +41,7 @@ export default { fillColor: 'rgb(172, 202, 199)', borderColor: 'rgb(172, 202, 199)', borderWidth: 2, - color: 'rgb(91, 102, 97)', - active: { - borderColor: 'rgb(207, 121, 105)' - } + color: 'rgb(91, 102, 97)' } } \ No newline at end of file diff --git a/web/src/customThemes/neonLamp.js b/web/src/customThemes/neonLamp.js index 104e04f0..6fe9e346 100644 --- a/web/src/customThemes/neonLamp.js +++ b/web/src/customThemes/neonLamp.js @@ -19,10 +19,7 @@ export default { borderColor: 'rgb(255, 0, 214)', borderWidth: 3, fontSize: 24, - shape: 'roundedRectangle', - active: { - borderColor: 'rgb(255, 181, 0)', - } + shape: 'roundedRectangle' }, // 二级节点样式 second: { @@ -30,18 +27,12 @@ export default { color: 'rgb(248, 177, 237)', borderColor: '', borderWidth: 3, - fontSize: 18, - active: { - borderColor: 'rgb(255, 181, 0)', - } + fontSize: 18 }, // 三级及以下节点样式 node: { fontSize: 14, - color: '#fff', - active: { - borderColor: 'rgb(255, 181, 0)' - } + color: '#fff' }, // 概要节点样式 generalization: { @@ -49,10 +40,7 @@ export default { fillColor: '#fff', borderColor: 'rgb(255, 181, 0)', borderWidth: 2, - color: 'rgb(17, 17, 84)', - active: { - borderColor: 'rgb(255, 0, 214)' - } + color: 'rgb(17, 17, 84)' } } \ No newline at end of file diff --git a/web/src/customThemes/oreo.js b/web/src/customThemes/oreo.js index dfa58cff..f0ee01b4 100644 --- a/web/src/customThemes/oreo.js +++ b/web/src/customThemes/oreo.js @@ -13,10 +13,7 @@ export default { color: '#fff', borderColor: 'rgb(22, 22, 22)', borderWidth: 3, - fontSize: 24, - active: { - borderColor: '#a13600', - } + fontSize: 24 }, // 二级节点样式 second: { @@ -25,18 +22,12 @@ export default { borderColor: '', borderWidth: 3, fontSize: 18, - shape: 'roundedRectangle', - active: { - borderColor: 'rgb(22, 22, 22)', - } + shape: 'roundedRectangle' }, // 三级及以下节点样式 node: { fontSize: 14, - color: 'rgb(0, 0, 0)', - active: { - borderColor: 'rgb(22, 22, 22)' - } + color: 'rgb(0, 0, 0)' }, // 概要节点样式 generalization: { @@ -44,9 +35,6 @@ export default { fillColor: 'transparent', borderColor: 'rgb(34, 34, 34)', borderWidth: 2, - color: 'rgb(34, 34, 34)', - active: { - borderColor: '#a13600' - } + color: 'rgb(34, 34, 34)' } } diff --git a/web/src/customThemes/rose.js b/web/src/customThemes/rose.js index c81368b8..ea499054 100644 --- a/web/src/customThemes/rose.js +++ b/web/src/customThemes/rose.js @@ -15,10 +15,7 @@ export default { borderColor: 'rgb(18, 187, 55)', borderWidth: 3, fontSize: 24, - shape: 'roundedRectangle', - active: { - borderColor: 'rgb(136, 100, 0)', - } + shape: 'roundedRectangle' }, // 二级节点样式 second: { @@ -27,18 +24,12 @@ export default { borderColor: '', borderWidth: 3, fontSize: 18, - shape: 'roundedRectangle', - active: { - borderColor: 'rgb(254, 92, 92)', - } + shape: 'roundedRectangle' }, // 三级及以下节点样式 node: { fontSize: 14, - color: 'rgb(26, 26, 26)', - active: { - borderColor: 'rgb(209, 237, 176)' - } + color: 'rgb(26, 26, 26)' }, // 概要节点样式 generalization: { @@ -46,10 +37,7 @@ export default { fillColor: '#fff', borderColor: 'rgb(136, 100, 0)', borderWidth: 2, - color: 'rgb(136, 100, 0)', - active: { - borderColor: 'rgb(254, 92, 92)' - } + color: 'rgb(136, 100, 0)' } } \ No newline at end of file diff --git a/web/src/customThemes/seaBlueLine.js b/web/src/customThemes/seaBlueLine.js index ffaba245..e54ac1ee 100644 --- a/web/src/customThemes/seaBlueLine.js +++ b/web/src/customThemes/seaBlueLine.js @@ -15,10 +15,7 @@ export default { borderColor: '#fff', borderWidth: 3, fontSize: 24, - shape: 'roundedRectangle', - active: { - borderColor: 'rgb(0, 155, 255)', - } + shape: 'roundedRectangle' }, // 二级节点样式 second: { @@ -27,18 +24,12 @@ export default { borderColor: '', borderWidth: 3, fontSize: 18, - shape: 'roundedRectangle', - active: { - borderColor: 'rgb(96, 189, 255)', - } + shape: 'roundedRectangle' }, // 三级及以下节点样式 node: { fontSize: 14, - color: 'rgb(0, 66, 157)', - active: { - borderColor: 'rgb(96, 189, 255)' - } + color: 'rgb(0, 66, 157)' }, // 概要节点样式 generalization: { @@ -46,10 +37,7 @@ export default { fillColor: '#fff', borderColor: 'rgb(0, 155, 255)', borderWidth: 2, - color: 'rgb(0, 155, 255)', - active: { - borderColor: 'rgba(2, 167, 240, 0.5)' - } + color: 'rgb(0, 155, 255)' } } \ No newline at end of file diff --git a/web/src/customThemes/shallowSea.js b/web/src/customThemes/shallowSea.js index 92f6d160..0374bb3b 100644 --- a/web/src/customThemes/shallowSea.js +++ b/web/src/customThemes/shallowSea.js @@ -15,10 +15,7 @@ export default { borderColor: 'rgb(51, 149, 255)', borderWidth: 3, fontSize: 24, - shape: 'roundedRectangle', - active: { - borderColor: 'rgb(255, 168, 101)', - } + shape: 'roundedRectangle' }, // 二级节点样式 second: { @@ -26,18 +23,12 @@ export default { color: '#fff', borderColor: '', borderWidth: 3, - fontSize: 18, - active: { - borderColor: 'rgb(255, 168, 101)', - } + fontSize: 18 }, // 三级及以下节点样式 node: { fontSize: 14, - color: 'rgb(0, 0, 0)', - active: { - borderColor: 'rgb(255, 168, 101)' - } + color: 'rgb(0, 0, 0)' }, // 概要节点样式 generalization: { @@ -45,10 +36,7 @@ export default { fillColor: '#fff', borderColor: 'rgb(255, 168, 101)', borderWidth: 2, - color: '#000', - active: { - borderColor: 'rgb(51, 149, 255)' - } + color: '#000' } } \ No newline at end of file From 5997c98b8fdc07e92d7cfe174c53dcc401c58ad3 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Mon, 28 Aug 2023 09:33:29 +0800 Subject: [PATCH 09/38] =?UTF-8?q?Demo=EF=BC=9A=E5=88=A0=E9=99=A4=E4=BE=A7?= =?UTF-8?q?=E8=BE=B9=E6=A0=8F=E8=8A=82=E7=82=B9=E6=A0=B7=E5=BC=8F=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E9=83=A8=E5=88=86=E7=9A=84=E6=BF=80=E6=B4=BB=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/pages/Edit/components/Style.vue | 131 +++++++----------------- 1 file changed, 35 insertions(+), 96 deletions(-) diff --git a/web/src/pages/Edit/components/Style.vue b/web/src/pages/Edit/components/Style.vue index 6bce2364..e20b2f7f 100644 --- a/web/src/pages/Edit/components/Style.vue +++ b/web/src/pages/Edit/components/Style.vue @@ -5,10 +5,6 @@ :class="{ isDark: isDark }" v-if="activeNodes.length > 0" > - - - -
{{ $t('style.text') }}
@@ -19,7 +15,6 @@ size="mini" v-model="style.fontFamily" placeholder="" - :disabled="checkDisabled('fontFamily')" @change="update('fontFamily')" >
-
+
A @@ -105,8 +93,7 @@
@@ -120,27 +107,16 @@
U
- + - +
- + @@ -223,7 +198,6 @@ style="width: 80px" v-model="style.borderWidth" placeholder="" - :disabled="checkDisabled('borderWidth')" @change="update('borderWidth')" >
-
+
{{ $t('style.borderRadius') }} - +
@@ -292,7 +259,6 @@ style="width: 120px" v-model="style.shape" placeholder="" - :disabled="checkDisabled('shape')" @change="update('shape')" > @@ -322,14 +294,8 @@ class="block" v-popover:popover5 :style="{ width: '80px', backgroundColor: style.lineColor }" - :class="{ disabled: checkDisabled('lineColor') }" > - +
@@ -340,7 +306,6 @@ style="width: 80px" v-model="style.lineDasharray" placeholder="" - :disabled="checkDisabled('lineDasharray')" @change="update('lineDasharray')" > @@ -372,7 +343,6 @@ style="width: 80px" v-model="style.lineWidth" placeholder="" - :disabled="checkDisabled('lineWidth')" @change="update('lineWidth')" >
@@ -410,7 +379,6 @@
@@ -437,7 +405,6 @@ import { shapeList, shapeListMap } from '@/config' -import { supportActiveStyle } from 'simple-mind-map/src/themes/default' import { mapState } from 'vuex' /** @@ -453,13 +420,11 @@ export default { }, data() { return { - supportActiveStyle, fontSizeList, borderWidthList, borderRadiusList, lineHeightList, activeNodes: [], - activeTab: 'normal', style: { shape: '', paddingX: 0, @@ -496,7 +461,7 @@ export default { }, shapeListMap() { return shapeListMap[this.$i18n.locale] || shapeListMap.zh - }, + } }, watch: { activeSidebar(val) { @@ -521,32 +486,11 @@ export default { */ onNodeActive(...args) { this.$nextTick(() => { - this.activeTab = 'normal' this.activeNodes = args[1] this.initNodeStyle() }) }, - - /** - * @Author: 王林 - * @Date: 2021-05-05 11:42:32 - * @Desc: tab切换 - */ - handleTabClick() { - this.initNodeStyle() - }, - - /** - * @Author: 王林 - * @Date: 2022-09-12 22:16:56 - * @Desc: 检查是否禁用 - */ - checkDisabled(prop) { - return ( - this.activeTab === 'active' && !this.supportActiveStyle.includes(prop) - ) - }, - + /** * @Author: 王林 * @Date: 2021-05-05 09:48:52 @@ -554,7 +498,6 @@ export default { */ initNodeStyle() { if (this.activeNodes.length <= 0) { - this.activeTab = 'normal' return } ;[ @@ -577,11 +520,7 @@ export default { 'lineDasharray', 'lineWidth' ].forEach(item => { - this.style[item] = this.activeNodes[0].getStyle( - item, - false, - this.activeTab === 'active' - ) + this.style[item] = this.activeNodes[0].getStyle(item, false) }) }, @@ -592,7 +531,7 @@ export default { */ update(prop) { this.activeNodes.forEach(node => { - node.setStyle(prop, this.style[prop], this.activeTab === 'active') + node.setStyle(prop, this.style[prop]) }) }, @@ -829,4 +768,4 @@ export default { background-color: #409eff; } } - \ No newline at end of file + From 5d133f74cfdc0c9dac1857986586ae21a6cf31a5 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Mon, 28 Aug 2023 09:41:08 +0800 Subject: [PATCH 10/38] =?UTF-8?q?Feat=EF=BC=9A=E5=88=A0=E9=99=A4=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E8=8A=82=E7=82=B9=E6=BF=80=E6=B4=BB=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E7=9A=84=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/core/render/Render.js | 30 ++++--------------- .../src/core/render/node/nodeCommandWraps.js | 8 ++--- .../core/render/node/nodeCreateContents.js | 8 ++--- 3 files changed, 11 insertions(+), 35 deletions(-) diff --git a/simple-mind-map/src/core/render/Render.js b/simple-mind-map/src/core/render/Render.js index b7bb3ba1..3a274fa5 100644 --- a/simple-mind-map/src/core/render/Render.js +++ b/simple-mind-map/src/core/render/Render.js @@ -921,19 +921,9 @@ class Render { } // 设置节点样式 - setNodeStyle(node, prop, value, isActive) { - let data = {} - if (isActive) { - data = { - activeStyle: { - ...(node.nodeData.data.activeStyle || {}), - [prop]: value - } - } - } else { - data = { - [prop]: value - } + setNodeStyle(node, prop, value) { + let data = { + [prop]: value } // 如果开启了富文本,则需要应用到富文本上 if (this.mindMap.richText) { @@ -954,18 +944,8 @@ class Render { } // 设置节点多个样式 - setNodeStyles(node, style, isActive) { - let data = {} - if (isActive) { - data = { - activeStyle: { - ...(node.nodeData.data.activeStyle || {}), - ...style - } - } - } else { - data = style - } + setNodeStyles(node, style) { + let data = { ...style } // 如果开启了富文本,则需要应用到富文本上 if (this.mindMap.richText) { let config = this.mindMap.richText.normalStyleToRichTextStyle(style) diff --git a/simple-mind-map/src/core/render/node/nodeCommandWraps.js b/simple-mind-map/src/core/render/node/nodeCommandWraps.js index 8745fa7f..1a51130e 100644 --- a/simple-mind-map/src/core/render/node/nodeCommandWraps.js +++ b/simple-mind-map/src/core/render/node/nodeCommandWraps.js @@ -39,13 +39,13 @@ function setShape(shape) { } // 修改某个样式 -function setStyle(prop, value, isActive) { - this.mindMap.execCommand('SET_NODE_STYLE', this, prop, value, isActive) +function setStyle(prop, value) { + this.mindMap.execCommand('SET_NODE_STYLE', this, prop, value) } // 修改多个样式 -function setStyles(style, isActive) { - this.mindMap.execCommand('SET_NODE_STYLES', this, style, isActive) +function setStyles(style) { + this.mindMap.execCommand('SET_NODE_STYLES', this, style) } export default { diff --git a/simple-mind-map/src/core/render/node/nodeCreateContents.js b/simple-mind-map/src/core/render/node/nodeCreateContents.js index d18d8ee0..9ff93cf5 100644 --- a/simple-mind-map/src/core/render/node/nodeCreateContents.js +++ b/simple-mind-map/src/core/render/node/nodeCreateContents.js @@ -160,12 +160,8 @@ function createTextNode() { return this.createRichTextNode() } let g = new G() - let fontSize = this.getStyle('fontSize', false, this.nodeData.data.isActive) - let lineHeight = this.getStyle( - 'lineHeight', - false, - this.nodeData.data.isActive - ) + let fontSize = this.getStyle('fontSize', false) + let lineHeight = this.getStyle('lineHeight', false) // 文本超长自动换行 let textStyle = this.style.getTextFontStyle() let textArr = this.nodeData.data.text.split(/\n/gim) From c65393d1bc0b58bd2922b4f7a13bb0a0b8dc45f7 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Mon, 28 Aug 2023 09:52:46 +0800 Subject: [PATCH 11/38] =?UTF-8?q?Fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E5=9B=BE=E7=89=87=E5=92=8Csvg=E6=97=B6=E8=8A=82?= =?UTF-8?q?=E7=82=B9hover=E7=9F=A9=E5=BD=A2=E9=BB=98=E8=AE=A4=E4=BC=9A?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/simple-mind-map/index.js b/simple-mind-map/index.js index 44ad317f..71075b36 100644 --- a/simple-mind-map/index.js +++ b/simple-mind-map/index.js @@ -106,7 +106,7 @@ class MindMap { return opt } - // 添加css到页面 + // 添加必要的css样式到页面 addCss() { this.cssEl = document.createElement('style') this.cssEl.type = 'text/css' @@ -357,6 +357,8 @@ class MindMap { draw.translate(-rect.x + elRect.left, -rect.y + elRect.top) // 克隆一份数据 let clone = svg.clone() + // 添加必要的样式 + clone.add(SVG(``)) // 如果实际图形宽高超出了屏幕宽高,且存在水印的话需要重新绘制水印,否则会出现超出部分没有水印的问题 if ( (rect.width > origWidth || rect.height > origHeight) && From d99895b8457055dae0c8b7bccffc1551eae4bb1a Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Mon, 28 Aug 2023 13:47:58 +0800 Subject: [PATCH 12/38] =?UTF-8?q?Fix=EF=BC=9A=E4=BF=AE=E6=94=B9=E8=8A=82?= =?UTF-8?q?=E7=82=B9hover=E5=92=8C=E6=BF=80=E6=B4=BB=E7=9F=A9=E5=BD=A2?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8C=E4=BF=AE=E5=A4=8D=E8=BF=9E=E7=BA=BF?= =?UTF-8?q?=E5=92=8C=E8=8A=82=E7=82=B9=E4=B8=8D=E9=87=8D=E5=90=88=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/core/render/node/Node.js | 15 ++++++++------- simple-mind-map/src/core/render/node/Shape.js | 5 ++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/simple-mind-map/src/core/render/node/Node.js b/simple-mind-map/src/core/render/node/Node.js index b19b7b51..cc948ad7 100644 --- a/simple-mind-map/src/core/render/node/Node.js +++ b/simple-mind-map/src/core/render/node/Node.js @@ -260,11 +260,9 @@ class Node { this.shapePadding.paddingY = shapePaddingY // 边框宽度,因为边框是以中线向两端发散,所以边框会超出节点 const borderWidth = this.getBorderWidth() - const { hoverRectPadding } = this.mindMap.opt return { - width: _width + paddingX * 2 + shapePaddingX * 2 + borderWidth + hoverRectPadding * 2, - height: - _height + paddingY * 2 + margin + shapePaddingY * 2 + borderWidth + hoverRectPadding * 2 + width: _width + paddingX * 2 + shapePaddingX * 2 + borderWidth, + height: _height + paddingY * 2 + margin + shapePaddingY * 2 + borderWidth } } @@ -276,11 +274,11 @@ class Node { let { width, height, textContentItemMargin } = this let { paddingY } = this.getPaddingVale() const halfBorderWidth = this.getBorderWidth() / 2 - paddingY += this.shapePadding.paddingY + halfBorderWidth + hoverRectPadding + paddingY += this.shapePadding.paddingY + halfBorderWidth // 节点形状 this.shapeNode = this.shapeInstance.createShape() this.shapeNode.addClass('smm-node-shape') - this.shapeNode.translate(halfBorderWidth + hoverRectPadding, halfBorderWidth + hoverRectPadding) + this.shapeNode.translate(halfBorderWidth, halfBorderWidth) this.style.shape(this.shapeNode) this.group.add(this.shapeNode) // 渲染一个隐藏的矩形区域,用来触发展开收起按钮的显示 @@ -370,7 +368,10 @@ class Node { ) this.group.add(textContentNested) // 激活hover和激活边框 - this.hoverNode = new Rect().size(width, height).x(0).y(0) + this.hoverNode = new Rect() + .size(width + hoverRectPadding * 2, height + hoverRectPadding * 2) + .x(-hoverRectPadding) + .y(-hoverRectPadding) this.hoverNode.addClass('smm-hover-node') this.style.hoverNode(this.hoverNode, width, height) this.group.add(this.hoverNode) diff --git a/simple-mind-map/src/core/render/node/Shape.js b/simple-mind-map/src/core/render/node/Shape.js index ef85c3b0..380a8447 100644 --- a/simple-mind-map/src/core/render/node/Shape.js +++ b/simple-mind-map/src/core/render/node/Shape.js @@ -98,9 +98,8 @@ export default class Shape { getNodeSize() { const borderWidth = this.node.getBorderWidth() let { width, height } = this.node - const { hoverRectPadding } = this.node.mindMap.opt - width -= borderWidth + hoverRectPadding * 2 - height -= borderWidth + hoverRectPadding * 2 + width -= borderWidth + height -= borderWidth return { width, height From c533459da12902fde3baf169ee2dcee0776ef11b Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Mon, 28 Aug 2023 13:54:37 +0800 Subject: [PATCH 13/38] =?UTF-8?q?Fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E8=8A=82=E7=82=B9=E5=86=85=E5=AE=B9=E6=97=B6?= =?UTF-8?q?hover=E5=92=8C=E6=BF=80=E6=B4=BB=E6=95=88=E6=9E=9C=E4=B8=8D?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/core/render/node/Node.js | 22 ++++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/simple-mind-map/src/core/render/node/Node.js b/simple-mind-map/src/core/render/node/Node.js index cc948ad7..016d3c7f 100644 --- a/simple-mind-map/src/core/render/node/Node.js +++ b/simple-mind-map/src/core/render/node/Node.js @@ -287,13 +287,24 @@ class Node { if (this.isGeneralization && this.generalizationBelongNode) { this.group.addClass('generalization_' + this.generalizationBelongNode.uid) } + // 激活hover和激活边框 + const addHoverNode = () => { + this.hoverNode = new Rect() + .size(width + hoverRectPadding * 2, height + hoverRectPadding * 2) + .x(-hoverRectPadding) + .y(-hoverRectPadding) + this.hoverNode.addClass('smm-hover-node') + this.style.hoverNode(this.hoverNode, width, height) + this.group.add(this.hoverNode) + } // 如果存在自定义节点内容,那么使用自定义节点内容 if (this.isUseCustomNodeContent()) { let foreignObject = new ForeignObject() foreignObject.width(width) foreignObject.height(height) - foreignObject.add(SVG(this._customNodeContent)) + foreignObject.add(this._customNodeContent) this.group.add(foreignObject) + addHoverNode() return } // 图片节点 @@ -367,14 +378,7 @@ class Node { : 0) ) this.group.add(textContentNested) - // 激活hover和激活边框 - this.hoverNode = new Rect() - .size(width + hoverRectPadding * 2, height + hoverRectPadding * 2) - .x(-hoverRectPadding) - .y(-hoverRectPadding) - this.hoverNode.addClass('smm-hover-node') - this.style.hoverNode(this.hoverNode, width, height) - this.group.add(this.hoverNode) + addHoverNode() } // 给节点绑定事件 From 48f1de5c2502eb687aa2ae3e1159bfb32b979837 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Mon, 28 Aug 2023 15:40:50 +0800 Subject: [PATCH 14/38] =?UTF-8?q?Fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E8=8A=82?= =?UTF-8?q?=E7=82=B9hover=E5=92=8C=E6=BF=80=E6=B4=BB=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E6=97=B6=E6=B8=B2=E6=9F=93=E7=9A=84=E7=9F=A9=E5=BD=A2=E4=BC=9A?= =?UTF-8?q?=E9=87=8D=E5=8F=A0=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/layouts/Base.js | 53 ++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/simple-mind-map/src/layouts/Base.js b/simple-mind-map/src/layouts/Base.js index f3876262..92c4e0e4 100644 --- a/simple-mind-map/src/layouts/Base.js +++ b/simple-mind-map/src/layouts/Base.js @@ -46,7 +46,10 @@ class Base { // 检查当前来源是否需要重新计算节点大小 checkIsNeedResizeSources() { - return [CONSTANTS.CHANGE_THEME, CONSTANTS.TRANSFORM_TO_NORMAL_NODE].includes(this.renderer.renderSource) + return [ + CONSTANTS.CHANGE_THEME, + CONSTANTS.TRANSFORM_TO_NORMAL_NODE + ].includes(this.renderer.renderSource) } // 层级类型改变 @@ -70,7 +73,10 @@ class Base { // 数据上保存了节点引用,那么直接复用节点 if (data && data._node && !this.renderer.reRender) { newNode = data._node - let isLayerTypeChange = this.checkIsLayerTypeChange(newNode.layerIndex, layerIndex) + let isLayerTypeChange = this.checkIsLayerTypeChange( + newNode.layerIndex, + layerIndex + ) newNode.reset() newNode.layerIndex = layerIndex this.cacheNode(data._node.uid, newNode) @@ -85,7 +91,10 @@ class Base { newNode = this.lru.get(data.data.uid) // 保存该节点上一次的数据 let lastData = JSON.stringify(newNode.nodeData.data) - let isLayerTypeChange = this.checkIsLayerTypeChange(newNode.layerIndex, layerIndex) + let isLayerTypeChange = this.checkIsLayerTypeChange( + newNode.layerIndex, + layerIndex + ) newNode.reset() newNode.nodeData = newNode.handleData(data || {}) newNode.layerIndex = layerIndex @@ -139,7 +148,7 @@ class Base { } else if (initRootNodePositionMap[value] !== undefined) { return size * initRootNodePositionMap[value] } else if (/^\d\d*%$/.test(value)) { - return Number.parseFloat(value) / 100 * size + return (Number.parseFloat(value) / 100) * size } else { return (size - nodeSize) / 2 } @@ -148,12 +157,24 @@ class Base { // 定位节点到画布中间 setNodeCenter(node) { let { initRootNodePosition } = this.mindMap.opt - let { CENTER }= CONSTANTS.INIT_ROOT_NODE_POSITION - if (!initRootNodePosition || !Array.isArray(initRootNodePosition) || initRootNodePosition.length < 2) { + let { CENTER } = CONSTANTS.INIT_ROOT_NODE_POSITION + if ( + !initRootNodePosition || + !Array.isArray(initRootNodePosition) || + initRootNodePosition.length < 2 + ) { initRootNodePosition = [CENTER, CENTER] } - node.left = this.formatPosition(initRootNodePosition[0], this.mindMap.width, node.width) - node.top = this.formatPosition(initRootNodePosition[1], this.mindMap.height, node.height) + node.left = this.formatPosition( + initRootNodePosition[0], + this.mindMap.width, + node.width + ) + node.top = this.formatPosition( + initRootNodePosition[1], + this.mindMap.height, + node.height + ) } // 更新子节点属性 @@ -170,7 +191,7 @@ class Base { // 更新子节点多个属性 updateChildrenPro(children, props) { children.forEach(item => { - Object.keys(props).forEach((prop) => { + Object.keys(props).forEach(prop => { item[prop] += props[prop] }) if (item.children && item.children.length && !item.hasCustomPosition()) { @@ -216,16 +237,22 @@ class Base { // 获取节点的marginX getMarginX(layerIndex) { + const { themeConfig, opt } = this.mindMap + const { second, node } = themeConfig + const hoverRectPadding = opt.hoverRectPadding * 2 return layerIndex === 1 - ? this.mindMap.themeConfig.second.marginX - : this.mindMap.themeConfig.node.marginX + ? second.marginX + hoverRectPadding + : node.marginX + hoverRectPadding } // 获取节点的marginY getMarginY(layerIndex) { + const { themeConfig, opt } = this.mindMap + const { second, node } = themeConfig + const hoverRectPadding = opt.hoverRectPadding * 2 return layerIndex === 1 - ? this.mindMap.themeConfig.second.marginY - : this.mindMap.themeConfig.node.marginY + ? second.marginY + hoverRectPadding + : node.marginY + hoverRectPadding } // 获取节点包括概要在内的宽度 From f7f234b4cb674effedeb81b99480882bf1e0c50a Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Mon, 28 Aug 2023 20:23:23 +0800 Subject: [PATCH 15/38] =?UTF-8?q?Feat=EF=BC=9A=E4=BC=98=E5=8C=96=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E6=A0=B7=E5=BC=8F=E7=9A=84=E8=AE=BE=E7=BD=AE=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=8D=E5=BD=B1=E5=93=8D=E5=A4=A7=E5=B0=8F?= =?UTF-8?q?=E7=9A=84=E4=B8=BB=E9=A2=98=E5=B1=9E=E6=80=A7=E6=97=B6=E4=B8=8D?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=E5=85=A8=E9=87=8F=E6=B8=B2=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/index.js | 6 ++++-- simple-mind-map/src/themes/default.js | 3 ++- simple-mind-map/src/utils/index.js | 25 +++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/simple-mind-map/index.js b/simple-mind-map/index.js index 71075b36..26715799 100644 --- a/simple-mind-map/index.js +++ b/simple-mind-map/index.js @@ -15,7 +15,7 @@ import { cssContent } from './src/constants/constant' import { SVG } from '@svgdotjs/svg.js' -import { simpleDeepClone, getType } from './src/utils' +import { simpleDeepClone, getType, getObjectChangedProps } from './src/utils' import defaultTheme, { checkIsNodeSizeIndependenceConfig } from './src/themes/default' @@ -201,9 +201,11 @@ class MindMap { // 设置主题配置 setThemeConfig(config) { + // 计算改变了的配置 + const changedConfig = getObjectChangedProps(this.themeConfig, config) this.opt.themeConfig = config // 检查改变的是否是节点大小无关的主题属性 - let res = checkIsNodeSizeIndependenceConfig(config) + let res = checkIsNodeSizeIndependenceConfig(changedConfig) this.render(null, res ? '' : CONSTANTS.CHANGE_THEME) } diff --git a/simple-mind-map/src/themes/default.js b/simple-mind-map/src/themes/default.js index addc497a..34bd20b6 100644 --- a/simple-mind-map/src/themes/default.js +++ b/simple-mind-map/src/themes/default.js @@ -158,7 +158,8 @@ const nodeSizeIndependenceList = [ 'backgroundImage', 'backgroundRepeat', 'backgroundPosition', - 'backgroundSize' + 'backgroundSize', + 'rootLineKeepSameInCurve' ] export const checkIsNodeSizeIndependenceConfig = (config) => { let keys = Object.keys(config) diff --git a/simple-mind-map/src/utils/index.js b/simple-mind-map/src/utils/index.js index c934eca6..405e2c56 100644 --- a/simple-mind-map/src/utils/index.js +++ b/simple-mind-map/src/utils/index.js @@ -632,3 +632,28 @@ export const isMobile = () => { navigator.userAgent ) } + +// 获取对象改变了的的属性 +export const getObjectChangedProps = (oldObject, newObject) => { + const res = {} + Object.keys(newObject).forEach((prop) => { + const oldVal = oldObject[prop] + const newVal = newObject[prop] + if (getType(oldVal) !== getType(newVal)) { + res[prop] = newVal + return + } + if (getType(oldVal) === 'Object') { + if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) { + res[prop] = newVal + return + } + } else { + if (oldVal !== newVal) { + res[prop] = newVal + return + } + } + }) + return res +} \ No newline at end of file From a3362e44fea4bdce4ded5f2621e8c4ce0837d904 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Tue, 29 Aug 2023 09:46:36 +0800 Subject: [PATCH 16/38] =?UTF-8?q?Feat=EF=BC=9A=E5=8F=B3=E9=94=AE=E5=A4=9A?= =?UTF-8?q?=E9=80=89=E8=8A=82=E7=82=B9=E7=BB=93=E6=9D=9F=E6=97=B6=E7=A6=81?= =?UTF-8?q?=E6=AD=A2=E8=A7=A6=E5=8F=91=E8=8A=82=E7=82=B9=E5=8F=B3=E9=94=AE?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E4=BA=8B=E4=BB=B6=EF=BC=8C=E9=81=BF=E5=85=8D?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=E5=8F=B3=E9=94=AE=E8=8F=9C=E5=8D=95=E6=98=BE?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/core/render/node/Node.js | 8 ++++++-- simple-mind-map/src/plugins/Select.js | 10 ++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/simple-mind-map/src/core/render/node/Node.js b/simple-mind-map/src/core/render/node/Node.js index 016d3c7f..a9896812 100644 --- a/simple-mind-map/src/core/render/node/Node.js +++ b/simple-mind-map/src/core/render/node/Node.js @@ -449,13 +449,17 @@ class Node { }) // 右键菜单事件 this.group.on('contextmenu', e => { + const { readonly, useLeftKeySelectionRightKeyDrag } = this.mindMap.opt // 按住ctrl键点击鼠标左键不知为何触发的是contextmenu事件 - if (this.mindMap.opt.readonly || e.ctrlKey) { - // || this.isGeneralization + if (readonly || e.ctrlKey) { return } e.stopPropagation() e.preventDefault() + // 如果是多选节点结束,那么不要触发右键菜单事件 + if(!useLeftKeySelectionRightKeyDrag && this.mindMap.select.hasSelectRange()) { + return + } if (this.nodeData.data.isActive) { this.renderer.clearActive() } diff --git a/simple-mind-map/src/plugins/Select.js b/simple-mind-map/src/plugins/Select.js index bba83b0c..e3fba7bc 100644 --- a/simple-mind-map/src/plugins/Select.js +++ b/simple-mind-map/src/plugins/Select.js @@ -11,6 +11,7 @@ class Select { this.mouseDownY = 0 this.mouseMoveX = 0 this.mouseMoveY = 0 + this.isSelecting = false this.bindEvent() } @@ -70,11 +71,15 @@ class Select { this.isMousedown = false if (this.rect) this.rect.remove() this.rect = null + setTimeout(() => { + this.isSelecting = false + }, 0) }) } // 鼠标移动事件 onMove(x, y) { + this.isSelecting = true // 绘制矩形 this.rect.plot([ [this.mouseDownX, this.mouseDownY], @@ -172,6 +177,11 @@ class Select { } }) } + + // 是否存在选区 + hasSelectRange() { + return this.isSelecting + } } Select.instanceName = 'select' From a31e93bacf27241f500f8b661d6c112607b27ec2 Mon Sep 17 00:00:00 2001 From: eseasky Date: Tue, 29 Aug 2023 19:46:22 +0800 Subject: [PATCH 17/38] =?UTF-8?q?Feat:=20=E6=94=B6=E8=B5=B7=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E6=97=B6=EF=BC=8C=E6=98=BE=E7=A4=BA=E6=8A=98=E5=8F=A0?= =?UTF-8?q?=E7=9A=84=E8=8A=82=E7=82=B9=E6=95=B0=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/core/render/node/nodeExpandBtn.js | 53 +++++++++++++++---- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/simple-mind-map/src/core/render/node/nodeExpandBtn.js b/simple-mind-map/src/core/render/node/nodeExpandBtn.js index 5b2675cd..99252eda 100644 --- a/simple-mind-map/src/core/render/node/nodeExpandBtn.js +++ b/simple-mind-map/src/core/render/node/nodeExpandBtn.js @@ -3,16 +3,29 @@ import { SVG, Circle, G } from '@svgdotjs/svg.js' // 创建展开收起按钮的内容节点 function createExpandNodeContent() { - if (this._openExpandNode) { - return - } - let { open, close } = this.mindMap.opt.expandBtnIcon || {} + // 实时更新收起节点数字 + // if (this._openExpandNode) { + // return + // } + let { close } = this.mindMap.opt.expandBtnIcon || {} + // 计算子节点数量 + const count = this.sumNode(this.nodeData.children) + // 生成按钮 + const node = SVG() + .text(count) + .font({ family: 'Inconsolata' }) + node.attr('font-size', 14) // 展开的节点 - this._openExpandNode = SVG(open || btnsSvg.open).size( - this.expandBtnSize, - this.expandBtnSize - ) - this._openExpandNode.x(0).y(-this.expandBtnSize / 2) + this._openExpandNode = node.size(this.expandBtnSize, this.expandBtnSize) + // 数字不同偏移量大小处理 + if (count < 10) { + this._openExpandNode.x(6).y(-this.expandBtnSize / 2) + } else if (count >= 10 && count < 100) { + this._openExpandNode.x(1).y(-this.expandBtnSize / 2) + } else { + this._openExpandNode.x(0).y(-this.expandBtnSize / 2) + node.attr('font-size', 12) + } // 收起的节点 this._closeExpandNode = SVG(close || btnsSvg.close).size( this.expandBtnSize, @@ -22,6 +35,7 @@ function createExpandNodeContent() { // 填充节点 this._fillExpandNode = new Circle().size(this.expandBtnSize) this._fillExpandNode.x(0).y(-this.expandBtnSize / 2) + // 设置样式 this.style.iconBtn( this._openExpandNode, @@ -30,6 +44,13 @@ function createExpandNodeContent() { ) } +// 统计折叠的子节点数量 +function sumNode(data = []) { + return data.reduce( + (total, cur) => total + this.sumNode(cur.children || []), + data.length + ) +} // 创建或更新展开收缩按钮内容 function updateExpandBtnNode() { let { expand } = this.nodeData.data @@ -47,7 +68,16 @@ function updateExpandBtnNode() { node = this._closeExpandNode this._lastExpandBtnType = true } - if (this._expandBtn) this._expandBtn.add(this._fillExpandNode).add(node) + + if (this._expandBtn) { + // 如果是收起按钮加上边框 + if (!expand) { + this._fillExpandNode.stroke({ + color: '#333333' + }) + } + this._expandBtn.add(this._fillExpandNode).add(node) + } } // 更新展开收缩按钮位置 @@ -138,5 +168,6 @@ export default { renderExpandBtn, removeExpandBtn, showExpandBtn, - hideExpandBtn + hideExpandBtn, + sumNode } From 3d86650f22e81ffc3844ee556f14dc392c0ea7bf Mon Sep 17 00:00:00 2001 From: eseasky Date: Tue, 29 Aug 2023 20:09:57 +0800 Subject: [PATCH 18/38] =?UTF-8?q?Feat:=20=E5=AE=9E=E7=8E=B0=E5=85=B3?= =?UTF-8?q?=E8=81=94=E7=BA=BF=E7=AB=AF=E7=82=B9=E4=BD=8D=E7=BD=AE=E9=9A=8F?= =?UTF-8?q?=E9=BC=A0=E6=A0=87=E6=8B=96=E6=8B=BD=E5=8F=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/plugins/AssociativeLine.js | 116 ++++++++++++++--- .../associativeLineControls.js | 73 ++++++++--- .../associativeLine/associativeLineText.js | 21 +-- .../associativeLine/associativeLineUtils.js | 123 +++++++++++++++++- 4 files changed, 280 insertions(+), 53 deletions(-) diff --git a/simple-mind-map/src/plugins/AssociativeLine.js b/simple-mind-map/src/plugins/AssociativeLine.js index 2a7ebba1..06dc6fa6 100644 --- a/simple-mind-map/src/plugins/AssociativeLine.js +++ b/simple-mind-map/src/plugins/AssociativeLine.js @@ -99,12 +99,32 @@ class AssociativeLine { // 创建箭头 createMarker() { return this.draw.marker(20, 20, add => { - add.ref(2, 5) + add.ref(12, 5) add.size(10, 10) add.attr('orient', 'auto-start-reverse') this.markerPath = add.path('M0,0 L2,5 L0,10 L10,5 Z') }) } + // 判断关联线坐标是否变更,有变更则使用变化后的坐标,无则默认坐标 + updateAllLinesPos(node, toNode, associativeLineTargets) { + let startPoint = {} + let endPoint = {} + let nodeRange = 0 + let nodeDir = 'right' + let toNodeRange = 0 + let toNodeDir = 'right' + if (associativeLineTargets.startPoint && associativeLineTargets.endPoint) { + nodeRange = associativeLineTargets.startPoint.range || 0 + nodeDir = associativeLineTargets.startPoint.dir || 'right' + startPoint = getNodePoint(node, nodeDir, nodeRange) + toNodeRange = associativeLineTargets.endPoint.range || 0 + toNodeDir = associativeLineTargets.endPoint.dir || 'right' + endPoint = getNodePoint(toNode, toNodeDir, toNodeRange) + } else { + ;[startPoint, endPoint] = computeNodePoints(node, toNode) + } + return [startPoint, endPoint] + } // 渲染所有连线 renderAllLines() { @@ -137,10 +157,17 @@ class AssociativeLine { 0 ) nodeToIds.forEach((ids, node) => { - ids.forEach(id => { - let toNode = idToNode.get(id) + ids.forEach((id, index) => { + let toNode = idToNode.get(id.id) if (!node || !toNode) return - let [startPoint, endPoint] = computeNodePoints(node, toNode) + const associativeLineTargets = + node.nodeData.data.associativeLineTargets[index] || {} + // 切换结构和布局,都会更新坐标 + const [startPoint, endPoint] = this.updateAllLinesPos( + node, + toNode, + associativeLineTargets + ) this.drawLine(startPoint, endPoint, node, toNode) }) }) @@ -183,11 +210,28 @@ class AssociativeLine { .fill({ color: 'none' }) clickPath.plot(pathStr) // 文字 - let text = this.createText({ path, clickPath, node, toNode, startPoint, endPoint, controlPoints }) + let text = this.createText({ + path, + clickPath, + node, + toNode, + startPoint, + endPoint, + controlPoints + }) // 点击事件 clickPath.click(e => { e.stopPropagation() - this.setActiveLine({ path, clickPath, text, node, toNode, startPoint, endPoint, controlPoints }) + this.setActiveLine({ + path, + clickPath, + text, + node, + toNode, + startPoint, + endPoint, + controlPoints + }) }) // 渲染关联线文字 this.renderText(this.getText(node, toNode), path, text) @@ -195,10 +239,17 @@ class AssociativeLine { } // 激活某根关联线 - setActiveLine({ path, clickPath, text, node, toNode, startPoint, endPoint, controlPoints }) { - let { - associativeLineActiveColor - } = this.mindMap.themeConfig + setActiveLine({ + path, + clickPath, + text, + node, + toNode, + startPoint, + endPoint, + controlPoints + }) { + let { associativeLineActiveColor } = this.mindMap.themeConfig // 如果当前存在激活节点,那么取消激活节点 if (this.mindMap.renderer.activeNodeList.length > 0) { this.clearActiveNodes() @@ -243,8 +294,10 @@ class AssociativeLine { // 创建连接线 createLine(fromNode) { - let { associativeLineWidth, associativeLineColor } = - this.mindMap.themeConfig + let { + associativeLineWidth, + associativeLineColor + } = this.mindMap.themeConfig if (this.isCreatingLine || !fromNode) return this.isCreatingLine = true this.creatingStartNode = fromNode @@ -279,14 +332,33 @@ class AssociativeLine { // 获取转换后的鼠标事件对象的坐标 getTransformedEventPos(e) { let { x, y } = this.mindMap.toPos(e.clientX, e.clientY) - let { scaleX, scaleY, translateX, translateY } = - this.mindMap.draw.transform() + let { + scaleX, + scaleY, + translateX, + translateY + } = this.mindMap.draw.transform() return { x: (x - translateX) / scaleX, y: (y - translateY) / scaleY } } + // 计算节点偏移位置 + getNodePos(node) { + const { translateX, translateY } = this.mindMap.draw.transform() + const { left, top, width, height } = node + let translateLeft = left + translateX + let translateTop = top + translateY + return { + left, + top, + translateLeft, + translateTop, + width, + height + } + } // 检测当前移动到的目标节点 checkOverlapNode(x, y) { this.overlapNode = null @@ -336,7 +408,12 @@ class AssociativeLine { } // 将目标节点id保存起来 let list = fromNode.nodeData.data.associativeLineTargets || [] - list.push(id) + // 连线节点是否存在相同的id,存在则阻止添加关联线 + const sameLine = list.some(item => item.id === id) + if (sameLine) { + return + } + list.push({ id }) // 保存控制点 let [startPoint, endPoint] = computeNodePoints(fromNode, toNode) let controlPoints = computeCubicBezierPathPoints( @@ -369,13 +446,16 @@ class AssociativeLine { if (!this.activeLine) return let [, , , node, toNode] = this.activeLine this.removeControls() - let { associativeLineTargets, associativeLineTargetControlOffsets, associativeLineText } = - node.nodeData.data + let { + associativeLineTargets, + associativeLineTargetControlOffsets, + associativeLineText + } = node.nodeData.data let targetIndex = getAssociativeLineTargetIndex(node, toNode) // 更新关联线文本数据 let newAssociativeLineText = {} if (associativeLineText) { - Object.keys(associativeLineText).forEach((item) => { + Object.keys(associativeLineText).forEach(item => { if (item !== toNode.nodeData.data.id) { newAssociativeLineText[item] = associativeLineText[item] } diff --git a/simple-mind-map/src/plugins/associativeLine/associativeLineControls.js b/simple-mind-map/src/plugins/associativeLine/associativeLineControls.js index 105a69ef..6e5c0641 100644 --- a/simple-mind-map/src/plugins/associativeLine/associativeLineControls.js +++ b/simple-mind-map/src/plugins/associativeLine/associativeLineControls.js @@ -1,7 +1,7 @@ import { getAssociativeLineTargetIndex, joinCubicBezierPath, - computeNodePoints, + getNodePoint, getDefaultControlPointOffsets } from './associativeLineUtils' @@ -62,14 +62,22 @@ function onControlPointMousemove(e) { // 更新当前拖拽的控制点的位置 this[this.mousedownControlPointKey].x(x - radius).y(y - radius) let [path, clickPath, text, node, toNode] = this.activeLine - let [startPoint, endPoint] = computeNodePoints(node, toNode) + let targetIndex = getAssociativeLineTargetIndex(node, toNode) + const { + associativeLineTargets, + associativeLineTargetControlOffsets + } = node.nodeData.data + const nodePos = this.getNodePos(node) + const toNodePos = this.getNodePos(toNode) + let [startPoint, endPoint] = this.updateAllLinesPos( + node, + toNode, + associativeLineTargets[targetIndex] + ) 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) @@ -80,6 +88,7 @@ function onControlPointMousemove(e) { let point2 = null // 拖拽的是控制点1 if (this.mousedownControlPointKey === 'controlPoint1') { + startPoint = getNodePoint(nodePos, '', 0, e) point1 = { x, y @@ -88,10 +97,22 @@ function onControlPointMousemove(e) { x: endPoint.x + offsets[1].x, y: endPoint.y + offsets[1].y } - // 更新控制点1的连线 - this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y) + if (startPoint) { + // 保存更新后的坐标 + associativeLineTargets[targetIndex].startPoint = startPoint + this.controlPointMousemoveState.startPoint = startPoint + // 更新控制点1的连线 + this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y) + // 更新关联线 + const pathStr = joinCubicBezierPath(startPoint, endPoint, point1, point2) + path.plot(pathStr) + clickPath.plot(pathStr) + this.updateTextPos(path, text) + this.updateTextEditBoxPos(text) + } } else { // 拖拽的是控制点2 + endPoint = getNodePoint(toNodePos, '', 0, e) point1 = { x: startPoint.x + offsets[0].x, y: startPoint.y + offsets[0].y @@ -100,15 +121,20 @@ function onControlPointMousemove(e) { x, y } - // 更新控制点2的连线 - this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y) + if (endPoint) { + // 保存更新后结束节点的坐标 + associativeLineTargets[targetIndex].endPoint = endPoint + this.controlPointMousemoveState.endPoint = endPoint + // 更新控制点2的连线 + this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y) + // 更新关联线 + const pathStr = joinCubicBezierPath(startPoint, endPoint, point1, point2) + path.plot(pathStr) + clickPath.plot(pathStr) + this.updateTextPos(path, text) + this.updateTextEditBoxPos(text) + } } - // 更新关联线 - let pathStr = joinCubicBezierPath(startPoint, endPoint, point1, point2) - path.plot(pathStr) - clickPath.plot(pathStr) - this.updateTextPos(path, text) - this.updateTextEditBoxPos(text) } // 控制点的鼠标移动事件 @@ -116,12 +142,18 @@ function onControlPointMouseup(e) { if (!this.isControlPointMousedown) return e.stopPropagation() e.preventDefault() - let { pos, startPoint, endPoint, targetIndex } = - this.controlPointMousemoveState + let { + pos, + startPoint, + endPoint, + targetIndex + } = this.controlPointMousemoveState let [, , , node] = this.activeLine let offsetList = [] - let associativeLineTargetControlOffsets = - node.nodeData.data.associativeLineTargetControlOffsets + const { + associativeLineTargets, + associativeLineTargetControlOffsets + } = node.nodeData.data if (!associativeLineTargetControlOffsets) { // 兼容0.4.5版本,没有associativeLineTargetControlOffsets的情况 offsetList[targetIndex] = getDefaultControlPointOffsets( @@ -150,7 +182,8 @@ function onControlPointMouseup(e) { } offsetList[targetIndex] = [offset1, offset2] this.mindMap.execCommand('SET_NODE_DATA', node, { - associativeLineTargetControlOffsets: offsetList + associativeLineTargetControlOffsets: offsetList, + associativeLineTargets }) // 这里要加个setTimeout0是因为draw_click事件比mouseup事件触发的晚,所以重置isControlPointMousedown需要等draw_click事件触发完以后 setTimeout(() => { diff --git a/simple-mind-map/src/plugins/associativeLine/associativeLineText.js b/simple-mind-map/src/plugins/associativeLine/associativeLineText.js index 26c31fdb..deb812d9 100644 --- a/simple-mind-map/src/plugins/associativeLine/associativeLineText.js +++ b/simple-mind-map/src/plugins/associativeLine/associativeLineText.js @@ -36,7 +36,7 @@ function showEditTextBox(g) { 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;` @@ -62,7 +62,8 @@ function showEditTextBox(g) { ).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.lineHeight = + textLines.length > 1 ? associativeLineTextLineHeight : 'normal' this.textEditNode.style.zIndex = this.mindMap.opt.nodeTextEditZIndex this.textEditNode.innerHTML = textLines.join('
') this.textEditNode.style.display = 'block' @@ -78,10 +79,12 @@ function onScale() { // 更新文本编辑框位置 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' + if (this.textEditNode) { + 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` + } } // 隐藏文本编辑框 @@ -116,8 +119,10 @@ function getText(node, toNode) { // 渲染关联线文字 function renderText(str, path, text) { if (!str) return - let { associativeLineTextFontSize, associativeLineTextLineHeight } = - this.mindMap.themeConfig + let { + associativeLineTextFontSize, + associativeLineTextLineHeight + } = this.mindMap.themeConfig text.clear() let textArr = str.split(/\n/gim) textArr.forEach((item, index) => { diff --git a/simple-mind-map/src/plugins/associativeLine/associativeLineUtils.js b/simple-mind-map/src/plugins/associativeLine/associativeLineUtils.js index 018c33b9..be1b619f 100644 --- a/simple-mind-map/src/plugins/associativeLine/associativeLineUtils.js +++ b/simple-mind-map/src/plugins/associativeLine/associativeLineUtils.js @@ -1,7 +1,7 @@ // 获取目标节点在起始节点的目标数组中的索引 export const getAssociativeLineTargetIndex = (node, toNode) => { return node.nodeData.data.associativeLineTargets.findIndex(item => { - return item === toNode.nodeData.data.id + return item.id === toNode.nodeData.data.id }) } @@ -54,28 +54,137 @@ export const cubicBezierPath = (x1, y1, x2, y2) => { ) } +// 计算关联线起始|结束坐标 +export const calcPoint = (node, e) => { + const { left, top, translateLeft, translateTop, width, height } = node + const clientX = e.clientX + const clientY = e.clientY + // 中心点的坐标 + const centerX = translateLeft + width / 2 + const centerY = translateTop + height / 2 + const translateCenterX = left + width / 2 + const translateCenterY = top + height / 2 + const theta = Math.atan(height / width) + // 矩形左上角坐标 + const deltaX = clientX - centerX + const deltaY = centerY - clientY + // 方向值 + const direction = Math.atan2(deltaY, deltaX) + // 默认坐标 + let x = left + width + let y = top + height + if (direction < theta && direction >= -theta) { + // 右边 + // 正切值 = 对边/邻边,对边 = 正切值*邻边 + const range = direction * (width / 2) + if (direction < theta && direction >= 0) { + // 中心点上边 + y = translateCenterY - range + } else if (direction >= -theta && direction < 0) { + // 中心点下方 + y = translateCenterY - range + } + return { + x, + y, + dir: 'right', + range + } + } else if (direction >= theta && direction < Math.PI - theta) { + // 上边 + y = top + let range = 0 + if (direction < Math.PI / 2 - theta && direction >= theta) { + // 正切值 = 对边/邻边,邻边 = 对边/正切值 + const side = height / 2 / direction + range = -side + // 中心点右侧 + x = translateCenterX + side + } else if ( + direction >= Math.PI / 2 - theta && + direction < Math.PI - theta + ) { + // 中心点左侧 + const tanValue = (centerX - clientX) / (centerY - clientY) + const side = (height / 2) * tanValue + range = side + x = translateCenterX - side + } + return { + x, + y, + dir: 'top', + range + } + } else if (direction < -theta && direction >= theta - Math.PI) { + // 下边 + let range = 0 + if (direction >= theta - Math.PI / 2 && direction < -theta) { + // 中心点右侧 + // 正切值 = 对边/邻边,邻边 = 对边/正切值 + const side = height / 2 / direction + range = side + x = translateCenterX - side + } else if ( + direction < theta - Math.PI / 2 && + direction >= theta - Math.PI + ) { + // 中心点左侧 + const tanValue = (centerX - clientX) / (centerY - clientY) + const side = (height / 2) * tanValue + range = -side + x = translateCenterX + side + } + return { + x, + y, + dir: 'bottom', + range + } + } + // 左边 + x = left + const tanValue = (centerY - clientY) / (centerX - clientX) + const range = tanValue * (width / 2) + if (direction >= -Math.PI && direction < theta - Math.PI) { + // 中心点右侧 + y = translateCenterY - range + } else if (direction < Math.PI && direction >= Math.PI - theta) { + // 中心点左侧 + y = translateCenterY - range + } + return { + x, + y, + dir: 'left', + range + } +} // 获取节点的连接点 -export const getNodePoint = (node, dir = 'right') => { +export const getNodePoint = (node, dir = 'right', range = 0, e = null) => { let { left, top, width, height } = node + if (e) { + return calcPoint(node, e) + } switch (dir) { case 'left': return { x: left, - y: top + height / 2 + y: top + height / 2 - range } case 'right': return { x: left + width, - y: top + height / 2 + y: top + height / 2 - range } case 'top': return { - x: left + width / 2, + x: left + width / 2 - range, y: top } case 'bottom': return { - x: left + width / 2, + x: left + width / 2 - range, y: top + height } default: @@ -179,4 +288,4 @@ export const getDefaultControlPointOffsets = (startPoint, endPoint) => { y: controlPoints[1].y - endPoint.y } ] -} \ No newline at end of file +} From df2dc96ba5fe4980c64a6cd13f8f81762b841da7 Mon Sep 17 00:00:00 2001 From: eseasky Date: Wed, 30 Aug 2023 20:09:16 +0800 Subject: [PATCH 19/38] =?UTF-8?q?Feat:=201.=E9=85=8D=E7=BD=AE=E5=BC=80?= =?UTF-8?q?=E5=90=AF=E6=94=B6=E8=B5=B7=E6=98=BE=E7=A4=BA=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E6=95=B0=E9=87=8F=EF=BC=9B2.=E6=94=AF=E6=8C=81=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E4=BC=A0=E5=85=A5=E4=B8=80=E4=B8=AA=E5=87=BD=E6=95=B0?= =?UTF-8?q?=EF=BC=8C=E5=85=81=E8=AE=B8=E6=8E=A7=E5=88=B6=E6=94=B6=E8=B5=B7?= =?UTF-8?q?=E6=97=B6=E6=98=BE=E7=A4=BA=E7=9A=84=E5=86=85=E5=AE=B9=EF=BC=9B?= =?UTF-8?q?3.=E6=94=AF=E6=8C=81=E9=85=8D=E7=BD=AEexpandBtnSize=E5=B1=95?= =?UTF-8?q?=E5=BC=80=E6=94=B6=E8=B5=B7=E6=8C=89=E9=92=AE=E7=9A=84=E5=B0=BA?= =?UTF-8?q?=E5=AF=B8=EF=BC=9B4.=E6=94=B6=E8=B5=B7=E6=97=B6=E7=9A=84?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E5=8F=AF=E4=BB=A5=E4=BD=BF=E7=94=A8expandBtn?= =?UTF-8?q?Style=E9=85=8D=E7=BD=AE=E9=A2=9C=E8=89=B2=EF=BC=8C=E5=AD=97?= =?UTF-8?q?=E4=BD=93=EF=BC=8C=E8=BE=B9=E6=A1=86=E9=A2=9C=E8=89=B2=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/constants/defaultOptions.js | 10 +++- simple-mind-map/src/core/render/node/Style.js | 46 +++++++++++----- .../src/core/render/node/nodeExpandBtn.js | 52 ++++++++++--------- 3 files changed, 71 insertions(+), 37 deletions(-) diff --git a/simple-mind-map/src/constants/defaultOptions.js b/simple-mind-map/src/constants/defaultOptions.js index 0c8262e1..226ef97b 100644 --- a/simple-mind-map/src/constants/defaultOptions.js +++ b/simple-mind-map/src/constants/defaultOptions.js @@ -68,13 +68,21 @@ export const defaultOpt = { // 展开收起按钮的颜色 expandBtnStyle: { color: '#808080', - fill: '#fff' + fill: '#fff', + fontSize: 13, + strokeColor: '#333333' }, // 自定义展开收起按钮的图标 expandBtnIcon: { open: '', // svg字符串 close: '' }, + // 处理收起节点数量 + expandBtnNumHandler: num => { + return num + }, + // 是否显示带数量的收起按钮 + isShowExpandNum: true, // 是否只有当鼠标在画布内才响应快捷键事件 enableShortcutOnlyWhenMouseInSvg: true, // 初始根节点的位置 diff --git a/simple-mind-map/src/core/render/node/Style.js b/simple-mind-map/src/core/render/node/Style.js index 9cd452d2..a0f922ce 100644 --- a/simple-mind-map/src/core/render/node/Style.js +++ b/simple-mind-map/src/core/render/node/Style.js @@ -1,6 +1,15 @@ -import { tagColorList, nodeDataNoStylePropList } from '../../../constants/constant' +import { + tagColorList, + nodeDataNoStylePropList +} from '../../../constants/constant' const rootProp = ['paddingX', 'paddingY'] -const backgroundStyleProps = ['backgroundColor', 'backgroundImage', 'backgroundRepeat', 'backgroundPosition', 'backgroundSize'] +const backgroundStyleProps = [ + 'backgroundColor', + 'backgroundImage', + 'backgroundRepeat', + 'backgroundPosition', + 'backgroundSize' +] // 样式类 class Style { @@ -10,12 +19,18 @@ class Style { if (!Style.cacheStyle) { Style.cacheStyle = {} let style = window.getComputedStyle(el) - backgroundStyleProps.forEach((prop) => { + backgroundStyleProps.forEach(prop => { Style.cacheStyle[prop] = style[prop] }) } // 设置新样式 - let { backgroundColor, backgroundImage, backgroundRepeat, backgroundPosition, backgroundSize } = themeConfig + let { + backgroundColor, + backgroundImage, + backgroundRepeat, + backgroundPosition, + backgroundSize + } = themeConfig el.style.backgroundColor = backgroundColor if (backgroundImage && backgroundImage !== 'none') { el.style.backgroundImage = `url(${backgroundImage})` @@ -30,7 +45,7 @@ class Style { // 移除背景样式 static removeBackgroundStyle(el) { if (!Style.cacheStyle) return - backgroundStyleProps.forEach((prop) => { + backgroundStyleProps.forEach(prop => { el.style[prop] = Style.cacheStyle[prop] }) Style.cacheStyle = null @@ -142,10 +157,10 @@ class Style { // 获取文本样式 getTextFontStyle() { - return { - italic: this.merge('fontStyle') === 'italic', - bold: this.merge('fontWeight'), - fontSize: this.merge('fontSize'), + return { + italic: this.merge('fontStyle') === 'italic', + bold: this.merge('fontWeight'), + fontSize: this.merge('fontSize'), fontFamily: this.merge('fontFamily') } } @@ -201,19 +216,26 @@ class Style { // 展开收起按钮 iconBtn(node, node2, fillNode) { - let { color, fill } = this.ctx.mindMap.opt.expandBtnStyle || { + let { color, fill, fontSize, fontColor } = this.ctx.mindMap.opt + .expandBtnStyle || { color: '#808080', - fill: '#fff' + fill: '#fff', + fontSize: 12, + strokeColor: '#333333', + fontColor: '#333333' } node.fill({ color: color }) node2.fill({ color: color }) fillNode.fill({ color: fill }) + if (this.ctx.mindMap.opt.isShowExpandNum) { + node.attr({ 'font-size': fontSize, 'font-color': fontColor }) + } } // 是否设置了自定义的样式 hasCustomStyle() { let res = false - Object.keys(this.ctx.nodeData.data).forEach((item) => { + Object.keys(this.ctx.nodeData.data).forEach(item => { if (!nodeDataNoStylePropList.includes(item)) { res = true } diff --git a/simple-mind-map/src/core/render/node/nodeExpandBtn.js b/simple-mind-map/src/core/render/node/nodeExpandBtn.js index 99252eda..3fb13f16 100644 --- a/simple-mind-map/src/core/render/node/nodeExpandBtn.js +++ b/simple-mind-map/src/core/render/node/nodeExpandBtn.js @@ -3,28 +3,32 @@ import { SVG, Circle, G } from '@svgdotjs/svg.js' // 创建展开收起按钮的内容节点 function createExpandNodeContent() { - // 实时更新收起节点数字 - // if (this._openExpandNode) { - // return - // } - let { close } = this.mindMap.opt.expandBtnIcon || {} - // 计算子节点数量 - const count = this.sumNode(this.nodeData.children) - // 生成按钮 - const node = SVG() - .text(count) - .font({ family: 'Inconsolata' }) - node.attr('font-size', 14) - // 展开的节点 - this._openExpandNode = node.size(this.expandBtnSize, this.expandBtnSize) - // 数字不同偏移量大小处理 - if (count < 10) { - this._openExpandNode.x(6).y(-this.expandBtnSize / 2) - } else if (count >= 10 && count < 100) { - this._openExpandNode.x(1).y(-this.expandBtnSize / 2) + if (this._openExpandNode && !this.mindMap.opt.isShowExpandNum) { + return + } + let { close, open } = this.mindMap.opt.expandBtnIcon || {} + // 根据配置判断是否显示数量按钮 + if (this.mindMap.opt.isShowExpandNum) { + // 计算子节点数量 + let count = this.sumNode(this.nodeData.children) + count = this.mindMap.opt.expandBtnNumHandler(count) + // 展开的节点 + this._openExpandNode = SVG() + .text(count) + .size(this.expandBtnSize, this.expandBtnSize) + // 文本垂直居中 + this._openExpandNode.attr({ + 'text-anchor': 'middle', + 'dominant-baseline': 'middle', + x: this.expandBtnSize / 2, + y: 2 + }) } else { + this._openExpandNode = SVG(open || btnsSvg.open).size( + this.expandBtnSize, + this.expandBtnSize + ) this._openExpandNode.x(0).y(-this.expandBtnSize / 2) - node.attr('font-size', 12) } // 收起的节点 this._closeExpandNode = SVG(close || btnsSvg.close).size( @@ -43,8 +47,6 @@ function createExpandNodeContent() { this._fillExpandNode ) } - -// 统计折叠的子节点数量 function sumNode(data = []) { return data.reduce( (total, cur) => total + this.sumNode(cur.children || []), @@ -71,9 +73,11 @@ function updateExpandBtnNode() { if (this._expandBtn) { // 如果是收起按钮加上边框 - if (!expand) { + let opt = this.mindMap.opt + if (!expand && opt.isShowExpandNum) { + // 数字按钮添加边框 this._fillExpandNode.stroke({ - color: '#333333' + color: opt.expandBtnStyle.strokeColor }) } this._expandBtn.add(this._fillExpandNode).add(node) From 91bc0351b8ee88bcbfe6cecaa0bbccdebd6c103a Mon Sep 17 00:00:00 2001 From: eseasky Date: Wed, 30 Aug 2023 20:11:44 +0800 Subject: [PATCH 20/38] =?UTF-8?q?Feat:=20=E5=85=B3=E8=81=94=E7=BA=BF?= =?UTF-8?q?=E6=8B=96=E5=8A=A8=E6=95=88=E6=9E=9C=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/plugins/AssociativeLine.js | 56 ++++++++++++------- .../associativeLineControls.js | 52 ++++++++++------- .../associativeLine/associativeLineUtils.js | 7 +-- 3 files changed, 70 insertions(+), 45 deletions(-) diff --git a/simple-mind-map/src/plugins/AssociativeLine.js b/simple-mind-map/src/plugins/AssociativeLine.js index 06dc6fa6..637ba053 100644 --- a/simple-mind-map/src/plugins/AssociativeLine.js +++ b/simple-mind-map/src/plugins/AssociativeLine.js @@ -6,7 +6,8 @@ import { cubicBezierPath, getNodePoint, computeNodePoints, - getNodeLinePath + getNodeLinePath, + getDefaultControlPointOffsets } from './associativeLine/associativeLineUtils' import associativeLineControlsMethods from './associativeLine/associativeLineControls' import associativeLineTextMethods from './associativeLine/associativeLineText' @@ -106,22 +107,21 @@ class AssociativeLine { }) } // 判断关联线坐标是否变更,有变更则使用变化后的坐标,无则默认坐标 - updateAllLinesPos(node, toNode, associativeLineTargets) { - let startPoint = {} - let endPoint = {} + updateAllLinesPos(node, toNode, associativeLinePoint) { + let [startPoint, endPoint] = computeNodePoints(node, toNode) let nodeRange = 0 let nodeDir = 'right' let toNodeRange = 0 let toNodeDir = 'right' - if (associativeLineTargets.startPoint && associativeLineTargets.endPoint) { - nodeRange = associativeLineTargets.startPoint.range || 0 - nodeDir = associativeLineTargets.startPoint.dir || 'right' + if (associativeLinePoint.startPoint) { + nodeRange = associativeLinePoint.startPoint.range || 0 + nodeDir = associativeLinePoint.startPoint.dir || 'right' startPoint = getNodePoint(node, nodeDir, nodeRange) - toNodeRange = associativeLineTargets.endPoint.range || 0 - toNodeDir = associativeLineTargets.endPoint.dir || 'right' + } + if (associativeLinePoint.endPoint) { + toNodeRange = associativeLinePoint.endPoint.range || 0 + toNodeDir = associativeLinePoint.endPoint.dir || 'right' endPoint = getNodePoint(toNode, toNodeDir, toNodeRange) - } else { - ;[startPoint, endPoint] = computeNodePoints(node, toNode) } return [startPoint, endPoint] } @@ -158,15 +158,15 @@ class AssociativeLine { ) nodeToIds.forEach((ids, node) => { ids.forEach((id, index) => { - let toNode = idToNode.get(id.id) + let toNode = idToNode.get(id) if (!node || !toNode) return - const associativeLineTargets = - node.nodeData.data.associativeLineTargets[index] || {} + const associativeLinePoint = + node.nodeData.data.associativeLinePoint[index] || {} // 切换结构和布局,都会更新坐标 const [startPoint, endPoint] = this.updateAllLinesPos( node, toNode, - associativeLineTargets + associativeLinePoint ) this.drawLine(startPoint, endPoint, node, toNode) }) @@ -346,10 +346,15 @@ class AssociativeLine { // 计算节点偏移位置 getNodePos(node) { - const { translateX, translateY } = this.mindMap.draw.transform() + const { + scaleX, + scaleY, + translateX, + translateY + } = this.mindMap.draw.transform() const { left, top, width, height } = node - let translateLeft = left + translateX - let translateTop = top + translateY + let translateLeft = left * scaleX + translateX + let translateTop = top * scaleY + translateY return { left, top, @@ -409,11 +414,11 @@ class AssociativeLine { // 将目标节点id保存起来 let list = fromNode.nodeData.data.associativeLineTargets || [] // 连线节点是否存在相同的id,存在则阻止添加关联线 - const sameLine = list.some(item => item.id === id) + const sameLine = list.some(item => item === id) if (sameLine) { return } - list.push({ id }) + list.push(id) // 保存控制点 let [startPoint, endPoint] = computeNodePoints(fromNode, toNode) let controlPoints = computeCubicBezierPathPoints( @@ -435,9 +440,13 @@ class AssociativeLine { y: controlPoints[1].y - endPoint.y } ] + let associativeLinePoint = fromNode.nodeData.data.associativeLinePoint || [] + // 记录关联的起始|结束坐标 + associativeLinePoint[list.length - 1] = [{ startPoint, endPoint }] this.mindMap.execCommand('SET_NODE_DATA', fromNode, { associativeLineTargets: list, - associativeLineTargetControlOffsets: offsetList + associativeLineTargetControlOffsets: offsetList, + associativeLinePoint }) } @@ -448,6 +457,7 @@ class AssociativeLine { this.removeControls() let { associativeLineTargets, + associativeLinePoint, associativeLineTargetControlOffsets, associativeLineText } = node.nodeData.data @@ -466,6 +476,10 @@ class AssociativeLine { associativeLineTargets: associativeLineTargets.filter((_, index) => { return index !== targetIndex }), + // 连接线坐标 + associativeLinePoint: associativeLinePoint.filter((_, index) => { + return index !== targetIndex + }), // 偏移量 associativeLineTargetControlOffsets: associativeLineTargetControlOffsets ? associativeLineTargetControlOffsets.filter((_, index) => { diff --git a/simple-mind-map/src/plugins/associativeLine/associativeLineControls.js b/simple-mind-map/src/plugins/associativeLine/associativeLineControls.js index 6e5c0641..22a8dceb 100644 --- a/simple-mind-map/src/plugins/associativeLine/associativeLineControls.js +++ b/simple-mind-map/src/plugins/associativeLine/associativeLineControls.js @@ -61,10 +61,10 @@ function onControlPointMousemove(e) { } // 更新当前拖拽的控制点的位置 this[this.mousedownControlPointKey].x(x - radius).y(y - radius) - let [path, clickPath, text, node, toNode] = this.activeLine + let [, , , node, toNode] = this.activeLine let targetIndex = getAssociativeLineTargetIndex(node, toNode) const { - associativeLineTargets, + associativeLinePoint, associativeLineTargetControlOffsets } = node.nodeData.data const nodePos = this.getNodePos(node) @@ -72,7 +72,7 @@ function onControlPointMousemove(e) { let [startPoint, endPoint] = this.updateAllLinesPos( node, toNode, - associativeLineTargets[targetIndex] + associativeLinePoint[targetIndex] ) this.controlPointMousemoveState.startPoint = startPoint this.controlPointMousemoveState.endPoint = endPoint @@ -99,16 +99,10 @@ function onControlPointMousemove(e) { } if (startPoint) { // 保存更新后的坐标 - associativeLineTargets[targetIndex].startPoint = startPoint + associativeLinePoint[targetIndex].startPoint = startPoint this.controlPointMousemoveState.startPoint = startPoint // 更新控制点1的连线 this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y) - // 更新关联线 - const pathStr = joinCubicBezierPath(startPoint, endPoint, point1, point2) - path.plot(pathStr) - clickPath.plot(pathStr) - this.updateTextPos(path, text) - this.updateTextEditBoxPos(text) } } else { // 拖拽的是控制点2 @@ -123,18 +117,35 @@ function onControlPointMousemove(e) { } if (endPoint) { // 保存更新后结束节点的坐标 - associativeLineTargets[targetIndex].endPoint = endPoint + associativeLinePoint[targetIndex].endPoint = endPoint this.controlPointMousemoveState.endPoint = endPoint // 更新控制点2的连线 this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y) - // 更新关联线 - const pathStr = joinCubicBezierPath(startPoint, endPoint, point1, point2) - path.plot(pathStr) - clickPath.plot(pathStr) - this.updateTextPos(path, text) - this.updateTextEditBoxPos(text) } } + this.updataAassociativeLine( + startPoint, + endPoint, + point1, + point2, + this.activeLine + ) +} + +function updataAassociativeLine( + startPoint, + endPoint, + point1, + point2, + activeLine +) { + const [path, clickPath, text] = activeLine + // 更新关联线 + const pathStr = joinCubicBezierPath(startPoint, endPoint, point1, point2) + path.plot(pathStr) + clickPath.plot(pathStr) + this.updateTextPos(path, text) + this.updateTextEditBoxPos(text) } // 控制点的鼠标移动事件 @@ -151,7 +162,7 @@ function onControlPointMouseup(e) { let [, , , node] = this.activeLine let offsetList = [] const { - associativeLineTargets, + associativeLinePoint, associativeLineTargetControlOffsets } = node.nodeData.data if (!associativeLineTargetControlOffsets) { @@ -183,7 +194,7 @@ function onControlPointMouseup(e) { offsetList[targetIndex] = [offset1, offset2] this.mindMap.execCommand('SET_NODE_DATA', node, { associativeLineTargetControlOffsets: offsetList, - associativeLineTargets + associativeLinePoint }) // 这里要加个setTimeout0是因为draw_click事件比mouseup事件触发的晚,所以重置isControlPointMousedown需要等draw_click事件触发完以后 setTimeout(() => { @@ -270,5 +281,6 @@ export default { renderControls, removeControls, hideControls, - showControls + showControls, + updataAassociativeLine } diff --git a/simple-mind-map/src/plugins/associativeLine/associativeLineUtils.js b/simple-mind-map/src/plugins/associativeLine/associativeLineUtils.js index be1b619f..b68216e5 100644 --- a/simple-mind-map/src/plugins/associativeLine/associativeLineUtils.js +++ b/simple-mind-map/src/plugins/associativeLine/associativeLineUtils.js @@ -1,7 +1,7 @@ // 获取目标节点在起始节点的目标数组中的索引 export const getAssociativeLineTargetIndex = (node, toNode) => { return node.nodeData.data.associativeLineTargets.findIndex(item => { - return item.id === toNode.nodeData.data.id + return item === toNode.nodeData.data.id }) } @@ -54,7 +54,6 @@ export const cubicBezierPath = (x1, y1, x2, y2) => { ) } -// 计算关联线起始|结束坐标 export const calcPoint = (node, e) => { const { left, top, translateLeft, translateTop, width, height } = node const clientX = e.clientX @@ -220,8 +219,8 @@ export const computeNodePoints = (fromNode, toNode) => { toDir = 'bottom' } else if (offsetY > 0 && -offsetY < offsetX && offsetY > offsetX) { // down - fromDir = 'bottom' - toDir = 'top' + fromDir = 'right' + toDir = 'right' } return [getNodePoint(fromNode, fromDir), getNodePoint(toNode, toDir)] } From 115725791147fb68bd91f24f8f02415e38c4cfaa Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Thu, 31 Aug 2023 19:43:59 +0800 Subject: [PATCH 21/38] =?UTF-8?q?Fix=EF=BC=9A1.=E4=BC=98=E5=8C=96=E5=B1=95?= =?UTF-8?q?=E5=BC=80=E6=94=B6=E8=B5=B7=E4=BB=A3=E7=A0=81=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=9B2.=E4=BF=AE=E5=A4=8D=E5=85=B3=E8=81=94=E7=BA=BF?= =?UTF-8?q?=E4=B8=8D=E5=85=BC=E5=AE=B9=E8=80=81=E7=89=88=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98=EF=BC=8C=E4=BF=AE=E5=A4=8D=E7=AB=AF=E7=82=B9=E5=81=8F?= =?UTF-8?q?=E7=A7=BB=E9=87=8F=E6=9C=AA=E4=BF=9D=E5=AD=98=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/core/render/node/nodeExpandBtn.js | 27 +++++++----- .../src/plugins/AssociativeLine.js | 36 ++++++---------- .../associativeLineControls.js | 43 +++++++++++-------- 3 files changed, 54 insertions(+), 52 deletions(-) diff --git a/simple-mind-map/src/core/render/node/nodeExpandBtn.js b/simple-mind-map/src/core/render/node/nodeExpandBtn.js index 3fb13f16..61936759 100644 --- a/simple-mind-map/src/core/render/node/nodeExpandBtn.js +++ b/simple-mind-map/src/core/render/node/nodeExpandBtn.js @@ -3,18 +3,15 @@ import { SVG, Circle, G } from '@svgdotjs/svg.js' // 创建展开收起按钮的内容节点 function createExpandNodeContent() { - if (this._openExpandNode && !this.mindMap.opt.isShowExpandNum) { + if (this._openExpandNode) { return } let { close, open } = this.mindMap.opt.expandBtnIcon || {} // 根据配置判断是否显示数量按钮 if (this.mindMap.opt.isShowExpandNum) { - // 计算子节点数量 - let count = this.sumNode(this.nodeData.children) - count = this.mindMap.opt.expandBtnNumHandler(count) // 展开的节点 this._openExpandNode = SVG() - .text(count) + .text() .size(this.expandBtnSize, this.expandBtnSize) // 文本垂直居中 this._openExpandNode.attr({ @@ -73,12 +70,20 @@ function updateExpandBtnNode() { if (this._expandBtn) { // 如果是收起按钮加上边框 - let opt = this.mindMap.opt - if (!expand && opt.isShowExpandNum) { - // 数字按钮添加边框 - this._fillExpandNode.stroke({ - color: opt.expandBtnStyle.strokeColor - }) + let { isShowExpandNum, expandBtnStyle, expandBtnNumHandler } = this.mindMap.opt + if (isShowExpandNum) { + if (!expand) { + // 数字按钮添加边框 + this._fillExpandNode.stroke({ + color: expandBtnStyle.strokeColor + }) + // 计算子节点数量 + let count = this.sumNode(this.nodeData.children) + count = expandBtnNumHandler(count) + node.text(count) + } else { + this._fillExpandNode.stroke('none') + } } this._expandBtn.add(this._fillExpandNode).add(node) } diff --git a/simple-mind-map/src/plugins/AssociativeLine.js b/simple-mind-map/src/plugins/AssociativeLine.js index 637ba053..dbca5808 100644 --- a/simple-mind-map/src/plugins/AssociativeLine.js +++ b/simple-mind-map/src/plugins/AssociativeLine.js @@ -6,8 +6,7 @@ import { cubicBezierPath, getNodePoint, computeNodePoints, - getNodeLinePath, - getDefaultControlPointOffsets + getNodeLinePath } from './associativeLine/associativeLineUtils' import associativeLineControlsMethods from './associativeLine/associativeLineControls' import associativeLineTextMethods from './associativeLine/associativeLineText' @@ -106,13 +105,15 @@ class AssociativeLine { this.markerPath = add.path('M0,0 L2,5 L0,10 L10,5 Z') }) } + // 判断关联线坐标是否变更,有变更则使用变化后的坐标,无则默认坐标 updateAllLinesPos(node, toNode, associativeLinePoint) { + associativeLinePoint = associativeLinePoint || {} let [startPoint, endPoint] = computeNodePoints(node, toNode) let nodeRange = 0 - let nodeDir = 'right' + let nodeDir = '' let toNodeRange = 0 - let toNodeDir = 'right' + let toNodeDir = '' if (associativeLinePoint.startPoint) { nodeRange = associativeLinePoint.startPoint.range || 0 nodeDir = associativeLinePoint.startPoint.dir || 'right' @@ -160,8 +161,8 @@ class AssociativeLine { ids.forEach((id, index) => { let toNode = idToNode.get(id) if (!node || !toNode) return - const associativeLinePoint = - node.nodeData.data.associativeLinePoint[index] || {} + const associativeLinePoint = (node.nodeData.data.associativeLinePoint || + [])[index] // 切换结构和布局,都会更新坐标 const [startPoint, endPoint] = this.updateAllLinesPos( node, @@ -294,10 +295,8 @@ class AssociativeLine { // 创建连接线 createLine(fromNode) { - let { - associativeLineWidth, - associativeLineColor - } = this.mindMap.themeConfig + let { associativeLineWidth, associativeLineColor } = + this.mindMap.themeConfig if (this.isCreatingLine || !fromNode) return this.isCreatingLine = true this.creatingStartNode = fromNode @@ -332,12 +331,8 @@ class AssociativeLine { // 获取转换后的鼠标事件对象的坐标 getTransformedEventPos(e) { let { x, y } = this.mindMap.toPos(e.clientX, e.clientY) - let { - scaleX, - scaleY, - translateX, - translateY - } = this.mindMap.draw.transform() + let { scaleX, scaleY, translateX, translateY } = + this.mindMap.draw.transform() return { x: (x - translateX) / scaleX, y: (y - translateY) / scaleY @@ -346,12 +341,8 @@ class AssociativeLine { // 计算节点偏移位置 getNodePos(node) { - const { - scaleX, - scaleY, - translateX, - translateY - } = this.mindMap.draw.transform() + const { scaleX, scaleY, translateX, translateY } = + this.mindMap.draw.transform() const { left, top, width, height } = node let translateLeft = left * scaleX + translateX let translateTop = top * scaleY + translateY @@ -461,6 +452,7 @@ class AssociativeLine { associativeLineTargetControlOffsets, associativeLineText } = node.nodeData.data + associativeLinePoint = associativeLinePoint || [] let targetIndex = getAssociativeLineTargetIndex(node, toNode) // 更新关联线文本数据 let newAssociativeLineText = {} diff --git a/simple-mind-map/src/plugins/associativeLine/associativeLineControls.js b/simple-mind-map/src/plugins/associativeLine/associativeLineControls.js index 22a8dceb..a35b4d7c 100644 --- a/simple-mind-map/src/plugins/associativeLine/associativeLineControls.js +++ b/simple-mind-map/src/plugins/associativeLine/associativeLineControls.js @@ -63,10 +63,9 @@ function onControlPointMousemove(e) { this[this.mousedownControlPointKey].x(x - radius).y(y - radius) let [, , , node, toNode] = this.activeLine let targetIndex = getAssociativeLineTargetIndex(node, toNode) - const { - associativeLinePoint, - associativeLineTargetControlOffsets - } = node.nodeData.data + let { associativeLinePoint, associativeLineTargetControlOffsets } = + node.nodeData.data + associativeLinePoint = associativeLinePoint || [] const nodePos = this.getNodePos(node) const toNodePos = this.getNodePos(toNode) let [startPoint, endPoint] = this.updateAllLinesPos( @@ -86,9 +85,14 @@ function onControlPointMousemove(e) { } let point1 = null let point2 = null + const { x: clientX, y: clientY } = this.mindMap.toPos(e.clientX, e.clientY) + const _e = { + clientX, + clientY + } // 拖拽的是控制点1 if (this.mousedownControlPointKey === 'controlPoint1') { - startPoint = getNodePoint(nodePos, '', 0, e) + startPoint = getNodePoint(nodePos, '', 0, _e) point1 = { x, y @@ -99,14 +103,13 @@ function onControlPointMousemove(e) { } if (startPoint) { // 保存更新后的坐标 - associativeLinePoint[targetIndex].startPoint = startPoint this.controlPointMousemoveState.startPoint = startPoint // 更新控制点1的连线 this.controlLine1.plot(startPoint.x, startPoint.y, point1.x, point1.y) } } else { // 拖拽的是控制点2 - endPoint = getNodePoint(toNodePos, '', 0, e) + endPoint = getNodePoint(toNodePos, '', 0, _e) point1 = { x: startPoint.x + offsets[0].x, y: startPoint.y + offsets[0].y @@ -117,7 +120,6 @@ function onControlPointMousemove(e) { } if (endPoint) { // 保存更新后结束节点的坐标 - associativeLinePoint[targetIndex].endPoint = endPoint this.controlPointMousemoveState.endPoint = endPoint // 更新控制点2的连线 this.controlLine2.plot(endPoint.x, endPoint.y, point2.x, point2.y) @@ -148,23 +150,24 @@ function updataAassociativeLine( this.updateTextEditBoxPos(text) } -// 控制点的鼠标移动事件 +// 控制点的鼠标松开事件 function onControlPointMouseup(e) { if (!this.isControlPointMousedown) return e.stopPropagation() e.preventDefault() - let { - pos, - startPoint, - endPoint, - targetIndex - } = this.controlPointMousemoveState + let { pos, startPoint, endPoint, targetIndex } = + this.controlPointMousemoveState let [, , , node] = this.activeLine let offsetList = [] - const { - associativeLinePoint, - associativeLineTargetControlOffsets - } = node.nodeData.data + let { associativeLinePoint, associativeLineTargetControlOffsets } = + node.nodeData.data + if (!associativeLinePoint) { + associativeLinePoint = [] + } + associativeLinePoint[targetIndex] = associativeLinePoint[targetIndex] || { + startPoint, + endPoint + } if (!associativeLineTargetControlOffsets) { // 兼容0.4.5版本,没有associativeLineTargetControlOffsets的情况 offsetList[targetIndex] = getDefaultControlPointOffsets( @@ -183,6 +186,7 @@ function onControlPointMouseup(e) { y: pos.y - startPoint.y } offset2 = offsetList[targetIndex][1] + associativeLinePoint[targetIndex].startPoint = startPoint } else { // 更新控制点2数据 offset1 = offsetList[targetIndex][0] @@ -190,6 +194,7 @@ function onControlPointMouseup(e) { x: pos.x - endPoint.x, y: pos.y - endPoint.y } + associativeLinePoint[targetIndex].endPoint = endPoint } offsetList[targetIndex] = [offset1, offset2] this.mindMap.execCommand('SET_NODE_DATA', node, { From 6b4c118a2b89b8c28dda982cf368c7e968744dcc Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Fri, 1 Sep 2023 09:34:22 +0800 Subject: [PATCH 22/38] =?UTF-8?q?Fix=EF=BC=9A=E4=BC=98=E5=8C=96Select?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=EF=BC=8C=E5=A6=82=E6=9E=9C=E5=A4=9A=E9=80=89?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E6=B2=A1=E6=9C=89=E5=8F=98=E5=8C=96=EF=BC=8C?= =?UTF-8?q?=E9=82=A3=E4=B9=88=E4=B8=8D=E8=A7=A6=E5=8F=91=E6=BF=80=E6=B4=BB?= =?UTF-8?q?=E6=BF=80=E6=B4=BB=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/plugins/Select.js | 33 +++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/simple-mind-map/src/plugins/Select.js b/simple-mind-map/src/plugins/Select.js index e3fba7bc..329b4dc1 100644 --- a/simple-mind-map/src/plugins/Select.js +++ b/simple-mind-map/src/plugins/Select.js @@ -12,6 +12,7 @@ class Select { this.mouseMoveX = 0 this.mouseMoveY = 0 this.isSelecting = false + this.cacheActiveList = [] this.bindEvent() } @@ -31,6 +32,7 @@ class Select { } e.preventDefault() this.isMousedown = true + this.cacheActiveList = [...this.mindMap.renderer.activeNodeList] let { x, y } = this.mindMap.toPos(e.clientX, e.clientY) this.mouseDownX = x this.mouseDownY = y @@ -62,13 +64,10 @@ class Select { if (!this.isMousedown) { return } - this.mindMap.emit( - 'node_active', - null, - this.mindMap.renderer.activeNodeList - ) + this.checkTriggerNodeActiveEvent() clearTimeout(this.autoMoveTimer) this.isMousedown = false + this.cacheActiveList = [] if (this.rect) this.rect.remove() this.rect = null setTimeout(() => { @@ -77,6 +76,30 @@ class Select { }) } + // 如果激活节点改变了,那么触发事件 + checkTriggerNodeActiveEvent() { + let isNumChange = this.cacheActiveList.length !== this.mindMap.renderer.activeNodeList.length + let isNodeChange = false + if (!isNumChange) { + for(let i = 0; i < this.cacheActiveList.length; i++) { + let cur = this.cacheActiveList[i] + if (!this.mindMap.renderer.activeNodeList.find((item) => { + return item.nodeData.data.uid === cur.nodeData.data.uid + })){ + isNodeChange = true + break + } + } + } + if (isNumChange || isNodeChange) { + this.mindMap.emit( + 'node_active', + null, + this.mindMap.renderer.activeNodeList + ) + } + } + // 鼠标移动事件 onMove(x, y) { this.isSelecting = true From a404a71ba273660e3ed21f8f942f44cd2eccbb53 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Fri, 1 Sep 2023 09:36:39 +0800 Subject: [PATCH 23/38] =?UTF-8?q?Demo=EF=BC=9A=E4=B8=8D=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E5=BC=95=E7=94=A8=E5=86=85=E9=83=A8=E6=BF=80=E6=B4=BB=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E5=88=97=E8=A1=A8=EF=BC=8C=E4=BC=98=E5=8C=96=E6=80=A7?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/pages/Edit/components/NodeHyperlink.vue | 2 +- web/src/pages/Edit/components/NodeIcon.vue | 2 +- web/src/pages/Edit/components/NodeIconSidebar.vue | 2 +- web/src/pages/Edit/components/NodeImage.vue | 2 +- web/src/pages/Edit/components/NodeNote.vue | 2 +- web/src/pages/Edit/components/NodeTag.vue | 2 +- web/src/pages/Edit/components/Style.vue | 4 ++-- web/src/pages/Edit/components/Toolbar.vue | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/web/src/pages/Edit/components/NodeHyperlink.vue b/web/src/pages/Edit/components/NodeHyperlink.vue index f2002044..0ce20506 100644 --- a/web/src/pages/Edit/components/NodeHyperlink.vue +++ b/web/src/pages/Edit/components/NodeHyperlink.vue @@ -59,7 +59,7 @@ export default { }, methods: { handleNodeActive(...args) { - this.activeNodes = args[1] + this.activeNodes = [...args[1]] if (this.activeNodes.length > 0) { let firstNode = this.activeNodes[0] this.link = firstNode.getData('hyperlink') diff --git a/web/src/pages/Edit/components/NodeIcon.vue b/web/src/pages/Edit/components/NodeIcon.vue index 6cc5e6db..eab91dfb 100644 --- a/web/src/pages/Edit/components/NodeIcon.vue +++ b/web/src/pages/Edit/components/NodeIcon.vue @@ -52,7 +52,7 @@ export default { }, methods: { handleNodeActive(...args) { - this.activeNodes = args[1] + this.activeNodes = [...args[1]] if (this.activeNodes.length > 0) { let firstNode = this.activeNodes[0] this.iconList = firstNode.getData('icon') || [] diff --git a/web/src/pages/Edit/components/NodeIconSidebar.vue b/web/src/pages/Edit/components/NodeIconSidebar.vue index 01e2fbdb..168eb970 100644 --- a/web/src/pages/Edit/components/NodeIconSidebar.vue +++ b/web/src/pages/Edit/components/NodeIconSidebar.vue @@ -92,7 +92,7 @@ export default { }, methods: { handleNodeActive(...args) { - this.activeNodes = args[1] + this.activeNodes = [...args[1]] if (this.activeNodes.length > 0) { let firstNode = this.activeNodes[0] this.nodeImage = firstNode.getData('image') diff --git a/web/src/pages/Edit/components/NodeImage.vue b/web/src/pages/Edit/components/NodeImage.vue index 649e7590..9bd2b8c2 100644 --- a/web/src/pages/Edit/components/NodeImage.vue +++ b/web/src/pages/Edit/components/NodeImage.vue @@ -68,7 +68,7 @@ export default { }, methods: { handleNodeActive(...args) { - this.activeNodes = args[1] + this.activeNodes = [...args[1]] }, handleShowNodeImage() { diff --git a/web/src/pages/Edit/components/NodeNote.vue b/web/src/pages/Edit/components/NodeNote.vue index 6567e139..31e98af2 100644 --- a/web/src/pages/Edit/components/NodeNote.vue +++ b/web/src/pages/Edit/components/NodeNote.vue @@ -52,7 +52,7 @@ export default { }, methods: { handleNodeActive(...args) { - this.activeNodes = args[1] + this.activeNodes = [...args[1]] if (this.activeNodes.length > 0) { let firstNode = this.activeNodes[0] this.note = firstNode.getData('note') diff --git a/web/src/pages/Edit/components/NodeTag.vue b/web/src/pages/Edit/components/NodeTag.vue index 7b7de7a7..ddf8e390 100644 --- a/web/src/pages/Edit/components/NodeTag.vue +++ b/web/src/pages/Edit/components/NodeTag.vue @@ -69,7 +69,7 @@ export default { }, methods: { handleNodeActive(...args) { - this.activeNodes = args[1] + this.activeNodes = [...args[1]] if (this.activeNodes.length > 0) { let firstNode = this.activeNodes[0] this.tagArr = firstNode.getData('tag') || [] diff --git a/web/src/pages/Edit/components/Style.vue b/web/src/pages/Edit/components/Style.vue index e20b2f7f..197728e4 100644 --- a/web/src/pages/Edit/components/Style.vue +++ b/web/src/pages/Edit/components/Style.vue @@ -5,7 +5,7 @@ :class="{ isDark: isDark }" v-if="activeNodes.length > 0" > -
+
{{ $t('style.text') }}
@@ -486,7 +486,7 @@ export default { */ onNodeActive(...args) { this.$nextTick(() => { - this.activeNodes = args[1] + this.activeNodes = [...args[1]] this.initNodeStyle() }) }, diff --git a/web/src/pages/Edit/components/Toolbar.vue b/web/src/pages/Edit/components/Toolbar.vue index 68a2062d..775f0bed 100644 --- a/web/src/pages/Edit/components/Toolbar.vue +++ b/web/src/pages/Edit/components/Toolbar.vue @@ -274,7 +274,7 @@ export default { * @Desc: 监听节点激活 */ onNodeActive(...args) { - this.activeNodes = args[1] + this.activeNodes = [...args[1]] }, /** From 4c3f3cb1ab838861d32bbe111c66a5c4bb264f6a Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Fri, 1 Sep 2023 09:45:17 +0800 Subject: [PATCH 24/38] =?UTF-8?q?Feat=EF=BC=9Anode=5Factive=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=E6=8A=9B=E5=87=BA=E7=9A=84=E6=BF=80=E6=B4=BB=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E5=88=97=E8=A1=A8=E4=B8=8D=E5=86=8D=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E5=BC=95=E7=94=A8=E5=86=85=E9=83=A8=E6=BF=80=E6=B4=BB=E5=88=97?= =?UTF-8?q?=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/core/render/Render.js | 8 ++++---- simple-mind-map/src/core/render/node/Node.js | 4 ++-- simple-mind-map/src/plugins/Select.js | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/simple-mind-map/src/core/render/Render.js b/simple-mind-map/src/core/render/Render.js index 3a274fa5..897e7ca9 100644 --- a/simple-mind-map/src/core/render/Render.js +++ b/simple-mind-map/src/core/render/Render.js @@ -342,7 +342,7 @@ class Render { } }) }) - this.mindMap.emit('node_active', null, this.activeNodeList) + this.mindMap.emit('node_active', null, [...this.activeNodeList]) } // 清除当前激活的节点 @@ -854,7 +854,7 @@ class Render { } } } - this.mindMap.emit('node_active', null, this.activeNodeList) + this.mindMap.emit('node_active', null, [...this.activeNodeList]) this.mindMap.render() } @@ -886,7 +886,7 @@ class Render { let copyData = copyNodeTree({}, node, true) this.removeActiveNode(node) this.removeOneNode(node) - this.mindMap.emit('node_active', null, this.activeNodeList) + this.mindMap.emit('node_active', null, [...this.activeNodeList]) this.mindMap.render() if (callback && typeof callback === 'function') { callback(copyData) @@ -901,7 +901,7 @@ class Render { // let copyData = copyNodeTree({}, node, false, true) this.removeActiveNode(node) this.removeOneNode(node) - this.mindMap.emit('node_active', null, this.activeNodeList) + this.mindMap.emit('node_active', null, [...this.activeNodeList]) toNode.nodeData.children.push(node.nodeData) this.mindMap.render() if (toNode.isRoot) { diff --git a/simple-mind-map/src/core/render/node/Node.js b/simple-mind-map/src/core/render/node/Node.js index a9896812..2bc4408c 100644 --- a/simple-mind-map/src/core/render/node/Node.js +++ b/simple-mind-map/src/core/render/node/Node.js @@ -417,7 +417,7 @@ class Node { this.mindMap.emit( 'node_active', isActive ? null : this, - this.mindMap.renderer.activeNodeList + [...this.mindMap.renderer.activeNodeList] ) } this.mindMap.emit('node_mousedown', this, e) @@ -481,7 +481,7 @@ class Node { this.renderer.clearActive() this.mindMap.execCommand('SET_NODE_ACTIVE', this, true) this.renderer.addActiveNode(this) - this.mindMap.emit('node_active', this, this.renderer.activeNodeList) + this.mindMap.emit('node_active', this, [...this.renderer.activeNodeList]) } // 更新节点 diff --git a/simple-mind-map/src/plugins/Select.js b/simple-mind-map/src/plugins/Select.js index 329b4dc1..fe6d801d 100644 --- a/simple-mind-map/src/plugins/Select.js +++ b/simple-mind-map/src/plugins/Select.js @@ -95,7 +95,7 @@ class Select { this.mindMap.emit( 'node_active', null, - this.mindMap.renderer.activeNodeList + [...this.mindMap.renderer.activeNodeList] ) } } From f43a2ebdef15de93de64f810983028b4c507c66b Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Fri, 1 Sep 2023 09:50:33 +0800 Subject: [PATCH 25/38] =?UTF-8?q?Fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E4=B8=BB=E9=A2=98=E6=97=B6=E5=AD=98=E5=9C=A8=E5=85=B3?= =?UTF-8?q?=E8=81=94=E7=BA=BF=E7=9A=84=E8=8A=82=E7=82=B9=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E4=B8=8D=E4=BC=9A=E6=9B=B4=E6=94=B9=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/constants/constant.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/simple-mind-map/src/constants/constant.js b/simple-mind-map/src/constants/constant.js index 3a90c01c..cc316228 100644 --- a/simple-mind-map/src/constants/constant.js +++ b/simple-mind-map/src/constants/constant.js @@ -325,7 +325,9 @@ export const nodeDataNoStylePropList = [ 'uid', 'activeStyle', 'associativeLineTargets', - 'associativeLineTargetControlOffsets' + 'associativeLineTargetControlOffsets', + 'associativeLinePoint', + 'associativeLineText' ] // 数据缓存 From 1d497b2f1392712fa06d4132167cfe941fc3b19d Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Fri, 1 Sep 2023 09:53:45 +0800 Subject: [PATCH 26/38] =?UTF-8?q?Feat=EF=BC=9A=E9=BB=98=E8=AE=A4=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E5=8F=8C=E5=87=BB=E5=A4=8D=E4=BD=8D=E7=94=BB=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/constants/defaultOptions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simple-mind-map/src/constants/defaultOptions.js b/simple-mind-map/src/constants/defaultOptions.js index 9bcf33de..14f8a915 100644 --- a/simple-mind-map/src/constants/defaultOptions.js +++ b/simple-mind-map/src/constants/defaultOptions.js @@ -174,7 +174,7 @@ export const defaultOpt = { } `, // 开启鼠标双击复位思维导图位置及缩放 - enableDblclickReset: true, + enableDblclickReset: false, // 导出图片时canvas的缩放倍数,该配置会和window.devicePixelRatio值取最大值 minExportImgCanvasScale: 2, // 节点鼠标hover和激活时显示的矩形边框的颜色 From c3a0e09f6d0a675927c53070a15efc04bd7cc795 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Fri, 1 Sep 2023 10:16:21 +0800 Subject: [PATCH 27/38] =?UTF-8?q?Feat=EF=BC=9A=E6=94=AF=E6=8C=81=E5=8F=8C?= =?UTF-8?q?=E5=87=BB=E8=8A=82=E7=82=B9=E8=BF=9B=E5=85=A5=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E6=97=B6=E5=85=A8=E9=80=89=E6=96=87=E6=9C=AC?= =?UTF-8?q?=E7=9A=84=E9=85=8D=E7=BD=AE=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/constants/defaultOptions.js | 4 +++- simple-mind-map/src/core/render/TextEdit.js | 20 ++++++++++--------- simple-mind-map/src/plugins/RichText.js | 10 ++++++---- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/simple-mind-map/src/constants/defaultOptions.js b/simple-mind-map/src/constants/defaultOptions.js index 14f8a915..b9e02257 100644 --- a/simple-mind-map/src/constants/defaultOptions.js +++ b/simple-mind-map/src/constants/defaultOptions.js @@ -180,5 +180,7 @@ export const defaultOpt = { // 节点鼠标hover和激活时显示的矩形边框的颜色 hoverRectColor: 'rgb(94, 200, 248)', // 节点鼠标hover和激活时显示的矩形边框距节点内容的距离 - hoverRectPadding: 2 + hoverRectPadding: 2, + // 双击节点进入节点文本编辑时是否默认选中文本,默认只在创建新节点时会选中 + selectTextOnEnterEditText: true } diff --git a/simple-mind-map/src/core/render/TextEdit.js b/simple-mind-map/src/core/render/TextEdit.js index 33569874..fe5faf95 100644 --- a/simple-mind-map/src/core/render/TextEdit.js +++ b/simple-mind-map/src/core/render/TextEdit.js @@ -63,7 +63,7 @@ export default class TextEdit { const node = activeNodeList[0] // 当正在输入中文或英文或数字时,如果没有按下组合键,那么自动进入文本编辑模式 if (node && this.checkIsAutoEnterTextEditKey(e)) { - this.show(node) + this.show(node, e, false, true) } }) } @@ -93,7 +93,8 @@ export default class TextEdit { // 显示文本编辑框 // isInserting:是否是刚创建的节点 - async show(node, e, isInserting = false) { + // isFromKeyDown:是否是在按键事件进入的编辑 + async show(node, e, isInserting = false, isFromKeyDown = false) { // 使用了自定义节点内容那么不响应编辑事件 if (node.isUseCustomNodeContent()) { return @@ -114,10 +115,10 @@ export default class TextEdit { this.mindMap.view.translateXY(offsetLeft, offsetTop) let rect = node._textData.node.node.getBoundingClientRect() if (this.mindMap.richText) { - this.mindMap.richText.showEditText(node, rect, isInserting) + this.mindMap.richText.showEditText(node, rect, isInserting, isFromKeyDown) return } - this.showEditTextBox(node, rect, isInserting) + this.showEditTextBox(node, rect, isInserting, isFromKeyDown) } // 处理画布缩放 @@ -135,8 +136,10 @@ export default class TextEdit { } // 显示文本编辑框 - showEditTextBox(node, rect, isInserting) { + showEditTextBox(node, rect, isInserting, isFromKeyDown) { if (this.showTextEdit) return + const { nodeTextEditZIndex, textAutoWrapWidth, selectTextOnEnterEditText } = + this.mindMap.opt this.mindMap.emit('before_show_text_edit') this.registerTmpShortcut() if (!this.textEditNode) { @@ -169,15 +172,14 @@ export default class TextEdit { ) let isMultiLine = node._textData.node.attr('data-ismultiLine') === 'true' node.style.domText(this.textEditNode, scale, isMultiLine) - this.textEditNode.style.zIndex = this.mindMap.opt.nodeTextEditZIndex + this.textEditNode.style.zIndex = nodeTextEditZIndex this.textEditNode.innerHTML = textLines.join('
') 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' this.textEditNode.style.display = 'block' - this.textEditNode.style.maxWidth = - this.mindMap.opt.textAutoWrapWidth * scale + 'px' + this.textEditNode.style.maxWidth = textAutoWrapWidth * scale + 'px' if (isMultiLine && lineHeight !== 1) { this.textEditNode.style.transform = `translateY(${ -((lineHeight * fontSize - fontSize) / 2) * scale @@ -188,7 +190,7 @@ export default class TextEdit { // if (!this.cacheEditingText) { // this.selectNodeText() // } - if (isInserting) { + if (isInserting || (selectTextOnEnterEditText && !isFromKeyDown)) { this.selectNodeText() } else { this.focus() diff --git a/simple-mind-map/src/plugins/RichText.js b/simple-mind-map/src/plugins/RichText.js index 2bd229e2..8c158fba 100644 --- a/simple-mind-map/src/plugins/RichText.js +++ b/simple-mind-map/src/plugins/RichText.js @@ -152,7 +152,7 @@ class RichText { } // 显示文本编辑控件 - showEditText(node, rect, isInserting) { + showEditText(node, rect, isInserting, isFromKeyDown) { if (this.showTextEdit) { return } @@ -160,7 +160,8 @@ class RichText { richTextEditFakeInPlace, customInnerElsAppendTo, nodeTextEditZIndex, - textAutoWrapWidth + textAutoWrapWidth, + selectTextOnEnterEditText } = this.mindMap.opt this.node = node this.isInserting = isInserting @@ -246,8 +247,9 @@ class RichText { this.initQuillEditor() document.querySelector('.ql-editor').style.minHeight = originHeight + 'px' this.showTextEdit = true - // 如果是刚创建的节点,那么默认全选,否则普通激活不全选 - this.focus(isInserting ? 0 : null) + // 如果是刚创建的节点,那么默认全选,否则普通激活不全选,除非selectTextOnEnterEditText配置为true + // 在selectTextOnEnterEditText时,如果是在keydown事件进入的节点编辑,也不需要全选 + this.focus(isInserting || (selectTextOnEnterEditText && !isFromKeyDown) ? 0 : null) if (!node.nodeData.data.richText) { // 如果是非富文本的情况,需要手动应用文本样式 this.setTextStyleIfNotRichText(node) From 7f9d03c8afc9468dcf7e4f283646c4658c386224 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Fri, 1 Sep 2023 10:17:39 +0800 Subject: [PATCH 28/38] update --- simple-mind-map/src/constants/defaultOptions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simple-mind-map/src/constants/defaultOptions.js b/simple-mind-map/src/constants/defaultOptions.js index b9e02257..1f1b5709 100644 --- a/simple-mind-map/src/constants/defaultOptions.js +++ b/simple-mind-map/src/constants/defaultOptions.js @@ -182,5 +182,5 @@ export const defaultOpt = { // 节点鼠标hover和激活时显示的矩形边框距节点内容的距离 hoverRectPadding: 2, // 双击节点进入节点文本编辑时是否默认选中文本,默认只在创建新节点时会选中 - selectTextOnEnterEditText: true + selectTextOnEnterEditText: false } From 29bb27aa2332092dad074a5dbe09fbb02ce856d7 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Fri, 1 Sep 2023 10:41:36 +0800 Subject: [PATCH 29/38] =?UTF-8?q?Feat=EF=BC=9A=E4=BC=98=E5=8C=96=E9=BC=A0?= =?UTF-8?q?=E6=A0=87=E6=8C=89=E4=B8=8B=E8=8A=82=E7=82=B9=E4=BA=8B=E4=BB=B6?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8C=E5=9C=A8=E5=8F=B3=E9=94=AE=E6=8B=96?= =?UTF-8?q?=E6=8B=BD=E7=94=BB=E5=B8=83=E6=A8=A1=E5=BC=8F=E4=B8=8B=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=8F=B3=E9=94=AE=E6=8C=89=E4=BD=8F=E6=A0=B9=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E6=8B=96=E6=8B=BD=E7=94=BB=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/constants/defaultOptions.js | 6 +++++- simple-mind-map/src/core/render/node/Node.js | 21 +++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/simple-mind-map/src/constants/defaultOptions.js b/simple-mind-map/src/constants/defaultOptions.js index 1f1b5709..1945944b 100644 --- a/simple-mind-map/src/constants/defaultOptions.js +++ b/simple-mind-map/src/constants/defaultOptions.js @@ -182,5 +182,9 @@ export const defaultOpt = { // 节点鼠标hover和激活时显示的矩形边框距节点内容的距离 hoverRectPadding: 2, // 双击节点进入节点文本编辑时是否默认选中文本,默认只在创建新节点时会选中 - selectTextOnEnterEditText: false + selectTextOnEnterEditText: false, + // 拖拽模式 + // default:按住画布、根节点都可拖拽画布 + // noCanvas:按住根节点和 + dragMode: '' } diff --git a/simple-mind-map/src/core/render/node/Node.js b/simple-mind-map/src/core/render/node/Node.js index 2bc4408c..9bca88b9 100644 --- a/simple-mind-map/src/core/render/node/Node.js +++ b/simple-mind-map/src/core/render/node/Node.js @@ -394,14 +394,23 @@ class Node { this.active(e) }) this.group.on('mousedown', e => { - if (this.isRoot && e.which === 3 && !this.mindMap.opt.readonly) { - e.stopPropagation() - } - if (!this.isRoot && e.which !== 2 && !this.mindMap.opt.readonly) { - e.stopPropagation() + const { readonly, enableCtrlKeyNodeSelection, useLeftKeySelectionRightKeyDrag } = this.mindMap.opt + // 只读模式不需要阻止冒泡 + if (!readonly) { + if (this.isRoot) { + // 根节点,右键拖拽画布模式下不需要阻止冒泡 + if (e.which === 3 && !useLeftKeySelectionRightKeyDrag) { + e.stopPropagation() + } + } else { + // 非根节点,且按下的是非鼠标中键,需要阻止事件冒泡 + if (e.which !== 2) { + e.stopPropagation() + } + } } // 多选和取消多选 - if (e.ctrlKey && this.mindMap.opt.enableCtrlKeyNodeSelection) { + if (e.ctrlKey && enableCtrlKeyNodeSelection) { this.isMultipleChoice = true let isActive = this.nodeData.data.isActive if (!isActive) From c183306ab2dc7da32bd5c143269868098fc54c7f Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Fri, 1 Sep 2023 10:43:48 +0800 Subject: [PATCH 30/38] update --- simple-mind-map/src/constants/defaultOptions.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/simple-mind-map/src/constants/defaultOptions.js b/simple-mind-map/src/constants/defaultOptions.js index 1945944b..1f1b5709 100644 --- a/simple-mind-map/src/constants/defaultOptions.js +++ b/simple-mind-map/src/constants/defaultOptions.js @@ -182,9 +182,5 @@ export const defaultOpt = { // 节点鼠标hover和激活时显示的矩形边框距节点内容的距离 hoverRectPadding: 2, // 双击节点进入节点文本编辑时是否默认选中文本,默认只在创建新节点时会选中 - selectTextOnEnterEditText: false, - // 拖拽模式 - // default:按住画布、根节点都可拖拽画布 - // noCanvas:按住根节点和 - dragMode: '' + selectTextOnEnterEditText: false } From 7ccf796c34e7debd92ed1cb3ba5e8dc8a21b9d11 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Fri, 1 Sep 2023 16:33:07 +0800 Subject: [PATCH 31/38] =?UTF-8?q?Feat=EF=BC=9A=E6=96=B0=E5=A2=9E=E6=BB=9A?= =?UTF-8?q?=E5=8A=A8=E6=9D=A1=E6=8F=92=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/plugins/Scrollbar.js | 180 ++++++++++++++++++++ web/src/pages/Edit/components/Edit.vue | 12 +- web/src/pages/Edit/components/Scrollbar.vue | 147 ++++++++++++++++ 3 files changed, 336 insertions(+), 3 deletions(-) create mode 100644 simple-mind-map/src/plugins/Scrollbar.js create mode 100644 web/src/pages/Edit/components/Scrollbar.vue diff --git a/simple-mind-map/src/plugins/Scrollbar.js b/simple-mind-map/src/plugins/Scrollbar.js new file mode 100644 index 00000000..c34b9b7a --- /dev/null +++ b/simple-mind-map/src/plugins/Scrollbar.js @@ -0,0 +1,180 @@ +import { throttle } from '../utils/index' + +// 滚动条插件 +class Scrollbar { + // 构造函数 + constructor(opt) { + this.mindMap = opt.mindMap + this.scrollbarWrapSize = { + width: 0, // 水平滚动条的容器宽度 + height: 0 // 垂直滚动条的容器高度 + } + this.reset() + this.bindEvent() + } + + // 绑定事件 + bindEvent() { + this.onMousemove = this.onMousemove.bind(this) + this.onMouseup = this.onMouseup.bind(this) + this.onNodeTreeRenderEnd = this.onNodeTreeRenderEnd.bind(this) + this.onViewDataChange = throttle(this.onViewDataChange, 16, this) // 加个节流 + this.mindMap.on('mousemove', this.onMousemove) + this.mindMap.on('mouseup', this.onMouseup) + this.mindMap.on('node_tree_render_end', this.onNodeTreeRenderEnd) + this.mindMap.on('view_data_change', this.onViewDataChange) + } + + // 解绑事件 + unBindEvent() { + this.mindMap.off('mousemove', this.onMousemove) + this.mindMap.off('mouseup', this.onMouseup) + this.mindMap.off('node_tree_render_end', this.onNodeTreeRenderEnd) + this.mindMap.off('view_data_change', this.onViewDataChange) + } + + // 每次渲染后需要更新滚动条 + onNodeTreeRenderEnd() { + this.emitEvent() + } + + // 思维导图视图数据改变需要更新滚动条 + onViewDataChange() { + this.emitEvent() + } + + // 发送滚动条改变事件 + emitEvent() { + this.mindMap.emit('scrollbar_change') + } + + // 复位数据 + reset() { + // 当前拖拽的滚动条类型 + this.currentScrollType = '' + this.isMousedown = false + this.mousedownPos = { + x: 0, + y: 0 + } + this.startViewPos = { + x: 0, + y: 0 + } + // 思维导图实际高度 + this.chartHeight = 0 + this.chartWidth = 0 + } + + // 设置滚动条容器的大小,指滚动条容器的大小,对于水平滚动条,即宽度,对于垂直滚动条,即高度 + setScrollBarWrapSize(width, height) { + this.scrollbarWrapSize.width = width + this.scrollbarWrapSize.height = height + } + + // 计算滚动条大小和位置 + calculationScrollbar() { + const rect = this.mindMap.draw.rbox() + // 减去画布距离浏览器窗口左上角的距离 + const elRect = this.mindMap.elRect + rect.x -= elRect.left + rect.x2 -= elRect.left + rect.y -= elRect.top + rect.y2 -= elRect.top + + // 垂直滚动条 + const canvasHeight = this.mindMap.height // 画布高度 + const paddingY = canvasHeight / 2 // 首尾允许超出的距离,默认为高度的一半 + const chartHeight = rect.height + paddingY * 2 // 思维导图高度 + this.chartHeight = chartHeight + const chartTop = rect.y - paddingY // 思维导图顶部距画布顶部的距离 + const height = Math.min((canvasHeight / chartHeight) * 100, 100) // 滚动条高度 = 画布高度 / 思维导图高度 + let top = (-chartTop / chartHeight) * 100 // 滚动条距离 = 思维导图顶部距画布顶部的距离 / 思维导图高度 + // 判断是否到达边界 + if (top < 0) { + top = 0 + } + if (top > 100 - height) { + top = 100 - height + } + + // 水平滚动条 + const canvasWidth = this.mindMap.width + const paddingX = canvasWidth / 2 + const chartWidth = rect.width + paddingX * 2 + this.chartWidth = chartWidth + const chartLeft = rect.x - paddingX + const width = Math.min((canvasWidth / chartWidth) * 100, 100) + let left = (-chartLeft / chartWidth) * 100 + if (left < 0) { + left = 0 + } + if (left > 100 - width) { + left = 100 - width + } + + const res = { + // 垂直滚动条 + vertical: { + top, + height + }, + // 水平滚动条 + horizontal: { + left, + width + } + } + + return res + } + + onMousedown(e, type) { + e.preventDefault() + this.currentScrollType = type + this.isMousedown = true + this.mousedownPos = { + x: e.clientX, + y: e.clientY + } + // 保存视图当前的偏移量 + let transformData = this.mindMap.view.getTransformData() + this.startViewPos = { + x: transformData.state.x, + y: transformData.state.y + } + } + + onMousemove(e) { + if (!this.isMousedown) { + return + } + if (this.currentScrollType === 'vertical') { + const oy = e.clientY - this.mousedownPos.y + const oyPercentage = -oy / this.scrollbarWrapSize.height + const oyPx = oyPercentage * this.chartHeight + // 在视图最初偏移量上累加更新量 + this.mindMap.view.translateYTo(oyPx + this.startViewPos.y) + } else { + const ox = e.clientX - this.mousedownPos.x + const oxPercentage = -ox / this.scrollbarWrapSize.width + const oxPx = oxPercentage * this.chartWidth + // 在视图最初偏移量上累加更新量 + this.mindMap.view.translateXTo(oxPx + this.startViewPos.x) + } + } + + onMouseup() { + this.isMousedown = false + this.reset() + } + + // 插件被卸载前做的事情 + beforePluginDestroy() { + this.unBindEvent() + } +} + +Scrollbar.instanceName = 'scrollbar' + +export default Scrollbar diff --git a/web/src/pages/Edit/components/Edit.vue b/web/src/pages/Edit/components/Edit.vue index 1d4d6c78..15ff7891 100644 --- a/web/src/pages/Edit/components/Edit.vue +++ b/web/src/pages/Edit/components/Edit.vue @@ -22,6 +22,7 @@ +
@@ -41,6 +42,7 @@ import TouchEvent from 'simple-mind-map/src/plugins/TouchEvent.js' import NodeImgAdjust from 'simple-mind-map/src/plugins/NodeImgAdjust.js' import SearchPlugin from 'simple-mind-map/src/plugins/Search.js' import Painter from 'simple-mind-map/src/plugins/Painter.js' +import ScrollbarPlugin from 'simple-mind-map/src/plugins/Scrollbar.js' import OutlineSidebar from './OutlineSidebar' import Style from './Style' import BaseStyle from './BaseStyle' @@ -71,6 +73,7 @@ import NodeIconToolbar from './NodeIconToolbar.vue' import OutlineEdit from './OutlineEdit.vue' import { showLoading, hideLoading } from '@/utils/loading' import handleClipboardText from '@/utils/handleClipboardText' +import Scrollbar from './Scrollbar.vue' // 注册插件 MindMap.usePlugin(MiniMap) @@ -86,6 +89,7 @@ MindMap.usePlugin(MiniMap) .usePlugin(TouchEvent) .usePlugin(SearchPlugin) .usePlugin(Painter) + .usePlugin(ScrollbarPlugin) // 注册自定义主题 customThemeList.forEach(item => { @@ -117,7 +121,8 @@ export default { Search, NodeIconSidebar, NodeIconToolbar, - OutlineEdit + OutlineEdit, + Scrollbar }, data() { return { @@ -235,7 +240,7 @@ export default { storeConfig({ view: data }) - }, 1000) + }, 300) }) }, @@ -344,7 +349,8 @@ export default { 'transforming-dom-to-images', 'generalization_node_contextmenu', 'painter_start', - 'painter_end' + 'painter_end', + 'scrollbar_change' ].forEach(event => { this.mindMap.on(event, (...args) => { this.$bus.$emit(event, ...args) diff --git a/web/src/pages/Edit/components/Scrollbar.vue b/web/src/pages/Edit/components/Scrollbar.vue new file mode 100644 index 00000000..828ef680 --- /dev/null +++ b/web/src/pages/Edit/components/Scrollbar.vue @@ -0,0 +1,147 @@ + + + + + From 726460c81277a9221a155f06522479a4a1cd5e85 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Fri, 1 Sep 2023 16:36:25 +0800 Subject: [PATCH 32/38] =?UTF-8?q?Fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E5=85=A8?= =?UTF-8?q?=E9=80=89=E6=B2=A1=E6=9C=89=E8=A7=A6=E5=8F=91node=5Factive?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/core/render/Render.js | 1 + 1 file changed, 1 insertion(+) diff --git a/simple-mind-map/src/core/render/Render.js b/simple-mind-map/src/core/render/Render.js index 897e7ca9..3aa2500e 100644 --- a/simple-mind-map/src/core/render/Render.js +++ b/simple-mind-map/src/core/render/Render.js @@ -416,6 +416,7 @@ class Render { 0, 0 ) + this.mindMap.emit('node_active', null, [...this.activeNodeList]) } // 回退 From 7c470c9b882fc7d6789fb3fb89cfec045afe2869 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Fri, 1 Sep 2023 16:44:02 +0800 Subject: [PATCH 33/38] Doc: update --- README.md | 4 +++ web/src/assets/avatar/布林.jpg | Bin 0 -> 28484 bytes web/src/pages/Doc/en/introduction/index.md | 4 +++ web/src/pages/Doc/en/introduction/index.vue | 34 +++++++++++--------- web/src/pages/Doc/zh/introduction/index.md | 4 +++ web/src/pages/Doc/zh/introduction/index.vue | 34 +++++++++++--------- 6 files changed, 50 insertions(+), 30 deletions(-) create mode 100644 web/src/assets/avatar/布林.jpg diff --git a/README.md b/README.md index 227c077b..dd269b3a 100644 --- a/README.md +++ b/README.md @@ -165,4 +165,8 @@ const mindMap = new MindMap({ Luke + + + 布林 +

\ No newline at end of file diff --git a/web/src/assets/avatar/布林.jpg b/web/src/assets/avatar/布林.jpg new file mode 100644 index 0000000000000000000000000000000000000000..742bbc202edbea8f3363b2eafa53e4b5ec025b88 GIT binary patch literal 28484 zcmbTdX+TnI6fTTeSy@t<*`#!`@}!lSnPm#6<4MahPEM2)m6bVXPN?9n%q&Ye)-lsk zGN&BLoF~YU9Ex(n83kt?KxKx_-JS1#_x|~QeD}V}u-SV9YroIC*0Y|qQ1mHAVVkWj zFId8qlwh#)&>u`O2D@|)=XnPPv$28c!Cx0bc4Z^-z&kCp+}`( zuf1RO|L#>XeXsn#pZ_{g@ddVJosy}Nsj|{O*s3i`%3G8aO)vy><{G8{#;=2Z4W(7e ztJkbmS+{%(&W@>Q;WaOSz4XHU~Omb;E1_; z&B^`N?K>WKJ-q^hfur{V&7*w_SrU3)rgvhVrUa%4?LBmDj9Y1C6!o*8Upn zHmv(^Z1|sJ<9|c#*MR@8p@7~5vQ&BX>eVXH|7O+os+<3hhhi84*EYofY@@Og1SaJz zFceHS47PRq_yOIFe9?XQ3Q)=jP?70WzgNIs2S(KviEKYeCd0i%`S+sCdR zJ#pp@Y9cOT8+fYX(cm1FlRxw6-&~-*QLoJFO&1O`&*X9b1{&ZW>)l?B0xOFv`5(Q1 zn8iw5j$*BYh`Jm;IyHy8rbz+g@g1pSuw@QY7F#CRGUFE0eQ1vYhRwrERT|&MO30&& zF{sbA`o?A5%;nCiQ^=od!i7=kRbX=+Ir-Z?>Kv_e;woA2>M==(*gC0zW%H~Qu=fu6 zyAh&y9Ak`q)SLpgjID z`QhUuy5GW*UVGBpOUaKK`(7gO=Npfa*1VU7HrX8SrQRyQ!~KnGQB}|sI7s|A$yCPo z*nwR9GwySN7ga6FEa;5-7MosmBSt8V^eT`YB*d z@R{Zy>?XX4_ZU^R-rpDg@{W9*IGKovSF&Uqx12gaS8npdD^?h+8-D z&+|l%%z2b2*|nGi?k~$?D`09aGmcEpCGXc!;MpE=%r*tAy@Y>xJE&zbi9je|U;lJk zszk8|gkTjWL&OIEG{U^Q2GGhT@aGIzq->zWaJd$OYUVd9l7oi=Mtr7#aS9JaQh+l_ zl%EcdYi%8rd&{1^t41h($VQ7ve|MH4_&xKljYP2#k@^`7q=r}$GvIrJ9Lgm}`@Zi= zFROl1P5ng3!j2$J_!$jBg=!;G@XvcayBFtt^`_*zc1Z-lhMBIxUsuJLf!s5QR=~7B zF={UA34n%^lKy3MLcwe`Q9@b?7tl9|ltH&5?;f+$W3DG#m~y>cb9myu>O;5PzLQHo zBAkyM_VJwq*uA@gy&O;n>$)E23t~}aAJgE-j|$lBlxgM27I364r@m_txI=eSUafsD zT-ZorihIkJ!|2jFi|ONJ;VXDErngJ9*eFD;%qw8cJ=iS@Scc5vE%Bkn3-f^ZPX2j~Bme;`YHj9QU8Aw_EyFz}Bbj7$;q zXwGSyl7n$rWqYZfl?qCR@3DRfh(vk(q0r4kj(C20fc>5KvzhK^FQp`_jHD$8xUQvr z2I<%(hafLGvsij!`wxJjC({rqD_{ql&naLRg! zn33yxZ&z@ikUURRZMIBU2EWi&3ccf;D1qFy`320oWz+Z29~skctdX6`ONl^i=so8_ zEI{iC3PEc9%>R0a(tSPem56wyxQKc4zSQoF;8ka-Gh%}R_JPrO4?@J&S&1qe{*=Ed z@Uqmn2j8!N9h>1SFC5B}8Lg!4R=|>CzJaISf(MNlY5##agmMh>Loa%ou0n`6T?VUK zu}bIV7T^tH$`l0ZWGbid+!eY4hA>jV7}olNSK^_%sUD~J>|i+X^zmbhBF84k>@bWve=fih?J%o)4shG`Dy3M?w>Roa(uBo z^kXfpuW(wbQFmP|G~Tv6^!M61uJhSz*lB*8xj^r{ZZqtzSep>;-}pPK^B~z&dM+H@ zn5=-Y+!ZjN{I(9%@T_)=vV1nV87GK%NXRsiaZ{<58dfa|*pl&GCV$>eW^Bm880b2rFP1A{HQgw$LYcKkDvk}Bd$&<1Ro!aXM3I2JR{?u-qDS0gH}^3O zFt+fwv$5w?^4AyXAN1<1EJ#tyv)qpe55L`P1()H!{k1IC_~8?d{vzc>AYxODdl)i4 zc_ad{W4deR2Pg%vVV3Em@KQ9}+-Q!v+)KUYgFnI|&rJipKpvTK$}g!j>u;I+^E|1U zjnRVtYEFo7SFL^)=>&-%112-Fa36Nt<+u$(D8zAEs2*^&A^XbcY|X(3LYz+AX?2K>J&YmK&3^b9fGQLWxZQ ze?t2ZVRzF(-!tErJt{c6w$x%Kc8~=rl(q!H3N=(Zkn=N5<>v_wLapCvI1ISElwT%Q zd#-?8;*8;pvD#M$2`P*Yc{2o=$7C95rIUIy>wzGM7@Hr9>`bFUt1UY?nviw33-gu9 zeIr^1Q|wXDV>^hu;7YaE?9J7V;`z}U%S})8aKU^N$%&p>B#Nh9Vq77+Bx~bxW}qo{ z#lGcP$!bvOF{&7c(~PA8fyVb!{AD;dXkAa^FF=Sq&N$<*fbET-N+u5AHz8Q0kC0DZ z>W0GovVlD4W9{rCKqO9iNxhDG+n#v9E?4j8FK67zwoRNZgUp z{6Ko3%Q*LDMP}fuvl#EK>v@=3$#vH}yPtxHXR|U>#|(4WE%}p@+StWWZb+GltT-Dz zO0e8uUZ~@LEAaKQfNaTCS>)%9@8wR=2D}+&@G<(QS>y4WdEoTQy??3UVc6!Xc@SDi zE=S!9n0o+P#S=d&C(93i6Jj+C9rw*hGU7}@L$y1t{opt6S; z@r{tFp837C5N*)rk2|8MU1aZN)12X-r`FFFI;G_?($DkP4mtXIx;Xk?=s7=in982? zDmK$5*vEj#JNXcW2qCRUjc4Nkkm4;_)mh+6ElHQtWS_}j5YfQW6*CmXh>YbMMNVJc zoMv9rDw41nw-C^mINOYEHht;n!w@DPvrl#ktlLtG)vm7w}Lh|vln*vJdtU3l4lg@6&-{#1E! zgpU>_;NF5zu+Mv48`Xl;sN@!vZTd!*MTn`@FR8`?3SE63{zCzKcCnvS`H!d53FcCZ zDXRE{zj#aw&zBnFB>uamZD&#*SJsuz%g6cTJk;lJkulcfQsllugF69CZv0*5a)T*Z z0b4f}157;*9swFo2N*j?HB^{$0H2$9L#d^w(~ob{l8qJ_dCSWS_%|eY%yn91|o4<)oBH6+@-htqeG4-gOi5iMl$#W7pw^zrML>(B!Qn%_OGrLD&NO@pv64KyekDJ)v46LA;c7t_ z=1LZN-0K)4x}Ff|?y4QbGPjaiLLuD7vj^NU18j?8Z$nHEq|)VquPfmiz}J`z7X_>f zTJ$R$7LZ5iQ}PP%ZL?N@)6^okvmfYM?@p#cfxW$vZ%t~^k{uJcHp4YhIB@W1de>2a@QXeS8PnCA&jtv{`)ViG z;pJZbpP?zP)eyTnQ95%4F@<191%x`>g1^|^pCHYXpQ#|zRJHCC)Y#I?4DhN8?MURu z8Ml4XkkmDFnK0H=+L1>t&BXU!4}Z6-uyj>o>(~!^xIGVutGMG9;JhoG$_4^6*o|7 zp=CF≱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

Luke

+
+ +

布林

+
\ No newline at end of file diff --git a/web/src/pages/Doc/en/introduction/index.vue b/web/src/pages/Doc/en/introduction/index.vue index 038b9696..5e6e21e2 100644 --- a/web/src/pages/Doc/en/introduction/index.vue +++ b/web/src/pages/Doc/en/introduction/index.vue @@ -8,16 +8,16 @@

Features

    -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • jsonpngsvgpdfmarkdownxmind, support import from jsonxmindmarkdown
  • -
  • -
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • jsonpngsvgpdfmarkdownxmind, support import from jsonxmindmarkdown
  • +
  • +

Repository Catalog Introduction

1.simple-mind-map

@@ -27,16 +27,16 @@ frameworks such as Vue and React, or without a framework.

This is an online mind map built using the simple-mind-map library and based on Vue2.x and ElementUI. Features include:

    -
  • +
  • images, icons, hyperlinks, notes, tags, and summaries
  • -
  • +
  • outline, theme selection, and structure selection
  • -
  • +
  • storage by default, but it also supports creating, opening, and editing local files on the computer directly
  • -
  • +
  • and organizing layout
  • -
  • +
  • between edit and read-only modes, zooming in and out, and switching to full screen, support mini map
@@ -143,6 +143,10 @@ full screen, support mini map

Luke

+
+ +

布林

+
diff --git a/web/src/pages/Doc/zh/introduction/index.md b/web/src/pages/Doc/zh/introduction/index.md index b2394e7f..17bf4a93 100644 --- a/web/src/pages/Doc/zh/introduction/index.md +++ b/web/src/pages/Doc/zh/introduction/index.md @@ -177,4 +177,8 @@

Luke

+
+ +

布林

+
\ No newline at end of file diff --git a/web/src/pages/Doc/zh/introduction/index.vue b/web/src/pages/Doc/zh/introduction/index.vue index ecadc35f..24a656ec 100644 --- a/web/src/pages/Doc/zh/introduction/index.vue +++ b/web/src/pages/Doc/zh/introduction/index.vue @@ -8,16 +8,16 @@

特性

仓库目录介绍

1.simple-mind-map

@@ -25,11 +25,11 @@

2.web

使用simple-mind-map库,基于vue2.xElementUI搭建的在线思维导图。特性:

提供文档页面服务。

3.dist

@@ -137,6 +137,10 @@

Luke

+
+ +

布林

+
From 32c17921caf3d2b8903ae69549582513ec60548e Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Sat, 2 Sep 2023 08:31:08 +0800 Subject: [PATCH 34/38] =?UTF-8?q?Demo:=E6=94=AF=E6=8C=81=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E6=98=BE=E7=A4=BA=E6=BB=9A=E5=8A=A8=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/lang/en_us.js | 3 +- web/src/lang/zh_cn.js | 3 +- web/src/pages/Edit/components/BaseStyle.vue | 44 +++++++++++++++------ web/src/pages/Edit/components/Edit.vue | 5 ++- web/src/store.js | 4 +- 5 files changed, 41 insertions(+), 18 deletions(-) diff --git a/web/src/lang/en_us.js b/web/src/lang/en_us.js index 5302cea9..f7548a76 100644 --- a/web/src/lang/en_us.js +++ b/web/src/lang/en_us.js @@ -50,7 +50,8 @@ export default { rootStyle: 'Root Node', associativeLineText: 'Associative line text', fontFamily: 'Font family', - fontSize: 'Font size' + fontSize: 'Font size', + isShowScrollbar: 'Is show scrollbar' }, color: { moreColor: 'More color' diff --git a/web/src/lang/zh_cn.js b/web/src/lang/zh_cn.js index dca9eb6e..2d2edf05 100644 --- a/web/src/lang/zh_cn.js +++ b/web/src/lang/zh_cn.js @@ -50,7 +50,8 @@ export default { rootStyle: '根节点', associativeLineText: '关联线文字', fontFamily: '字体', - fontSize: '字号' + fontSize: '字号', + isShowScrollbar: '是否显示滚动条' }, color: { moreColor: '更多颜色' diff --git a/web/src/pages/Edit/components/BaseStyle.vue b/web/src/pages/Edit/components/BaseStyle.vue index d9705bf6..da5f0cde 100644 --- a/web/src/pages/Edit/components/BaseStyle.vue +++ b/web/src/pages/Edit/components/BaseStyle.vue @@ -753,6 +753,16 @@ + +
+
+ {{ $t('baseStyle.isShowScrollbar') }} +
+
@@ -848,7 +858,10 @@ export default { } }, updateWatermarkTimer: null, - enableNodeRichText: true + enableNodeRichText: true, + localConfigs: { + isShowScrollbar: false + } } }, computed: { @@ -894,9 +907,7 @@ export default { } }, created() { - this.enableNodeRichText = this.localConfig.openNodeRichText - this.mousewheelAction = this.localConfig.mousewheelAction - this.mousewheelZoomActionReverse = this.localConfig.mousewheelZoomActionReverse + this.initLoacalConfig() this.$bus.$on('setData', this.onSetData) }, beforeDestroy() { @@ -963,6 +974,18 @@ export default { }) }, + // 初始化本地配置 + initLoacalConfig() { + this.enableNodeRichText = this.localConfig.openNodeRichText + this.mousewheelAction = this.localConfig.mousewheelAction + this.mousewheelZoomActionReverse = this.localConfig.mousewheelZoomActionReverse + ;[ + 'isShowScrollbar' + ].forEach(key => { + this.localConfigs[key] = this.localConfig[key] + }) + }, + // 初始化水印配置 initWatermark() { let config = this.mindMap.getConfig('watermarkConfig') @@ -1038,11 +1061,7 @@ export default { }, 300) }, - /** - * @Author: 王林 - * @Date: 2021-07-03 22:08:12 - * @Desc: 设置margin - */ + // 设置margin updateMargin(type, value) { this.style[type] = value if (!this.data.theme.config[this.marginActiveTab]) { @@ -1078,12 +1097,11 @@ export default { }) }, - // 切换鼠标滚轮的行为 - mousewheelActionChange(e) { + // 本地配置 + updateLocalConfig(key, value) { this.setLocalConfig({ - mousewheelAction: e + [key]: value }) - this.mindMap.updateConfig } } } diff --git a/web/src/pages/Edit/components/Edit.vue b/web/src/pages/Edit/components/Edit.vue index 15ff7891..6eb7d9db 100644 --- a/web/src/pages/Edit/components/Edit.vue +++ b/web/src/pages/Edit/components/Edit.vue @@ -22,7 +22,7 @@ - + @@ -138,7 +138,8 @@ export default { isZenMode: state => state.localConfig.isZenMode, openNodeRichText: state => state.localConfig.openNodeRichText, useLeftKeySelectionRightKeyDrag: state => - state.localConfig.useLeftKeySelectionRightKeyDrag + state.localConfig.useLeftKeySelectionRightKeyDrag, + isShowScrollbar: state => state.localConfig.isShowScrollbar }) }, watch: { diff --git a/web/src/store.js b/web/src/store.js index 160ce417..c9808b36 100644 --- a/web/src/store.js +++ b/web/src/store.js @@ -15,7 +15,9 @@ const store = new Vuex.Store({ // 是否开启节点富文本 openNodeRichText: true, // 鼠标行为 - useLeftKeySelectionRightKeyDrag: false + useLeftKeySelectionRightKeyDrag: false, + // 是否显示滚动条 + isShowScrollbar: false }, activeSidebar: '', // 当前显示的侧边栏 isDark: false,// 是否是暗黑模式 From 075e578bb45bc27605694f0183a694124016fda9 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Sat, 2 Sep 2023 09:53:05 +0800 Subject: [PATCH 35/38] =?UTF-8?q?Feat:=E6=94=AF=E6=8C=81=E5=9C=A8url?= =?UTF-8?q?=E4=B8=AD=E9=80=9A=E8=BF=87fileURL=E6=9F=A5=E8=AF=A2=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E6=89=93=E5=BC=80=E6=8C=87=E5=AE=9A=E7=9A=84=E5=9C=A8?= =?UTF-8?q?=E7=BA=BF=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/pages/Edit/components/Edit.vue | 25 +++++++++++++++++++ web/src/pages/Edit/components/Import.vue | 31 ++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/web/src/pages/Edit/components/Edit.vue b/web/src/pages/Edit/components/Edit.vue index 6eb7d9db..d4141aed 100644 --- a/web/src/pages/Edit/components/Edit.vue +++ b/web/src/pages/Edit/components/Edit.vue @@ -74,6 +74,7 @@ import OutlineEdit from './OutlineEdit.vue' import { showLoading, hideLoading } from '@/utils/loading' import handleClipboardText from '@/utils/handleClipboardText' import Scrollbar from './Scrollbar.vue' +import exampleData from 'simple-mind-map/example/exampleData' // 注册插件 MindMap.usePlugin(MiniMap) @@ -261,7 +262,20 @@ export default { * @Desc: 初始化 */ init() { + let hasFileURL = this.hasFileURL() let { root, layout, theme, view, config } = this.mindMapData + // 如果url中存在要打开的文件,那么思维导图数据、主题、布局都使用默认的 + if (hasFileURL) { + root = { + "data": { + "text": "根节点" + }, + "children": [] + } + layout = exampleData.layout + theme = exampleData.theme + view = null + } this.mindMap = new MindMap({ el: this.$refs.mindMapContainer, data: root, @@ -376,6 +390,17 @@ export default { if (window.takeOverApp) { this.$bus.$emit('app_inited', this.mindMap) } + // 解析url中的文件 + if (hasFileURL) { + this.$bus.$emit('handle_file_url') + } + }, + + // url中是否存在要打开的文件 + hasFileURL() { + const fileURL = this.$route.query.fileURL + if (!fileURL) return false + return /\.(smm|json|xmind|md|xlsx)$/.test(fileURL) }, /** diff --git a/web/src/pages/Edit/components/Import.vue b/web/src/pages/Edit/components/Import.vue index d2d865e1..1693ccbb 100644 --- a/web/src/pages/Edit/components/Import.vue +++ b/web/src/pages/Edit/components/Import.vue @@ -59,15 +59,46 @@ export default { }, created() { this.$bus.$on('showImport', this.handleShowImport) + this.$bus.$on('handle_file_url', this.handleFileURL) }, beforeDestroy() { this.$bus.$off('showImport', this.handleShowImport) + this.$bus.$off('handle_file_url', this.handleFileURL) }, methods: { handleShowImport() { this.dialogVisible = true }, + // 检查url中是否操作需要打开的文件 + async handleFileURL() { + try { + const fileURL = this.$route.query.fileURL + if (!fileURL) return + const macth = /\.(smm|json|xmind|md|xlsx)$/.exec(fileURL) + if (!macth) { + return + } + const type = macth[1] + const res = await fetch(fileURL) + const file = await res.blob() + const data = { + raw: file + } + if (type === 'smm' || type === 'json') { + this.handleSmm(data) + } else if (type === 'xmind') { + this.handleXmind(data) + } else if (type === 'xlsx') { + this.handleExcel(data) + } else if (type === 'md') { + this.handleMd(data) + } + } catch (error) { + console.log(error) + } + }, + /** * @Author: 王林 * @Date: 2021-08-03 22:48:42 From 5d1f460a94fff36065f124303d8e7daecfa3e697 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Sat, 2 Sep 2023 20:58:20 +0800 Subject: [PATCH 36/38] =?UTF-8?q?Feat:=E9=B1=BC=E9=AA=A8=E5=9B=BE=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E8=AE=BE=E7=BD=AE=E8=8A=82=E7=82=B9margin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/src/layouts/Fishbone.js | 22 +++++++++------ simple-mind-map/src/layouts/fishboneUtils.js | 29 ++++++++++++-------- simple-mind-map/src/themes/classic.js | 1 + 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/simple-mind-map/src/layouts/Fishbone.js b/simple-mind-map/src/layouts/Fishbone.js index b11c2634..0706caba 100644 --- a/simple-mind-map/src/layouts/Fishbone.js +++ b/simple-mind-map/src/layouts/Fishbone.js @@ -56,10 +56,11 @@ class Fishbone extends Base { } // 计算二级节点的top值 if (parent._node.isRoot) { + let marginY = this.getMarginY(layerIndex) if (this.checkIsTop(newNode)) { - newNode.top = parent._node.top - newNode.height + newNode.top = parent._node.top - newNode.height - marginY } else { - newNode.top = parent._node.top + parent._node.height + newNode.top = parent._node.top + parent._node.height + marginY } } } @@ -80,15 +81,16 @@ class Fishbone extends Base { null, (node, parent, isRoot, layerIndex) => { if (node.isRoot) { - let topTotalLeft = node.left + node.width + node.height - let bottomTotalLeft = node.left + node.width + node.height + let marginX = this.getMarginX(layerIndex + 1) + let topTotalLeft = node.left + node.width + node.height + marginX + let bottomTotalLeft = node.left + node.width + node.height + marginX node.children.forEach(item => { if (this.checkIsTop(item)) { item.left = topTotalLeft - topTotalLeft += item.width + topTotalLeft += item.width + marginX } else { item.left = bottomTotalLeft + 20 - bottomTotalLeft += item.width + bottomTotalLeft += item.width + marginX } }) } @@ -154,9 +156,10 @@ class Fishbone extends Base { getNodeAreaHeight(node) { let totalHeight = 0 let loop = node => { + let marginY = this.getMarginY(node.layerIndex) totalHeight += node.height + - (this.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) + (this.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) + marginY if (node.children.length) { node.children.forEach(item => { loop(item) @@ -244,8 +247,9 @@ class Fishbone extends Base { maxx = item.left } // 水平线段到二级节点的连线 + let marginY = this.getMarginY(item.layerIndex) let nodeLineX = item.left - let offset = node.height / 2 + let offset = node.height / 2 + marginY let offsetX = offset / Math.tan(degToRad(this.mindMap.opt.fishboneDeg)) let line = this.draw.path() if (this.checkIsTop(item)) { @@ -267,7 +271,7 @@ class Fishbone extends Base { }) // 从根节点出发的水平线 let nodeHalfTop = node.top + node.height / 2 - let offset = node.height / 2 + let offset = node.height / 2 + this.getMarginY(node.layerIndex + 1) let line = this.draw.path() line.plot( `M ${node.left + node.width},${nodeHalfTop} L ${ diff --git a/simple-mind-map/src/layouts/fishboneUtils.js b/simple-mind-map/src/layouts/fishboneUtils.js index ec47f1e5..e95e2ec2 100644 --- a/simple-mind-map/src/layouts/fishboneUtils.js +++ b/simple-mind-map/src/layouts/fishboneUtils.js @@ -47,30 +47,32 @@ export default { computedLeftTopValue({ layerIndex, node, ctx }) { if (layerIndex >= 1 && node.children) { // 遍历三级及以下节点的子节点 + let marginY = ctx.getMarginY(layerIndex + 1) let startLeft = node.left + node.width * ctx.childIndent let totalTop = node.top + node.height + - (ctx.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) + (ctx.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) + marginY node.children.forEach(item => { item.left = startLeft item.top += totalTop totalTop += item.height + - (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + marginY }) } }, - adjustLeftTopValueBefore({ node, parent, ctx }) { + adjustLeftTopValueBefore({ node, parent, ctx, layerIndex }) { // 调整top let len = node.children.length + let marginY = ctx.getMarginY(layerIndex + 1) // 调整三级及以下节点的top if (parent && !parent.isRoot && len > 0) { let totalHeight = node.children.reduce((h, item) => { return ( h + item.height + - (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + marginY ) }, 0) ctx.updateBrothersTop(node, totalHeight) @@ -80,7 +82,8 @@ export default { // 将二级节点的子节点移到上方 if (parent && parent.isRoot) { // 遍历二级节点的子节点 - let totalHeight = node.expandBtnSize + let marginY = ctx.getMarginY(node.layerIndex + 1) + let totalHeight = node.expandBtnSize + marginY node.children.forEach(item => { // 调整top let nodeTotalHeight = ctx.getNodeAreaHeight(item) @@ -134,13 +137,14 @@ export default { } }, computedLeftTopValue({ layerIndex, node, ctx }) { + let marginY = ctx.getMarginY(layerIndex + 1) if (layerIndex === 1 && node.children) { // 遍历二级节点的子节点 let startLeft = node.left + node.width * ctx.childIndent let totalTop = node.top + node.height + - (ctx.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) + (ctx.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) + marginY node.children.forEach(item => { item.left = startLeft @@ -149,7 +153,7 @@ export default { (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) totalTop += item.height + - (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + marginY }) } if (layerIndex > 1 && node.children) { @@ -157,25 +161,26 @@ export default { let startLeft = node.left + node.width * ctx.childIndent let totalTop = node.top - - (ctx.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) + (ctx.getNodeActChildrenLength(node) > 0 ? node.expandBtnSize : 0) - marginY node.children.forEach(item => { item.left = startLeft item.top = totalTop - item.height totalTop -= item.height + - (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + marginY }) } }, adjustLeftTopValueBefore({ node, ctx, layerIndex }) { // 调整top + let marginY = ctx.getMarginY(layerIndex + 1) let len = node.children.length if (layerIndex > 2 && len > 0) { let totalHeight = node.children.reduce((h, item) => { return ( h + item.height + - (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + (ctx.getNodeActChildrenLength(item) > 0 ? item.expandBtnSize : 0) + marginY ) }, 0) ctx.updateBrothersTop(node, -totalHeight) @@ -185,6 +190,7 @@ export default { // 将二级节点的子节点移到上方 if (parent && parent.isRoot) { // 遍历二级节点的子节点 + let marginY = ctx.getMarginY(node.layerIndex + 1) let totalHeight = 0 let totalHeight2 = node.expandBtnSize node.children.forEach(item => { @@ -192,11 +198,12 @@ export default { let hasChildren = ctx.getNodeActChildrenLength(item) > 0 let nodeTotalHeight = ctx.getNodeAreaHeight(item) let offset = - hasChildren > 0 + hasChildren ? nodeTotalHeight - item.height - (hasChildren ? item.expandBtnSize : 0) : 0 + offset -= (hasChildren ? marginY : 0) let _top = totalHeight + offset let _left = item.left item.top += _top diff --git a/simple-mind-map/src/themes/classic.js b/simple-mind-map/src/themes/classic.js index e58a7d3d..792026cf 100644 --- a/simple-mind-map/src/themes/classic.js +++ b/simple-mind-map/src/themes/classic.js @@ -18,6 +18,7 @@ export default merge(defaultTheme, { 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowQzg5QTQ0NDhENzgxMUUzOENGREE4QTg0RDgzRTZDNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowQzg5QTQ0NThENzgxMUUzOENGREE4QTg0RDgzRTZDNyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkMwOEQ1NDRGOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMwOEQ1NDUwOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+e9P33AAAACVJREFUeNpisXJ0YUACTAyoAMr/+eM7EGGRZ4FQ7BycEAZAgAEAHbEGtkoQm/wAAAAASUVORK5CYII=', // 背景重复 backgroundRepeat: 'repeat', + backgroundSize: 'auto', // 根节点样式 root: { fillColor: 'rgb(233, 223, 152)', From a4611d11a8926f72347a62a179b397659b30bdb7 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Mon, 4 Sep 2023 16:49:24 +0800 Subject: [PATCH 37/38] Doc: update --- README.md | 4 + web/src/assets/avatar/南风.jpg | Bin 0 -> 34225 bytes web/src/pages/Doc/catalogList.js | 3 +- web/src/pages/Doc/en/changelog/index.md | 50 + web/src/pages/Doc/en/changelog/index.vue | 31 + web/src/pages/Doc/en/constructor/index.md | 14 +- web/src/pages/Doc/en/constructor/index.vue | 1065 +------------------ web/src/pages/Doc/en/doExport/index.md | 8 +- web/src/pages/Doc/en/doExport/index.vue | 10 +- web/src/pages/Doc/en/introduction/index.md | 4 + web/src/pages/Doc/en/introduction/index.vue | 4 + web/src/pages/Doc/en/node/index.md | 6 +- web/src/pages/Doc/en/node/index.vue | 8 +- web/src/pages/Doc/en/scrollbar/index.md | 64 ++ web/src/pages/Doc/en/scrollbar/index.vue | 70 ++ web/src/pages/Doc/en/search/index.md | 2 +- web/src/pages/Doc/en/search/index.vue | 2 +- web/src/pages/Doc/routerList.js | 8 +- web/src/pages/Doc/zh/changelog/index.md | 50 + web/src/pages/Doc/zh/changelog/index.vue | 31 + web/src/pages/Doc/zh/constructor/index.md | 14 +- web/src/pages/Doc/zh/constructor/index.vue | 52 +- web/src/pages/Doc/zh/course23/index.md | 116 ++ web/src/pages/Doc/zh/course23/index.vue | 112 ++ web/src/pages/Doc/zh/course24/index.md | 65 ++ web/src/pages/Doc/zh/course24/index.vue | 60 ++ web/src/pages/Doc/zh/course3/index.md | 22 + web/src/pages/Doc/zh/course3/index.vue | 15 + web/src/pages/Doc/zh/doExport/index.md | 8 +- web/src/pages/Doc/zh/doExport/index.vue | 10 +- web/src/pages/Doc/zh/introduction/index.md | 4 + web/src/pages/Doc/zh/introduction/index.vue | 4 + web/src/pages/Doc/zh/node/index.md | 6 +- web/src/pages/Doc/zh/node/index.vue | 4 +- web/src/pages/Doc/zh/scrollbar/index.md | 64 ++ web/src/pages/Doc/zh/scrollbar/index.vue | 70 ++ 36 files changed, 966 insertions(+), 1094 deletions(-) create mode 100644 web/src/assets/avatar/南风.jpg create mode 100644 web/src/pages/Doc/en/scrollbar/index.md create mode 100644 web/src/pages/Doc/en/scrollbar/index.vue create mode 100644 web/src/pages/Doc/zh/course23/index.md create mode 100644 web/src/pages/Doc/zh/course23/index.vue create mode 100644 web/src/pages/Doc/zh/course24/index.md create mode 100644 web/src/pages/Doc/zh/course24/index.vue create mode 100644 web/src/pages/Doc/zh/scrollbar/index.md create mode 100644 web/src/pages/Doc/zh/scrollbar/index.vue diff --git a/README.md b/README.md index dd269b3a..04935180 100644 --- a/README.md +++ b/README.md @@ -169,4 +169,8 @@ const mindMap = new MindMap({ 布林 + + + 南风 +

\ No newline at end of file diff --git a/web/src/assets/avatar/南风.jpg b/web/src/assets/avatar/南风.jpg new file mode 100644 index 0000000000000000000000000000000000000000..562987e90f6a08462e766be663b67300bd842b59 GIT binary patch literal 34225 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 { +let CourseList = new Array(24).fill(0).map((_, index) => { return 'course' + (index + 1) }) let APIList = [ @@ -34,6 +34,7 @@ let APIList = [ 'nodeImgAdjust', 'search', 'painter', + 'scrollbar', 'xmind', 'markdown', 'utils' diff --git a/web/src/pages/Doc/en/changelog/index.md b/web/src/pages/Doc/en/changelog/index.md index 2122c69f..19a4a42d 100644 --- a/web/src/pages/Doc/en/changelog/index.md +++ b/web/src/pages/Doc/en/changelog/index.md @@ -1,5 +1,55 @@ # Changelog +## 0.7.0 + +Breaking change: Removed the section of node activation style in the theme file, Setting the activation style of nodes is no longer supported, and the activation effect has been changed to a unified node outer border style, while also supporting the mouse hover effect. + +Fix: + +> 1.Fix rendering anomalies when the node border size is relatively large. +> +> 2.Fixed an issue where the node style of the associated line will not be updated when switching themes. +> +> 3.Fix that selecting all did not trigger node_ The issue with active events. + +新增: + +> 1.When folding nodes, displays the number of collapsed nodes. +> +> 2.Support the position of the endpoint of the associated line to follow mouse drag changes. +> +> 3.Add a scrollbar plugin. +> +> 4.Support opening specified online files through fileURL query parameters in URLs. +> +> 5.The fishbone diagram supports setting node margins. +> +> 6.By default, double-click to reset the canvas. +> +> 7.Modify the parameters of the export image method, and when exporting PDF, if the size of the mind map is smaller than A4 paper, do not rotate the direction. +> +> 8.Improve the clarity of exported images and PDFs on high-definition screens. +> +> 9.Add a pre destruction lifecycle function to the plugin to address the issue of some side effects that were not cleared during the destruction of the mind map. +> +> 10.Optimize the settings of the basic style and do not trigger full rendering when modifying theme attributes that do not affect size. +> +> 11.Prohibit triggering node right-click menu events when multiple node selections are completed, to avoid triggering the right-click menu display. +> +> 12.Optimize the Select plugin so that if multiple selected nodes do not change, the activation event is not triggered. +> +> 13.The activation node list thrown by event node_active no longer directly references the internal activation list. +> +> 14.Optimize the logic of mouse button down node events, and support dragging the canvas by holding down the root node with the right mouse button in the right-click drag and drop canvas mode. + +Demo: + +> 1.Do not directly reference the internal activation node list to optimize performance. +> +> 2.Support configuring whether to display scrollbars. +> +> 3.Delete the active node configuration in the sidebar node style configuration section. + ## 0.6.17 Fix: diff --git a/web/src/pages/Doc/en/changelog/index.vue b/web/src/pages/Doc/en/changelog/index.vue index e0c5e977..cdcf02fe 100644 --- a/web/src/pages/Doc/en/changelog/index.vue +++ b/web/src/pages/Doc/en/changelog/index.vue @@ -1,6 +1,37 @@ diff --git a/web/src/pages/Doc/en/node/index.md b/web/src/pages/Doc/en/node/index.md index 74d54d3a..60bd459e 100644 --- a/web/src/pages/Doc/en/node/index.md +++ b/web/src/pages/Doc/en/node/index.md @@ -110,17 +110,21 @@ Get the final style value applied to this node `root`: whether it is the root node, default `false` -`isActive`: whether the value being fetched is the active state style value, +`isActive`: v0.7.0+has been abandoned, whether the value being fetched is the active state style value, default `false` ### setStyle(prop, value, isActive) +- `isActive`: v0.7.0+has been abandoned + Modify a style of the node, a shortcut method for the `SET_NODE_STYLE` command ### setStyles(style, isActive) > v0.6.12+ +- `isActive`: v0.7.0+has been abandoned + Modify multiple styles of nodes, a shortcut method for the `SET_NODE_STYLES` command ### getData(key) diff --git a/web/src/pages/Doc/en/node/index.vue b/web/src/pages/Doc/en/node/index.vue index eb479570..fc5f1ed9 100644 --- a/web/src/pages/Doc/en/node/index.vue +++ b/web/src/pages/Doc/en/node/index.vue @@ -64,14 +64,20 @@

Get the final style value applied to this node

prop: the style property to get

root: whether it is the root node, default false

-

isActive: whether the value being fetched is the active state style value, +

isActive: v0.7.0+has been abandoned, whether the value being fetched is the active state style value, default false

setStyle(prop, value, isActive)

+
    +
  • isActive: v0.7.0+has been abandoned
  • +

Modify a style of the node, a shortcut method for the SET_NODE_STYLE command

setStyles(style, isActive)

v0.6.12+

+
    +
  • isActive: v0.7.0+has been abandoned
  • +

Modify multiple styles of nodes, a shortcut method for the SET_NODE_STYLES command

getData(key)

Get the specified value in the data object of the node's real data nodeData, diff --git a/web/src/pages/Doc/en/scrollbar/index.md b/web/src/pages/Doc/en/scrollbar/index.md new file mode 100644 index 00000000..10974f51 --- /dev/null +++ b/web/src/pages/Doc/en/scrollbar/index.md @@ -0,0 +1,64 @@ +# Scrollbar plugin + +> v0.7.0+ + +This plugin is used to help develop the functionality of horizontal and vertical scrollbar. + +## Register + +```js +import MindMap from 'simple-mind-map' +import Scrollbar from 'simple-mind-map/src/plugins/Scrollbar.js' +MindMap.usePlugin(Scrollbar) +``` + +After registration and instantiation of `MindMap`, the instance can be obtained through `mindMap.scrollbar`. + +## Event + +#### scrollbar_change + +Triggered when the scrollbar data changes, you can listen to this event to update the position and size of the scrollbar. + +## Method + +### setScrollBarWrapSize(width, height) + +- `width`: Number, The width of your scrollbar container element. + +- `height`: Number, The height of your scrollbar container element. + +Set the size of the scroll bar container, which is the width of the container for horizontal scrollbars and the height of the container for vertical scrollbars. When your scrollbar container size changes, you need to call this method again. + +### calculationScrollbar() + +> You need to first call the setScrollBarWrapSize method to set the width and height of the scroll bar container element. +> +> Generally, it is necessary to monitor scrollbar_change event, and then call it to update the scroll bar. + +Return value: + +```js +{ + // Vertical scrollbar + vertical: { + top, + height + }, + // Horizontal scrollbar + horizontal: { + left, + width + } +} +``` + +Obtain the size and position of the scroll bar, and you can set it to the scroll bar element based on the return value to achieve the effect of rendering and caring about the scroll bar. + +### onMousedown(e, type) + +- `e`: The event object for the mouse down event. + +- `type`: The type of scroll bar pressed, vertical(Vertical scrollbar)、horizontal(Horizontal scrollbar)。 + +This method needs to be called when the mouse press event of the scrollbar element occurs. \ No newline at end of file diff --git a/web/src/pages/Doc/en/scrollbar/index.vue b/web/src/pages/Doc/en/scrollbar/index.vue new file mode 100644 index 00000000..d5851f33 --- /dev/null +++ b/web/src/pages/Doc/en/scrollbar/index.vue @@ -0,0 +1,70 @@ + + + + + \ No newline at end of file diff --git a/web/src/pages/Doc/en/search/index.md b/web/src/pages/Doc/en/search/index.md index 215917f6..8ba3235b 100644 --- a/web/src/pages/Doc/en/search/index.md +++ b/web/src/pages/Doc/en/search/index.md @@ -12,7 +12,7 @@ import Search from 'simple-mind-map/src/plugins/Search.js' MindMap.usePlugin(Search) ``` -After registration and instantiation of `MindMap`, the instance can be obtained through `mindMap.Search`. +After registration and instantiation of `MindMap`, the instance can be obtained through `mindMap.search`. ## Event diff --git a/web/src/pages/Doc/en/search/index.vue b/web/src/pages/Doc/en/search/index.vue index c47b9d40..26f7d4e9 100644 --- a/web/src/pages/Doc/en/search/index.vue +++ b/web/src/pages/Doc/en/search/index.vue @@ -10,7 +10,7 @@ import Search from 'simple-mind-map/src/plugins/Search.js' MindMap.usePlugin(Search) -

After registration and instantiation of MindMap, the instance can be obtained through mindMap.Search.

+

After registration and instantiation of MindMap, the instance can be obtained through mindMap.search.

Event

search_info_change

You can listen to 'search_info_change' event to get the number of current search results and the index currently located.

diff --git a/web/src/pages/Doc/routerList.js b/web/src/pages/Doc/routerList.js index 578b9aa2..88f8964b 100644 --- a/web/src/pages/Doc/routerList.js +++ b/web/src/pages/Doc/routerList.js @@ -29,6 +29,8 @@ export default [ { path: 'course20', title: '如何自定义节点内容' }, { path: 'course21', title: '如何复制、剪切、粘贴' }, { path: 'course22', title: '如何实现搜索、替换' }, + { path: 'course23', title: '如何渲染滚动条' }, + { path: 'course24', title: '如何开发一个插件' }, { path: 'doExport', title: 'Export 插件' }, { path: 'drag', title: 'Drag插件' }, { path: 'introduction', title: '简介' }, @@ -52,7 +54,8 @@ export default [ { path: 'nodeImgAdjust', title: 'NodeImgAdjust插件' }, { path: 'search', title: 'Search插件' }, { path: 'painter', title: 'Painter插件' }, - { path: 'help1', title: '概要/关联线' }, + { path: 'painter', title: 'Painter插件' }, + { path: 'scrollbar', title: 'Scrollbar插件' }, { path: 'help2', title: '客户端' } ] }, @@ -85,7 +88,8 @@ export default [ { path: 'touchEvent', title: 'TouchEvent plugin' }, { path: 'nodeImgAdjust', title: 'NodeImgAdjust plugin' }, { path: 'search', title: 'Search plugin' }, - { path: 'painter', title: 'Painter plugin' } + { path: 'painter', title: 'Painter plugin' }, + { path: 'scrollbar', title: 'Scrollbar plugin' } ] } ] diff --git a/web/src/pages/Doc/zh/changelog/index.md b/web/src/pages/Doc/zh/changelog/index.md index 213a6609..32722852 100644 --- a/web/src/pages/Doc/zh/changelog/index.md +++ b/web/src/pages/Doc/zh/changelog/index.md @@ -1,5 +1,55 @@ # Changelog +## 0.7.0 + +破坏性更新:删除了主题文件中节点激活样式的部分,不再支持设置节点的激活样式,激活效果改为统一的节点外边框样式,同时支持鼠标hover效果。 + +修复: + +> 1.修复节点边框尺寸比较大的情况下的的渲染异常问题。 +> +> 2.修复切换主题时存在关联线的节点样式不会更新的问题。 +> +> 3.修复全选没有触发node_active事件的问题。 + +新增: + +> 1.收起节点时,显示折叠的节点数量。 +> +> 2.支持关联线端点的位置跟随鼠标拖拽变化。 +> +> 3.新增滚动条插件。 +> +> 4.支持在url中通过fileURL查询参数打开指定的在线文件。 +> +> 5.鱼骨图支持设置节点margin。 +> +> 6.默认关闭双击复位画布。 +> +> 7.修改导出图片方法的参数,导出pdf时如果思维导图尺寸小于a4纸那么不旋转方向。 +> +> 8.提升导出的图片和pdf在高清屏的清晰度。 +> +> 9.插件新增销毁前生命周期函数,解决销毁思维导图时插件的一些副作用没有清除的问题。 +> +> 10.优化基础样式的设置,修改不影响大小的主题属性时不触发全量渲染。 +> +> 11.右键多选节点结束时禁止触发节点右键菜单事件,避免触发右键菜单显示。 +> +> 12.优化Select插件,如果多选节点没有变化,那么不触发激活激活事件。 +> +> 13.node_active事件抛出的激活节点列表不再直接引用内部激活列表。 +> +> 14.优化鼠标按下节点事件逻辑,在右键拖拽画布模式下支持右键按住根节点拖拽画布。 + +Demo: + +> 1.不直接引用内部激活节点列表,优化性能。 +> +> 2.支持配置是否显示滚动条。 +> +> 3.删除侧边栏节点样式配置部分的激活节点配置。 + ## 0.6.17 修复: diff --git a/web/src/pages/Doc/zh/changelog/index.vue b/web/src/pages/Doc/zh/changelog/index.vue index a98e89cd..73cfeb0f 100644 --- a/web/src/pages/Doc/zh/changelog/index.vue +++ b/web/src/pages/Doc/zh/changelog/index.vue @@ -1,6 +1,37 @@ diff --git a/web/src/pages/Doc/zh/node/index.md b/web/src/pages/Doc/zh/node/index.md index dd7ea6a9..67706eb7 100644 --- a/web/src/pages/Doc/zh/node/index.md +++ b/web/src/pages/Doc/zh/node/index.md @@ -110,16 +110,20 @@ `root`:是否是根节点,默认`false` -`isActive`:获取的是否是激活状态的样式值,默认`false` +`isActive`:v0.7.0+已废弃,获取的是否是激活状态的样式值,默认`false` ### setStyle(prop, value, isActive) +`isActive`:v0.7.0+已废弃 + 修改节点的某个样式,`SET_NODE_STYLE`命令的快捷方法 ### setStyles(style, isActive) > v0.6.12+ +`isActive`:v0.7.0+已废弃 + 修改节点多个样式,`SET_NODE_STYLES`命令的快捷方法 ### getData(key) diff --git a/web/src/pages/Doc/zh/node/index.vue b/web/src/pages/Doc/zh/node/index.vue index ffc2a5cc..8642a410 100644 --- a/web/src/pages/Doc/zh/node/index.vue +++ b/web/src/pages/Doc/zh/node/index.vue @@ -64,13 +64,15 @@

获取某个最终应用到该节点的样式值

prop:要获取的样式属性

root:是否是根节点,默认false

-

isActive:获取的是否是激活状态的样式值,默认false

+

isActive:v0.7.0+已废弃,获取的是否是激活状态的样式值,默认false

setStyle(prop, value, isActive)

+

isActive:v0.7.0+已废弃

修改节点的某个样式,SET_NODE_STYLE命令的快捷方法

setStyles(style, isActive)

v0.6.12+

+

isActive:v0.7.0+已废弃

修改节点多个样式,SET_NODE_STYLES命令的快捷方法

getData(key)

获取该节点真实数据nodeDatadata对象里的指定值,key不传返回这个data对象

diff --git a/web/src/pages/Doc/zh/scrollbar/index.md b/web/src/pages/Doc/zh/scrollbar/index.md new file mode 100644 index 00000000..49a20d1e --- /dev/null +++ b/web/src/pages/Doc/zh/scrollbar/index.md @@ -0,0 +1,64 @@ +# Scrollbar 插件 + +> v0.7.0+ + +该插件用于帮助开发水平和垂直滚动条的功能。 + +## 注册 + +```js +import MindMap from 'simple-mind-map' +import Scrollbar from 'simple-mind-map/src/plugins/Scrollbar.js' +MindMap.usePlugin(Scrollbar) +``` + +注册完且实例化`MindMap`后可通过`mindMap.scrollbar`获取到该实例。 + +## 事件 + +#### scrollbar_change + +当滚动条数据发生改变时触发,你可以监听该事件来更新滚动条位置和大小。 + +## 方法 + +### setScrollBarWrapSize(width, height) + +- `width`:Number,你的滚动条容器元素的宽度。 + +- `height`: Number,你的滚动条容器元素的高度。 + +设置滚动条容器的大小,对于水平滚动条,即容器的宽度,对于垂直滚动条,即容器的高度。当你的滚动条容器尺寸改变时需要再次调用该方法。 + +### calculationScrollbar() + +> 需要先调用setScrollBarWrapSize方法设置滚动条容器元素的宽高。 +> +> 一般需要监听scrollbar_change事件,然后调用该方法更新滚动条。 + +返回值: + +```js +{ + // 垂直滚动条 + vertical: { + top, + height + }, + // 水平滚动条 + horizontal: { + left, + width + } +} +``` + +获取滚动条大小和位置,你可以根据返回值来设置到滚动条元素上,达到渲染和关心滚动条的效果。 + +### onMousedown(e, type) + +- `e`:鼠标按下事件的事件对象。 + +- `type`:按下的滚动条类型,vertical(垂直滚动条)、horizontal(水平滚动条)。 + +滚动条元素的鼠标按下事件时需要调用该方法。 \ No newline at end of file diff --git a/web/src/pages/Doc/zh/scrollbar/index.vue b/web/src/pages/Doc/zh/scrollbar/index.vue new file mode 100644 index 00000000..fc06ba0f --- /dev/null +++ b/web/src/pages/Doc/zh/scrollbar/index.vue @@ -0,0 +1,70 @@ + + + + + \ No newline at end of file From 063af62cf69ad6796157216d08da08e76d23a733 Mon Sep 17 00:00:00 2001 From: wanglin2 <1013335014@qq.com> Date: Mon, 4 Sep 2023 16:58:57 +0800 Subject: [PATCH 38/38] =?UTF-8?q?=E6=89=93=E5=8C=850.7.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 4 ++-- simple-mind-map/full.js | 2 ++ simple-mind-map/package.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index e5fe4b27..88608025 100644 --- a/index.html +++ b/index.html @@ -1,7 +1,7 @@ 思绪思维导图
\ No newline at end of file + } \ No newline at end of file diff --git a/simple-mind-map/full.js b/simple-mind-map/full.js index 3d140c83..8ee27854 100644 --- a/simple-mind-map/full.js +++ b/simple-mind-map/full.js @@ -13,6 +13,7 @@ import NodeImgAdjust from './src/plugins/NodeImgAdjust.js' import TouchEvent from './src/plugins/TouchEvent.js' import Search from './src/plugins/Search.js' import Painter from './src/plugins/Painter.js' +import Scrollbar from './src/plugins/Scrollbar.js' import xmind from './src/parse/xmind.js' import markdown from './src/parse/markdown.js' import icons from './src/svg/icons.js' @@ -42,5 +43,6 @@ MindMap .usePlugin(NodeImgAdjust) .usePlugin(Search) .usePlugin(Painter) + .usePlugin(Scrollbar) export default MindMap \ No newline at end of file diff --git a/simple-mind-map/package.json b/simple-mind-map/package.json index 3f8a51d7..30feca85 100644 --- a/simple-mind-map/package.json +++ b/simple-mind-map/package.json @@ -1,6 +1,6 @@ { "name": "simple-mind-map", - "version": "0.6.17", + "version": "0.7.0", "description": "一个简单的web在线思维导图", "authors": [ {