Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ecb199608 | ||
|
|
e093cb1741 | ||
|
|
a63a92c423 | ||
|
|
2c4f065626 | ||
|
|
ae5d4dd2a6 | ||
|
|
e574883e5f | ||
|
|
1bf60c49c7 | ||
|
|
ddc173cf84 | ||
|
|
2c71c7d102 | ||
|
|
f6cb08bdaa | ||
|
|
05728de21b | ||
|
|
b51027f641 | ||
|
|
ea4fdf8290 | ||
|
|
55796b4e39 | ||
|
|
e534c3138d | ||
|
|
e185abd223 | ||
|
|
3b73f72866 | ||
|
|
0db2f47133 | ||
|
|
80c7ec0fac | ||
|
|
780ce363de | ||
|
|
eaa8929457 | ||
|
|
ad0f62a5ac | ||
|
|
31f21ce013 | ||
|
|
7a20ce2f79 | ||
|
|
b98e7a97ec | ||
|
|
0bb50b3371 | ||
|
|
a03091b28c | ||
|
|
6c33984b8c | ||
|
|
28a4be0631 | ||
|
|
b8a3be7a62 | ||
|
|
259d4028f3 | ||
|
|
fb1251afc1 | ||
|
|
06e3fd428a |
37
README.md
@@ -86,7 +86,7 @@ const mindMap = new MindMap({
|
||||
|
||||
# License
|
||||
|
||||
MIT
|
||||
[MIT](./LICENSE)
|
||||
|
||||
# 微信交流群
|
||||
|
||||
@@ -96,11 +96,44 @@ MIT
|
||||
|
||||
# 请作者喝杯咖啡
|
||||
|
||||
开源不易,如果本项目有帮助到你的话,可以考虑请作者喝杯咖啡哟~
|
||||
|
||||
> 厚椰乳一盒 + 纯牛奶半盒 + 冰块 + 咖啡液 = 生椰拿铁 yyds
|
||||
|
||||
> 转账请备注哦~你的头像和名称会出现在[文档页面](https://wanglin2.github.io/mind-map/#/doc/zh/introduction/%E8%AF%B7%E4%BD%9C%E8%80%85%E5%96%9D%E6%9D%AF%E5%92%96%E5%95%A1)
|
||||
> 转账请备注【思维导图】。你的头像和名字将会出现在下面和[文档页面](https://wanglin2.github.io/mind-map/#/doc/zh/introduction/%E8%AF%B7%E4%BD%9C%E8%80%85%E5%96%9D%E6%9D%AF%E5%92%96%E5%95%A1)
|
||||
|
||||
<p>
|
||||
<img src="./web/src/assets/img/alipay.jpg" style="width: 300px" />
|
||||
<img src="./web/src/assets/img/wechat.jpg" style="width: 300px" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/Think.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>Think</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/志斌.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>志斌</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/小土渣的宇宙.jpeg" style="width: 50px;height: 50px;" />
|
||||
<span>小土渣的宇宙</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/qp.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>qp</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/ZXR.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>ZXR</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/花儿朵朵.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>花儿朵朵</span>
|
||||
</span>
|
||||
<span>
|
||||
<img src="./web/src/assets/avatar/suka.jpg" style="width: 50px;height: 50px;" />
|
||||
<span>suka</span>
|
||||
</span>
|
||||
</p>
|
||||
BIN
qrcode.jpg
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 184 KiB |
@@ -194,7 +194,7 @@ class MindMap {
|
||||
this.opt.layout = layout
|
||||
this.view.reset()
|
||||
this.renderer.setLayout()
|
||||
this.render()
|
||||
this.render(null, CONSTANTS.CHANGE_LAYOUT)
|
||||
}
|
||||
|
||||
// 执行命令
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "simple-mind-map",
|
||||
"version": "0.6.5",
|
||||
"version": "0.6.6",
|
||||
"description": "一个简单的web在线思维导图",
|
||||
"authors": [
|
||||
{
|
||||
|
||||
@@ -157,6 +157,7 @@ export const themeList = [
|
||||
// 常量
|
||||
export const CONSTANTS = {
|
||||
CHANGE_THEME: 'changeTheme',
|
||||
CHANGE_LAYOUT: 'changeLayout',
|
||||
SET_DATA: 'setData',
|
||||
TRANSFORM_TO_NORMAL_NODE: 'transformAllNodesToNormalNode',
|
||||
MODE: {
|
||||
@@ -170,7 +171,8 @@ export const CONSTANTS = {
|
||||
CATALOG_ORGANIZATION: 'catalogOrganization',
|
||||
TIMELINE: 'timeline',
|
||||
TIMELINE2: 'timeline2',
|
||||
FISHBONE: 'fishbone'
|
||||
FISHBONE: 'fishbone',
|
||||
VERTICAL_TIMELINE: 'verticalTimeline'
|
||||
},
|
||||
DIR: {
|
||||
UP: 'up',
|
||||
@@ -206,8 +208,10 @@ export const CONSTANTS = {
|
||||
BOTTOM: 'bottom',
|
||||
CENTER: 'center'
|
||||
},
|
||||
TIMELINE_DIR: {
|
||||
LAYOUT_GROW_DIR: {
|
||||
LEFT: 'left',
|
||||
TOP: 'top',
|
||||
RIGHT: 'right',
|
||||
BOTTOM: 'bottom'
|
||||
}
|
||||
}
|
||||
@@ -246,6 +250,10 @@ export const layoutList = [
|
||||
name: '时间轴2',
|
||||
value: CONSTANTS.LAYOUT.TIMELINE2,
|
||||
},
|
||||
{
|
||||
name: '竖向时间轴',
|
||||
value: CONSTANTS.LAYOUT.VERTICAL_TIMELINE,
|
||||
},
|
||||
{
|
||||
name: '鱼骨图',
|
||||
value: CONSTANTS.LAYOUT.FISHBONE,
|
||||
@@ -258,6 +266,7 @@ export const layoutValueList = [
|
||||
CONSTANTS.LAYOUT.ORGANIZATION_STRUCTURE,
|
||||
CONSTANTS.LAYOUT.TIMELINE,
|
||||
CONSTANTS.LAYOUT.TIMELINE2,
|
||||
CONSTANTS.LAYOUT.VERTICAL_TIMELINE,
|
||||
CONSTANTS.LAYOUT.FISHBONE
|
||||
]
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import MindMap from '../../layouts/MindMap'
|
||||
import CatalogOrganization from '../../layouts/CatalogOrganization'
|
||||
import OrganizationStructure from '../../layouts/OrganizationStructure'
|
||||
import Timeline from '../../layouts/Timeline'
|
||||
import VerticalTimeline from '../../layouts/VerticalTimeline'
|
||||
import Fishbone from '../../layouts/Fishbone'
|
||||
import TextEdit from './TextEdit'
|
||||
import { copyNodeTree, simpleDeepClone, walk } from '../../utils'
|
||||
@@ -25,6 +26,8 @@ const layouts = {
|
||||
[CONSTANTS.LAYOUT.TIMELINE]: Timeline,
|
||||
// 时间轴2
|
||||
[CONSTANTS.LAYOUT.TIMELINE2]: Timeline,
|
||||
// 竖向时间轴
|
||||
[CONSTANTS.LAYOUT.VERTICAL_TIMELINE]: VerticalTimeline,
|
||||
// 鱼骨图
|
||||
[CONSTANTS.LAYOUT.FISHBONE]: Fishbone,
|
||||
}
|
||||
@@ -693,11 +696,11 @@ class Render {
|
||||
if (node.isRoot) {
|
||||
return
|
||||
}
|
||||
let copyData = copyNodeTree({}, node, false, true)
|
||||
// let copyData = copyNodeTree({}, node, false, true)
|
||||
this.removeActiveNode(node)
|
||||
this.removeOneNode(node)
|
||||
this.mindMap.emit('node_active', null, this.activeNodeList)
|
||||
toNode.nodeData.children.push(copyData)
|
||||
toNode.nodeData.children.push(node.nodeData)
|
||||
this.mindMap.render()
|
||||
if (toNode.isRoot) {
|
||||
toNode.destroy()
|
||||
|
||||
@@ -65,7 +65,8 @@ export default class TextEdit {
|
||||
}
|
||||
|
||||
// 显示文本编辑框
|
||||
async show(node) {
|
||||
// isInserting:是否是刚创建的节点
|
||||
async show(node, e, isInserting = false) {
|
||||
// 使用了自定义节点内容那么不响应编辑事件
|
||||
if (node.isUseCustomNodeContent()) {
|
||||
return
|
||||
@@ -74,7 +75,7 @@ export default class TextEdit {
|
||||
if (typeof beforeTextEdit === 'function') {
|
||||
let isShow = false
|
||||
try {
|
||||
isShow = await beforeTextEdit(node)
|
||||
isShow = await beforeTextEdit(node, isInserting)
|
||||
} catch (error) {
|
||||
isShow = false
|
||||
}
|
||||
@@ -85,7 +86,7 @@ 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)
|
||||
this.mindMap.richText.showEditText(node, rect, isInserting)
|
||||
return
|
||||
}
|
||||
this.showEditTextBox(node, rect)
|
||||
|
||||
@@ -269,15 +269,7 @@ class Node {
|
||||
this.group.add(this.shapeNode)
|
||||
this.updateNodeShape()
|
||||
// 渲染一个隐藏的矩形区域,用来触发展开收起按钮的显示
|
||||
if (!this.mindMap.opt.alwaysShowExpandBtn) {
|
||||
if (!this._unVisibleRectRegionNode) {
|
||||
this._unVisibleRectRegionNode = new Rect()
|
||||
}
|
||||
this._unVisibleRectRegionNode.fill({
|
||||
color: 'transparent'
|
||||
}).size(this.expandBtnSize, height).x(width).y(0)
|
||||
this.group.add(this._unVisibleRectRegionNode)
|
||||
}
|
||||
this.renderExpandBtnPlaceholderRect()
|
||||
// 概要节点添加一个带所属节点id的类名
|
||||
if (this.isGeneralization && this.generalizationBelongNode) {
|
||||
this.group.addClass('generalization_' + this.generalizationBelongNode.uid)
|
||||
@@ -289,7 +281,7 @@ class Node {
|
||||
foreignObject.height(height)
|
||||
foreignObject.add(SVG(this._customNodeContent))
|
||||
this.group.add(foreignObject)
|
||||
return
|
||||
return
|
||||
}
|
||||
// 图片节点
|
||||
let imgHeight = 0
|
||||
@@ -364,6 +356,21 @@ class Node {
|
||||
this.group.add(textContentNested)
|
||||
}
|
||||
|
||||
// 渲染展开收起按钮的隐藏占位元素
|
||||
renderExpandBtnPlaceholderRect() {
|
||||
if (!this.mindMap.opt.alwaysShowExpandBtn) {
|
||||
let { width, height } = this
|
||||
if (!this._unVisibleRectRegionNode) {
|
||||
this._unVisibleRectRegionNode = new Rect()
|
||||
this._unVisibleRectRegionNode.fill({
|
||||
color: 'transparent'
|
||||
})
|
||||
this.group.add(this._unVisibleRectRegionNode)
|
||||
}
|
||||
this.renderer.layout.renderExpandBtnRect(this._unVisibleRectRegionNode, this.expandBtnSize, width, height, this)
|
||||
}
|
||||
}
|
||||
|
||||
// 给节点绑定事件
|
||||
bindGroupEvent() {
|
||||
// 单击事件,选中节点
|
||||
@@ -433,7 +440,8 @@ class Node {
|
||||
// 右键菜单事件
|
||||
this.group.on('contextmenu', e => {
|
||||
// 按住ctrl键点击鼠标左键不知为何触发的是contextmenu事件
|
||||
if (this.mindMap.opt.readonly || e.ctrlKey) {// || this.isGeneralization
|
||||
if (this.mindMap.opt.readonly || e.ctrlKey) {
|
||||
// || this.isGeneralization
|
||||
return
|
||||
}
|
||||
e.stopPropagation()
|
||||
@@ -467,8 +475,11 @@ class Node {
|
||||
if (!this.group) {
|
||||
return
|
||||
}
|
||||
let { enableNodeTransitionMove, nodeTransitionMoveDuration, alwaysShowExpandBtn } =
|
||||
this.mindMap.opt
|
||||
let {
|
||||
enableNodeTransitionMove,
|
||||
nodeTransitionMoveDuration,
|
||||
alwaysShowExpandBtn
|
||||
} = this.mindMap.opt
|
||||
if (alwaysShowExpandBtn) {
|
||||
// 需要移除展开收缩按钮
|
||||
if (this._expandBtn && this.nodeData.children.length <= 0) {
|
||||
@@ -543,6 +554,10 @@ class Node {
|
||||
this.needLayout = false
|
||||
this.layout()
|
||||
}
|
||||
if (this.needRerenderExpandBtnPlaceholderRect) {
|
||||
this.needRerenderExpandBtnPlaceholderRect = false
|
||||
this.renderExpandBtnPlaceholderRect()
|
||||
}
|
||||
this.update()
|
||||
}
|
||||
// 子节点
|
||||
@@ -578,7 +593,7 @@ class Node {
|
||||
delete this.nodeData.inserting
|
||||
this.active()
|
||||
setTimeout(() => {
|
||||
this.mindMap.emit('node_dblclick', this)
|
||||
this.mindMap.emit('node_dblclick', this, null, true)
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
@@ -783,7 +798,7 @@ class Node {
|
||||
|
||||
// 获取padding值
|
||||
getPaddingVale() {
|
||||
let { isActive }= this.nodeData.data
|
||||
let { isActive } = this.nodeData.data
|
||||
return {
|
||||
paddingX: this.getStyle('paddingX', true, isActive),
|
||||
paddingY: this.getStyle('paddingY', true, isActive)
|
||||
|
||||
@@ -82,12 +82,12 @@ class View {
|
||||
// 鼠标滚轮,向上和向左,都是缩小
|
||||
case CONSTANTS.DIR.UP:
|
||||
case CONSTANTS.DIR.LEFT:
|
||||
mousewheelZoomActionReverse ? this.enlarge(cx, cy) : this.narrow(cx, cy)
|
||||
mousewheelZoomActionReverse ? this.enlarge(cx, cy, isTouchPad) : this.narrow(cx, cy, isTouchPad)
|
||||
break
|
||||
// 鼠标滚轮,向下和向右,都是放大
|
||||
case CONSTANTS.DIR.DOWN:
|
||||
case CONSTANTS.DIR.RIGHT:
|
||||
mousewheelZoomActionReverse ? this.narrow(cx, cy) : this.enlarge(cx, cy)
|
||||
mousewheelZoomActionReverse ? this.narrow(cx, cy, isTouchPad) : this.enlarge(cx, cy, isTouchPad)
|
||||
break
|
||||
}
|
||||
} else {// 鼠标滚轮事件控制画布移动
|
||||
@@ -199,16 +199,18 @@ class View {
|
||||
}
|
||||
|
||||
// 缩小
|
||||
narrow(cx, cy) {
|
||||
const scale = Math.max(this.scale - this.mindMap.opt.scaleRatio, 0.1)
|
||||
narrow(cx, cy, isTouchPad) {
|
||||
const scaleRatio = this.mindMap.opt.scaleRatio / (isTouchPad ? 5 : 1)
|
||||
const scale = Math.max(this.scale - scaleRatio, 0.1)
|
||||
this.scaleInCenter(scale, cx, cy)
|
||||
this.transform()
|
||||
this.mindMap.emit('scale', this.scale)
|
||||
}
|
||||
|
||||
// 放大
|
||||
enlarge(cx, cy) {
|
||||
const scale = this.scale + this.mindMap.opt.scaleRatio
|
||||
enlarge(cx, cy, isTouchPad) {
|
||||
const scaleRatio = this.mindMap.opt.scaleRatio / (isTouchPad ? 5 : 1)
|
||||
const scale = this.scale + scaleRatio
|
||||
this.scaleInCenter(scale, cx, cy)
|
||||
this.transform()
|
||||
this.mindMap.emit('scale', this.scale)
|
||||
|
||||
@@ -48,6 +48,20 @@ class Base {
|
||||
return [CONSTANTS.CHANGE_THEME, CONSTANTS.TRANSFORM_TO_NORMAL_NODE].includes(this.renderer.renderSource)
|
||||
}
|
||||
|
||||
// 层级类型改变
|
||||
checkIsLayerTypeChange(oldIndex, newIndex) {
|
||||
if (oldIndex >= 2 && newIndex >= 2) return false
|
||||
if (oldIndex >= 2 && newIndex < 2) return true
|
||||
if (oldIndex < 2 && newIndex >= 2) return true
|
||||
}
|
||||
|
||||
// 检查是否是结构布局改变重新渲染展开收起按钮占位元素
|
||||
checkIsLayoutChangeRerenderExpandBtnPlaceholderRect(node) {
|
||||
if (this.renderer.renderSource === CONSTANTS.CHANGE_LAYOUT) {
|
||||
node.needRerenderExpandBtnPlaceholderRect = true
|
||||
}
|
||||
}
|
||||
|
||||
// 创建节点实例
|
||||
createNode(data, parent, isRoot, layerIndex) {
|
||||
// 创建节点
|
||||
@@ -55,11 +69,13 @@ class Base {
|
||||
// 数据上保存了节点引用,那么直接复用节点
|
||||
if (data && data._node && !this.renderer.reRender) {
|
||||
newNode = data._node
|
||||
let isLayerTypeChange = this.checkIsLayerTypeChange(newNode.layerIndex, layerIndex)
|
||||
newNode.reset()
|
||||
newNode.layerIndex = layerIndex
|
||||
this.cacheNode(data._node.uid, newNode)
|
||||
this.checkIsLayoutChangeRerenderExpandBtnPlaceholderRect(newNode)
|
||||
// 主题或主题配置改变了需要重新计算节点大小和布局
|
||||
if (this.checkIsNeedResizeSources()) {
|
||||
if (this.checkIsNeedResizeSources() || isLayerTypeChange) {
|
||||
newNode.getSize()
|
||||
newNode.needLayout = true
|
||||
}
|
||||
@@ -68,16 +84,18 @@ class Base {
|
||||
newNode = this.lru.get(data.data.uid)
|
||||
// 保存该节点上一次的数据
|
||||
let lastData = JSON.stringify(newNode.nodeData.data)
|
||||
let isLayerTypeChange = this.checkIsLayerTypeChange(newNode.layerIndex, layerIndex)
|
||||
newNode.reset()
|
||||
newNode.nodeData = newNode.handleData(data || {})
|
||||
newNode.layerIndex = layerIndex
|
||||
this.cacheNode(data.data.uid, newNode)
|
||||
this.checkIsLayoutChangeRerenderExpandBtnPlaceholderRect(newNode)
|
||||
data._node = newNode
|
||||
// 主题或主题配置改变了需要重新计算节点大小和布局
|
||||
let isResizeSource = this.checkIsNeedResizeSources()
|
||||
// 节点数据改变了需要重新计算节点大小和布局
|
||||
let isNodeDataChange = lastData !== JSON.stringify(data.data)
|
||||
if (isResizeSource || isNodeDataChange) {
|
||||
if (isResizeSource || isNodeDataChange || isLayerTypeChange) {
|
||||
newNode.getSize()
|
||||
newNode.needLayout = true
|
||||
}
|
||||
|
||||
@@ -349,6 +349,11 @@ class CatalogOrganization extends Base {
|
||||
gNode.left = right + generalizationNodeMargin
|
||||
gNode.top = top + (bottom - top - gNode.height) / 2
|
||||
}
|
||||
|
||||
// 渲染展开收起按钮的隐藏占位元素
|
||||
renderExpandBtnRect(rect, expandBtnSize, width, height, node) {
|
||||
rect.size(width, expandBtnSize).x(0).y(height)
|
||||
}
|
||||
}
|
||||
|
||||
export default CatalogOrganization
|
||||
|
||||
@@ -51,8 +51,8 @@ class Fishbone extends Base {
|
||||
// 节点生长方向
|
||||
newNode.dir =
|
||||
index % 2 === 0
|
||||
? CONSTANTS.TIMELINE_DIR.TOP
|
||||
: CONSTANTS.TIMELINE_DIR.BOTTOM
|
||||
? CONSTANTS.LAYOUT_GROW_DIR.TOP
|
||||
: CONSTANTS.LAYOUT_GROW_DIR.BOTTOM
|
||||
}
|
||||
// 计算二级节点的top值
|
||||
if (parent._node.isRoot) {
|
||||
@@ -222,7 +222,7 @@ class Fishbone extends Base {
|
||||
|
||||
// 检查节点是否是上方节点
|
||||
checkIsTop(node) {
|
||||
return node.dir === CONSTANTS.TIMELINE_DIR.TOP
|
||||
return node.dir === CONSTANTS.LAYOUT_GROW_DIR.TOP
|
||||
}
|
||||
|
||||
// 绘制连线,连接该节点到其子节点
|
||||
@@ -239,7 +239,7 @@ class Fishbone extends Base {
|
||||
// 当前节点是根节点
|
||||
// 根节点的子节点是和根节点同一水平线排列
|
||||
let maxx = -Infinity
|
||||
node.children.forEach((item) => {
|
||||
node.children.forEach(item => {
|
||||
if (item.left > maxx) {
|
||||
maxx = item.left
|
||||
}
|
||||
@@ -250,15 +250,15 @@ class Fishbone extends Base {
|
||||
let line = this.draw.path()
|
||||
if (this.checkIsTop(item)) {
|
||||
line.plot(
|
||||
`M ${nodeLineX - offsetX},${item.top + item.height + offset} L ${item.left},${
|
||||
item.top + item.height
|
||||
}`
|
||||
`M ${nodeLineX - offsetX},${item.top + item.height + offset} L ${
|
||||
item.left
|
||||
},${item.top + item.height}`
|
||||
)
|
||||
} else {
|
||||
line.plot(
|
||||
`M ${nodeLineX - offsetX},${
|
||||
item.top - offset
|
||||
} L ${nodeLineX},${item.top}`
|
||||
`M ${nodeLineX - offsetX},${item.top - offset} L ${nodeLineX},${
|
||||
item.top
|
||||
}`
|
||||
)
|
||||
}
|
||||
node.style.line(line)
|
||||
@@ -373,6 +373,27 @@ class Fishbone extends Base {
|
||||
gNode.left = right + generalizationNodeMargin
|
||||
gNode.top = top + (bottom - top - gNode.height) / 2
|
||||
}
|
||||
|
||||
// 渲染展开收起按钮的隐藏占位元素
|
||||
renderExpandBtnRect(rect, expandBtnSize, width, height, node) {
|
||||
let dir = ''
|
||||
if (node.dir === CONSTANTS.LAYOUT_GROW_DIR.TOP) {
|
||||
dir =
|
||||
node.layerIndex === 1
|
||||
? CONSTANTS.LAYOUT_GROW_DIR.TOP
|
||||
: CONSTANTS.LAYOUT_GROW_DIR.BOTTOM
|
||||
} else {
|
||||
dir =
|
||||
node.layerIndex === 1
|
||||
? CONSTANTS.LAYOUT_GROW_DIR.BOTTOM
|
||||
: CONSTANTS.LAYOUT_GROW_DIR.TOP
|
||||
}
|
||||
if (dir === CONSTANTS.LAYOUT_GROW_DIR.TOP) {
|
||||
rect.size(width, expandBtnSize).x(0).y(-expandBtnSize)
|
||||
} else {
|
||||
rect.size(width, expandBtnSize).x(0).y(height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Fishbone
|
||||
|
||||
@@ -52,8 +52,8 @@ class Fishbone extends Base {
|
||||
// 节点生长方向
|
||||
newNode.dir =
|
||||
index % 2 === 0
|
||||
? CONSTANTS.TIMELINE_DIR.TOP
|
||||
: CONSTANTS.TIMELINE_DIR.BOTTOM
|
||||
? CONSTANTS.LAYOUT_GROW_DIR.TOP
|
||||
: CONSTANTS.LAYOUT_GROW_DIR.BOTTOM
|
||||
}
|
||||
// 计算二级节点的top值
|
||||
if (parent._node.isRoot) {
|
||||
|
||||
@@ -52,8 +52,8 @@ class Fishbone extends Base {
|
||||
// 节点生长方向
|
||||
newNode.dir =
|
||||
index % 2 === 0
|
||||
? CONSTANTS.TIMELINE_DIR.TOP
|
||||
: CONSTANTS.TIMELINE_DIR.BOTTOM
|
||||
? CONSTANTS.LAYOUT_GROW_DIR.TOP
|
||||
: CONSTANTS.LAYOUT_GROW_DIR.BOTTOM
|
||||
}
|
||||
// 计算二级节点的top值
|
||||
if (parent._node.isRoot) {
|
||||
@@ -281,7 +281,7 @@ class Fishbone extends Base {
|
||||
if (
|
||||
node.parent &&
|
||||
node.parent.isRoot &&
|
||||
node.dir === CONSTANTS.TIMELINE_DIR.TOP
|
||||
node.dir === CONSTANTS.LAYOUT_GROW_DIR.TOP
|
||||
) {
|
||||
line.plot(
|
||||
`M ${x},${top} L ${x + lineLength},${
|
||||
|
||||
@@ -172,9 +172,7 @@ class LogicalStructure extends Base {
|
||||
let x2 = item.left
|
||||
let y2 = item.top + item.height / 2
|
||||
// 节点使用横线风格,需要额外渲染横线
|
||||
let nodeUseLineStyleOffset = nodeUseLineStyle
|
||||
? item.width
|
||||
: 0
|
||||
let nodeUseLineStyleOffset = nodeUseLineStyle ? item.width : 0
|
||||
y1 = nodeUseLineStyle && !node.isRoot ? y1 + height / 2 : y1
|
||||
y2 = nodeUseLineStyle ? y2 + item.height / 2 : y2
|
||||
let path = `M ${x1},${y1} L ${x1 + s1},${y1} L ${x1 + s1},${y2} L ${
|
||||
@@ -260,10 +258,7 @@ class LogicalStructure extends Base {
|
||||
if (_x === translateX && _y === translateY) {
|
||||
return
|
||||
}
|
||||
btn.translate(
|
||||
_x - translateX,
|
||||
_y - translateY
|
||||
)
|
||||
btn.translate(_x - translateX, _y - translateY)
|
||||
}
|
||||
|
||||
// 创建概要节点
|
||||
@@ -286,6 +281,11 @@ class LogicalStructure extends Base {
|
||||
gNode.left = right + generalizationNodeMargin
|
||||
gNode.top = top + (bottom - top - gNode.height) / 2
|
||||
}
|
||||
|
||||
// 渲染展开收起按钮的隐藏占位元素
|
||||
renderExpandBtnRect(rect, expandBtnSize, width, height, node) {
|
||||
rect.size(expandBtnSize, height).x(width).y(0)
|
||||
}
|
||||
}
|
||||
|
||||
export default LogicalStructure
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Base from './Base'
|
||||
import { walk, asyncRun } from '../utils'
|
||||
import { CONSTANTS } from '../constants/constant'
|
||||
|
||||
// 思维导图
|
||||
class MindMap extends Base {
|
||||
@@ -45,11 +46,14 @@ class MindMap extends Base {
|
||||
newNode.dir = parent._node.dir
|
||||
} else {
|
||||
// 节点生长方向
|
||||
newNode.dir = index % 2 === 0 ? 'right' : 'left'
|
||||
newNode.dir =
|
||||
index % 2 === 0
|
||||
? CONSTANTS.LAYOUT_GROW_DIR.RIGHT
|
||||
: CONSTANTS.LAYOUT_GROW_DIR.LEFT
|
||||
}
|
||||
// 根据生长方向定位到父节点的左侧或右侧
|
||||
newNode.left =
|
||||
newNode.dir === 'right'
|
||||
newNode.dir === CONSTANTS.LAYOUT_GROW_DIR.RIGHT
|
||||
? parent._node.left +
|
||||
parent._node.width +
|
||||
this.getMarginX(layerIndex)
|
||||
@@ -72,7 +76,7 @@ class MindMap extends Base {
|
||||
let leftChildrenAreaHeight = 0
|
||||
let rightChildrenAreaHeight = 0
|
||||
cur._node.children.forEach(item => {
|
||||
if (item.dir === 'left') {
|
||||
if (item.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT) {
|
||||
leftLen++
|
||||
leftChildrenAreaHeight += item.height
|
||||
} else {
|
||||
@@ -109,7 +113,7 @@ class MindMap extends Base {
|
||||
let leftTotalTop = baseTop - node.leftChildrenAreaHeight / 2
|
||||
let rightTotalTop = baseTop - node.rightChildrenAreaHeight / 2
|
||||
node.children.forEach(cur => {
|
||||
if (cur.dir === 'left') {
|
||||
if (cur.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT) {
|
||||
cur.top = leftTotalTop
|
||||
leftTotalTop += cur.height + marginY
|
||||
} else {
|
||||
@@ -162,7 +166,10 @@ class MindMap extends Base {
|
||||
return
|
||||
}
|
||||
let _offset = 0
|
||||
let addHeight = item.dir === 'left' ? leftAddHeight : rightAddHeight
|
||||
let addHeight =
|
||||
item.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT
|
||||
? leftAddHeight
|
||||
: rightAddHeight
|
||||
// 上面的节点往上移
|
||||
if (_index < index) {
|
||||
_offset = -addHeight
|
||||
@@ -208,10 +215,8 @@ class MindMap extends Base {
|
||||
let x1 = 0
|
||||
let _s = 0
|
||||
// 节点使用横线风格,需要额外渲染横线
|
||||
let nodeUseLineStyleOffset = nodeUseLineStyle
|
||||
? item.width
|
||||
: 0
|
||||
if (item.dir === 'left') {
|
||||
let nodeUseLineStyleOffset = nodeUseLineStyle ? item.width : 0
|
||||
if (item.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT) {
|
||||
_s = -s1
|
||||
x1 = node.layerIndex === 0 ? left : left - expandBtnSize
|
||||
nodeUseLineStyleOffset = -nodeUseLineStyleOffset
|
||||
@@ -220,7 +225,10 @@ class MindMap extends Base {
|
||||
x1 = node.layerIndex === 0 ? left + width : left + width + expandBtnSize
|
||||
}
|
||||
let y1 = top + height / 2
|
||||
let x2 = item.dir === 'left' ? item.left + item.width : item.left
|
||||
let x2 =
|
||||
item.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT
|
||||
? item.left + item.width
|
||||
: item.left
|
||||
let y2 = item.top + item.height / 2
|
||||
y1 = nodeUseLineStyle && !node.isRoot ? y1 + height / 2 : y1
|
||||
y2 = nodeUseLineStyle ? y2 + item.height / 2 : y2
|
||||
@@ -246,18 +254,21 @@ class MindMap extends Base {
|
||||
let x1 =
|
||||
node.layerIndex === 0
|
||||
? left + width / 2
|
||||
: item.dir === 'left'
|
||||
: item.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT
|
||||
? left - expandBtnSize
|
||||
: left + width + expandBtnSize
|
||||
let y1 = top + height / 2
|
||||
let x2 = item.dir === 'left' ? item.left + item.width : item.left
|
||||
let x2 =
|
||||
item.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT
|
||||
? item.left + item.width
|
||||
: item.left
|
||||
let y2 = item.top + item.height / 2
|
||||
y1 = nodeUseLineStyle && !node.isRoot ? y1 + height / 2 : y1
|
||||
y2 = nodeUseLineStyle ? y2 + item.height / 2 : y2
|
||||
// 节点使用横线风格,需要额外渲染横线
|
||||
let nodeUseLineStylePath = ''
|
||||
if (nodeUseLineStyle) {
|
||||
if (item.dir === 'left') {
|
||||
if (item.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT) {
|
||||
nodeUseLineStylePath = ` L ${item.left},${y2}`
|
||||
} else {
|
||||
nodeUseLineStylePath = ` L ${item.left + item.width},${y2}`
|
||||
@@ -283,11 +294,14 @@ class MindMap extends Base {
|
||||
let x1 =
|
||||
node.layerIndex === 0
|
||||
? left + width / 2
|
||||
: item.dir === 'left'
|
||||
: item.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT
|
||||
? left - expandBtnSize
|
||||
: left + width + expandBtnSize
|
||||
let y1 = top + height / 2
|
||||
let x2 = item.dir === 'left' ? item.left + item.width : item.left
|
||||
let x2 =
|
||||
item.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT
|
||||
? item.left + item.width
|
||||
: item.left
|
||||
let y2 = item.top + item.height / 2
|
||||
let path = ''
|
||||
y1 = nodeUseLineStyle && !node.isRoot ? y1 + height / 2 : y1
|
||||
@@ -295,7 +309,7 @@ class MindMap extends Base {
|
||||
// 节点使用横线风格,需要额外渲染横线
|
||||
let nodeUseLineStylePath = ''
|
||||
if (this.mindMap.themeConfig.nodeUseLineStyle) {
|
||||
if (item.dir === 'left') {
|
||||
if (item.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT) {
|
||||
nodeUseLineStylePath = ` L ${item.left},${y2}`
|
||||
} else {
|
||||
nodeUseLineStylePath = ` L ${item.left + item.width},${y2}`
|
||||
@@ -320,7 +334,8 @@ class MindMap extends Base {
|
||||
? height / 2
|
||||
: 0
|
||||
// 位置没有变化则返回
|
||||
let _x = (node.dir === 'left' ? 0 - expandBtnSize : width)
|
||||
let _x =
|
||||
node.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT ? 0 - expandBtnSize : width
|
||||
let _y = height / 2 + nodeUseLineStyleOffset
|
||||
if (_x === translateX && _y === translateY) {
|
||||
return
|
||||
@@ -332,7 +347,7 @@ class MindMap extends Base {
|
||||
|
||||
// 创建概要节点
|
||||
renderGeneralization(node, gLine, gNode) {
|
||||
let isLeft = node.dir === 'left'
|
||||
let isLeft = node.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT
|
||||
let {
|
||||
top,
|
||||
bottom,
|
||||
@@ -358,6 +373,15 @@ class MindMap extends Base {
|
||||
(isLeft ? gNode.width : 0)
|
||||
gNode.top = top + (bottom - top - gNode.height) / 2
|
||||
}
|
||||
|
||||
// 渲染展开收起按钮的隐藏占位元素
|
||||
renderExpandBtnRect(rect, expandBtnSize, width, height, node) {
|
||||
if (node.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT) {
|
||||
rect.size(expandBtnSize, height).x(-expandBtnSize).y(0)
|
||||
} else {
|
||||
rect.size(expandBtnSize, height).x(width).y(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default MindMap
|
||||
|
||||
@@ -255,6 +255,11 @@ class OrganizationStructure extends Base {
|
||||
gNode.top = bottom + generalizationNodeMargin
|
||||
gNode.left = left + (right - left - gNode.width) / 2
|
||||
}
|
||||
|
||||
// 渲染展开收起按钮的隐藏占位元素
|
||||
renderExpandBtnRect(rect, expandBtnSize, width, height, node) {
|
||||
rect.size(width, expandBtnSize).x(0).y(height)
|
||||
}
|
||||
}
|
||||
|
||||
export default OrganizationStructure
|
||||
|
||||
@@ -50,8 +50,8 @@ class Timeline extends Base {
|
||||
// 节点生长方向
|
||||
newNode.dir =
|
||||
index % 2 === 0
|
||||
? CONSTANTS.TIMELINE_DIR.BOTTOM
|
||||
: CONSTANTS.TIMELINE_DIR.TOP
|
||||
? CONSTANTS.LAYOUT_GROW_DIR.BOTTOM
|
||||
: CONSTANTS.LAYOUT_GROW_DIR.TOP
|
||||
}
|
||||
} else {
|
||||
newNode.dir = ''
|
||||
@@ -151,7 +151,7 @@ class Timeline extends Base {
|
||||
if (
|
||||
parent &&
|
||||
parent.isRoot &&
|
||||
node.dir === CONSTANTS.TIMELINE_DIR.TOP
|
||||
node.dir === CONSTANTS.LAYOUT_GROW_DIR.TOP
|
||||
) {
|
||||
// 遍历二级节点的子节点
|
||||
node.children.forEach(item => {
|
||||
@@ -280,7 +280,7 @@ class Timeline extends Base {
|
||||
if (
|
||||
node.parent &&
|
||||
node.parent.isRoot &&
|
||||
node.dir === CONSTANTS.TIMELINE_DIR.TOP
|
||||
node.dir === CONSTANTS.LAYOUT_GROW_DIR.TOP
|
||||
) {
|
||||
line.plot(`M ${x},${top} L ${x},${miny}`)
|
||||
} else {
|
||||
@@ -301,7 +301,7 @@ class Timeline extends Base {
|
||||
if (
|
||||
node.parent &&
|
||||
node.parent.isRoot &&
|
||||
node.dir === CONSTANTS.TIMELINE_DIR.TOP
|
||||
node.dir === CONSTANTS.LAYOUT_GROW_DIR.TOP
|
||||
) {
|
||||
btn.translate(
|
||||
width * 0.3 - expandBtnSize / 2 - translateX,
|
||||
@@ -336,6 +336,28 @@ class Timeline extends Base {
|
||||
gNode.left = right + generalizationNodeMargin
|
||||
gNode.top = top + (bottom - top - gNode.height) / 2
|
||||
}
|
||||
|
||||
// 渲染展开收起按钮的隐藏占位元素
|
||||
renderExpandBtnRect(rect, expandBtnSize, width, height, node) {
|
||||
if (this.layout === CONSTANTS.LAYOUT.TIMELINE) {
|
||||
rect.size(width, expandBtnSize).x(0).y(height)
|
||||
} else {
|
||||
let dir = ''
|
||||
if (node.dir === CONSTANTS.LAYOUT_GROW_DIR.TOP) {
|
||||
dir =
|
||||
node.layerIndex === 1
|
||||
? CONSTANTS.LAYOUT_GROW_DIR.TOP
|
||||
: CONSTANTS.LAYOUT_GROW_DIR.BOTTOM
|
||||
} else {
|
||||
dir = CONSTANTS.LAYOUT_GROW_DIR.BOTTOM
|
||||
}
|
||||
if (dir === CONSTANTS.LAYOUT_GROW_DIR.TOP) {
|
||||
rect.size(width, expandBtnSize).x(0).y(-expandBtnSize)
|
||||
} else {
|
||||
rect.size(width, expandBtnSize).x(0).y(height)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Timeline
|
||||
|
||||
431
simple-mind-map/src/layouts/VerticalTimeline.js
Normal file
@@ -0,0 +1,431 @@
|
||||
import Base from './Base'
|
||||
import { walk, asyncRun } from '../utils'
|
||||
import { CONSTANTS } from '../constants/constant'
|
||||
|
||||
// 竖向时间轴
|
||||
class VerticalTimeline extends Base {
|
||||
// 构造函数
|
||||
constructor(opt = {}, layout) {
|
||||
super(opt)
|
||||
this.layout = layout
|
||||
}
|
||||
|
||||
// 布局
|
||||
doLayout(callback) {
|
||||
let task = [
|
||||
() => {
|
||||
this.computedBaseValue()
|
||||
},
|
||||
() => {
|
||||
this.computedTopValue()
|
||||
},
|
||||
() => {
|
||||
this.adjustLeftTopValue()
|
||||
},
|
||||
() => {
|
||||
callback(this.root)
|
||||
}
|
||||
]
|
||||
asyncRun(task)
|
||||
}
|
||||
|
||||
// 遍历数据创建节点、计算根节点的位置,计算根节点的子节点的top值
|
||||
computedBaseValue() {
|
||||
walk(
|
||||
this.renderer.renderTree,
|
||||
null,
|
||||
(cur, parent, isRoot, layerIndex, index) => {
|
||||
let newNode = this.createNode(cur, parent, isRoot, layerIndex)
|
||||
// 根节点定位在画布中心位置
|
||||
if (isRoot) {
|
||||
this.setNodeCenter(newNode)
|
||||
} else {
|
||||
// 非根节点
|
||||
// 节点生长方向
|
||||
// 三级及以下节点以上级为准
|
||||
if (parent._node.dir) {
|
||||
newNode.dir = parent._node.dir
|
||||
} else {
|
||||
newNode.dir =
|
||||
index % 2 === 0
|
||||
? CONSTANTS.LAYOUT_GROW_DIR.RIGHT
|
||||
: CONSTANTS.LAYOUT_GROW_DIR.LEFT
|
||||
}
|
||||
// 定位二级节点的left
|
||||
if (parent._node.isRoot) {
|
||||
newNode.left =
|
||||
parent._node.left +
|
||||
(cur._node.width > parent._node.width
|
||||
? -(cur._node.width - parent._node.width) / 2
|
||||
: (parent._node.width - cur._node.width) / 2)
|
||||
} else {
|
||||
newNode.left =
|
||||
newNode.dir === CONSTANTS.LAYOUT_GROW_DIR.RIGHT
|
||||
? parent._node.left +
|
||||
parent._node.width +
|
||||
this.getMarginX(layerIndex)
|
||||
: parent._node.left -
|
||||
this.getMarginX(layerIndex) -
|
||||
newNode.width
|
||||
}
|
||||
}
|
||||
if (!cur.data.expand) {
|
||||
return true
|
||||
}
|
||||
},
|
||||
(cur, parent, isRoot, layerIndex) => {
|
||||
// 返回时计算节点的areaHeight,也就是子节点所占的高度之和,包括外边距
|
||||
if (isRoot) {
|
||||
return
|
||||
}
|
||||
let len = cur.data.expand === false ? 0 : cur._node.children.length
|
||||
cur._node.childrenAreaHeight = len
|
||||
? cur._node.children.reduce((h, item) => {
|
||||
return h + item.height
|
||||
}, 0) +
|
||||
(len + 1) * this.getMarginY(layerIndex + 1)
|
||||
: 0
|
||||
},
|
||||
true,
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
// 遍历节点树计算节点的top
|
||||
computedTopValue() {
|
||||
walk(
|
||||
this.root,
|
||||
null,
|
||||
(node, parent, isRoot, layerIndex, index) => {
|
||||
if (
|
||||
node.nodeData.data.expand &&
|
||||
node.children &&
|
||||
node.children.length
|
||||
) {
|
||||
let marginY = this.getMarginY(layerIndex + 1)
|
||||
// 定位二级节点的top
|
||||
if (isRoot) {
|
||||
let top = node.top + node.height
|
||||
let totalTop = top + marginY
|
||||
node.children.forEach(cur => {
|
||||
cur.top = totalTop
|
||||
totalTop += cur.height + marginY
|
||||
})
|
||||
} else {
|
||||
// 定位三级及以下节点的top
|
||||
let marginY = this.getMarginY(layerIndex + 1)
|
||||
let baseTop = node.top + node.height / 2 + marginY
|
||||
// 第一个子节点的top值 = 该节点中心的top值 - 子节点的高度之和的一半
|
||||
let totalTop = baseTop - node.childrenAreaHeight / 2
|
||||
node.children.forEach(cur => {
|
||||
cur.top = totalTop
|
||||
totalTop += cur.height + marginY
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
null,
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
// 调整节点left、top
|
||||
adjustLeftTopValue() {
|
||||
walk(
|
||||
this.root,
|
||||
null,
|
||||
(node, parent, isRoot, layerIndex) => {
|
||||
if (!node.nodeData.data.expand) {
|
||||
return
|
||||
}
|
||||
if (isRoot) return
|
||||
// 判断子节点所占的高度之和是否大于该节点自身,大于则需要调整位置
|
||||
let base = this.getMarginY(layerIndex + 1) * 2 + node.height
|
||||
let difference = node.childrenAreaHeight - base
|
||||
if (difference > 0) {
|
||||
this.updateBrothers(node, difference / 2)
|
||||
}
|
||||
},
|
||||
null,
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
// 更新兄弟节点的top
|
||||
updateBrothers(node, addHeight) {
|
||||
if (node.parent) {
|
||||
let childrenList = node.parent.children
|
||||
let index = childrenList.findIndex(item => {
|
||||
return item === node
|
||||
})
|
||||
childrenList.forEach((item, _index) => {
|
||||
// 自定义节点位置
|
||||
if (item.hasCustomPosition()) return
|
||||
// 三级或三级以下节点自身位置不需要动
|
||||
if (!node.parent.isRoot && item === node) return
|
||||
let _offset = 0
|
||||
// 二级节点上面的兄弟节点不需要移动,自身需要往下移动
|
||||
if (node.parent.isRoot) {
|
||||
// 上面的节点不用移
|
||||
if (_index < index) {
|
||||
_offset = 0
|
||||
} else if (_index > index) {
|
||||
// 下面的节点往下移
|
||||
_offset = addHeight * 2
|
||||
} else {
|
||||
// 自身也要移动
|
||||
_offset = addHeight
|
||||
}
|
||||
} else {
|
||||
// 三级或三级以下节点两侧的兄弟节点向两侧移动
|
||||
// 上面的节点往上移
|
||||
if (_index < index) {
|
||||
_offset = -addHeight
|
||||
} else if (_index > index) {
|
||||
// 下面的节点往下移
|
||||
_offset = addHeight
|
||||
}
|
||||
}
|
||||
item.top += _offset
|
||||
// 同步更新子节点的位置
|
||||
if (item.children && item.children.length) {
|
||||
this.updateChildren(item.children, 'top', _offset)
|
||||
}
|
||||
})
|
||||
// 更新父节点的位置
|
||||
this.updateBrothers(node.parent, addHeight)
|
||||
}
|
||||
}
|
||||
|
||||
// 调整兄弟节点的top
|
||||
updateBrothersTop(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) => {
|
||||
if (item.hasCustomPosition()) {
|
||||
// 适配自定义位置
|
||||
return
|
||||
}
|
||||
let _offset = 0
|
||||
// 下面的节点往下移
|
||||
if (_index > index) {
|
||||
_offset = addHeight
|
||||
}
|
||||
item.top += _offset
|
||||
// 同步更新子节点的位置
|
||||
if (item.children && item.children.length) {
|
||||
this.updateChildren(item.children, 'top', _offset)
|
||||
}
|
||||
})
|
||||
// 更新父节点的位置
|
||||
this.updateBrothersTop(node.parent, addHeight)
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制连线,连接该节点到其子节点
|
||||
renderLine(node, lines, style, lineStyle) {
|
||||
if (lineStyle === 'curve') {
|
||||
this.renderLineCurve(node, lines, style)
|
||||
} else if (lineStyle === 'direct') {
|
||||
this.renderLineDirect(node, lines, style)
|
||||
} else {
|
||||
this.renderLineStraight(node, lines, style)
|
||||
}
|
||||
}
|
||||
|
||||
// 直线连接
|
||||
renderLineStraight(node, lines, style) {
|
||||
if (node.children.length <= 0) {
|
||||
return []
|
||||
}
|
||||
let { expandBtnSize } = node
|
||||
if (!this.mindMap.opt.alwaysShowExpandBtn) {
|
||||
expandBtnSize = 0
|
||||
}
|
||||
if (node.isRoot) {
|
||||
// 当前节点是根节点
|
||||
let prevBother = node
|
||||
// 根节点的子节点是和根节点同一水平线排列
|
||||
node.children.forEach((item, index) => {
|
||||
let y1 = prevBother.top + prevBother.height
|
||||
let y2 = item.top
|
||||
let x = node.left + node.width / 2
|
||||
let path = `M ${x},${y1} L ${x},${y2}`
|
||||
lines[index].plot(path)
|
||||
style && style(lines[index], item)
|
||||
prevBother = item
|
||||
})
|
||||
} else {
|
||||
// 当前节点为非根节点
|
||||
if (node.dir === CONSTANTS.LAYOUT_GROW_DIR.RIGHT) {
|
||||
let nodeRight = node.left + node.width
|
||||
let nodeYCenter = node.top + node.height / 2
|
||||
let marginX = this.getMarginX(node.layerIndex + 1)
|
||||
let offset = (marginX - expandBtnSize) * 0.6
|
||||
node.children.forEach((item, index) => {
|
||||
let itemLeft = item.left
|
||||
let itemYCenter = item.top + item.height / 2
|
||||
let path = `
|
||||
M ${nodeRight},${nodeYCenter}
|
||||
L ${nodeRight + offset},${nodeYCenter}
|
||||
L ${nodeRight + offset},${itemYCenter}
|
||||
L ${itemLeft},${itemYCenter}`
|
||||
lines[index].plot(path)
|
||||
style && style(lines[index], item)
|
||||
})
|
||||
} else {
|
||||
let nodeLeft = node.left
|
||||
let nodeYCenter = node.top + node.height / 2
|
||||
let marginX = this.getMarginX(node.layerIndex + 1)
|
||||
let offset = (marginX - expandBtnSize) * 0.6
|
||||
node.children.forEach((item, index) => {
|
||||
let itemRight = item.left + item.width
|
||||
let itemYCenter = item.top + item.height / 2
|
||||
let path = `
|
||||
M ${nodeLeft},${nodeYCenter}
|
||||
L ${nodeLeft - offset},${nodeYCenter}
|
||||
L ${nodeLeft - offset},${itemYCenter}
|
||||
L ${itemRight},${itemYCenter}`
|
||||
lines[index].plot(path)
|
||||
style && style(lines[index], item)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 直连
|
||||
renderLineDirect(node, lines, style) {
|
||||
if (node.children.length <= 0) {
|
||||
return []
|
||||
}
|
||||
let { left, top, width, height, expandBtnSize } = node
|
||||
if (!this.mindMap.opt.alwaysShowExpandBtn) {
|
||||
expandBtnSize = 0
|
||||
}
|
||||
node.children.forEach((item, index) => {
|
||||
if (node.isRoot) {
|
||||
let prevBother = node
|
||||
// 根节点的子节点是和根节点同一水平线排列
|
||||
node.children.forEach((item, index) => {
|
||||
let y1 = prevBother.top + prevBother.height
|
||||
let y2 = item.top
|
||||
let x = node.left + node.width / 2
|
||||
let path = `M ${x},${y1} L ${x},${y2}`
|
||||
lines[index].plot(path)
|
||||
style && style(lines[index], item)
|
||||
prevBother = item
|
||||
})
|
||||
} else {
|
||||
let x1 =
|
||||
item.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT
|
||||
? left - expandBtnSize
|
||||
: left + width + expandBtnSize
|
||||
let y1 = top + height / 2
|
||||
let x2 =
|
||||
item.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT
|
||||
? item.left + item.width
|
||||
: item.left
|
||||
let y2 = item.top + item.height / 2
|
||||
let path = `M ${x1},${y1} L ${x2},${y2}`
|
||||
lines[index].plot(path)
|
||||
style && style(lines[index], item)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 曲线风格连线
|
||||
renderLineCurve(node, lines, style) {
|
||||
if (node.children.length <= 0) {
|
||||
return []
|
||||
}
|
||||
let { left, top, width, height, expandBtnSize } = node
|
||||
if (!this.mindMap.opt.alwaysShowExpandBtn) {
|
||||
expandBtnSize = 0
|
||||
}
|
||||
node.children.forEach((item, index) => {
|
||||
if (node.isRoot) {
|
||||
let prevBother = node
|
||||
// 根节点的子节点是和根节点同一水平线排列
|
||||
node.children.forEach((item, index) => {
|
||||
let y1 = prevBother.top + prevBother.height
|
||||
let y2 = item.top
|
||||
let x = node.left + node.width / 2
|
||||
let path = `M ${x},${y1} L ${x},${y2}`
|
||||
lines[index].plot(path)
|
||||
style && style(lines[index], item)
|
||||
prevBother = item
|
||||
})
|
||||
} else {
|
||||
let x1 =
|
||||
item.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT
|
||||
? left - expandBtnSize
|
||||
: left + width + expandBtnSize
|
||||
let y1 = top + height / 2
|
||||
let x2 =
|
||||
item.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT
|
||||
? item.left + item.width
|
||||
: item.left
|
||||
let y2 = item.top + item.height / 2
|
||||
let path = this.cubicBezierPath(x1, y1, x2, y2)
|
||||
lines[index].plot(path)
|
||||
style && style(lines[index], item)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 渲染按钮
|
||||
renderExpandBtn(node, btn) {
|
||||
let { width, height, expandBtnSize, isRoot } = node
|
||||
if (!isRoot) {
|
||||
let { translateX, translateY } = btn.transform()
|
||||
if (node.dir === CONSTANTS.LAYOUT_GROW_DIR.RIGHT) {
|
||||
btn.translate(width - translateX, height / 2 - translateY)
|
||||
} else {
|
||||
btn.translate(-expandBtnSize - translateX, height / 2 - translateY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建概要节点
|
||||
renderGeneralization(node, gLine, gNode) {
|
||||
let isLeft = node.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT
|
||||
let {
|
||||
top,
|
||||
bottom,
|
||||
left,
|
||||
right,
|
||||
generalizationLineMargin,
|
||||
generalizationNodeMargin
|
||||
} = this.getNodeBoundaries(node, 'h', isLeft)
|
||||
let x = isLeft
|
||||
? left - generalizationLineMargin
|
||||
: right + generalizationLineMargin
|
||||
let x1 = x
|
||||
let y1 = top
|
||||
let x2 = x
|
||||
let y2 = bottom
|
||||
let cx = x1 + (isLeft ? -20 : 20)
|
||||
let cy = y1 + (y2 - y1) / 2
|
||||
let path = `M ${x1},${y1} Q ${cx},${cy} ${x2},${y2}`
|
||||
gLine.plot(path)
|
||||
gNode.left =
|
||||
x +
|
||||
(isLeft ? -generalizationNodeMargin : generalizationNodeMargin) -
|
||||
(isLeft ? gNode.width : 0)
|
||||
gNode.top = top + (bottom - top - gNode.height) / 2
|
||||
}
|
||||
|
||||
// 渲染展开收起按钮的隐藏占位元素
|
||||
renderExpandBtnRect(rect, expandBtnSize, width, height, node) {
|
||||
if (node.dir === CONSTANTS.LAYOUT_GROW_DIR.LEFT) {
|
||||
rect.size(expandBtnSize, height).x(-expandBtnSize).y(0)
|
||||
} else {
|
||||
rect.size(expandBtnSize, height).x(width).y(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default VerticalTimeline
|
||||
@@ -1,5 +1,11 @@
|
||||
import JSZip from 'jszip'
|
||||
import xmlConvert from 'xml-js'
|
||||
import {
|
||||
getTextFromHtml,
|
||||
imgToDataUrl,
|
||||
parseDataUrl,
|
||||
getImageSize
|
||||
} from '../utils/index'
|
||||
|
||||
// 解析.xmind文件
|
||||
const parseXmindFile = file => {
|
||||
@@ -10,7 +16,7 @@ const parseXmindFile = file => {
|
||||
let content = ''
|
||||
if (zip.files['content.json']) {
|
||||
let json = await zip.files['content.json'].async('string')
|
||||
content = transformXmind(json)
|
||||
content = await transformXmind(json, zip.files)
|
||||
} else if (zip.files['content.xml']) {
|
||||
let xml = await zip.files['content.xml'].async('string')
|
||||
let json = xmlConvert.xml2json(xml)
|
||||
@@ -33,11 +39,12 @@ const parseXmindFile = file => {
|
||||
}
|
||||
|
||||
// 转换xmind数据
|
||||
const transformXmind = content => {
|
||||
const transformXmind = async (content, files) => {
|
||||
let data = JSON.parse(content)[0]
|
||||
let nodeTree = data.rootTopic
|
||||
let newTree = {}
|
||||
let walk = (node, newNode) => {
|
||||
let waitLoadImageList = []
|
||||
let walk = async (node, newNode) => {
|
||||
newNode.data = {
|
||||
// 节点内容
|
||||
text: node.title
|
||||
@@ -55,6 +62,42 @@ const transformXmind = content => {
|
||||
if (node.labels && node.labels.length > 0) {
|
||||
newNode.data.tag = node.labels
|
||||
}
|
||||
// 图片
|
||||
if (node.image && /\.(jpg|jpeg|png|gif|webp)$/.test(node.image.src)) {
|
||||
try {
|
||||
// 处理异步逻辑
|
||||
let resolve = null
|
||||
let promise = new Promise(_resolve => {
|
||||
resolve = _resolve
|
||||
})
|
||||
waitLoadImageList.push(promise)
|
||||
// 读取图片
|
||||
let imageType = /\.([^.]+)$/.exec(node.image.src)[1]
|
||||
let imageBase64 =
|
||||
`data:image/${imageType};base64,` +
|
||||
(await files['resources/' + node.image.src.split('/')[1]].async(
|
||||
'base64'
|
||||
))
|
||||
newNode.data.image = imageBase64
|
||||
// 如果图片尺寸不存在
|
||||
if (!node.image.width && !node.image.height) {
|
||||
let imageSize = await getImageSize(imageBase64)
|
||||
newNode.data.imageSize = {
|
||||
width: imageSize.width,
|
||||
height: imageSize.height
|
||||
}
|
||||
} else {
|
||||
newNode.data.imageSize = {
|
||||
width: node.image.width,
|
||||
height: node.image.height
|
||||
}
|
||||
}
|
||||
resolve()
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
// 子节点
|
||||
newNode.children = []
|
||||
if (
|
||||
@@ -70,6 +113,7 @@ const transformXmind = content => {
|
||||
}
|
||||
}
|
||||
walk(nodeTree, newTree)
|
||||
await Promise.all(waitLoadImageList)
|
||||
return newTree
|
||||
}
|
||||
|
||||
@@ -158,8 +202,127 @@ const transformOldXmind = content => {
|
||||
return newTree
|
||||
}
|
||||
|
||||
// 数据转换为xmind文件
|
||||
const transformToXmind = async (data, name) => {
|
||||
const id = 'simpleMindMap_' + Date.now()
|
||||
const imageList = []
|
||||
// 转换核心数据
|
||||
let newTree = {}
|
||||
let waitLoadImageList = []
|
||||
let walk = async (node, newNode, isRoot) => {
|
||||
let newData = {
|
||||
structureClass: 'org.xmind.ui.logic.right',
|
||||
title: getTextFromHtml(node.data.text), // 节点文本
|
||||
children: {
|
||||
attached: []
|
||||
}
|
||||
}
|
||||
// 备注
|
||||
if (node.data.note !== undefined) {
|
||||
newData.notes = {
|
||||
realHTML: {
|
||||
content: node.data.note
|
||||
},
|
||||
plain: {
|
||||
content: node.data.note
|
||||
}
|
||||
}
|
||||
}
|
||||
// 超链接
|
||||
if (node.data.hyperlink !== undefined) {
|
||||
newData.href = node.data.hyperlink
|
||||
}
|
||||
// 标签
|
||||
if (node.data.tag !== undefined) {
|
||||
newData.labels = node.data.tag || []
|
||||
}
|
||||
// 图片
|
||||
if (node.data.image) {
|
||||
try {
|
||||
// 处理异步逻辑
|
||||
let resolve = null
|
||||
let promise = new Promise(_resolve => {
|
||||
resolve = _resolve
|
||||
})
|
||||
waitLoadImageList.push(promise)
|
||||
let imgName = ''
|
||||
let imgData = node.data.image
|
||||
// 网络图片要先转换成data:url
|
||||
if (/^https?:\/\//.test(node.data.image)) {
|
||||
imgData = await imgToDataUrl(node.data.image)
|
||||
}
|
||||
// 从data:url中解析出图片类型和base64
|
||||
let dataUrlRes = parseDataUrl(imgData)
|
||||
imgName = 'image_' + imageList.length + '.' + dataUrlRes.type
|
||||
imageList.push({
|
||||
name: imgName,
|
||||
data: dataUrlRes.base64
|
||||
})
|
||||
newData.image = {
|
||||
src: 'xap:resources/' + imgName,
|
||||
width: node.data.imageSize.width,
|
||||
height: node.data.imageSize.height
|
||||
}
|
||||
resolve()
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
// 样式
|
||||
// 暂时不考虑样式
|
||||
if (isRoot) {
|
||||
newData.class = 'topic'
|
||||
newNode.id = id
|
||||
newNode.class = 'sheet'
|
||||
newNode.title = name
|
||||
newNode.extensions = []
|
||||
newNode.topicPositioning = 'fixed'
|
||||
newNode.topicOverlapping = 'overlap'
|
||||
newNode.coreVersion = '2.100.0'
|
||||
newNode.rootTopic = newData
|
||||
} else {
|
||||
Object.keys(newData).forEach(key => {
|
||||
newNode[key] = newData[key]
|
||||
})
|
||||
}
|
||||
if (node.children && node.children.length > 0) {
|
||||
node.children.forEach(child => {
|
||||
let newChild = {}
|
||||
walk(child, newChild)
|
||||
newData.children.attached.push(newChild)
|
||||
})
|
||||
}
|
||||
}
|
||||
walk(data, newTree, true)
|
||||
await Promise.all(waitLoadImageList)
|
||||
const contentData = [newTree]
|
||||
// 创建压缩包
|
||||
const zip = new JSZip()
|
||||
zip.file('content.json', JSON.stringify(contentData))
|
||||
zip.file(
|
||||
'metadata.json',
|
||||
`{"modifier":"","dataStructureVersion":"1","layoutEngineVersion":"2","activeSheetId":"${id}"}`
|
||||
)
|
||||
const manifestData = {
|
||||
'file-entries': { 'content.json': {}, 'metadata.json': {} }
|
||||
}
|
||||
// 图片
|
||||
if (imageList.length > 0) {
|
||||
imageList.forEach(item => {
|
||||
manifestData['file-entries']['resources/' + item.name] = {}
|
||||
const img = zip.folder('resources')
|
||||
img.file(item.name, item.data, { base64: true })
|
||||
})
|
||||
}
|
||||
zip.file('manifest.json', JSON.stringify(manifestData))
|
||||
const zipData = await zip.generateAsync({ type: 'blob' })
|
||||
return zipData
|
||||
}
|
||||
|
||||
export default {
|
||||
parseXmindFile,
|
||||
transformXmind,
|
||||
transformOldXmind
|
||||
transformOldXmind,
|
||||
transformToXmind
|
||||
}
|
||||
|
||||
@@ -180,6 +180,17 @@ class Export {
|
||||
this.mindMap.doExportPDF.pdf(name, img)
|
||||
}
|
||||
|
||||
// 导出为xmind
|
||||
async xmind(name) {
|
||||
if (!this.mindMap.doExportXMind) {
|
||||
throw new Error('请注册ExportXMind插件')
|
||||
}
|
||||
const data = this.mindMap.getData()
|
||||
const blob = await this.mindMap.doExportXMind.xmind(data, name)
|
||||
const res = await readBlob(blob)
|
||||
return res
|
||||
}
|
||||
|
||||
// 导出为svg
|
||||
// plusCssText:附加的css样式,如果svg中存在dom节点,想要设置一些针对节点的样式可以通过这个参数传入
|
||||
async svg(name, plusCssText) {
|
||||
|
||||
19
simple-mind-map/src/plugins/ExportXMind.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import xmind from '../parse/xmind'
|
||||
|
||||
// 导出XMind类,需要通过Export插件使用
|
||||
class ExportXMind {
|
||||
// 构造函数
|
||||
constructor(opt) {
|
||||
this.mindMap = opt.mindMap
|
||||
}
|
||||
|
||||
// 导出xmind
|
||||
async xmind(data, name) {
|
||||
const zipData = await xmind.transformToXmind(data, name)
|
||||
return zipData
|
||||
}
|
||||
}
|
||||
|
||||
ExportXMind.instanceName = 'doExportXMind'
|
||||
|
||||
export default ExportXMind
|
||||
@@ -194,11 +194,12 @@ class NodeImgAdjust {
|
||||
this.hideHandleEl()
|
||||
// 更新节点图片为新的大小
|
||||
let { image, imageTitle } = this.node.nodeData.data
|
||||
let { scaleX, scaleY } = this.mindMap.draw.transform()
|
||||
this.mindMap.execCommand('SET_NODE_IMAGE', this.node, {
|
||||
url: image,
|
||||
title: imageTitle,
|
||||
width: this.currentImgWidth,
|
||||
height: this.currentImgHeight,
|
||||
width: this.currentImgWidth / scaleX,
|
||||
height: this.currentImgHeight / scaleY,
|
||||
custom: true // 代表自定义了图片大小
|
||||
})
|
||||
this.isAdjusted = true
|
||||
|
||||
@@ -39,6 +39,7 @@ class RichText {
|
||||
this.range = null
|
||||
this.lastRange = null
|
||||
this.node = null
|
||||
this.isInserting = false
|
||||
this.styleEl = null
|
||||
this.cacheEditingText = ''
|
||||
this.lostStyle = false
|
||||
@@ -145,11 +146,12 @@ class RichText {
|
||||
}
|
||||
|
||||
// 显示文本编辑控件
|
||||
showEditText(node, rect) {
|
||||
showEditText(node, rect, isInserting) {
|
||||
if (this.showTextEdit) {
|
||||
return
|
||||
}
|
||||
this.node = node
|
||||
this.isInserting = isInserting
|
||||
if (!rect) rect = node._textData.node.node.getBoundingClientRect()
|
||||
this.mindMap.emit('before_show_text_edit')
|
||||
this.mindMap.renderer.textEdit.registerTmpShortcut()
|
||||
@@ -200,7 +202,8 @@ class RichText {
|
||||
this.initQuillEditor()
|
||||
document.querySelector('.ql-editor').style.minHeight = originHeight + 'px'
|
||||
this.showTextEdit = true
|
||||
this.focus()
|
||||
// 如果是刚创建的节点,那么默认全选,否则普通激活不全选
|
||||
this.focus(isInserting ? 0 : null)
|
||||
if (!node.nodeData.data.richText) {
|
||||
// 如果是非富文本的情况,需要手动应用文本样式
|
||||
this.setTextStyleIfNotRichText(node)
|
||||
@@ -250,6 +253,7 @@ class RichText {
|
||||
this.showTextEdit = false
|
||||
this.mindMap.emit('rich_text_selection_change', false)
|
||||
this.node = null
|
||||
this.isInserting = false
|
||||
}
|
||||
|
||||
// 初始化Quill富文本编辑器
|
||||
@@ -271,6 +275,8 @@ class RichText {
|
||||
theme: 'snow'
|
||||
})
|
||||
this.quill.on('selection-change', range => {
|
||||
// 刚创建的节点全选不需要显示操作条
|
||||
if (this.isInserting) return
|
||||
this.lastRange = this.range
|
||||
this.range = null
|
||||
if (range) {
|
||||
@@ -338,9 +344,9 @@ class RichText {
|
||||
}
|
||||
|
||||
// 聚焦
|
||||
focus() {
|
||||
focus(start) {
|
||||
let len = this.quill.getLength()
|
||||
this.quill.setSelection(len, len)
|
||||
this.quill.setSelection(typeof start === 'number' ? start : len, len)
|
||||
}
|
||||
|
||||
// 格式化当前选中的文本
|
||||
|
||||
@@ -86,7 +86,8 @@ class TouchEvent {
|
||||
this.clickNum = 0
|
||||
this.dispatchMouseEvent('dblclick', ev.target, ev)
|
||||
} else {
|
||||
this.dispatchMouseEvent('click', ev.target, ev)
|
||||
// 点击事件应该不用模拟
|
||||
// this.dispatchMouseEvent('click', ev.target, ev)
|
||||
}
|
||||
}
|
||||
this.touchesNum = 0
|
||||
|
||||
@@ -213,6 +213,18 @@ export const imgToDataUrl = src => {
|
||||
})
|
||||
}
|
||||
|
||||
// 解析dataUrl
|
||||
export const parseDataUrl = data => {
|
||||
if (!/^data:/.test(data)) return data
|
||||
let [typeStr, base64] = data.split(',')
|
||||
let res = /^data:[^/]+\/([^;]+);/.exec(typeStr)
|
||||
let type = res[1]
|
||||
return {
|
||||
type,
|
||||
base64
|
||||
}
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
export const downloadFile = (file, fileName) => {
|
||||
let a = document.createElement('a')
|
||||
@@ -392,3 +404,23 @@ export const nodeToHTML = node => {
|
||||
nodeToHTMLWrapEl.appendChild(node)
|
||||
return nodeToHTMLWrapEl.innerHTML
|
||||
}
|
||||
|
||||
// 获取图片大小
|
||||
export const getImageSize = src => {
|
||||
return new Promise(resolve => {
|
||||
let img = new Image()
|
||||
img.src = src
|
||||
img.onload = () => {
|
||||
resolve({
|
||||
width: img.width,
|
||||
height: img.height
|
||||
})
|
||||
}
|
||||
img.onerror = () => {
|
||||
resolve({
|
||||
width: 0,
|
||||
height: 0
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0">
|
||||
<link rel="icon" href="./dist/logo.png">
|
||||
<title>一个简单的web思维导图实现</title>
|
||||
<link rel="icon" href="./dist/logo.ico">
|
||||
<title>思绪思维导图</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
|
||||
BIN
web/public/logo.ico
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
web/src/assets/avatar/ZXR.jpg
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
web/src/assets/avatar/default.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
web/src/assets/avatar/qp.jpg
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
web/src/assets/avatar/suka.jpg
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
web/src/assets/avatar/花儿朵朵.jpg
Normal file
|
After Width: | Height: | Size: 54 KiB |
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 2479351 */
|
||||
src: url('iconfont.woff2?t=1686298427624') format('woff2'),
|
||||
url('iconfont.woff?t=1686298427624') format('woff'),
|
||||
url('iconfont.ttf?t=1686298427624') format('truetype');
|
||||
src: url('iconfont.woff2?t=1689210173189') format('woff2'),
|
||||
url('iconfont.woff?t=1689210173189') format('woff'),
|
||||
url('iconfont.ttf?t=1689210173189') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,6 +13,42 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.iconwangzhan:before {
|
||||
content: "\e628";
|
||||
}
|
||||
|
||||
.iconcsdn:before {
|
||||
content: "\e608";
|
||||
}
|
||||
|
||||
.iconshejiaotubiao-10:before {
|
||||
content: "\e644";
|
||||
}
|
||||
|
||||
.iconstar:before {
|
||||
content: "\e7df";
|
||||
}
|
||||
|
||||
.iconfork:before {
|
||||
content: "\e641";
|
||||
}
|
||||
|
||||
.iconxiazai:before {
|
||||
content: "\e613";
|
||||
}
|
||||
|
||||
.iconteamwork:before {
|
||||
content: "\e870";
|
||||
}
|
||||
|
||||
.iconshuiyin:before {
|
||||
content: "\e67a";
|
||||
}
|
||||
|
||||
.iconxmind:before {
|
||||
content: "\ea57";
|
||||
}
|
||||
|
||||
.iconmouseR:before {
|
||||
content: "\e6bd";
|
||||
}
|
||||
|
||||
BIN
web/src/assets/img/block1.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
web/src/assets/img/block3.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
web/src/assets/img/block4.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
web/src/assets/img/catalogOrganization.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
web/src/assets/img/fishbone.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
web/src/assets/img/logicalStructure.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
web/src/assets/img/logo2.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
web/src/assets/img/mindMap.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
web/src/assets/img/organizationStructure.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
web/src/assets/img/split.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
web/src/assets/img/timeline.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
web/src/assets/img/timeline2.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
web/src/assets/img/verticalTimeline.jpg
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
web/src/assets/img/verticalTimeline.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
@@ -1,12 +1,13 @@
|
||||
// 布局结构图片映射
|
||||
export const layoutImgMap = {
|
||||
logicalStructure: require('../assets/img/logicalStructure.jpg'),
|
||||
mindMap: require('../assets/img/mindMap.jpg'),
|
||||
organizationStructure: require('../assets/img/organizationStructure.jpg'),
|
||||
catalogOrganization: require('../assets/img/catalogOrganization.jpg'),
|
||||
timeline: require('../assets/img/timeline.jpg'),
|
||||
timeline2: require('../assets/img/timeline2.jpg'),
|
||||
fishbone: require('../assets/img/fishbone.jpg'),
|
||||
logicalStructure: require('../assets/img/logicalStructure.png'),
|
||||
mindMap: require('../assets/img/mindMap.png'),
|
||||
organizationStructure: require('../assets/img/organizationStructure.png'),
|
||||
catalogOrganization: require('../assets/img/catalogOrganization.png'),
|
||||
timeline: require('../assets/img/timeline.png'),
|
||||
timeline2: require('../assets/img/timeline2.png'),
|
||||
fishbone: require('../assets/img/fishbone.png'),
|
||||
verticalTimeline: require('../assets/img/verticalTimeline.png'),
|
||||
}
|
||||
|
||||
// 主题图片映射
|
||||
|
||||
@@ -412,5 +412,11 @@ export const downTypeList = [
|
||||
type: 'md',
|
||||
icon: 'iconmarkdown',
|
||||
desc: 'Easy for other software to open'
|
||||
},
|
||||
{
|
||||
name: 'XMind',
|
||||
type: 'xmind',
|
||||
icon: 'iconxmind',
|
||||
desc: 'XMind file'
|
||||
}
|
||||
]
|
||||
@@ -484,5 +484,11 @@ export const downTypeList = [
|
||||
type: 'md',
|
||||
icon: 'iconmarkdown',
|
||||
desc: '便于其他软件打开'
|
||||
},
|
||||
{
|
||||
name: 'XMind',
|
||||
type: 'xmind',
|
||||
icon: 'iconxmind',
|
||||
desc: 'XMind格式'
|
||||
}
|
||||
]
|
||||
@@ -15,7 +15,8 @@
|
||||
import Header from './components/Header.vue'
|
||||
import Sidebar from './components/Sidebar.vue'
|
||||
import CatalogBar from './components/CatalogBar.vue'
|
||||
import 'highlight.js/styles/atom-one-dark.css'
|
||||
// import 'highlight.js/styles/atom-one-dark.css'
|
||||
import 'highlight.js/styles/github.css'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -103,7 +104,7 @@ export default {
|
||||
a {
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
color: #42b883;
|
||||
color: #1ea59a;
|
||||
transition: color 0.25s;
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -225,7 +225,7 @@ export default {
|
||||
left: -10px;
|
||||
width: 4px;
|
||||
height: 20px;
|
||||
background-color: #42b883;
|
||||
background-color: #1ea59a;
|
||||
border-radius: 4px;
|
||||
transition: top 0.25s cubic-bezier(0, 1, 0.5, 1), opacity 0.25s,
|
||||
background-color 0.5s;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="headerContainer">
|
||||
<div class="left">
|
||||
<div class="title">
|
||||
<img src="../../../assets/img/logo.png" alt="">
|
||||
<img src="../../../assets/img/logo2.png" alt="">
|
||||
SimpleMindMap
|
||||
</div>
|
||||
</div>
|
||||
@@ -130,7 +130,7 @@ export default {
|
||||
font-size: 14px;
|
||||
|
||||
&:hover {
|
||||
color: #42b883;
|
||||
color: #1ea59a;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ export default {
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: #42b883;
|
||||
color: #1ea59a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# Changelog
|
||||
|
||||
## 0.6.6
|
||||
|
||||
New: 1.Support exporting to Xmind new version files. 2.Importing the new version of Xmind file supports importing images from nodes. 3.Add a vertical timeline structure.
|
||||
|
||||
Fix: 1.The TouchEvent plugin no longer sends click events, solving the problem of two windows opening when clicking on a hyperlink on the mobile end. 2.Fix the issue of dragging and moving a node to become a child node of another node, where the parent node of that node points to not being updated. 3.Fixed an issue where the node border style was not updated when dragging a second level node into a third level node. 4.Fix the issue where the mouse will not trigger the button display when moving into the unfolded or retracted button position, except for the structure growing to the right.
|
||||
|
||||
optimization: 1.The issue of excessive amplitude when optimizing the touchpad to scale the canvas. 2.The newly created node defaults to selecting all for easy deletion of default text.
|
||||
|
||||
## 0.6.5-fix.1
|
||||
|
||||
Fix: 1.Fix the issue of adjusting the image size incorrectly while zooming.
|
||||
|
||||
## 0.6.5
|
||||
|
||||
Fix: 1.Fix the issue of xmind file import errors. 2.Fixed a rare issue where line breaks occur when the width of the node text is decimal.
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Changelog</h1>
|
||||
<h2>0.6.6</h2>
|
||||
<p>New: 1.Support exporting to Xmind new version files. 2.Importing the new version of Xmind file supports importing images from nodes. 3.Add a vertical timeline structure.</p>
|
||||
<p>Fix: 1.The TouchEvent plugin no longer sends click events, solving the problem of two windows opening when clicking on a hyperlink on the mobile end. 2.Fix the issue of dragging and moving a node to become a child node of another node, where the parent node of that node points to not being updated. 3.Fixed an issue where the node border style was not updated when dragging a second level node into a third level node. 4.Fix the issue where the mouse will not trigger the button display when moving into the unfolded or retracted button position, except for the structure growing to the right.</p>
|
||||
<p>optimization: 1.The issue of excessive amplitude when optimizing the touchpad to scale the canvas. 2.The newly created node defaults to selecting all for easy deletion of default text.</p>
|
||||
<h2>0.6.5-fix.1</h2>
|
||||
<p>Fix: 1.Fix the issue of adjusting the image size incorrectly while zooming.</p>
|
||||
<h2>0.6.5</h2>
|
||||
<p>Fix: 1.Fix the issue of xmind file import errors.</p>
|
||||
<p>Fix: 1.Fix the issue of xmind file import errors. 2.Fixed a rare issue where line breaks occur when the width of the node text is decimal.</p>
|
||||
<p>New: 1.The packaged library supports obtaining built-in constants, themes, and other data. 2.Supports configuring the zoom behavior corresponding to the direction of the mouse wheel. 3.Node images support dragging and resizing.</p>
|
||||
<h2>0.6.4-fix.1</h2>
|
||||
<p>New: 1.When zooming with the mouse wheel, the default zoom is centered around the current position of the mouse, which can be turned off by configuring.</p>
|
||||
|
||||
@@ -104,4 +104,15 @@ Gets `svg` data, an async method that returns an object:
|
||||
node // svg node
|
||||
str // svg string
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
### xmind(name)
|
||||
|
||||
> v0.6.6+, an additional ExportXMind plugin needs to be registered
|
||||
|
||||
```js
|
||||
import ExportXMind from 'simple-mind-map/src/plugins/ExportXMind.js'
|
||||
MindMap.usePlugin(ExportXMind)
|
||||
```
|
||||
|
||||
Export as an `xmind` file type, asynchronous method, returns a `Promise` instance, and the returned data is the `data:url` data of a `zip` compressed package, which can be directly downloaded.
|
||||
@@ -85,6 +85,14 @@ MindMap.usePlugin(ExportPDF)
|
||||
str <span class="hljs-comment">// svg string</span>
|
||||
}
|
||||
</code></pre>
|
||||
<h3>xmind(name)</h3>
|
||||
<blockquote>
|
||||
<p>v0.6.6+, an additional ExportXMind plugin needs to be registered</p>
|
||||
</blockquote>
|
||||
<pre class="hljs"><code><span class="hljs-keyword">import</span> ExportXMind <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map/src/plugins/ExportXMind.js'</span>
|
||||
MindMap.usePlugin(ExportXMind)
|
||||
</code></pre>
|
||||
<p>Export as an <code>xmind</code> file type, asynchronous method, returns a <code>Promise</code> instance, and the returned data is the <code>data:url</code> data of a <code>zip</code> compressed package, which can be directly downloaded.</p>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -136,4 +136,24 @@ Open source is not easy. If this project is helpful to you, you can invite the a
|
||||
<img src="../../../../assets/avatar/志斌.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>志斌</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/小土渣的宇宙.jpeg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>小土渣的宇宙</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/qp.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>qp</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/ZXR.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>ZXR</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/花儿朵朵.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>花儿朵朵</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/suka.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>suka</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -95,6 +95,26 @@ full screen, support mini map</li>
|
||||
<img src="../../../../assets/avatar/志斌.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>志斌</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/小土渣的宇宙.jpeg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>小土渣的宇宙</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/qp.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>qp</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/ZXR.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>ZXR</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/花儿朵朵.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>花儿朵朵</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/suka.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>suka</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -153,6 +153,34 @@ Extract plain text content from an HTML string.
|
||||
|
||||
Convert `blob` data to `data:url` data.
|
||||
|
||||
#### parseDataUrl(data)
|
||||
|
||||
> v0.6.6+
|
||||
|
||||
Parse `data:url` data, return:
|
||||
|
||||
```js
|
||||
{
|
||||
type,// file type of data
|
||||
base64// base64 data
|
||||
}
|
||||
```
|
||||
|
||||
#### getImageSize(src)
|
||||
|
||||
> v0.6.6+
|
||||
|
||||
- `src`: The url of img
|
||||
|
||||
Get the size of image, return:
|
||||
|
||||
```js
|
||||
{
|
||||
width,
|
||||
height
|
||||
}
|
||||
```
|
||||
|
||||
## Simulate CSS background in Canvas
|
||||
|
||||
Import:
|
||||
|
||||
@@ -98,6 +98,29 @@ and copying the <code>data</code> of the data object, example:</p>
|
||||
<p>v0.5.9+</p>
|
||||
</blockquote>
|
||||
<p>Convert <code>blob</code> data to <code>data:url</code> data.</p>
|
||||
<h4>parseDataUrl(data)</h4>
|
||||
<blockquote>
|
||||
<p>v0.6.6+</p>
|
||||
</blockquote>
|
||||
<p>Parse <code>data:url</code> data, return:</p>
|
||||
<pre class="hljs"><code>{
|
||||
type,<span class="hljs-comment">// file type of data</span>
|
||||
base64<span class="hljs-comment">// base64 data</span>
|
||||
}
|
||||
</code></pre>
|
||||
<h4>getImageSize(src)</h4>
|
||||
<blockquote>
|
||||
<p>v0.6.6+</p>
|
||||
</blockquote>
|
||||
<ul>
|
||||
<li><code>src</code>: The url of img</li>
|
||||
</ul>
|
||||
<p>Get the size of image, return:</p>
|
||||
<pre class="hljs"><code>{
|
||||
width,
|
||||
height
|
||||
}
|
||||
</code></pre>
|
||||
<h2>Simulate CSS background in Canvas</h2>
|
||||
<p>Import:</p>
|
||||
<pre class="hljs"><code><span class="hljs-keyword">import</span> drawBackgroundImageToCanvas <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map/src/utils/simulateCSSBackgroundInCanvas'</span>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> v0.2.7+
|
||||
|
||||
Provides methods for importing `XMind` files.
|
||||
Provides methods for importing and export `XMind` files.
|
||||
|
||||
## Import
|
||||
|
||||
@@ -31,6 +31,8 @@ Parsing the `.xmind` file and returning the parsed data. You can use
|
||||
|
||||
### xmind.transformXmind(content)
|
||||
|
||||
> V0.6.6+version changes the method to asynchronous and returns a Promise instance
|
||||
|
||||
Convert `xmind` data. The `.xmind` file is essentially a `zip` file that can be
|
||||
decompressed by changing the suffix to zip. Inside, there is a `content.json`
|
||||
file. If you have parsed this file yourself, you can pass the contents of this
|
||||
@@ -48,4 +50,14 @@ For data parsing of the `xmind8` version, because the `.xmind` file in this
|
||||
version does not have a `content.json`, it corresponds to `content.xml`.
|
||||
|
||||
`content`: the contents of the `content.xml` file within the `.xmind` zip
|
||||
package
|
||||
package
|
||||
|
||||
### transformToXmind(data, name)
|
||||
|
||||
> v0.6.6+
|
||||
|
||||
- `data`: `simple-mind-map` data, you can get it by `mindMap.getData()` method.
|
||||
|
||||
- `name`: The file name to export.
|
||||
|
||||
Convert the `simple mind map` data to an `xmind` file. This method is asynchronous and returns an instance of `Promise`. The returned data is a `blob` type `zip` compressed package data, which you can download as a file yourself.
|
||||
@@ -4,7 +4,7 @@
|
||||
<blockquote>
|
||||
<p>v0.2.7+</p>
|
||||
</blockquote>
|
||||
<p>Provides methods for importing <code>XMind</code> files.</p>
|
||||
<p>Provides methods for importing and export <code>XMind</code> files.</p>
|
||||
<h2>Import</h2>
|
||||
<pre class="hljs"><code><span class="hljs-keyword">import</span> xmind <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map/src/parse/xmind.js'</span>
|
||||
</code></pre>
|
||||
@@ -19,6 +19,9 @@
|
||||
<code>mindMap.setData(data)</code> to render the returned data to the canvas.</p>
|
||||
<p><code>file</code>: <code>File</code> object</p>
|
||||
<h3>xmind.transformXmind(content)</h3>
|
||||
<blockquote>
|
||||
<p>V0.6.6+version changes the method to asynchronous and returns a Promise instance</p>
|
||||
</blockquote>
|
||||
<p>Convert <code>xmind</code> data. The <code>.xmind</code> file is essentially a <code>zip</code> file that can be
|
||||
decompressed by changing the suffix to zip. Inside, there is a <code>content.json</code>
|
||||
file. If you have parsed this file yourself, you can pass the contents of this
|
||||
@@ -34,6 +37,19 @@ package</p>
|
||||
version does not have a <code>content.json</code>, it corresponds to <code>content.xml</code>.</p>
|
||||
<p><code>content</code>: the contents of the <code>content.xml</code> file within the <code>.xmind</code> zip
|
||||
package</p>
|
||||
<h3>transformToXmind(data, name)</h3>
|
||||
<blockquote>
|
||||
<p>v0.6.6+</p>
|
||||
</blockquote>
|
||||
<ul>
|
||||
<li>
|
||||
<p><code>data</code>: <code>simple-mind-map</code> data, you can get it by <code>mindMap.getData()</code> method.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>name</code>: The file name to export.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Convert the <code>simple mind map</code> data to an <code>xmind</code> file. This method is asynchronous and returns an instance of <code>Promise</code>. The returned data is a <code>blob</code> type <code>zip</code> compressed package data, which you can download as a file yourself.</p>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# Changelog
|
||||
|
||||
## 0.6.6
|
||||
|
||||
新增:1.支持导出为Xmind新版文件。2.导入Xmind新版文件支持导入节点中的图片。 3.新增竖向时间轴结构。
|
||||
|
||||
修复:1.TouchEvent插件不再派发click事件,解决移动端点击超链接会打开两个窗口的问题。 2.修复拖拽移动一个节点成为另一个节点的子节点时该节点的父节点指向未更新的问题。 3.修复二级节点拖拽成三级节点时节点边框样式未更新的问题。 4.修复向右生长的结构外其他结构鼠标移入展开收起按钮位置时不会触发按钮显示的问题。
|
||||
|
||||
优化:1.优化触控板缩放画布时幅度过大的问题。2.刚创建的节点默认全选方便删除默认文本。
|
||||
|
||||
## 0.6.5-fix.1
|
||||
|
||||
修复:1.修复在缩放情况下调整图片大小不正确的问题。
|
||||
|
||||
## 0.6.5
|
||||
|
||||
修复:1.修复xmind文件导入报错的问题。 2.修复极少数情况下当节点文本的宽度为小数时显示发生换行的问题。
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Changelog</h1>
|
||||
<h2>0.6.6</h2>
|
||||
<p>新增:1.支持导出为Xmind新版文件。2.导入Xmind新版文件支持导入节点中的图片。 3.新增竖向时间轴结构。</p>
|
||||
<p>修复:1.TouchEvent插件不再派发click事件,解决移动端点击超链接会打开两个窗口的问题。 2.修复拖拽移动一个节点成为另一个节点的子节点时该节点的父节点指向未更新的问题。 3.修复二级节点拖拽成三级节点时节点边框样式未更新的问题。 4.修复向右生长的结构外其他结构鼠标移入展开收起按钮位置时不会触发按钮显示的问题。</p>
|
||||
<p>优化:1.优化触控板缩放画布时幅度过大的问题。2.刚创建的节点默认全选方便删除默认文本。</p>
|
||||
<h2>0.6.5-fix.1</h2>
|
||||
<p>修复:1.修复在缩放情况下调整图片大小不正确的问题。</p>
|
||||
<h2>0.6.5</h2>
|
||||
<p>修复:1.修复xmind文件导入报错的问题。</p>
|
||||
<p>修复:1.修复xmind文件导入报错的问题。 2.修复极少数情况下当节点文本的宽度为小数时显示发生换行的问题。</p>
|
||||
<p>新增:1.打包后的库支持获取内置常量、主题等数据。 2.支持配置鼠标滚轮方向对应的缩放行为。 3.节点图片支持拖拽调整大小。</p>
|
||||
<h2>0.6.4-fix.1</h2>
|
||||
<p>新增:1.鼠标滚轮缩放时默认以鼠标当前位置为中心进行缩放,可以通过配置关闭该特性。</p>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 结构
|
||||
|
||||
`simple-mind-map`目前支持四种结构:logicalStructure(逻辑结构图)、mindMap(思维导图)、organizationStructure(组织结构图)、catalogOrganization(目录组织图)、timeline(时间轴)、timeline2(时间轴2)、fishbone(鱼骨图)。
|
||||
`simple-mind-map`目前支持四种结构:logicalStructure(逻辑结构图)、mindMap(思维导图)、organizationStructure(组织结构图)、catalogOrganization(目录组织图)、timeline(时间轴)、timeline2(时间轴2)、fishbone(鱼骨图)、verticalTimeline(v0.6.6+竖向时间轴)。
|
||||
|
||||
可以在实例化`simple-mind-map`时通过选项指定使用的结构:
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>结构</h1>
|
||||
<p><code>simple-mind-map</code>目前支持四种结构:logicalStructure(逻辑结构图)、mindMap(思维导图)、organizationStructure(组织结构图)、catalogOrganization(目录组织图)、timeline(时间轴)、timeline2(时间轴2)、fishbone(鱼骨图)。</p>
|
||||
<p><code>simple-mind-map</code>目前支持四种结构:logicalStructure(逻辑结构图)、mindMap(思维导图)、organizationStructure(组织结构图)、catalogOrganization(目录组织图)、timeline(时间轴)、timeline2(时间轴2)、fishbone(鱼骨图)、verticalTimeline(v0.6.6+竖向时间轴)。</p>
|
||||
<p>可以在实例化<code>simple-mind-map</code>时通过选项指定使用的结构:</p>
|
||||
<pre class="hljs"><code><span class="hljs-keyword">new</span> MindMap({
|
||||
<span class="hljs-comment">// ...</span>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
> 要使用导出功能需要使用导出插件。
|
||||
|
||||
目前支持导出为`.smm`、`.json`、`.svg`、`.png`、`.pdf`、`.md`文件。
|
||||
目前支持导出为`.smm`、`.json`、`.svg`、`.png`、`.pdf`、`.md`、`.xmind`文件。
|
||||
|
||||
`.smm`是`simple-mind-map`自己定义的一种文件,其实就是`json`文件,换了一个扩展名而已。
|
||||
|
||||
@@ -94,6 +94,18 @@ mindMap.export(
|
||||
mindMap.export('md', true, '文件名')
|
||||
```
|
||||
|
||||
### 导出为xmind
|
||||
|
||||
> v0.6.6+
|
||||
|
||||
> 需要注册`ExportXMind`插件
|
||||
|
||||
导出为`Xmind`新版文件。
|
||||
|
||||
```js
|
||||
mindMap.export('xmind', '文件名')
|
||||
```
|
||||
|
||||
## 导入
|
||||
|
||||
目前支持从`.smm`、`.json`、`.xmind`、`.xlsx`、`.md`格式的文件导入。
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<blockquote>
|
||||
<p>要使用导出功能需要使用导出插件。</p>
|
||||
</blockquote>
|
||||
<p>目前支持导出为<code>.smm</code>、<code>.json</code>、<code>.svg</code>、<code>.png</code>、<code>.pdf</code>、<code>.md</code>文件。</p>
|
||||
<p>目前支持导出为<code>.smm</code>、<code>.json</code>、<code>.svg</code>、<code>.png</code>、<code>.pdf</code>、<code>.md</code>、<code>.xmind</code>文件。</p>
|
||||
<p><code>.smm</code>是<code>simple-mind-map</code>自己定义的一种文件,其实就是<code>json</code>文件,换了一个扩展名而已。</p>
|
||||
<p>导出直接调用<code>export</code>方法即可:</p>
|
||||
<pre class="hljs"><code>mindMap.export(type, isDownload, fileName, ...)
|
||||
@@ -62,6 +62,16 @@ mindMap.export(<span class="hljs-string">'pdf'</span>, <span class="hl
|
||||
<p>导出为<code>markdown</code>文件只要传递默认的三个参数即可:</p>
|
||||
<pre class="hljs"><code>mindMap.export(<span class="hljs-string">'md'</span>, <span class="hljs-literal">true</span>, <span class="hljs-string">'文件名'</span>)
|
||||
</code></pre>
|
||||
<h3>导出为xmind</h3>
|
||||
<blockquote>
|
||||
<p>v0.6.6+</p>
|
||||
</blockquote>
|
||||
<blockquote>
|
||||
<p>需要注册<code>ExportXMind</code>插件</p>
|
||||
</blockquote>
|
||||
<p>导出为<code>Xmind</code>新版文件。</p>
|
||||
<pre class="hljs"><code>mindMap.export(<span class="hljs-string">'xmind'</span>, <span class="hljs-string">'文件名'</span>)
|
||||
</code></pre>
|
||||
<h2>导入</h2>
|
||||
<p>目前支持从<code>.smm</code>、<code>.json</code>、<code>.xmind</code>、<code>.xlsx</code>、<code>.md</code>格式的文件导入。</p>
|
||||
<h3>导入smm、json</h3>
|
||||
|
||||
@@ -108,4 +108,15 @@ MindMap.usePlugin(ExportPDF)
|
||||
node// svg节点
|
||||
str// svg字符串
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
### xmind(name)
|
||||
|
||||
> v0.6.6+,需要额外注册一个ExportXMind插件
|
||||
|
||||
```js
|
||||
import ExportXMind from 'simple-mind-map/src/plugins/ExportXMind.js'
|
||||
MindMap.usePlugin(ExportXMind)
|
||||
```
|
||||
|
||||
导出为`xmind`文件类型,异步方法,返回一个`Promise`实例,返回的数据为一个`zip`压缩包的`data:url`数据,可以直接下载。
|
||||
@@ -87,6 +87,14 @@ MindMap.usePlugin(ExportPDF)
|
||||
str<span class="hljs-comment">// svg字符串</span>
|
||||
}
|
||||
</code></pre>
|
||||
<h3>xmind(name)</h3>
|
||||
<blockquote>
|
||||
<p>v0.6.6+,需要额外注册一个ExportXMind插件</p>
|
||||
</blockquote>
|
||||
<pre class="hljs"><code><span class="hljs-keyword">import</span> ExportXMind <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map/src/plugins/ExportXMind.js'</span>
|
||||
MindMap.usePlugin(ExportXMind)
|
||||
</code></pre>
|
||||
<p>导出为<code>xmind</code>文件类型,异步方法,返回一个<code>Promise</code>实例,返回的数据为一个<code>zip</code>压缩包的<code>data:url</code>数据,可以直接下载。</p>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -108,7 +108,9 @@
|
||||
|
||||
## 请作者喝杯咖啡
|
||||
|
||||
开源不易,如果本项目有帮助到你的话,可以请作者喝杯咖啡哟~
|
||||
开源不易,如果本项目有帮助到你的话,可以考虑请作者喝杯咖啡哟~
|
||||
|
||||
> 厚椰乳一盒 + 纯牛奶半盒 + 冰块 + 咖啡液 = 生椰拿铁 yyds
|
||||
|
||||
> 转账请备注【思维导图】。你的头像和名字将会出现在下面。
|
||||
|
||||
@@ -129,4 +131,20 @@
|
||||
<img src="../../../../assets/avatar/小土渣的宇宙.jpeg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>小土渣的宇宙</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/qp.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>qp</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/ZXR.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>ZXR</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/花儿朵朵.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>花儿朵朵</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/suka.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>suka</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -8,19 +8,19 @@
|
||||
</blockquote>
|
||||
<h2>特性</h2>
|
||||
<ul>
|
||||
<li><input type="checkbox" id="checkbox18" checked="true" /><label for="checkbox18">插件化架构,除核心功能外,其他功能作为插件提供,按需使用,减小整体体积</label></li>
|
||||
<li><input type="checkbox" id="checkbox19" checked="true" /><label for="checkbox19">支持逻辑结构图、思维导图、组织结构图、目录组织图、时间轴、鱼骨图六种结构</label></li>
|
||||
<li><input type="checkbox" id="checkbox20" checked="true" /><label for="checkbox20">内置多种主题,允许高度自定义样式,支持注册新主题</label></li>
|
||||
<li><input type="checkbox" id="checkbox21" checked="true" /><label for="checkbox21">支持快捷键</label></li>
|
||||
<li><input type="checkbox" id="checkbox22" checked="true" /><label for="checkbox22">节点内容支持图片、图标、超链接、备注、标签、概要</label></li>
|
||||
<li><input type="checkbox" id="checkbox23" checked="true" /><label for="checkbox23">支持前进后退</label></li>
|
||||
<li><input type="checkbox" id="checkbox24" checked="true" /><label for="checkbox24">支持拖动、缩放</label></li>
|
||||
<li><input type="checkbox" id="checkbox25" checked="true" /><label for="checkbox25">支持右键和Ctrl+左键两种多选方式</label></li>
|
||||
<li><input type="checkbox" id="checkbox26" checked="true" /><label for="checkbox26">支持节点自由拖拽、拖拽调整</label></li>
|
||||
<li><input type="checkbox" id="checkbox27" checked="true" /><label for="checkbox27">支持多种节点形状</label></li>
|
||||
<li><input type="checkbox" id="checkbox28" checked="true" /><label for="checkbox28">支持导出为</label><code>json</code>、<code>png</code>、<code>svg</code>、<code>pdf</code>、<code>markdown</code>,支持从<code>json</code>、<code>xmind</code>、<code>markdown</code>导入</li>
|
||||
<li><input type="checkbox" id="checkbox29" checked="true" /><label for="checkbox29">支持小地图、支持水印</label></li>
|
||||
<li><input type="checkbox" id="checkbox30" checked="true" /><label for="checkbox30">支持关联线</label></li>
|
||||
<li><input type="checkbox" id="checkbox0" checked="true" /><label for="checkbox0">插件化架构,除核心功能外,其他功能作为插件提供,按需使用,减小整体体积</label></li>
|
||||
<li><input type="checkbox" id="checkbox1" checked="true" /><label for="checkbox1">支持逻辑结构图、思维导图、组织结构图、目录组织图、时间轴、鱼骨图六种结构</label></li>
|
||||
<li><input type="checkbox" id="checkbox2" checked="true" /><label for="checkbox2">内置多种主题,允许高度自定义样式,支持注册新主题</label></li>
|
||||
<li><input type="checkbox" id="checkbox3" checked="true" /><label for="checkbox3">支持快捷键</label></li>
|
||||
<li><input type="checkbox" id="checkbox4" checked="true" /><label for="checkbox4">节点内容支持图片、图标、超链接、备注、标签、概要</label></li>
|
||||
<li><input type="checkbox" id="checkbox5" checked="true" /><label for="checkbox5">支持前进后退</label></li>
|
||||
<li><input type="checkbox" id="checkbox6" checked="true" /><label for="checkbox6">支持拖动、缩放</label></li>
|
||||
<li><input type="checkbox" id="checkbox7" checked="true" /><label for="checkbox7">支持右键和Ctrl+左键两种多选方式</label></li>
|
||||
<li><input type="checkbox" id="checkbox8" checked="true" /><label for="checkbox8">支持节点自由拖拽、拖拽调整</label></li>
|
||||
<li><input type="checkbox" id="checkbox9" checked="true" /><label for="checkbox9">支持多种节点形状</label></li>
|
||||
<li><input type="checkbox" id="checkbox10" checked="true" /><label for="checkbox10">支持导出为</label><code>json</code>、<code>png</code>、<code>svg</code>、<code>pdf</code>、<code>markdown</code>,支持从<code>json</code>、<code>xmind</code>、<code>markdown</code>导入</li>
|
||||
<li><input type="checkbox" id="checkbox11" checked="true" /><label for="checkbox11">支持小地图、支持水印</label></li>
|
||||
<li><input type="checkbox" id="checkbox12" checked="true" /><label for="checkbox12">支持关联线</label></li>
|
||||
</ul>
|
||||
<h2>仓库目录介绍</h2>
|
||||
<p>1.<code>simple-mind-map</code></p>
|
||||
@@ -28,11 +28,11 @@
|
||||
<p>2.<code>web</code></p>
|
||||
<p>使用<code>simple-mind-map</code>库,基于<code>vue2.x</code>、<code>ElementUI</code>搭建的在线思维导图。特性:</p>
|
||||
<ul>
|
||||
<li><input type="checkbox" id="checkbox31" checked="true" /><label for="checkbox31">工具栏,支持插入节点、删除节点;编辑节点图片、图标、超链接、备注、标签、概要</label></li>
|
||||
<li><input type="checkbox" id="checkbox32" checked="true" /><label for="checkbox32">侧边栏,基础样式设置面板、节点样式设置面板、大纲面板、主题选择面板、结构选择面板</label></li>
|
||||
<li><input type="checkbox" id="checkbox33" checked="true" /><label for="checkbox33">导入导出功能;数据默认保存在浏览器本地存储,也支持直接创建、打开、编辑电脑本地文件</label></li>
|
||||
<li><input type="checkbox" id="checkbox34" checked="true" /><label for="checkbox34">右键菜单,支持展开、收起、整理布局等操作</label></li>
|
||||
<li><input type="checkbox" id="checkbox35" checked="true" /><label for="checkbox35">底部栏,支持节点数量、字数统计;支持切换编辑和只读模式;支持放大缩小;支持全屏切换;支持小地图</label></li>
|
||||
<li><input type="checkbox" id="checkbox13" checked="true" /><label for="checkbox13">工具栏,支持插入节点、删除节点;编辑节点图片、图标、超链接、备注、标签、概要</label></li>
|
||||
<li><input type="checkbox" id="checkbox14" checked="true" /><label for="checkbox14">侧边栏,基础样式设置面板、节点样式设置面板、大纲面板、主题选择面板、结构选择面板</label></li>
|
||||
<li><input type="checkbox" id="checkbox15" checked="true" /><label for="checkbox15">导入导出功能;数据默认保存在浏览器本地存储,也支持直接创建、打开、编辑电脑本地文件</label></li>
|
||||
<li><input type="checkbox" id="checkbox16" checked="true" /><label for="checkbox16">右键菜单,支持展开、收起、整理布局等操作</label></li>
|
||||
<li><input type="checkbox" id="checkbox17" checked="true" /><label for="checkbox17">底部栏,支持节点数量、字数统计;支持切换编辑和只读模式;支持放大缩小;支持全屏切换;支持小地图</label></li>
|
||||
</ul>
|
||||
<p>提供文档页面服务。</p>
|
||||
<p>3.<code>dist</code></p>
|
||||
@@ -69,7 +69,10 @@
|
||||
<h2>License</h2>
|
||||
<p><a href="https://opensource.org/licenses/MIT">MIT</a></p>
|
||||
<h2>请作者喝杯咖啡</h2>
|
||||
<p>开源不易,如果本项目有帮助到你的话,可以请作者喝杯咖啡哟~</p>
|
||||
<p>开源不易,如果本项目有帮助到你的话,可以考虑请作者喝杯咖啡哟~</p>
|
||||
<blockquote>
|
||||
<p>厚椰乳一盒 + 纯牛奶半盒 + 冰块 + 咖啡液 = 生椰拿铁 yyds</p>
|
||||
</blockquote>
|
||||
<blockquote>
|
||||
<p>转账请备注【思维导图】。你的头像和名字将会出现在下面。</p>
|
||||
</blockquote>
|
||||
@@ -88,6 +91,22 @@
|
||||
<img src="../../../../assets/avatar/小土渣的宇宙.jpeg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>小土渣的宇宙</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/qp.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>qp</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/ZXR.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>ZXR</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/花儿朵朵.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>花儿朵朵</p>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; width: fit-content; margin: 5px;">
|
||||
<img src="../../../../assets/avatar/suka.jpg" style="width: 50px;height: 50px;object-fit: cover;border-radius: 50%;" />
|
||||
<p>suka</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -148,6 +148,34 @@ copyNodeTree({}, node)
|
||||
|
||||
将`blob`数据转成`data:url`数据。
|
||||
|
||||
#### parseDataUrl(data)
|
||||
|
||||
> v0.6.6+
|
||||
|
||||
解析`data:url`数据,返回:
|
||||
|
||||
```js
|
||||
{
|
||||
type,// 数据的文件类型
|
||||
base64// base64数据
|
||||
}
|
||||
```
|
||||
|
||||
#### getImageSize(src)
|
||||
|
||||
> v0.6.6+
|
||||
|
||||
- `src`:图片的url
|
||||
|
||||
获取图片的大小。返回:
|
||||
|
||||
```js
|
||||
{
|
||||
width,
|
||||
height
|
||||
}
|
||||
```
|
||||
|
||||
## 在canvas中模拟css的背景属性
|
||||
|
||||
引入:
|
||||
|
||||
@@ -93,6 +93,29 @@
|
||||
<p>v0.5.9+</p>
|
||||
</blockquote>
|
||||
<p>将<code>blob</code>数据转成<code>data:url</code>数据。</p>
|
||||
<h4>parseDataUrl(data)</h4>
|
||||
<blockquote>
|
||||
<p>v0.6.6+</p>
|
||||
</blockquote>
|
||||
<p>解析<code>data:url</code>数据,返回:</p>
|
||||
<pre class="hljs"><code>{
|
||||
type,<span class="hljs-comment">// 数据的文件类型</span>
|
||||
base64<span class="hljs-comment">// base64数据</span>
|
||||
}
|
||||
</code></pre>
|
||||
<h4>getImageSize(src)</h4>
|
||||
<blockquote>
|
||||
<p>v0.6.6+</p>
|
||||
</blockquote>
|
||||
<ul>
|
||||
<li><code>src</code>:图片的url</li>
|
||||
</ul>
|
||||
<p>获取图片的大小。返回:</p>
|
||||
<pre class="hljs"><code>{
|
||||
width,
|
||||
height
|
||||
}
|
||||
</code></pre>
|
||||
<h2>在canvas中模拟css的背景属性</h2>
|
||||
<p>引入:</p>
|
||||
<pre class="hljs"><code><span class="hljs-keyword">import</span> drawBackgroundImageToCanvas <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map/src/utils/simulateCSSBackgroundInCanvas'</span>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> v0.2.7+
|
||||
|
||||
提供导入`XMind`文件的方法。
|
||||
提供导入和导出`XMind`文件的方法。
|
||||
|
||||
## 引入
|
||||
|
||||
@@ -30,6 +30,8 @@ simpleMindMap.xmind
|
||||
|
||||
### xmind.transformXmind(content)
|
||||
|
||||
> v0.6.6+版本该方法改为异步方法,返回一个Promise实例
|
||||
|
||||
转换`xmind`数据,`.xmind`文件本质上是一个压缩包,改成`zip`后缀可以解压缩,里面存在一个`content.json`文件,如果你自己解析出了这个文件,那么可以把这个文件内容传递给这个方法进行转换,转换后的数据,可以使用`mindMap.setData(data)`来将返回的数据渲染到画布上
|
||||
|
||||
`content`:`.xmind`压缩包内的`content.json`文件内容
|
||||
@@ -40,4 +42,14 @@ simpleMindMap.xmind
|
||||
|
||||
针对`xmind8`版本的数据解析,因为该版本的`.xmind`文件内没有`content.json`,对应的是`content.xml`。
|
||||
|
||||
`content`:`.xmind`压缩包内的`content.xml`文件内容
|
||||
`content`:`.xmind`压缩包内的`content.xml`文件内容
|
||||
|
||||
### transformToXmind(data, name)
|
||||
|
||||
> v0.6.6+
|
||||
|
||||
- `data`:`simple-mind-map`思维导图数据,可以通过`mindMap.getData()`方法获取。
|
||||
|
||||
- `name`:要导出的文件名。
|
||||
|
||||
将`simple-mind-map`数据转为`xmind`文件。该方法为异步方法,返回一个`Promise`实例,返回的数据是一个`blob`类型的`zip`压缩包数据,你可以自行下载为文件。
|
||||
@@ -4,7 +4,7 @@
|
||||
<blockquote>
|
||||
<p>v0.2.7+</p>
|
||||
</blockquote>
|
||||
<p>提供导入<code>XMind</code>文件的方法。</p>
|
||||
<p>提供导入和导出<code>XMind</code>文件的方法。</p>
|
||||
<h2>引入</h2>
|
||||
<pre class="hljs"><code><span class="hljs-keyword">import</span> xmind <span class="hljs-keyword">from</span> <span class="hljs-string">'simple-mind-map/src/parse/xmind.js'</span>
|
||||
</code></pre>
|
||||
@@ -18,6 +18,9 @@
|
||||
<p>解析<code>.xmind</code>文件,返回解析后的数据,可以使用<code>mindMap.setData(data)</code>来将返回的数据渲染到画布上</p>
|
||||
<p><code>file</code>:<code>File</code>对象</p>
|
||||
<h3>xmind.transformXmind(content)</h3>
|
||||
<blockquote>
|
||||
<p>v0.6.6+版本该方法改为异步方法,返回一个Promise实例</p>
|
||||
</blockquote>
|
||||
<p>转换<code>xmind</code>数据,<code>.xmind</code>文件本质上是一个压缩包,改成<code>zip</code>后缀可以解压缩,里面存在一个<code>content.json</code>文件,如果你自己解析出了这个文件,那么可以把这个文件内容传递给这个方法进行转换,转换后的数据,可以使用<code>mindMap.setData(data)</code>来将返回的数据渲染到画布上</p>
|
||||
<p><code>content</code>:<code>.xmind</code>压缩包内的<code>content.json</code>文件内容</p>
|
||||
<h3>xmind.transformOldXmind(content)</h3>
|
||||
@@ -26,6 +29,19 @@
|
||||
</blockquote>
|
||||
<p>针对<code>xmind8</code>版本的数据解析,因为该版本的<code>.xmind</code>文件内没有<code>content.json</code>,对应的是<code>content.xml</code>。</p>
|
||||
<p><code>content</code>:<code>.xmind</code>压缩包内的<code>content.xml</code>文件内容</p>
|
||||
<h3>transformToXmind(data, name)</h3>
|
||||
<blockquote>
|
||||
<p>v0.6.6+</p>
|
||||
</blockquote>
|
||||
<ul>
|
||||
<li>
|
||||
<p><code>data</code>:<code>simple-mind-map</code>思维导图数据,可以通过<code>mindMap.getData()</code>方法获取。</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><code>name</code>:要导出的文件名。</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>将<code>simple-mind-map</code>数据转为<code>xmind</code>文件。该方法为异步方法,返回一个<code>Promise</code>实例,返回的数据是一个<code>blob</code>类型的<code>zip</code>压缩包数据,你可以自行下载为文件。</p>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -27,6 +27,7 @@ import MiniMap from 'simple-mind-map/src/plugins/MiniMap.js'
|
||||
import Watermark from 'simple-mind-map/src/plugins/Watermark.js'
|
||||
import KeyboardNavigation from 'simple-mind-map/src/plugins/KeyboardNavigation.js'
|
||||
import ExportPDF from 'simple-mind-map/src/plugins/ExportPDF.js'
|
||||
import ExportXMind from 'simple-mind-map/src/plugins/ExportXMind.js'
|
||||
import Export from 'simple-mind-map/src/plugins/Export.js'
|
||||
import Drag from 'simple-mind-map/src/plugins/Drag.js'
|
||||
import Select from 'simple-mind-map/src/plugins/Select.js'
|
||||
@@ -66,6 +67,7 @@ MindMap
|
||||
.usePlugin(Drag)
|
||||
.usePlugin(KeyboardNavigation)
|
||||
.usePlugin(ExportPDF)
|
||||
.usePlugin(ExportXMind)
|
||||
.usePlugin(Export)
|
||||
.usePlugin(Select)
|
||||
.usePlugin(AssociativeLine)
|
||||
|
||||
@@ -150,7 +150,6 @@ export default {
|
||||
async handleXmind(file) {
|
||||
try {
|
||||
let data = await xmind.parseXmindFile(file.raw)
|
||||
console.log(data);
|
||||
this.$bus.$emit('setData', data)
|
||||
this.$message.success('导入成功')
|
||||
} catch (error) {
|
||||
|
||||
41
web/src/pages/Index/Index.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div class="indexContainer">
|
||||
<Header></Header>
|
||||
<Block1></Block1>
|
||||
<Block2></Block2>
|
||||
<Block3></Block3>
|
||||
<Block4></Block4>
|
||||
<Block5></Block5>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Header from './components/Header.vue'
|
||||
import Block1 from './components/Block1.vue'
|
||||
import Block2 from './components/Block2.vue'
|
||||
import Block3 from './components/Block3.vue'
|
||||
import Block4 from './components/Block4.vue'
|
||||
import Block5 from './components/Block5.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Header,
|
||||
Block1,
|
||||
Block2,
|
||||
Block3,
|
||||
Block4,
|
||||
Block5
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
|
||||
created() {},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.indexContainer {
|
||||
}
|
||||
</style>
|
||||
206
web/src/pages/Index/components/Block1.vue
Normal file
@@ -0,0 +1,206 @@
|
||||
<template>
|
||||
<div class="block1Container" :style="{ height: height + 'px' }">
|
||||
<div class="blockContent">
|
||||
<div class="infoBox">
|
||||
<div class="blockTitle">Simple mind map</div>
|
||||
<Split></Split>
|
||||
<div class="infoList">
|
||||
<p class="infoRow">是一个思维导图库</p>
|
||||
<p class="infoRow">同时也是一个思维导图软件</p>
|
||||
<p class="infoRow">开源、免费、强大...</p>
|
||||
</div>
|
||||
<div class="desc">
|
||||
无论你是开发者,还是使用者,只要喜欢思维导图,都能在这个项目里找到你需要的。
|
||||
</div>
|
||||
<div class="btnBox">
|
||||
<div class="btn" @click="useOnline">在线使用</div>
|
||||
<div class="btn btn2" @click="jumpDoc">开发文档</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="picBox">
|
||||
<div class="animation1"></div>
|
||||
<div class="animation2"></div>
|
||||
<div class="animation3"></div>
|
||||
<div class="pic"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Split from './Split.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Split
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
height: 0
|
||||
}
|
||||
},
|
||||
created() {
|
||||
window.addEventListener('resize', this.onResize)
|
||||
this.onResize()
|
||||
},
|
||||
mounted() {},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.onResize)
|
||||
},
|
||||
methods: {
|
||||
onResize() {
|
||||
this.height = window.innerHeight
|
||||
},
|
||||
|
||||
useOnline() {
|
||||
this.$router.push('/')
|
||||
},
|
||||
|
||||
jumpDoc() {
|
||||
this.$router.push('/doc/zh/')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.block1Container {
|
||||
background-color: #f0f9fa;
|
||||
border-radius: 0 0 0 450px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.blockContent {
|
||||
width: 100%;
|
||||
max-width: 1140px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.infoBox {
|
||||
.blockTitle {
|
||||
font-size: 16px;
|
||||
color: #1e3547;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.infoList {
|
||||
margin-top: 20px;
|
||||
|
||||
.infoRow {
|
||||
font-size: 45px;
|
||||
color: #1e3547;
|
||||
font-weight: 700;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.desc {
|
||||
color: #828f99;
|
||||
font-size: 20px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.btnBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
|
||||
.btn {
|
||||
height: 44px;
|
||||
padding: 0 20px;
|
||||
line-height: 44px;
|
||||
cursor: pointer;
|
||||
background: #1ea59a;
|
||||
border-color: #1ea59a;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
border-radius: 5px;
|
||||
transition: all 0.5s;
|
||||
margin-right: 10px;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
&.btn2 {
|
||||
background-color: #f5828b;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.picBox {
|
||||
position: relative;
|
||||
.pic {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
background-image: url('../../../assets/img/block1.png');
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.animation1 {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border: 7px solid #f5828b;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
right: -50px;
|
||||
bottom: 86px;
|
||||
animation-name: zoom1;
|
||||
animation-duration: 3s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
box-shadow: 0 12px 50px 0 rgba(0, 0, 0, 0.14);
|
||||
}
|
||||
|
||||
.animation2 {
|
||||
border-radius: 50%;
|
||||
background-color: #1ea59a;
|
||||
box-shadow: 0 20px 30px 0 rgba(48, 61, 114, 0.4);
|
||||
position: absolute;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
top: -60px;
|
||||
right: 60px;
|
||||
animation: spin 2s infinite alternate;
|
||||
bottom: 60px;
|
||||
}
|
||||
|
||||
.animation3 {
|
||||
border-radius: 50%;
|
||||
background-color: #25233a;
|
||||
box-shadow: 0 20px 30px 0 rgba(245, 130, 139, 0.4);
|
||||
position: absolute;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
bottom: 50px;
|
||||
left: 0px;
|
||||
animation: spin 3s infinite alternate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zoom1 {
|
||||
0% {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1.5);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(40px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
263
web/src/pages/Index/components/Block2.vue
Normal file
@@ -0,0 +1,263 @@
|
||||
<template>
|
||||
<div class="block2Container">
|
||||
<div class="blockContent">
|
||||
<div class="blockTitle">为什么选择Simple mind map?</div>
|
||||
<div class="dataList">
|
||||
<div class="dataItem" v-for="(item, index) in dataList" :key="index">
|
||||
<div class="iconBox">
|
||||
<span class="icon iconfont" :class="[item.icon]"></span>
|
||||
</div>
|
||||
<div class="dataValue">{{ item.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="desc">
|
||||
如果你是开发者:Simple mind
|
||||
map提供了一个功能完善的 js 思维导图库,不依赖任何框架,你可以使用它来快速完成Web思维导图产品的开发。
|
||||
</div>
|
||||
<div class="desc">
|
||||
如果你是使用者:Simple mind
|
||||
map提供了一个完整的思维导图软件,支持在线和客户端两种使用方式,所有功能完全免费。
|
||||
</div>
|
||||
<div class="functionList">
|
||||
<div
|
||||
class="functionItem"
|
||||
v-for="(item, index) in functionList"
|
||||
:key="index"
|
||||
>
|
||||
<div class="icon iconfont" :class="[item.icon]"></div>
|
||||
<div class="info">
|
||||
<div class="name">{{ item.name }}</div>
|
||||
<div class="value">{{ item.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dataList: [
|
||||
{
|
||||
icon: 'iconstar',
|
||||
value: 'Github star数量450+'
|
||||
},
|
||||
{
|
||||
icon: 'iconfork',
|
||||
value: 'Github fork数量100+'
|
||||
},
|
||||
{
|
||||
icon: 'iconxiazai',
|
||||
value: 'npm总下载次数10000+'
|
||||
},
|
||||
{
|
||||
icon: 'iconteamwork',
|
||||
value: '代码贡献者6+'
|
||||
}
|
||||
],
|
||||
functionList: [
|
||||
{
|
||||
icon: 'iconjingzi',
|
||||
name: '主题',
|
||||
value: '内置多种主题,允许高度自定义样式,支持注册新主题。'
|
||||
},
|
||||
{
|
||||
icon: 'iconjiegou',
|
||||
name: '结构',
|
||||
value:
|
||||
'支持常见的逻辑结构图、思维导图、组织结构图、目录组织图、时间轴、鱼骨图结构。'
|
||||
},
|
||||
{
|
||||
icon: 'iconjianpan',
|
||||
name: '快捷键',
|
||||
value: '常用操作支持快捷键,方便使用。'
|
||||
},
|
||||
{
|
||||
icon: 'iconzitixiahuaxian',
|
||||
name: '富文本',
|
||||
value:
|
||||
'节点支持普通文本和富文本两种类型,通过富文本可以创建样式丰富的节点文本内容。'
|
||||
},
|
||||
{
|
||||
icon: 'iconimage',
|
||||
name: '图片',
|
||||
value: '选中任一节点,选择上传图片,让内容达到图文并茂。'
|
||||
},
|
||||
{
|
||||
icon: 'icongaikuozonglan',
|
||||
name: '概要',
|
||||
value: '补充表述几个节点之间的关系。'
|
||||
},
|
||||
{
|
||||
icon: 'iconxiaolian',
|
||||
name: '图标',
|
||||
value: '通过添加图标来让节点内容更丰富。'
|
||||
},
|
||||
{
|
||||
icon: 'iconchaolianjie',
|
||||
name: '超链接',
|
||||
value: '节点可插入超链接,鼠标点击即可实现跳转。'
|
||||
},
|
||||
{
|
||||
icon: 'iconflow-Mark',
|
||||
name: '备注',
|
||||
value: '详细的内容可以放在备注中,节点内显得更简单明了。'
|
||||
},
|
||||
{
|
||||
icon: 'iconbiaoqian',
|
||||
name: '标签',
|
||||
value: '如果添加带颜色的标签,来突出要表达的重点。'
|
||||
},
|
||||
{
|
||||
icon: 'iconlianjiexian',
|
||||
name: '关联线',
|
||||
value: '通过添加关联线来表明节点之间的关联关系。'
|
||||
},
|
||||
{
|
||||
icon: 'iconmouseL',
|
||||
name: '拖动',
|
||||
value: '画布和节点都可以进行拖动。'
|
||||
},
|
||||
{
|
||||
icon: 'icondaohang',
|
||||
name: '导航器',
|
||||
value: '通过导航器可以方便知道当前画布处于思维导图的哪个部分。'
|
||||
},
|
||||
{
|
||||
icon: 'icondaochu',
|
||||
name: '导入导出',
|
||||
value: '支持多种文件格式的导入和导出。'
|
||||
},
|
||||
{
|
||||
icon: 'iconshuiyin',
|
||||
name: '水印',
|
||||
value: '内置支持水印功能,防止隐私泄露。'
|
||||
},
|
||||
{
|
||||
icon: 'iconwithdraw',
|
||||
name: '前进后退',
|
||||
value: '不小心误操作删除或修改内容支持一键撤回或恢复。'
|
||||
},
|
||||
{
|
||||
icon: 'iconfuhao-dagangshu',
|
||||
name: '大纲',
|
||||
value: '根据大纲编辑思维导图,让内容更详细也不容易出错。'
|
||||
},
|
||||
{
|
||||
icon: 'iconshezhi',
|
||||
name: '丰富的设置',
|
||||
value: '提供了丰富的功能设置,可以选择合适你的操作行为。'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.block2Container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.blockContent {
|
||||
padding: 100px 0;
|
||||
width: 100%;
|
||||
max-width: 1140px;
|
||||
|
||||
.dataList {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 40px;
|
||||
.dataItem {
|
||||
box-shadow: 0 5px 30px -10px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 20px;
|
||||
margin-right: 30px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
&:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.iconBox {
|
||||
width: 55px;
|
||||
height: 55px;
|
||||
border-radius: 10px;
|
||||
background-color: rgba(30, 165, 154, 0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.icon {
|
||||
font-size: 30px;
|
||||
color: #1ea59a;
|
||||
}
|
||||
}
|
||||
|
||||
.dataValue {
|
||||
color: #1e3547;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.blockTitle {
|
||||
font-size: 30px;
|
||||
font-weight: 700;
|
||||
color: #1e3547;
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.desc {
|
||||
color: #828f99;
|
||||
font-size: 17px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.functionList {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 60px;
|
||||
.functionItem {
|
||||
display: flex;
|
||||
width: 30%;
|
||||
margin-bottom: 50px;
|
||||
|
||||
.icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 50px;
|
||||
margin-right: 24px;
|
||||
color: #1ea59a;
|
||||
}
|
||||
|
||||
.info {
|
||||
.name {
|
||||
margin-bottom: 5px;
|
||||
color: #1e3547;
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 14px;
|
||||
color: #828f99;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
102
web/src/pages/Index/components/Block3.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<div class="block3Container">
|
||||
<div class="blockContent">
|
||||
<div class="picBox"></div>
|
||||
<div class="infoBox">
|
||||
<div class="infoTitle">客户端</div>
|
||||
<div class="info">支持Windows、Mac、Linux平台。</div>
|
||||
<div class="info">
|
||||
在线版数据默认保存在浏览器缓存里,同时也可以操作电脑本地文件,但是在线版受限于网络环境,访问可能比较慢,多个文件切换也不够方便,所以提供客户端版本,功能简单但不简陋。
|
||||
</div>
|
||||
<div class="btnList">
|
||||
<div class="btn">
|
||||
<a href="https://pan.baidu.com/s/1huasEbKsGNH2Af68dvWiOg?pwd=3bp3" target="_blank">百度网盘下载</a>
|
||||
</div>
|
||||
<div class="btn btn2">
|
||||
<a href="https://github.com/wanglin2/mind-map/releases" target="_blank">Github下载</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.block3Container {
|
||||
background-color: #f0f9fa;
|
||||
border-radius: 0 0 350px 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.blockContent {
|
||||
width: 100%;
|
||||
max-width: 1140px;
|
||||
height: 520px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.picBox {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
background-image: url('../../../assets/img/block3.png');
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.infoBox {
|
||||
.infoTitle {
|
||||
font-weight: 700;
|
||||
color: #1e3547;
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.info {
|
||||
color: #828f99;
|
||||
font-size: 16px;
|
||||
line-height: 1.7;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.btnList {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.btn {
|
||||
height: 44px;
|
||||
cursor: pointer;
|
||||
background: #1ea59a;
|
||||
border-color: #1ea59a;
|
||||
border-radius: 5px;
|
||||
transition: all 0.5s;
|
||||
margin-right: 10px;
|
||||
margin-top: 20px;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
&.btn2 {
|
||||
background-color: #f5828b;
|
||||
}
|
||||
|
||||
a {
|
||||
height: 100%;
|
||||
padding: 0 20px;
|
||||
line-height: 44px;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
140
web/src/pages/Index/components/Block4.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div class="block4Container">
|
||||
<div class="blockContent">
|
||||
<div class="infoBox">
|
||||
<div class="infoTitle">JavaScript库</div>
|
||||
<div class="info">
|
||||
simple-mind-map是一个简单&强大的Web思维导图库,不依赖任何特定框架,可以帮助你快速开发思维导图产品。
|
||||
</div>
|
||||
<div class="info">
|
||||
使用非常简单,只需三步即可渲染出一个思维导图:
|
||||
</div>
|
||||
<div class="info">第一步:安装</div>
|
||||
<div class="codeBox">
|
||||
<pre><code class="language-bash" ref="code1">
|
||||
npm i simple-mind-map
|
||||
</code></pre>
|
||||
</div>
|
||||
<div class="info">第二步:引入</div>
|
||||
<div class="codeBox">
|
||||
<pre><code class="language-javascript" ref="code2">
|
||||
import MindMap from "simple-mind-map"
|
||||
</code></pre>
|
||||
</div>
|
||||
<div class="info">第三步:实例化</div>
|
||||
<div class="codeBox">
|
||||
<pre><code class="language-javascript" ref="code3">
|
||||
const mindMap = new MindMap({
|
||||
// 提供一个宽高不为0的容器元素
|
||||
el: document.getElementById('mindMapContainer'),
|
||||
// 思维导图数据
|
||||
data: {
|
||||
"data": {
|
||||
"text": "根节点"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
})
|
||||
</code></pre>
|
||||
</div>
|
||||
<div class="btnList">
|
||||
<div class="btn" @click="jumpDoc">查看更多</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="picBox"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import hljs from 'highlight.js/lib/core'
|
||||
import javascript from 'highlight.js/lib/languages/javascript'
|
||||
import bash from 'highlight.js/lib/languages/bash'
|
||||
import 'highlight.js/styles/github.css'
|
||||
hljs.registerLanguage('javascript', javascript)
|
||||
hljs.registerLanguage('bash', bash)
|
||||
|
||||
export default {
|
||||
mounted() {
|
||||
hljs.highlightElement(this.$refs.code1)
|
||||
hljs.highlightElement(this.$refs.code2)
|
||||
hljs.highlightElement(this.$refs.code3)
|
||||
},
|
||||
methods: {
|
||||
jumpDoc() {
|
||||
this.$router.push('/doc/zh/')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.block4Container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.blockContent {
|
||||
width: 100%;
|
||||
max-width: 1140px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 50px 0;
|
||||
|
||||
.picBox {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
background-image: url('../../../assets/img/block4.png');
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.infoBox {
|
||||
margin-right: 50px;
|
||||
|
||||
.infoTitle {
|
||||
font-weight: 700;
|
||||
color: #1e3547;
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.info {
|
||||
color: #828f99;
|
||||
font-size: 16px;
|
||||
line-height: 1.7;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.codeBox {
|
||||
font-size: 16px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.btnList {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.btn {
|
||||
height: 44px;
|
||||
cursor: pointer;
|
||||
background: #1ea59a;
|
||||
border-color: #1ea59a;
|
||||
border-radius: 5px;
|
||||
transition: all 0.5s;
|
||||
margin-right: 10px;
|
||||
margin-top: 20px;
|
||||
height: 100%;
|
||||
padding: 0 20px;
|
||||
line-height: 44px;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
229
web/src/pages/Index/components/Block5.vue
Normal file
@@ -0,0 +1,229 @@
|
||||
<template>
|
||||
<div class="block5Container">
|
||||
<div class="blockContent">
|
||||
<div class="infoBox">
|
||||
<div class="infoTitle">街角小林出品</div>
|
||||
<div class="infoDesc">
|
||||
男,90后,六年+前端开发工程师,热爱前端、写作、开源。
|
||||
</div>
|
||||
<div class="linkBtnList">
|
||||
<div class="linkBtn">
|
||||
<a href="https://github.com/wanglin2" target="_blank">
|
||||
<span class="linkBtnIcon iconfont icongithub"></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="linkBtn">
|
||||
<a href="https://juejin.cn/user/325111170756279" target="_blank">
|
||||
<span class="linkBtnIcon text">掘金</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="linkBtn">
|
||||
<a
|
||||
href="https://segmentfault.com/u/jiejiaoxiaolin/articles"
|
||||
target="_blank"
|
||||
>
|
||||
<span class="linkBtnIcon text">思否</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="linkBtn">
|
||||
<a href="http://lxqnsys.com/" target="_blank">
|
||||
<span class="linkBtnIcon iconfont iconwangzhan"></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="linkBtn">
|
||||
<a
|
||||
href="https://www.zhihu.com/people/wang-lin-49-43-65"
|
||||
target="_blank"
|
||||
>
|
||||
<span class="linkBtnIcon iconfont iconshejiaotubiao-10"></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="linkBtn">
|
||||
<a
|
||||
href="https://blog.csdn.net/sinat_33488770?type=blog"
|
||||
target="_blank"
|
||||
>
|
||||
<span class="linkBtnIcon iconfont iconcsdn"></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="linkBox">
|
||||
<div class="linkTitle">更多作品</div>
|
||||
<div class="linkList">
|
||||
<div class="linkItem" v-for="item in linkList" :key="item.name">
|
||||
<a :href="item.url" target="_blank">{{ item.name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
linkList: [
|
||||
{
|
||||
name: 'CodeRun',
|
||||
url: 'https://github.com/wanglin2/code-run'
|
||||
},
|
||||
{
|
||||
name: 'TinyWhiteboard',
|
||||
url: 'https://github.com/wanglin2/tiny_whiteboard'
|
||||
},
|
||||
{
|
||||
name: 'Mark.js',
|
||||
url: 'https://github.com/wanglin2/markjs'
|
||||
},
|
||||
{
|
||||
name: 'WebMapEngine',
|
||||
url: 'https://github.com/wanglin2/web_map_demo'
|
||||
},
|
||||
{
|
||||
name: 'SimpleNoviceGuide',
|
||||
url: 'https://github.com/wanglin2/simple-novice-guide'
|
||||
},
|
||||
{
|
||||
name: 'CanvasEditor',
|
||||
url: 'https://github.com/wanglin2/canvas-editor-demo'
|
||||
},
|
||||
{
|
||||
name: 'JsonTreeView',
|
||||
url: 'https://github.com/wanglin2/json-tree-view'
|
||||
},
|
||||
{
|
||||
name: 'SimpleFlowChart',
|
||||
url: 'https://github.com/wanglin2/simple-flow-chart'
|
||||
},
|
||||
{
|
||||
name: 'VideoTimeLine',
|
||||
url: 'https://github.com/wanglin2/VideoTimeLine'
|
||||
},
|
||||
{
|
||||
name: 'MarkdownEditor',
|
||||
url: 'https://github.com/wanglin2/markdown_editor_sync_scroll_demo'
|
||||
},
|
||||
{
|
||||
name: 'AssociationLine',
|
||||
url: 'https://github.com/wanglin2/AssociationLineDemo'
|
||||
},
|
||||
{
|
||||
name: 'HandPaintedStyle',
|
||||
url: 'https://github.com/wanglin2/handPaintedStyle'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.block5Container {
|
||||
background-color: #f0f9fa;
|
||||
border-radius: 450px 0 0 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.blockContent {
|
||||
width: 100%;
|
||||
max-width: 1140px;
|
||||
height: 350px;
|
||||
display: flex;
|
||||
padding-top: 100px;
|
||||
|
||||
.infoBox {
|
||||
.infoTitle {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #1e3547;
|
||||
line-height: 1.4;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.infoDesc {
|
||||
color: #828f99;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.linkBtnList {
|
||||
margin-top: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.linkBtn {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
background-color: #f0f9fa;
|
||||
border: 2px solid #dbf1f1;
|
||||
margin-right: 10px;
|
||||
|
||||
a {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: all 0.5s;
|
||||
|
||||
&:hover {
|
||||
background-color: #1ea59a;
|
||||
|
||||
.linkBtnIcon {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.linkBtnIcon {
|
||||
font-size: 20px;
|
||||
color: #1e3547;
|
||||
transition: all 0.5s;
|
||||
|
||||
&.text {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.linkBox {
|
||||
margin-left: 150px;
|
||||
.linkTitle {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #1e3547;
|
||||
line-height: 1.4;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.linkList {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.linkItem {
|
||||
margin-right: 20px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
a {
|
||||
color: #828f99;
|
||||
font-size: 15px;
|
||||
transition: all 0.5s;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
color: #1ea59a;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
167
web/src/pages/Index/components/Header.vue
Normal file
@@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<div class="indexHeaderContainer" :class="{ active: active }">
|
||||
<div class="headerContent">
|
||||
<div class="logoBox">
|
||||
<span class="logo"></span>
|
||||
<span class="title">SimpleMindMap</span>
|
||||
</div>
|
||||
<div class="nav">
|
||||
<div
|
||||
class="navItem"
|
||||
@click="jumpTop"
|
||||
:class="{
|
||||
active: activeTab === 'home'
|
||||
}"
|
||||
>
|
||||
首页
|
||||
</div>
|
||||
<div class="navItem" @click="useOnline">在线使用</div>
|
||||
<div
|
||||
class="navItem"
|
||||
@click="jumpClient"
|
||||
:class="{
|
||||
active: activeTab === 'client'
|
||||
}"
|
||||
>
|
||||
客户端
|
||||
</div>
|
||||
<div class="navItem" @click="jumpDoc">开发文档</div>
|
||||
<div class="navItem">
|
||||
<a href="https://github.com/wanglin2/mind-map" target="_blank"
|
||||
>Github</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
active: false,
|
||||
activeTab: 'home',
|
||||
clientEl: null
|
||||
}
|
||||
},
|
||||
created() {
|
||||
window.addEventListener('scroll', this.onScroll)
|
||||
},
|
||||
mounted() {
|
||||
this.clientEl = document.querySelector('.block3Container')
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('scroll', this.onScroll)
|
||||
},
|
||||
methods: {
|
||||
onScroll() {
|
||||
this.active = window.scrollY > 0
|
||||
let offsetTop = this.clientEl.offsetTop
|
||||
if (
|
||||
window.scrollY + window.innerHeight >= offsetTop &&
|
||||
window.scrollY <= offsetTop + this.clientEl.offsetHeight
|
||||
) {
|
||||
this.activeTab = 'client'
|
||||
} else {
|
||||
this.activeTab = 'home'
|
||||
}
|
||||
},
|
||||
|
||||
useOnline() {
|
||||
this.$router.push('/')
|
||||
},
|
||||
|
||||
jumpDoc() {
|
||||
this.$router.push('/doc/zh/')
|
||||
},
|
||||
|
||||
jumpTop() {
|
||||
window.scrollTo(0, 0)
|
||||
},
|
||||
|
||||
jumpClient() {
|
||||
window.scrollTo(0, this.clientEl.offsetTop - 76)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.indexHeaderContainer {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 76px;
|
||||
transition: all 0.5s;
|
||||
background-color: transparent;
|
||||
z-index: 999;
|
||||
|
||||
&.active {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 5px 30px -10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.headerContent {
|
||||
height: 100%;
|
||||
max-width: 1140px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.logoBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.logo {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
background-image: url('../../../assets/img/logo2.png');
|
||||
background-size: cover;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #000;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.navItem {
|
||||
color: #828f99;
|
||||
font-size: 15px;
|
||||
transition: all 0.5s;
|
||||
margin-right: 40px;
|
||||
cursor: pointer;
|
||||
|
||||
&:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: #1ea59a;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #1ea59a;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #828f99;
|
||||
|
||||
&:hover {
|
||||
color: #1ea59a;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
16
web/src/pages/Index/components/Split.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div class="splitContainer"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.splitContainer {
|
||||
width: 122px;
|
||||
height: 15px;
|
||||
background-image: url('../../../assets/img/split.png');
|
||||
background-size: cover;
|
||||
}
|
||||
</style>
|
||||
@@ -3,6 +3,7 @@ import VueRouter from 'vue-router'
|
||||
import EditPage from '@/pages/Edit/Index'
|
||||
import DocPage from '@/pages/Doc/Index'
|
||||
import routerList from '@/pages/Doc/routerList'
|
||||
import IndexPage from '@/pages/Index/Index'
|
||||
|
||||
// 处理没有翻译的章节路由
|
||||
const handleRouterList = () => {
|
||||
@@ -26,6 +27,11 @@ handleRouterList()
|
||||
Vue.use(VueRouter)
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/index',
|
||||
name: 'Index',
|
||||
component: IndexPage
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
name: 'Edit',
|
||||
|
||||