commit e9b5b752c81f2b86e71b660e5d85df01b01fbf7c
Author: wanglin <1013335014@qq.com>
Date: Thu Jun 10 23:22:22 2021 +0800
初始化仓库
diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 00000000..35759397
Binary files /dev/null and b/.DS_Store differ
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..05223c9f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+node_modules
+oss.js
diff --git a/babel.config.js b/babel.config.js
new file mode 100644
index 00000000..e9558405
--- /dev/null
+++ b/babel.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+ presets: [
+ '@vue/cli-plugin-babel/preset'
+ ]
+}
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..01d4412c
--- /dev/null
+++ b/package.json
@@ -0,0 +1,50 @@
+{
+ "name": "thoughts",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "serve": "vue-cli-service serve",
+ "build": "vue-cli-service build",
+ "lint": "vue-cli-service lint"
+ },
+ "dependencies": {
+ "@svgdotjs/svg.js": "^3.0.16",
+ "core-js": "^3.6.5",
+ "deepmerge": "^1.5.2",
+ "element-ui": "^2.15.1",
+ "vue": "^2.6.11",
+ "vue-router": "^3.5.1",
+ "vuex": "^3.6.2"
+ },
+ "devDependencies": {
+ "@vue/cli-plugin-babel": "^4.5.0",
+ "@vue/cli-plugin-eslint": "^4.5.0",
+ "@vue/cli-service": "^4.5.0",
+ "babel-eslint": "^10.1.0",
+ "eslint": "^6.7.2",
+ "eslint-plugin-vue": "^6.2.2",
+ "less": "^3.12.2",
+ "less-loader": "^7.1.0",
+ "vue-template-compiler": "^2.6.11",
+ "webpack": "^4.44.2"
+ },
+ "eslintConfig": {
+ "root": true,
+ "env": {
+ "node": true
+ },
+ "extends": [
+ "plugin:vue/essential",
+ "eslint:recommended"
+ ],
+ "parserOptions": {
+ "parser": "babel-eslint"
+ },
+ "rules": {}
+ },
+ "browserslist": [
+ "> 1%",
+ "last 2 versions",
+ "not dead"
+ ]
+}
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 00000000..37d3bac8
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ 思绪_一个极简的在线思维导图
+
+
+
+
+
+
+
+
+
diff --git a/src/.DS_Store b/src/.DS_Store
new file mode 100644
index 00000000..81a5b725
Binary files /dev/null and b/src/.DS_Store differ
diff --git a/src/App.vue b/src/App.vue
new file mode 100644
index 00000000..530b42d1
--- /dev/null
+++ b/src/App.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/.DS_Store b/src/assets/.DS_Store
new file mode 100644
index 00000000..3c81c154
Binary files /dev/null and b/src/assets/.DS_Store differ
diff --git a/src/assets/icon-font/.DS_Store b/src/assets/icon-font/.DS_Store
new file mode 100644
index 00000000..f522a516
Binary files /dev/null and b/src/assets/icon-font/.DS_Store differ
diff --git a/src/assets/icon-font/demo.css b/src/assets/icon-font/demo.css
new file mode 100644
index 00000000..a67054a0
--- /dev/null
+++ b/src/assets/icon-font/demo.css
@@ -0,0 +1,539 @@
+/* Logo 字体 */
+@font-face {
+ font-family: "iconfont logo";
+ src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
+ src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
+ url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
+ url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
+ url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
+}
+
+.logo {
+ font-family: "iconfont logo";
+ font-size: 160px;
+ font-style: normal;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+/* tabs */
+.nav-tabs {
+ position: relative;
+}
+
+.nav-tabs .nav-more {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ height: 42px;
+ line-height: 42px;
+ color: #666;
+}
+
+#tabs {
+ border-bottom: 1px solid #eee;
+}
+
+#tabs li {
+ cursor: pointer;
+ width: 100px;
+ height: 40px;
+ line-height: 40px;
+ text-align: center;
+ font-size: 16px;
+ border-bottom: 2px solid transparent;
+ position: relative;
+ z-index: 1;
+ margin-bottom: -1px;
+ color: #666;
+}
+
+
+#tabs .active {
+ border-bottom-color: #f00;
+ color: #222;
+}
+
+.tab-container .content {
+ display: none;
+}
+
+/* 页面布局 */
+.main {
+ padding: 30px 100px;
+ width: 960px;
+ margin: 0 auto;
+}
+
+.main .logo {
+ color: #333;
+ text-align: left;
+ margin-bottom: 30px;
+ line-height: 1;
+ height: 110px;
+ margin-top: -50px;
+ overflow: hidden;
+ *zoom: 1;
+}
+
+.main .logo a {
+ font-size: 160px;
+ color: #333;
+}
+
+.helps {
+ margin-top: 40px;
+}
+
+.helps pre {
+ padding: 20px;
+ margin: 10px 0;
+ border: solid 1px #e7e1cd;
+ background-color: #fffdef;
+ overflow: auto;
+}
+
+.icon_lists {
+ width: 100% !important;
+ overflow: hidden;
+ *zoom: 1;
+}
+
+.icon_lists li {
+ width: 100px;
+ margin-bottom: 10px;
+ margin-right: 20px;
+ text-align: center;
+ list-style: none !important;
+ cursor: default;
+}
+
+.icon_lists li .code-name {
+ line-height: 1.2;
+}
+
+.icon_lists .icon {
+ display: block;
+ height: 100px;
+ line-height: 100px;
+ font-size: 42px;
+ margin: 10px auto;
+ color: #333;
+ -webkit-transition: font-size 0.25s linear, width 0.25s linear;
+ -moz-transition: font-size 0.25s linear, width 0.25s linear;
+ transition: font-size 0.25s linear, width 0.25s linear;
+}
+
+.icon_lists .icon:hover {
+ font-size: 100px;
+}
+
+.icon_lists .svg-icon {
+ /* 通过设置 font-size 来改变图标大小 */
+ width: 1em;
+ /* 图标和文字相邻时,垂直对齐 */
+ vertical-align: -0.15em;
+ /* 通过设置 color 来改变 SVG 的颜色/fill */
+ fill: currentColor;
+ /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
+ normalize.css 中也包含这行 */
+ overflow: hidden;
+}
+
+.icon_lists li .name,
+.icon_lists li .code-name {
+ color: #666;
+}
+
+/* markdown 样式 */
+.markdown {
+ color: #666;
+ font-size: 14px;
+ line-height: 1.8;
+}
+
+.highlight {
+ line-height: 1.5;
+}
+
+.markdown img {
+ vertical-align: middle;
+ max-width: 100%;
+}
+
+.markdown h1 {
+ color: #404040;
+ font-weight: 500;
+ line-height: 40px;
+ margin-bottom: 24px;
+}
+
+.markdown h2,
+.markdown h3,
+.markdown h4,
+.markdown h5,
+.markdown h6 {
+ color: #404040;
+ margin: 1.6em 0 0.6em 0;
+ font-weight: 500;
+ clear: both;
+}
+
+.markdown h1 {
+ font-size: 28px;
+}
+
+.markdown h2 {
+ font-size: 22px;
+}
+
+.markdown h3 {
+ font-size: 16px;
+}
+
+.markdown h4 {
+ font-size: 14px;
+}
+
+.markdown h5 {
+ font-size: 12px;
+}
+
+.markdown h6 {
+ font-size: 12px;
+}
+
+.markdown hr {
+ height: 1px;
+ border: 0;
+ background: #e9e9e9;
+ margin: 16px 0;
+ clear: both;
+}
+
+.markdown p {
+ margin: 1em 0;
+}
+
+.markdown>p,
+.markdown>blockquote,
+.markdown>.highlight,
+.markdown>ol,
+.markdown>ul {
+ width: 80%;
+}
+
+.markdown ul>li {
+ list-style: circle;
+}
+
+.markdown>ul li,
+.markdown blockquote ul>li {
+ margin-left: 20px;
+ padding-left: 4px;
+}
+
+.markdown>ul li p,
+.markdown>ol li p {
+ margin: 0.6em 0;
+}
+
+.markdown ol>li {
+ list-style: decimal;
+}
+
+.markdown>ol li,
+.markdown blockquote ol>li {
+ margin-left: 20px;
+ padding-left: 4px;
+}
+
+.markdown code {
+ margin: 0 3px;
+ padding: 0 5px;
+ background: #eee;
+ border-radius: 3px;
+}
+
+.markdown strong,
+.markdown b {
+ font-weight: 600;
+}
+
+.markdown>table {
+ border-collapse: collapse;
+ border-spacing: 0px;
+ empty-cells: show;
+ border: 1px solid #e9e9e9;
+ width: 95%;
+ margin-bottom: 24px;
+}
+
+.markdown>table th {
+ white-space: nowrap;
+ color: #333;
+ font-weight: 600;
+}
+
+.markdown>table th,
+.markdown>table td {
+ border: 1px solid #e9e9e9;
+ padding: 8px 16px;
+ text-align: left;
+}
+
+.markdown>table th {
+ background: #F7F7F7;
+}
+
+.markdown blockquote {
+ font-size: 90%;
+ color: #999;
+ border-left: 4px solid #e9e9e9;
+ padding-left: 0.8em;
+ margin: 1em 0;
+}
+
+.markdown blockquote p {
+ margin: 0;
+}
+
+.markdown .anchor {
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ margin-left: 8px;
+}
+
+.markdown .waiting {
+ color: #ccc;
+}
+
+.markdown h1:hover .anchor,
+.markdown h2:hover .anchor,
+.markdown h3:hover .anchor,
+.markdown h4:hover .anchor,
+.markdown h5:hover .anchor,
+.markdown h6:hover .anchor {
+ opacity: 1;
+ display: inline-block;
+}
+
+.markdown>br,
+.markdown>p>br {
+ clear: both;
+}
+
+
+.hljs {
+ display: block;
+ background: white;
+ padding: 0.5em;
+ color: #333333;
+ overflow-x: auto;
+}
+
+.hljs-comment,
+.hljs-meta {
+ color: #969896;
+}
+
+.hljs-string,
+.hljs-variable,
+.hljs-template-variable,
+.hljs-strong,
+.hljs-emphasis,
+.hljs-quote {
+ color: #df5000;
+}
+
+.hljs-keyword,
+.hljs-selector-tag,
+.hljs-type {
+ color: #a71d5d;
+}
+
+.hljs-literal,
+.hljs-symbol,
+.hljs-bullet,
+.hljs-attribute {
+ color: #0086b3;
+}
+
+.hljs-section,
+.hljs-name {
+ color: #63a35c;
+}
+
+.hljs-tag {
+ color: #333333;
+}
+
+.hljs-title,
+.hljs-attr,
+.hljs-selector-id,
+.hljs-selector-class,
+.hljs-selector-attr,
+.hljs-selector-pseudo {
+ color: #795da3;
+}
+
+.hljs-addition {
+ color: #55a532;
+ background-color: #eaffea;
+}
+
+.hljs-deletion {
+ color: #bd2c00;
+ background-color: #ffecec;
+}
+
+.hljs-link {
+ text-decoration: underline;
+}
+
+/* 代码高亮 */
+/* PrismJS 1.15.0
+https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
+/**
+ * prism.js default theme for JavaScript, CSS and HTML
+ * Based on dabblet (http://dabblet.com)
+ * @author Lea Verou
+ */
+code[class*="language-"],
+pre[class*="language-"] {
+ color: black;
+ background: none;
+ text-shadow: 0 1px white;
+ font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
+ text-align: left;
+ white-space: pre;
+ word-spacing: normal;
+ word-break: normal;
+ word-wrap: normal;
+ line-height: 1.5;
+
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+
+ -webkit-hyphens: none;
+ -moz-hyphens: none;
+ -ms-hyphens: none;
+ hyphens: none;
+}
+
+pre[class*="language-"]::-moz-selection,
+pre[class*="language-"] ::-moz-selection,
+code[class*="language-"]::-moz-selection,
+code[class*="language-"] ::-moz-selection {
+ text-shadow: none;
+ background: #b3d4fc;
+}
+
+pre[class*="language-"]::selection,
+pre[class*="language-"] ::selection,
+code[class*="language-"]::selection,
+code[class*="language-"] ::selection {
+ text-shadow: none;
+ background: #b3d4fc;
+}
+
+@media print {
+
+ code[class*="language-"],
+ pre[class*="language-"] {
+ text-shadow: none;
+ }
+}
+
+/* Code blocks */
+pre[class*="language-"] {
+ padding: 1em;
+ margin: .5em 0;
+ overflow: auto;
+}
+
+:not(pre)>code[class*="language-"],
+pre[class*="language-"] {
+ background: #f5f2f0;
+}
+
+/* Inline code */
+:not(pre)>code[class*="language-"] {
+ padding: .1em;
+ border-radius: .3em;
+ white-space: normal;
+}
+
+.token.comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+ color: slategray;
+}
+
+.token.punctuation {
+ color: #999;
+}
+
+.namespace {
+ opacity: .7;
+}
+
+.token.property,
+.token.tag,
+.token.boolean,
+.token.number,
+.token.constant,
+.token.symbol,
+.token.deleted {
+ color: #905;
+}
+
+.token.selector,
+.token.attr-name,
+.token.string,
+.token.char,
+.token.builtin,
+.token.inserted {
+ color: #690;
+}
+
+.token.operator,
+.token.entity,
+.token.url,
+.language-css .token.string,
+.style .token.string {
+ color: #9a6e3a;
+ background: hsla(0, 0%, 100%, .5);
+}
+
+.token.atrule,
+.token.attr-value,
+.token.keyword {
+ color: #07a;
+}
+
+.token.function,
+.token.class-name {
+ color: #DD4A68;
+}
+
+.token.regex,
+.token.important,
+.token.variable {
+ color: #e90;
+}
+
+.token.important,
+.token.bold {
+ font-weight: bold;
+}
+
+.token.italic {
+ font-style: italic;
+}
+
+.token.entity {
+ cursor: help;
+}
diff --git a/src/assets/icon-font/demo_index.html b/src/assets/icon-font/demo_index.html
new file mode 100644
index 00000000..00979aec
--- /dev/null
+++ b/src/assets/icon-font/demo_index.html
@@ -0,0 +1,464 @@
+
+
+
+
+ iconfont Demo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Unicode
+ - Font class
+ - Symbol
+
+
+
查看项目
+
+
+
+
+
+
+ -
+
+
超链接
+ 
+
+
+ -
+
+
主题
+ 
+
+
+ -
+
+
笑脸
+ 
+
+
+ -
+
+
图 片
+ 
+
+
+ -
+
+
结构
+ 
+
+
+ -
+
+
样式
+ 
+
+
+ -
+
+
符号-大纲树
+ 
+
+
+ -
+
+
添加子节点
+ 
+
+
+ -
+
+
节点
+ 
+
+
+ -
+
+
删 除
+ 
+
+
+ -
+
+
HTSCIT_展开
+ 
+
+
+ -
+
+
HTSCIT_展开2
+ 
+
+
+
+
+
Unicode 引用
+
+
+
Unicode 是字体在网页端最原始的应用方式,特点是:
+
+ - 支持按字体的方式去动态调整图标大小,颜色等等。
+ - 默认情况下不支持多色,直接添加多色图标会自动去色。
+
+
+ 注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)
+
+
Unicode 使用步骤如下:
+
第一步:拷贝项目下面生成的 @font-face
+
@font-face {
+ font-family: 'iconfont';
+ src: url('iconfont.woff2?t=1622956585729') format('woff2'),
+ url('iconfont.woff?t=1622956585729') format('woff'),
+ url('iconfont.ttf?t=1622956585729') format('truetype');
+}
+
+
第二步:定义使用 iconfont 的样式
+
.iconfont {
+ font-family: "iconfont" !important;
+ font-size: 16px;
+ font-style: normal;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+
第三步:挑选相应图标并获取字体编码,应用于页面
+
+<span class="iconfont">3</span>
+
+
+ "iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。
+
+
+
+
+
+
+ -
+
+
+ 超链接
+
+ .iconchaolianjie
+
+
+
+ -
+
+
+ 主题
+
+ .iconjingzi
+
+
+
+ -
+
+
+ 笑脸
+
+ .iconxiaolian
+
+
+
+ -
+
+
+ 图 片
+
+ .iconimage
+
+
+
+ -
+
+
+ 结构
+
+ .iconjiegou
+
+
+
+ -
+
+
+ 样式
+
+ .iconyangshi
+
+
+
+ -
+
+
+ 符号-大纲树
+
+ .iconfuhao-dagangshu
+
+
+
+ -
+
+
+ 添加子节点
+
+ .icontianjiazijiedian
+
+
+
+ -
+
+
+ 节点
+
+ .iconjiedian
+
+
+
+ -
+
+
+ 删 除
+
+ .iconshanchu
+
+
+
+ -
+
+
+ HTSCIT_展开
+
+ .iconzhankai
+
+
+
+ -
+
+
+ HTSCIT_展开2
+
+ .iconzhankai1
+
+
+
+
+
+
font-class 引用
+
+
+
font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。
+
与 Unicode 使用方式相比,具有如下特点:
+
+ - 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
+ - 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
+
+
使用步骤如下:
+
第一步:引入项目下面生成的 fontclass 代码:
+
<link rel="stylesheet" href="./iconfont.css">
+
+
第二步:挑选相应图标并获取类名,应用于页面:
+
<span class="iconfont iconxxx"></span>
+
+
+ "
+ iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。
+
+
+
+
+
+
+ -
+
+
超链接
+ #iconchaolianjie
+
+
+ -
+
+
主题
+ #iconjingzi
+
+
+ -
+
+
笑脸
+ #iconxiaolian
+
+
+ -
+
+
图 片
+ #iconimage
+
+
+ -
+
+
结构
+ #iconjiegou
+
+
+ -
+
+
样式
+ #iconyangshi
+
+
+ -
+
+
符号-大纲树
+ #iconfuhao-dagangshu
+
+
+ -
+
+
添加子节点
+ #icontianjiazijiedian
+
+
+ -
+
+
节点
+ #iconjiedian
+
+
+ -
+
+
删 除
+ #iconshanchu
+
+
+ -
+
+
HTSCIT_展开
+ #iconzhankai
+
+
+ -
+
+
HTSCIT_展开2
+ #iconzhankai1
+
+
+
+
+
Symbol 引用
+
+
+
这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章
+ 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:
+
+ - 支持多色图标了,不再受单色限制。
+ - 通过一些技巧,支持像字体那样,通过
font-size, color 来调整样式。
+ - 兼容性较差,支持 IE9+,及现代浏览器。
+ - 浏览器渲染 SVG 的性能一般,还不如 png。
+
+
使用步骤如下:
+
第一步:引入项目下面生成的 symbol 代码:
+
<script src="./iconfont.js"></script>
+
+
第二步:加入通用 CSS 代码(引入一次就行):
+
<style>
+.icon {
+ width: 1em;
+ height: 1em;
+ vertical-align: -0.15em;
+ fill: currentColor;
+ overflow: hidden;
+}
+</style>
+
+
第三步:挑选相应图标并获取类名,应用于页面:
+
<svg class="icon" aria-hidden="true">
+ <use xlink:href="#icon-xxx"></use>
+</svg>
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/icon-font/iconfont.css b/src/assets/icon-font/iconfont.css
new file mode 100644
index 00000000..619ac08a
--- /dev/null
+++ b/src/assets/icon-font/iconfont.css
@@ -0,0 +1,63 @@
+@font-face {
+ font-family: "iconfont"; /* Project id 2479351 */
+ src: url('iconfont.woff2?t=1622956585729') format('woff2'),
+ url('iconfont.woff?t=1622956585729') format('woff'),
+ url('iconfont.ttf?t=1622956585729') format('truetype');
+}
+
+.iconfont {
+ font-family: "iconfont" !important;
+ font-size: 16px;
+ font-style: normal;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.iconchaolianjie:before {
+ content: "\e6f4";
+}
+
+.iconjingzi:before {
+ content: "\e610";
+}
+
+.iconxiaolian:before {
+ content: "\e60f";
+}
+
+.iconimage:before {
+ content: "\e629";
+}
+
+.iconjiegou:before {
+ content: "\e61d";
+}
+
+.iconyangshi:before {
+ content: "\e631";
+}
+
+.iconfuhao-dagangshu:before {
+ content: "\e71f";
+}
+
+.icontianjiazijiedian:before {
+ content: "\e622";
+}
+
+.iconjiedian:before {
+ content: "\e655";
+}
+
+.iconshanchu:before {
+ content: "\e696";
+}
+
+.iconzhankai:before {
+ content: "\e64c";
+}
+
+.iconzhankai1:before {
+ content: "\e673";
+}
+
diff --git a/src/assets/icon-font/iconfont.js b/src/assets/icon-font/iconfont.js
new file mode 100644
index 00000000..c5fb302b
--- /dev/null
+++ b/src/assets/icon-font/iconfont.js
@@ -0,0 +1 @@
+!function(c){var a,t,e,l,h,i,o='',s=(s=document.getElementsByTagName("script"))[s.length-1].getAttribute("data-injectcss");if(s&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}function n(){h||(h=!0,e())}a=function(){var c,a,t;(t=document.createElement("div")).innerHTML=o,o=null,(a=t.getElementsByTagName("svg")[0])&&(a.setAttribute("aria-hidden","true"),a.style.position="absolute",a.style.width=0,a.style.height=0,a.style.overflow="hidden",c=a,(t=document.body).firstChild?(a=t.firstChild).parentNode.insertBefore(c,a):t.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(a,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),a()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(e=a,l=c.document,h=!1,(i=function(){try{l.documentElement.doScroll("left")}catch(c){return void setTimeout(i,50)}n()})(),l.onreadystatechange=function(){"complete"==l.readyState&&(l.onreadystatechange=null,n())})}(window);
\ No newline at end of file
diff --git a/src/assets/icon-font/iconfont.json b/src/assets/icon-font/iconfont.json
new file mode 100644
index 00000000..b22e9bcb
--- /dev/null
+++ b/src/assets/icon-font/iconfont.json
@@ -0,0 +1,93 @@
+{
+ "id": "2479351",
+ "name": "思绪",
+ "font_family": "iconfont",
+ "css_prefix_text": "icon",
+ "description": "思维导图",
+ "glyphs": [
+ {
+ "icon_id": "1790486",
+ "name": "超链接",
+ "font_class": "chaolianjie",
+ "unicode": "e6f4",
+ "unicode_decimal": 59124
+ },
+ {
+ "icon_id": "4608986",
+ "name": "主题",
+ "font_class": "jingzi",
+ "unicode": "e610",
+ "unicode_decimal": 58896
+ },
+ {
+ "icon_id": "11903017",
+ "name": "笑脸",
+ "font_class": "xiaolian",
+ "unicode": "e60f",
+ "unicode_decimal": 58895
+ },
+ {
+ "icon_id": "19657962",
+ "name": "图 片",
+ "font_class": "image",
+ "unicode": "e629",
+ "unicode_decimal": 58921
+ },
+ {
+ "icon_id": "20784489",
+ "name": "结构",
+ "font_class": "jiegou",
+ "unicode": "e61d",
+ "unicode_decimal": 58909
+ },
+ {
+ "icon_id": "15969341",
+ "name": "样式",
+ "font_class": "yangshi",
+ "unicode": "e631",
+ "unicode_decimal": 58929
+ },
+ {
+ "icon_id": "2967176",
+ "name": "符号-大纲树",
+ "font_class": "fuhao-dagangshu",
+ "unicode": "e71f",
+ "unicode_decimal": 59167
+ },
+ {
+ "icon_id": "12316668",
+ "name": "添加子节点",
+ "font_class": "tianjiazijiedian",
+ "unicode": "e622",
+ "unicode_decimal": 58914
+ },
+ {
+ "icon_id": "14435368",
+ "name": "节点",
+ "font_class": "jiedian",
+ "unicode": "e655",
+ "unicode_decimal": 58965
+ },
+ {
+ "icon_id": "15765352",
+ "name": "删 除",
+ "font_class": "shanchu",
+ "unicode": "e696",
+ "unicode_decimal": 59030
+ },
+ {
+ "icon_id": "9592600",
+ "name": "HTSCIT_展开",
+ "font_class": "zhankai",
+ "unicode": "e64c",
+ "unicode_decimal": 58956
+ },
+ {
+ "icon_id": "9900009",
+ "name": "HTSCIT_展开2",
+ "font_class": "zhankai1",
+ "unicode": "e673",
+ "unicode_decimal": 58995
+ }
+ ]
+}
diff --git a/src/assets/icon-font/iconfont.ttf b/src/assets/icon-font/iconfont.ttf
new file mode 100644
index 00000000..fc5803d9
Binary files /dev/null and b/src/assets/icon-font/iconfont.ttf differ
diff --git a/src/assets/icon-font/iconfont.woff b/src/assets/icon-font/iconfont.woff
new file mode 100644
index 00000000..a61b65b4
Binary files /dev/null and b/src/assets/icon-font/iconfont.woff differ
diff --git a/src/assets/icon-font/iconfont.woff2 b/src/assets/icon-font/iconfont.woff2
new file mode 100644
index 00000000..b1fc435f
Binary files /dev/null and b/src/assets/icon-font/iconfont.woff2 differ
diff --git a/src/components/ImgUpload/index.vue b/src/components/ImgUpload/index.vue
new file mode 100644
index 00000000..5841c686
--- /dev/null
+++ b/src/components/ImgUpload/index.vue
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/ImgUpload/style.less b/src/components/ImgUpload/style.less
new file mode 100644
index 00000000..239ba37d
--- /dev/null
+++ b/src/components/ImgUpload/style.less
@@ -0,0 +1,80 @@
+.imgUploadContainer {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background-color: rgba(255,255,255,.9);
+ z-index: 1000;
+
+ .imgUploadPanel {
+ position: relative;
+ width: 100%;
+ font-size: 22px;
+ white-space: nowrap;
+ color: #909090;
+ cursor: default;
+ user-select: none;
+
+ .title {
+ margin-bottom: 15px;
+ font-size: 22px;
+ font-weight: 700;
+ color: hsla(218,9%,51%,.8);
+ }
+
+ .closeBtn {
+ position: absolute;
+ right: 25px;
+ top: 32px;
+ cursor: pointer;
+ }
+
+ .imgUploadInputArea {
+ display: block;
+ width: 100%;
+ height: 200px;
+ font-size: 20px;
+ color: rgba(51,51,51,.4);
+ background-color: hsla(0,0%,87%,.6);
+ border: none;
+ outline: none;
+ cursor: pointer;
+ text-align: center;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ white-space: normal;
+ padding: 10px;
+ }
+
+ #imgUploadInput {
+ display: none;
+ }
+
+ .uploadInfoBox {
+ position: relative;
+ width: 100%;
+ height: 200px;
+ background-color: hsla(0,0%,87%,.6);
+
+ .previewBox {
+ width: 100%;
+ height: 100%;
+ background-size: contain;
+ background-repeat: no-repeat;
+ background-position: center;
+ }
+
+ .delBtn {
+ position: absolute;
+ right: 0px;
+ top: 0px;
+ cursor: pointer;
+ width: 20px;
+ height: 20px;
+ background-color: #fff;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/config/index.js b/src/config/index.js
new file mode 100644
index 00000000..2785bd82
--- /dev/null
+++ b/src/config/index.js
@@ -0,0 +1,128 @@
+// 字体列表
+export const fontFamilyList = [
+ {
+ name: '宋体',
+ value: '宋体, SimSun, Songti SC'
+ }, {
+ name: '微软雅黑',
+ value: '微软雅黑, Microsoft YaHei'
+ }, {
+ name: '楷体',
+ value: '楷体, 楷体_GB2312, SimKai, STKaiti'
+ }, {
+ name: '黑体',
+ value: '黑体, SimHei, Heiti SC'
+ }, {
+ name: '隶书',
+ value: '隶书, SimLi'
+ }, {
+ name: 'Andale Mono',
+ value: 'andale mono'
+ }, {
+ name: 'Arial',
+ value: 'arial, helvetica, sans-serif'
+ }, {
+ name: 'arialBlack',
+ value: 'arial black, avant garde'
+ }, {
+ name: 'Comic Sans Ms',
+ value: 'comic sans ms'
+ }, {
+ name: 'Impact',
+ value: 'impact, chicago'
+ }, {
+ name: 'Times New Roman',
+ value: 'times new roman'
+ }, {
+ name: 'Sans-Serif',
+ value: 'sans-serif'
+ },
+ {
+ name: 'serif',
+ value: 'serif'
+ }
+]
+
+// 字号
+export const fontSizeList = [10, 12, 16, 18, 24, 32, 48]
+
+// 颜色
+export const colorList = [
+ '#4D4D4D',
+ '#999999',
+ '#FFFFFF',
+ '#F44E3B',
+ '#FE9200',
+ '#FCDC00',
+ '#DBDF00',
+ '#A4DD00',
+ '#68CCCA',
+ '#73D8FF',
+ '#AEA1FF',
+ '#FDA1FF',
+ '#333333',
+ '#808080',
+ '#cccccc',
+ '#D33115',
+ '#E27300',
+ '#FCC400',
+ '#B0BC00',
+ '#68BC00',
+ '#16A5A5',
+ '#009CE0',
+ '#7B64FF',
+ '#FA28FF',
+ '#000000',
+ '#666666',
+ '#B3B3B3',
+ '#9F0500',
+ '#C45100',
+ '#FB9E00',
+ '#808900',
+ '#194D33',
+ '#0C797D',
+ '#0062B1',
+ '#653294',
+ '#AB149E'
+]
+
+// 边框宽度
+export const borderWidthList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+
+// 边框样式
+export const borderDasharrayList = [
+ {
+ name: '实线',
+ value: 'none'
+ },
+ {
+ name: '虚线1',
+ value: '5,5'
+ },
+ {
+ name: '虚线2',
+ value: '10,10'
+ },
+ {
+ name: '虚线3',
+ value: '20,10,5,5,5,10'
+ },
+ {
+ name: '虚线4',
+ value: '5, 5, 1, 5'
+ },
+ {
+ name: '虚线5',
+ value: '15, 10, 5, 10, 15'
+ },
+ {
+ name: '虚线6',
+ value: '1, 5'
+ }
+]
+
+// 圆角
+export const borderRadiusList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+
+// 线宽
+export const lineWidthList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
\ No newline at end of file
diff --git a/src/main.js b/src/main.js
new file mode 100644
index 00000000..bbf920d2
--- /dev/null
+++ b/src/main.js
@@ -0,0 +1,17 @@
+import Vue from 'vue'
+import App from './App.vue'
+import router from './router'
+import store from './store'
+import ElementUI from 'element-ui'
+import 'element-ui/lib/theme-chalk/index.css'
+import '@/assets/icon-font/iconfont.css';
+
+Vue.config.productionTip = false
+Vue.prototype.$bus = new Vue()
+Vue.use(ElementUI)
+
+new Vue({
+ render: h => h(App),
+ router,
+ store
+}).$mount('#app')
diff --git a/src/package/.DS_Store b/src/package/.DS_Store
new file mode 100644
index 00000000..fa590cfd
Binary files /dev/null and b/src/package/.DS_Store differ
diff --git a/src/package/mind-map/.DS_Store b/src/package/mind-map/.DS_Store
new file mode 100644
index 00000000..feca8613
Binary files /dev/null and b/src/package/mind-map/.DS_Store differ
diff --git a/src/package/mind-map/example/exampleData.js b/src/package/mind-map/example/exampleData.js
new file mode 100644
index 00000000..de6c1ee6
--- /dev/null
+++ b/src/package/mind-map/example/exampleData.js
@@ -0,0 +1,44 @@
+/**
+ * @Author: 王林
+ * @Date: 2021-04-15 22:23:24
+ * @Desc: 完整示例数据
+ */
+export default {
+ "root": {
+ "data": {
+ "text": "鱼骨头图",
+ },
+ "children": [{
+ "data": {
+ "text": "分支主题",
+ "expand": true
+ },
+ "children": [{
+ "data": {
+ "text": "分支主题",
+ "hyperlink": "https://naotu.baidu.com/",
+ "hyperlinkTitle": "百度脑图",
+ "image": "https://kityminder-img.gz.bcebos.com/865551aedebd1e02ac6e76d24c093231df9aafda",
+ "imageTitle": "图片名称",
+ "imageSize": {
+ "width": 200,
+ "height": 112
+ },
+ "note": "我是备注",
+ "resource": ["标签1", "标签2"],
+ "priority": 5,
+ "progress": 7,
+ // ... 其他类型的图标
+ },
+ "children": []
+ }]
+ }]
+ },
+ "theme": {
+ "template": "default",
+ "config": {
+ // 自定义配置...
+ }
+ },
+ "layout": "logicalStructure"
+}
\ No newline at end of file
diff --git a/src/package/mind-map/index.js b/src/package/mind-map/index.js
new file mode 100644
index 00000000..90fc234c
--- /dev/null
+++ b/src/package/mind-map/index.js
@@ -0,0 +1,174 @@
+import View from './src/View'
+import Event from './src/Event'
+import Render from './src/Render'
+import merge from 'deepmerge'
+import theme from './src/themes'
+import Style from './src/Style'
+import KeyCommand from './src/KeyCommand'
+import Command from './src/Command';
+import { SVG } from '@svgdotjs/svg.js'
+
+const defaultOpt = {
+ // 布局
+ layout: 'logicalStructure',
+ // 放大缩小的增量比例,即step = scaleRatio * width|height
+ scaleRatio: 0.1,
+ // 主题
+ theme: 'default',// 内置主题:default(默认主题)
+ // 主题配置,会和所选择的主题进行合并
+ themeConfig: {}
+}
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 11:18:47
+ * @Desc: 思维导图
+ */
+class MindMap {
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 11:19:01
+ * @Desc: 构造函数
+ */
+ constructor(opt = {}) {
+ this.opt = merge(defaultOpt, opt)
+ // 容器元素
+ this.el = this.opt.el
+ let {
+ width,
+ height
+ } = this.el.getBoundingClientRect()
+ // 画布宽高
+ this.width = width
+ this.height = height
+ // 画笔
+ this.draw = SVG().addTo(this.el).size(width, height)
+ // 节点id
+ this.uid = 0
+
+ // 主题
+ this.initTheme()
+
+ // 事件类
+ this.event = new Event({
+ mindMap: this
+ })
+
+ // 按键类
+ this.keyCommand = new KeyCommand({
+ mindMap: this
+ })
+
+ // 命令类
+ this.command = new Command({
+ mindMap: this
+ })
+
+ // 渲染类
+ this.renderer = new Render({
+ mindMap: this
+ })
+
+ // 视图操作类
+ this.view = new View({
+ mindMap: this,
+ draw: this.draw
+ })
+
+ this.render()
+ setTimeout(() => {
+ this.command.addHistory()
+ }, 0);
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-24 13:25:50
+ * @Desc: 监听事件
+ */
+ on(event, fn) {
+ this.event.on(event, fn)
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-24 13:51:35
+ * @Desc: 触发事件
+ */
+ emit(event, ...args) {
+ this.event.emit(event, ...args)
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-24 13:53:54
+ * @Desc: 解绑事件
+ */
+ off(event, fn) {
+ this.event.off(event, fn)
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-05 13:32:43
+ * @Desc: 设置主题
+ */
+ initTheme() {
+ this.themeConfig = merge(this.opt.theme && theme[this.opt.theme] ? theme[this.opt.theme] : theme.default, this.opt.themeConfig)
+ Style.setBackgroundStyle(this.el, this.themeConfig)
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-05 13:52:08
+ * @Desc: 设置主题
+ */
+ setTheme(theme) {
+ this.opt.theme = theme
+ this.render()
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-05 13:50:17
+ * @Desc: 设置主题配置
+ */
+ setThemeConfig(config) {
+ this.opt.themeConfig = config
+ this.render()
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-05 14:01:29
+ * @Desc: 获取某个主题配置值
+ */
+ getThemeConfig(prop) {
+ return prop === undefined ? this.themeConfig : this.themeConfig[prop]
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 18:47:29
+ * @Desc: 渲染节点
+ */
+ render() {
+ this.draw.clear()
+ this.initTheme()
+ this.renderer.render()
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 13:01:00
+ * @Desc: 执行命令
+ */
+ execCommand(...args) {
+ this.command.exec(...args)
+ }
+}
+
+export default MindMap
\ No newline at end of file
diff --git a/src/package/mind-map/src/Command.js b/src/package/mind-map/src/Command.js
new file mode 100644
index 00000000..aa023b0c
--- /dev/null
+++ b/src/package/mind-map/src/Command.js
@@ -0,0 +1,71 @@
+import { copyRenderTree, simpleDeepClone } from './Utils';
+
+/**
+ * @Author: 王林
+ * @Date: 2021-05-04 13:10:06
+ * @Desc: 命令类
+ */
+class Command {
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 13:10:24
+ * @Desc: 构造函数
+ */
+ constructor(opt = {}) {
+ this.opt = opt
+ this.mindMap = opt.mindMap
+ this.commands = {}
+ this.history = []
+ this.activeHistoryIndex = 0
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 13:12:30
+ * @Desc: 执行命令
+ */
+ exec(name, ...args) {
+ if (this.commands[name]) {
+ this.commands[name].forEach((fn) => {
+ fn(...args)
+ })
+ this.addHistory()
+ }
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 13:13:01
+ * @Desc: 添加命令
+ */
+ add(name, fn) {
+ if (this.commands[name]) {
+ this.commands[name].push(fn)
+ } else[
+ this.commands[name] = [fn]
+ ]
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 14:35:43
+ * @Desc: 添加回退数据
+ */
+ addHistory() {
+ let data = this.getCopyData()
+ this.history.push(simpleDeepClone(data))
+ this.activeHistoryIndex++
+ this.mindMap.emit('data_change', data)
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 15:02:58
+ * @Desc: 获取渲染树数据副本
+ */
+ getCopyData() {
+ return copyRenderTree({}, this.mindMap.renderer.renderTree)
+ }
+}
+
+export default Command
\ No newline at end of file
diff --git a/src/package/mind-map/src/Event.js b/src/package/mind-map/src/Event.js
new file mode 100644
index 00000000..dab2c563
--- /dev/null
+++ b/src/package/mind-map/src/Event.js
@@ -0,0 +1,149 @@
+import EventEmitter from 'eventemitter3'
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 14:53:09
+ * @Desc: 事件类
+ */
+class Event extends EventEmitter {
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 14:53:25
+ * @Desc: 构造函数
+ */
+ constructor(opt = {}) {
+ super()
+ this.opt = opt
+ this.mindMap = opt.mindMap
+ this.isMousedown = false
+ this.mousedownPos = {
+ x: 0,
+ y: 0
+ }
+ this.mousemovePos = {
+ x: 0,
+ y: 0
+ }
+ this.mousemoveOffset = {
+ x: 0,
+ y: 0
+ }
+ this.bindFn()
+ this.bind()
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 15:52:24
+ * @Desc: 绑定函数上下文
+ */
+ bindFn() {
+ this.onDrawClick = this.onDrawClick.bind(this)
+ this.onMousedown = this.onMousedown.bind(this)
+ this.onMousemove = this.onMousemove.bind(this)
+ this.onMouseup = this.onMouseup.bind(this)
+ this.onMousewheel = this.onMousewheel.bind(this)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 14:53:43
+ * @Desc: 绑定事件
+ */
+ bind() {
+ this.mindMap.draw.on('click', this.onDrawClick)
+ this.mindMap.el.addEventListener('mousedown', this.onMousedown)
+ window.addEventListener('mousemove', this.onMousemove)
+ window.addEventListener('mouseup', this.onMouseup)
+ this.mindMap.el.addEventListener('mousewheel', this.onMousewheel)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 15:40:51
+ * @Desc: 解绑事件
+ */
+ unbind() {
+ this.mindMap.el.removeEventListener('mousedown', this.onMousedown)
+ window.removeEventListener('mousemove', this.onMousemove)
+ window.removeEventListener('mouseup', this.onMouseup)
+ this.mindMap.el.removeEventListener('mousewheel', this.onMousewheel)
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-24 13:19:39
+ * @Desc: 画布的单击事件
+ */
+ onDrawClick(e) {
+ this.emit('draw_click', e)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 15:17:35
+ * @Desc: 鼠标按下事件
+ */
+ onMousedown(e) {
+ e.preventDefault()
+ this.isMousedown = true
+ this.mousedownPos.x = e.clientX
+ this.mousedownPos.y = e.clientY
+ this.emit('mousedown', e, this)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 15:18:32
+ * @Desc: 鼠标移动事件
+ */
+ onMousemove(e) {
+ e.preventDefault()
+ this.mousemovePos.x = e.clientX
+ this.mousemovePos.y = e.clientY
+ this.mousemoveOffset.x = e.clientX - this.mousedownPos.x
+ this.mousemoveOffset.y = e.clientY - this.mousedownPos.y
+ this.emit('mousemove', e, this)
+ if (this.isMousedown) {
+ this.emit('drag', e, this)
+ }
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 15:18:57
+ * @Desc: 鼠标松开事件
+ */
+ onMouseup(e) {
+ this.isMousedown = false
+ this.emit('mouseup', e, this)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 15:46:27
+ * @Desc: 鼠标滚动
+ */
+ onMousewheel(e) {
+ e.stopPropagation()
+ e.preventDefault()
+ let dir
+ if (e.wheelDeltaY > 0) {
+ dir = 'up'
+ } else {
+ dir = 'down'
+ }
+ this.emit('mousewheel', e, dir, this)
+ }
+}
+
+export default Event
\ No newline at end of file
diff --git a/src/package/mind-map/src/KeyCommand.js b/src/package/mind-map/src/KeyCommand.js
new file mode 100644
index 00000000..30ce7303
--- /dev/null
+++ b/src/package/mind-map/src/KeyCommand.js
@@ -0,0 +1,116 @@
+import { keyMap } from './utils/keyMap';
+/**
+ * @Author: 王林
+ * @Date: 2021-04-24 15:20:46
+ * @Desc: 快捷按键、命令处理类
+ */
+export default class KeyCommand {
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-24 15:21:32
+ * @Desc: 构造函数
+ */
+ constructor(opt) {
+ this.opt = opt
+ this.mindMap = opt.mindMap
+ this.shortcutMap = {
+ //Enter: [fn]
+ }
+ this.bindEvent()
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-24 15:23:22
+ * @Desc: 绑定事件
+ */
+ bindEvent() {
+ window.addEventListener('keydown', (e) => {
+ Object.keys(this.shortcutMap).forEach((key) => {
+ if (this.checkKey(e, key)) {
+ e.stopPropagation()
+ e.preventDefault()
+ this.shortcutMap[key].forEach((fn) => {
+ fn()
+ })
+ }
+ })
+ })
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-24 19:24:53
+ * @Desc: 检查键值是否符合
+ */
+ checkKey(e, key) {
+ let o = this.getOriginEventCodeArr(e)
+ let k = this.getKeyCodeArr(key)
+ if (o.length !== k.length) {
+ return false
+ }
+ for (let i = 0; i < o.length; i++) {
+ let index = k.findIndex((item) => {
+ return item === o[i];
+ })
+ if (index === -1) {
+ return false
+ } else {
+ k.splice(index, 1)
+ }
+ }
+ return true
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-24 19:15:19
+ * @Desc: 获取事件对象里的键值数组
+ */
+ getOriginEventCodeArr(e) {
+ let arr = []
+ if (e.ctrlKey || e.metaKey) {
+ arr.push(keyMap['Control'])
+ }
+ if (e.altKey) {
+ arr.push(keyMap['Alt'])
+ }
+ if (e.shiftKey) {
+ arr.push(keyMap['Shift'])
+ }
+ arr.push(e.keyCode)
+ return arr
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-24 19:40:11
+ * @Desc: 获取快捷键对应的键值数组
+ */
+ getKeyCodeArr(key) {
+ let keyArr = key.split(/\s*\+\s*/)
+ let arr = []
+ keyArr.forEach((item) => {
+ arr.push(keyMap[item])
+ })
+ return arr
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-24 15:23:00
+ * @Desc: 添加快捷键命令
+ * Enter
+ * Tab | Insert
+ * Shift + a
+ */
+ addShortcut(key, fn) {
+ key.split(/\s*\|\s*/).forEach((item) => {
+ if (this.shortcutMap[item]) {
+ this.shortcutMap[item].push(fn)
+ } else {
+ this.shortcutMap[item] = [fn]
+ }
+ })
+ }
+}
\ No newline at end of file
diff --git a/src/package/mind-map/src/Node.js b/src/package/mind-map/src/Node.js
new file mode 100644
index 00000000..38f1bedd
--- /dev/null
+++ b/src/package/mind-map/src/Node.js
@@ -0,0 +1,397 @@
+import Style from './Style';
+import {
+ resizeImgSize
+} from './Utils'
+import {
+ Image,
+ Text,
+ SVG,
+ Circle,
+ Element
+} from '@svgdotjs/svg.js'
+import btnsSvg from './svg/btns';
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 11:26:00
+ * @Desc: 节点类
+ */
+class Node {
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 11:26:17
+ * @Desc: 构造函数
+ */
+ constructor(opt = {}) {
+ // 原始数据
+ this.originData = opt.originData
+ // 原始数据里的数据部分
+ this.data = opt.data
+ // id
+ this.uid = opt.uid
+ // 控制实例
+ this.mindMap = opt.mindMap
+ // 渲染实例
+ this.renderer = opt.renderer
+ // 主题配置
+ this.themeConfig = this.mindMap.themeConfig
+ // 样式实例
+ this.style = new Style(this, this.themeConfig)
+ // 渲染器
+ this.draw = opt.draw || null
+ // 是否是根节点
+ this.isRoot = opt.isRoot === undefined ? false : opt.isRoot
+ // 是否激活
+ this.isActive = opt.isActive === undefined ? false : opt.isActive
+ // 是否展开
+ this.expand = opt.expand === undefined ? true : opt.expand
+ // 节点层级
+ this.layerIndex = opt.layerIndex === undefined ? 0 : opt.layerIndex
+ // 节点宽
+ this.width = opt.width || 0
+ // 节点高
+ this.height = opt.height || 0
+ // left
+ this.left = opt.left || 0
+ // top
+ this.top = opt.top || 0
+ // 父节点
+ this.parent = opt.parent || null
+ // 子节点
+ this.children = opt.children || []
+ // 全部子节点所占的高度之和
+ this.childrenAreaHeight = opt.childrenAreaHeight || 0
+ // 文本节点
+ this.textNode = null
+ // 其他数据
+ Object.keys(opt.data).forEach((key) => {
+ this[key] = opt.data[key]
+ })
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 15:55:04
+ * @Desc: 添加子节点
+ */
+ addChildren(node) {
+ this.children.push(node)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-09 09:46:23
+ * @Desc: 刷新节点的宽高
+ */
+ refreshSize() {
+ let {
+ width,
+ height
+ } = this.getNodeRect()
+ this.width = width
+ this.height = height
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 14:52:17
+ * @Desc: 计算节点尺寸信息
+ */
+ getNodeRect() {
+ let width = this.themeConfig.paddingX * 2
+ let height = this.themeConfig.paddingY * 2
+ let maxWidth = 0
+ if (this.img) {
+ let img = this.createImgNode()
+ if (img.width > maxWidth) {
+ maxWidth = img.width
+ }
+ height += img.height
+ }
+ if (this.icon && this.text) {
+ let icon = this.createIconNode()
+ let text = this.createTextNode()
+ if (icon.width + text.width > maxWidth) {
+ maxWidth = icon.width + text.width
+ }
+ height += Math.max(text.height, icon.height)
+ } else if (this.text) {
+ let text = this.createTextNode()
+ if (text.width > maxWidth) {
+ maxWidth = text.width
+ }
+ height += text.height
+ } else if (this.icon) {
+ let icon = this.createIconNode()
+ if (icon.width > maxWidth) {
+ maxWidth = icon.width
+ }
+ height += icon.height
+ }
+ return {
+ width: width + maxWidth,
+ height
+ }
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-09 14:06:17
+ * @Desc: 创建图片节点
+ */
+ createImgNode() {
+ if (!this.img) {
+ return
+ }
+ let imgSize = this.getImgShowSize()
+ return {
+ node: new Image().load(this.img).size(...imgSize),
+ width: imgSize[0],
+ height: imgSize[1]
+ }
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-09 14:08:56
+ * @Desc: 创建文本节点
+ */
+ createTextNode() {
+ if (!this.text) {
+ return
+ }
+ let node = this.draw.text(this.text)
+ this.style.text(node)
+ let {
+ width,
+ height
+ } = node.bbox()
+ let cloneNode = node.clone()
+ node.remove()
+ return {
+ node: cloneNode,
+ width,
+ height
+ }
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-09 14:10:48
+ * @Desc: 创建icon节点
+ */
+ createIconNode() {
+ if (!this.icon) {
+ return
+ }
+ let node = SVG('').size(this.themeConfig.iconSize, this.themeConfig.iconSize)
+ return {
+ node,
+ width: this.themeConfig.iconSize,
+ height: this.themeConfig.iconSize
+ }
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-09 11:10:11
+ * @Desc: 创建内容节点
+ */
+ createNode() {
+ let {
+ left,
+ top,
+ width,
+ height
+ } = this
+ let paddingY = this.themeConfig.paddingY
+ // 创建组
+ let group = this.draw.group()
+ // 节点矩形
+ let _rectNode = group.rect(width, height).x(left).y(top)
+ this.style.rect(_rectNode)
+ // 内容节点
+ let imgNode = this.createImgNode()
+ let iconNode = this.createIconNode()
+ let textNode = this.createTextNode()
+ let imgHeight = imgNode ? imgNode.height : 0
+ // 图片
+ if (imgNode) {
+ group.add(imgNode.node)
+ imgNode.node.cx(left + width / 2).y(top + paddingY)
+ }
+ // icon
+ if (iconNode) {
+ group.add(iconNode.node)
+ iconNode.node.x(left + width / 2).y(top + paddingY + imgHeight + (textNode && textNode.height > iconNode.height ? (textNode.height - iconNode.height) / 2 : 0)).dx(textNode ? -textNode.width / 2 - iconNode.width / 2 : 0)
+ }
+ // 文字
+ if (textNode) {
+ this.textNode = textNode
+ group.add(textNode.node)
+ textNode.node.cx(left + width / 2).y(top + paddingY + imgHeight).dx(iconNode ? iconNode.width / 2 : 0)
+ }
+ // 单击事件
+ group.click((e) => {
+ e.stopPropagation()
+ if (this.isActive) {
+ return;
+ }
+ this.mindMap.emit('before_node_active', this, this.renderer.activeNodeList)
+ this.renderer.clearActive()
+ this.isActive = true
+ this.mindMap.execCommand('UPDATE_NODE_DATA', this, {
+ isActive: this.isActive
+ })
+ this.renderer.activeNodeList.push(this)
+ this.mindMap.render()
+ this.mindMap.emit('node_active', this, this.renderer.activeNodeList)
+ })
+ // 双击事件
+ group.dblclick(() => {
+ this.showTextEditBox()
+ })
+ return group
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-13 22:15:56
+ * @Desc: 显示文本编辑框
+ */
+ showTextEditBox() {
+ if (!this.text) {
+ return;
+ }
+ this.renderer.showEditTextBox(this, this.textNode.node.node.getBoundingClientRect())
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 13:55:58
+ * @Desc: 渲染
+ */
+ render() {
+ // 连线
+ this.drawLine()
+ // 按钮
+ this.drawBtn()
+ // 节点
+ this.draw.add(this.createNode())
+ // 子节点
+ if (this.children && this.children.length && this.expand) {
+ this.children.forEach((child) => {
+ child.render()
+ })
+ }
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-10 22:01:53
+ * @Desc: 连线
+ */
+ drawLine() {
+ if (!this.expand) {
+ return;
+ }
+ let lines = this.renderer.layout.drawLine(this)
+ lines.forEach((line) => {
+ this.style.line(line)
+ })
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-11 19:47:01
+ * @Desc: 展开收缩按钮
+ */
+ drawBtn() {
+ if (this.children.length <= 0 || this.isRoot) {
+ return;
+ }
+ let g = this.draw.group()
+ let iconSvg
+ if (this.expand) {
+ iconSvg = btnsSvg.close
+ } else {
+ iconSvg = btnsSvg.open
+ }
+ let node = SVG(iconSvg).size(20, 20)
+ let fillNode = new Circle().size(20)
+ this.renderer.layout.drawIcon(this, [node, fillNode])
+ node.dx(0).dy(-10)
+ fillNode.dx(0).dy(-10)
+ this.style.iconBtn(node, fillNode)
+ g.mouseover(() => {
+ g.css({ cursor: 'pointer' })
+ })
+ g.mouseout(() => {
+ g.css({ cursor: 'auto' })
+ })
+ g.click(() => {
+ this.expand = !this.expand
+ // 需要反映到实际数据上
+ this.mindMap.execCommand('UPDATE_NODE_DATA', this, {
+ expand: this.expand
+ })
+ this.mindMap.render()
+ this.mindMap.emit('expand_btn_click', this)
+ })
+ g.add(fillNode)
+ g.add(node)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-09 10:12:51
+ * @Desc: 获取图片显示宽高
+ */
+ getImgShowSize() {
+ return resizeImgSize(this.imgWidth, this.imgHeight, this.themeConfig.imgMaxWidth, this.themeConfig.imgMaxHeight)
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 21:48:49
+ * @Desc: 获取某个样式
+ */
+ getStyle(prop, root, isActive) {
+ let v = this.style.merge(prop, root, isActive)
+ return v === undefined ? '' : v
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 22:18:07
+ * @Desc: 修改某个样式
+ */
+ setStyle(prop, value, isActive) {
+ if (isActive) {
+ this.mindMap.execCommand('UPDATE_NODE_DATA', this, {
+ activeStyle: {
+ ...(this.data.activeStyle || {}),
+ [prop]: value
+ }
+ })
+ } else {
+ this.mindMap.execCommand('UPDATE_NODE_DATA', this, {
+ [prop]: value
+ })
+ }
+ this.mindMap.render()
+ }
+}
+
+export default Node
\ No newline at end of file
diff --git a/src/package/mind-map/src/Render.js b/src/package/mind-map/src/Render.js
new file mode 100644
index 00000000..afd75d57
--- /dev/null
+++ b/src/package/mind-map/src/Render.js
@@ -0,0 +1,256 @@
+import merge from 'deepmerge'
+import LogicalStructure from './layouts/LogicalStructure'
+import { getStrWithBrFromHtml } from './Utils'
+
+// 布局列表
+const layouts = {
+ logicalStructure: LogicalStructure
+}
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:25:07
+ * @Desc: 渲染
+ */
+class Render {
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:25:32
+ * @Desc: 构造函数
+ */
+ constructor(opt = {}) {
+ this.opt = opt
+ this.mindMap = opt.mindMap
+ this.themeConfig = this.mindMap.themeConfig
+ this.draw = this.mindMap.draw
+ // 渲染树,操作过程中修改的都是这里的数据
+ this.renderTree = merge({}, this.mindMap.opt.data || {})
+ // 当前激活的节点列表
+ this.activeNodeList = []
+ // 根节点
+ this.root = null
+ // 文本编辑框
+ this.textEditNode = null
+ // 文本编辑框是否显示
+ this.showTextEdit = false
+ // 布局
+ this.layout = new (layouts[this.mindMap.opt.layout] ? layouts[this.mindMap.opt.layout] : layouts.logicalStructure)({
+ mindMap: this.mindMap,
+ renderer: this,
+ renderTree: this.renderTree,
+ themeConfig: this.mindMap.themeConfig,
+ draw: this.mindMap.draw
+ })
+ // 绑定事件
+ this.bindEvent()
+ // 注册命令
+ this.registerCommands()
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-24 13:27:04
+ * @Desc: 事件
+ */
+ bindEvent() {
+ this.mindMap.on('draw_click', () => {
+ // 隐藏文本编辑框
+ this.hideEditTextBox()
+ // 清除激活状态
+ if (this.activeNodeList.length > 0) {
+ this.clearActive()
+ this.mindMap.render()
+ this.mindMap.emit('node_active', null, [])
+ }
+ })
+ this.mindMap.on('expand_btn_click', () => {
+ this.hideEditTextBox()
+ })
+ this.mindMap.on('before_node_active', () => {
+ this.hideEditTextBox()
+ })
+ this.mindMap.keyCommand.addShortcut('Enter', () => {
+ this.hideEditTextBox()
+ })
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 13:19:06
+ * @Desc: 注册命令
+ */
+ registerCommands() {
+ this.insertNode = this.insertNode.bind(this)
+ this.mindMap.command.add('INSERT_NODE', this.insertNode)
+ this.insertChildNode = this.insertChildNode.bind(this)
+ this.mindMap.command.add('INSERT_CHILD_NODE', this.insertChildNode)
+ this.removeNode = this.removeNode.bind(this)
+ this.mindMap.command.add('REMOVE_NODE', this.removeNode)
+ this.updateNodeData = this.updateNodeData.bind(this)
+ this.mindMap.command.add('UPDATE_NODE_DATA', this.updateNodeData)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 16:27:55
+ * @Desc: 渲染
+ */
+ render() {
+ this.root = this.layout.doLayout()
+ this.root.render()
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-12 22:45:01
+ * @Desc: 清楚当前激活的节点
+ */
+ clearActive() {
+ this.activeNodeList.forEach((item) => {
+ this.mindMap.execCommand('UPDATE_NODE_DATA', item, {
+ isActive: false
+ })
+ })
+ this.activeNodeList = []
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 13:46:08
+ * @Desc: 获取节点在同级里的索引位置
+ */
+ getNodeIndex(node) {
+ return node.parent ? node.parent.children.findIndex((item) => {
+ return item === node
+ }) : 0
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 13:19:54
+ * @Desc: 插入同级节点
+ */
+ insertNode() {
+ if (this.activeNodeList.length <= 0) {
+ return;
+ }
+ let first = this.activeNodeList[0]
+ if (first.isRoot) {
+ this.insertChildNode()
+ } else {
+ let index = this.getNodeIndex(first)
+ first.parent.originData.children.splice(index + 1, 0, {
+ "data": {
+ "text": "分支主题",
+ "expand": true
+ },
+ "children": []
+ })
+ this.mindMap.render()
+ }
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 13:31:02
+ * @Desc: 插入子节点
+ */
+ insertChildNode() {
+ if (this.activeNodeList.length <= 0) {
+ return;
+ }
+ let first = this.activeNodeList[0]
+ first.originData.children.push({
+ "data": {
+ "text": "分支主题",
+ "expand": true
+ },
+ "children": []
+ })
+ this.mindMap.render()
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 13:40:39
+ * @Desc: 移除节点
+ */
+ removeNode() {
+ if (this.activeNodeList.length <= 0) {
+ return;
+ }
+ this.activeNodeList.forEach((item) => {
+ if (item.isRoot) {
+ item.children = []
+ item.originData.children = []
+ } else {
+ let index = this.getNodeIndex(item)
+ item.parent.children.splice(index, 1)
+ item.parent.originData.children.splice(index, 1)
+ }
+ })
+ this.clearActive()
+ this.mindMap.render()
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-05-04 14:19:48
+ * @Desc: 更新节点数据
+ */
+ updateNodeData(node, data) {
+ Object.keys(data).forEach((key) => {
+ node.data[key] = data[key]
+ })
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-13 22:13:02
+ * @Desc: 显示文本编辑框
+ */
+ showEditTextBox(node, rect) {
+ if (!this.textEditNode) {
+ this.textEditNode = document.createElement('div')
+ this.textEditNode.style.cssText = `position:fixed;box-sizing: border-box;background-color:#fff;box-shadow: 0 0 20px rgba(0,0,0,.5);padding: 3px 5px;margin-left: -5px;margin-top: -3px;outline: none;`
+ this.textEditNode.setAttribute('contenteditable', true)
+ document.body.appendChild(this.textEditNode)
+ }
+ node.style.domText(this.textEditNode)
+ this.textEditNode.innerHTML = node.data.text.split(/\n/img).join('
')
+ this.textEditNode.style.minWidth = rect.width + 10 + 'px'
+ this.textEditNode.style.minHeight = rect.height + 6 + 'px'
+ this.textEditNode.style.left = rect.left + 'px'
+ this.textEditNode.style.top = rect.top + 'px'
+ this.textEditNode.style.display = 'block'
+ this.showTextEdit = true
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-24 13:48:16
+ * @Desc: 隐藏文本编辑框
+ */
+ hideEditTextBox() {
+ if (!this.showTextEdit) {
+ return
+ }
+ this.activeNodeList.forEach((node) => {
+ let str = getStrWithBrFromHtml(this.textEditNode.innerHTML)
+ node.data.text = str
+ this.mindMap.render()
+ })
+ this.mindMap.emit('hide_text_edit', this.textEditNode, this.activeNodeList)
+ this.textEditNode.style.display = 'none'
+ this.textEditNode.innerHTML = ''
+ this.textEditNode.style.fontFamily = 'inherit'
+ this.textEditNode.style.fontSize = 'inherit'
+ this.textEditNode.style.fontWeight = 'normal'
+ this.showTextEdit = false
+ }
+}
+
+export default Render
\ No newline at end of file
diff --git a/src/package/mind-map/src/Style.js b/src/package/mind-map/src/Style.js
new file mode 100644
index 00000000..006b6893
--- /dev/null
+++ b/src/package/mind-map/src/Style.js
@@ -0,0 +1,122 @@
+
+/**
+ * @Author: 王林
+ * @Date: 2021-04-11 10:09:08
+ * @Desc: 样式类
+ */
+class Style {
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-11 16:01:53
+ * @Desc: 设置背景样式
+ */
+ static setBackgroundStyle(el, themeConfig) {
+ let { backgroundColor, backgroundImage, backgroundRepeat } = themeConfig
+ el.style.backgroundColor = backgroundColor
+ if (backgroundImage) {
+ el.style.backgroundImage = `url(${backgroundImage})`
+ el.style.backgroundRepeat = backgroundRepeat
+ }
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-11 10:10:11
+ * @Desc: 构造函数
+ */
+ constructor(ctx, themeConfig) {
+ this.ctx = ctx
+ this.themeConfig = themeConfig
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-11 12:02:55
+ * @Desc: 合并样式
+ */
+ merge(prop, root, isActive) {
+ // 三级及以下节点
+ let defaultConfig = this.themeConfig.node
+ if (root) {// 直接使用最外层样式
+ defaultConfig = this.themeConfig
+ } else if (this.ctx.layerIndex === 0) {// 根节点
+ defaultConfig = this.themeConfig.root
+ } else if (this.ctx.layerIndex === 1) {// 二级节点
+ defaultConfig = this.themeConfig.secondLevel
+ }
+ // 激活状态
+ if (isActive !== undefined ? isActive : this.ctx.isActive) {
+ if (this.ctx.activeStyle && this.ctx.activeStyle[prop] !== undefined) {
+ return this.ctx.activeStyle[prop];
+ } else if (defaultConfig.active && defaultConfig.active[prop]) {
+ return defaultConfig.active[prop]
+ }
+ }
+ // 优先使用节点本身的样式
+ return this.ctx[prop] !== undefined ? this.ctx[prop] : defaultConfig[prop]
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-11 10:12:56
+ * @Desc: 矩形
+ */
+ rect(node) {
+ node.fill({
+ color: this.merge('fillColor')
+ }).stroke({
+ color: this.merge('borderColor'),
+ width: this.merge('borderWidth'),
+ dasharray: this.merge('borderDasharray')
+ }).radius(this.merge('borderRadius'))
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-11 12:07:59
+ * @Desc: 文字
+ */
+ text(node) {
+ node.fill({
+ color: this.merge('color')
+ }).css({
+ 'font-family': this.merge('fontFamily'),
+ 'font-size': this.merge('fontSize'),
+ 'font-weight': this.merge('fontWeight'),
+ 'font-style': this.merge('fontStyle'),
+ 'text-decoration': this.merge('textDecoration')
+ })
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-13 08:14:34
+ * @Desc: html文字节点
+ */
+ domText(node) {
+ node.style.fontFamily = this.merge('fontFamily')
+ node.style.fontSize = this.merge('fontSize') + 'px'
+ node.style.fontWeight = this.merge('fontWeight') || 'normal'
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-11 14:50:49
+ * @Desc: 连线
+ */
+ line(node) {
+ node.stroke({ width: this.merge('lineWidth', true), color: this.merge('lineColor', true) }).fill({ color: 'none' })
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-11 20:03:59
+ * @Desc: 按钮
+ */
+ iconBtn(node, fillNode) {
+ node.fill({ color: '#808080' })
+ fillNode.fill({ color: '#fff' })
+ }
+}
+
+export default Style
\ No newline at end of file
diff --git a/src/package/mind-map/src/Utils.js b/src/package/mind-map/src/Utils.js
new file mode 100644
index 00000000..895abd14
--- /dev/null
+++ b/src/package/mind-map/src/Utils.js
@@ -0,0 +1,134 @@
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 14:13:17
+ * @Desc: 深度优先遍历树
+ */
+export const walk = (root, parent, beforeCallback, afterCallback, isRoot, layerIndex = 0) => {
+ beforeCallback && beforeCallback(root, parent, isRoot, layerIndex)
+ if (root.children && root.children.length > 0) {
+ let _layerIndex = layerIndex + 1
+ root.children.forEach((node) => {
+ walk(node, root, beforeCallback, afterCallback, false, _layerIndex)
+ })
+ }
+ afterCallback && afterCallback(root, parent, isRoot, layerIndex)
+}
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 18:47:20
+ * @Desc: 广度优先遍历树
+ */
+export const bfsWalk = (root, callback) => {
+ callback(root)
+ let stack = [root]
+ while (stack.length) {
+ let cur = stack.shift()
+ if (cur.children && cur.children.length) {
+ cur.children.forEach((item) => {
+ stack.push(item)
+ callback(item)
+ })
+ }
+ }
+}
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-09 10:44:54
+ * @Desc: 缩放图片尺寸
+ */
+export const resizeImgSize = (width, height, maxWidth, maxHeight) => {
+ let nRatio = width / height
+ let arr = []
+ if (maxWidth && maxHeight) {
+ if (width <= maxWidth && height <= maxHeight) {
+ arr = [width, height]
+ } else {
+ let mRatio = maxWidth / maxHeight
+ if (nRatio > mRatio) { // 固定高度
+ arr = [nRatio * maxHeight, maxHeight]
+ } else { // 固定宽度
+ arr = [maxWidth, maxWidth / nRatio]
+ }
+ }
+ } else if (maxWidth) {
+ if (width <= maxWidth) {
+ arr = [width, height]
+ } else {
+ arr = [maxWidth, maxWidth / nRatio]
+ }
+ } else if (maxHeight) {
+ if (height <= maxHeight) {
+ arr = [width, height]
+ } else {
+ arr = [nRatio * maxHeight, maxHeight]
+ }
+ }
+ return arr
+}
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-09 10:18:42
+ * @Desc: 缩放图片
+ */
+export const resizeImg = (imgUrl, maxWidth, maxHeight) => {
+ return new Promise((resolve, reject) => {
+ let img = new Image()
+ img.src = imgUrl
+ img.onload = () => {
+ let arr = resizeImgSize(img.naturalWidth, img.naturalHeight, maxWidth, maxHeight)
+ resolve(arr)
+ }
+ img.onerror = (e) => {
+ reject(e)
+ }
+ })
+}
+
+/**
+ * @Author: 王林
+ * @Date: 2021-05-04 12:26:56
+ * @Desc: 从头html结构字符串里获取带换行符的字符串
+ */
+export const getStrWithBrFromHtml = (str) => {
+ str = str.replace(/
/img, '\n')
+ let el = document.createElement('div')
+ el.innerHTML = str
+ str = el.textContent
+ return str;
+}
+
+/**
+ * @Author: 王林
+ * @Date: 2021-05-04 14:45:39
+ * @Desc: 极简的深拷贝
+ */
+export const simpleDeepClone = (data) => {
+ try {
+ return JSON.parse(JSON.stringify(data))
+ } catch (error) {
+ return null
+ }
+}
+
+/**
+ * @Author: 王林
+ * @Date: 2021-05-04 14:40:11
+ * @Desc: 复制渲染树数据
+ */
+export const copyRenderTree = (tree, root) => {
+ tree.data = simpleDeepClone(root.data)
+ tree.children = []
+ if (root.children.length > 0) {
+ root.children.forEach((item, index) => {
+ tree.children[index] = copyRenderTree({}, item)
+ })
+ }
+ return tree;
+}
\ No newline at end of file
diff --git a/src/package/mind-map/src/View.js b/src/package/mind-map/src/View.js
new file mode 100644
index 00000000..981fe615
--- /dev/null
+++ b/src/package/mind-map/src/View.js
@@ -0,0 +1,87 @@
+import merge from 'deepmerge'
+
+/**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 14:45:24
+ * @Desc: 视图操作类
+ */
+class View {
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 14:45:40
+ * @Desc: 构造函数
+ */
+ constructor(opt = {}) {
+ this.opt = opt
+ this.mindMap = this.opt.mindMap
+ this.viewBox = {
+ x: 0,
+ y: 0,
+ width: this.mindMap.width,
+ height: this.mindMap.height
+ }
+ this.cacheViewBox = {
+ x: 0,
+ y: 0,
+ width: this.mindMap.width,
+ height: this.mindMap.height
+ }
+ this.scale = 1
+ this.bind()
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 15:38:51
+ * @Desc: 绑定
+ */
+ bind() {
+ // 拖动视图
+ this.mindMap.event.on('mousedown', () => {
+ this.cacheViewBox = merge({}, this.viewBox)
+ })
+ this.mindMap.event.on('drag', (e, event) => {
+ // 视图放大缩小后拖动的距离也要相应变化
+ this.viewBox.x = this.cacheViewBox.x - event.mousemoveOffset.x * this.scale
+ this.viewBox.y = this.cacheViewBox.y - event.mousemoveOffset.y * this.scale
+ this.setViewBox()
+ })
+ // 放大缩小视图
+ this.mindMap.event.on('mousewheel', (e, dir) => {
+ let stepWidth = this.viewBox.width * this.mindMap.opt.scaleRatio
+ let stepHeight = this.viewBox.height * this.mindMap.opt.scaleRatio
+ // 放大
+ if (dir === 'down') {
+ this.scale += this.mindMap.opt.scaleRatio
+ this.viewBox.width += stepWidth
+ this.viewBox.height += stepHeight
+ } else { // 缩小
+ this.scale -= this.mindMap.opt.scaleRatio
+ this.viewBox.width -= stepWidth
+ this.viewBox.height -= stepHeight
+ }
+ this.setViewBox()
+ })
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 15:43:26
+ * @Desc: 设置视图
+ */
+ setViewBox() {
+ let {
+ x,
+ y,
+ width,
+ height
+ } = this.viewBox
+ this.opt.draw.viewbox(x, y, width, height)
+ }
+}
+
+export default View
\ No newline at end of file
diff --git a/src/package/mind-map/src/layouts/Base.js b/src/package/mind-map/src/layouts/Base.js
new file mode 100644
index 00000000..f5324fdd
--- /dev/null
+++ b/src/package/mind-map/src/layouts/Base.js
@@ -0,0 +1,70 @@
+/**
+ * @Author: 王林
+ * @Date: 2021-04-12 22:24:30
+ * @Desc: 布局基类
+ */
+class Base {
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-12 22:25:16
+ * @Desc: 构造函数
+ */
+ constructor(opt) {
+ // 控制实例
+ this.mindMap = opt.mindMap
+ // 渲染实例
+ this.renderer = opt.renderer
+ // 渲染树
+ this.renderTree = opt.renderTree
+ // 主题配置
+ this.themeConfig = opt.themeConfig
+ // 绘图对象
+ this.draw = opt.draw
+ // 根节点
+ this.root = null
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-12 22:39:50
+ * @Desc: 计算节点位置
+ */
+ doLayout() {
+ throw new Error('【computed】方法为必要方法,需要子类进行重写!')
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-12 22:41:04
+ * @Desc: 连线
+ */
+ drawLine() {
+ throw new Error('【drawLine】方法为必要方法,需要子类进行重写!')
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-12 22:42:08
+ * @Desc: 定位显示展开收缩按钮
+ */
+ drawIcon() {
+ throw new Error('【drawIcon】方法为必要方法,需要子类进行重写!')
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 11:25:52
+ * @Desc: 更新子节点属性
+ */
+ updateChildren(children, prop, offset) {
+ children.forEach((item) => {
+ item[prop] += offset
+ if (item.children && item.children.length) {
+ this.updateChildren(item.children, prop, offset)
+ }
+ })
+ }
+}
+
+export default Base
\ No newline at end of file
diff --git a/src/package/mind-map/src/layouts/LogicalStructure.js b/src/package/mind-map/src/layouts/LogicalStructure.js
new file mode 100644
index 00000000..3c41ec35
--- /dev/null
+++ b/src/package/mind-map/src/layouts/LogicalStructure.js
@@ -0,0 +1,240 @@
+import Base from './Base';
+import {
+ walk
+} from '../Utils'
+import Node from '../Node'
+
+/**
+ * @Author: 王林
+ * @Date: 2021-04-12 22:25:58
+ * @Desc: 逻辑结构图
+ */
+class LogicalStructure extends Base {
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-12 22:26:31
+ * @Desc: 构造函数
+ */
+ constructor(opt = {}) {
+ super(opt)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-06 14:04:20
+ * @Desc: 布局
+ */
+ doLayout() {
+ // 计算节点的left、width、height
+ this.computedBaseValue()
+ // 计算节点的top
+ this.computedTopValue()
+ // 调整节点top
+ this.adjustTopValue()
+
+ return this.root;
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 09:49:32
+ * @Desc: 计算节点的left、width、height
+ */
+ computedBaseValue() {
+ walk(this.renderTree, null, (node, parent, isRoot, layerIndex) => {
+ // 遍历子节点前设置left、width、height
+ if (!node.data) {
+ node.data = {}
+ }
+ // 创建节点
+ let newNode = new Node({
+ uid: this.mindMap.uid++,
+ originData: node,
+ data: node.data,
+ renderer: this.renderer,
+ mindMap: this.mindMap,
+ draw: this.draw,
+ layerIndex
+ })
+ // 计算节点的宽高
+ newNode.refreshSize()
+ if (isRoot) {
+ newNode.isRoot = true
+ newNode.left = (this.mindMap.width - newNode.width) / 2
+ newNode.top = (this.mindMap.height - newNode.height) / 2
+ this.root = newNode
+ } else {
+ let marginX = layerIndex === 1 ? this.themeConfig.secondLevel.marginX : this.themeConfig.node.marginX
+ newNode.left = parent._node.left + parent._node.width + marginX,
+ newNode.parent = parent._node
+ parent._node.addChildren(newNode)
+ }
+ node._node = newNode
+ }, (node, parent, isRoot, layerIndex) => {
+ // 返回时计算节点的areaHeight,也就是子节点所占的高度之和,包括外边距
+ let len = node.expand === false ? 0 : node._node.children.length
+ node._node.childrenAreaHeight = len ? node._node.children.reduce((h, cur) => {
+ return h + cur.height
+ }, 0) + (len + 1) * this.getMarginY(layerIndex) : 0
+ }, true, 0)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 09:59:25
+ * @Desc: 计算节点的top
+ */
+ computedTopValue() {
+ walk(this.root, null, (node, parent, isRoot, layerIndex) => {
+ if (node.children && node.children.length) {
+ let marginY = this.getMarginY(layerIndex)
+ // 第一个子节点的top值 = 该节点中心的top值 - 子节点的高度之和的一半
+ let top = node.top + node.height / 2 - node.childrenAreaHeight / 2
+ let totalTop = top + marginY
+ node.children.forEach((cur) => {
+ cur.top = totalTop
+ totalTop += cur.height + marginY
+ })
+ }
+ }, null, true)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-08 10:04:05
+ * @Desc: 调整节点top
+ */
+ adjustTopValue() {
+ walk(this.root, null, (node, parent, isRoot, layerIndex) => {
+ // 判断子节点所占的高度之和是否大于该节点自身,大于则需要调整位置
+ let difference = node.childrenAreaHeight - this.getMarginY(layerIndex) - node.height
+ if (difference > 0) {
+ this.updateBrothers(node, difference / 2)
+ }
+ }, null, true)
+ }
+
+ /**
+ * javascript comment
+ * @Author: 王林25
+ * @Date: 2021-04-07 14:26:03
+ * @Desc: 更新兄弟节点的top
+ */
+ updateBrothers(node, addHeight) {
+ if (node.parent) {
+ let childrenList = node.parent.children
+ let index = childrenList.findIndex((item) => {
+ return item === node
+ })
+ childrenList.forEach((item, _index) => {
+ let _offset = 0
+ // 上面的节点往上移
+ if (_index < index) {
+ _offset = -addHeight
+ } 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)
+ }
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-11 15:34:20
+ * @Desc: 获取节点的marginY
+ */
+ getMarginY(layerIndex) {
+ return layerIndex === 1 ? this.themeConfig.secondLevel.marginY : this.themeConfig.node.marginY;
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-11 15:05:01
+ * @Desc: 二次贝塞尔曲线
+ */
+ quadraticCurvePath(x1, y1, x2, y2) {
+ let cx = x1 + (x2 - x1) * 0.2
+ let cy = y1 + (y2 - y1) * 0.8
+ return `M ${x1},${y1} Q ${cx},${cy} ${x2},${y2}`
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-11 15:05:18
+ * @Desc: 三次贝塞尔曲线
+ */
+ cubicBezierPath(x1, y1, x2, y2) {
+ let cx1 = x1 + (x2 - x1) / 2
+ let cy1 = y1
+ let cx2 = x2 - (x2 - x1) / 2
+ let cy2 = y2
+ return `M ${x1},${y1} C ${cx1},${cy1} ${cx2},${cy2} ${x2},${y2}`
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-11 14:42:48
+ * @Desc: 绘制连线,连接该节点到其子节点
+ */
+ drawLine(node) {
+ if (node.children.length <= 0) {
+ return [];
+ }
+ let {
+ left,
+ top,
+ width,
+ height
+ } = node
+ let lines = []
+ if (!node.isRoot) {
+ let line = this.draw.line(left + width, top + height / 2, left + width + 20, top + height / 2)
+ lines.push(line)
+ }
+ node.children.forEach((item) => {
+ let x1 = node.layerIndex === 0 ? left + width / 2 : left + width + 20
+ let y1 = node.layerIndex === 0 ? top + height / 2 : top + height / 2
+ let x2 = item.left
+ let y2 = item.top + item.height / 2
+ let path = ''
+ if (node.isRoot) {
+ path = this.quadraticCurvePath(x1, y1, x2, y2)
+ } else {
+ path = this.cubicBezierPath(x1, y1, x2, y2)
+ }
+ let line = this.draw.path(path)
+ lines.push(line)
+ })
+ return lines;
+ }
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-11 19:54:26
+ * @Desc: 渲染按钮
+ */
+ drawIcon(node, icons) {
+ let {
+ left,
+ top,
+ width,
+ height
+ } = node
+ icons.forEach((icon) => {
+ icon.x(left + width).y(top + height / 2)
+ })
+ }
+}
+
+export default LogicalStructure
\ No newline at end of file
diff --git a/src/package/mind-map/src/svg/btns.js b/src/package/mind-map/src/svg/btns.js
new file mode 100644
index 00000000..06af683d
--- /dev/null
+++ b/src/package/mind-map/src/svg/btns.js
@@ -0,0 +1,18 @@
+/**
+ * @Author: 王林
+ * @Date: 2021-04-11 19:46:10
+ * @Desc: 展开按钮
+ */
+const open = ``
+
+/**
+ * @Author: 王林
+ * @Date: 2021-04-11 19:46:23
+ * @Desc: 收缩按钮
+ */
+const close = ``
+
+export default {
+ open,
+ close
+}
\ No newline at end of file
diff --git a/src/package/mind-map/src/themes/blueSky.js b/src/package/mind-map/src/themes/blueSky.js
new file mode 100644
index 00000000..551ed1db
--- /dev/null
+++ b/src/package/mind-map/src/themes/blueSky.js
@@ -0,0 +1,40 @@
+import defaultTheme from './default';
+import merge from 'deepmerge';
+
+/**
+ * @Author: 王林
+ * @Date: 2021-04-11 15:22:18
+ * @Desc: 天空蓝
+ */
+export default merge(defaultTheme, {
+ // 连线的颜色
+ lineColor: 'rgb(115, 161, 191)',
+ // 背景颜色
+ backgroundColor: 'rgb(251, 251, 251)',
+ // 根节点样式
+ root: {
+ fillColor: 'rgb(115, 161, 191)',
+ active: {
+ borderColor: 'rgb(57, 80, 96)'
+ }
+ },
+ // 二级节点样式
+ secondLevel: {
+ fillColor: 'rgb(238, 243, 246)',
+ color: '#333',
+ borderColor: 'rgb(115, 161, 191)',
+ borderWidth: 1,
+ fontSize: 14,
+ active: {
+ borderColor: 'rgb(57, 80, 96)'
+ }
+ },
+ // 三级及以下节点样式
+ node: {
+ fontSize: 12,
+ color: '#333',
+ active: {
+ borderColor: 'rgb(57, 80, 96)'
+ }
+ },
+})
\ No newline at end of file
diff --git a/src/package/mind-map/src/themes/brainImpairedPink.js b/src/package/mind-map/src/themes/brainImpairedPink.js
new file mode 100644
index 00000000..1e91793c
--- /dev/null
+++ b/src/package/mind-map/src/themes/brainImpairedPink.js
@@ -0,0 +1,40 @@
+import defaultTheme from './default';
+import merge from 'deepmerge';
+
+/**
+ * @Author: 王林
+ * @Date: 2021-04-11 15:22:18
+ * @Desc: 脑残粉
+ */
+export default merge(defaultTheme, {
+ // 连线的颜色
+ lineColor: 'rgb(191, 115, 148)',
+ // 背景颜色
+ backgroundColor: 'rgb(251, 251, 251)',
+ // 根节点样式
+ root: {
+ fillColor: 'rgb(191, 115, 148)',
+ active: {
+ borderColor: 'rgb(96, 57, 74)'
+ }
+ },
+ // 二级节点样式
+ secondLevel: {
+ fillColor: 'rgb(246, 238, 242)',
+ color: '#333',
+ borderColor: 'rgb(191, 115, 148)',
+ borderWidth: 1,
+ fontSize: 14,
+ active: {
+ borderColor: 'rgb(96, 57, 74)'
+ }
+ },
+ // 三级及以下节点样式
+ node: {
+ fontSize: 12,
+ color: '#333',
+ active: {
+ borderColor: 'rgb(96, 57, 74)'
+ }
+ }
+})
\ No newline at end of file
diff --git a/src/package/mind-map/src/themes/classic.js b/src/package/mind-map/src/themes/classic.js
new file mode 100644
index 00000000..0661183c
--- /dev/null
+++ b/src/package/mind-map/src/themes/classic.js
@@ -0,0 +1,53 @@
+import defaultTheme from './default';
+import merge from 'deepmerge';
+
+/**
+ * @Author: 王林
+ * @Date: 2021-04-11 15:22:18
+ * @Desc: 脑图经典
+ */
+export default merge(defaultTheme, {
+ // 连线的颜色
+ lineColor: '#fff',
+ // 连线的粗细
+ lineWidth: 3,
+ // 背景颜色
+ backgroundColor: 'rgb(58, 65, 68)',
+ // 背景图片
+ backgroundImage: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowQzg5QTQ0NDhENzgxMUUzOENGREE4QTg0RDgzRTZDNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowQzg5QTQ0NThENzgxMUUzOENGREE4QTg0RDgzRTZDNyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkMwOEQ1NDRGOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkMwOEQ1NDUwOEQ3NzExRTM4Q0ZEQThBODREODNFNkM3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+e9P33AAAACVJREFUeNpisXJ0YUACTAyoAMr/+eM7EGGRZ4FQ7BycEAZAgAEAHbEGtkoQm/wAAAAASUVORK5CYII=',
+ // 背景重复
+ backgroundRepeat: 'repeat',
+ // 根节点样式
+ root: {
+ fillColor: 'rgb(233, 223, 152)',
+ color: '#333',
+ fontSize: 24,
+ borderRadius: 21,
+ active: {
+ fillColor: 'rgb(254, 219, 0)',
+ borderColor: 'transparent'
+ }
+ },
+ // 二级节点样式
+ secondLevel: {
+ fillColor: 'rgb(164, 197, 192)',
+ borderColor: 'transparent',
+ color: '#333',
+ fontSize: 16,
+ borderRadius: 10,
+ active: {
+ fillColor: 'rgb(254, 219, 0)',
+ borderColor: 'transparent'
+ }
+ },
+ // 三级及以下节点样式
+ node: {
+ fontSize: 12,
+ color: '#fff',
+ fontWeight: 'bold',
+ active: {
+ fillColor: 'rgb(254, 219, 0)',
+ borderColor: 'transparent'
+ }
+ }
+})
\ No newline at end of file
diff --git a/src/package/mind-map/src/themes/classic2.js b/src/package/mind-map/src/themes/classic2.js
new file mode 100644
index 00000000..a2841006
--- /dev/null
+++ b/src/package/mind-map/src/themes/classic2.js
@@ -0,0 +1,45 @@
+import defaultTheme from './default';
+import merge from 'deepmerge';
+
+/**
+ * @Author: 王林
+ * @Date: 2021-04-11 15:22:18
+ * @Desc: 经典2
+ */
+export default merge(defaultTheme, {
+ // 连线的颜色
+ lineColor: 'rgb(51, 51, 51)',
+ // 连线的粗细
+ lineWidth: 2,
+ // 背景颜色
+ backgroundColor: '#fff',
+ // 根节点样式
+ root: {
+ fillColor: 'rgb(18, 187, 55)',
+ color: '#fff',
+ fontSize: 24,
+ borderRadius: 10,
+ active: {
+ borderColor: 'rgb(51, 51, 51)'
+ }
+ },
+ // 二级节点样式
+ secondLevel: {
+ fillColor: 'rgb(241, 242, 241)',
+ borderColor: 'transparent',
+ color: '#1a1a1a',
+ fontSize: 18,
+ borderRadius: 10,
+ active: {
+ borderColor: 'rgb(51, 51, 51)'
+ }
+ },
+ // 三级及以下节点样式
+ node: {
+ fontSize: 14,
+ color: '#1a1a1a',
+ active: {
+ borderColor: 'rgb(51, 51, 51)'
+ }
+ }
+})
\ No newline at end of file
diff --git a/src/package/mind-map/src/themes/classic3.js b/src/package/mind-map/src/themes/classic3.js
new file mode 100644
index 00000000..a14184a3
--- /dev/null
+++ b/src/package/mind-map/src/themes/classic3.js
@@ -0,0 +1,48 @@
+import defaultTheme from './default';
+import merge from 'deepmerge';
+
+/**
+ * @Author: 王林
+ * @Date: 2021-04-11 15:22:18
+ * @Desc: 经典3
+ */
+export default merge(defaultTheme, {
+ // 连线的颜色
+ lineColor: 'rgb(94, 202, 110)',
+ // 连线的粗细
+ lineWidth: 2,
+ // 背景颜色
+ backgroundColor: 'rgb(241, 241, 241)',
+ // 根节点样式
+ root: {
+ fillColor: 'rgb(255, 245, 214)',
+ color: '#1a1a1a',
+ fontSize: 24,
+ borderRadius: 10,
+ borderColor: 'rgb(249, 199, 84)',
+ borderWidth: 1,
+ active: {
+ borderColor: 'rgb(94, 202, 110)'
+ }
+ },
+ // 二级节点样式
+ secondLevel: {
+ fillColor: 'rgb(255, 245, 214)',
+ borderColor: 'rgb(249, 199, 84)',
+ borderWidth: 1,
+ color: '#1a1a1a',
+ fontSize: 18,
+ borderRadius: 10,
+ active: {
+ borderColor: 'rgb(94, 202, 110)'
+ }
+ },
+ // 三级及以下节点样式
+ node: {
+ fontSize: 14,
+ color: '#1a1a1a',
+ active: {
+ borderColor: 'rgb(94, 202, 110)'
+ }
+ }
+})
\ No newline at end of file
diff --git a/src/package/mind-map/src/themes/dark.js b/src/package/mind-map/src/themes/dark.js
new file mode 100644
index 00000000..8c4faf86
--- /dev/null
+++ b/src/package/mind-map/src/themes/dark.js
@@ -0,0 +1,45 @@
+import defaultTheme from './default';
+import merge from 'deepmerge';
+
+/**
+ * @Author: 王林
+ * @Date: 2021-04-11 15:22:18
+ * @Desc: 暗色
+ */
+export default merge(defaultTheme, {
+ // 连线的颜色
+ lineColor: 'rgb(17, 68, 23)',
+ // 连线的粗细
+ lineWidth: 2,
+ // 背景颜色
+ backgroundColor: 'rgb(15, 16, 17)',
+ // 根节点样式
+ root: {
+ fillColor: 'rgb(28, 178, 43)',
+ color: '#fff',
+ fontSize: 24,
+ borderRadius: 10,
+ active: {
+ borderColor: 'rgb(17, 68, 23)'
+ }
+ },
+ // 二级节点样式
+ secondLevel: {
+ fillColor: 'rgb(55, 56, 58)',
+ color: 'rgb(147,148,149)',
+ fontSize: 18,
+ borderRadius: 10,
+ borderWidth: 0,
+ active: {
+ borderColor: 'rgb(17, 68, 23)'
+ }
+ },
+ // 三级及以下节点样式
+ node: {
+ fontSize: 14,
+ color: 'rgb(147, 148, 149)',
+ active: {
+ borderColor: 'rgb(17, 68, 23)'
+ }
+ }
+})
\ No newline at end of file
diff --git a/src/package/mind-map/src/themes/default.js b/src/package/mind-map/src/themes/default.js
new file mode 100644
index 00000000..7a3da117
--- /dev/null
+++ b/src/package/mind-map/src/themes/default.js
@@ -0,0 +1,87 @@
+/**
+ * @Author: 王林
+ * @Date: 2021-04-11 10:19:55
+ * @Desc: 默认主题
+ */
+export default {
+ // 节点内边距
+ paddingX: 20,
+ paddingY: 10,
+ // 图片显示的最大宽度
+ imgMaxWidth: 200,
+ // 图片显示的最大高度
+ imgMaxHeight: 200,
+ // icon的大小
+ iconSize: 20,
+ // 连线的粗细
+ lineWidth: 1,
+ // 连线的颜色
+ lineColor: '#549688',
+ // 背景颜色
+ backgroundColor: '#fafafa',
+ // 背景图片
+ backgroundImage: '',
+ // 背景重复
+ backgroundRepeat: 'none',
+ // 根节点样式
+ root: {
+ fillColor: '#549688',
+ fontFamily: '微软雅黑, Microsoft YaHei',
+ color: '#fff',
+ fontSize: 16,
+ fontWeight: 'bold',
+ fontStyle: 'normal',
+ borderColor: 'transparent',
+ borderWidth: 0,
+ borderDasharray: 'none',
+ borderRadius: 5,
+ textDecoration: 'none',
+ active: {
+ borderColor: 'rgb(57, 80, 96)',
+ borderWidth: 3,
+ borderDasharray: 'none',
+ }
+ },
+ // 二级节点样式
+ secondLevel: {
+ marginX: 100,
+ marginY: 40,
+ fillColor: '#fff',
+ fontFamily: '微软雅黑, Microsoft YaHei',
+ color: '#565656',
+ fontSize: 16,
+ fontWeight: 'noraml',
+ fontStyle: 'normal',
+ borderColor: '#549688',
+ borderWidth: 1,
+ borderDasharray: 'none',
+ borderRadius: 5,
+ textDecoration: 'none',
+ active: {
+ borderColor: 'rgb(57, 80, 96)',
+ borderWidth: 3,
+ borderDasharray: 'none',
+ }
+ },
+ // 三级及以下节点样式
+ node: {
+ marginX: 60,
+ marginY: 40,
+ fillColor: 'transparent',
+ fontFamily: '微软雅黑, Microsoft YaHei',
+ color: '#6a6d6c',
+ fontSize: 14,
+ fontWeight: 'noraml',
+ fontStyle: 'normal',
+ borderColor: 'transparent',
+ borderWidth: 0,
+ borderRadius: 5,
+ borderDasharray: 'none',
+ textDecoration: 'none',
+ active: {
+ borderColor: 'rgb(57, 80, 96)',
+ borderWidth: 3,
+ borderDasharray: 'none',
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/package/mind-map/src/themes/earthYellow.js b/src/package/mind-map/src/themes/earthYellow.js
new file mode 100644
index 00000000..c7e906f7
--- /dev/null
+++ b/src/package/mind-map/src/themes/earthYellow.js
@@ -0,0 +1,40 @@
+import defaultTheme from './default';
+import merge from 'deepmerge';
+
+/**
+ * @Author: 王林
+ * @Date: 2021-04-11 15:22:18
+ * @Desc: 泥土黄
+ */
+export default merge(defaultTheme, {
+ // 连线的颜色
+ lineColor: 'rgb(191, 147, 115)',
+ // 背景颜色
+ backgroundColor: 'rgb(251, 251, 251)',
+ // 根节点样式
+ root: {
+ fillColor: 'rgb(191, 147, 115)',
+ active: {
+ borderColor: 'rgb(96, 73, 57)'
+ }
+ },
+ // 二级节点样式
+ secondLevel: {
+ fillColor: 'rgb(246, 242, 238)',
+ color: '#333',
+ borderColor: 'rgb(191, 147, 115)',
+ borderWidth: 1,
+ fontSize: 14,
+ active: {
+ borderColor: 'rgb(96, 73, 57)'
+ }
+ },
+ // 三级及以下节点样式
+ node: {
+ fontSize: 12,
+ color: '#333',
+ active: {
+ borderColor: 'rgb(96, 73, 57)'
+ }
+ }
+})
\ No newline at end of file
diff --git a/src/package/mind-map/src/themes/freshGreen.js b/src/package/mind-map/src/themes/freshGreen.js
new file mode 100644
index 00000000..5db06ac1
--- /dev/null
+++ b/src/package/mind-map/src/themes/freshGreen.js
@@ -0,0 +1,25 @@
+import defaultTheme from './default';
+import merge from 'deepmerge';
+
+/**
+ * @Author: 王林
+ * @Date: 2021-04-11 15:22:18
+ * @Desc: 清新绿
+ */
+export default merge(defaultTheme, {
+ // 连线的颜色
+ lineColor: '#333',
+ // 背景颜色
+ backgroundColor: '#d1f6ec',
+ // 根节点样式
+ root: {
+ fillColor: '#1fb27d'
+ },
+ // 二级节点样式
+ secondLevel: {
+ fillColor: '#fff',
+ color: '#565656',
+ borderColor: 'transparent',
+ borderWidth: 0
+ },
+})
\ No newline at end of file
diff --git a/src/package/mind-map/src/themes/freshRed.js b/src/package/mind-map/src/themes/freshRed.js
new file mode 100644
index 00000000..ec3a754b
--- /dev/null
+++ b/src/package/mind-map/src/themes/freshRed.js
@@ -0,0 +1,40 @@
+import defaultTheme from './default';
+import merge from 'deepmerge';
+
+/**
+ * @Author: 王林
+ * @Date: 2021-04-11 15:22:18
+ * @Desc: 清新红
+ */
+export default merge(defaultTheme, {
+ // 连线的颜色
+ lineColor: 'rgb(191, 115, 115)',
+ // 背景颜色
+ backgroundColor: 'rgb(251, 251, 251)',
+ // 根节点样式
+ root: {
+ fillColor: 'rgb(191, 115, 115)',
+ active: {
+ borderColor: 'rgb(96, 57, 57)'
+ }
+ },
+ // 二级节点样式
+ secondLevel: {
+ fillColor: 'rgb(246, 238, 238)',
+ color: '#333',
+ borderColor: 'rgb(191, 115, 115)',
+ borderWidth: 1,
+ fontSize: 14,
+ active: {
+ borderColor: 'rgb(96, 57, 57)'
+ }
+ },
+ // 三级及以下节点样式
+ node: {
+ fontSize: 12,
+ color: '#333',
+ active: {
+ borderColor: 'rgb(96, 57, 57)'
+ }
+ }
+})
\ No newline at end of file
diff --git a/src/package/mind-map/src/themes/index.js b/src/package/mind-map/src/themes/index.js
new file mode 100644
index 00000000..60fd2042
--- /dev/null
+++ b/src/package/mind-map/src/themes/index.js
@@ -0,0 +1,25 @@
+import defaultTheme from './default';
+import freshGreen from './freshGreen';
+import blueSky from './blueSky';
+import brainImpairedPink from './brainImpairedPink';
+import romanticPurple from './romanticPurple';
+import freshRed from './freshRed';
+import earthYellow from './earthYellow';
+import classic from './classic';
+import classic2 from './classic2';
+import classic3 from './classic3';
+import dark from './dark';
+
+export default {
+ default: defaultTheme,
+ freshGreen,
+ blueSky,
+ brainImpairedPink,
+ romanticPurple,
+ freshRed,
+ earthYellow,
+ classic,
+ classic2,
+ classic3,
+ dark
+}
\ No newline at end of file
diff --git a/src/package/mind-map/src/themes/romanticPurple.js b/src/package/mind-map/src/themes/romanticPurple.js
new file mode 100644
index 00000000..ce8b045b
--- /dev/null
+++ b/src/package/mind-map/src/themes/romanticPurple.js
@@ -0,0 +1,40 @@
+import defaultTheme from './default';
+import merge from 'deepmerge';
+
+/**
+ * @Author: 王林
+ * @Date: 2021-04-11 15:22:18
+ * @Desc: 浪漫紫
+ */
+export default merge(defaultTheme, {
+ // 连线的颜色
+ lineColor: 'rgb(123, 115, 191)',
+ // 背景颜色
+ backgroundColor: 'rgb(251, 251, 251)',
+ // 根节点样式
+ root: {
+ fillColor: 'rgb(123, 115, 191)',
+ active: {
+ borderColor: 'rgb(61, 57, 96)'
+ }
+ },
+ // 二级节点样式
+ secondLevel: {
+ fillColor: 'rgb(239, 238, 246)',
+ color: '#333',
+ borderColor: 'rgb(123, 115, 191)',
+ borderWidth: 1,
+ fontSize: 14,
+ active: {
+ borderColor: 'rgb(61, 57, 96)'
+ }
+ },
+ // 三级及以下节点样式
+ node: {
+ fontSize: 12,
+ color: '#333',
+ active: {
+ borderColor: 'rgb(61, 57, 96)'
+ }
+ }
+})
\ No newline at end of file
diff --git a/src/package/mind-map/src/utils/keyMap.js b/src/package/mind-map/src/utils/keyMap.js
new file mode 100644
index 00000000..2c3ce920
--- /dev/null
+++ b/src/package/mind-map/src/utils/keyMap.js
@@ -0,0 +1,120 @@
+const map = {
+ 'Backspace': 8,
+ 'Tab': 9,
+ 'Enter': 13,
+
+ 'Shift': 16,
+ 'Control': 17,
+ 'Alt': 18,
+ 'CapsLock': 20,
+
+ 'Esc': 27,
+
+ 'Spacebar': 32,
+
+ 'PageUp': 33,
+ 'PageDown': 34,
+ 'End': 35,
+ 'Home': 36,
+
+ 'Insert': 45,
+
+ 'Left': 37,
+ 'Up': 38,
+ 'Right': 39,
+ 'Down': 40,
+
+ 'Del': 46,
+
+ 'NumLock': 144,
+
+ 'Cmd': 91,
+ 'CmdFF': 224,
+ 'F1': 112,
+ 'F2': 113,
+ 'F3': 114,
+ 'F4': 115,
+ 'F5': 116,
+ 'F6': 117,
+ 'F7': 118,
+ 'F8': 119,
+ 'F9': 120,
+ 'F10': 121,
+ 'F11': 122,
+ 'F12': 123,
+
+ '`': 192,
+ '=': 187,
+ '-': 189,
+
+ '/': 191,
+ '.': 190,
+
+ 'direction': {
+ 37: 1,
+ 38: 1,
+ 39: 1,
+ 40: 1
+ },
+
+ controlKeys: {
+ 16: 1,
+ 17: 1,
+ 18: 1,
+ 20: 1,
+ 91: 1,
+ 224: 1
+ },
+ 'notContentChange': {
+ 13: 1,
+ 9: 1,
+
+ 33: 1,
+ 34: 1,
+ 35: 1,
+ 36: 1,
+
+ 16: 1,
+ 17: 1,
+ 18: 1,
+ 20: 1,
+ 91: 1,
+
+ //上下左右
+ 37: 1,
+ 38: 1,
+ 39: 1,
+ 40: 1,
+
+ 113: 1,
+ 114: 1,
+ 115: 1,
+ 144: 1,
+ 27: 1
+ },
+ 'isSelectedNodeKey': {
+ //上下左右
+ 37: 1,
+ 38: 1,
+ 39: 1,
+ 40: 1,
+ 13: 1,
+ 9: 1
+ }
+}
+// 数字
+for (let i = 0; i <= 9; i++) {
+ map[i] = i + 48
+}
+
+// 字母
+'abcdefghijklmnopqrstuvwxyz'.split('').forEach((n, index) => {
+ map[n] = index + 65
+})
+
+export const keyMap = map
+
+export const isKey = (e, key) => {
+ let code = typeof e === 'object' ? e.keyCode : e
+ return map[key] === code
+}
\ No newline at end of file
diff --git a/src/pages/Edit/Index.vue b/src/pages/Edit/Index.vue
new file mode 100644
index 00000000..969c976b
--- /dev/null
+++ b/src/pages/Edit/Index.vue
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/Edit/components/BaseStyle.vue b/src/pages/Edit/components/BaseStyle.vue
new file mode 100644
index 00000000..28fd3a7b
--- /dev/null
+++ b/src/pages/Edit/components/BaseStyle.vue
@@ -0,0 +1,236 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/Edit/components/Color.vue b/src/pages/Edit/components/Color.vue
new file mode 100644
index 00000000..095d81af
--- /dev/null
+++ b/src/pages/Edit/components/Color.vue
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
diff --git a/src/pages/Edit/components/Edit.vue b/src/pages/Edit/components/Edit.vue
new file mode 100644
index 00000000..aed6f328
--- /dev/null
+++ b/src/pages/Edit/components/Edit.vue
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
diff --git a/src/pages/Edit/components/NodeIcon.vue b/src/pages/Edit/components/NodeIcon.vue
new file mode 100644
index 00000000..e69de29b
diff --git a/src/pages/Edit/components/NodeImage.vue b/src/pages/Edit/components/NodeImage.vue
new file mode 100644
index 00000000..a8f6e698
--- /dev/null
+++ b/src/pages/Edit/components/NodeImage.vue
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/pages/Edit/components/Outline.vue b/src/pages/Edit/components/Outline.vue
new file mode 100644
index 00000000..77866dcc
--- /dev/null
+++ b/src/pages/Edit/components/Outline.vue
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/Edit/components/Sidebar.vue b/src/pages/Edit/components/Sidebar.vue
new file mode 100644
index 00000000..bbadf390
--- /dev/null
+++ b/src/pages/Edit/components/Sidebar.vue
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
diff --git a/src/pages/Edit/components/Style.vue b/src/pages/Edit/components/Style.vue
new file mode 100644
index 00000000..5bd6284e
--- /dev/null
+++ b/src/pages/Edit/components/Style.vue
@@ -0,0 +1,441 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/Edit/components/Toolbar.vue b/src/pages/Edit/components/Toolbar.vue
new file mode 100644
index 00000000..dbb312c3
--- /dev/null
+++ b/src/pages/Edit/components/Toolbar.vue
@@ -0,0 +1,155 @@
+
+
+
+
+
+
+
diff --git a/src/pages/Index/Index.vue b/src/pages/Index/Index.vue
new file mode 100644
index 00000000..308dabc0
--- /dev/null
+++ b/src/pages/Index/Index.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
diff --git a/src/router.js b/src/router.js
new file mode 100644
index 00000000..54fba037
--- /dev/null
+++ b/src/router.js
@@ -0,0 +1,18 @@
+import Vue from 'vue'
+import VueRouter from 'vue-router'
+
+import IndexPage from '@/pages/Index/Index'
+import EditPage from '@/pages/Edit/Index'
+
+Vue.use(VueRouter)
+
+const routes = [
+ { path: '/', name: 'Index', component: IndexPage },
+ { path: '/edit/:id', name: 'Edit', component: EditPage }
+]
+
+const router = new VueRouter({
+ routes
+})
+
+export default router
\ No newline at end of file
diff --git a/src/store.js b/src/store.js
new file mode 100644
index 00000000..b8baeb93
--- /dev/null
+++ b/src/store.js
@@ -0,0 +1,68 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+import exampleData from './package/mind-map/example/exampleData';
+
+Vue.use(Vuex)
+
+const store = new Vuex.Store({
+ state: {
+ userInfo: null,// 用户信息
+ mindMapData: null// 思维导图数据
+ },
+ mutations: {
+ /**
+ * @Author: 王林
+ * @Date: 2020-11-28 15:32:32
+ * @Desc: 设置用户信息
+ */
+ setUserInfo(state, userInfo) {
+ state.userInfo = userInfo
+ },
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-10 14:50:01
+ * @Desc: 设置思维导图数据
+ */
+ setMindMapData(state, data) {
+ state.mindMapData = data
+ }
+ },
+ actions: {
+ /**
+ * @Author: 王林
+ * @Date: 2020-11-28 15:28:03
+ * @Desc: 获取用户信息
+ */
+ async getUserInfo(ctx) {
+ try {
+ let { data } = await api.getUserInfo()
+ ctx.commit('setUserInfo', data.data)
+ } catch (error) {
+ console.log(error)
+ }
+ },
+
+ /**
+ * @Author: 王林
+ * @Date: 2021-04-10 14:50:40
+ * @Desc: 获取思维导图数据
+ */
+ async getUserMindMapData(ctx) {
+ try {
+ let { data } = {
+ data: {
+ data: {
+ mindMapData: exampleData
+ }
+ }
+ }
+ ctx.commit('setMindMapData', data.data)
+ } catch (error) {
+ console.log(error)
+ }
+ }
+ }
+})
+
+export default store
\ No newline at end of file
diff --git a/src/utils/index.js b/src/utils/index.js
new file mode 100644
index 00000000..e69de29b
diff --git a/vue.config.js b/vue.config.js
new file mode 100644
index 00000000..17523762
--- /dev/null
+++ b/vue.config.js
@@ -0,0 +1,13 @@
+const path = require('path');
+
+module.exports = {
+ lintOnSave: false,
+ productionSourceMap: false,
+ configureWebpack: {
+ resolve: {
+ alias: {
+ '@': path.resolve(__dirname, './src/')
+ }
+ }
+ }
+}
\ No newline at end of file