Demo:新增外框设置功能

This commit is contained in:
街角小林 2024-07-01 17:22:10 +08:00
parent 53eb608007
commit 1fc5b951a0
10 changed files with 401 additions and 6 deletions

View File

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 2479351 */
src: url('iconfont.woff2?t=1718261316837') format('woff2'),
url('iconfont.woff?t=1718261316837') format('woff'),
url('iconfont.ttf?t=1718261316837') format('truetype');
src: url('iconfont.woff2?t=1719815803051') format('woff2'),
url('iconfont.woff?t=1719815803051') format('woff'),
url('iconfont.ttf?t=1719815803051') format('truetype');
}
.iconfont {
@ -13,6 +13,10 @@
-moz-osx-font-smoothing: grayscale;
}
.iconwaikuang:before {
content: "\e640";
}
.iconhighlight:before {
content: "\e6b8";
}

View File

@ -266,6 +266,7 @@ export default {
painter: 'Painter',
formula: 'Formula',
attachment: 'Attachment',
outerFrame: 'Outer frame',
more: 'More',
selectFileTip: 'Please select a file',
notSupportTip:
@ -357,5 +358,12 @@ export default {
lineWidth: 'Line width',
padding: 'Padding',
animate: 'Animate'
},
nodeOuterFrame: {
outerFrameSetting: 'Setting',
deleteOuterFrame: 'Delete outer frame',
boxStyle: 'Box style',
boxColor: 'Box color',
fillColor: 'Fill color'
}
}

View File

@ -263,6 +263,7 @@ export default {
painter: '格式刷',
formula: '公式',
attachment: '附件',
outerFrame: '外框',
more: '更多',
selectFileTip: '请选择文件',
notSupportTip: '你的浏览器不支持该功能或者当前页面非https协议',
@ -349,5 +350,12 @@ export default {
lineWidth: '线宽',
padding: '内边距',
animate: '开启动画'
},
nodeOuterFrame: {
outerFrameSetting: '外框设置',
deleteOuterFrame: '删除外框',
boxStyle: '边框样式',
boxColor: '边框颜色',
fillColor: '填充颜色'
}
}

View File

@ -36,6 +36,7 @@
<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>
<div
class="dragMask"
v-if="showDragMask"
@ -68,6 +69,7 @@ import ScrollbarPlugin from 'simple-mind-map/src/plugins/Scrollbar.js'
import Formula from 'simple-mind-map/src/plugins/Formula.js'
import RainbowLines from 'simple-mind-map/src/plugins/RainbowLines.js'
import Demonstrate from 'simple-mind-map/src/plugins/Demonstrate.js'
import OuterFrame from 'simple-mind-map/src/plugins/OuterFrame.js'
//
// import Cooperate from 'simple-mind-map/src/plugins/Cooperate.js'
//
@ -109,6 +111,7 @@ 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'
//
MindMap.usePlugin(MiniMap)
@ -127,6 +130,7 @@ MindMap.usePlugin(MiniMap)
.usePlugin(Formula)
.usePlugin(RainbowLines)
.usePlugin(Demonstrate)
.usePlugin(OuterFrame)
// .usePlugin(Cooperate) //
//
@ -163,7 +167,8 @@ export default {
Scrollbar,
FormulaSidebar,
SourceCodeEdit,
NodeAttachment
NodeAttachment,
NodeOuterFrame
},
data() {
return {
@ -404,7 +409,7 @@ export default {
cssText,
height: 30
}
},
}
// createNodePrefixContent: (node) => {
// const el = document.createElement('div')
// el.style.width = '50px'

View File

@ -0,0 +1,358 @@
<template>
<div
class="nodeOuterFrameContainer"
ref="elRef"
:style="position"
v-show="show"
:class="{ isDark: isDark }"
>
<div class="btn" @click="showPanel = !showPanel">
<span class="iconfont iconjingzi"></span>
</div>
<div class="panel" v-if="showPanel">
<div class="panelHeader">
<span class="name">{{ $t('nodeOuterFrame.outerFrameSetting') }}</span>
<span class="deleteBtn" @click="deleteOuterFrame">
{{ $t('nodeOuterFrame.deleteOuterFrame') }}
<span class="iconfont iconshanchu"></span>
</span>
</div>
<div class="panelBody">
<div class="row">
<div class="rowItem">
<span class="name">{{ $t('nodeOuterFrame.boxStyle') }}</span>
<!-- 宽度 -->
<el-select
size="mini"
style="width: 80px"
v-model="styleConfig.strokeWidth"
placeholder=""
@change="
value => {
updateOuterFrame('strokeWidth', value)
}
"
>
<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>
<!-- 实现虚线 -->
<el-select
size="mini"
style="width: 80px;margin-left: 4px;"
v-model="styleConfig.strokeDasharray"
placeholder=""
@change="
value => {
updateOuterFrame('strokeDasharray', value)
}
"
>
<el-option
v-for="item in borderDasharrayList"
:key="item.value"
:label="item.name"
:value="item.value"
>
<svg width="120" height="34">
<line
x1="10"
y1="17"
x2="110"
y2="17"
stroke-width="2"
:stroke="
styleConfig.strokeDasharray === item.value
? '#409eff'
: isDark
? '#fff'
: '#000'
"
:stroke-dasharray="item.value"
></line>
</svg>
</el-option>
</el-select>
</div>
</div>
<div class="row">
<div class="rowItem">
<span class="name">{{ $t('nodeOuterFrame.boxColor') }}</span>
<span
class="block"
v-popover:popover
:style="{ backgroundColor: styleConfig.strokeColor }"
></span>
<el-popover ref="popover" placement="bottom" trigger="click">
<Color
:color="styleConfig.strokeColor"
@change="
color => {
updateOuterFrame('strokeColor', color)
}
"
></Color>
</el-popover>
</div>
</div>
<div class="row">
<div class="rowItem">
<span class="name">{{ $t('nodeOuterFrame.fillColor') }}</span>
<span
class="block"
v-popover:popover2
:style="{ backgroundColor: styleConfig.fill }"
></span>
<el-popover ref="popover2" placement="bottom" trigger="click">
<Color
:color="styleConfig.fill"
@change="
color => {
updateOuterFrame('fill', color)
}
"
></Color>
</el-popover>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Color from './Color'
import { mapState } from 'vuex'
import { lineWidthList, borderDasharrayList } from '@/config'
export default {
components: {
Color
},
props: {
mindMap: {
type: Object
}
},
data() {
return {
lineWidthList,
show: false,
showPanel: false,
position: {
left: 0,
top: 0
},
styleConfig: {
radius: 5,
strokeWidth: 2,
strokeColor: '#0984e3',
strokeDasharray: '5,5',
fill: 'rgba(9,132,227,0.05)'
}
}
},
computed: {
...mapState({
isDark: state => state.localConfig.isDark,
borderDasharrayList() {
return borderDasharrayList[this.$i18n.locale] || borderDasharrayList.zh
}
})
},
created() {
this.mindMap.on('outer_frame_active', this.onOuterFrameActive)
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)
this.mindMap.on('outer_frame_delete', this.hide)
},
beforeDestroy() {
this.mindMap.off('outer_frame_active', this.onOuterFrameActive)
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)
this.mindMap.off('outer_frame_delete', this.hide)
},
mounted() {
document.body.appendChild(this.$refs.elRef)
},
methods: {
onOuterFrameActive(el, parentNode, range) {
//
const firstNode = parentNode.children[range[0]]
const firstNodeOuterFrame = firstNode.getData('outerFrame')
Object.keys(firstNodeOuterFrame).forEach(key => {
this.styleConfig[key] = firstNodeOuterFrame[key]
})
//
const { x, y, width } = el.rbox()
this.position.left = x + width + 'px'
this.position.top = y + 'px'
this.show = true
},
updateOuterFrame(key, val) {
this.styleConfig[key] = val
this.mindMap.outerFrame.updateActiveOuterFrame({
[key]: val
})
this.hide()
},
deleteOuterFrame() {
this.mindMap.outerFrame.removeActiveOuterFrame()
},
hide() {
this.show = false
this.showPanel = false
}
}
}
</script>
<style lang="less">
.el-select-dropdown__item.selected {
.borderLine {
background-color: #409eff;
}
}
</style>
<style lang="less" scoped>
.nodeOuterFrameContainer {
position: fixed;
transform: translate(-12px, -12px);
&.isDark {
.panel {
background-color: #262a2e;
border-left-color: hsla(0, 0%, 100%, 0.1);
.panelHeader {
.name {
color: #fff;
}
}
.panelBody {
.row {
.rowItem {
.name {
color: hsla(0, 0%, 100%, 0.6);
}
}
}
}
}
}
.btn {
width: 24px;
height: 24px;
background-color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: 0 2px 16px 0 rgba(0, 0, 0, 0.06);
border: 1px solid rgba(0, 0, 0, 0.06);
}
.panel {
position: absolute;
left: 0;
top: 24px;
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);
width: 250px;
padding: 12px;
.panelHeader {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
.name {
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: rgba(26, 26, 26, 0.9);
}
.deleteBtn {
display: flex;
align-items: center;
color: #909090;
font-size: 14px;
cursor: pointer;
user-select: none;
.iconfont {
margin-left: 2px;
font-size: 14px;
}
}
}
.panelBody {
.row {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
&:last-of-type {
margin-bottom: 0px;
}
.rowItem {
display: flex;
align-items: center;
.name {
font-size: 12px;
margin-right: 10px;
white-space: nowrap;
}
.block {
display: inline-block;
width: 20px;
height: 20px;
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

@ -194,7 +194,8 @@ export default {
'associativeLine',
'formula',
// 'attachment',
'annotation'
'annotation',
'outerFrame'
],
horizontalList: [],
verticalList: [],

View File

@ -167,6 +167,17 @@
<span class="icon iconfont iconfujian"></span>
<span class="text">{{ $t('toolbar.attachment') }}</span>
</div>
<div
v-if="item === 'outerFrame'"
class="toolbarBtn"
:class="{
disabled: activeNodes.length <= 0 || hasGeneralization
}"
@click="$bus.$emit('execCommand', 'ADD_OUTER_FRAME')"
>
<span class="icon iconfont iconwaikuang"></span>
<span class="text">{{ $t('toolbar.outerFrame') }}</span>
</div>
<NodeAnnotationBtn
v-if="item === 'annotation' && supportMark"
:isDark="isDark"