This commit is contained in:
街角小林
2025-05-14 09:52:27 +08:00
parent 475dd4754a
commit a8c13b8f9a
20 changed files with 63 additions and 1781 deletions

View File

@@ -362,6 +362,13 @@ const mindMap = new MindMap({
<sub style="font-size:14px"><b>兔子快跑</b></sub>
</a>
</td>
<td align="center" style="word-wrap: break-word; width: 75.0; height: 75.0">
<a href="#">
<img src="./web/src/assets/avatar/default.png" width="50;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px"/>
<br />
<sub style="font-size:14px"><b>LSHM</b></sub>
</a>
</td>
</tr>
</table>
@@ -994,5 +1001,12 @@ const mindMap = new MindMap({
<sub style="font-size:14px"><b>神话</b></sub>
</a>
</td>
<td align="center" style="word-wrap: break-word; width: 75.0; height: 75.0">
<a href="#">
<img src="./web/src/assets/avatar/default.png" width="50;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px"/>
<br />
<sub style="font-size:14px"><b>Towards the future</b></sub>
</a>
</td>
</tr>
</table>

View File

@@ -672,13 +672,11 @@ export const layoutGroupList = [
list: [
'timeline',
'timeline2',
'verticalTimeline2',
'verticalTimeline3',
'verticalTimeline'
]
},
{
name: 'Fishbone',
list: ['fishbone', 'fishbone2', 'rightFishbone', 'rightFishbone2']
list: ['fishbone']
}
]

View File

@@ -695,13 +695,11 @@ export const layoutGroupList = [
list: [
'timeline',
'timeline2',
'verticalTimeline2',
'verticalTimeline3',
'verticalTimeline'
]
},
{
name: 'Bản đồ Fishbone',
list: ['fishbone', 'fishbone2', 'rightFishbone', 'rightFishbone2']
list: ['fishbone']
}
]

View File

@@ -773,13 +773,11 @@ export const layoutGroupList = [
list: [
'timeline',
'timeline2',
'verticalTimeline2',
'verticalTimeline3',
'verticalTimeline'
]
},
{
name: '鱼骨图',
list: ['fishbone', 'fishbone2', 'rightFishbone', 'rightFishbone2']
list: ['fishbone']
}
]

View File

@@ -671,13 +671,11 @@ export const layoutGroupList = [
list: [
'timeline',
'timeline2',
'verticalTimeline2',
'verticalTimeline3',
'verticalTimeline'
]
},
{
name: '魚骨圖',
list: ['fishbone', 'fishbone2', 'rightFishbone', 'rightFishbone2']
list: ['fishbone']
}
]

View File

@@ -301,61 +301,6 @@
>
</div>
</div>
<!-- 流动效果 -->
<div class="row" v-if="supportLineFlow">
<div class="rowItem">
<span class="name">{{ $t('style.openLineFlow') }}</span>
<el-checkbox
v-model="style.lineFlow"
@change="
value => {
update('lineFlow', value)
}
"
></el-checkbox>
</div>
<div class="rowItem">
<span class="name">{{ $t('style.direction') }}</span>
<el-select
size="mini"
style="width: 80px"
v-model="style.lineFlowForward"
placeholder=""
@change="
value => {
update('lineFlowForward', value)
}
"
>
<el-option
key="1"
:label="$t('style.forward')"
:value="true"
></el-option>
<el-option
key="2"
:label="$t('style.reverse')"
:value="false"
></el-option>
</el-select>
</div>
</div>
<div class="row" v-if="supportLineFlow">
<div class="rowItem">
<span class="name">{{ $t('style.lineFlowDuration') }}</span>
<el-input-number
v-model="style.lineFlowDuration"
@change="
value => {
update('lineFlowDuration', value)
}
"
:min="0.1"
size="mini"
:step="0.5"
></el-input-number>
</div>
</div>
<!-- 彩虹线条 -->
<div class="title">{{ $t('baseStyle.rainbowLines') }}</div>
<div class="row">
@@ -933,7 +878,6 @@ export default {
activeSidebar: state => state.activeSidebar,
localConfig: state => state.localConfig,
isDark: state => state.localConfig.isDark,
supportLineFlow: state => state.supportLineFlow,
bgList: state => state.bgList
}),
lineStyleList() {

View File

@@ -62,41 +62,6 @@
<div class="item" @click="exec('EXPAND_ALL')">
<span class="name">{{ $t('contextmenu.expandNodeChild') }}</span>
</div>
<div class="item vip" v-if="supportNumbers">
<span class="name">{{ $t('contextmenu.number') }}</span>
<span class="el-icon-arrow-right"></span>
<div
class="subItems listBox"
:class="{ isDark: isDark, showLeft: subItemsShowLeft }"
style="top: -170px"
>
<div
class="item"
v-for="item in numberTypeList"
:key="'type' + item.value"
@click="setNodeNumber('type', item.value)"
>
<span class="name">{{ item.name }}</span>
{{ numberType === item.value ? '√' : '' }}
</div>
<div class="splitLine"></div>
<div
class="item"
v-for="item in numberLevelList"
:key="'level' + item.value"
:class="{ disabled: numberType === '' }"
@click="setNodeNumber('level', item.value)"
>
<span class="name">{{ item.name }}</span>
{{ numberLevel === item.value ? '√' : '' }}
</div>
</div>
</div>
<div class="item vip" @click="setCheckbox" v-if="supportCheckbox">
<span class="name">{{
hasCheckbox ? $t('contextmenu.removeToDo') : $t('contextmenu.addToDo')
}}</span>
</div>
<div class="splitLine"></div>
<div class="item danger" @click="exec('REMOVE_NODE')">
<span class="name">{{ $t('contextmenu.deleteNode') }}</span>
@@ -134,16 +99,6 @@
<div class="item" @click="exec('REMOVE_NOTE')" v-if="hasNote">
<span class="name">{{ $t('contextmenu.removeNote') }}</span>
</div>
<div class="item vip" @click="exec('LINK_NODE')">
<span class="name">{{
hasNodeLink
? $t('contextmenu.modifyNodeLink')
: $t('contextmenu.linkToNode')
}}</span>
</div>
<div class="item vip" @click="exec('REMOVE_LINK_NODE')" v-if="hasNodeLink">
<span class="name">{{ $t('contextmenu.removeNodeLink') }}</span>
</div>
<div class="item" @click="exec('REMOVE_CUSTOM_STYLES')">
<span class="name">{{ $t('contextmenu.removeCustomStyles') }}</span>
</div>
@@ -262,8 +217,6 @@ export default {
...mapState({
isZenMode: state => state.localConfig.isZenMode,
isDark: state => state.localConfig.isDark,
supportNumbers: state => state.supportNumbers,
supportCheckbox: state => state.supportCheckbox,
enableAi: state => state.localConfig.enableAi
}),
expandList() {
@@ -491,13 +444,6 @@ export default {
case 'REMOVE_NOTE':
this.node.setNote('')
break
case 'LINK_NODE':
this.$bus.$emit('show_link_node', this.node)
this.hide()
break
case 'REMOVE_LINK_NODE':
this.$bus.$emit('execCommand', 'SET_NODE_LINK', this.node, null)
break
case 'EXPORT_CUR_NODE_TO_PNG':
this.mindMap.export(
'png',
@@ -521,45 +467,6 @@ export default {
this.hide()
},
// 设置节点编号
setNodeNumber(prop, value) {
if (prop === 'type') {
this.numberType = value
if (value === '') {
// 无编号
this.numberLevel = ''
this.mindMap.execCommand('SET_NUMBER', [], null)
return
} else {
// 有编号
if (this.numberLevel === '') {
this.numberLevel = 1
}
}
}
if (prop === 'level') {
this.numberLevel = value
}
this.mindMap.execCommand('SET_NUMBER', [], {
[prop]: value
})
this.hide()
},
// 设置待办
setCheckbox() {
this.mindMap.execCommand(
'SET_CHECKBOX',
[],
this.hasCheckbox
? null
: {
done: false
}
)
this.hide()
},
// 复制到剪贴板
async copyToClipboard(type) {
try {

View File

@@ -1,36 +0,0 @@
<template>
<div class="customNodeContent">
<p>{{ title }}</p>
<p v-html="html"></p>
<p :style="{ backgroundColor: color }" @click="onClick">点击我会变色</p>
</div>
</template>
<script>
export default {
props: {
html: {
type: String,
default: ''
}
},
data() {
return {
title: '我是自定义节点',
color: ''
}
},
methods: {
onClick() {
this.color = 'red'
}
}
}
</script>
<style lang="less" scoped>
.customNodeContent {
padding: 10px;
cursor: pointer;
}
</style>

View File

@@ -34,7 +34,6 @@
v-if="mindMap"
:mindMap="mindMap"
></NodeNoteContentShow>
<NodeAttachment v-if="mindMap" :mindMap="mindMap"></NodeAttachment>
<NodeImgPreview v-if="mindMap" :mindMap="mindMap"></NodeImgPreview>
<SidebarTrigger v-if="!isZenMode"></SidebarTrigger>
<Search v-if="mindMap" :mindMap="mindMap"></Search>
@@ -43,7 +42,6 @@
<OutlineEdit v-if="mindMap" :mindMap="mindMap"></OutlineEdit>
<Scrollbar v-if="isShowScrollbar && mindMap" :mindMap="mindMap"></Scrollbar>
<FormulaSidebar v-if="mindMap" :mindMap="mindMap"></FormulaSidebar>
<SourceCodeEdit v-if="mindMap" :mindMap="mindMap"></SourceCodeEdit>
<NodeOuterFrame v-if="mindMap" :mindMap="mindMap"></NodeOuterFrame>
<NodeTagStyle v-if="mindMap" :mindMap="mindMap"></NodeTagStyle>
<Setting :configData="mindMapConfig" :mindMap="mindMap"></Setting>
@@ -54,10 +52,6 @@
<NodeNoteSidebar v-if="mindMap" :mindMap="mindMap"></NodeNoteSidebar>
<AiCreate v-if="mindMap && enableAi" :mindMap="mindMap"></AiCreate>
<AiChat v-if="enableAi"></AiChat>
<LinkNodeSelect
v-if="mindMap && supportNodeLink"
:mindMap="mindMap"
></LinkNodeSelect>
<div
class="dragMask"
v-if="showDragMask"
@@ -96,22 +90,6 @@ import NodeBase64ImageStorage from 'simple-mind-map/src/plugins/NodeBase64ImageS
import Themes from 'simple-mind-map-plugin-themes'
// 协同编辑插件
// import Cooperate from 'simple-mind-map/src/plugins/Cooperate.js'
// 以下插件为付费插件详情请查看开发文档。依次为手绘风格插件、标记插件、编号插件、Freemind软件格式导入导出插件、Excel软件格式导入导出插件、待办插件、节点连线流动效果插件、动量效果插件、向右鱼骨图插件、节点链接插件、扩展节点形状插件、扩展主题列表插件
import HandDrawnLikeStyle from 'simple-mind-map-plugin-handdrawnlikestyle'
import Notation from 'simple-mind-map-plugin-notation'
import Numbers from 'simple-mind-map-plugin-numbers'
import Freemind from 'simple-mind-map-plugin-freemind'
import Excel from 'simple-mind-map-plugin-excel'
import Checkbox from 'simple-mind-map-plugin-checkbox'
import LineFlow from 'simple-mind-map-plugin-lineflow'
import Momentum from 'simple-mind-map-plugin-momentum'
import RightFishbone from 'simple-mind-map-plugin-right-fishbone'
import NodeLink from 'simple-mind-map-plugin-node-link'
// import MoreShapes from 'simple-mind-map-plugin-more-shapes'
// import MoreThemes from 'simple-mind-map-plugin-more-themes'
// npm link simple-mind-map simple-mind-map-plugin-excel simple-mind-map-plugin-freemind simple-mind-map-plugin-numbers simple-mind-map-plugin-notation simple-mind-map-plugin-handdrawnlikestyle simple-mind-map-plugin-checkbox simple-mind-map-plugin-lineflow simple-mind-map-plugin-momentum simple-mind-map-plugin-right-fishbone simple-mind-map-plugin-node-link
// simple-mind-map-plugin-themes
// simple-mind-map-plugin-more-themes simple-mind-map-plugin-more-shapes
import OutlineSidebar from './OutlineSidebar.vue'
import Style from './Style.vue'
import BaseStyle from './BaseStyle.vue'
@@ -129,12 +107,7 @@ import NodeImgPreview from './NodeImgPreview.vue'
import SidebarTrigger from './SidebarTrigger.vue'
import { mapState } from 'vuex'
import icon from '@/config/icon'
import CustomNodeContent from './CustomNodeContent.vue'
import Color from './Color.vue'
import Vue from 'vue'
import router from '../../../router'
import store from '../../../store'
import i18n from '../../../i18n'
import Search from './Search.vue'
import NodeIconSidebar from './NodeIconSidebar.vue'
import NodeIconToolbar from './NodeIconToolbar.vue'
@@ -145,8 +118,6 @@ import { getParentWithClass } from '@/utils'
import Scrollbar from './Scrollbar.vue'
import exampleData from 'simple-mind-map/example/exampleData'
import FormulaSidebar from './FormulaSidebar.vue'
import SourceCodeEdit from './SourceCodeEdit.vue'
import NodeAttachment from './NodeAttachment.vue'
import NodeOuterFrame from './NodeOuterFrame.vue'
import NodeTagStyle from './NodeTagStyle.vue'
import Setting from './Setting.vue'
@@ -155,7 +126,6 @@ import NodeImgPlacementToolbar from './NodeImgPlacementToolbar.vue'
import NodeNoteSidebar from './NodeNoteSidebar.vue'
import AiCreate from './AiCreate.vue'
import AiChat from './AiChat.vue'
import LinkNodeSelect from './LinkNodeSelect.vue'
// 注册插件
MindMap.usePlugin(MiniMap)
@@ -208,8 +178,6 @@ export default {
OutlineEdit,
Scrollbar,
FormulaSidebar,
SourceCodeEdit,
NodeAttachment,
NodeOuterFrame,
NodeTagStyle,
Setting,
@@ -217,8 +185,7 @@ export default {
NodeImgPlacementToolbar,
NodeNoteSidebar,
AiCreate,
AiChat,
LinkNodeSelect
AiChat
},
data() {
return {
@@ -239,13 +206,9 @@ export default {
enableDragImport: state => state.localConfig.enableDragImport,
useLeftKeySelectionRightKeyDrag: state =>
state.localConfig.useLeftKeySelectionRightKeyDrag,
isUseHandDrawnLikeStyle: state =>
state.localConfig.isUseHandDrawnLikeStyle,
isUseMomentum: state => state.localConfig.isUseMomentum,
extraTextOnExport: state => state.extraTextOnExport,
isDragOutlineTreeNode: state => state.isDragOutlineTreeNode,
enableAi: state => state.localConfig.enableAi,
supportNodeLink: state => state.supportNodeLink
enableAi: state => state.localConfig.enableAi
})
},
watch: {
@@ -262,25 +225,10 @@ export default {
} else {
this.removeScrollbarPlugin()
}
},
isUseHandDrawnLikeStyle() {
if (this.isUseHandDrawnLikeStyle) {
this.addHandDrawnLikeStylePlugin()
} else {
this.removeHandDrawnLikeStylePlugin()
}
},
isUseMomentum() {
if (this.isUseMomentum) {
this.addMomentumPlugin()
} else {
this.removeMomentumPlugin()
}
}
},
mounted() {
showLoading()
// this.showNewFeatureInfo()
this.getData()
this.init()
this.$bus.$on('execCommand', this.execCommand)
@@ -295,9 +243,8 @@ export default {
this.$bus.$on('showLoading', this.handleShowLoading)
this.$bus.$on('localStorageExceeded', this.onLocalStorageExceeded)
window.addEventListener('resize', this.handleResize)
document.body.addEventListener('click', this.onVipCheckClick)
this.$bus.$on('showDownloadTip', this.showDownloadTip)
this.$bus.$on('vipCheckClick', this.onVipCheckClick)
this.webTip()
},
beforeDestroy() {
this.$bus.$off('execCommand', this.execCommand)
@@ -312,9 +259,7 @@ export default {
this.$bus.$off('showLoading', this.handleShowLoading)
this.$bus.$off('localStorageExceeded', this.onLocalStorageExceeded)
window.removeEventListener('resize', this.handleResize)
document.body.removeEventListener('click', this.onVipCheckClick)
this.$bus.$off('showDownloadTip', this.showDownloadTip)
this.$bus.$off('vipCheckClick', this.onVipCheckClick)
this.mindMap.destroy()
},
methods: {
@@ -425,7 +370,7 @@ export default {
openRealtimeRenderOnNodeTextEdit: true,
enableAutoEnterTextEditWhenKeydown: true,
demonstrateConfig: {
openBlankMode: true
openBlankMode: false
},
...(config || {}),
iconList: [...icon],
@@ -500,116 +445,6 @@ export default {
})
})
}
// createNodePrefixContent: node => {
// const el = document.createElement('div')
// el.style.width = '50px'
// el.style.height = '50px'
// el.style.background = 'red'
// return {
// el,
// width: 50,
// height: 50
// }
// },
// createNodePostfixContent: node => {
// const domparser = new DOMParser()
// const doc = domparser.parseFromString(
// '<b style="background-color: rgb(214, 239, 214);">白日依山尽</b>',
// 'text/html'
// )
// const el = doc.querySelector('b')
// return {
// el,
// width: 50,
// height: 50
// }
// },
// addContentToHeader: () => {
// const el = document.createElement('div')
// el.className = 'footer'
// el.innerHTML = '理想青年实验室'
// const cssText = `
// .header {
// width: 100%;
// height: 50px;
// background: #f5f5f5;
// display: flex;
// justify-content: center;
// align-items: center
// }
// `
// return {
// el,
// cssText,
// height: 50
// }
// },
// beforeShortcutRun: (key, activeNodeList) => {
// console.log(key, activeNodeList)
// // 阻止删除快捷键行为
// if (key === 'Backspace') {
// return true
// }
// }
// handleNodePasteImg: img => {
// console.log(img)
// return new Promise(resolve => {
// setTimeout(() => {
// resolve({
// url: require('../../../assets/img/themes/autumn.jpg'),
// size: {
// width: 100,
// height: 100
// }
// })
// }, 200)
// })
// }
// isUseCustomNodeContent: true,
// 示例1组件里用到了router、store、i18n等实例化vue组件时需要用到的东西
// customCreateNodeContent: (node) => {
// let el = document.createElement('div')
// let Comp = Vue.extend(Color)
// let comp = new Comp({
// router,
// store,
// i18n
// })
// comp.$mount(el)
// return comp.$el
// },
// 示例2组件里没有用到示例1的东西
// customCreateNodeContent: (node) => {
// let el = document.createElement('div')
// let Comp = Vue.extend(CustomNodeContent)
// let comp = new Comp({
// propsData: {
// html: node.nodeData.data.text
// }
// })
// comp.$mount(el)
// return comp.$el
// },
// 示例3普通元素
// customCreateNodeContent: node => {
// let el = document.createElement('div')
// el.style.cssText = `
// width: 203px;
// height: 78px;
// opacity: 0.8;
// background-image: linear-gradient(0deg, rgba(53,130,172,0.06) 0%, rgba(24,75,116,0.06) 100%);
// box-shadow: inset 0 1px 15px 0 rgba(119,196,255,0.40);
// border-radius: 2px;
// display: flex;
// justify-content: center;
// align-items: center;
// `
// el.innerHTML = `
// ${node.nodeData.data.text}
// <img crossOrigin="anonymous" src="/img/cactus.jpg" />
// `
// return el
// }
})
this.loadPlugins()
this.mindMap.keyCommand.addShortcut('Control+s', () => {
@@ -649,7 +484,6 @@ export default {
})
})
this.bindSaveEvent()
this.testDynamicCreateNodes()
// 如果应用被接管,那么抛出事件传递思维导图实例
if (window.takeOverApp) {
this.$bus.$emit('app_inited', this.mindMap)
@@ -666,93 +500,12 @@ export default {
}
// 协同测试
this.cooperateTest()
// 销毁
// setTimeout(() => {
// console.log('销毁')
// this.mindMap.destroy()
// }, 10000)
// 测试
// setTimeout(() => {
// console.log(this.mindMap.renderer.root.getRect())
// console.log(this.mindMap.renderer.root.getRectInSvg())
// }, 5000);
// setTimeout(() => {
// this.mindMap.renderer.renderTree.data.fillColor = 'red'
// this.mindMap.render()
// this.mindMap.reRender()
// this.mindMap.render()
// }, 5000)
},
// 加载相关插件
loadPlugins() {
if (this.openNodeRichText) this.addRichTextPlugin()
if (this.isShowScrollbar) this.addScrollbarPlugin()
if (typeof HandDrawnLikeStyle !== 'undefined') {
this.$store.commit('setSupportHandDrawnLikeStyle', true)
if (this.isUseHandDrawnLikeStyle) this.addHandDrawnLikeStylePlugin()
}
if (typeof Momentum !== 'undefined') {
this.$store.commit('setSupportMomentum', true)
if (this.isUseMomentum) this.addMomentumPlugin()
}
if (typeof Notation !== 'undefined') {
this.mindMap.addPlugin(Notation)
this.$store.commit('setSupportMark', true)
}
if (typeof Numbers !== 'undefined') {
this.mindMap.addPlugin(Numbers)
this.$store.commit('setSupportNumbers', true)
}
if (typeof Freemind !== 'undefined') {
this.mindMap.addPlugin(Freemind)
this.$store.commit('setSupportFreemind', true)
Vue.prototype.Freemind = Freemind
}
if (typeof Excel !== 'undefined') {
this.mindMap.addPlugin(Excel)
this.$store.commit('setSupportExcel', true)
Vue.prototype.Excel = Excel
}
if (typeof Checkbox !== 'undefined') {
this.mindMap.addPlugin(Checkbox)
this.$store.commit('setSupportCheckbox', true)
}
if (typeof LineFlow !== 'undefined') {
this.mindMap.addPlugin(LineFlow)
this.$store.commit('setSupportLineFlow', true)
}
if (typeof RightFishbone !== 'undefined') {
this.mindMap.addPlugin(RightFishbone)
this.$store.commit('setSupportRightFishbone', true)
}
if (typeof NodeLink !== 'undefined') {
this.mindMap.addPlugin(NodeLink)
this.$store.commit('setSupportNodeLink', true)
}
if (typeof MoreShapes !== 'undefined') {
this.mindMap.addPlugin(MoreShapes)
this.$store.commit('setSupportMoreShapes', true)
}
// 扩展侧边主题列表
if (typeof MoreThemes !== 'undefined') {
const extendThemeGroupList = [
{
name: this.$t('edit.withBg'), // 主题组名称
// 主题列表
list: [...MoreThemes.lightList, ...MoreThemes.darkList].map(
item => {
return {
...item,
img: MoreThemes.themeImgMap[item.value]
}
}
)
}
]
this.$store.commit('setExtendThemeGroupList', extendThemeGroupList)
this.$store.commit('setBgList', MoreThemes.bgList)
}
},
// url中是否存在要打开的文件
@@ -812,21 +565,6 @@ export default {
this.mindMap.updateConfig(data)
},
// 显示新特性提示
showNewFeatureInfo() {
let showed = localStorage.getItem('SIMPLE_MIND_MAP_NEW_FEATURE_TIP_1')
if (!showed) {
this.$notify.info({
title: this.$t('edit.newFeatureNoticeTitle'),
message: this.$t('edit.newFeatureNoticeMessage'),
duration: 0,
onClose: () => {
localStorage.setItem('SIMPLE_MIND_MAP_NEW_FEATURE_TIP_1', true)
}
})
}
},
// 加载节点富文本编辑插件
addRichTextPlugin() {
if (!this.mindMap) return
@@ -849,153 +587,6 @@ export default {
this.mindMap.removePlugin(ScrollbarPlugin)
},
// 加载手绘风格插件
addHandDrawnLikeStylePlugin() {
try {
if (!this.mindMap) return
this.mindMap.addPlugin(HandDrawnLikeStyle)
this.mindMap.reRender()
} catch (error) {
console.log('手绘风格插件不存在')
}
},
// 移除手绘风格插件
removeHandDrawnLikeStylePlugin() {
try {
this.mindMap.removePlugin(HandDrawnLikeStyle)
this.mindMap.reRender()
} catch (error) {
console.log('手绘风格插件不存在')
}
},
// 加载动量效果插件
addMomentumPlugin() {
try {
if (!this.mindMap) return
this.mindMap.addPlugin(Momentum)
} catch (error) {
console.log('动量效果插件不存在')
}
},
// 移除动量效果插件
removeMomentumPlugin() {
try {
this.mindMap.removePlugin(Momentum)
} catch (error) {
console.log('动量效果插件不存在')
}
},
// 测试动态插入节点
testDynamicCreateNodes() {
// return
setTimeout(() => {
// 动态给指定节点添加子节点
// this.mindMap.execCommand(
// 'INSERT_CHILD_NODE',
// false,
// null,
// {
// text: '自定义内容'
// },
// [
// {
// data: {
// text: '自定义子节点'
// }
// }
// ]
// )
// 动态给指定节点添加同级节点
// this.mindMap.execCommand(
// 'INSERT_NODE',
// false,
// null,
// {
// text: '自定义内容'
// },
// [
// {
// data: {
// text: '自定义同级节点'
// },
// children: [
// {
// data: {
// text: '自定义同级节点2'
// },
// children: []
// }
// ]
// }
// ]
// )
// 动态插入多个子节点
// this.mindMap.execCommand('INSERT_MULTI_CHILD_NODE', null, [
// {
// data: {
// text: '自定义节点1'
// },
// children: [
// {
// data: {
// text: '自定义节点1-1'
// },
// children: []
// }
// ]
// },
// {
// data: {
// text: '自定义节点2'
// },
// children: [
// {
// data: {
// text: '自定义节点2-1'
// },
// children: []
// }
// ]
// }
// ])
// 动态插入多个同级节点
// this.mindMap.execCommand('INSERT_MULTI_NODE', null, [
// {
// data: {
// text: '自定义节点1'
// },
// children: [
// {
// data: {
// text: '自定义节点1-1'
// },
// children: []
// }
// ]
// },
// {
// data: {
// text: '自定义节点2'
// },
// children: [
// {
// data: {
// text: '自定义节点2-1'
// },
// children: []
// }
// ]
// }
// ])
// 动态删除指定节点
// this.mindMap.execCommand('REMOVE_NODE', this.mindMap.renderer.root.children[0])
}, 5000)
},
// 协同测试
cooperateTest() {
if (this.mindMap.cooperate && this.$route.query.userName) {
@@ -1022,9 +613,11 @@ export default {
if (!this.enableDragImport || this.isDragOutlineTreeNode) return
this.showDragMask = true
},
onDragleave() {
this.showDragMask = false
},
onDrop(e) {
if (!this.enableDragImport) return
this.showDragMask = false
@@ -1034,30 +627,18 @@ export default {
this.$bus.$emit('importFile', file)
},
// 网页版功能试用提示
onVipCheckClick(e) {
const el = getParentWithClass(e.target, 'vip')
if (el) {
const className = el.classList.value.split(/\s+/).join('_')
const storageKey = 'VIP_USAGE_TIP'
let data = localStorage.getItem(storageKey)
// 网页版试用提示
webTip() {
const storageKey = 'webUseTip'
const data = localStorage.getItem(storageKey)
if (data) {
data = JSON.parse(data)
} else {
data = {}
return
}
if (!data[className]) {
data[className] = 0
}
data[className]++
if (data[className] > 3) {
this.showDownloadTip(
this.$t('edit.tryTipTitle'),
this.$t('edit.tryTipDesc')
'重要提示',
'网页版已暂停更新,部分功能缺失,请下载客户端获得完整体验~'
)
}
localStorage.setItem(storageKey, JSON.stringify(data))
}
localStorage.setItem(storageKey, 1)
},
showDownloadTip(title, desc) {
@@ -1065,7 +646,15 @@ export default {
this.$msgbox({
title,
message: h('div', null, [
h('p', null, desc),
h(
'p',
{
style: {
marginBottom: '12px'
}
},
desc
),
h('div', null, [
h(
'a',
@@ -1137,11 +726,6 @@ export default {
top: 0px;
width: 100%;
height: 100%;
// left: 100px;
// top: 100px;
// right: 100px;
// bottom: 100px;
}
}
</style>

View File

@@ -21,8 +21,7 @@
v-for="item in downTypeList"
:key="item.type"
:class="{
active: exportType === item.type,
vip: ['mm', 'xlsx'].includes(item.type)
active: exportType === item.type
}"
@click="exportType = item.type"
>
@@ -80,7 +79,6 @@
<span class="name">{{ $t('export.format') }}</span>
<el-radio-group v-model="imageFormat">
<el-radio label="png">PNG</el-radio>
<el-radio label="jpg" class="vip">JPG</el-radio>
</el-radio-group>
</div>
<div class="valueSubItem">
@@ -176,18 +174,16 @@ export default {
...mapState({
openNodeRichText: state => state.localConfig.openNodeRichText,
isDark: state => state.localConfig.isDark,
supportFreemind: state => state.supportFreemind,
supportExcel: state => state.supportExcel
}),
downTypeList() {
const list = downTypeList[this.$i18n.locale] || downTypeList.zh
return list.filter(item => {
if (item.type === 'mm') {
return this.supportFreemind
return false
}
if (item.type === 'xlsx') {
return this.supportExcel
return false
} else {
return true
}
@@ -274,22 +270,6 @@ export default {
this.isTransparent,
this.isFitBg
)
} else if (this.exportType === 'mm') {
this.$bus.$emit('export', this.exportType, true, this.fileName, {
transformNote: note => {
if (!md) {
md = new MarkdownIt()
}
return md.render(note)
},
transformImage: img => {
if (/^https?:\/\//.test(img)) {
return img
} else {
return ''
}
}
})
} else {
this.$bus.$emit('export', this.exportType, true, this.fileName)
}

View File

@@ -21,20 +21,13 @@
<el-button slot="trigger" size="small" type="primary">{{
$t('import.selectFile')
}}</el-button>
<el-button
size="small"
style="margin-left: 10px;"
@click="mdImportDialogVisible = true"
class="vip"
>{{ $t('import.mdImportDialogTitle') }}</el-button
>
<div slot="tip" class="el-upload__tip">
{{ $t('import.support') }}{{ supportFileStr }}{{ $t('import.file') }}
</div>
</el-upload>
<span slot="footer" class="dialog-footer">
<el-button @click="cancel">{{ $t('dialog.cancel') }}</el-button>
<el-button class="vip" type="primary" @click="confirm">{{
<el-button type="primary" @click="confirm">{{
$t('dialog.confirm')
}}</el-button>
</span>
@@ -60,34 +53,13 @@
}}</el-button>
</span>
</el-dialog>
<el-dialog
class="mdImportDialog"
:title="$t('import.mdImportDialogTitle')"
:visible.sync="mdImportDialogVisible"
width="500px"
:show-close="false"
>
<el-input
type="textarea"
:rows="10"
:placeholder="$t('import.mdPlaceholder')"
v-model="mdStr"
>
</el-input>
<span slot="footer" class="dialog-footer">
<el-button @click="cancelImportMd">{{ $t('dialog.cancel') }}</el-button>
<el-button type="primary" @click="confirmImportFromMd">{{
$t('dialog.confirm')
}}</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import xmind from 'simple-mind-map/src/parse/xmind.js'
import markdown from 'simple-mind-map/src/parse/markdown.js'
import { mapMutations, mapState } from 'vuex'
import { mapMutations } from 'vuex'
import Vue from 'vue'
// 导入
@@ -100,24 +72,12 @@ export default {
xmindCanvasSelectDialogVisible: false,
selectCanvas: '',
canvasList: [],
mdImportDialogVisible: false,
mdStr: ''
}
},
computed: {
...mapState({
supportFreemind: state => state.supportFreemind,
supportExcel: state => state.supportExcel
}),
supportFileStr() {
let res = '.smm,.json,.xmind,.md'
if (this.supportFreemind) {
res += ',.mm'
}
if (this.supportExcel) {
res += ',.xlsx'
}
return res
return '.smm,.json,.xmind,.md'
}
},
watch: {
@@ -145,11 +105,7 @@ export default {
},
getRegexp() {
return new RegExp(
`\.(smm|json|xmind|md${this.supportFreemind ? '|mm' : ''}${
this.supportExcel ? '|xlsx' : ''
})$`
)
return new RegExp(`\.(smm|json|xmind|md)$`)
},
// 检查url中是否操作需要打开的文件
@@ -171,12 +127,8 @@ export default {
this.handleSmm(data)
} else if (type === 'xmind') {
this.handleXmind(data)
} else if (type === 'xlsx') {
this.handleExcel(data)
} else if (type === 'md') {
this.handleMd(data)
} else if (type === 'mm') {
this.handleMm(data)
}
} catch (error) {
console.log(error)
@@ -223,12 +175,8 @@ export default {
this.handleSmm(file)
} else if (/\.xmind$/.test(file.name)) {
this.handleXmind(file)
} else if (/\.xlsx$/.test(file.name)) {
this.handleExcel(file)
} else if (/\.md$/.test(file.name)) {
this.handleMd(file)
} else if (/\.mm$/.test(file.name)) {
this.handleMm(file)
}
this.cancel()
this.setActiveSidebar(null)
@@ -270,36 +218,6 @@ export default {
}
},
// 处理Freemind格式
handleMm(file) {
const fileReader = new FileReader()
fileReader.readAsText(file.raw)
fileReader.onload = async evt => {
try {
const data = await Vue.prototype.Freemind.freemindToSmm(
evt.target.result,
{
// withStyle: true,
transformImg: image => {
return new Promise(resolve => {
if (/^https?:\/\//.test(image)) {
resolve({ url: image })
} else {
resolve(null)
}
})
}
}
)
this.$bus.$emit('setData', data)
this.$message.success(this.$t('import.importSuccess'))
} catch (error) {
console.log(error)
this.$message.error(this.$t('import.fileParsingFailed'))
}
}
},
// 显示xmind文件的多个画布选择弹窗
showSelectXmindCanvasDialog(content) {
this.canvasList = content
@@ -315,18 +233,6 @@ export default {
this.selectCanvas = 0
},
// 处理.xlsx文件
async handleExcel(file) {
try {
const res = await Vue.prototype.Excel.excelTo(file.raw)
this.$bus.$emit('setData', res)
this.$message.success(this.$t('import.importSuccess'))
} catch (error) {
console.log(error)
this.$message.error(this.$t('import.fileParsingFailed'))
}
},
// 处理markdown文件
async handleMd(file) {
let fileReader = new FileReader()
@@ -351,29 +257,6 @@ export default {
})
if (this.fileList.length <= 0) return
this.confirm()
},
cancelImportMd() {
this.mdImportDialogVisible = false
this.mdStr = ''
},
confirmImportFromMd() {
if (!this.mdStr.trim()) {
this.$message.warning(this.$t('import.mdEmptyTip'))
return
}
try {
const data = markdown.transformMarkdownTo(this.mdStr.trim())
this.$bus.$emit('setData', data)
this.$message.success(this.$t('import.importSuccess'))
this.cancelImportMd()
this.cancel()
this.setActiveSidebar(null)
} catch (error) {
console.log(error)
this.$message.error(this.$t('import.fileParsingFailed'))
}
}
}
}

View File

@@ -1,177 +0,0 @@
<template>
<el-dialog
class="nodeLinkSelectDialog"
:title="$t('nodeLink.linkToNode')"
:visible.sync="dialogVisible"
:show-close="false"
append-to-body
width="400px"
>
<div class="nodeTreeWrap customScrollbar">
<el-tree
ref="treeRef"
class="outlineTree"
node-key="uid"
default-expand-all
:class="{ isDark: isDark }"
:data="treeData"
:props="defaultProps"
:highlight-current="true"
:expand-on-click-node="false"
@current-change="onCurrentChange"
>
</el-tree>
</div>
<div slot="footer" class="footer">
<el-checkbox v-model="isAddReturn" style="margin-right: auto;">{{
$t('nodeLink.addReturn')
}}</el-checkbox>
<el-button @click="cancel">{{ $t('dialog.cancel') }}</el-button>
<el-button type="primary" @click="confirm">{{
$t('dialog.confirm')
}}</el-button>
</div>
</el-dialog>
</template>
<script>
import {
nodeRichTextToTextWithWrap,
htmlEscape
} from 'simple-mind-map/src/utils'
import { mapState } from 'vuex'
export default {
props: {
mindMap: {
type: Object
}
},
data() {
return {
dialogVisible: false,
treeData: [],
defaultProps: {
label: 'label'
},
currentNodeData: null,
node: null,
isAddReturn: false
}
},
computed: {
...mapState({
isDark: state => state.localConfig.isDark
})
},
created() {
this.$bus.$on('show_link_node', this.onShowDialog)
this.mindMap.on('node_link_not_find', this.onNodeLinkNotFind)
},
beforeDestroy() {
this.$bus.$off('show_link_node', this.onShowDialog)
this.mindMap.off('node_link_not_find', this.onNodeLinkNotFind)
},
methods: {
onShowDialog(node) {
this.node = node
let data = this.mindMap.getData()
let walk = root => {
let text = root.data.richText
? nodeRichTextToTextWithWrap(root.data.text)
: root.data.text
text = htmlEscape(text)
text = text.replace(/\n/g, '<br>')
root.label = text
root.uid = root.data.uid
if (root.children && root.children.length > 0) {
root.children.forEach(item => {
walk(item)
})
}
}
walk(data)
this.treeData = [data]
this.dialogVisible = true
this.$nextTick(() => {
const linkUid = node.getData('nodeLink')
if (linkUid) {
this.$refs.treeRef.setCurrentKey(linkUid)
}
})
},
close() {
this.dialogVisible = false
this.node = null
this.treeData = []
this.currentNodeData = null
this.isAddReturn = false
},
// 当前选中的树节点变化事件
onCurrentChange(data) {
this.currentNodeData = data
},
cancel() {
this.close()
},
confirm() {
if (!this.currentNodeData) {
this.$message.warning(this.$t('nodeLink.tip1'))
return
}
if (this.currentNodeData.uid === this.node.getData('uid')) {
this.$message.warning(this.$t('nodeLink.tip2'))
return
}
this.$bus.$emit(
'execCommand',
'SET_NODE_LINK',
this.node,
this.currentNodeData.uid,
this.isAddReturn
)
this.$message.success(this.$t('nodeLink.tip3'))
this.close()
},
onNodeLinkNotFind(node) {
this.$confirm(this.$t('nodeLink.tip5'), this.$t('edit.tip'), {
confirmButtonText: this.$t('setting.confirm'),
cancelButtonText: this.$t('setting.cancel'),
type: 'warning'
}).then(() => {
this.$bus.$emit('execCommand', 'SET_NODE_LINK', node, null)
this.$message({
type: 'success',
message: this.$t('nodeLink.tip4')
})
})
}
}
}
</script>
<style lang="less" scoped>
.nodeLinkSelectDialog {
/deep/ .el-dialog__body {
padding: 0 20px;
}
.footer {
display: flex;
align-items: center;
justify-content: space-between;
}
}
.nodeTreeWrap {
height: 400px;
overflow: auto;
}
</style>
<style lang="less" scoped>
@import url('../../../style/outlineTree.less');
</style>

View File

@@ -1,275 +0,0 @@
<template>
<el-popover placement="bottom" width="200" trigger="click">
<div class="annotationConfigBox" :class="{ isDark: isDark }">
<div class="annotationConfigItem">
<span class="name">{{ $t('annotation.show') }}</span>
<el-switch
v-model="show"
active-color="#13ce66"
inactive-color="#ff4949"
@change="onChange"
>
</el-switch>
</div>
<template v-if="show">
<div class="annotationConfigItem">
<span class="name">{{ $t('annotation.type') }}</span>
<el-select
size="mini"
v-model="annotationConfig.type"
@change="onChange"
>
<el-option
v-for="item in annotationTypeList"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</div>
<div class="annotationConfigItem">
<span class="name">{{ $t('annotation.color') }}</span>
<span
class="block"
v-popover:popover
:style="{ backgroundColor: annotationConfig.color }"
></span>
<el-popover ref="popover" placement="bottom" trigger="hover">
<Color
:color="annotationConfig.color"
@change="onColorChange"
></Color>
</el-popover>
</div>
<div class="annotationConfigItem">
<span class="name">{{ $t('annotation.lineWidth') }}</span>
<el-select
size="mini"
style="width: 80px"
v-model="annotationConfig.strokeWidth"
placeholder=""
@change="onChange"
>
<el-option
v-for="item in lineWidthList"
:key="item"
:label="item"
:value="item"
>
<span
v-if="item > 0"
class="borderLine"
:class="{ isDark: isDark }"
:style="{ height: item + 'px' }"
></span>
</el-option>
</el-select>
</div>
<div class="annotationConfigItem">
<span class="name">{{ $t('annotation.padding') }}</span>
<el-input-number
v-model="annotationConfig.padding"
:step="5"
size="mini"
@change="onChange"
></el-input-number>
</div>
<div class="annotationConfigItem">
<span class="name">{{ $t('annotation.animate') }}</span>
<el-switch
v-model="annotationConfig.animate"
active-color="#13ce66"
inactive-color="#ff4949"
@change="onChange"
>
</el-switch>
</div>
</template>
</div>
<div
slot="reference"
class="toolbarBtn vip"
:style="{
marginLeft: dir === 'v' || rightHasBtn ? '0px' : '20px',
marginTop: dir === 'v' ? '10px' : '0px',
marginRight: rightHasBtn ? '20px' : '0px',
marginBottom: dir === 'v' && rightHasBtn ? '10px' : '0px'
}"
:class="{
disabled: activeNodes.length <= 0 || hasGeneralization
}"
>
<span class="icon iconfont iconhighlight"></span>
<span class="text">{{ $t('annotation.mark') }}</span>
</div>
</el-popover>
</template>
<script>
import { lineWidthList } from '@/config'
import Color from './Color.vue'
const defaultConfig = {
type: 'circle',
color: '',
strokeWidth: 1,
animate: true,
padding: 20
}
export default {
components: {
Color
},
props: {
isDark: {
type: Boolean,
default: false
},
dir: {
type: String,
default: ''
},
rightHasBtn: {
type: Boolean,
default: false
}
},
data() {
return {
lineWidthList: lineWidthList.slice(1),
activeNodes: [],
show: false,
annotationConfig: {
...defaultConfig
},
annotationTypeList: [
{
label: '圆',
value: 'circle'
},
{
label: '边框',
value: 'box'
},
{
label: '高亮',
value: 'highlight'
},
{
label: '下划线',
value: 'underline'
},
{
label: '删除线',
value: 'strike-through'
},
{
label: '叉',
value: 'crossed-off'
}
]
}
},
computed: {
hasGeneralization() {
return (
this.activeNodes.findIndex(node => {
return node.isGeneralization
}) !== -1
)
}
},
mounted() {
this.$bus.$on('node_active', this.onNodeActive)
},
beforeDestroy() {
this.$bus.$off('node_active', this.onNodeActive)
},
methods: {
onNodeActive(...args) {
this.activeNodes = [...args[1]]
const node = this.activeNodes[0]
if (node) {
const notationData = node.getData('notation')
if (notationData) {
const { show, config } = notationData
this.show = show
this.annotationConfig = {
...defaultConfig,
...config
}
} else {
this.reset()
}
} else {
this.reset()
}
},
reset() {
this.show = false
this.annotationConfig = {
...defaultConfig
}
},
onChange() {
this.$emit('setAnnotation', this.show, {
...this.annotationConfig
})
},
onColorChange(color) {
this.annotationConfig.color = color
this.onChange()
}
}
}
</script>
<style lang="less" scoped>
.annotationConfigBox {
&.isDark {
.annotationConfigItem {
.name {
color: hsla(0, 0%, 100%, 0.9);
}
}
}
.annotationConfigItem {
display: flex;
align-items: center;
margin-bottom: 12px;
&:last-of-type {
margin-bottom: 0px;
}
.name {
flex-shrink: 0;
margin-right: 10px;
}
.block {
width: 30px;
height: 30px;
border: 1px solid #dcdfe6;
border-radius: 4px;
cursor: pointer;
}
}
}
.borderLine {
display: inline-block;
width: 100%;
background-color: #000;
&.isDark {
background-color: #fff;
}
}
</style>

View File

@@ -1,133 +0,0 @@
<template>
<div
class="nodeAttachmentContextMenu"
:style="{
left: this.left + 'px',
top: this.top + 'px',
visibility: show ? 'visible' : 'hidden'
}"
@click.stop="deleteAttachment"
>
<div class="menuItem">{{ $t('attachment.deleteAttachment') }}</div>
</div>
</template>
<script>
export default {
props: {
mindMap: {
type: Object,
default() {
return null
}
}
},
data() {
return {
show: false,
left: 0,
top: 0,
node: null,
icon: null
}
},
created() {
this.$bus.$on('node_attachmentClick', this.onNodeAttachmentClick)
this.$bus.$on('selectAttachment', this.onSelectAttachment)
this.$bus.$on(
'node_attachmentContextmenu',
this.onNodeAttachmentContextmenu
)
this.$bus.$on('hide', this.hide)
document.body.addEventListener('click', this.hide)
this.$bus.$on('node_active', this.hide)
this.$bus.$on('scale', this.onScale)
this.$bus.$on('translate', this.onScale)
this.$bus.$on('svg_mousedown', this.hide)
},
beforeDestroy() {
this.$bus.$off('node_attachmentClick', this.onNodeAttachmentClick)
this.$bus.$off('selectAttachment', this.onSelectAttachment)
this.$bus.$off(
'node_attachmentContextmenu',
this.onNodeAttachmentContextmenu
)
this.$bus.$off('hide', this.hide)
document.body.removeEventListener('click', this.hide)
this.$bus.$off('node_active', this.hide)
this.$bus.$off('scale', this.onScale)
this.$bus.$off('translate', this.onScale)
this.$bus.$off('svg_mousedown', this.hide)
},
methods: {
// 选择附件
onSelectAttachment(activeNodes) {
// activeNodes.forEach(node => {
// node.setAttachment('/test.md', '我去')
// })
},
// 点击附件图标,一般用来打开或下载附件
onNodeAttachmentClick(node, e, icon) {
// console.log(node.getData('attachmentUrl'))
this.$message.info(this.$t('attachment.tip'))
},
// 显示删除浮层
onNodeAttachmentContextmenu(node, e, icon) {
e.stopPropagation()
e.preventDefault()
this.node = node
this.icon = icon
this.updatePosition()
this.show = true
},
// 更新位置
updatePosition() {
const iconSize = this.mindMap.themeConfig.iconSize
const { x, y } = this.icon.rbox()
this.left = x + iconSize
this.top = y
},
// 画布缩放事件
onScale() {
if (!this.node || !this.show) return
this.updatePosition()
},
// 删除附件
deleteAttachment() {
if (!this.node || !this.show) return
this.node.setAttachment('', '')
this.hide()
},
// 隐藏浮层
hide() {
this.show = false
}
}
}
</script>
<style lang="less" scoped>
.nodeAttachmentContextMenu {
position: fixed;
background-color: #fff;
padding: 10px;
border-radius: 5px;
box-shadow: 0 2px 16px 0 rgba(0, 0, 0, 0.06);
border: 1px solid rgba(0, 0, 0, 0.06);
.menuItem {
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #1a1a1a;
cursor: pointer;
user-select: none;
}
}
</style>

View File

@@ -4,7 +4,6 @@
class="sidebarContent customScrollbar"
:class="{ isDark: isDark }"
v-if="configData"
@click="onClick"
>
<!-- 水印 -->
<div class="row">
@@ -257,40 +256,6 @@
>
</div>
</div>
<!-- 是否开启手绘风格 -->
<div class="row vip" v-if="supportHandDrawnLikeStyle">
<div class="rowItem">
<el-checkbox
v-model="localConfigs.isUseHandDrawnLikeStyle"
@change="updateLocalConfig('isUseHandDrawnLikeStyle', $event)"
>{{ $t('setting.isUseHandDrawnLikeStyle') }}</el-checkbox
>
</div>
</div>
<!-- 是否开启动量效果 -->
<div class="row vip" v-if="supportMomentum">
<div class="rowItem">
<el-checkbox
v-model="localConfigs.isUseMomentum"
@change="updateLocalConfig('isUseMomentum', $event)"
>{{ $t('setting.isUseMomentum') }}</el-checkbox
>
</div>
</div>
<!-- 是否开启演示模式的填空功能 -->
<div class="row vip">
<div class="rowItem">
<el-checkbox
v-model="config.demonstrateConfig.openBlankMode"
@change="
value => {
updateOtherConfig('openBlankMode', value)
}
"
>{{ $t('setting.openBlankMode') }}</el-checkbox
>
</div>
</div>
<!-- 配置鼠标滚轮行为 -->
<div class="row">
<div class="rowItem">
@@ -439,10 +404,7 @@ export default {
enableAutoEnterTextEditWhenKeydown: true,
imgTextMargin: 0,
textContentMargin: 0,
enableInheritAncestorLineStyle: false,
demonstrateConfig: {
openBlankMode: false
}
enableInheritAncestorLineStyle: false
},
watermarkConfig: {
show: false,
@@ -461,8 +423,6 @@ export default {
enableNodeRichText: true,
localConfigs: {
isShowScrollbar: false,
isUseHandDrawnLikeStyle: false,
isUseMomentum: false,
enableDragImport: false,
enableAi: false
}
@@ -472,9 +432,7 @@ export default {
...mapState({
activeSidebar: state => state.activeSidebar,
localConfig: state => state.localConfig,
isDark: state => state.localConfig.isDark,
supportHandDrawnLikeStyle: state => state.supportHandDrawnLikeStyle,
supportMomentum: state => state.supportMomentum
isDark: state => state.localConfig.isDark
})
},
watch: {
@@ -535,23 +493,10 @@ export default {
// 更新其他配置
updateOtherConfig(key, value) {
if (key === 'openBlankMode') {
this.mindMap.updateConfig({
demonstrateConfig: {
...(this.mindMap.getConfig('demonstrateConfig') || {}),
openBlankMode: value
}
})
if (!this.configData.demonstrateConfig) {
this.configData.demonstrateConfig = {}
}
this.configData.demonstrateConfig[key] = value
} else {
this.mindMap.updateConfig({
[key]: value
})
this.configData[key] = value
}
storeConfig(this.configData)
if (
[
@@ -628,10 +573,6 @@ export default {
this.setLocalConfig({
[key]: value
})
},
onClick(e) {
this.$bus.$emit('vipCheckClick', e)
}
}
}

View File

@@ -1,214 +0,0 @@
<template>
<div
class="sourceCodeEditContainer"
:class="{ isDark: isDark }"
ref="sourceCodeEditContainer"
v-if="isSourceCodeEdit"
>
<div class="closeBtn">
<el-tooltip
effect="dark"
:content="$t('sourceCodeEdit.copy')"
placement="top"
>
<span
class="icon iconfont iconfuzhi"
style="font-size: 26px"
@click="copy"
></span>
</el-tooltip>
<el-tooltip
effect="dark"
:content="$t('sourceCodeEdit.format')"
placement="top"
>
<span
class="icon iconfont icongeshihua"
style="font-size: 24px"
@click="format"
></span>
</el-tooltip>
<el-tooltip
effect="dark"
:content="$t('sourceCodeEdit.sourceCodeTip')"
placement="top"
>
<span class="icon el-icon-info"></span>
</el-tooltip>
<el-tooltip
effect="dark"
:content="$t('sourceCodeEdit.confirm')"
placement="top"
>
<span class="icon el-icon-circle-check" @click="onConfirm"></span>
</el-tooltip>
<el-tooltip
effect="dark"
:content="$t('sourceCodeEdit.close')"
placement="top"
>
<span class="icon iconfont iconguanbi" @click="onClose"></span>
</el-tooltip>
</div>
<div class="sourceCodeEditBox">
<div class="outlineEdit" ref="outlineEditRef" @keydown.stop></div>
</div>
</div>
</template>
<script>
import { mapState, mapMutations } from 'vuex'
import CodeMirror from 'codemirror'
import 'codemirror/mode/javascript/javascript'
import 'codemirror/lib/codemirror.css'
import { copy } from '@/utils/index'
let editor = null
// 源码编辑
export default {
props: {
mindMap: {
type: Object
}
},
data() {
return {}
},
computed: {
...mapState({
isReadonly: state => state.isReadonly,
isDark: state => state.localConfig.isDark,
isSourceCodeEdit: state => state.isSourceCodeEdit
})
},
watch: {
isSourceCodeEdit(val) {
if (val) {
this.$nextTick(() => {
document.body.appendChild(this.$refs.sourceCodeEditContainer)
this.initEditor()
this.initData()
})
}
}
},
methods: {
...mapMutations(['setIsSourceCodeEdit']),
// 初始化编辑器
initEditor() {
editor = CodeMirror(this.$refs.outlineEditRef, {
mode: { name: 'javascript', json: true },
lineWrapping: true,
lineNumbers: true
})
},
// 初始化数据
initData() {
editor.setValue(JSON.stringify(this.mindMap.getData(), null, 2))
},
// 完成
onConfirm() {
try {
const content = editor.getValue()
const data = JSON.parse(content)
this.setIsSourceCodeEdit(false)
this.$bus.$emit('setData', data)
} catch (error) {
console.log(error)
this.$message.error(this.$t('sourceCodeEdit.formatErrorTip'))
}
},
// 关闭
onClose() {
this.setIsSourceCodeEdit(false)
},
// 复制
copy() {
const content = editor.getValue()
copy(content)
this.$message.success(this.$t('sourceCodeEdit.copyTip'))
},
// 格式化
format() {
try {
const content = editor.getValue()
const data = JSON.parse(content)
editor.setValue(JSON.stringify(data, null, 2))
this.$message.success(this.$t('sourceCodeEdit.formatTip'))
} catch (error) {
console.log(error)
this.$message.error(this.$t('sourceCodeEdit.formatErrorTip'))
}
}
}
}
</script>
<style lang="less" scoped>
.sourceCodeEditContainer {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 1999;
background-color: #f5f5f5;
overflow: hidden;
&.isDark {
background-color: #262a2e;
.closeBtn {
.icon {
color: #fff;
}
}
}
.closeBtn {
position: absolute;
right: 40px;
top: 20px;
cursor: pointer;
display: flex;
align-items: center;
.icon {
font-size: 28px;
margin-left: 10px;
}
}
.sourceCodeEditBox {
width: 100%;
height: 100%;
overflow: hidden;
padding: 50px 0;
.outlineEdit {
width: 1000px;
height: 100%;
margin: 0 auto;
font-size: 17px;
background-color: #fff;
font-family: Menlo, Monaco, Consolas, Andale Mono, Ubuntu Mono,
Courier New, monospace;
padding: 12px;
border-radius: 5px;
/deep/ .CodeMirror {
height: 100%;
font-family: Menlo, Monaco, Consolas, Andale Mono, Ubuntu Mono,
Courier New, monospace;
}
}
}
}
</style>

View File

@@ -49,19 +49,15 @@ export default {
computed: {
...mapState({
isDark: state => state.localConfig.isDark,
activeSidebar: state => state.activeSidebar,
supportRightFishbone: state => state.supportRightFishbone
activeSidebar: state => state.activeSidebar
}),
layoutGroupList() {
const groupList = layoutGroupList[this.$i18n.locale] || layoutGroupList.zh
return groupList.map(group => {
let list = [...group.list]
if (!this.supportRightFishbone) {
list = list.filter(item => {
let list = [...group.list].filter(item => {
return !['rightFishbone', 'rightFishbone2'].includes(item)
})
}
return {
name: group.name,
list

View File

@@ -437,49 +437,6 @@
</el-select>
</div>
</div>
<!-- 流动效果 -->
<div class="row" v-if="supportLineFlow">
<div class="rowItem">
<span class="name">{{ $t('style.openLineFlow') }}</span>
<el-checkbox
v-model="style.lineFlow"
@change="update('lineFlow')"
></el-checkbox>
</div>
<div class="rowItem">
<span class="name">{{ $t('style.direction') }}</span>
<el-select
size="mini"
style="width: 80px"
v-model="style.lineFlowForward"
placeholder=""
@change="update('lineFlowForward')"
>
<el-option
key="1"
:label="$t('style.forward')"
:value="true"
></el-option>
<el-option
key="2"
:label="$t('style.reverse')"
:value="false"
></el-option>
</el-select>
</div>
</div>
<div class="row" v-if="supportLineFlow">
<div class="rowItem">
<span class="name">{{ $t('style.lineFlowDuration') }}</span>
<el-input-number
v-model="style.lineFlowDuration"
@change="update('lineFlowDuration')"
:min="0.1"
size="mini"
:step="0.5"
></el-input-number>
</div>
</div>
<!-- 节点内边距 -->
<div class="title">{{ $t('style.nodePadding') }}</div>
<div class="row noBottom">
@@ -623,8 +580,7 @@ export default {
computed: {
...mapState({
isDark: state => state.localConfig.isDark,
activeSidebar: state => state.activeSidebar,
supportLineFlow: state => state.supportLineFlow
activeSidebar: state => state.activeSidebar
}),
fontFamilyList() {
return fontFamilyList[this.$i18n.locale] || fontFamilyList.zh

View File

@@ -178,13 +178,6 @@
<span class="icon iconfont iconwaikuang"></span>
<span class="text">{{ $t('toolbar.outerFrame') }}</span>
</div>
<NodeAnnotationBtn
v-if="item === 'annotation' && supportMark"
:isDark="isDark"
:dir="dir"
:rightHasBtn="annotationRightHasBtn"
@setAnnotation="onSetAnnotation"
></NodeAnnotationBtn>
<div
v-if="item === 'ai'"
class="toolbarBtn"
@@ -202,10 +195,8 @@
<script>
import { mapState, mapMutations } from 'vuex'
import NodeAnnotationBtn from './NodeAnnotationBtn.vue'
export default {
components: { NodeAnnotationBtn },
props: {
dir: {
type: String,
@@ -231,8 +222,7 @@ export default {
},
computed: {
...mapState({
isDark: state => state.localConfig.isDark,
supportMark: state => state.supportMark
isDark: state => state.localConfig.isDark
}),
hasRoot() {
return (

View File

@@ -16,10 +16,6 @@ const store = new Vuex.Store({
useLeftKeySelectionRightKeyDrag: false,
// 是否显示滚动条
isShowScrollbar: false,
// 是否开启手绘风格
isUseHandDrawnLikeStyle: false,
// 是否开启动量效果
isUseMomentum: true,
// 是否是暗黑模式
isDark: false,
// 是否开启AI功能
@@ -30,17 +26,6 @@ const store = new Vuex.Store({
isReadonly: false, // 是否只读
isSourceCodeEdit: false, // 是否是源码编辑模式
extraTextOnExport: '', // 导出时底部添加的文字
supportHandDrawnLikeStyle: false, // 是否支持设置手绘风格
supportMark: false, // 是否支持标记
supportNumbers: false, // 是否支持编号
supportFreemind: false, // 是否支持Freemind插件
supportExcel: false, // 是否支持Excel插件
supportCheckbox: false, // 是否支持Checkbox插件
supportLineFlow: false, // 是否支持LineFlow插件
supportMomentum: false, // 是否支持Momentum插件
supportRightFishbone: false, // 是否支持RightFishbone插件
supportNodeLink: false, // 是否支持NodeLink插件
supportMoreShapes: false, // 是否支持MoreShapes插件
isDragOutlineTreeNode: false, // 当前是否正在拖拽大纲树的节点
aiConfig: {
api: 'http://ark.cn-beijing.volces.com/api/v3/chat/completions',
@@ -101,61 +86,6 @@ const store = new Vuex.Store({
state.extraTextOnExport = data
},
// 设置是否支持手绘风格
setSupportHandDrawnLikeStyle(state, data) {
state.supportHandDrawnLikeStyle = data
},
// 设置是否支持标记
setSupportMark(state, data) {
state.supportMark = data
},
// 设置是否支持编号
setSupportNumbers(state, data) {
state.supportNumbers = data
},
// 设置是否支持Freemind插件
setSupportFreemind(state, data) {
state.supportFreemind = data
},
// 设置是否支持Excel插件
setSupportExcel(state, data) {
state.supportExcel = data
},
// 设置是否支持Checkbox插件
setSupportCheckbox(state, data) {
state.supportCheckbox = data
},
// 设置是否支持Lineflow插件
setSupportLineFlow(state, data) {
state.supportLineFlow = data
},
// 设置是否支持Momentum插件
setSupportMomentum(state, data) {
state.supportMomentum = data
},
// 设置是否支持RightFishbone插件
setSupportRightFishbone(state, data) {
state.supportRightFishbone = data
},
// 设置是否支持NodeLink插件
setSupportNodeLink(state, data) {
state.supportNodeLink = data
},
// 设置是否支持MoreShapes插件
setSupportMoreShapes(state, data) {
state.supportMoreShapes = data
},
// 设置树节点拖拽
setIsDragOutlineTreeNode(state, data) {
state.isDragOutlineTreeNode = data