Demo:支持点击节点标签进行文本和颜色的修改

This commit is contained in:
街角小林 2024-07-09 15:57:46 +08:00
parent 12c6479c0d
commit 4777ab3e58
5 changed files with 260 additions and 2 deletions

View File

@ -97,7 +97,7 @@ export const colorList = [
'#0C797D',
'#0062B1',
'#653294',
'#AB149E',
// '#AB149E',
'transparent'
]

View File

@ -373,5 +373,9 @@ export default {
boxStyle: 'Box style',
boxColor: 'Box color',
fillColor: 'Fill color'
},
nodeTagStyle: {
placeholder: 'Please enter the tag content',
delete: 'Delete this tag'
}
}

View File

@ -365,5 +365,9 @@ export default {
boxStyle: '边框样式',
boxColor: '边框颜色',
fillColor: '填充颜色'
},
nodeTagStyle: {
placeholder: '请输入标签内容',
delete: '删除此标签'
}
}

View File

@ -37,6 +37,7 @@
<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>
<div
class="dragMask"
v-if="showDragMask"
@ -112,6 +113,7 @@ 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'
//
MindMap.usePlugin(MiniMap)
@ -168,7 +170,8 @@ export default {
FormulaSidebar,
SourceCodeEdit,
NodeAttachment,
NodeOuterFrame
NodeOuterFrame,
NodeTagStyle
},
data() {
return {

View File

@ -0,0 +1,247 @@
<template>
<div
class="nodeTagStyleContainer"
ref="elRef"
:style="position"
v-show="show"
:class="{ isDark: isDark }"
>
<div class="row">
<el-input
v-model="text"
:placeholder="$t('nodeTagStyle.placeholder')"
size="mini"
@blur="updateTagText"
@keydown.native.stop
@keyup.native.enter.stop="updateTagText"
></el-input>
<div class="deleteBtn" @click.stop="deleteTag">
<span class="iconfont iconshanchu"></span>
<span class="text">{{ $t('nodeTagStyle.delete') }}</span>
</div>
</div>
<div class="row">
<Color :color="fill" @change="updateTagFill"></Color>
</div>
</div>
</template>
<script>
import Color from './Color'
import { mapState } from 'vuex'
export default {
components: {
Color
},
props: {
mindMap: {
type: Object
}
},
data() {
return {
show: false,
position: {
left: 0,
top: 0
},
node: null,
index: 0,
text: '',
fill: ''
}
},
computed: {
...mapState({
isDark: state => state.localConfig.isDark
})
},
created() {
this.mindMap.on('node_tag_click', this.onNodeTagClick)
this.mindMap.on('scale', this.hide)
this.mindMap.on('translate', this.hide)
this.mindMap.on('svg_mousedown', this.hide)
this.mindMap.on('expand_btn_click', this.hide)
},
beforeDestroy() {
this.mindMap.off('node_tag_click', this.onNodeTagClick)
this.mindMap.off('scale', this.hide)
this.mindMap.off('translate', this.hide)
this.mindMap.off('svg_mousedown', this.hide)
this.mindMap.off('expand_btn_click', this.hide)
},
mounted() {
document.body.appendChild(this.$refs.elRef)
},
methods: {
onNodeTagClick(node, tag, index, el) {
this.node = node
this.index = index
if (typeof tag === 'string') {
this.text = tag
} else {
// v0.10.3+
this.text = tag.text
this.fill = tag.style && tag.style.fill ? tag.style.fill : ''
}
//
const { x, y, width, height } = el.rbox()
const boxWidth = 260
const boxHeight = 152
let left = x + width / 2 - boxWidth / 2
if (left < 0) {
left = 0
}
if (left + boxWidth > window.innerWidth) {
left = window.innerWidth - boxWidth
}
this.position.left = left + 'px'
let top = y + height + 5
if (top + boxHeight > window.innerHeight) {
top = window.innerHeight - boxHeight
}
this.position.top = top + 'px'
this.show = true
},
updateTagText() {
const text = this.text.trim()
if (!text) {
return
}
this.updateTagInfo({
text
})
},
updateTagFill(color) {
this.updateTagInfo({
style: {
fill: color
}
})
this.fill = color
},
updateTagInfo({ text, style }) {
if (!this.node) return
const tagData = [...this.node.getData('tag')]
let item = null
if (typeof tagData[this.index] === 'string') {
item = {
text: tagData[this.index],
style: {}
}
} else {
item = tagData[this.index]
if (!item.style) {
item.style = {}
}
}
if (text) {
item.text = text
}
if (style) {
Object.keys(style).forEach(key => {
item.style[key] = style[key]
})
}
tagData[this.index] = item
this.mindMap.execCommand('SET_NODE_TAG', this.node, tagData)
},
deleteTag() {
if (!this.node) return
const tagData = [...this.node.getData('tag')]
tagData.splice(this.index, 1)
this.mindMap.execCommand('SET_NODE_TAG', this.node, tagData)
this.hide()
},
hide() {
this.show = false
this.node = null
this.index = 0
this.text = ''
this.fill = ''
}
}
}
</script>
<style lang="less" scoped>
.nodeTagStyleContainer {
position: fixed;
width: 260px;
padding: 12px;
background-color: #fff;
border-radius: 5px;
box-shadow: 0 2px 16px 0 rgba(0, 0, 0, 0.06);
border: 1px solid rgba(0, 0, 0, 0.06);
&.isDark {
background-color: #262a2e;
border-left-color: hsla(0, 0%, 100%, 0.1);
}
.row {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
&:last-of-type {
margin-bottom: 0px;
}
.colorItem {
display: inline-block;
width: 20px;
height: 20px;
border: 1px solid #dcdfe6;
border-radius: 4px;
cursor: pointer;
}
.colorItemBox {
height: 20px;
cursor: pointer;
.colorTriggerBtn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.colorItem {
height: 5px;
}
}
}
.deleteBtn {
white-space: nowrap;
display: flex;
align-items: center;
margin-left: 5px;
cursor: pointer;
color: #9aa5b8;
font-size: 12px;
user-select: none;
&:hover {
color: #eb5555;
}
.iconfont {
font-size: 12px;
margin-right: 2px;
}
.text {
}
}
}
}
</style>