Demo:新增节点标记功能
This commit is contained in:
parent
d14fb0b666
commit
e7f1608605
@ -314,7 +314,8 @@ export const nodeDataNoStylePropList = [
|
|||||||
'associativeLinePoint',
|
'associativeLinePoint',
|
||||||
'associativeLineText',
|
'associativeLineText',
|
||||||
'attachmentUrl',
|
'attachmentUrl',
|
||||||
'attachmentName'
|
'attachmentName',
|
||||||
|
'notation'
|
||||||
]
|
]
|
||||||
|
|
||||||
// 错误类型
|
// 错误类型
|
||||||
|
|||||||
@ -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=1713438156457') format('woff2'),
|
src: url('iconfont.woff2?t=1718261316837') format('woff2'),
|
||||||
url('iconfont.woff?t=1713438156457') format('woff'),
|
url('iconfont.woff?t=1718261316837') format('woff'),
|
||||||
url('iconfont.ttf?t=1713438156457') format('truetype');
|
url('iconfont.ttf?t=1718261316837') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
@ -13,6 +13,10 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.iconhighlight:before {
|
||||||
|
content: "\e6b8";
|
||||||
|
}
|
||||||
|
|
||||||
.iconyanshibofang:before {
|
.iconyanshibofang:before {
|
||||||
content: "\e648";
|
content: "\e648";
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -278,8 +278,10 @@ export default {
|
|||||||
defaultFileName: 'Mind map',
|
defaultFileName: 'Mind map',
|
||||||
creatingTip: 'Creating file',
|
creatingTip: 'Creating file',
|
||||||
directory: 'Directory',
|
directory: 'Directory',
|
||||||
newFileTip: 'Please export the currently edited file before creating a new one, otherwise the content will be lost',
|
newFileTip:
|
||||||
openFileTip: 'Please export the currently edited file before opening it, otherwise the content will be lost'
|
'Please export the currently edited file before creating a new one, otherwise the content will be lost',
|
||||||
|
openFileTip:
|
||||||
|
'Please export the currently edited file before opening it, otherwise the content will be lost'
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
newFeatureNoticeTitle: 'New feature reminder',
|
newFeatureNoticeTitle: 'New feature reminder',
|
||||||
@ -333,7 +335,8 @@ export default {
|
|||||||
loading: 'Loading, please wait...'
|
loading: 'Loading, please wait...'
|
||||||
},
|
},
|
||||||
sourceCodeEdit: {
|
sourceCodeEdit: {
|
||||||
sourceCodeTip: 'It is not recommended to modify the style in rich text mode because it requires synchronous modification of data and HTML structure.',
|
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',
|
format: 'Format',
|
||||||
copy: 'Copy',
|
copy: 'Copy',
|
||||||
confirm: 'Complete',
|
confirm: 'Complete',
|
||||||
@ -345,5 +348,14 @@ export default {
|
|||||||
attachment: {
|
attachment: {
|
||||||
deleteAttachment: 'Delete attachment',
|
deleteAttachment: 'Delete attachment',
|
||||||
tip: 'The attachment function is only available on the client side'
|
tip: 'The attachment function is only available on the client side'
|
||||||
|
},
|
||||||
|
annotation: {
|
||||||
|
mark: 'Mark',
|
||||||
|
show: 'Show mark',
|
||||||
|
type: 'Type',
|
||||||
|
color: 'Color',
|
||||||
|
lineWidth: 'Line width',
|
||||||
|
padding: 'Padding',
|
||||||
|
animate: 'Animate'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -327,7 +327,8 @@ export default {
|
|||||||
loading: '正在加载,请稍后...'
|
loading: '正在加载,请稍后...'
|
||||||
},
|
},
|
||||||
sourceCodeEdit: {
|
sourceCodeEdit: {
|
||||||
sourceCodeTip: '富文本模式下不建议修改样式,因为需要同步修改数据及html结构。',
|
sourceCodeTip:
|
||||||
|
'富文本模式下不建议修改样式,因为需要同步修改数据及html结构。',
|
||||||
format: '格式化',
|
format: '格式化',
|
||||||
copy: '复制',
|
copy: '复制',
|
||||||
confirm: '完成',
|
confirm: '完成',
|
||||||
@ -339,5 +340,14 @@ export default {
|
|||||||
attachment: {
|
attachment: {
|
||||||
deleteAttachment: '删除附件',
|
deleteAttachment: '删除附件',
|
||||||
tip: '附件功能仅在客户端可用'
|
tip: '附件功能仅在客户端可用'
|
||||||
|
},
|
||||||
|
annotation: {
|
||||||
|
mark: '标记',
|
||||||
|
show: '显示标记',
|
||||||
|
type: '类型',
|
||||||
|
color: '颜色',
|
||||||
|
lineWidth: '线宽',
|
||||||
|
padding: '内边距',
|
||||||
|
animate: '开启动画'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,6 +72,8 @@ import Demonstrate from 'simple-mind-map/src/plugins/Demonstrate.js'
|
|||||||
// import Cooperate from 'simple-mind-map/src/plugins/Cooperate.js'
|
// import Cooperate from 'simple-mind-map/src/plugins/Cooperate.js'
|
||||||
// 手绘风格插件,该插件为付费插件,详情请查看开发文档
|
// 手绘风格插件,该插件为付费插件,详情请查看开发文档
|
||||||
// import HandDrawnLikeStyle from 'simple-mind-map-plugin-handdrawnlikestyle'
|
// import HandDrawnLikeStyle from 'simple-mind-map-plugin-handdrawnlikestyle'
|
||||||
|
// 标记插件,该插件为付费插件,详情请查看开发文档
|
||||||
|
// import Notation from 'simple-mind-map-plugin-notation'
|
||||||
import OutlineSidebar from './OutlineSidebar'
|
import OutlineSidebar from './OutlineSidebar'
|
||||||
import Style from './Style'
|
import Style from './Style'
|
||||||
import BaseStyle from './BaseStyle'
|
import BaseStyle from './BaseStyle'
|
||||||
@ -493,6 +495,13 @@ export default {
|
|||||||
if (this.openNodeRichText) this.addRichTextPlugin()
|
if (this.openNodeRichText) this.addRichTextPlugin()
|
||||||
if (this.isShowScrollbar) this.addScrollbarPlugin()
|
if (this.isShowScrollbar) this.addScrollbarPlugin()
|
||||||
if (this.isUseHandDrawnLikeStyle) this.addHandDrawnLikeStylePlugin()
|
if (this.isUseHandDrawnLikeStyle) this.addHandDrawnLikeStylePlugin()
|
||||||
|
if (typeof HandDrawnLikeStyle !== 'undefined') {
|
||||||
|
this.$store.commit('setSupportHandDrawnLikeStyle', true)
|
||||||
|
}
|
||||||
|
if (typeof Notation !== 'undefined') {
|
||||||
|
this.mindMap.addPlugin(Notation)
|
||||||
|
this.$store.commit('setSupportMark', true)
|
||||||
|
}
|
||||||
this.mindMap.keyCommand.addShortcut('Control+s', () => {
|
this.mindMap.keyCommand.addShortcut('Control+s', () => {
|
||||||
this.manualSave()
|
this.manualSave()
|
||||||
})
|
})
|
||||||
|
|||||||
269
web/src/pages/Edit/components/NodeAnnotationBtn.vue
Normal file
269
web/src/pages/Edit/components/NodeAnnotationBtn.vue
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
<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"
|
||||||
|
:style="{
|
||||||
|
marginLeft: dir === 'v' ? '0px' : '20px',
|
||||||
|
marginTop: dir === 'v' ? '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'
|
||||||
|
|
||||||
|
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: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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>
|
||||||
@ -189,8 +189,9 @@ export default {
|
|||||||
'tag',
|
'tag',
|
||||||
'summary',
|
'summary',
|
||||||
'associativeLine',
|
'associativeLine',
|
||||||
'formula'
|
'formula',
|
||||||
// 'attachment'
|
// 'attachment',
|
||||||
|
'annotation'
|
||||||
],
|
],
|
||||||
horizontalList: [],
|
horizontalList: [],
|
||||||
verticalList: [],
|
verticalList: [],
|
||||||
|
|||||||
@ -167,14 +167,22 @@
|
|||||||
<span class="icon iconfont iconfujian"></span>
|
<span class="icon iconfont iconfujian"></span>
|
||||||
<span class="text">{{ $t('toolbar.attachment') }}</span>
|
<span class="text">{{ $t('toolbar.attachment') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<NodeAnnotationBtn
|
||||||
|
v-if="item === 'annotation' && supportMark"
|
||||||
|
:isDark="isDark"
|
||||||
|
:dir="dir"
|
||||||
|
@setAnnotation="onSetAnnotation"
|
||||||
|
></NodeAnnotationBtn>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapMutations } from 'vuex'
|
import { mapState, mapMutations } from 'vuex'
|
||||||
|
import NodeAnnotationBtn from './NodeAnnotationBtn.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: { NodeAnnotationBtn },
|
||||||
props: {
|
props: {
|
||||||
dir: {
|
dir: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -200,7 +208,8 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
isDark: state => state.localConfig.isDark
|
isDark: state => state.localConfig.isDark,
|
||||||
|
supportMark: state => state.supportMark
|
||||||
}),
|
}),
|
||||||
hasRoot() {
|
hasRoot() {
|
||||||
return (
|
return (
|
||||||
@ -274,12 +283,17 @@ export default {
|
|||||||
// 选择附件
|
// 选择附件
|
||||||
selectAttachmentFile() {
|
selectAttachmentFile() {
|
||||||
this.$bus.$emit('selectAttachment', this.activeNodes)
|
this.$bus.$emit('selectAttachment', this.activeNodes)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 设置标记
|
||||||
|
onSetAnnotation(...args) {
|
||||||
|
this.$bus.$emit('execCommand', 'SET_NOTATION', this.activeNodes, ...args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less">
|
||||||
.toolbarNodeBtnList {
|
.toolbarNodeBtnList {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
|
|||||||
@ -21,13 +21,15 @@ const store = new Vuex.Store({
|
|||||||
// 是否开启手绘风格
|
// 是否开启手绘风格
|
||||||
isUseHandDrawnLikeStyle: false,
|
isUseHandDrawnLikeStyle: false,
|
||||||
// 是否是暗黑模式
|
// 是否是暗黑模式
|
||||||
isDark: false,
|
isDark: false
|
||||||
},
|
},
|
||||||
activeSidebar: '', // 当前显示的侧边栏
|
activeSidebar: '', // 当前显示的侧边栏
|
||||||
isOutlineEdit: false, // 是否是大纲编辑模式
|
isOutlineEdit: false, // 是否是大纲编辑模式
|
||||||
isReadonly: false, // 是否只读
|
isReadonly: false, // 是否只读
|
||||||
isSourceCodeEdit: false, // 是否是源码编辑模式
|
isSourceCodeEdit: false, // 是否是源码编辑模式
|
||||||
extraTextOnExport: ''// 导出时底部添加的文字
|
extraTextOnExport: '', // 导出时底部添加的文字
|
||||||
|
supportHandDrawnLikeStyle: false, // 是否支持设置手绘风格
|
||||||
|
supportMark: false // 是否支持标记
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
// 设置思维导图数据
|
// 设置思维导图数据
|
||||||
@ -72,6 +74,16 @@ const store = new Vuex.Store({
|
|||||||
// 设置导出时底部添加的文字
|
// 设置导出时底部添加的文字
|
||||||
setExtraTextOnExport(state, data) {
|
setExtraTextOnExport(state, data) {
|
||||||
state.extraTextOnExport = data
|
state.extraTextOnExport = data
|
||||||
|
},
|
||||||
|
|
||||||
|
// 设置是否支持手绘风格
|
||||||
|
setSupportHandDrawnLikeStyle(state, data) {
|
||||||
|
state.supportHandDrawnLikeStyle = data
|
||||||
|
},
|
||||||
|
|
||||||
|
// 设置是否支持标记
|
||||||
|
setSupportMark(state, data) {
|
||||||
|
state.supportMark = data
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user