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 + + + + + + + + + + + + + +
+

+ + +

+ +
+
+
    + +
  • + +
    超链接
    +
    &#xe6f4;
    +
  • + +
  • + +
    主题
    +
    &#xe610;
    +
  • + +
  • + +
    笑脸
    +
    &#xe60f;
    +
  • + +
  • + +
    图 片
    +
    &#xe629;
    +
  • + +
  • + +
    结构
    +
    &#xe61d;
    +
  • + +
  • + +
    样式
    +
    &#xe631;
    +
  • + +
  • + +
    符号-大纲树
    +
    &#xe71f;
    +
  • + +
  • + +
    添加子节点
    +
    &#xe622;
    +
  • + +
  • + +
    节点
    +
    &#xe655;
    +
  • + +
  • + +
    删 除
    +
    &#xe696;
    +
  • + +
  • + +
    HTSCIT_展开
    +
    &#xe64c;
    +
  • + +
  • + +
    HTSCIT_展开2
    +
    &#xe673;
    +
  • + +
+
+

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">&#x33;</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