Compare commits

...

14 Commits

Author SHA1 Message Date
wanglin2
660906e4c5 【春节快乐】修改文档 2023-01-17 10:55:28 +08:00
wanglin2
971f0d3446 打包0.2.24 2023-01-17 09:24:33 +08:00
wanglin2
63eccede7f 完成水印功能、新增水印文档 2023-01-17 09:20:37 +08:00
wanglin2
5bd6adb488 节点自由拖拽支持配置是否开启 2023-01-14 16:26:51 +08:00
wanglin2
fbdc5e0f39 完善文档 2023-01-14 14:44:53 +08:00
wanglin2
5e994322fe 全新文档 2023-01-14 11:33:05 +08:00
wanglin2
fd0a594b56 打包0.2.23 2023-01-12 15:50:10 +08:00
wanglin2
d7b93ba0a0 新增支持注册新主题 2023-01-12 15:45:41 +08:00
wanglin2
c14fb5ad9d 修改文档 2023-01-12 09:07:10 +08:00
wanglin2
4d82dfdf9e 修复文档错误 2023-01-11 18:01:56 +08:00
wanglin2
7d3f08bed0 去除文档中的当前版本信息 2023-01-11 16:59:52 +08:00
wanglin2
60597616ec 取消内置在simple-mind-map包内的主题和结构图片 2023-01-11 16:55:44 +08:00
wanglin2
6d758a988c 打包 2023-01-11 14:23:27 +08:00
wanglin2
503506dfd4 区分全屏查看和全屏编辑 2023-01-11 14:12:42 +08:00
179 changed files with 8495 additions and 2801 deletions

1305
README.md

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1 @@
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>一个简单的web思维导图实现</title><link href="dist/js/chunk-2d20ec02.917aff76.js" rel="prefetch"><link href="dist/js/chunk-2d216b67.2d06497a.js" rel="prefetch"><link href="dist/js/chunk-3a2f3e67.13278516.js" rel="prefetch"><link href="dist/css/app.92b546b0.css" rel="preload" as="style"><link href="dist/css/chunk-vendors.94891485.css" rel="preload" as="style"><link href="dist/js/app.aca24f03.js" rel="preload" as="script"><link href="dist/js/chunk-vendors.6cac1a4d.js" rel="preload" as="script"><link href="dist/css/chunk-vendors.94891485.css" rel="stylesheet"><link href="dist/css/app.92b546b0.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but thoughts doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="dist/js/chunk-vendors.6cac1a4d.js"></script><script src="dist/js/app.aca24f03.js"></script></body></html>
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="./dist/logo.png"><title>一个简单的web思维导图实现</title><link href="dist/js/chunk-2d0a3179.8bac0e6a.js" rel="prefetch"><link href="dist/js/chunk-2d0aa579.3848b480.js" rel="prefetch"><link href="dist/js/chunk-2d0aa978.0c0ab348.js" rel="prefetch"><link href="dist/js/chunk-2d0ab10b.89baf4c9.js" rel="prefetch"><link href="dist/js/chunk-2d0abe0f.ba566b9e.js" rel="prefetch"><link href="dist/js/chunk-2d0b361e.d57feb19.js" rel="prefetch"><link href="dist/js/chunk-2d0b91e5.9058553b.js" rel="prefetch"><link href="dist/js/chunk-2d0b92c3.ff912e01.js" rel="prefetch"><link href="dist/js/chunk-2d0bd54e.988f0b69.js" rel="prefetch"><link href="dist/js/chunk-2d0be174.b87008fb.js" rel="prefetch"><link href="dist/js/chunk-2d0c0a44.d53f93f5.js" rel="prefetch"><link href="dist/js/chunk-2d0c18d8.ef38347c.js" rel="prefetch"><link href="dist/js/chunk-2d0c191e.fe3d1357.js" rel="prefetch"><link href="dist/js/chunk-2d0c1a01.dc5d667f.js" rel="prefetch"><link href="dist/js/chunk-2d0d9fbc.7c0e8c3b.js" rel="prefetch"><link href="dist/js/chunk-2d0da701.7110cb06.js" rel="prefetch"><link href="dist/js/chunk-2d0dad5f.789c08d8.js" rel="prefetch"><link href="dist/js/chunk-2d0db0f2.773ee0f5.js" rel="prefetch"><link href="dist/js/chunk-2d0dddce.29899f8c.js" rel="prefetch"><link href="dist/js/chunk-2d0de01b.4f180262.js" rel="prefetch"><link href="dist/js/chunk-2d0e2326.7b20d38b.js" rel="prefetch"><link href="dist/js/chunk-2d0e268c.314fd603.js" rel="prefetch"><link href="dist/js/chunk-2d0e5089.cd1baa18.js" rel="prefetch"><link href="dist/js/chunk-2d0e9742.62e1ccbf.js" rel="prefetch"><link href="dist/js/chunk-2d0f026c.af35b5ce.js" rel="prefetch"><link href="dist/js/chunk-2d2082b9.ffe557f3.js" rel="prefetch"><link href="dist/js/chunk-2d208ffa.6b617b98.js" rel="prefetch"><link href="dist/js/chunk-2d20ec02.917aff76.js" rel="prefetch"><link href="dist/js/chunk-2d20f68f.66cba965.js" rel="prefetch"><link href="dist/js/chunk-2d210a7a.25884d5e.js" rel="prefetch"><link href="dist/js/chunk-2d216004.3416e02c.js" rel="prefetch"><link href="dist/js/chunk-2d216b67.2d06497a.js" rel="prefetch"><link href="dist/js/chunk-2d217907.96994ac6.js" rel="prefetch"><link href="dist/js/chunk-2d226d0a.d30a47fe.js" rel="prefetch"><link href="dist/js/chunk-2d2299c3.5aa1632c.js" rel="prefetch"><link href="dist/js/chunk-2d2308b0.d813ba33.js" rel="prefetch"><link href="dist/js/chunk-3a2f3e67.13278516.js" rel="prefetch"><link href="dist/css/app.a6908b68.css" rel="preload" as="style"><link href="dist/css/chunk-vendors.1790fe42.css" rel="preload" as="style"><link href="dist/js/app.2f5edb91.js" rel="preload" as="script"><link href="dist/js/chunk-vendors.7f3b8358.js" rel="preload" as="script"><link href="dist/css/chunk-vendors.1790fe42.css" rel="stylesheet"><link href="dist/css/app.a6908b68.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but thoughts doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="dist/js/chunk-vendors.7f3b8358.js"></script><script src="dist/js/app.2f5edb91.js"></script></body></html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

View File

@@ -925,5 +925,6 @@ export default {
"layout": "logicalStructure",
// "layout": "mindMap",
// "layout": "catalogOrganization"
// "layout": "organizationStructure"
// "layout": "organizationStructure",
"config": {}
}

View File

@@ -62,5 +62,6 @@
"sx": 0,
"sy": 0
}
}
},
"config": {}
}

View File

@@ -11,11 +11,13 @@ import Export from './src/Export'
import Select from './src/Select'
import Drag from './src/Drag'
import MiniMap from './src/MiniMap'
import Watermark from './src/Watermark'
import { layoutValueList } from './src/utils/constant'
import { SVG } from '@svgdotjs/svg.js'
import xmind from './src/parse/xmind'
import { simpleDeepClone } from './src/utils'
import KeyboardNavigation from './src/KeyboardNavigation'
import defaultTheme from './src/themes/default'
// 默认选项配置
const defaultOpt = {
@@ -44,13 +46,27 @@ const defaultOpt = {
// 多选节点时鼠标移动距边缘多少距离时开始偏移
selectTranslateLimit: 20,
// 自定义节点备注内容显示
customNoteContentShow: null
customNoteContentShow: null,
/*
{
show(){},
hide(){}
}
*/
// 是否开启节点自由拖拽
enableFreeDrag: false,
// 水印配置
watermarkConfig: {
text: '',
lineSpacing: 100,
textSpacing: 100,
angle: 30,
textStyle: {
color: '#999',
opacity: 0.5,
fontSize: 14
}
}
}
// 思维导图
@@ -129,6 +145,11 @@ class MindMap {
mindMap: this
})
// 水印类
this.watermark = new Watermark({
mindMap: this
})
// 批量执行类
this.batchExecution = new BatchExecution()
@@ -228,6 +249,16 @@ class MindMap {
return prop === undefined ? this.themeConfig : this.themeConfig[prop]
}
// 获取配置
getConfig(prop) {
return prop === undefined ? this.opt : this.opt[prop]
}
// 更新配置
updateConfig(opt = {}) {
this.opt = this.handleOpt(merge.all([defaultOpt, this.opt, opt]))
}
// 获取当前布局结构
getLayout() {
return this.opt.layout
@@ -328,4 +359,12 @@ class MindMap {
MindMap.xmind = xmind
// 定义新主题
MindMap.defineTheme = (name, config = {}) => {
if (theme[name]) {
return new Error('该主题名称已存在')
}
theme[name] = merge(defaultTheme, config)
}
export default MindMap

View File

@@ -1,11 +1,11 @@
{
"name": "simple-mind-map",
"version": "0.2.20",
"version": "0.2.21",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"version": "0.2.20",
"version": "0.2.21",
"license": "MIT",
"dependencies": {
"@svgdotjs/svg.js": "^3.0.16",

View File

@@ -1,6 +1,6 @@
{
"name": "simple-mind-map",
"version": "0.2.21",
"version": "0.2.24",
"description": "一个简单的web在线思维导图",
"authors": [
{

View File

@@ -1,4 +1,4 @@
// 将/** */类型的注释转换为//类型
// 遍历所有js文件
const path = require('path')
const fs = require('fs')
@@ -11,13 +11,26 @@ const transform = dir => {
if (fs.statSync(file).isDirectory()) {
transform(file)
} else if (/\.js$/.test(file)) {
rewriteComments(file)
transformFile(file)
}
})
}
const rewriteComments = file => {
const transformFile = file => {
console.log(file);
let content = fs.readFileSync(file, 'utf-8')
countCodeLines(content)
// transformComments(file, content)
}
// 统计代码行数
let totalLines = 0
const countCodeLines = (content) => {
totalLines += content.split(/\n/).length
}
// 转换注释类型
const transformComments = (file, content) => {
console.log('当前转换文件:', file)
content = content.replace(/\/\*\*[^/]+\*\//g, str => {
let res = /@Desc:([^\n]+)\n/g.exec(str)
@@ -29,4 +42,5 @@ const rewriteComments = file => {
}
transform(entryPath)
rewriteComments(path.join(__dirname, '../index.js'))
transformFile(path.join(__dirname, '../index.js'))
console.log(totalLines);

View File

@@ -1,254 +1,263 @@
import { bfsWalk, throttle } from './utils'
import Base from './layouts/Base'
// 节点拖动类
class Drag extends Base {
// 构造函数
constructor({ mindMap }) {
super(mindMap.renderer)
this.mindMap = mindMap
this.reset()
this.bindEvent()
}
// 复位
reset() {
// 当前拖拽节点
this.node = null
// 当前重叠节点
this.overlapNode = null
// 当前上一个同级节点
this.prevNode = null
// 当前下一个同级节点
this.nextNode = null
// 画布的变换数据
this.drawTransform = null
// 克隆节点
this.clone = null
// 连接线
this.line = null
// 同级位置占位符
this.placeholder = null
// 鼠标按下位置和节点左上角的偏移量
this.offsetX = 0
this.offsetY = 0
// 克隆节点左上角的坐标
this.cloneNodeLeft = 0
this.cloneNodeTop = 0
// 当前鼠标是否按下
this.isMousedown = false
// 拖拽的鼠标位置变量
this.mouseDownX = 0
this.mouseDownY = 0
this.mouseMoveX = 0
this.mouseMoveY = 0
}
// 绑定事件
bindEvent() {
this.checkOverlapNode = throttle(this.checkOverlapNode, 300, this)
this.mindMap.on('node_mousedown', (node, e) => {
if (this.mindMap.opt.readonly || node.isGeneralization) {
return
}
if (e.which !== 1 || node.isRoot) {
return
}
e.preventDefault()
// 计算鼠标按下的位置距离节点左上角的距离
this.drawTransform = this.mindMap.draw.transform()
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
this.offsetX = x - (node.left * scaleX + translateX)
this.offsetY = y - (node.top * scaleY + translateY)
this.node = node
this.isMousedown = true
this.mouseDownX = x
this.mouseDownY = y
})
this.mindMap.on('mousemove', e => {
if (this.mindMap.opt.readonly) {
return
}
if (!this.isMousedown) {
return
}
e.preventDefault()
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
this.mouseMoveX = x
this.mouseMoveY = y
if (
Math.abs(x - this.mouseDownX) <= 10 &&
Math.abs(y - this.mouseDownY) <= 10 &&
!this.node.isDrag
) {
return
}
this.mindMap.renderer.clearAllActive()
this.onMove(x, y)
})
this.onMouseup = this.onMouseup.bind(this)
this.mindMap.on('node_mouseup', this.onMouseup)
this.mindMap.on('mouseup', this.onMouseup)
}
// 鼠标松开事件
onMouseup(e) {
if (!this.isMousedown) {
return
}
this.isMousedown = false
let _nodeIsDrag = this.node.isDrag
this.node.isDrag = false
this.node.show()
this.removeCloneNode()
// 存在重叠子节点,则移动作为其子节点
if (this.overlapNode) {
this.mindMap.renderer.setNodeActive(this.overlapNode, false)
this.mindMap.execCommand('MOVE_NODE_TO', this.node, this.overlapNode)
} else if (this.prevNode) {
// 存在前一个相邻节点,作为其下一个兄弟节点
this.mindMap.renderer.setNodeActive(this.prevNode, false)
this.mindMap.execCommand('INSERT_AFTER', this.node, this.prevNode)
} else if (this.nextNode) {
// 存在下一个相邻节点,作为其前一个兄弟节点
this.mindMap.renderer.setNodeActive(this.nextNode, false)
this.mindMap.execCommand('INSERT_BEFORE', this.node, this.nextNode)
} else if (_nodeIsDrag) {
// 自定义位置
let { x, y } = this.mindMap.toPos(
e.clientX - this.offsetX,
e.clientY - this.offsetY
)
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
x = (x - translateX) / scaleX
y = (y - translateY) / scaleY
this.node.left = x
this.node.top = y
this.node.customLeft = x
this.node.customTop = y
this.mindMap.execCommand('SET_NODE_CUSTOM_POSITION', this.node, x, y)
this.mindMap.render()
}
this.reset()
}
// 创建克隆节点
createCloneNode() {
if (!this.clone) {
// 节点
this.clone = this.node.group.clone()
this.clone.opacity(0.5)
this.clone.css('z-index', 99999)
this.node.isDrag = true
this.node.hide()
// 连接线
this.line = this.draw.path()
this.line.opacity(0.5)
this.node.styleLine(this.line, this.node)
// 同级位置占位符
this.placeholder = this.draw.rect().fill({
color: this.node.style.merge('lineColor', true)
})
this.mindMap.draw.add(this.clone)
}
}
// 移除克隆节点
removeCloneNode() {
if (!this.clone) {
return
}
this.clone.remove()
this.line.remove()
this.placeholder.remove()
}
// 拖动中
onMove(x, y) {
if (!this.isMousedown) {
return
}
this.createCloneNode()
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
this.cloneNodeLeft = x - this.offsetX
this.cloneNodeTop = y - this.offsetY
x = (this.cloneNodeLeft - translateX) / scaleX
y = (this.cloneNodeTop - translateY) / scaleY
let t = this.clone.transform()
this.clone.translate(x - t.translateX, y - t.translateY)
// 连接线
let parent = this.node.parent
this.line.plot(
this.quadraticCurvePath(
parent.left + parent.width / 2,
parent.top + parent.height / 2,
x + this.node.width / 2,
y + this.node.height / 2
)
)
this.checkOverlapNode()
}
// 检测重叠节点
checkOverlapNode() {
if (!this.drawTransform) {
return
}
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
let checkRight = this.cloneNodeLeft + this.node.width * scaleX
let checkBottom = this.cloneNodeTop + this.node.height * scaleX
this.overlapNode = null
this.prevNode = null
this.nextNode = null
this.placeholder.size(0, 0)
bfsWalk(this.mindMap.renderer.root, node => {
if (node.nodeData.data.isActive) {
this.mindMap.renderer.setNodeActive(node, false)
}
if (node === this.node || this.node.isParent(node)) {
return
}
if (this.overlapNode || (this.prevNode && this.nextNode)) {
return
}
let { left, top, width, height } = node
let _left = left
let _top = top
let _bottom = top + height
let right = (left + width) * scaleX + translateX
let bottom = (top + height) * scaleY + translateY
left = left * scaleX + translateX
top = top * scaleY + translateY
// 检测是否重叠
if (!this.overlapNode) {
if (
left <= checkRight &&
right >= this.cloneNodeLeft &&
top <= checkBottom &&
bottom >= this.cloneNodeTop
) {
this.overlapNode = node
}
}
// 检测兄弟节点位置
if (!this.prevNode && !this.nextNode && !node.isRoot) {
// && this.node.isBrother(node)
if (left <= checkRight && right >= this.cloneNodeLeft) {
if (this.cloneNodeTop > bottom && this.cloneNodeTop <= bottom + 10) {
this.prevNode = node
this.placeholder.size(node.width, 10).move(_left, _bottom)
} else if (checkBottom < top && checkBottom >= top - 10) {
this.nextNode = node
this.placeholder.size(node.width, 10).move(_left, _top - 10)
}
}
import { bfsWalk, throttle } from './utils'
import Base from './layouts/Base'
// 节点拖动类
class Drag extends Base {
// 构造函数
constructor({ mindMap }) {
super(mindMap.renderer)
this.mindMap = mindMap
this.reset()
this.bindEvent()
}
// 复位
reset() {
// 当前拖拽节点
this.node = null
// 当前重叠节点
this.overlapNode = null
// 当前上一个同级节点
this.prevNode = null
// 当前下一个同级节点
this.nextNode = null
// 画布的变换数据
this.drawTransform = null
// 克隆节点
this.clone = null
// 连接线
this.line = null
// 同级位置占位符
this.placeholder = null
// 鼠标按下位置和节点左上角的偏移量
this.offsetX = 0
this.offsetY = 0
// 克隆节点左上角的坐标
this.cloneNodeLeft = 0
this.cloneNodeTop = 0
// 当前鼠标是否按下
this.isMousedown = false
// 拖拽的鼠标位置变量
this.mouseDownX = 0
this.mouseDownY = 0
this.mouseMoveX = 0
this.mouseMoveY = 0
}
// 绑定事件
bindEvent() {
this.checkOverlapNode = throttle(this.checkOverlapNode, 300, this)
this.mindMap.on('node_mousedown', (node, e) => {
if (this.mindMap.opt.readonly || node.isGeneralization) {
return
}
if (e.which !== 1 || node.isRoot) {
return
}
e.preventDefault()
// 计算鼠标按下的位置距离节点左上角的距离
this.drawTransform = this.mindMap.draw.transform()
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
this.offsetX = x - (node.left * scaleX + translateX)
this.offsetY = y - (node.top * scaleY + translateY)
this.node = node
this.isMousedown = true
this.mouseDownX = x
this.mouseDownY = y
})
this.mindMap.on('mousemove', e => {
if (this.mindMap.opt.readonly) {
return
}
if (!this.isMousedown) {
return
}
e.preventDefault()
let { x, y } = this.mindMap.toPos(e.clientX, e.clientY)
this.mouseMoveX = x
this.mouseMoveY = y
if (
Math.abs(x - this.mouseDownX) <= 10 &&
Math.abs(y - this.mouseDownY) <= 10 &&
!this.node.isDrag
) {
return
}
this.mindMap.renderer.clearAllActive()
this.onMove(x, y)
})
this.onMouseup = this.onMouseup.bind(this)
this.mindMap.on('node_mouseup', this.onMouseup)
this.mindMap.on('mouseup', this.onMouseup)
}
// 鼠标松开事件
onMouseup(e) {
if (!this.isMousedown) {
return
}
this.isMousedown = false
let _nodeIsDrag = this.node.isDrag
this.node.isDrag = false
this.node.show()
this.removeCloneNode()
// 存在重叠子节点,则移动作为其子节点
if (this.overlapNode) {
this.mindMap.renderer.setNodeActive(this.overlapNode, false)
this.mindMap.execCommand('MOVE_NODE_TO', this.node, this.overlapNode)
} else if (this.prevNode) {
// 存在前一个相邻节点,作为其下一个兄弟节点
this.mindMap.renderer.setNodeActive(this.prevNode, false)
this.mindMap.execCommand('INSERT_AFTER', this.node, this.prevNode)
} else if (this.nextNode) {
// 存在下一个相邻节点,作为其前一个兄弟节点
this.mindMap.renderer.setNodeActive(this.nextNode, false)
this.mindMap.execCommand('INSERT_BEFORE', this.node, this.nextNode)
} else if (_nodeIsDrag && this.mindMap.opt.enableFreeDrag) {
// 自定义位置
let { x, y } = this.mindMap.toPos(
e.clientX - this.offsetX,
e.clientY - this.offsetY
)
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
x = (x - translateX) / scaleX
y = (y - translateY) / scaleY
this.node.left = x
this.node.top = y
this.node.customLeft = x
this.node.customTop = y
this.mindMap.execCommand('SET_NODE_CUSTOM_POSITION', this.node, x, y)
this.mindMap.render()
}
this.reset()
}
// 创建克隆节点
createCloneNode() {
if (!this.clone) {
// 节点
this.clone = this.node.group.clone()
this.clone.opacity(0.5)
this.clone.css('z-index', 99999)
this.node.isDrag = true
this.node.hide()
// 连接线
this.line = this.draw.path()
this.line.opacity(0.5)
this.node.styleLine(this.line, this.node)
// 同级位置占位符
this.placeholder = this.draw.rect().fill({
color: this.node.style.merge('lineColor', true)
})
this.mindMap.draw.add(this.clone)
}
}
// 移除克隆节点
removeCloneNode() {
if (!this.clone) {
return
}
this.clone.remove()
this.line.remove()
this.placeholder.remove()
}
// 拖动中
onMove(x, y) {
if (!this.isMousedown) {
return
}
this.createCloneNode()
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
this.cloneNodeLeft = x - this.offsetX
this.cloneNodeTop = y - this.offsetY
x = (this.cloneNodeLeft - translateX) / scaleX
y = (this.cloneNodeTop - translateY) / scaleY
let t = this.clone.transform()
this.clone.translate(x - t.translateX, y - t.translateY)
// 连接线
let parent = this.node.parent
this.line.plot(
this.quadraticCurvePath(
parent.left + parent.width / 2,
parent.top + parent.height / 2,
x + this.node.width / 2,
y + this.node.height / 2
)
)
this.checkOverlapNode()
}
// 检测重叠节点
checkOverlapNode() {
if (!this.drawTransform) {
return
}
let { scaleX, scaleY, translateX, translateY } = this.drawTransform
let checkRight = this.cloneNodeLeft + this.node.width * scaleX
let checkBottom = this.cloneNodeTop + this.node.height * scaleX
this.overlapNode = null
this.prevNode = null
this.nextNode = null
this.placeholder.size(0, 0)
bfsWalk(this.mindMap.renderer.root, node => {
if (node.nodeData.data.isActive) {
this.mindMap.renderer.setNodeActive(node, false)
}
if (node === this.node || this.node.isParent(node)) {
return
}
if (this.overlapNode || (this.prevNode && this.nextNode)) {
return
}
let { left, top, width, height } = node
let _left = left
let _top = top
let _bottom = top + height
let right = (left + width) * scaleX + translateX
let bottom = (top + height) * scaleY + translateY
left = left * scaleX + translateX
top = top * scaleY + translateY
// 检测是否重叠
if (!this.overlapNode) {
if (
left <= checkRight &&
right >= this.cloneNodeLeft &&
top <= checkBottom &&
bottom >= this.cloneNodeTop
) {
this.overlapNode = node
}
}
// 检测兄弟节点位置
if (!this.prevNode && !this.nextNode && !node.isRoot) {
// && this.node.isBrother(node)
if (left <= checkRight && right >= this.cloneNodeLeft) {
if (this.cloneNodeTop > bottom && this.cloneNodeTop <= bottom + 10) {
this.prevNode = node
this.placeholder.size(node.width, 10).move(_left, _bottom)
} else if (checkBottom < top && checkBottom >= top - 10) {
this.nextNode = node
this.placeholder.size(node.width, 10).move(_left, _top - 10)
}
}
}
})
if (this.overlapNode) {
this.mindMap.renderer.setNodeActive(this.overlapNode, true)
}
}
}
export default Drag

View File

@@ -0,0 +1,111 @@
import { Text, G } from '@svgdotjs/svg.js'
import { degToRad, camelCaseToHyphen } from './utils'
import merge from 'deepmerge'
// 水印类
export default class Watermark {
constructor(opt = {}) {
this.mindMap = opt.mindMap
this.lineSpacing = 0 // 水印行间距
this.textSpacing = 0 // 行内水印间距
this.angle = 0 // 旋转角度
this.text = '' // 水印文字
this.textStyle = {} // 水印文字样式
this.watermarkDraw = this.mindMap.svg
.group()
.css({ 'pointer-events': 'none', 'user-select': 'none' })
this.maxLong = Math.sqrt(
Math.pow(this.mindMap.width, 2) + Math.pow(this.mindMap.height, 2)
)
this.updateWatermark(this.mindMap.opt.watermarkConfig || {})
}
// 处理水印配置
handleConfig({ text, lineSpacing, textSpacing, angle, textStyle }) {
this.text = text === undefined ? '' : String(text).trim()
this.lineSpacing =
typeof lineSpacing === 'number' && lineSpacing > 0 ? lineSpacing : 100
this.textSpacing =
typeof textSpacing === 'number' && textSpacing > 0 ? textSpacing : 100
this.angle =
typeof angle === 'number' && angle >= 0 && angle <= 90 ? angle : 30
this.textStyle = Object.assign(this.textStyle, textStyle || {})
}
// 绘制水印
// 非精确绘制,会绘制一些超出可视区域的水印
draw() {
this.watermarkDraw.clear()
if (!this.text.trim()) {
return
}
let x = 0
while (x < this.mindMap.width) {
this.drawText(x)
x += this.lineSpacing / Math.sin(degToRad(this.angle))
}
let yOffset =
this.lineSpacing / Math.cos(degToRad(this.angle)) || this.lineSpacing
let y = yOffset
while (y < this.mindMap.height) {
this.drawText(0, y)
y += yOffset
}
}
// 绘制文字
drawText(x, y) {
let long = Math.min(
this.maxLong,
(this.mindMap.width - x) / Math.cos(degToRad(this.angle))
)
let g = new G()
let bbox = null
let bboxWidth = 0
let textHeight = -1
while (bboxWidth < long) {
let text = new Text().text(this.text)
g.add(text)
text.transform({
translateX: bboxWidth
})
this.setTextStyle(text)
bbox = g.bbox()
if (textHeight === -1) {
textHeight = bbox.height
}
bboxWidth = bbox.width + this.textSpacing
}
let params = {
rotate: this.angle,
origin: 'top left',
translateX: x,
translateY: textHeight
}
if (y !== undefined) {
params.translateY = y + textHeight
}
g.transform(params)
this.watermarkDraw.add(g)
}
// 给文字设置样式
setTextStyle(text) {
Object.keys(this.textStyle).forEach(item => {
let value = this.textStyle[item]
if (item === 'color') {
text.fill(value)
} else {
text.css(camelCaseToHyphen(item), value)
}
})
}
// 更新水印
updateWatermark(config) {
this.mindMap.opt.watermarkConfig = merge(this.mindMap.opt.watermarkConfig, config)
this.handleConfig(config)
this.draw()
}
}

View File

@@ -27,22 +27,18 @@ export const layoutList = [
{
name: '逻辑结构图',
value: 'logicalStructure',
img: require('../assets/logicalStructure.jpg')
},
{
name: '思维导图',
value: 'mindMap',
img: require('../assets/mindMap.jpg')
},
{
name: '组织结构图',
value: 'organizationStructure',
img: require('../assets/organizationStructure.jpg')
},
{
name: '目录组织图',
value: 'catalogOrganization',
img: require('../assets/catalogOrganization.jpg')
}
]
export const layoutValueList = [
@@ -57,111 +53,89 @@ export const themeList = [
{
name: '默认',
value: 'default',
img: require('../assets/default.jpg')
},
{
name: '脑图经典',
value: 'classic',
img: require('../assets/classic.jpg')
},
{
name: '小黄人',
value: 'minions',
img: require('../assets/minions.jpg')
},
{
name: '粉红葡萄',
value: 'pinkGrape',
img: require('../assets/pinkGrape.jpg')
},
{
name: '薄荷',
value: 'mint',
img: require('../assets/mint.jpg')
},
{
name: '金色vip',
value: 'gold',
img: require('../assets/gold.jpg')
},
{
name: '活力橙',
value: 'vitalityOrange',
img: require('../assets/vitalityOrange.jpg')
},
{
name: '绿叶',
value: 'greenLeaf',
img: require('../assets/greenLeaf.jpg')
},
{
name: '暗色2',
value: 'dark2',
img: require('../assets/dark2.jpg')
},
{
name: '天清绿',
value: 'skyGreen',
img: require('../assets/skyGreen.jpg')
},
{
name: '脑图经典2',
value: 'classic2',
img: require('../assets/classic2.jpg')
},
{
name: '脑图经典3',
value: 'classic3',
img: require('../assets/classic3.jpg')
},
{
name: '脑图经典4',
value: 'classic4',
img: require('../assets/classic4.jpg')
},
{
name: '经典绿',
value: 'classicGreen',
img: require('../assets/classicGreen.jpg')
},
{
name: '经典蓝',
value: 'classicBlue',
img: require('../assets/classicBlue.jpg')
},
{
name: '天空蓝',
value: 'blueSky',
img: require('../assets/blueSky.jpg')
},
{
name: '脑残粉',
value: 'brainImpairedPink',
img: require('../assets/brainImpairedPink.jpg')
},
{
name: '暗色',
value: 'dark',
img: require('../assets/dark.jpg')
},
{
name: '泥土黄',
value: 'earthYellow',
img: require('../assets/earthYellow.jpg')
},
{
name: '清新绿',
value: 'freshGreen',
img: require('../assets/freshGreen.jpg')
},
{
name: '清新红',
value: 'freshRed',
img: require('../assets/freshRed.jpg')
},
{
name: '浪漫紫',
value: 'romanticPurple',
img: require('../assets/romanticPurple.jpg')
}
]

View File

@@ -224,3 +224,15 @@ export const asyncRun = (taskList, callback = () => {}) => {
}
loop()
}
// 角度转弧度
export const degToRad = deg => {
return deg * (Math.PI / 180)
}
// 驼峰转连字符
export const camelCaseToHyphen = (str) => {
return str.replace(/([a-z])([A-Z])/g, (...args) => {
return args[1] + '-' + args[2].toLowerCase()
})
}

140
web/package-lock.json generated
View File

@@ -11,6 +11,7 @@
"@toast-ui/editor": "^3.1.5",
"core-js": "^3.6.5",
"element-ui": "^2.15.1",
"highlight.js": "^10.7.3",
"v-viewer": "^1.6.4",
"vue": "^2.6.11",
"vue-i18n": "^8.27.2",
@@ -27,6 +28,8 @@
"eslint-plugin-vue": "^6.2.2",
"less": "^3.12.2",
"less-loader": "^7.1.0",
"markdown-it": "^13.0.1",
"markdown-it-checkbox": "^1.1.0",
"prettier": "^1.19.1",
"vue-template-compiler": "^2.6.11",
"webpack": "^4.44.2"
@@ -7846,7 +7849,6 @@
"version": "10.7.3",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
"dev": true,
"engines": {
"node": "*"
}
@@ -9334,6 +9336,15 @@
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true
},
"node_modules/linkify-it": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz",
"integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==",
"dev": true,
"dependencies": {
"uc.micro": "^1.0.1"
}
},
"node_modules/loader-fs-cache": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz",
@@ -9554,6 +9565,49 @@
"node": ">=0.10.0"
}
},
"node_modules/markdown-it": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz",
"integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==",
"dev": true,
"dependencies": {
"argparse": "^2.0.1",
"entities": "~3.0.1",
"linkify-it": "^4.0.1",
"mdurl": "^1.0.1",
"uc.micro": "^1.0.5"
},
"bin": {
"markdown-it": "bin/markdown-it.js"
}
},
"node_modules/markdown-it-checkbox": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/markdown-it-checkbox/-/markdown-it-checkbox-1.1.0.tgz",
"integrity": "sha512-NkZVjnXo5G+cLNdi7DPZxICypBuxFE9F8sx3YGMZn+Cfizr8EZ/1TFUKl7ZnefF6cr1aFHbnQ5iA3rc4cp7EyA==",
"dev": true,
"dependencies": {
"underscore": "^1.8.2"
}
},
"node_modules/markdown-it/node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
"node_modules/markdown-it/node_modules/entities": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
"dev": true,
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/md5.js": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
@@ -9571,6 +9625,12 @@
"integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==",
"dev": true
},
"node_modules/mdurl": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
"integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==",
"dev": true
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -14266,6 +14326,12 @@
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
"dev": true
},
"node_modules/uc.micro": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
"dev": true
},
"node_modules/uglify-js": {
"version": "3.4.10",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz",
@@ -14303,6 +14369,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/underscore": {
"version": "1.13.6",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz",
"integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==",
"dev": true
},
"node_modules/unicode-canonical-property-names-ecmascript": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
@@ -22289,8 +22361,7 @@
"highlight.js": {
"version": "10.7.3",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
"dev": true
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="
},
"hmac-drbg": {
"version": "1.0.1",
@@ -23414,6 +23485,15 @@
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true
},
"linkify-it": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz",
"integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==",
"dev": true,
"requires": {
"uc.micro": "^1.0.1"
}
},
"loader-fs-cache": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz",
@@ -23593,6 +23673,42 @@
"object-visit": "^1.0.0"
}
},
"markdown-it": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz",
"integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==",
"dev": true,
"requires": {
"argparse": "^2.0.1",
"entities": "~3.0.1",
"linkify-it": "^4.0.1",
"mdurl": "^1.0.1",
"uc.micro": "^1.0.5"
},
"dependencies": {
"argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true
},
"entities": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
"dev": true
}
}
},
"markdown-it-checkbox": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/markdown-it-checkbox/-/markdown-it-checkbox-1.1.0.tgz",
"integrity": "sha512-NkZVjnXo5G+cLNdi7DPZxICypBuxFE9F8sx3YGMZn+Cfizr8EZ/1TFUKl7ZnefF6cr1aFHbnQ5iA3rc4cp7EyA==",
"dev": true,
"requires": {
"underscore": "^1.8.2"
}
},
"md5.js": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
@@ -23610,6 +23726,12 @@
"integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==",
"dev": true
},
"mdurl": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
"integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==",
"dev": true
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -27502,6 +27624,12 @@
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
"dev": true
},
"uc.micro": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
"dev": true
},
"uglify-js": {
"version": "3.4.10",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz",
@@ -27532,6 +27660,12 @@
"which-boxed-primitive": "^1.0.2"
}
},
"underscore": {
"version": "1.13.6",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz",
"integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==",
"dev": true
},
"unicode-canonical-property-names-ecmascript": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",

View File

@@ -7,12 +7,14 @@
"build": "vue-cli-service build && node ../copy.js",
"lint": "vue-cli-service lint",
"buildLibrary": "vue-cli-service build --target lib --name simpleMindMap ../simple-mind-map/index.js --dest ../simple-mind-map/dist",
"format": "prettier --write src/* src/*/* src/*/*/* src/*/*/*/*"
"format": "prettier --write src/* src/*/* src/*/*/* src/*/*/*/*",
"buildDoc": "node ./scripts/buildDoc.js"
},
"dependencies": {
"@toast-ui/editor": "^3.1.5",
"core-js": "^3.6.5",
"element-ui": "^2.15.1",
"highlight.js": "^10.7.3",
"v-viewer": "^1.6.4",
"vue": "^2.6.11",
"vue-i18n": "^8.27.2",
@@ -29,6 +31,8 @@
"eslint-plugin-vue": "^6.2.2",
"less": "^3.12.2",
"less-loader": "^7.1.0",
"markdown-it": "^13.0.1",
"markdown-it-checkbox": "^1.1.0",
"prettier": "^1.19.1",
"vue-template-compiler": "^2.6.11",
"webpack": "^4.44.2"

View File

@@ -4,6 +4,7 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="./dist/logo.png">
<title>一个简单的web思维导图实现</title>
</head>
<body>

BIN
web/public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

105
web/scripts/buildDoc.js Normal file
View File

@@ -0,0 +1,105 @@
// 编译文档
const path = require('path')
const fs = require('fs')
const hljs = require('highlight.js')
const md = require('markdown-it')({
highlight: function(str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return (
'<pre class="hljs"><code>' +
hljs.highlight(lang, str, true).value +
'</code></pre>'
)
} catch (__) {}
}
return (
'<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>'
)
}
}).use(require('markdown-it-checkbox'))
// 文档语言种类
let langList = ['zh', 'en']
// 开始转换
const transform = (dir, routerList) => {
let dirs = fs.readdirSync(dir)
dirs.forEach(item => {
let cur = path.join(dir, item)
if (fs.statSync(cur).isDirectory()) {
compilerDir(cur, item, routerList)
}
})
}
// 编译某种语言下的文档
const compilerDir = (dir, dirName, routerList) => {
let files = fs.readdirSync(dir)
files.forEach(file => {
if (file.endsWith('.md')) {
compilerFile(dir, file, dirName, routerList)
}
})
}
// 编译具体的文档
const compilerFile = (dir, file, dirName, routerList) => {
let filePath = path.join(dir, file)
let destPath = path.join(dir, './index.vue')
let templatePath = path.join(__dirname, '../src/pages/Doc/Template.vue')
let content = fs.readFileSync(filePath, 'utf-8')
let title = /(^|\n\r)\s*#\s+([^\n\r]+)/g.exec(content)
if (title && title[2]) {
addRouter(dirName, routerList, title[2])
}
let result = md.render(content)
let template = fs.readFileSync(templatePath, 'utf-8')
let doc = template.replace('$$$$', result)
fs.writeFileSync(destPath, doc)
}
// 收集文档路由
const addRouter = (item, routerList, title) => {
routerList.push({
path: item,
title
})
}
// 创建路由
const createRouter = () => {
let content = `
export default ${JSON.stringify(
routerTypeList.map(item => {
return {
lang: item.lang,
children: item.routerList
}
})
)}
`
fs.writeFileSync(
path.join(__dirname, '../src/pages/Doc/routerList.js'),
content
)
}
// 创建目录列表
const createCatalogList = () => {}
// 开始编译
let routerTypeList = []
langList.forEach(lang => {
let dir = path.join(__dirname, '../src/pages/Doc/', `./${lang}/`)
let routerList = []
transform(dir, routerList)
routerTypeList.push({
lang,
routerList
})
})
// 创建路由
createRouter()
console.log('编译完成')

View File

@@ -54,6 +54,12 @@
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xe64f;</span>
<div class="name">github</div>
<div class="code-name">&amp;#xe64f;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe6c5;</span>
<div class="name">选择</div>
@@ -342,9 +348,9 @@
<pre><code class="language-css"
>@font-face {
font-family: 'iconfont';
src: url('iconfont.woff2?t=1668512547595') format('woff2'),
url('iconfont.woff?t=1668512547595') format('woff'),
url('iconfont.ttf?t=1668512547595') format('truetype');
src: url('iconfont.woff2?t=1673600274529') format('woff2'),
url('iconfont.woff?t=1673600274529') format('woff'),
url('iconfont.ttf?t=1673600274529') format('truetype');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -370,6 +376,15 @@
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont icongithub"></span>
<div class="name">
github
</div>
<div class="code-name">.icongithub
</div>
</li>
<li class="dib">
<span class="icon iconfont iconchoose1"></span>
<div class="name">
@@ -802,6 +817,14 @@
<div class="content symbol">
<ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icongithub"></use>
</svg>
<div class="name">github</div>
<div class="code-name">#icongithub</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconchoose1"></use>

View File

@@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 2479351 */
src: url('iconfont.woff2?t=1668512547595') format('woff2'),
url('iconfont.woff?t=1668512547595') format('woff'),
url('iconfont.ttf?t=1668512547595') format('truetype');
src: url('iconfont.woff2?t=1673600274529') format('woff2'),
url('iconfont.woff?t=1673600274529') format('woff'),
url('iconfont.ttf?t=1673600274529') format('truetype');
}
.iconfont {
@@ -13,6 +13,10 @@
-moz-osx-font-smoothing: grayscale;
}
.icongithub:before {
content: "\e64f";
}
.iconchoose1:before {
content: "\e6c5";
}

File diff suppressed because one or more lines are too long

View File

@@ -5,6 +5,13 @@
"css_prefix_text": "icon",
"description": "思维导图",
"glyphs": [
{
"icon_id": "8760187",
"name": "github",
"font_class": "github",
"unicode": "e64f",
"unicode_decimal": 58959
},
{
"icon_id": "1009019",
"name": "选择",

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

BIN
web/src/assets/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@@ -0,0 +1,41 @@
// 布局结构图片映射
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')
}
// 主题图片映射
export const themeMap = {
default: require('../assets/img/default.jpg'),
classic: require('../assets/img/classic.jpg'),
minions: require('../assets/img/minions.jpg'),
pinkGrape: require('../assets/img/pinkGrape.jpg'),
mint: require('../assets/img/mint.jpg'),
gold: require('../assets/img/gold.jpg'),
vitalityOrange: require('../assets/img/vitalityOrange.jpg'),
greenLeaf: require('../assets/img/greenLeaf.jpg'),
dark2: require('../assets/img/dark2.jpg'),
skyGreen: require('../assets/img/skyGreen.jpg'),
classic2: require('../assets/img/classic2.jpg'),
classic3: require('../assets/img/classic3.jpg'),
classic4: require('../assets/img/classic4.jpg'),
classicGreen: require('../assets/img/classicGreen.jpg'),
classicBlue: require('../assets/img/classicBlue.jpg'),
blueSky: require('../assets/img/blueSky.jpg'),
brainImpairedPink: require('../assets/img/brainImpairedPink.jpg'),
dark: require('../assets/img/dark.jpg'),
earthYellow: require('../assets/img/earthYellow.jpg'),
freshGreen: require('../assets/img/freshGreen.jpg'),
freshRed: require('../assets/img/freshRed.jpg'),
romanticPurple: require('../assets/img/romanticPurple.jpg'),
simpleBlack: require('../assets/img/simpleBlack.jpg'),
courseGreen: require('../assets/img/courseGreen.jpg'),
coffee: require('../assets/img/coffee.jpg'),
redSpirit: require('../assets/img/redSpirit.jpg'),
blackHumour: require('../assets/img/blackHumour.jpg'),
lateNightOffice: require('../assets/img/lateNightOffice.jpg'),
blackGold: require('../assets/img/blackGold.jpg'),
}

View File

@@ -0,0 +1,55 @@
// 黑金
export default {
// 背景颜色
backgroundColor: 'rgb(18, 20, 20)',
// 连线的颜色
lineColor: 'rgb(205, 186, 156)',
lineWidth: 3,
// 概要连线的粗细
generalizationLineWidth: 3,
// 概要连线的颜色
generalizationLineColor: 'rgb(245, 224, 191)',
// 根节点样式
root: {
fillColor: 'rgb(255, 208, 124)',
color: 'rgb(111, 61, 6)',
borderColor: '',
borderWidth: 0,
fontSize: 24,
active: {
borderColor: '#fff',
borderWidth: 3,
}
},
// 二级节点样式
second: {
fillColor: 'rgb(66, 57, 46)',
color: 'rgb(225, 201, 158)',
borderColor: 'rgb(245, 224, 191)',
borderWidth: 2,
fontSize: 18,
active: {
borderColor: 'rgb(255, 208, 124)',
}
},
// 三级及以下节点样式
node: {
fontSize: 14,
color: 'rgb(231, 203, 155)',
active: {
borderColor: 'rgb(255, 208, 124)'
}
},
// 概要节点样式
generalization: {
fontSize: 14,
fillColor: 'rgb(56, 45, 34)',
borderColor: 'rgb(104, 84, 61)',
borderWidth: 2,
color: 'rgb(242, 216, 176)',
active: {
borderColor: 'rgb(255, 208, 124)'
}
}
}

View File

@@ -0,0 +1,56 @@
// 黑色幽默
export default {
// 背景颜色
backgroundColor: 'rgb(27, 31, 34)',
// 连线的颜色
lineColor: 'rgb(75, 81, 78)',
lineWidth: 3,
// 概要连线的粗细
generalizationLineWidth: 3,
// 概要连线的颜色
generalizationLineColor: 'rgb(255, 119, 34)',
// 根节点样式
root: {
fillColor: 'rgb(36, 179, 96)',
color: '#fff',
borderColor: '',
borderWidth: 0,
fontSize: 24,
active: {
borderColor: 'rgb(254, 199, 13)',
borderWidth: 3,
}
},
// 二级节点样式
second: {
fillColor: 'rgb(254, 199, 13)',
color: 'rgb(0, 0, 0)',
borderColor: '',
borderWidth: 0,
fontSize: 18,
active: {
borderColor: 'rgb(36, 179, 96)',
borderWidth: 3,
}
},
// 三级及以下节点样式
node: {
fontSize: 14,
color: 'rgb(204, 204, 204)',
active: {
borderColor: 'rgb(254, 199, 13)'
}
},
// 概要节点样式
generalization: {
fontSize: 14,
fillColor: 'rgb(27, 31, 34)',
borderColor: 'rgb(255, 119, 34)',
borderWidth: 2,
color: 'rgb(204, 204, 204)',
active: {
borderColor: 'rgb(36, 179, 96)'
}
}
}

View File

@@ -0,0 +1,53 @@
// 咖啡
export default {
// 连线的颜色
lineColor: 'rgb(173, 123, 91)',
lineWidth: 4,
// 概要连线的粗细
generalizationLineWidth: 4,
// 概要连线的颜色
generalizationLineColor: 'rgb(173, 123, 91)',
// 根节点样式
root: {
fillColor: 'rgb(202, 117, 79)',
color: '#fff',
borderColor: '',
borderWidth: 0,
fontSize: 24,
active: {
borderColor: 'rgb(173, 123, 91)',
borderWidth: 3,
}
},
// 二级节点样式
second: {
fillColor: 'rgb(245, 231, 216)',
color: 'rgb(125, 86, 42)',
borderColor: '',
borderWidth: 0,
fontSize: 18,
active: {
borderColor: 'rgb(173, 123, 91)',
}
},
// 三级及以下节点样式
node: {
fontSize: 14,
color: 'rgb(96, 71, 47)',
active: {
borderColor: 'rgb(173, 123, 91)'
}
},
// 概要节点样式
generalization: {
fontSize: 14,
fillColor: 'rgb(255, 249, 239)',
borderColor: 'rgb(173, 123, 91)',
borderWidth: 2,
color: 'rgb(122, 83, 44)',
active: {
borderColor: 'rgb(202, 117, 79)'
}
}
}

View File

@@ -0,0 +1,53 @@
// 课程绿
export default {
// 连线的颜色
lineColor: 'rgb(113, 195, 169)',
lineWidth: 3,
// 概要连线的粗细
generalizationLineWidth: 3,
// 概要连线的颜色
generalizationLineColor: 'rgb(113, 195, 169)',
// 根节点样式
root: {
fillColor: 'rgb(16, 160, 121)',
color: '#fff',
borderColor: '',
borderWidth: 0,
fontSize: 24,
active: {
borderColor: 'rgb(173, 91, 12)',
borderWidth: 3,
}
},
// 二级节点样式
second: {
fillColor: 'rgb(240, 252, 249)',
color: 'rgb(50, 113, 96)',
borderColor: 'rgb(113, 195, 169)',
borderWidth: 2,
fontSize: 18,
active: {
borderColor: 'rgb(173, 91, 12)',
}
},
// 三级及以下节点样式
node: {
fontSize: 14,
color: 'rgb(10, 59, 43)',
active: {
borderColor: 'rgb(173, 91, 12)'
}
},
// 概要节点样式
generalization: {
fontSize: 14,
fillColor: 'rgb(246, 238, 211)',
borderColor: '',
borderWidth: 0,
color: 'rgb(173, 91, 12)',
active: {
borderColor: 'rgb(113, 195, 169)'
}
}
}

View File

@@ -0,0 +1,45 @@
import simpleBlack from './simpleBlack'
import courseGreen from './courseGreen'
import coffee from './coffee'
import redSpirit from './redSpirit'
import blackHumour from './blackHumour'
import lateNightOffice from './lateNightOffice'
import blackGold from './blackGold'
export default [
{
name: '简约黑',
value: 'simpleBlack',
theme: simpleBlack
},
{
name: '课程绿',
value: 'courseGreen',
theme: courseGreen
},
{
name: '咖啡',
value: 'coffee',
theme: coffee
},
{
name: '红色精神',
value: 'redSpirit',
theme: redSpirit
},
{
name: '黑色幽默',
value: 'blackHumour',
theme: blackHumour
},
{
name: '深夜办公室',
value: 'lateNightOffice',
theme: lateNightOffice
},
{
name: '黑金',
value: 'blackGold',
theme: blackGold
}
]

View File

@@ -0,0 +1,56 @@
// 深夜办公室
export default {
// 背景颜色
backgroundColor: 'rgb(32, 37, 49)',
// 连线的颜色
lineColor: 'rgb(137, 167, 196)',
lineWidth: 3,
// 概要连线的粗细
generalizationLineWidth: 3,
// 概要连线的颜色
generalizationLineColor: 'rgb(255, 119, 34)',
// 根节点样式
root: {
fillColor: 'rgb(23, 153, 243)',
color: 'rgb(255, 255, 255)',
borderColor: '',
borderWidth: 0,
fontSize: 24,
active: {
borderColor: 'rgb(255, 119, 34)',
borderWidth: 3,
}
},
// 二级节点样式
second: {
fillColor: 'rgb(70, 78, 94)',
color: 'rgb(209, 210, 210)',
borderColor: '',
borderWidth: 0,
fontSize: 18,
active: {
borderColor: 'rgb(255, 119, 34)',
borderWidth: 3,
}
},
// 三级及以下节点样式
node: {
fontSize: 14,
color: 'rgb(204, 204, 204)',
active: {
borderColor: 'rgb(255, 119, 34)'
}
},
// 概要节点样式
generalization: {
fontSize: 14,
fillColor: 'rgb(255, 119, 34)',
borderColor: '',
borderWidth: 2,
color: '#fff',
active: {
borderColor: 'rgb(23, 153, 243)'
}
}
}

View File

@@ -0,0 +1,55 @@
// 红色精神
export default {
// 背景颜色
backgroundColor: 'rgb(255, 238, 228)',
// 连线的颜色
lineColor: 'rgb(230, 138, 131)',
lineWidth: 3,
// 概要连线的粗细
generalizationLineWidth: 3,
// 概要连线的颜色
generalizationLineColor: 'rgb(222, 101, 85)',
// 根节点样式
root: {
fillColor: 'rgb(207, 44, 44)',
color: 'rgb(255, 233, 157)',
borderColor: '',
borderWidth: 0,
fontSize: 24,
active: {
borderColor: 'rgb(255, 233, 157)',
borderWidth: 3,
}
},
// 二级节点样式
second: {
fillColor: 'rgb(255, 255, 255)',
color: 'rgb(211, 58, 21)',
borderColor: 'rgb(222, 101, 85)',
borderWidth: 2,
fontSize: 18,
active: {
borderColor: 'rgb(255, 233, 157)',
}
},
// 三级及以下节点样式
node: {
fontSize: 14,
color: 'rgb(144, 71, 43)',
active: {
borderColor: 'rgb(255, 233, 157)'
}
},
// 概要节点样式
generalization: {
fontSize: 14,
fillColor: 'rgb(255, 247, 211)',
borderColor: 'rgb(255, 202, 162)',
borderWidth: 2,
color: 'rgb(187, 101, 69)',
active: {
borderColor: 'rgb(222, 101, 85)'
}
}
}

View File

@@ -0,0 +1,51 @@
// 简约黑
export default {
// 连线的颜色
lineColor: 'rgb(34, 34, 34)',
lineWidth: 4,
// 概要连线的粗细
generalizationLineWidth: 4,
// 概要连线的颜色
generalizationLineColor: 'rgb(34, 34, 34)',
// 根节点样式
root: {
fillColor: '#fff',
color: 'rgb(34, 34, 34)',
borderColor: 'rgb(34, 34, 34)',
borderWidth: 3,
fontSize: 24,
active: {
borderColor: '#a13600',
}
},
// 二级节点样式
second: {
fillColor: 'rgb(241, 246, 248)',
color: 'rgb(34, 34, 34)',
borderColor: 'rgb(34, 34, 34)',
borderWidth: 3,
fontSize: 18,
active: {
borderColor: '#a13600',
}
},
// 三级及以下节点样式
node: {
fontSize: 14,
color: 'rgb(34, 34, 34)',
active: {
borderColor: '#a13600'
}
},
// 概要节点样式
generalization: {
fontSize: 14,
fillColor: 'transparent',
borderColor: 'rgb(34, 34, 34)',
borderWidth: 2,
color: 'rgb(34, 34, 34)',
active: {
borderColor: '#a13600'
}
}
}

View File

@@ -20,7 +20,19 @@ export default {
level2Node: 'Level2 node',
belowLevel2Node: 'Below level2 node',
nodeBorderType: 'Node border style',
nodeUseLineStyle: 'Use only has bottom border style'
nodeUseLineStyle: 'Use only has bottom border style',
otherConfig: 'Other config',
enableFreeDrag: 'Enable node free drag',
watermark: 'Watermark',
showWatermark: 'Is show watermark',
watermarkDefaultText: 'Watermark text',
watermarkText: 'Watermark text',
watermarkTextColor: 'Text color',
watermarkLineSpacing: 'Line spacing',
watermarkTextSpacing: 'Text spacing',
watermarkAngle: 'Angle',
watermarkTextOpacity: 'Text opacity',
watermarkTextFontSize: 'Font size'
},
color: {
moreColor: 'More color'
@@ -68,7 +80,8 @@ export default {
tips: 'tips.smm and .json file can be import'
},
fullscreen: {
fullscreen: 'Fullscreen'
fullscreenShow: 'Full screen show',
fullscreenEdit: 'Full screen edit'
},
import: {
title: 'Import',

View File

@@ -20,7 +20,19 @@ export default {
level2Node: '二级节点',
belowLevel2Node: '三级及以下节点',
nodeBorderType: '节点边框风格',
nodeUseLineStyle: '是否使用只有底边框的风格'
nodeUseLineStyle: '是否使用只有底边框的风格',
otherConfig: '其他配置',
enableFreeDrag: '是否开启节点自由拖拽',
watermark: '水印',
showWatermark: '是否显示水印',
watermarkDefaultText: '水印文字',
watermarkText: '水印文字',
watermarkTextColor: '文字颜色',
watermarkLineSpacing: '水印行间距',
watermarkTextSpacing: '水印文字间距',
watermarkAngle: '旋转角度',
watermarkTextOpacity: '文字透明度',
watermarkTextFontSize: '文字字号'
},
color: {
moreColor: '更多颜色'
@@ -68,7 +80,8 @@ export default {
tips: 'tips.smm和.json文件可用于导入'
},
fullscreen: {
fullscreen: '全屏'
fullscreenShow: '全屏查看',
fullscreenEdit: '全屏编辑'
},
import: {
title: '导入',

191
web/src/pages/Doc/Index.vue Normal file
View File

@@ -0,0 +1,191 @@
<template>
<div class="docContainer">
<Header></Header>
<div class="content">
<Sidebar></Sidebar>
<div class="doc" ref="doc" id="doc" @scroll="onScroll">
<router-view></router-view>
</div>
<CatalogBar :scrollTop="scrollTop" @scroll="doScroll"></CatalogBar>
</div>
</div>
</template>
<script>
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'
export default {
components: {
Header,
Sidebar,
CatalogBar
},
data () {
return {
scrollTop: 0,
}
},
methods: {
doScroll(top) {
this.$nextTick(() => {
try {
this.$refs.doc.scrollTop = top
} catch (error) {
console.log(error)
}
})
},
onScroll() {
this.scrollTop = this.$refs.doc.scrollTop
}
}
}
</script>
<style lang="less">
.docContainer {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
font-family: Quotes, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
.content {
display: flex;
flex-grow: 1;
overflow: hidden;
.doc {
overflow: auto;
flex-grow: 1;
font-weight: 400;
color: #213547;
font-size: 16px;
-webkit-font-smoothing: antialiased;
line-height: 1.7;
padding: 30px;
h1 {
margin: 30px 0;
font-size: 38px;
line-height: 1.4;
}
h2 {
margin: 20px 0;
border-top: 1px solid rgba(60, 60, 60, 0.12);
font-size: 24px;
padding-top: 10px;
}
h3 {
font-size: 19px;
margin: 10px 0;
}
p {
margin-bottom: 20px;
}
a {
font-weight: 500;
text-decoration: none;
color: #42b883;
transition: color 0.25s;
&:hover {
color: #33a06f;
}
}
pre {
margin-bottom: 20px;
border-radius: 5px;
font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
code {
font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
}
}
:not(pre) > code {
background-color: #f1f1f1;
padding: 0.15em 0.5em;
border-radius: 4px;
color: #476582;
transition: color 0.5s, background-color 0.5s;
font-family: Quotes, -apple-system, BlinkMacSystemFont, 'Segoe UI',
Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans',
'Helvetica Neue', sans-serif;
}
input[type='checkbox'] {
margin-right: 5px;
}
ul {
list-style: none;
padding-left: 1.25rem;
> li {
position: relative;
margin: 1px 0;
&:before {
content: '';
position: absolute;
width: 5px;
height: 5px;
border-radius: 50%;
background-color: rgba(60, 60, 60, 0.33);
transition: background-color 0.5s;
left: -1.25rem;
top: 0.75rem;
}
}
}
table {
border-collapse: collapse;
border-spacing: 0;
margin-top: 0.8rem;
margin-bottom: 1.4rem;
}
tr {
background-color: #fff;
border-top: 1px solid #ccc;
}
th,
td {
padding: 5px 14px;
border: 1px solid #ddd;
}
blockquote {
margin: 1rem 0;
border-left: 0.2rem solid rgba(60, 60, 60, 0.29);
padding-left: 1rem;
transition: border-color 0.5s;
> p {
margin: 0;
font-size: 16px;
color: rgba(60, 60, 60, 0.7);
transition: color 0.5s;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,15 @@
<template>
<div>
$$$$
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>

View File

@@ -0,0 +1,76 @@
import routerList from './routerList'
let langList = [
{
name: '中文',
path: 'zh'
},
{
name: 'English',
path: 'en'
}
]
let StartList = ['introduction', 'start', 'translate', 'changelog']
let APIList = [
'constructor',
'node',
'render',
'view',
'miniMap',
'doExport',
'watermark',
'keyCommand',
'keyboardNavigation',
'command',
'select',
'batchExecution',
'utils'
]
const createList = (lang, list) => {
let langRouter = routerList.find(item => {
return item.lang === lang
})
let children = langRouter.children
return list
.filter(item => {
return children.find(child => {
return child.path === item
})
})
.map(item => {
return {
path: item,
name: children.find(child => {
return child.path === item
}).title
}
})
}
export default {
zh: [
{
groupName: '开始',
list: createList('zh', StartList)
},
{
groupName: 'API',
list: createList('zh', APIList)
}
],
en: [
{
groupName: 'Start',
list: createList('en', StartList)
},
{
groupName: 'API',
list: createList('en', APIList)
}
]
}
export {
langList
}

View File

@@ -0,0 +1,236 @@
<template>
<div class="catalogBarContainer">
<div class="catalogBarTitle">{{ pageCatalogTitle }}</div>
<div class="catalogList">
<div
class="catalogItem"
v-for="(item, index) in list"
:key="item.title + index"
:class="{ active: item.title === activeCatalog }"
@click="scrollTo(item, index)"
>
{{ item.title }}
</div>
<div
v-if="activeCatalogIndex !== -1"
class="activeBar"
:style="{ top: 4 + activeCatalogIndex * 28 + 'px' }"
></div>
</div>
</div>
</template>
<script>
import t from '../i18n'
export default {
props: {
scrollTop: {
type: Number
}
},
data() {
return {
lang: '',
list: [],
activeCatalog: '',
activeCatalogIndex: -1,
appointCatalog: true
}
},
computed: {
pageCatalogTitle() {
return t('pageCatalog', this.lang)
}
},
watch: {
$route(newVal, oldVal) {
this.initLang()
this.initCatalogList(newVal.path, oldVal.path)
},
scrollTop() {
this.onScroll()
},
lang(newVal, oldVal) {
console.log(newVal, oldVal)
if (!oldVal) {
return
}
this.initCatalogList()
}
},
mounted() {
this.initLang()
this.initCatalogList()
this.scrollToCatalog()
},
methods: {
// 获取当前语言
initLang() {
let lang = /^\/doc\/([^\/]+)\//.exec(this.$route.path)
if (lang && lang[1]) {
this.lang = lang[1]
}
},
// 初始化二级标题目录
initCatalogList(newPath, oldPath) {
let newPathRes = /^\/doc\/[^\/]+\/([^\/]+)/.exec(newPath)
let oldPathRes = /^\/doc\/[^\/]+\/([^\/]+)/.exec(oldPath)
// 语言变了、章节变了,需要重新获取二级标题目录
if ((!newPath && !oldPath) || newPathRes[1] !== oldPathRes[1]) {
this.$emit('scroll', 0)
this.resetActive()
let container = document.getElementById('doc')
let els = document.querySelectorAll('#doc h2')
this.list = Array.from(els).map(item => {
return {
title: item.textContent,
top: item.offsetTop - container.offsetTop
}
})
}
},
// 如果url中存在二级标题那么滚动到该标题所在位置
scrollToCatalog() {
let url = /^\/doc\/[^\/]+\/[^\/]+\/([^\/]+)($|\/)/.exec(this.$route.path)
if (url && url[1]) {
let h = decodeURIComponent(url[1])
let item = this.list.find(item => {
return item.title === h
})
let index = this.list.findIndex(item => {
return item.title === h
})
if (item) {
this.activeCatalog = item.title
this.activeCatalogIndex = index
this.$emit('scroll', item.top)
}
}
},
// 手动点击切换到指定二级标题
scrollTo(item, index) {
this.appointCatalog = true
this.routeToNewCatalog(item.title)
this.$nextTick(() => {
this.activeCatalog = item.title
this.activeCatalogIndex = index
this.scrollToCatalog()
})
},
// 路由到指定二级标题
routeToNewCatalog(title) {
let path = this.$route.path
let url = ''
if (!title) {
url = path.replace(/^(\/doc\/[^\/]+\/[^\/]+)($|\/|.*)$/, '$1')
} else if (/^\/doc\/[^\/]+\/[^\/]+($|\/)$/.test(path)) {
url = path.replace(
/^(\/doc\/[^\/]+\/[^\/]+)($|\/)$/,
'$1/' + encodeURIComponent(title)
)
} else {
url = path.replace(
/^(\/doc\/[^\/]+\/[^\/]+\/)([^\/]+)($|\/)/,
(...args) => {
return args[1] + encodeURIComponent(title)
}
)
}
if (path === url) {
return
}
this.$router.push(url)
},
// 文档滚动时判断当前滚动到哪个二级标题
onScroll() {
if (this.appointCatalog) {
this.appointCatalog = false
return
}
let find = false
for (let i = 0; i < this.list.length; i++) {
let cur = this.list[i]
let next = this.list[i + 1]
if (this.scrollTop >= cur.top && (!next || this.scrollTop < next.top)) {
find = true
if (cur.title === this.activeCatalog) {
break
}
this.activeCatalog = cur.title
this.activeCatalogIndex = i
this.routeToNewCatalog(cur.title)
break
}
}
if (!find) {
this.resetActive()
this.routeToNewCatalog('')
}
},
resetActive() {
this.activeCatalog = ''
this.activeCatalogIndex = -1
}
}
}
</script>
<style lang="less" scoped>
.catalogBarContainer {
width: 20%;
flex-shrink: 0;
overflow-x: hidden;
overflow-y: auto;
padding-top: 60px;
padding-bottom: 30px;
padding-left: 20px;
.catalogBarTitle {
font-weight: 700;
margin-bottom: 4px;
text-transform: uppercase;
font-size: 11px;
letter-spacing: 0.4px;
}
.catalogList {
position: relative;
.catalogItem {
color: rgba(60, 60, 60, 0.7);
transition: color 0.5s;
line-height: 28px;
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 13px;
font-weight: 500;
cursor: pointer;
&.active {
color: rgba(60, 60, 60, 1);
font-weight: bold;
}
}
.activeBar {
position: absolute;
left: -10px;
width: 4px;
height: 20px;
background-color: #42b883;
border-radius: 4px;
transition: top 0.25s cubic-bezier(0, 1, 0.5, 1), opacity 0.25s,
background-color 0.5s;
}
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More