Demo:右键菜单新增设置节点编号功能

This commit is contained in:
街角小林 2024-08-14 09:41:33 +08:00
parent 68bf2d361c
commit 82473027da
10 changed files with 267 additions and 16 deletions

View File

@ -218,8 +218,8 @@ class Node {
this._tagData = this.createTagNode()
this._noteData = this.createNoteNode()
this._attachmentData = this.createAttachmentNode()
if (this.mindMap.number) {
this._numberData = this.mindMap.number.createNumberContent(this)
if (this.mindMap.numbers) {
this._numberData = this.mindMap.numbers.createNumberContent(this)
}
this._prefixData = createNodePrefixContent
? createNodePrefixContent(this)

View File

@ -72,13 +72,13 @@ class Base {
// 获取节点编号信息
getNumberInfo({ parent, ancestors, layerIndex, index }) {
// 编号
const hasNumberPlugin = !!this.mindMap.number
const hasNumberPlugin = !!this.mindMap.numbers
const parentNumberStr =
hasNumberPlugin && parent && parent._node.number
? parent._node.number
: ''
const newNumberStr = hasNumberPlugin
? this.mindMap.number.getNodeNumberStr({
? this.mindMap.numbers.getNodeNumberStr({
ancestors,
layerIndex,
num: index + 1,
@ -122,7 +122,7 @@ class Base {
// 判断编号是否改变
let isNumberChange = false
if (hasNumberPlugin) {
isNumberChange = this.mindMap.number.updateNumber(newNode, newNumberStr)
isNumberChange = this.mindMap.numbers.updateNumber(newNode, newNumberStr)
}
// 主题或主题配置改变了、节点层级改变了,需要重新渲染节点文本等情况需要重新计算节点大小和布局
if (
@ -167,7 +167,7 @@ class Base {
// 判断编号是否改变
let isNumberChange = false
if (hasNumberPlugin) {
isNumberChange = this.mindMap.number.updateNumber(newNode, newNumberStr)
isNumberChange = this.mindMap.numbers.updateNumber(newNode, newNumberStr)
}
if (
isResizeSource ||

View File

@ -497,3 +497,63 @@ export const downTypeList = [
desc: 'Plain text file'
}
]
// 编号类型列表
export const numberTypeList = [
{
name: 'None',
value: ''
},
{
name: '1, 2, 3',
value: 1
},
{
name: '1., 2., 3.',
value: 2
},
{
name: '(1), (2), (3)',
value: 3
},
{
name: 'a., b., c.',
value: 4
},
{
name: 'A., B., C.',
value: 5
},
{
name: 'i., ii., iii.',
value: 6
},
{
name: 'I., II., III.',
value: 7
},
{
name: '一、, 二、, 三、',
value: 8
}
]
// 编号层级列表
export const numberLevelList = [
{
name: '1 level',
value: 1
},
{
name: '2 level',
value: 2
},
{
name: '3 level',
value: 3
},
{
name: 'All level',
value: 0
}
]

View File

@ -19,7 +19,9 @@ import {
backgroundSizeList as backgroundSizeListZh,
downTypeList as downTypeListZh,
shapeListMap as shapeListMapZh,
lineStyleMap as lineStyleMapZh
lineStyleMap as lineStyleMapZh,
numberTypeList as numberTypeListZh,
numberLevelList as numberLevelListZh
} from './zh'
import {
fontFamilyList as fontFamilyListEn,
@ -32,7 +34,9 @@ import {
shapeList as shapeListEn,
sidebarTriggerList as sidebarTriggerListEn,
backgroundSizeList as backgroundSizeListEn,
downTypeList as downTypeListEn
downTypeList as downTypeListEn,
numberTypeList as numberTypeListEn,
numberLevelList as numberLevelListEn
} from './en'
const fontFamilyList = {
@ -100,6 +104,16 @@ const downTypeList = {
en: downTypeListEn
}
const numberTypeList = {
zh: numberTypeListZh,
en: numberTypeListEn
}
const numberLevelList = {
zh: numberLevelListZh,
en: numberLevelListEn
}
export {
fontSizeList,
lineHeightList,
@ -121,5 +135,7 @@ export {
shapeList,
shapeListMap,
sidebarTriggerList,
downTypeList
downTypeList,
numberTypeList,
numberLevelList
}

View File

@ -591,3 +591,63 @@ export const downTypeList = [
desc: '纯文本文件'
}
]
// 编号类型列表
export const numberTypeList = [
{
name: '无编号',
value: ''
},
{
name: '1, 2, 3',
value: 1
},
{
name: '1., 2., 3.',
value: 2
},
{
name: '(1), (2), (3)',
value: 3
},
{
name: 'a., b., c.',
value: 4
},
{
name: 'A., B., C.',
value: 5
},
{
name: 'i., ii., iii.',
value: 6
},
{
name: 'I., II., III.',
value: 7
},
{
name: '一、, 二、, 三、',
value: 8
}
]
// 编号层级列表
export const numberLevelList = [
{
name: '编号首层',
value: 1
},
{
name: '编号前两层',
value: 2
},
{
name: '编号前三层',
value: 3
},
{
name: '编号所有层',
value: 0
}
]

View File

@ -110,7 +110,8 @@ export default {
copyToTxt: 'Txt',
copyToPng: 'Png',
copySuccess: 'Copy success',
copyFail: 'Copy fail'
copyFail: 'Copy fail',
number: 'Number child nodes'
},
count: {
words: 'Words',

View File

@ -110,7 +110,8 @@ export default {
copyToTxt: 'Txt',
copyToPng: '图片',
copySuccess: '复制成功',
copyFail: '复制失败'
copyFail: '复制失败',
number: '编号其子节点'
},
count: {
words: '字数',

View File

@ -39,6 +39,7 @@
<span class="name">{{ $t('contextmenu.insertSummary') }}</span>
<span class="desc">Ctrl + G</span>
</div>
<div class="splitLine"></div>
<div
class="item"
@click="exec('UP_NODE')"
@ -55,6 +56,37 @@
<span class="name">{{ $t('contextmenu.moveDownNode') }}</span>
<span class="desc">Ctrl + </span>
</div>
<div class="item" 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="splitLine"></div>
<div class="item danger" @click="exec('REMOVE_NODE')">
<span class="name">{{ $t('contextmenu.deleteNode') }}</span>
<span class="desc">Delete</span>
@ -63,6 +95,7 @@
<span class="name">{{ $t('contextmenu.deleteCurrentNode') }}</span>
<span class="desc">Shift + Backspace</span>
</div>
<div class="splitLine"></div>
<div
class="item"
@click="exec('COPY_NODE')"
@ -83,6 +116,7 @@
<span class="name">{{ $t('contextmenu.pasteNode') }}</span>
<span class="desc">Ctrl + V</span>
</div>
<div class="splitLine"></div>
<div class="item" @click="exec('REMOVE_HYPERLINK')" v-if="hasHyperlink">
<span class="name">{{ $t('contextmenu.removeHyperlink') }}</span>
</div>
@ -101,6 +135,7 @@
<span class="name">{{ $t('contextmenu.backCenter') }}</span>
<span class="desc">Ctrl + Enter</span>
</div>
<div class="splitLine"></div>
<div class="item" @click="exec('EXPAND_ALL')">
<span class="name">{{ $t('contextmenu.expandAll') }}</span>
</div>
@ -110,7 +145,11 @@
<div class="item">
<span class="name">{{ $t('contextmenu.expandTo') }}</span>
<span class="el-icon-arrow-right"></span>
<div class="subItems listBox" :class="{ isDark: isDark }">
<div
class="subItems listBox"
:class="{ isDark: isDark, showLeft: subItemsShowLeft }"
style="top: -10px"
>
<div
class="item"
v-for="(item, index) in expandList"
@ -121,6 +160,7 @@
</div>
</div>
</div>
<div class="splitLine"></div>
<div class="item" @click="exec('RESET_LAYOUT')">
<span class="name">{{ $t('contextmenu.arrangeLayout') }}</span>
<span class="desc">Ctrl + L</span>
@ -133,6 +173,7 @@
<span class="name">{{ $t('contextmenu.zenMode') }}</span>
{{ isZenMode ? '√' : '' }}
</div>
<div class="splitLine"></div>
<div class="item" @click="exec('REMOVE_ALL_NODE_CUSTOM_STYLES')">
<span class="name">{{
$t('contextmenu.removeAllNodeCustomStyles')
@ -141,7 +182,11 @@
<div class="item">
<span class="name">{{ $t('contextmenu.copyToClipboard') }}</span>
<span class="el-icon-arrow-right"></span>
<div class="subItems listBox" :class="{ isDark: isDark }">
<div
class="subItems listBox"
:class="{ isDark: isDark, showLeft: subItemsShowLeft }"
style="top: -130px"
>
<div
class="item"
v-for="item in copyList"
@ -162,6 +207,7 @@ import { getTextFromHtml, imgToDataUrl } from 'simple-mind-map/src/utils'
import { transformToMarkdown } from 'simple-mind-map/src/parse/toMarkdown'
import { transformToTxt } from 'simple-mind-map/src/parse/toTxt'
import { setDataToClipboard, setImgToClipboard, copy } from '@/utils'
import { numberTypeList, numberLevelList } from '@/config'
/**
* @Author: 王林
@ -185,13 +231,17 @@ export default {
isMousedown: false,
mosuedownX: 0,
mosuedownY: 0,
enableCopyToClipboardApi: navigator.clipboard
enableCopyToClipboardApi: navigator.clipboard,
numberType: '',
numberLevel: '',
subItemsShowLeft: false
}
},
computed: {
...mapState({
isZenMode: state => state.localConfig.isZenMode,
isDark: state => state.localConfig.isDark
isDark: state => state.localConfig.isDark,
supportNumbers: state => state.supportNumbers
}),
expandList() {
return [
@ -263,6 +313,12 @@ export default {
},
hasNote() {
return !!this.node.getData('note')
},
numberTypeList() {
return numberTypeList[this.$i18n.locale] || numberTypeList.zh
},
numberLevelList() {
return numberLevelList[this.$i18n.locale] || numberLevelList.zh
}
},
created() {
@ -288,9 +344,11 @@ export default {
//
getShowPosition(x, y) {
this.subItemsShowLeft = false
const rect = this.$refs.contextmenuRef.getBoundingClientRect()
if (x + rect.width > window.innerWidth) {
x = x - rect.width - 20
this.subItemsShowLeft = true
}
if (y + rect.height > window.innerHeight) {
y = window.innerHeight - rect.height - 10
@ -303,6 +361,11 @@ export default {
this.type = 'node'
this.isShow = true
this.node = node
const number = this.node.getData('number')
if (number) {
this.numberType = number.type || 1
this.numberLevel = number.level === '' ? 1 : number.level
}
this.$nextTick(() => {
const { x, y } = this.getShowPosition(e.clientX + 10, e.clientY + 10)
this.left = x
@ -353,6 +416,9 @@ export default {
this.left = -9999
this.top = -9999
this.type = ''
this.node = ''
this.numberType = ''
this.numberLevel = ''
},
//
@ -403,6 +469,30 @@ 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
})
},
//
async copyToClipboard(type) {
try {
@ -478,6 +568,13 @@ export default {
}
}
.splitLine {
width: 95%;
height: 1px;
background-color: #e9edf2;
margin: 2px auto;
}
.item {
position: relative;
height: 28px;
@ -525,9 +622,13 @@ export default {
.subItems {
position: absolute;
left: 100%;
top: 0;
visibility: hidden;
width: 150px;
cursor: auto;
&.showLeft {
left: -150px;
}
}
}
}

View File

@ -77,6 +77,8 @@ import OuterFrame from 'simple-mind-map/src/plugins/OuterFrame.js'
// 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 OutlineSidebar from './OutlineSidebar'
import Style from './Style'
import BaseStyle from './BaseStyle'
@ -538,6 +540,10 @@ export default {
this.mindMap.addPlugin(Notation)
this.$store.commit('setSupportMark', true)
}
if (typeof Numbers !== 'undefined') {
this.mindMap.addPlugin(Numbers)
this.$store.commit('setSupportNumbers', true)
}
this.mindMap.keyCommand.addShortcut('Control+s', () => {
this.manualSave()
})

View File

@ -30,6 +30,7 @@ const store = new Vuex.Store({
extraTextOnExport: '', // 导出时底部添加的文字
supportHandDrawnLikeStyle: false, // 是否支持设置手绘风格
supportMark: false, // 是否支持标记
supportNumbers: false, // 是否支持编号
isDragOutlineTreeNode: false // 当前是否正在拖拽大纲树的节点
},
mutations: {
@ -87,6 +88,11 @@ const store = new Vuex.Store({
state.supportMark = data
},
// 设置是否支持编号
setSupportNumbers(state, data) {
state.supportNumbers = data
},
// 设置树节点拖拽
setIsDragOutlineTreeNode(state, data) {
state.isDragOutlineTreeNode = data