mirror of
https://github.com/wanglin2/mind-map.git
synced 2026-02-21 10:27:44 +08:00
Demo:新增外框设置功能
This commit is contained in:
parent
53eb608007
commit
1fc5b951a0
@ -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";
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
@ -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: '填充颜色'
|
||||
}
|
||||
}
|
||||
|
||||
@ -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'
|
||||
|
||||
358
web/src/pages/Edit/components/NodeOuterFrame.vue
Normal file
358
web/src/pages/Edit/components/NodeOuterFrame.vue
Normal 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>
|
||||
@ -194,7 +194,8 @@ export default {
|
||||
'associativeLine',
|
||||
'formula',
|
||||
// 'attachment',
|
||||
'annotation'
|
||||
'annotation',
|
||||
'outerFrame'
|
||||
],
|
||||
horizontalList: [],
|
||||
verticalList: [],
|
||||
|
||||
@ -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"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user