2022-09-24 17:08:11 +08:00

483 lines
13 KiB
Vue

<template>
<div class="toolbarContainer">
<div class="toolbar">
<!-- 节点操作 -->
<div class="toolbarBlock">
<div
class="toolbarBtn"
:class="{
disabled: readonly || backEnd,
}"
@click="$bus.$emit('execCommand', 'BACK')"
>
<span class="icon iconfont iconhoutui-shi"></span>
<span class="text">回退</span>
</div>
<div
class="toolbarBtn"
:class="{
disabled: readonly || forwardEnd,
}"
@click="$bus.$emit('execCommand', 'FORWARD')"
>
<span class="icon iconfont iconqianjin1"></span>
<span class="text">前进</span>
</div>
<div
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0 || hasRoot || hasGeneralization,
}"
@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 || hasGeneralization,
}"
@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
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0 || hasRoot || hasGeneralization,
}"
@click="$bus.$emit('execCommand', 'ADD_GENERALIZATION')"
>
<span class="icon iconfont icongaikuozonglan"></span>
<span class="text">概要</span>
</div>
</div>
<!-- 通用操作 -->
<div class="toolbarBlock">
<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 class="toolbarBtn" @click="$bus.$emit('showStructure')">
<span class="icon iconfont iconjiegou"></span>
<span class="text">结构</span>
</div>
</div>
<!-- 导出 -->
<div class="toolbarBlock">
<div class="toolbarBtn" @click="createNewLocalFile">
<span class="icon iconfont iconxinjian"></span>
<span class="text">新建</span>
</div>
<div class="toolbarBtn" @click="openLocalFile">
<span class="icon iconfont icondakai"></span>
<span class="text">打开</span>
</div>
<div class="toolbarBtn" @click="saveLocalFile">
<span class="icon iconfont iconlingcunwei"></span>
<span class="text">另存为</span>
</div>
<div class="toolbarBtn" @click="$bus.$emit('showImport')">
<span class="icon iconfont icondaoru"></span>
<span class="text">导入</span>
</div>
<div class="toolbarBtn" @click="$bus.$emit('showExport')">
<span class="icon iconfont iconexport"></span>
<span class="text">导出</span>
</div>
<div class="toolbarBtn" @click="$bus.$emit('showShortcutKey')">
<span class="icon iconfont iconjianpan"></span>
<span class="text">快捷键</span>
</div>
</div>
</div>
<NodeImage></NodeImage>
<NodeHyperlink></NodeHyperlink>
<NodeIcon></NodeIcon>
<NodeNote></NodeNote>
<NodeTag></NodeTag>
<Export></Export>
<Import></Import>
</div>
</template>
<script>
import NodeImage from "./NodeImage";
import NodeHyperlink from "./NodeHyperlink";
import NodeIcon from "./NodeIcon";
import NodeNote from "./NodeNote";
import NodeTag from "./NodeTag";
import Export from "./Export";
import Import from './Import';
import { mapState } from 'vuex';
import { Notification } from 'element-ui';
import exampleData from 'simple-mind-map/example/exampleData';
import { getData } from '../../../api';
/**
* @Author: 王林
* @Date: 2021-06-24 22:54:58
* @Desc: 工具栏
*/
let fileHandle = null;
export default {
name: "Toolbar",
components: {
NodeImage,
NodeHyperlink,
NodeIcon,
NodeNote,
NodeTag,
Export,
Import
},
data() {
return {
activeNodes: [],
backEnd: false,
forwardEnd: true,
readonly: false,
isFullDataFile: false,
timer: null,
};
},
computed: {
...mapState(['isHandleLocalFile']),
hasRoot() {
return this.activeNodes.findIndex((node) => {
return node.isRoot;
}) !== -1;
},
hasGeneralization() {
return this.activeNodes.findIndex((node) => {
return node.isGeneralization;
}) !== -1;;
}
},
watch: {
isHandleLocalFile(val) {
if (!val) {
Notification.closeAll();
}
}
},
created() {
this.$bus.$on("mode_change", (mode) => {
this.readonly = mode === 'readonly'
});
this.$bus.$on("node_active", (...args) => {
this.activeNodes = args[1];
});
this.$bus.$on("back_forward", (index, len) => {
this.backEnd = index <= 0
this.forwardEnd = index >= len - 1
});
this.$bus.$on("write_local_file", (content) => {
clearTimeout(this.timer);
this.timer = setTimeout(() => {
this.writeLocalFile(content);
}, 1000);
});
},
methods: {
/**
* @Author: 王林
* @Date: 2022-09-24 15:40:09
* @Desc: 打开本地文件
*/
async openLocalFile() {
try {
let [ _fileHandle ] = await window.showOpenFilePicker({
types: [
{
description: 'file',
accept: {
'application/*': ['.json', '.smm']
}
},
],
excludeAcceptAllOption: true,
multiple: false
});
if (!_fileHandle) {
return;
}
fileHandle = _fileHandle;
if (fileHandle.kind === 'directory') {
this.$message.warning('请选择文件');
return;
}
this.readFile();
} catch (error) {
console.log(error);
this.$message.warning('你的浏览器可能不支持哦');
}
},
/**
* @Author: 王林
* @Date: 2022-09-24 15:40:18
* @Desc: 读取本地文件
*/
async readFile() {
let file = await fileHandle.getFile();
let fileReader = new FileReader();
fileReader.onload = async () => {
this.$store.commit('setIsHandleLocalFile', true);
this.setData(fileReader.result);
Notification.closeAll();
Notification({
title: '提示',
message: `当前正在编辑你本机的【${ file.name }】文件`,
duration: 0,
showClose: false
});
}
fileReader.readAsText(file);
},
/**
* @Author: 王林
* @Date: 2022-09-24 15:40:26
* @Desc: 渲染读取的数据
*/
setData(str) {
try {
let data = JSON.parse(str);
if (typeof data !== 'object') {
throw new Error('文件内容有误');
}
if (data.root) {
this.isFullDataFile = true;
} else {
this.isFullDataFile = false;
data = {
...exampleData,
root: data
}
}
this.$bus.$emit('setData', data);
} catch (error) {
console.log(error)
this.$message.error("文件打开失败");
}
},
/**
* @Author: 王林
* @Date: 2022-09-24 15:40:42
* @Desc: 写入本地文件
*/
async writeLocalFile(content) {
if (!fileHandle || !this.isHandleLocalFile) {
return;
}
if (!this.isFullDataFile) {
content = content.root;
}
let string = JSON.stringify(content);
const writable = await fileHandle.createWritable();
await writable.write(string);
await writable.close();
},
/**
* @Author: 王林
* @Date: 2022-09-24 15:40:48
* @Desc: 创建本地文件
*/
async createNewLocalFile() {
await this.createLocalFile(exampleData);
},
/**
* @Author: 王林
* @Date: 2022-09-24 15:49:17
* @Desc: 另存为
*/
async saveLocalFile() {
let data = getData();
await this.createLocalFile(data);
},
/**
* @Author: 王林
* @Date: 2022-09-24 15:50:22
* @Desc: 创建本地文件
*/
async createLocalFile(content) {
try {
let _fileHandle = await window.showSaveFilePicker({
types: [{
description: 'file',
accept: {'application/*': ['.json', '.smm']},
}],
});
if (!_fileHandle) {
return;
}
const loading = this.$loading({
lock: true,
text: '正在创建文件',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
fileHandle = _fileHandle;
this.$store.commit('setIsHandleLocalFile', true);
this.isFullDataFile = true;
await this.writeLocalFile(content);
await this.readFile();
loading.close();
} catch (error) {
console.log(error);
this.$message.warning('你的浏览器可能不支持哦');
}
},
}
};
</script>
<style lang="less" scoped>
.toolbarContainer {
.toolbar {
position: fixed;
left: 0;
top: 0;
display: flex;
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;
.toolbarBlock {
display: flex;
background-color: #fff;
padding: 10px 20px;
border-radius: 6px;
box-shadow: 0 2px 16px 0 rgba(0, 0, 0, 0.06);
border: 1px solid rgba(0, 0, 0, 0.06);
margin-right: 20px;
&:last-of-type {
margin-right: 0;
}
}
.toolbarBtn {
display: flex;
justify-content: center;
flex-direction: column;
cursor: pointer;
margin-right: 20px;
&:last-of-type {
margin-right: 0;
}
&:hover {
&:not(.disabled) {
.icon {
background: #f5f5f5;
}
}
}
&.disabled {
color: #bcbcbc;
cursor: not-allowed;
pointer-events: none;
}
.icon {
display: flex;
height: 26px;
background: #fff;
border-radius: 4px;
border: 1px solid #e9e9e9;
justify-content: center;
flex-direction: column;
text-align: center;
padding: 0 5px;
}
.text {
margin-top: 3px;
}
}
}
}
</style>