From 7a977d74dcdaa40725393aa89274e0e9d6066395 Mon Sep 17 00:00:00 2001 From: wanglin <1013335014@qq.com> Date: Mon, 28 Jun 2021 07:46:12 +0800 Subject: [PATCH] =?UTF-8?q?=E6=97=A5=E5=B8=B8=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simple-mind-map/example/exampleData.js | 195 +++++++++++-- simple-mind-map/index.js | 16 +- simple-mind-map/src/BatchExecution.js | 85 ++++++ simple-mind-map/src/Node.js | 30 +- simple-mind-map/src/Render.js | 16 +- simple-mind-map/src/TextEdit.js | 1 + .../src/assets/icon_newcreat_classify.png | Bin 0 -> 2651 bytes .../src/assets/icon_newcreat_mindmap.png | Bin 0 -> 2328 bytes .../src/assets/icon_newcreat_structure.png | Bin 0 -> 2274 bytes .../src/assets/icon_newcreat_theright.png | Bin 0 -> 1943 bytes simple-mind-map/src/layouts/Base.js | 43 +++ simple-mind-map/src/layouts/BubbleChart.js | 186 ------------ .../src/layouts/CatalogOrganization.js | 266 ++++++++++-------- .../src/layouts/LogicalStructure.js | 33 --- simple-mind-map/src/layouts/MindMap.js | 208 ++++++++------ simple-mind-map/src/layouts/Structure.js | 11 - .../{Fishbone.js => _CatalogOrganization.js} | 164 ++--------- ...Structure.js => _OrganizationStructure.js} | 0 simple-mind-map/src/utils/index.js | 18 +- web/src/config/index.js | 7 +- web/src/pages/Edit/components/BaseStyle.vue | 28 +- web/src/pages/Edit/components/Edit.vue | 4 + web/src/pages/Edit/components/Outline.vue | 17 +- web/src/pages/Edit/components/Sidebar.vue | 43 ++- web/src/pages/Edit/components/Structure.vue | 33 +++ web/src/pages/Edit/components/Style.vue | 13 +- web/src/pages/Edit/components/Theme.vue | 7 +- web/src/pages/Edit/components/Toolbar.vue | 4 + 28 files changed, 776 insertions(+), 652 deletions(-) create mode 100644 simple-mind-map/src/BatchExecution.js create mode 100644 simple-mind-map/src/assets/icon_newcreat_classify.png create mode 100644 simple-mind-map/src/assets/icon_newcreat_mindmap.png create mode 100644 simple-mind-map/src/assets/icon_newcreat_structure.png create mode 100644 simple-mind-map/src/assets/icon_newcreat_theright.png delete mode 100644 simple-mind-map/src/layouts/BubbleChart.js delete mode 100644 simple-mind-map/src/layouts/Structure.js rename simple-mind-map/src/layouts/{Fishbone.js => _CatalogOrganization.js} (56%) rename simple-mind-map/src/layouts/{OrganizationStructure.js => _OrganizationStructure.js} (100%) create mode 100644 web/src/pages/Edit/components/Structure.vue diff --git a/simple-mind-map/example/exampleData.js b/simple-mind-map/example/exampleData.js index ad243813..bea996fa 100644 --- a/simple-mind-map/example/exampleData.js +++ b/simple-mind-map/example/exampleData.js @@ -1,3 +1,19 @@ +const createFullData = () => { + return { + // "image": "http://aliyuncdn.lxqnsys.com/whbm/enJFNMHnedQTYTESGfDkctCp2", + // "imageTitle": "图片名称", + // "imageSize": { + // "width": 1000, + // "height": 563 + // }, + // "icon": ['priority_1'], + // "tag": ["标签1", "标签2"], + // "hyperlink": "http://lxqnsys.com/", + // "hyperlinkTitle": "理想青年实验室", + // "note": "理想青年实验室\n一个有意思的角落" + }; +} + /** * @Author: 王林 * @Date: 2021-04-15 22:23:24 @@ -8,33 +24,162 @@ export default { "data": { "text": "根节点", }, - "children": [{ - "data": { - "text": "二级节点", - "expand": true, - }, - "children": [{ + "children": [ + { "data": { - "text": "子节点", - // "image": "http://aliyuncdn.lxqnsys.com/whbm/enJFNMHnedQTYTESGfDkctCp2", - // "imageTitle": "图片名称", - // "imageSize": { - // "width": 1000, - // "height": 563 - // }, - // "icon": ['priority_1'], - // "tag": ["标签1", "标签2"], - // "hyperlink": "http://lxqnsys.com/", - // "hyperlinkTitle": "理想青年实验室", - // "note": "理想青年实验室\n一个有意思的角落" + "text": "二级节点1", + "expand": true, }, - "children": [] - }, { + "children": [{ + "data": { + "text": "子节点1-1", + ...createFullData() + }, + }, { + "data": { + "text": "子节点1-2", + ...createFullData() + } + },] + }, + { "data": { - "text": "子节点", - } - }] - }] + "text": "二级节点2" + }, + "children": [ + { + "data": { + "text": "子节点2-1", + ...createFullData() + }, + "children": [ + { + "data": { + "text": "子节点2-1-1", + ...createFullData() + } + }, + { + "data": { + "text": "子节点2-1-2", + ...createFullData() + }, + "children": [ + { + "data": { + "text": "子节点2-1-2-1", + ...createFullData() + } + }, + { + "data": { + "text": "子节点2-1-2-2", + ...createFullData() + }, + "children": [ + { + "data": { + "text": "子节点2-1-2-2-1", + ...createFullData() + } + }, + { + "data": { + "text": "子节点2-1-2-2-2", + ...createFullData() + } + }, + { + "data": { + "text": "子节点2-1-2-2-3", + ...createFullData() + } + } + ] + }, + { + "data": { + "text": "子节点4-1-2-3", + ...createFullData() + } + } + ] + }, + { + "data": { + "text": "子节点2-1-3", + ...createFullData() + } + } + ] + }, + { + "data": { + "text": "子节点2-2", + ...createFullData() + } + } + ] + }, + { + "data": { + "text": "二级节点3", + }, + "children": [ + { + "data": { + "text": "子节点3-1", + ...createFullData() + } + }, + { + "data": { + "text": "子节点3-2", + ...createFullData() + } + } + ] + }, + { + "data": { + "text": "二级节点4", + }, + "children": [ + { + "data": { + "text": "子节点4-1", + ...createFullData() + }, + "children": [ + { + "data": { + "text": "子节点4-1-1", + ...createFullData() + } + }, + { + "data": { + "text": "子节点4-1-2", + ...createFullData() + } + }, + { + "data": { + "text": "子节点4-1-3", + ...createFullData() + } + } + ] + }, + { + "data": { + "text": "子节点4-2", + ...createFullData() + } + } + ] + } + ] }, "theme": { "template": "default", @@ -42,5 +187,7 @@ export default { // 自定义配置... } }, + // "layout": "mindMap", "layout": "logicalStructure" + // "layout": "catalogOrganization" } \ No newline at end of file diff --git a/simple-mind-map/index.js b/simple-mind-map/index.js index 47f910e4..b04ac4cd 100644 --- a/simple-mind-map/index.js +++ b/simple-mind-map/index.js @@ -5,7 +5,8 @@ import merge from 'deepmerge' import theme from './src/themes' import Style from './src/Style' import KeyCommand from './src/KeyCommand' -import Command from './src/Command'; +import Command from './src/Command' +import BatchExecution from './src/BatchExecution' import { SVG } from '@svgdotjs/svg.js' @@ -52,7 +53,7 @@ class MindMap { // 画笔 this.draw = SVG().addTo(this.el).size(width, height) - + // 节点id this.uid = 0 @@ -85,6 +86,9 @@ class MindMap { draw: this.draw }) + // 批量执行类 + this.batchExecution = new BatchExecution() + // 初始渲染 this.renderer.render() setTimeout(() => { @@ -99,9 +103,11 @@ class MindMap { * @Desc: 渲染 */ render() { - this.draw.clear() - this.initTheme() - this.renderer.render() + this.batchExecution.push('render', () => { + this.draw.clear() + this.initTheme() + this.renderer.render() + }) } /** diff --git a/simple-mind-map/src/BatchExecution.js b/simple-mind-map/src/BatchExecution.js new file mode 100644 index 00000000..9db557d1 --- /dev/null +++ b/simple-mind-map/src/BatchExecution.js @@ -0,0 +1,85 @@ +/** + * @Author: 王林 + * @Date: 2021-06-27 13:16:23 + * @Desc: 在下一个事件循环里执行任务 + */ +const nextTick = function (fn, ctx) { + let pending = false + let timerFunc = null + let handle = () => { + pending = false + ctx ? fn.call(ctx) : fn() + } + // 支持MutationObserver接口的话使用MutationObserver + if (typeof MutationObserver !== 'undefined') { + let counter = 1 + let observer = new MutationObserver(handle) + let textNode = document.createTextNode(counter) + observer.observe(textNode, { + characterData: true// 设为 true 表示监视指定目标节点或子节点树中节点所包含的字符数据的变化 + }) + timerFunc = function () { + counter = (counter + 1) % 2// counter会在0和1两者循环变化 + textNode.data = counter// 节点变化会触发回调handle, + } + } else {// 否则使用定时器 + timerFunc = setTimeout + } + return function (cb, ctx) { + if (pending) return + pending = true + timerFunc(handle, 0) + } +} + + +/** + * @Author: 王林 + * @Date: 2021-06-26 22:40:52 + * @Desc: 批量执行 + */ +class BatchExecution { + /** + * @Author: 王林 + * @Date: 2021-06-26 22:41:41 + * @Desc: 构造函数 + */ + constructor() { + this.has = {} + this.queue = [] + this.nextTick = nextTick(this.flush, this) + } + + /** + * @Author: 王林 + * @Date: 2021-06-27 12:54:04 + * @Desc: 添加任务 + */ + push(name, fn) { + if (this.has[name]) { + return; + } + this.has[name] = true + this.queue.push({ + name, + fn + }) + this.nextTick() + } + + /** + * @Author: 王林 + * @Date: 2021-06-27 13:09:24 + * @Desc: 执行队列 + */ + flush() { + let fns = this.queue.slice(0) + this.queue = [] + fns.forEach(({ name, fn }) => { + this.has[name] = false + fn() + }) + } +} + +export default BatchExecution \ No newline at end of file diff --git a/simple-mind-map/src/Node.js b/simple-mind-map/src/Node.js index 9ccbdf36..ebf03e2f 100644 --- a/simple-mind-map/src/Node.js +++ b/simple-mind-map/src/Node.js @@ -1,6 +1,7 @@ import Style from './Style' import { - resizeImgSize + resizeImgSize, + copyRenderTree } from './utils' import { Image, @@ -62,6 +63,8 @@ class Node { this._textContentItemMargin = 2 // 图片和文字节点的间距 this._blockContentMargin = 5 + // 展开收缩按钮尺寸 + this._expandBtnSize = 20 // 计算节点尺寸 this.refreshSize() } @@ -491,7 +494,7 @@ class Node { * @Desc: 展开收缩按钮 */ renderExpandBtn() { - if (this.children.length <= 0 || this.isRoot) { + if ((!this.nodeData.data.cacheChildren || this.nodeData.data.cacheChildren.length <= 0) && this.children.length <= 0 || this.isRoot) { return; } let g = this.draw.group() @@ -501,8 +504,8 @@ class Node { } else { iconSvg = btnsSvg.close } - let node = SVG(iconSvg).size(20, 20) - let fillNode = new Circle().size(20) + let node = SVG(iconSvg).size(this._expandBtnSize, this._expandBtnSize) + let fillNode = new Circle().size(this._expandBtnSize) this.renderer.layout.renderExpandBtn(this, [node, fillNode]) node.dx(0).dy(-10) fillNode.dx(0).dy(-10) @@ -518,10 +521,21 @@ class Node { }) }) g.click(() => { - // 需要反映到实际数据上 - this.mindMap.execCommand('UPDATE_NODE_DATA', this, { - expand: !this.nodeData.data.expand - }) + // 展开收缩 + let data = {} + let children = [] + if (this.nodeData.data.expand) { + data.expand = false + data.cacheChildren = this.nodeData.children.map((item) => { + return copyRenderTree({}, item); + }) + children = [] + } else { + data.expand = true + children = this.nodeData.data.cacheChildren + data.cacheChildren = [] + } + this.mindMap.execCommand('UPDATE_NODE_DATA', this, data, children) this.mindMap.emit('expand_btn_click', this) }) g.add(fillNode) diff --git a/simple-mind-map/src/Render.js b/simple-mind-map/src/Render.js index b7146b8e..f8fdad03 100644 --- a/simple-mind-map/src/Render.js +++ b/simple-mind-map/src/Render.js @@ -1,11 +1,17 @@ import merge from 'deepmerge' import LogicalStructure from './layouts/LogicalStructure' +import MindMap from './layouts/MindMap' +import CatalogOrganization from './layouts/CatalogOrganization'; import TextEdit from './TextEdit' // 布局列表 const layouts = { + // 逻辑结构图 + logicalStructure: LogicalStructure, // 思维导图 - logicalStructure: LogicalStructure + mindMap: MindMap, + // 目录组织图 + catalogOrganization: CatalogOrganization } /** @@ -146,6 +152,9 @@ class Render { return; } let first = this.activeNodeList[0] + if (!first.nodeData.children) { + first.nodeData.children = [] + } first.nodeData.children.push({ "data": { "text": "分支主题", @@ -184,10 +193,13 @@ class Render { * @Date: 2021-05-04 14:19:48 * @Desc: 更新节点数据 */ - updateNodeData(node, data) { + updateNodeData(node, data, children) { Object.keys(data).forEach((key) => { node.nodeData.data[key] = data[key] }) + if (children) { + node.nodeData.children = children + } this.mindMap.render() } } diff --git a/simple-mind-map/src/TextEdit.js b/simple-mind-map/src/TextEdit.js index 33a65428..fb93573a 100644 --- a/simple-mind-map/src/TextEdit.js +++ b/simple-mind-map/src/TextEdit.js @@ -99,6 +99,7 @@ export default class TextEdit { this.renderer.activeNodeList.forEach((node) => { let str = getStrWithBrFromHtml(this.textEditNode.innerHTML) node.nodeData.data.text = str + console.log(8) this.mindMap.render() }) this.mindMap.emit('hide_text_edit', this.textEditNode, this.renderer.activeNodeList) diff --git a/simple-mind-map/src/assets/icon_newcreat_classify.png b/simple-mind-map/src/assets/icon_newcreat_classify.png new file mode 100644 index 0000000000000000000000000000000000000000..6fa0bcf8e35e3e8fb3411923074592d3088c2300 GIT binary patch literal 2651 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 VT3jM{j`iaKSeRKMX$ZHt{{T%(W`Y0! literal 0 HcmV?d00001 diff --git a/simple-mind-map/src/assets/icon_newcreat_structure.png b/simple-mind-map/src/assets/icon_newcreat_structure.png new file mode 100644 index 0000000000000000000000000000000000000000..24f3b15cf28b5755183b084f9865db438beaf7fb GIT binary patch literal 2274 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 { - // 设置width、height - let { - children, - ...props - } = node - let newNode = new Node({ - ...props, - mindMap: this.mindMap, - draw: this.draw, - layerIndex - }) - // 计算节点的宽高 - newNode.refreshSize() - // 计算节点的top - if (isRoot) { - newNode.isRoot = true - newNode.left = this.mindMap.width / 2 - newNode.top = this.mindMap.height / 2 - this.root = newNode - } else { - newNode.parent = parent._node - parent._node.addChildren(newNode) - } - node._node = newNode - }, (node) => { - // 遍历完子节点返回时 - }, true) - } - - /** - * javascript comment - * @Author: 王林25 - * @Date: 2021-04-08 09:59:25 - * @Desc: 计算节点的left、top - */ - computedLeftTopValue() { - let margin = Math.max(this.mindMap.opt.marginX, this.mindMap.opt.marginY) - walk(this.root, null, (node) => { - if (node.children && node.children.length) { - let rad = (360 / node.children.length) * (Math.PI / 180) - let totalRad = 0 - node.children.forEach((item) => { - let r = node.width / 2 + margin + item.width / 2 - item.left = node.left + r * Math.cos(totalRad) - item.top = node.top + r * Math.sin(totalRad) - totalRad += rad - }) - } - }, null, true) - // return - walk(this.root, null, null, (node) => { - if (node.children && node.children.length) { - let minLeft = Infinity, - minTop = Infinity, - maxRight = -Infinity, - maxBottom = -Infinity - node.children.concat([node]).forEach((item) => { - if ((item.left - item.width / 2) < minLeft) { - minLeft = item.left - item.width / 2 - } - if ((item.top - item.width / 2) < minTop) { - minTop = item.top - item.width / 2 - } - if ((item.left + item.width / 2) > maxRight) { - maxRight = item.left + item.width / 2 - } - if ((item.top + item.width / 2) < maxBottom) { - maxBottom = item.top + item.width / 2 - } - }) - let width = Math.max(maxRight - minLeft, maxBottom - minTop) - let difference = width - node.width - this.update(node, difference) - } - }, true) - } - - update(node, difference) { - if (node.parent) { - // console.log(node.text, difference) - let rad = (360 / node.parent.children.length) * (Math.PI / 180) - let totalRad = 0 - node.parent.children.forEach((item) => { - if (item === node) { - item.left += difference * Math.cos(totalRad) - item.top += difference * Math.sin(totalRad) - if (node.children && node.children.length) { - // this.updateChildren(node) - } - } - totalRad += rad - }) - - this.update(node.parent, difference) - } - } - - /** - * javascript comment - * @Author: 王林25 - * @Date: 2021-04-07 11:25:52 - * @Desc: 更新子节点 - */ - updateChildren(node, difference) { - let margin = Math.max(this.mindMap.opt.marginX, this.mindMap.opt.marginY) - walk(node, null, (node) => { - if (node.children && node.children.length) { - let rad = (360 / node.children.length) * (Math.PI / 180) - let totalRad = 0 - node.children.forEach((item) => { - let r = node.width / 2 + margin + item.width / 2 - item.left = node.left + r * Math.cos(totalRad) - item.top = node.top + r * Math.sin(totalRad) - totalRad += rad - }) - } - }, null, true) - } -} - -export default Render \ No newline at end of file diff --git a/simple-mind-map/src/layouts/CatalogOrganization.js b/simple-mind-map/src/layouts/CatalogOrganization.js index 909aad42..f56a4faf 100644 --- a/simple-mind-map/src/layouts/CatalogOrganization.js +++ b/simple-mind-map/src/layouts/CatalogOrganization.js @@ -1,105 +1,87 @@ +import Base from './Base'; import { walk -} from '../Utils' +} from '../utils' import Node from '../Node' -import merge from 'deepmerge' /** - * javascript comment - * @Author: 王林25 - * @Date: 2021-04-08 16:25:07 - * @Desc: 目录组织图 + * @Author: 王林 + * @Date: 2021-04-12 22:25:58 + * @Desc: 目录组织图 * 思路:第一轮只计算节点的宽高,以及某个节点的所有子节点所占的高度之和,以及该节点里所有子节点中宽度最宽是多少、第二轮计算节点的left和top,需要区分二级节点和其他节点,二级节点top相同,一行依次从做向右排开,其他节点的left相同,一列从上往下依次排开 */ -class Render { +class CatalogOrganization extends Base { /** - * javascript comment - * @Author: 王林25 - * @Date: 2021-04-08 16:25:32 + * @Author: 王林 + * @Date: 2021-04-12 22:26:31 * @Desc: 构造函数 */ constructor(opt = {}) { - this.opt = opt - this.mindMap = opt.mindMap - this.draw = this.mindMap.draw - // 渲染树 - this.renderTree = merge({}, this.mindMap.opt.data || {}) - // 根节点 - this.root = null - } - - /** - * javascript comment - * @Author: 王林25 - * @Date: 2021-04-08 16:27:55 - * @Desc: 渲染 - */ - render() { - this.computed() - this.root.render() + super(opt) } /** * javascript comment * @Author: 王林25 * @Date: 2021-04-06 14:04:20 - * @Desc: 计算位置数据 + * @Desc: 布局 */ - computed() { - // 计算节点的width、height + doLayout() { + // 遍历数据计算节点的width、height this.computedBaseValue() // 计算节点的left、top this.computedLeftTopValue() // 调整节点top this.adjustTopValue() // 调整节点left - this.adjustLeftValue() + // this.adjustLeftValue() + + return this.root; } /** * javascript comment * @Author: 王林25 * @Date: 2021-04-08 09:49:32 - * @Desc: 计算节点的width、height + * @Desc: 遍历数据计算节点的width、height */ computedBaseValue() { - walk(this.renderTree, null, (node, parent, isRoot, index) => { - // 设置width、height - let { - children, - ...props - } = node + walk(this.renderTree, null, (cur, parent, isRoot, layerIndex) => { + // 创建节点 let newNode = new Node({ - ...props, + data: cur, + uid: this.mindMap.uid++, + renderer: this.renderer, mindMap: this.mindMap, - draw: this.draw + draw: this.draw, + layerIndex }) - // 计算节点的宽高 - newNode.refreshSize() - // 计算节点的top + // 数据关联实际节点 + cur._node = newNode + // 根节点定位在画布中心位置 if (isRoot) { newNode.isRoot = true newNode.left = (this.mindMap.width - newNode.width) / 2 newNode.top = (this.mindMap.height - newNode.height) / 2 this.root = newNode } else { + // 互相收集 newNode.parent = parent._node parent._node.addChildren(newNode) } - node._node = newNode - }, (node) => { - // 遍历完子节点返回时 - // 计算节点的areaHeight,也就是子节点所占的高度之和,包括外边距 - let len = node._node.children.length - if (node._node.isRoot) { - node._node.childrenAreaWidth = len ? node._node.children.reduce((h, cur) => { - return h + cur.width - }, 0) + (len + 1) * this.mindMap.opt.marginX : 0 + }, (cur, parent, isRoot, layerIndex) => { + // 返回时计算节点的areaHeight,也就是子节点所占的高度之和,包括外边距 + let len = cur._node.children.length + if (isRoot) {// 计算二级节点所占的宽度之和 + cur._node.childrenAreaWidth = len ? cur._node.children.reduce((h, item) => { + return h + item.width + }, 0) + (len + 1) * this.getMarginX(layerIndex) : 0 } - node._node.childrenAreaHeight = len ? node._node.children.reduce((h, cur) => { - return h + cur.height - }, 0) + (len + 1) * this.mindMap.opt.marginY : 0 - }, true) + // 计算子节点所占的高度之和 + cur._node.childrenAreaHeight = len ? cur._node.children.reduce((h, item) => { + return h + item.height + }, 0) + (len + 1) * this.getMarginY(layerIndex) : 0 + }, true, 0) } /** @@ -109,30 +91,79 @@ class Render { * @Desc: 计算节点的left、top */ computedLeftTopValue() { - walk(this.root, null, (node) => { + walk(this.root, null, (node, parent, isRoot, layerIndex) => { + let marginX = this.getMarginX(layerIndex) + let marginY = this.getMarginY(layerIndex) if (node.children && node.children.length) { if (node.isRoot) { let left = node.left + node.width / 2 - node.childrenAreaWidth / 2 - let totalLeft = left + this.mindMap.opt.marginX + let totalLeft = left + marginX node.children.forEach((cur) => { // left cur.left = totalLeft - totalLeft += cur.width + this.mindMap.opt.marginX + totalLeft += cur.width + marginX // top - cur.top = node.top + node.height + this.mindMap.opt.marginY + cur.top = node.top + node.height + marginY }) } else { - let totalTop = node.top + node.height + this.mindMap.opt.marginY + let totalTop = node.top + node.height + marginY node.children.forEach((cur) => { - cur.left = node.left + node.width / 5 + this.mindMap.opt.marginX + cur.left = node.left + node.width / 5 cur.top = totalTop - totalTop += cur.height + this.mindMap.opt.marginY + totalTop += cur.height + marginY }) } } }, null, true) } + /** + * javascript comment + * @Author: 王林25 + * @Date: 2021-04-08 10:04:05 + * @Desc: 调整节点top,该节点之后的节点都往下进行偏移 + */ + adjustTopValue() { + walk(this.root, null, (node, parent, isRoot, layerIndex) => { + let marginY = this.getMarginY(layerIndex) + if (!node.isRoot && !node.parent.isRoot) { + // 判断子节点的areaHeight是否大于该节点自身,大于则需要调整位置 + if (node.children && node.children.length > 0) { + let difference = node.childrenAreaHeight - marginY + this.updateBrothersTopValue(node, difference) + } + } + }, null, true) + } + + /** + * javascript comment + * @Author: 王林25 + * @Date: 2021-04-07 14:26:03 + * @Desc: 更新兄弟节点的top + */ + updateBrothersTopValue(node, addHeight) { + if (node.parent && !node.parent.isRoot) { + let childrenList = node.parent.children + let index = childrenList.findIndex((item) => { + return item === node + }) + childrenList.forEach((item, _index) => { + let _offset = 0 + if (_index > index) { + _offset = addHeight + } + item.top += _offset + // 同步更新子节点的位置 + if (item.children && item.children.length && _offset > 0) { + this.updateChildren(item.children, 'top', _offset) + } + }) + // 更新父节点的位置 + this.updateBrothersTopValue(node.parent, addHeight) + } + } + /** * javascript comment * @Author: 王林25 @@ -140,10 +171,11 @@ class Render { * @Desc: 调整节点left */ adjustLeftValue() { - walk(this.root, null, (node) => { + walk(this.root, null, (node, parent, isRoot, layerIndex) => { + let marginX = this.getMarginY(layerIndex) if (node.parent && node.parent.isRoot) { let childrenAreaWidth = this.getNodeWidth(node) - let difference = childrenAreaWidth - node.width + let difference = childrenAreaWidth - node.width - marginX if (difference > 0) { this.updateBrothersLeftValue(node, difference / 2) } @@ -161,7 +193,7 @@ class Render { let widthArr = [] let loop = (node, width) => { if (node.children.length) { - width += node.width / 5 + this.mindMap.opt.marginX + width += node.width / 5 node.children.forEach((item) => { loop(item, width) }) @@ -205,66 +237,60 @@ class Render { } /** - * javascript comment - * @Author: 王林25 - * @Date: 2021-04-08 10:04:05 - * @Desc: 调整节点top,该节点之后的节点都往下进行偏移 + * @Author: 王林 + * @Date: 2021-04-11 14:42:48 + * @Desc: 绘制连线,连接该节点到其子节点 */ - adjustTopValue() { - let marginY = this.mindMap.opt.marginY - walk(this.root, null, (node) => { - if (!node.isRoot && !node.parent.isRoot) { - // 判断子节点的areaHeight是否大于该节点自身,大于则需要调整位置 - if (node.children && node.children.length > 0) { - let difference = node.childrenAreaHeight - marginY - this.updateBrothersTopValue(node, difference) - } - } - }, null, true) - } - - /** - * javascript comment - * @Author: 王林25 - * @Date: 2021-04-07 14:26:03 - * @Desc: 更新兄弟节点的top - */ - updateBrothersTopValue(node, addHeight) { - if (node.parent && !node.parent.isRoot) { - let childrenList = node.parent.children - let index = childrenList.findIndex((item) => { - return item === node - }) - childrenList.forEach((item, _index) => { - let _offset = 0 - if (_index > index) { - _offset = addHeight - } - item.top += _offset - // 同步更新子节点的位置 - if (item.children && item.children.length) { - this.updateChildren(item.children, 'top', _offset) - } - }) - // 更新父节点的位置 - this.updateBrothersTopValue(node.parent, addHeight) + renderLine(node) { + return []; + if (node.children.length <= 0) { + return []; } + let { + left, + top, + width, + height + } = node + let lines = [] + if (!node.isRoot) { + let line = this.draw.line(left + width, top + height / 2, left + width + 20, top + height / 2) + lines.push(line) + } + node.children.forEach((item) => { + let x1 = node.layerIndex === 0 ? left + width / 2 : left + width + 20 + let y1 = node.layerIndex === 0 ? top + height / 2 : top + height / 2 + let x2 = item.left + let y2 = item.top + item.height / 2 + let path = '' + if (node.isRoot) { + path = this.quadraticCurvePath(x1, y1, x2, y2) + } else { + path = this.cubicBezierPath(x1, y1, x2, y2) + } + let line = this.draw.path(path) + lines.push(line) + }) + return lines; } /** - * javascript comment - * @Author: 王林25 - * @Date: 2021-04-07 11:25:52 - * @Desc: 更新子节点属性 + * @Author: 王林 + * @Date: 2021-04-11 19:54:26 + * @Desc: 渲染按钮 */ - updateChildren(children, prop, offset) { - children.forEach((item) => { - item[prop] += offset - if (item.children && item.children.length) { - this.updateChildren(item.children, prop, offset) - } + renderExpandBtn(node, icons) { + return; + let { + left, + top, + width, + height + } = node + icons.forEach((icon) => { + icon.x(left + width).y(top + height / 2) }) } } -export default Render \ No newline at end of file +export default CatalogOrganization \ No newline at end of file diff --git a/simple-mind-map/src/layouts/LogicalStructure.js b/simple-mind-map/src/layouts/LogicalStructure.js index 73c32000..70f096ea 100644 --- a/simple-mind-map/src/layouts/LogicalStructure.js +++ b/simple-mind-map/src/layouts/LogicalStructure.js @@ -147,39 +147,6 @@ class LogicalStructure extends Base { } } - /** - * @Author: 王林 - * @Date: 2021-04-11 15:34:20 - * @Desc: 获取节点的marginY - */ - getMarginY(layerIndex) { - return layerIndex === 1 ? this.themeConfig.second.marginY : this.themeConfig.node.marginY; - } - - /** - * @Author: 王林 - * @Date: 2021-04-11 15:05:01 - * @Desc: 二次贝塞尔曲线 - */ - quadraticCurvePath(x1, y1, x2, y2) { - let cx = x1 + (x2 - x1) * 0.2 - let cy = y1 + (y2 - y1) * 0.8 - return `M ${x1},${y1} Q ${cx},${cy} ${x2},${y2}` - } - - /** - * @Author: 王林 - * @Date: 2021-04-11 15:05:18 - * @Desc: 三次贝塞尔曲线 - */ - cubicBezierPath(x1, y1, x2, y2) { - let cx1 = x1 + (x2 - x1) / 2 - let cy1 = y1 - let cx2 = x2 - (x2 - x1) / 2 - let cy2 = y2 - return `M ${x1},${y1} C ${cx1},${cy1} ${cx2},${cy2} ${x2},${y2}` - } - /** * @Author: 王林 * @Date: 2021-04-11 14:42:48 diff --git a/simple-mind-map/src/layouts/MindMap.js b/simple-mind-map/src/layouts/MindMap.js index cba93d07..c471f9a2 100644 --- a/simple-mind-map/src/layouts/MindMap.js +++ b/simple-mind-map/src/layouts/MindMap.js @@ -1,125 +1,131 @@ +import Base from './Base'; import { walk -} from '../Utils' +} from '../utils' import Node from '../Node' -import merge from 'deepmerge' /** - * javascript comment - * @Author: 王林25 - * @Date: 2021-04-08 16:25:07 - * @Desc: 思维导图 + * @Author: 王林 + * @Date: 2021-04-12 22:25:58 + * @Desc: 思维导图 * 思路:在逻辑结构图的基础上增加一个变量来记录生长方向,向左还是向右,同时在计算left的时候根据方向来计算、调整top时只考虑同方向的节点即可 */ -class Render { +class MindMap extends Base { /** - * javascript comment - * @Author: 王林25 - * @Date: 2021-04-08 16:25:32 + * @Author: 王林 + * @Date: 2021-04-12 22:26:31 * @Desc: 构造函数 */ constructor(opt = {}) { - this.opt = opt - this.mindMap = opt.mindMap - this.draw = this.mindMap.draw - // 渲染树 - this.renderTree = merge({}, this.mindMap.opt.data || {}) - // 根节点 - this.root = null - } - - /** - * javascript comment - * @Author: 王林25 - * @Date: 2021-04-08 16:27:55 - * @Desc: 渲染 - */ - render() { - this.computed() - this.root.render() + super(opt) } /** * javascript comment * @Author: 王林25 * @Date: 2021-04-06 14:04:20 - * @Desc: 计算位置数据 + * @Desc: 布局 */ - computed() { - // 计算节点的left、width、height + doLayout() { + // 遍历数据计算节点的left、width、height this.computedBaseValue() // 计算节点的top this.computedTopValue() // 调整节点top this.adjustTopValue() + return this.root; } /** * javascript comment * @Author: 王林25 * @Date: 2021-04-08 09:49:32 - * @Desc: 计算节点的left、width、height + * @Desc: 遍历数据计算节点的left、width、height */ computedBaseValue() { - walk(this.renderTree, null, (node, parent, isRoot, index) => { - // 生长方向 + walk(this.renderTree, null, (cur, parent, isRoot, layerIndex, index) => { + // 节点生长方向 let dir = '' if (isRoot) { dir = '' - } else if (parent._node.isRoot) { + } else if (parent._node.isRoot) {// 二级节点根据索引来判断显示在左侧还是右侧 dir = index % 2 === 0 ? 'right' : 'left' - } else { + } else {// 三级及以下节点以上级为准 dir = parent._node.dir } - // 设置left、width、height - let { - children, - ...props - } = node + // 创建节点 let newNode = new Node({ - ...props, + data: cur, + uid: this.mindMap.uid++, + renderer: this.renderer, mindMap: this.mindMap, draw: this.draw, - dir + layerIndex }) - // 计算节点的宽高 - newNode.refreshSize() - // 计算节点的left + newNode.dir = dir + // 数据关联实际节点 + cur._node = newNode + // 根节点定位在画布中心位置 if (isRoot) { newNode.isRoot = true newNode.left = (this.mindMap.width - newNode.width) / 2 newNode.top = (this.mindMap.height - newNode.height) / 2 this.root = newNode } else { - newNode.left = dir === 'right' ? parent._node.left + parent._node.width + this.mindMap.opt.marginX : parent._node.left - this.mindMap.opt.marginX - newNode.width + // 非根节点 + let marginX = layerIndex === 1 ? this.themeConfig.second.marginX : this.themeConfig.node.marginX + // 根据生长方向定位到父节点的左侧还是右侧 + newNode.left = dir === 'right' ? parent._node.left + parent._node.width + marginX : parent._node.left - newNode.width - marginX + // 互相收集 newNode.parent = parent._node parent._node.addChildren(newNode) } - node._node = newNode - }, (node) => { + }, (cur, parent, isRoot, layerIndex) => { // 返回时计算节点的areaHeight,也就是子节点所占的高度之和,包括外边距 - let len = node._node.children.length - node._node.childrenAreaHeight = len ? node._node.children.reduce((h, cur) => { - return h + cur.height - }, 0) + (len + 1) * this.mindMap.opt.marginY : 0 - }, true) + if (cur.data.expand === false) { + cur._node.leftChildrenAreaHeight = 0 + cur._node.rightChildrenAreaHeight = 0 + return ; + } + let leftLen = 0 + let rightLen = 0 + let leftAreaHeight = 0 + let rightAreaHeight = 0 + cur._node.children.forEach((item) => { + if (item.dir === 'left') { + leftLen++ + leftAreaHeight += item.height + } else { + rightLen++ + rightAreaHeight += item.height + } + }) + cur._node.leftChildrenAreaHeight = leftAreaHeight + (leftLen + 1) * this.getMarginY(layerIndex) + cur._node.rightChildrenAreaHeight = rightAreaHeight + (rightLen + 1) * this.getMarginY(layerIndex) + }, true, 0) } /** * javascript comment * @Author: 王林25 * @Date: 2021-04-08 09:59:25 - * @Desc: 计算节点的top + * @Desc: 遍历节点树计算节点的top */ computedTopValue() { - walk(this.root, null, (node) => { + walk(this.root, null, (node, parent, isRoot, layerIndex) => { if (node.children && node.children.length) { - // 第一个子节点的top值 = 该节点中心的top值 - 子节点的高度之和的一半 - let top = node.top + node.height / 2 - node.childrenAreaHeight / 2 - let totalTop = top + this.mindMap.opt.marginY + let marginY = this.getMarginY(layerIndex) + let top = node.top + node.height / 2 + let leftTotalTop = top - node.leftChildrenAreaHeight / 2 + marginY + let rightTotalTop = top - node.rightChildrenAreaHeight / 2 + marginY node.children.forEach((cur) => { - cur.top = totalTop - totalTop += cur.height + this.mindMap.opt.marginY + if (cur.dir === 'left') { + cur.top = leftTotalTop + leftTotalTop += cur.height + marginY + } else { + cur.top = rightTotalTop + rightTotalTop += cur.height + marginY + } }) } }, null, true) @@ -132,12 +138,13 @@ class Render { * @Desc: 调整节点top */ adjustTopValue() { - let margin = this.mindMap.opt.marginY * 2 - walk(this.root, null, (node) => { + walk(this.root, null, (node, parent, isRoot, layerIndex) => { // 判断子节点所占的高度之和是否大于该节点自身,大于则需要调整位置 - let difference = node.childrenAreaHeight - margin - node.height - if (difference > 0) { - this.updateBrothers(node, difference / 2) + let h = this.getMarginY(layerIndex) + node.height + let leftDifference = node.leftChildrenAreaHeight - h + let rightDifference = node.rightChildrenAreaHeight - h + if (leftDifference > 0 || rightDifference > 0) { + this.updateBrothers(node, leftDifference / 2, rightDifference / 2) } }, null, true) } @@ -148,7 +155,7 @@ class Render { * @Date: 2021-04-07 14:26:03 * @Desc: 更新兄弟节点的top */ - updateBrothers(node, addHeight) { + updateBrothers(node, leftAddHeight, rightAddHeight) { if (node.parent) { let childrenList = node.parent.children.filter((item) => { return item.dir === node.dir @@ -158,9 +165,11 @@ class Render { }) childrenList.forEach((item, _index) => { let _offset = 0 + let addHeight = item.dir === 'left' ? leftAddHeight : rightAddHeight + // 上面的节点往上移 if (_index < index) { _offset = -addHeight - } else if (_index > index) { + } else if (_index > index) {// 下面的节点往下移 _offset = addHeight } item.top += _offset @@ -170,26 +179,65 @@ class Render { } }) // 更新父节点的位置 - this.updateBrothers(node.parent, addHeight) + this.updateBrothers(node.parent, leftAddHeight, rightAddHeight) } } /** - * javascript comment - * @Author: 王林25 - * @Date: 2021-04-07 11:25:52 - * @Desc: 更新子节点属性 + * @Author: 王林 + * @Date: 2021-04-11 14:42:48 + * @Desc: 绘制连线,连接该节点到其子节点 */ - updateChildren(children, prop, offset) { - children.forEach((item) => { - item[prop] += offset - if (item.children && item.children.length) { - this.updateChildren(item.children, prop, offset) + renderLine(node) { + if (node.children.length <= 0) { + return []; + } + let { + left, + top, + width, + height, + _expandBtnSize + } = node + let lines = [] + // if (!node.isRoot) { + // let line = this.draw.line(left + width, top + height / 2, left + width + 20, top + height / 2) + // lines.push(line) + // } + node.children.forEach((item) => { + let x1 = node.layerIndex === 0 ? left + width / 2 : item.dir === 'right' ? left + width + _expandBtnSize : left - _expandBtnSize + let y1 = node.layerIndex === 0 ? top + height / 2 : top + height / 2 + let x2 = item.dir === 'right' ? item.left : item.left + item.width + let y2 = item.top + item.height / 2 + let path = '' + if (node.isRoot) { + path = this.quadraticCurvePath(x1, y1, x2, y2) + } else { + path = this.cubicBezierPath(x1, y1, x2, y2) } + let line = this.draw.path(path) + lines.push(line) }) + return lines; } - + /** + * @Author: 王林 + * @Date: 2021-04-11 19:54:26 + * @Desc: 渲染按钮 + */ + renderExpandBtn(node, icons) { + let { + left, + top, + width, + height, + _expandBtnSize + } = node + icons.forEach((icon) => { + node.dir === 'right' ? icon.x(left + width).y(top + height / 2) : icon.x(left - _expandBtnSize).y(top + height / 2) + }) + } } -export default Render \ No newline at end of file +export default MindMap \ No newline at end of file diff --git a/simple-mind-map/src/layouts/Structure.js b/simple-mind-map/src/layouts/Structure.js deleted file mode 100644 index 2cc8c28c..00000000 --- a/simple-mind-map/src/layouts/Structure.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * javascript comment - * @Author: 王林25 - * @Date: 2021-04-12 17:21:04 - * @Desc: 基类 - */ -class Structure { - -} - -export default Structure \ No newline at end of file diff --git a/simple-mind-map/src/layouts/Fishbone.js b/simple-mind-map/src/layouts/_CatalogOrganization.js similarity index 56% rename from simple-mind-map/src/layouts/Fishbone.js rename to simple-mind-map/src/layouts/_CatalogOrganization.js index b42c644b..e2796840 100644 --- a/simple-mind-map/src/layouts/Fishbone.js +++ b/simple-mind-map/src/layouts/_CatalogOrganization.js @@ -8,7 +8,8 @@ import merge from 'deepmerge' * javascript comment * @Author: 王林25 * @Date: 2021-04-08 16:25:07 - * @Desc: 鱼骨图 + * @Desc: 目录组织图 + * 思路:第一轮只计算节点的宽高,以及某个节点的所有子节点所占的高度之和,以及该节点里所有子节点中宽度最宽是多少、第二轮计算节点的left和top,需要区分二级节点和其他节点,二级节点top相同,一行依次从做向右排开,其他节点的left相同,一列从上往下依次排开 */ class Render { /** @@ -50,9 +51,9 @@ class Render { // 计算节点的left、top this.computedLeftTopValue() // 调整节点top - // this.adjustTopValue() + this.adjustTopValue() // 调整节点left - // this.adjustLeftValue() + this.adjustLeftValue() } /** @@ -62,16 +63,7 @@ class Render { * @Desc: 计算节点的width、height */ computedBaseValue() { - walk(this.renderTree, null, (node, parent, isRoot, index, layerIndex) => { - // 生长方向 - let dir = '' - if (isRoot) { - dir = '' - } else if (parent._node.isRoot) { - dir = index % 2 === 0 ? 'up' : 'down' - } else { - dir = parent._node.dir - } + walk(this.renderTree, null, (node, parent, isRoot) => { // 设置width、height let { children, @@ -80,9 +72,7 @@ class Render { let newNode = new Node({ ...props, mindMap: this.mindMap, - draw: this.draw, - dir, - layerIndex + draw: this.draw }) // 计算节点的宽高 newNode.refreshSize() @@ -99,7 +89,13 @@ class Render { node._node = newNode }, (node) => { // 遍历完子节点返回时 + // 计算节点的areaHeight,也就是子节点所占的高度之和,包括外边距 let len = node._node.children.length + if (node._node.isRoot) { + node._node.childrenAreaWidth = len ? node._node.children.reduce((h, cur) => { + return h + cur.width + }, 0) + (len + 1) * this.mindMap.opt.marginX : 0 + } node._node.childrenAreaHeight = len ? node._node.children.reduce((h, cur) => { return h + cur.height }, 0) + (len + 1) * this.mindMap.opt.marginY : 0 @@ -114,126 +110,24 @@ class Render { */ computedLeftTopValue() { walk(this.root, null, (node) => { - // 二级节点 - if (node.isRoot && node.children && node.children.length) { - let totalLeft = node.left + node.width + this.mindMap.opt.marginX - node.children.forEach((item) => { - item.left = totalLeft - item.top = node.top + node.height / 2 - this.mindMap.opt.marginY - item.height - totalLeft += item.width + this.mindMap.opt.marginX - this.computedThirdLevelLeftTopValue(item) - }) - } - }, null, true) - } - - /** - * javascript comment - * @Author: 王林25 - * @Date: 2021-04-13 09:33:04 - * @Desc: 计算三级节点 - */ - computedThirdLevelLeftTopValue(node) { - if (node.children && node.children.length > 0) { - let totalLeft = node.left - let totalTop = node.top - this.mindMap.opt.marginY - node.children.forEach((item) => { - let h = node.height + this.mindMap.opt.marginY - let w = h / Math.tan(70) - item.left = totalLeft + w - totalLeft += w - item.top = totalTop - item.height - totalTop -= this.mindMap.opt.marginY + item.height - this.computedThirdAfterLevelLeftTopValue(item) - }) - } - } - - /** - * javascript comment - * @Author: 王林25 - * @Date: 2021-04-13 09:55:54 - * @Desc: 计算三级以后的节点 - */ - computedThirdAfterLevelLeftTopValue(root) { - let marginY = this.mindMap.opt.marginY - let marginX = this.mindMap.opt.marginX - // 计算left、top - walk(root, null, (node) => { if (node.children && node.children.length) { - let totalTop = node.top + node.height + marginY - node.children.forEach((cur) => { - cur.left = node.left + node.width / 5 + marginX - cur.top = totalTop - totalTop += cur.height + marginY - }) - } - }, null, true) - // 调整top - const updateBrothersTopValue = (node, addHeight) => { - if (node.parent) { - let childrenList = node.parent.children - let index = childrenList.findIndex((item) => { - return item === node - }) - childrenList.forEach((item, _index) => { - let _offset = 0 - if (_index > index) { - _offset = addHeight - } - item.top += _offset - // 同步更新子节点的位置 - if (item.children && item.children.length) { - this.updateChildren(item.children, 'top', _offset) - } - }) - // 更新父节点的位置 - updateBrothersTopValue(node.parent, addHeight) - } - } - walk(root, null, (node) => { - // 判断子节点的areaHeight是否大于该节点自身,大于则需要调整位置 - if (node.children && node.children.length > 0) { - let difference = node.childrenAreaHeight - marginY - updateBrothersTopValue(node, difference) - } - }, null, true) - // 调整left - const updateBrothersLeftValue = (node, w, h) => { - if (node.parent && node.parent.layerIndex > 0) { - let childrenList = node.parent.children - let index = childrenList.findIndex((item) => { - return item === node - }) - childrenList.forEach((item, _index) => { - let _w = 0 - let _h = 0 - if (_index >= index) { - _w = w - _h = -h - } - console.log(item.text, _w, _h) - item.left += _w - item.top += _h - // 同步更新子节点的位置 - if (item.children && item.children.length) { - this.updateChildren(item.children, 'left', _w) - this.updateChildren(item.children, 'left', _h) - } - }) - // 更新父节点的位置 - updateBrothersLeftValue(node.parent, w, h) - } - } - walk(root, null, (node) => { - if (node.layerIndex > 1) { - let h = node.childrenAreaHeight - marginY - if (h > 0) { - let w = h / Math.tan(70) - console.log(node.text, w, h) - // let childrenAreaWidth = getNodeWidth(node) - // let differenceX = childrenAreaWidth - node.width - // updateBrothersLeftValue(node, w, h) + if (node.isRoot) { + let left = node.left + node.width / 2 - node.childrenAreaWidth / 2 + let totalLeft = left + this.mindMap.opt.marginX + node.children.forEach((cur) => { + // left + cur.left = totalLeft + totalLeft += cur.width + this.mindMap.opt.marginX + // top + cur.top = node.top + node.height + this.mindMap.opt.marginY + }) + } else { + let totalTop = node.top + node.height + this.mindMap.opt.marginY + node.children.forEach((cur) => { + cur.left = node.left + node.width / 5 + this.mindMap.opt.marginX + cur.top = totalTop + totalTop += cur.height + this.mindMap.opt.marginY + }) } } }, null, true) diff --git a/simple-mind-map/src/layouts/OrganizationStructure.js b/simple-mind-map/src/layouts/_OrganizationStructure.js similarity index 100% rename from simple-mind-map/src/layouts/OrganizationStructure.js rename to simple-mind-map/src/layouts/_OrganizationStructure.js diff --git a/simple-mind-map/src/utils/index.js b/simple-mind-map/src/utils/index.js index 9588c686..47a4d50e 100644 --- a/simple-mind-map/src/utils/index.js +++ b/simple-mind-map/src/utils/index.js @@ -4,15 +4,15 @@ * @Date: 2021-04-06 14:13:17 * @Desc: 深度优先遍历树 */ -export const walk = (root, parent, beforeCallback, afterCallback, isRoot, layerIndex = 0) => { - beforeCallback && beforeCallback(root, parent, isRoot, layerIndex) +export const walk = (root, parent, beforeCallback, afterCallback, isRoot, layerIndex = 0, index = 0) => { + beforeCallback && beforeCallback(root, parent, isRoot, layerIndex, index) if (root.children && root.children.length > 0) { let _layerIndex = layerIndex + 1 - root.children.forEach((node) => { - walk(node, root, beforeCallback, afterCallback, false, _layerIndex) + root.children.forEach((node, nodeIndex) => { + walk(node, root, beforeCallback, afterCallback, false, _layerIndex, nodeIndex) }) } - afterCallback && afterCallback(root, parent, isRoot, layerIndex) + afterCallback && afterCallback(root, parent, isRoot, layerIndex, index) } /** @@ -118,10 +118,10 @@ export const simpleDeepClone = (data) => { } /** - * @Author: 王林 - * @Date: 2021-05-04 14:40:11 - * @Desc: 复制渲染树数据 - */ + * @Author: 王林 + * @Date: 2021-05-04 14:40:11 + * @Desc: 复制渲染树数据 + */ export const copyRenderTree = (tree, root) => { tree.data = simpleDeepClone(root.data) tree.children = [] diff --git a/web/src/config/index.js b/web/src/config/index.js index 24650e59..242c65d7 100644 --- a/web/src/config/index.js +++ b/web/src/config/index.js @@ -205,4 +205,9 @@ export const backgroundPositionList = [ name: '中下', value: 'center bottom' } -] \ No newline at end of file +] + +// 数据存储 +export const store = { + sidebarZIndex: 1//侧边栏zIndex +} \ No newline at end of file diff --git a/web/src/pages/Edit/components/BaseStyle.vue b/web/src/pages/Edit/components/BaseStyle.vue index 13d4326c..20f841c6 100644 --- a/web/src/pages/Edit/components/BaseStyle.vue +++ b/web/src/pages/Edit/components/BaseStyle.vue @@ -227,13 +227,18 @@ diff --git a/web/src/pages/Edit/components/Sidebar.vue b/web/src/pages/Edit/components/Sidebar.vue index da4ae4a4..d0ddb6a3 100644 --- a/web/src/pages/Edit/components/Sidebar.vue +++ b/web/src/pages/Edit/components/Sidebar.vue @@ -1,5 +1,10 @@ @@ -43,11 +58,11 @@ export default { border-left: 1px solid #e8e8e8; display: flex; flex-direction: column; - transition: all 0.3s; + transition: all 0.3s; - &.show { - right: 0; - } + &.show { + right: 0; + } .closeBtn { position: absolute; diff --git a/web/src/pages/Edit/components/Structure.vue b/web/src/pages/Edit/components/Structure.vue new file mode 100644 index 00000000..6aaf7001 --- /dev/null +++ b/web/src/pages/Edit/components/Structure.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/web/src/pages/Edit/components/Style.vue b/web/src/pages/Edit/components/Style.vue index 3e0f279c..469df5a2 100644 --- a/web/src/pages/Edit/components/Style.vue +++ b/web/src/pages/Edit/components/Style.vue @@ -261,11 +261,14 @@ export default { }, created() { this.$bus.$on("node_active", (...args) => { - this.activeTab = "normal"; - let activeNodes = args[1]; - this.activeNode = activeNodes[0]; - this.$refs.sidebar.show = activeNodes.length > 0; - this.initNodeStyle(); + this.$refs.sidebar.show = false; + this.$nextTick(() => { + this.activeTab = "normal"; + let activeNodes = args[1]; + this.activeNode = activeNodes[0]; + this.$refs.sidebar.show = activeNodes.length > 0; + this.initNodeStyle(); + }); }); }, methods: { diff --git a/web/src/pages/Edit/components/Theme.vue b/web/src/pages/Edit/components/Theme.vue index 3b87b7a8..403433c6 100644 --- a/web/src/pages/Edit/components/Theme.vue +++ b/web/src/pages/Edit/components/Theme.vue @@ -44,8 +44,11 @@ export default { }, created() { this.$bus.$on("showTheme", () => { - this.theme = this.mindMap.getTheme(); - this.$refs.sidebar.show = true; + this.$refs.sidebar.show = false; + this.$nextTick(() => { + this.theme = this.mindMap.getTheme(); + this.$refs.sidebar.show = true; + }); }); }, methods: { diff --git a/web/src/pages/Edit/components/Toolbar.vue b/web/src/pages/Edit/components/Toolbar.vue index 32efc9fe..1fdc9895 100644 --- a/web/src/pages/Edit/components/Toolbar.vue +++ b/web/src/pages/Edit/components/Toolbar.vue @@ -98,6 +98,10 @@ 主题 +

+ + 结构 +