完成节点内容设置及主题

This commit is contained in:
wanglin
2021-06-26 00:01:03 +08:00
parent 1528c3d64b
commit b0929da054
25 changed files with 1215 additions and 168 deletions

BIN
web/src/.DS_Store vendored

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -54,6 +54,24 @@
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xe63d;</span>
<div class="name">导出</div>
<div class="code-name">&amp;#xe63d;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe63c;</span>
<div class="name">标签</div>
<div class="code-name">&amp;#xe63c;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe65b;</span>
<div class="name">流程-备注</div>
<div class="code-name">&amp;#xe65b;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe6f4;</span>
<div class="name">超链接</div>
@@ -144,9 +162,9 @@
<pre><code class="language-css"
>@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');
src: url('iconfont.woff2?t=1624540474886') format('woff2'),
url('iconfont.woff?t=1624540474886') format('woff'),
url('iconfont.ttf?t=1624540474886') format('truetype');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -172,6 +190,33 @@
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont icondaochu"></span>
<div class="name">
导出
</div>
<div class="code-name">.icondaochu
</div>
</li>
<li class="dib">
<span class="icon iconfont iconbiaoqian"></span>
<div class="name">
标签
</div>
<div class="code-name">.iconbiaoqian
</div>
</li>
<li class="dib">
<span class="icon iconfont iconflow-Mark"></span>
<div class="name">
流程-备注
</div>
<div class="code-name">.iconflow-Mark
</div>
</li>
<li class="dib">
<span class="icon iconfont iconchaolianjie"></span>
<div class="name">
@@ -307,6 +352,30 @@
<div class="content symbol">
<ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icondaochu"></use>
</svg>
<div class="name">导出</div>
<div class="code-name">#icondaochu</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconbiaoqian"></use>
</svg>
<div class="name">标签</div>
<div class="code-name">#iconbiaoqian</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconflow-Mark"></use>
</svg>
<div class="name">流程-备注</div>
<div class="code-name">#iconflow-Mark</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#iconchaolianjie"></use>

View File

@@ -1,8 +1,8 @@
@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');
src: url('iconfont.woff2?t=1624540474886') format('woff2'),
url('iconfont.woff?t=1624540474886') format('woff'),
url('iconfont.ttf?t=1624540474886') format('truetype');
}
.iconfont {
@@ -13,6 +13,18 @@
-moz-osx-font-smoothing: grayscale;
}
.icondaochu:before {
content: "\e63d";
}
.iconbiaoqian:before {
content: "\e63c";
}
.iconflow-Mark:before {
content: "\e65b";
}
.iconchaolianjie:before {
content: "\e6f4";
}

File diff suppressed because one or more lines are too long

View File

@@ -5,6 +5,27 @@
"css_prefix_text": "icon",
"description": "思维导图",
"glyphs": [
{
"icon_id": "788015",
"name": "导出",
"font_class": "daochu",
"unicode": "e63d",
"unicode_decimal": 58941
},
{
"icon_id": "2678575",
"name": "标签",
"font_class": "biaoqian",
"unicode": "e63c",
"unicode_decimal": 58940
},
{
"icon_id": "6265396",
"name": "流程-备注",
"font_class": "flow-Mark",
"unicode": "e65b",
"unicode_decimal": 58971
},
{
"icon_id": "1790486",
"name": "超链接",

View File

@@ -1,16 +1,14 @@
<template>
<div class="imgUploadContainer">
<div class="imgUploadPanel">
<div class="upBtn" v-if="!previewSrc">
<div class="upBtn" v-if="!value">
<label
for="imgUploadInput"
class="imgUploadInputArea"
@dragenter.stop.prevent
@dragover.stop.prevent
@drop.stop.prevent="onDrop"
v-loading="loading"
element-loading-text="上传中..."
>点击此处添加图片或拖动图片到此</label
>点击此处选择图片或拖动图片到此</label
>
<input
type="file"
@@ -19,10 +17,10 @@
@change="onImgUploadInputChange"
/>
</div>
<div v-if="previewSrc" class="uploadInfoBox">
<div v-if="value" class="uploadInfoBox">
<div
class="previewBox"
:style="{ backgroundImage: `url('${previewSrc}')` }"
:style="{ backgroundImage: `url('${value}')` }"
></div>
<span class="delBtn el-icon-close" @click="deleteImg"></span>
</div>
@@ -33,11 +31,19 @@
<script>
export default {
name: "ImgUpload",
model: {
prop: "value",
event: "change",
},
props: {
value: {
type: String,
default: "",
},
},
data() {
return {
file: null,
previewSrc: "",
loading: false,
};
},
methods: {
@@ -48,7 +54,7 @@ export default {
*/
onImgUploadInputChange(e) {
let file = e.target.files[0];
this.uploadImg(file);
this.selectImg(file);
},
/**
@@ -59,40 +65,42 @@ export default {
onDrop(e) {
let dt = e.dataTransfer;
let file = dt.files && dt.files[0];
this.uploadImg(file);
this.selectImg(file);
},
/**
* @Author: 王林
* @Date: 2021-06-06 16:56:14
* @Desc: 显示图片
* @Desc: 选择图片
*/
showImg(file) {
selectImg(file) {
this.file = file;
let fr = new FileReader();
fr.readAsDataURL(file);
fr.onload = (e) => {
this.previewSrc = e.target.result;
this.$emit("change", e.target.result);
};
},
/**
* @Author: 王林
* @Date: 2019-12-22 19:51:13
* @Desc: 上传图片
* @Date: 2021-06-22 23:03:46
* @Desc: 获取图片大小
*/
async uploadImg(file) {
this.loading = true;
},
/**
* @Author: 王林
* @Date: 2021-06-06 22:33:28
* @Desc: 获取图片url
*/
getUrl() {
return this.this.previewSrc;
getSize() {
return new Promise((resolve, reject) => {
let img = new Image();
img.src = this.value;
img.onload = () => {
resolve({
width: img.width,
height: img.height,
});
};
img.onerror = (e) => {
reject(e);
};
});
},
/**
@@ -101,7 +109,7 @@ export default {
* @Desc: 删除图片
*/
deleteImg() {
this.previewSrc = "";
this.$emit("change", "none");
this.file = null;
},
},

View File

@@ -125,4 +125,84 @@ export const borderDasharrayList = [
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]
export const lineWidthList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// 图片重复方式
export const backgroundRepeatList = [
{
name: '不重复',
value: 'no-repeat'
},
{
name: '重复',
value: 'repeat'
},
{
name: '水平方向重复',
value: 'repeat-x'
},
{
name: '垂直方向重复',
value: 'repeat-y'
}
]
// 背景图片大小
export const backgroundSizeList = [
{
name: '自动',
value: 'auto'
},
{
name: '完全覆盖',
value: 'cover'
},
{
name: '最合适',
value: 'contain'
}
]
// 背景图片定位
export const backgroundPositionList = [
{
name: '默认',
value: '0% 0%'
},
{
name: '左上',
value: 'left top'
},
{
name: '左中',
value: 'left center'
},
{
name: '左下',
value: 'left bottom'
},
{
name: '右上',
value: 'right top'
},
{
name: '右中',
value: 'right center'
},
{
name: '右下',
value: 'right bottom'
},
{
name: '中上',
value: 'center top'
},
{
name: '居中',
value: 'center center'
},
{
name: '中下',
value: 'center bottom'
}
]

View File

@@ -1,17 +1,100 @@
<template>
<Sidebar ref="sidebar" title="基础样式">
<div class="sidebarContent" v-if="data">
<!-- 背景 -->
<div class="title noTop">背景</div>
<div class="row">
<Color
:color="style.backgroundColor"
@change="
(color) => {
update('backgroundColor', color);
}
"
></Color>
<el-tabs class="tab" v-model="activeTab">
<el-tab-pane label="颜色" name="color">
<Color
:color="style.backgroundColor"
@change="
(color) => {
update('backgroundColor', color);
}
"
></Color>
</el-tab-pane>
<el-tab-pane label="图片" name="image">
<ImgUpload
class="imgUpload"
v-model="style.backgroundImage"
@change="
(img) => {
update('backgroundImage', img);
}
"
></ImgUpload>
<div class="rowItem">
<span class="name">图片重复</span>
<el-select
size="mini"
style="width: 80px"
v-model="style.backgroundRepeat"
placeholder=""
@change="
(value) => {
update('backgroundRepeat', value);
}
"
>
<el-option
v-for="item in backgroundRepeatList"
:key="item.value"
:label="item.name"
:value="item.value"
>
</el-option>
</el-select>
</div>
<div class="rowItem">
<span class="name">图片大小</span>
<el-select
size="mini"
style="width: 80px"
v-model="style.backgroundSize"
placeholder=""
@change="
(value) => {
update('backgroundSize', value);
}
"
>
<el-option
v-for="item in backgroundSizeList"
:key="item.value"
:label="item.name"
:value="item.value"
>
</el-option>
</el-select>
</div>
<div class="rowItem">
<span class="name">图片定位</span>
<el-select
size="mini"
style="width: 80px"
v-model="style.backgroundPosition"
placeholder=""
@change="
(value) => {
update('backgroundPosition', value);
}
"
>
<el-option
v-for="item in backgroundPositionList"
:key="item.value"
:label="item.name"
:value="item.value"
>
</el-option>
</el-select>
</div>
</el-tab-pane>
</el-tabs>
</div>
<!-- 连线 -->
<div class="title noTop">连线</div>
<div class="row">
<div class="rowItem">
@@ -55,12 +138,13 @@
</el-select>
</div>
</div>
<!-- 内边距 -->
<div class="title noTop">节点内边距</div>
<div class="row">
<div class="rowItem">
<span class="name">水平</span>
<el-slider
style="width: 230px"
style="width: 210px"
v-model="style.paddingX"
@change="
(value) => {
@@ -74,7 +158,7 @@
<div class="rowItem">
<span class="name">垂直</span>
<el-slider
style="width: 230px"
style="width: 210px"
v-model="style.paddingY"
@change="
(value) => {
@@ -84,6 +168,58 @@
></el-slider>
</div>
</div>
<!-- 图片 -->
<div class="title noTop">图片</div>
<div class="row">
<div class="rowItem">
<span class="name">显示的最大宽度</span>
<el-slider
style="width: 150px"
v-model="style.imgMaxWidth"
:min="10"
:max="300"
@change="
(value) => {
update('imgMaxWidth', value);
}
"
></el-slider>
</div>
</div>
<div class="row">
<div class="rowItem">
<span class="name">显示的最大高度</span>
<el-slider
style="width: 150px"
v-model="style.imgMaxHeight"
:min="10"
:max="300"
@change="
(value) => {
update('imgMaxHeight', value);
}
"
></el-slider>
</div>
</div>
<!-- 图标 -->
<div class="title noTop">图标</div>
<div class="row">
<div class="rowItem">
<span class="name">大小</span>
<el-slider
style="width: 210px"
v-model="style.iconSize"
:min="12"
:max="50"
@change="
(value) => {
update('iconSize', value);
}
"
></el-slider>
</div>
</div>
</div>
</Sidebar>
</template>
@@ -91,13 +227,20 @@
<script>
import Sidebar from "./Sidebar";
import Color from "./Color";
import { lineWidthList } from "@/config";
import { lineWidthList, backgroundRepeatList, backgroundSizeList, backgroundPositionList } from "@/config";
import ImgUpload from "@/components/ImgUpload";
/**
* @Author: 王林
* @Date: 2021-06-24 22:52:56
* @Desc: 基础样式
*/
export default {
name: "BaseStyle",
components: {
Sidebar,
Color,
ImgUpload,
},
props: {
data: {
@@ -111,17 +254,28 @@ export default {
data() {
return {
lineWidthList,
backgroundRepeatList,
backgroundSizeList,
backgroundPositionList,
activeTab: "color",
style: {
backgroundColor: "",
lineColor: "",
lineWidth: "",
paddingX: 0,
paddingY: 0,
imgMaxWidth: 0,
imgMaxHeight: 0,
iconSize: 0,
backgroundImage: "",
backgroundRepeat: "no-repeat",
backgroundSize: 'auto',
backgroundPosition: '0% 0%'
},
};
},
created() {
this.$bus.$on("showTheme", () => {
this.$bus.$on("showBaseStyle", () => {
this.$refs.sidebar.show = true;
this.initStyle();
});
@@ -139,6 +293,13 @@ export default {
"lineColor",
"paddingX",
"paddingY",
"imgMaxWidth",
"imgMaxHeight",
"iconSize",
"backgroundImage",
"backgroundRepeat",
"backgroundSize",
"backgroundPosition"
].forEach((key) => {
this.style[key] = this.mindMap.getThemeConfig(key);
});
@@ -181,6 +342,14 @@ export default {
justify-content: space-between;
margin-bottom: 10px;
.tab {
width: 100%;
}
.imgUpload {
margin-bottom: 5px;
}
.btnGroup {
width: 100%;
display: flex;
@@ -190,6 +359,7 @@ export default {
.rowItem {
display: flex;
align-items: center;
margin-bottom: 5px;
.name {
font-size: 12px;

View File

@@ -23,6 +23,11 @@
<script>
import { colorList } from "@/config";
/**
* @Author: 王林
* @Date: 2021-06-24 22:53:10
* @Desc: 颜色选择器
*/
export default {
name: "Color",
props: {

View File

@@ -8,33 +8,41 @@
:mindMap="mindMap"
@change="changeThemeConfig"
></BaseStyle>
<Theme :mindMap="mindMap"></Theme>
</div>
</template>
<script>
import MindMap from 'simple-mind-map'
import Outline from './Outline'
import Style from './Style'
import BaseStyle from './BaseStyle'
import exampleData from 'simple-mind-map/example/exampleData'
import MindMap from "simple-mind-map";
import Outline from "./Outline";
import Style from "./Style";
import BaseStyle from "./BaseStyle";
import exampleData from "simple-mind-map/example/exampleData";
import Theme from "./Theme";
/**
* @Author: 王林
* @Date: 2021-06-24 22:56:17
* @Desc: 编辑区域
*/
export default {
name: 'Edit',
name: "Edit",
components: {
Outline,
Style,
BaseStyle,
Theme,
},
data() {
return {
mindMap: null,
mindMapData: exampleData,
}
};
},
created() {},
mounted() {
this.init()
this.$bus.$on('execCommand', this.execCommand)
this.init();
this.$bus.$on("execCommand", this.execCommand);
},
methods: {
/**
@@ -43,20 +51,20 @@ export default {
* @Desc: 初始化
*/
init() {
let { root, layout, theme } = this.mindMapData
let { root, layout, theme } = this.mindMapData;
this.mindMap = new MindMap({
el: this.$refs.mindMapContainer,
data: root,
layout: layout,
theme: theme.template,
themeConfig: theme.config,
})
this.mindMap.on('node_active', (...args) => {
this.$bus.$emit('node_active', ...args)
})
this.mindMap.on('data_change', (...args) => {
this.$bus.$emit('data_change', ...args)
})
});
this.mindMap.on("node_active", (...args) => {
this.$bus.$emit("node_active", ...args);
});
this.mindMap.on("data_change", (...args) => {
this.$bus.$emit("data_change", ...args);
});
},
/**
@@ -65,7 +73,7 @@ export default {
* @Desc: 修改主题配置
*/
changeThemeConfig() {
this.mindMap.setThemeConfig(this.mindMapData.theme.config)
this.mindMap.setThemeConfig(this.mindMapData.theme.config);
},
/**
@@ -74,7 +82,7 @@ export default {
* @Desc: 重新渲染
*/
reRender() {
this.mindMap.render()
this.mindMap.render();
},
/**
@@ -83,10 +91,10 @@ export default {
* @Desc: 执行命令
*/
execCommand(...args) {
this.mindMap.execCommand(...args)
this.mindMap.execCommand(...args);
},
},
}
};
</script>
<style lang="less" scoped>
@@ -94,7 +102,7 @@ export default {
position: fixed;
left: 0;
right: 0;
top: 62px;
top: 0;
bottom: 0;
.mindMapContainer {

View File

@@ -0,0 +1,94 @@
<template>
<el-dialog
class="nodeDialog"
title="超链接"
:visible.sync="dialogVisible"
width="500"
>
<div class="item">
<span class="name">链接</span>
<el-input v-model="link" size="mini" placeholder="http://xxxx.com/"></el-input>
</div>
<div class="item">
<span class="name">名称</span>
<el-input v-model="linkTitle" size="mini"></el-input>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="cancel"> </el-button>
<el-button type="primary" @click="confirm"> </el-button>
</span>
</el-dialog>
</template>
<script>
/**
* @Author: 王林
* @Date: 2021-06-24 22:53:17
* @Desc: 节点超链接内容设置
*/
export default {
name: "NodeHyperlink",
data() {
return {
dialogVisible: false,
link: "",
linkTitle: "",
activeNode: null
};
},
created() {
this.$bus.$on("node_active", (...args) => {
let activeNodes = args[1];
if (activeNodes.length > 0) {
this.activeNode = activeNodes[0];
this.link = this.activeNode.getData("hyperlink");
this.linkTitle = this.activeNode.getData("hyperlinkTitle");
} else {
this.link = "";
this.linkTitle = "";
}
});
this.$bus.$on("showNodeLink", () => {
this.dialogVisible = true;
});
},
methods: {
/**
* @Author: 王林
* @Date: 2021-06-22 22:08:11
* @Desc: 取消
*/
cancel() {
this.dialogVisible = false;
},
/**
* @Author: 王林
* @Date: 2021-06-06 22:28:20
* @Desc: 确定
*/
confirm() {
this.activeNode.setData({
hyperlink: this.link,
hyperlinkTitle: this.linkTitle,
});
this.cancel();
},
},
};
</script>
<style lang="less" scoped>
.nodeDialog {
.item {
display: flex;
align-items: center;
margin-bottom: 10px;
.name {
display: block;
width: 50px;
}
}
}
</style>

View File

@@ -0,0 +1,106 @@
<template>
<el-dialog
class="nodeDialog"
title="图标"
:visible.sync="dialogVisible"
width="500"
>
<div class="item" v-for="item in nodeIconList" :key="item.name">
<div class="title">{{ item.name }}</div>
<div class="list">
<div
class="icon"
v-for="icon in item.list"
:key="icon.name"
v-html="icon.icon"
@click="setIcon(item.type, icon.name)"
></div>
</div>
</div>
</el-dialog>
</template>
<script>
import { nodeIconList } from "simple-mind-map/src/svg/icons";
/**
* @Author: 王林
* @Date: 2021-06-24 22:53:33
* @Desc: 节点图标内容设置
*/
export default {
name: "NodeIcon",
data() {
return {
nodeIconList,
dialogVisible: false,
icon: [],
activeNode: null,
};
},
created() {
this.$bus.$on("node_active", (...args) => {
let activeNodes = args[1];
if (activeNodes.length > 0) {
this.activeNode = activeNodes[0];
this.icon = this.activeNode.getData("icon") || [];
} else {
this.icon = [];
}
});
this.$bus.$on("showNodeIcon", () => {
this.dialogVisible = true;
});
},
methods: {
/**
* @Author: 王林
* @Date: 2021-06-23 23:16:56
* @Desc: 设置icon
*/
setIcon(type, name) {
let index = this.icon.findIndex((item) => {
return item.split('_')[0] === type;
})
if (index !== -1) {
this.icon.splice(index, 1, type + '_' + name)
} else {
this.icon.push(type + '_' + name)
}
this.activeNode.setData({
icon: [...this.icon]
})
}
},
};
</script>
<style lang="less" scoped>
.nodeDialog {
/deep/ .el-dialog__body {
padding: 0 20px;
}
.item {
margin-bottom: 20px;
font-weight: bold;
.title {
margin-bottom: 10px;
}
.list {
display: flex;
flex-wrap: wrap;
.icon {
width: 24px;
height: 24px;
margin-right: 10px;
margin-bottom: 10px;
cursor: pointer;
}
}
}
}
</style>

View File

@@ -1,6 +1,15 @@
<template>
<el-dialog class="nodeImageDialog" title="图片" :visible.sync="dialogVisible" width="500">
<ImgUpload ref="imgUpload"></ImgUpload>
<el-dialog
class="nodeDialog"
title="图片"
:visible.sync="dialogVisible"
width="500"
>
<ImgUpload ref="ImgUpload" v-model="img"></ImgUpload>
<div class="imgTitleBox">
<span class="title">图片标题</span>
<el-input v-model="imgTitle" size="mini"></el-input>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="cancel"> </el-button>
<el-button type="primary" @click="confirm"> </el-button>
@@ -9,42 +18,86 @@
</template>
<script>
import ImgUpload from '@/components/ImgUpload';
import ImgUpload from "@/components/ImgUpload";
/**
* @Author: 王林
* @Date: 2021-06-24 22:53:45
* @Desc: 节点图片内容设置
*/
export default {
name: 'NodeImage',
components: {
ImgUpload
},
name: "NodeImage",
components: {
ImgUpload,
},
data() {
return {
dialogVisible: false,
img: "",
imgTitle: "",
activeNode: null,
};
},
created() {
this.$bus.$on("node_active", (...args) => {
let activeNodes = args[1];
this.activeNode = activeNodes[0];
if (activeNodes.length > 0) {
this.activeNode = activeNodes[0];
this.img = this.activeNode.getData("image");
this.imgTitle = this.activeNode.getData("imageTitle");
} else {
this.img = "";
this.imgTitle = "";
this.activeNode = null;
}
});
this.$bus.$on("showNodeImage", () => {
this.dialogVisible = true;
});
},
methods: {
cancel() {},
/**
* @Author: 王林
* @Date: 2021-06-22 22:08:11
* @Desc: 取消
*/
cancel() {
this.dialogVisible = false;
},
/**
* @Author: 王林
* @Date: 2021-06-06 22:28:20
* @Desc: 确定
*/
confirm() {},
/**
* @Author: 王林
* @Date: 2021-06-06 22:28:20
* @Desc: 确定
*/
async confirm() {
try {
let { width, height } = await this.$refs.ImgUpload.getSize();
this.activeNode.setData({
image: this.img,
imageTitle: this.imgTitle,
imageSize: {
width,
height,
},
});
this.cancel();
} catch (error) {}
},
},
};
</script>
<style lang="less" scoped>
.nodeImageDialog {
.nodeDialog {
.imgTitleBox {
display: flex;
align-items: center;
margin-top: 10px;
.title {
width: 100px;
}
}
}
</style>

View File

@@ -0,0 +1,84 @@
<template>
<el-dialog
class="nodeDialog"
title="备注"
:visible.sync="dialogVisible"
width="500"
>
<el-input
type="textarea"
:autosize="{ minRows: 3, maxRows: 5 }"
placeholder="请输入内容"
v-model="note"
>
</el-input>
<div class="tip">换行请使用Enter+Shift</div>
<span slot="footer" class="dialog-footer">
<el-button @click="cancel"> </el-button>
<el-button type="primary" @click="confirm"> </el-button>
</span>
</el-dialog>
</template>
<script>
/**
* @Author: 王林
* @Date: 2021-06-24 22:53:54
* @Desc: 节点备注内容设置
*/
export default {
name: "NodeNote",
data() {
return {
dialogVisible: false,
note: "",
activeNode: null,
};
},
created() {
this.$bus.$on("node_active", (...args) => {
let activeNodes = args[1];
if (activeNodes.length > 0) {
this.activeNode = activeNodes[0];
this.note = this.activeNode.getData("note");
} else {
this.note = "";
}
});
this.$bus.$on("showNodeNote", () => {
this.dialogVisible = true;
});
},
methods: {
/**
* @Author: 王林
* @Date: 2021-06-22 22:08:11
* @Desc: 取消
*/
cancel() {
this.dialogVisible = false;
},
/**
* @Author: 王林
* @Date: 2021-06-06 22:28:20
* @Desc: 确定
*/
confirm() {
this.activeNode.setData({
note: this.note,
});
this.cancel();
},
},
};
</script>
<style lang="less" scoped>
.nodeDialog {
.tip {
margin-top: 5px;
color: #DCDFE6;
}
}
</style>

View File

@@ -0,0 +1,152 @@
<template>
<el-dialog
class="nodeDialog"
title="标签"
:visible.sync="dialogVisible"
width="500"
>
<el-input
v-model="tag"
@keyup.native.enter="add"
:disabled="tagArr.length >= max"
>
</el-input>
<div class="tagList">
<div
class="tagItem"
v-for="(item, index) in tagArr"
:key="index"
:style="{
backgroundColor: tagColorList[index].background,
color: tagColorList[index].color,
}"
>
{{ item }}
<div class="delBtn" @click="del(index)">
<span class="iconfont iconshanchu"></span>
</div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="cancel"> </el-button>
<el-button type="primary" @click="confirm"> </el-button>
</span>
</el-dialog>
</template>
<script>
import { tagColorList } from "simple-mind-map/src/utils/constant";
/**
* @Author: 王林
* @Date: 2021-06-24 22:54:03
* @Desc: 节点标签内容设置
*/
export default {
name: "NodeTag",
data() {
return {
dialogVisible: false,
tagColorList,
tagArr: [],
tag: "",
activeNode: null,
max: 5,
};
},
created() {
this.$bus.$on("node_active", (...args) => {
let activeNodes = args[1];
if (activeNodes.length > 0) {
this.activeNode = activeNodes[0];
this.tagArr = this.activeNode.getData("tag") || [];
} else {
this.tagArr = [];
this.tag = "";
}
});
this.$bus.$on("showNodeTag", () => {
this.dialogVisible = true;
});
},
methods: {
/**
* @Author: 王林
* @Date: 2021-06-24 21:48:14
* @Desc: 添加
*/
add() {
this.tagArr.push(this.tag);
this.tag = "";
},
/**
* @Author: 王林
* @Date: 2021-06-24 21:57:53
* @Desc: 删除
*/
del(index) {
this.tagArr.splice(index, 1);
},
/**
* @Author: 王林
* @Date: 2021-06-22 22:08:11
* @Desc: 取消
*/
cancel() {
this.dialogVisible = false;
},
/**
* @Author: 王林
* @Date: 2021-06-06 22:28:20
* @Desc: 确定
*/
confirm() {
this.activeNode.setData({
tag: this.tagArr,
});
this.cancel();
},
},
};
</script>
<style lang="less" scoped>
.nodeDialog {
.tagList {
display: flex;
flex-wrap: wrap;
margin-top: 5px;
.tagItem {
position: relative;
padding: 3px 5px;
margin-right: 5px;
margin-bottom: 5px;
.delBtn {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.4);
color: #fff;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
visibility: hidden;
}
&:hover {
.delBtn {
visibility: visible;
}
}
}
}
}
</style>

View File

@@ -7,6 +7,11 @@
<script>
import Sidebar from "./Sidebar";
/**
* @Author: 王林
* @Date: 2021-06-24 22:54:14
* @Desc: 大纲内容
*/
export default {
name: "Outline",
components: {

View File

@@ -11,6 +11,11 @@
</template>
<script>
/**
* @Author: 王林
* @Date: 2021-06-24 22:54:25
* @Desc: 侧边栏容器
*/
export default {
name: "Sidebar",
props: {
@@ -31,7 +36,7 @@ export default {
.sidebarContainer {
position: fixed;
right: -300px;
top: 62px;
top: 100px;
bottom: 0;
width: 300px;
background-color: #fff;

View File

@@ -222,6 +222,11 @@ import {
borderRadiusList,
} from "@/config";
/**
* @Author: 王林
* @Date: 2021-06-24 22:54:47
* @Desc: 节点样式设置
*/
export default {
name: "Style",
components: {

View File

@@ -0,0 +1,104 @@
<template>
<Sidebar ref="sidebar" title="主题">
<div class="themeList">
<div
class="themeItem"
v-for="item in themeList"
:key="item.value"
@click="useTheme(item)"
:class="{ active: item.value === theme }"
>
<div class="imgBox">
<img :src="item.img" alt="" />
</div>
<div class="name">{{ item.name }}</div>
</div>
</div>
</Sidebar>
</template>
<script>
import Sidebar from "./Sidebar";
import { themeList } from "simple-mind-map/src/utils/constant";
/**
* @Author: 王林
* @Date: 2021-06-24 22:53:04
* @Desc: 主题
*/
export default {
name: "Theme",
components: {
Sidebar,
},
props: {
mindMap: {
type: Object,
},
},
data() {
return {
themeList,
theme: "",
};
},
created() {
this.$bus.$on("showTheme", () => {
this.theme = this.mindMap.getTheme();
this.$refs.sidebar.show = true;
});
},
methods: {
/**
* @Author: 王林
* @Date: 2021-06-24 23:04:38
* @Desc: 使用主题
*/
useTheme(theme) {
this.theme = theme.value;
this.mindMap.setTheme(theme.value);
},
},
};
</script>
<style lang="less" scoped>
.themeList {
padding: 20px;
.themeItem {
width: 100%;
cursor: pointer;
border-bottom: 1px solid #e9e9e9;
margin-bottom: 20px;
padding-bottom: 20px;
transition: all 0.2s;
border: 1px solid transparent;
&:last-of-type {
border: none;
}
&:hover {
box-shadow: 0 1px 2px -2px rgba(0, 0, 0, 0.16),
0 3px 6px 0 rgba(0, 0, 0, 0.12), 0 5px 12px 4px rgba(0, 0, 0, 0.09);
}
&.active {
border: 1px solid #67c23a;
}
.imgBox {
width: 100%;
img {
width: 100%;
}
}
.name {
text-align: center;
font-size: 14px;
}
}
}
</style>

View File

@@ -1,92 +1,146 @@
<template>
<div class="toolbarContainer">
<div class="toolbar">
<div
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0,
}"
@click="$bus.$emit('execCommand', 'INSERT_NODE')"
>
<span class="icon iconfont iconjiedian"></span>
<span class="text">插入同级节点</span>
<!-- 节点操作 -->
<div class="left">
<div
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0 || hasRoot,
}"
@click="$bus.$emit('execCommand', 'INSERT_NODE')"
>
<span class="icon iconfont iconjiedian"></span>
<span class="text">插入同级节点</span>
</div>
<div
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0,
}"
@click="$bus.$emit('execCommand', 'INSERT_CHILD_NODE')"
>
<span class="icon iconfont icontianjiazijiedian"></span>
<span class="text">插入子节点</span>
</div>
<div
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0,
}"
@click="$bus.$emit('execCommand', 'REMOVE_NODE')"
>
<span class="icon iconfont iconshanchu"></span>
<span class="text">删除节点</span>
</div>
<div
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0,
}"
@click="$bus.$emit('showNodeImage')"
>
<span class="icon iconfont iconimage"></span>
<span class="text">图片</span>
</div>
<div
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0,
}"
@click="$bus.$emit('showNodeIcon')"
>
<span class="icon iconfont iconxiaolian"></span>
<span class="text">图标</span>
</div>
<div
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0,
}"
@click="$bus.$emit('showNodeLink')"
>
<span class="icon iconfont iconchaolianjie"></span>
<span class="text">超链接</span>
</div>
<div
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0,
}"
@click="$bus.$emit('showNodeNote')"
>
<span class="icon iconfont iconflow-Mark"></span>
<span class="text">备注</span>
</div>
<div
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0,
}"
@click="$bus.$emit('showNodeTag')"
>
<span class="icon iconfont iconbiaoqian"></span>
<span class="text">标签</span>
</div>
</div>
<div
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0,
}"
@click="$bus.$emit('execCommand', 'INSERT_CHILD_NODE')"
>
<span class="icon iconfont icontianjiazijiedian"></span>
<span class="text">插入子节点</span>
</div>
<div
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0,
}"
@click="$bus.$emit('execCommand', 'REMOVE_NODE')"
>
<span class="icon iconfont iconshanchu"></span>
<span class="text">删除节点</span>
</div>
<div
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0,
}"
@click="$bus.$emit('showNodeImage')"
>
<span class="icon iconfont iconimage"></span>
<span class="text">图片</span>
</div>
<div
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0,
}"
@click="$bus.$emit('showNodeIcon')"
>
<span class="icon iconfont iconxiaolian"></span>
<span class="text">图标</span>
</div>
<div
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0,
}"
@click="$bus.$emit('showNodeLink')"
>
<span class="icon iconfont iconchaolianjie"></span>
<span class="text">超链接</span>
</div>
<div class="toolbarBtn" @click="$bus.$emit('showOutline')">
<span class="icon iconfont iconfuhao-dagangshu"></span>
<span class="text">显示大纲</span>
</div>
<div class="toolbarBtn" @click="$bus.$emit('showTheme')">
<span class="icon iconfont iconyangshi"></span>
<span class="text">基础样式</span>
<!-- 通用操作 -->
<div class="center">
<div class="toolbarBtn" @click="$bus.$emit('showOutline')">
<span class="icon iconfont iconfuhao-dagangshu"></span>
<span class="text">显示大纲</span>
</div>
<div class="toolbarBtn" @click="$bus.$emit('showBaseStyle')">
<span class="icon iconfont iconyangshi"></span>
<span class="text">基础样式</span>
</div>
<div class="toolbarBtn" @click="$bus.$emit('showTheme')">
<span class="icon iconfont iconjingzi"></span>
<span class="text">主题</span>
</div>
</div>
</div>
<NodeImage></NodeImage>
<NodeHyperlink></NodeHyperlink>
<NodeIcon></NodeIcon>
<NodeNote></NodeNote>
<NodeTag></NodeTag>
</div>
</template>
<script>
import NodeImage from "./NodeImage";
import NodeHyperlink from "./NodeHyperlink";
import NodeIcon from "./NodeIcon";
import NodeNote from "./NodeNote";
import NodeTag from "./NodeTag";
/**
* @Author: 王林
* @Date: 2021-06-24 22:54:58
* @Desc: 工具栏
*/
export default {
name: "Toolbar",
components: {
NodeImage,
NodeHyperlink,
NodeIcon,
NodeNote,
NodeTag,
},
data() {
return {
activeNodes: [],
activeNodes: []
};
},
computed: {
hasRoot() {
return this.activeNodes.find((node) => {
return node.isRoot;
});
},
},
created() {
this.$bus.$on("node_active", (...args) => {
this.activeNodes = args[1];
@@ -97,22 +151,30 @@ export default {
<style lang="less" scoped>
.toolbarContainer {
height: 62px;
background: #fafafa;
padding-left: 40px;
width: 100%;
display: flex;
align-items: center;
border-top: 1px solid #e8e8e8;
border-bottom: 1px solid #e8e8e8;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: rgba(26, 26, 26, 0.8);
.toolbar {
position: fixed;
left: 0;
top: 0;
width: 100%;
display: flex;
align-items: center;
padding: 0 20px;
padding-top: 20px;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: rgba(26, 26, 26, 0.8);
z-index: 2;
.left,
.center {
display: flex;
background-color: #fff;
padding: 10px 20px;
border-radius: 6px;
box-shadow: 0 2px 16px 0 rgb(0 0 0 / 6%);
border: 1px solid rgba(0, 0, 0, 0.06);
margin-right: 20px;
}
.toolbarBtn {
display: flex;
@@ -121,6 +183,10 @@ export default {
cursor: pointer;
margin-right: 20px;
&:last-of-type {
margin-right: 0;
}
&:hover {
&:not(.disabled) {
.icon {