Demo:新增源码编辑模式
This commit is contained in:
parent
bff683cb5c
commit
102cbeb821
11
web/package-lock.json
generated
11
web/package-lock.json
generated
@ -9,6 +9,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@toast-ui/editor": "^3.1.5",
|
"@toast-ui/editor": "^3.1.5",
|
||||||
|
"codemirror": "^5.65.16",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"element-ui": "^2.15.1",
|
"element-ui": "^2.15.1",
|
||||||
"highlight.js": "^10.7.3",
|
"highlight.js": "^10.7.3",
|
||||||
@ -5009,6 +5010,11 @@
|
|||||||
"node": ">= 4.0"
|
"node": ">= 4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/codemirror": {
|
||||||
|
"version": "5.65.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.16.tgz",
|
||||||
|
"integrity": "sha512-br21LjYmSlVL0vFCPWPfhzUCT34FM/pAdK7rRIZwa0rrtrIdotvP4Oh4GUHsu2E3IrQMCfRkL/fN3ytMNxVQvg=="
|
||||||
|
},
|
||||||
"node_modules/codepage": {
|
"node_modules/codepage": {
|
||||||
"version": "1.15.0",
|
"version": "1.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
|
||||||
@ -21021,6 +21027,11 @@
|
|||||||
"q": "^1.1.2"
|
"q": "^1.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"codemirror": {
|
||||||
|
"version": "5.65.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.16.tgz",
|
||||||
|
"integrity": "sha512-br21LjYmSlVL0vFCPWPfhzUCT34FM/pAdK7rRIZwa0rrtrIdotvP4Oh4GUHsu2E3IrQMCfRkL/fN3ytMNxVQvg=="
|
||||||
|
},
|
||||||
"codepage": {
|
"codepage": {
|
||||||
"version": "1.15.0",
|
"version": "1.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@toast-ui/editor": "^3.1.5",
|
"@toast-ui/editor": "^3.1.5",
|
||||||
|
"codemirror": "^5.65.16",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"element-ui": "^2.15.1",
|
"element-ui": "^2.15.1",
|
||||||
"highlight.js": "^10.7.3",
|
"highlight.js": "^10.7.3",
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: "iconfont"; /* Project id 2479351 */
|
font-family: "iconfont"; /* Project id 2479351 */
|
||||||
src: url('iconfont.woff2?t=1711432586441') format('woff2'),
|
src: url('iconfont.woff2?t=1711536835850') format('woff2'),
|
||||||
url('iconfont.woff?t=1711432586441') format('woff'),
|
url('iconfont.woff?t=1711536835850') format('woff'),
|
||||||
url('iconfont.ttf?t=1711432586441') format('truetype');
|
url('iconfont.ttf?t=1711536835850') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
@ -13,6 +13,14 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icongeshihua:before {
|
||||||
|
content: "\e7a3";
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconyuanma:before {
|
||||||
|
content: "\e658";
|
||||||
|
}
|
||||||
|
|
||||||
.icongundongtiao:before {
|
.icongundongtiao:before {
|
||||||
content: "\e670";
|
content: "\e670";
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -152,7 +152,8 @@ export default {
|
|||||||
closeMiniMap: 'Close mini map',
|
closeMiniMap: 'Close mini map',
|
||||||
readonly: 'Change to eadonly',
|
readonly: 'Change to eadonly',
|
||||||
edit: 'Change to edit',
|
edit: 'Change to edit',
|
||||||
backToRoot: 'Back to root node'
|
backToRoot: 'Back to root node',
|
||||||
|
changeSourceCodeEdit: 'Switch to source code editing mode'
|
||||||
},
|
},
|
||||||
nodeHyperlink: {
|
nodeHyperlink: {
|
||||||
title: 'Link',
|
title: 'Link',
|
||||||
@ -317,5 +318,15 @@ export default {
|
|||||||
},
|
},
|
||||||
other: {
|
other: {
|
||||||
loading: 'Loading, please wait...'
|
loading: 'Loading, please wait...'
|
||||||
|
},
|
||||||
|
sourceCodeEdit: {
|
||||||
|
sourceCodeTip: 'It is not recommended to modify the style in rich text mode because it requires synchronous modification of data and HTML structure.',
|
||||||
|
format: 'Format',
|
||||||
|
copy: 'Copy',
|
||||||
|
confirm: 'Complete',
|
||||||
|
close: 'Close',
|
||||||
|
formatErrorTip: 'The JSON format is incorrect. Please check and try again',
|
||||||
|
copyTip: 'Copied to clipboard',
|
||||||
|
formatTip: 'Format complete'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -150,7 +150,8 @@ export default {
|
|||||||
closeMiniMap: '关闭小地图',
|
closeMiniMap: '关闭小地图',
|
||||||
readonly: '切换为只读模式',
|
readonly: '切换为只读模式',
|
||||||
edit: '切换为编辑模式',
|
edit: '切换为编辑模式',
|
||||||
backToRoot: '回到根节点'
|
backToRoot: '回到根节点',
|
||||||
|
changeSourceCodeEdit: '切换为源码编辑模式'
|
||||||
},
|
},
|
||||||
nodeHyperlink: {
|
nodeHyperlink: {
|
||||||
title: '超链接',
|
title: '超链接',
|
||||||
@ -311,5 +312,15 @@ export default {
|
|||||||
},
|
},
|
||||||
other: {
|
other: {
|
||||||
loading: '正在加载,请稍后...'
|
loading: '正在加载,请稍后...'
|
||||||
|
},
|
||||||
|
sourceCodeEdit: {
|
||||||
|
sourceCodeTip: '富文本模式下不建议修改样式,因为需要同步修改数据及html结构。',
|
||||||
|
format: '格式化',
|
||||||
|
copy: '复制',
|
||||||
|
confirm: '完成',
|
||||||
|
close: '关闭',
|
||||||
|
formatErrorTip: 'JSON格式有误,请检查后再试',
|
||||||
|
copyTip: '已复制到剪贴板',
|
||||||
|
formatTip: '格式化完成'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,7 @@
|
|||||||
<OutlineEdit v-if="mindMap" :mindMap="mindMap"></OutlineEdit>
|
<OutlineEdit v-if="mindMap" :mindMap="mindMap"></OutlineEdit>
|
||||||
<Scrollbar v-if="isShowScrollbar && mindMap" :mindMap="mindMap"></Scrollbar>
|
<Scrollbar v-if="isShowScrollbar && mindMap" :mindMap="mindMap"></Scrollbar>
|
||||||
<FormulaSidebar v-if="mindMap" :mindMap="mindMap"></FormulaSidebar>
|
<FormulaSidebar v-if="mindMap" :mindMap="mindMap"></FormulaSidebar>
|
||||||
|
<SourceCodeEdit v-if="mindMap" :mindMap="mindMap"></SourceCodeEdit>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -83,6 +84,7 @@ import handleClipboardText from '@/utils/handleClipboardText'
|
|||||||
import Scrollbar from './Scrollbar.vue'
|
import Scrollbar from './Scrollbar.vue'
|
||||||
import exampleData from 'simple-mind-map/example/exampleData'
|
import exampleData from 'simple-mind-map/example/exampleData'
|
||||||
import FormulaSidebar from './FormulaSidebar.vue'
|
import FormulaSidebar from './FormulaSidebar.vue'
|
||||||
|
import SourceCodeEdit from './SourceCodeEdit.vue'
|
||||||
|
|
||||||
// 注册插件
|
// 注册插件
|
||||||
MindMap.usePlugin(MiniMap)
|
MindMap.usePlugin(MiniMap)
|
||||||
@ -134,7 +136,8 @@ export default {
|
|||||||
NodeIconToolbar,
|
NodeIconToolbar,
|
||||||
OutlineEdit,
|
OutlineEdit,
|
||||||
Scrollbar,
|
Scrollbar,
|
||||||
FormulaSidebar
|
FormulaSidebar,
|
||||||
|
SourceCodeEdit
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -80,6 +80,15 @@
|
|||||||
@click="toggleDark"
|
@click="toggleDark"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<el-tooltip
|
||||||
|
effect="dark"
|
||||||
|
:content="$t('navigatorToolbar.changeSourceCodeEdit')"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<div class="btn iconfont iconyuanma" @click="openSourceCodeEdit"></div>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<el-dropdown @command="handleCommand">
|
<el-dropdown @command="handleCommand">
|
||||||
<div class="btn iconfont iconbangzhu"></div>
|
<div class="btn iconfont iconbangzhu"></div>
|
||||||
@ -141,7 +150,7 @@ export default {
|
|||||||
this.lang = getLang()
|
this.lang = getLang()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(['setLocalConfig', 'setIsReadonly']),
|
...mapMutations(['setLocalConfig', 'setIsReadonly', 'setIsOutlineEdit']),
|
||||||
|
|
||||||
readonlyChange() {
|
readonlyChange() {
|
||||||
this.setIsReadonly(!this.isReadonly)
|
this.setIsReadonly(!this.isReadonly)
|
||||||
@ -198,6 +207,10 @@ export default {
|
|||||||
|
|
||||||
backToRoot() {
|
backToRoot() {
|
||||||
this.mindMap.renderer.setRootNodeCenter()
|
this.mindMap.renderer.setRootNodeCenter()
|
||||||
|
},
|
||||||
|
|
||||||
|
openSourceCodeEdit() {
|
||||||
|
this.setIsOutlineEdit(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,7 +35,6 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
isDark: state => state.localConfig.isDark,
|
isDark: state => state.localConfig.isDark,
|
||||||
isOutlineEdit: state => state.isOutlineEdit,
|
|
||||||
activeSidebar: state => state.activeSidebar
|
activeSidebar: state => state.activeSidebar
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
216
web/src/pages/Edit/components/SourceCodeEdit.vue
Normal file
216
web/src/pages/Edit/components/SourceCodeEdit.vue
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
<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 { storeData } from '@/api'
|
||||||
|
import CodeMirror from 'codemirror'
|
||||||
|
import 'codemirror/mode/javascript/javascript'
|
||||||
|
import 'codemirror/lib/codemirror.css'
|
||||||
|
import { copy } from '@/utils/index'
|
||||||
|
|
||||||
|
let editor = null
|
||||||
|
|
||||||
|
// 源码编辑
|
||||||
|
export default {
|
||||||
|
name: 'SourceCodeEdit',
|
||||||
|
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(['setIsOutlineEdit']),
|
||||||
|
|
||||||
|
// 初始化编辑器
|
||||||
|
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.setIsOutlineEdit(false)
|
||||||
|
this.$bus.$emit('setData', data)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
this.$message.error(this.$t('sourceCodeEdit.formatErrorTip'))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 关闭
|
||||||
|
onClose() {
|
||||||
|
this.setIsOutlineEdit(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>
|
||||||
@ -25,7 +25,8 @@ const store = new Vuex.Store({
|
|||||||
},
|
},
|
||||||
activeSidebar: '', // 当前显示的侧边栏
|
activeSidebar: '', // 当前显示的侧边栏
|
||||||
isOutlineEdit: false, // 是否是大纲编辑模式
|
isOutlineEdit: false, // 是否是大纲编辑模式
|
||||||
isReadonly: false // 是否只读
|
isReadonly: false, // 是否只读
|
||||||
|
isSourceCodeEdit: false// 是否是源码编辑模式
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
// 设置思维导图数据
|
// 设置思维导图数据
|
||||||
@ -60,7 +61,12 @@ const store = new Vuex.Store({
|
|||||||
// 设置是否只读
|
// 设置是否只读
|
||||||
setIsReadonly(state, data) {
|
setIsReadonly(state, data) {
|
||||||
state.isReadonly = data
|
state.isReadonly = data
|
||||||
}
|
},
|
||||||
|
|
||||||
|
// 设置源码编辑模式
|
||||||
|
setIsOutlineEdit(state, data) {
|
||||||
|
state.isSourceCodeEdit = data
|
||||||
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
// 设置初始思维导图数据
|
// 设置初始思维导图数据
|
||||||
|
|||||||
@ -47,3 +47,13 @@ export const fileToBuffer = file => {
|
|||||||
reader.readAsArrayBuffer(file)
|
reader.readAsArrayBuffer(file)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 复制文本到剪贴板
|
||||||
|
export const copy = (text) => {
|
||||||
|
const input = document.createElement('input')
|
||||||
|
input.setAttribute('value', text)
|
||||||
|
document.body.appendChild(input)
|
||||||
|
input.select()
|
||||||
|
document.execCommand('copy')
|
||||||
|
document.body.removeChild(input)
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user