1.修复删除背景图片不生效的问题;2.背景图片展示增加位置和大小设置;3.修复节点拖拽到根节点时连接线跑到根节点上方的问题

This commit is contained in:
wanglin2 2023-01-31 15:04:38 +08:00
parent c26149fa42
commit f547f741f2
9 changed files with 1388 additions and 1206 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,167 +1,188 @@
import { tagColorList } from './utils/constant' import { tagColorList } from './utils/constant'
const rootProp = ['paddingX', 'paddingY'] const rootProp = ['paddingX', 'paddingY']
// 样式类 // 样式类
class Style {
// 设置背景样式 class Style {
static setBackgroundStyle(el, themeConfig) { // 设置背景样式
let { backgroundColor, backgroundImage, backgroundRepeat } = themeConfig
el.style.backgroundColor = backgroundColor static setBackgroundStyle(el, themeConfig) {
if (backgroundImage) { let { backgroundColor, backgroundImage, backgroundRepeat, backgroundPosition, backgroundSize } = themeConfig
el.style.backgroundImage = `url(${backgroundImage})` el.style.backgroundColor = backgroundColor
el.style.backgroundRepeat = backgroundRepeat if (backgroundImage) {
} el.style.backgroundImage = `url(${backgroundImage})`
} el.style.backgroundRepeat = backgroundRepeat
el.style.backgroundPosition = backgroundPosition
// 构造函数 el.style.backgroundSize = backgroundSize
constructor(ctx, themeConfig) { } else {
this.ctx = ctx el.style.backgroundImage = 'none'
this.themeConfig = themeConfig }
} }
// 更新主题配置 // 构造函数
updateThemeConfig(themeConfig) {
this.themeConfig = themeConfig constructor(ctx, themeConfig) {
} this.ctx = ctx
this.themeConfig = themeConfig
// 合并样式 }
merge(prop, root, isActive) {
// 三级及以下节点 // 更新主题配置
let defaultConfig = this.themeConfig.node
if (root || rootProp.includes(prop)) { updateThemeConfig(themeConfig) {
// 直接使用最外层样式 this.themeConfig = themeConfig
defaultConfig = this.themeConfig }
} else if (this.ctx.isGeneralization) {
// 概要节点 // 合并样式
defaultConfig = this.themeConfig.generalization
} else if (this.ctx.layerIndex === 0) { merge(prop, root, isActive) {
// 根节点 // 三级及以下节点
defaultConfig = this.themeConfig.root let defaultConfig = this.themeConfig.node
} else if (this.ctx.layerIndex === 1) { if (root || rootProp.includes(prop)) {
// 二级节点 // 直接使用最外层样式
defaultConfig = this.themeConfig.second defaultConfig = this.themeConfig
} } else if (this.ctx.isGeneralization) {
// 激活状态 // 概要节点
if (isActive !== undefined ? isActive : this.ctx.nodeData.data.isActive) { defaultConfig = this.themeConfig.generalization
if ( } else if (this.ctx.layerIndex === 0) {
this.ctx.nodeData.data.activeStyle && // 根节点
this.ctx.nodeData.data.activeStyle[prop] !== undefined defaultConfig = this.themeConfig.root
) { } else if (this.ctx.layerIndex === 1) {
return this.ctx.nodeData.data.activeStyle[prop] // 二级节点
} else if (defaultConfig.active && defaultConfig.active[prop]) { defaultConfig = this.themeConfig.second
return defaultConfig.active[prop] }
} // 激活状态
} if (isActive !== undefined ? isActive : this.ctx.nodeData.data.isActive) {
// 优先使用节点本身的样式 if (
return this.getSelfStyle(prop) !== undefined this.ctx.nodeData.data.activeStyle &&
? this.getSelfStyle(prop) this.ctx.nodeData.data.activeStyle[prop] !== undefined
: defaultConfig[prop] ) {
} return this.ctx.nodeData.data.activeStyle[prop]
} else if (defaultConfig.active && defaultConfig.active[prop]) {
// 获取某个样式值 return defaultConfig.active[prop]
getStyle(prop, root, isActive) { }
return this.merge(prop, root, isActive) }
} // 优先使用节点本身的样式
return this.getSelfStyle(prop) !== undefined
// 获取自身自定义样式 ? this.getSelfStyle(prop)
getSelfStyle(prop) { : defaultConfig[prop]
return this.ctx.nodeData.data[prop] }
}
// 获取某个样式值
// 矩形
rect(node) { getStyle(prop, root, isActive) {
this.shape(node) return this.merge(prop, root, isActive)
node.radius(this.merge('borderRadius')) }
}
// 获取自身自定义样式
// 矩形外的其他形状
shape(node) { getSelfStyle(prop) {
node.fill({ return this.ctx.nodeData.data[prop]
color: this.merge('fillColor') }
})
// 节点使用横线样式,不需要渲染非激活状态的边框样式 // 矩形
if (
!this.ctx.isRoot && rect(node) {
!this.ctx.isGeneralization && this.shape(node)
this.themeConfig.nodeUseLineStyle && node.radius(this.merge('borderRadius'))
!this.ctx.nodeData.data.isActive }
) {
return // 矩形外的其他形状
}
node.stroke({ shape(node) {
color: this.merge('borderColor'), node.fill({
width: this.merge('borderWidth'), color: this.merge('fillColor')
dasharray: this.merge('borderDasharray') })
}) // 节点使用横线样式,不需要渲染非激活状态的边框样式
} if (
!this.ctx.isRoot &&
// 文字 !this.ctx.isGeneralization &&
text(node) { this.themeConfig.nodeUseLineStyle &&
node !this.ctx.nodeData.data.isActive
.fill({ ) {
color: this.merge('color') return
}) }
.css({ node.stroke({
'font-family': this.merge('fontFamily'), color: this.merge('borderColor'),
'font-size': this.merge('fontSize'), width: this.merge('borderWidth'),
'font-weight': this.merge('fontWeight'), dasharray: this.merge('borderDasharray')
'font-style': this.merge('fontStyle'), })
'text-decoration': this.merge('textDecoration') }
})
} // 文字
// html文字节点 text(node) {
domText(node, fontSizeScale = 1) { node
node.style.fontFamily = this.merge('fontFamily') .fill({
node.style.fontSize = this.merge('fontSize') * fontSizeScale + 'px' color: this.merge('color')
node.style.fontWeight = this.merge('fontWeight') || 'normal' })
} .css({
'font-family': this.merge('fontFamily'),
// 标签文字 'font-size': this.merge('fontSize'),
tagText(node, index) { 'font-weight': this.merge('fontWeight'),
node 'font-style': this.merge('fontStyle'),
.fill({ 'text-decoration': this.merge('textDecoration')
color: tagColorList[index].color })
}) }
.css({
'font-size': '12px' // html文字节点
})
} domText(node, fontSizeScale = 1) {
node.style.fontFamily = this.merge('fontFamily')
// 标签矩形 node.style.fontSize = this.merge('fontSize') * fontSizeScale + 'px'
tagRect(node, index) { node.style.fontWeight = this.merge('fontWeight') || 'normal'
node.fill({ }
color: tagColorList[index].background
}) // 标签文字
}
tagText(node, index) {
// 内置图标 node
iconNode(node) { .fill({
node.attr({ color: tagColorList[index].color
fill: this.merge('color') })
}) .css({
} 'font-size': '12px'
})
// 连线 }
line(node, { width, color, dasharray } = {}) {
node.stroke({ width, color, dasharray }).fill({ color: 'none' }) // 标签矩形
}
tagRect(node, index) {
// 概要连线 node.fill({
generalizationLine(node) { color: tagColorList[index].background
node })
.stroke({ }
width: this.merge('generalizationLineWidth', true),
color: this.merge('generalizationLineColor', true) // 内置图标
})
.fill({ color: 'none' }) iconNode(node) {
} node.attr({
fill: this.merge('color')
// 按钮 })
iconBtn(node, fillNode) { }
node.fill({ color: '#808080' })
fillNode.fill({ color: '#fff' }) // 连线
}
} line(node, { width, color, dasharray } = {}) {
node.stroke({ width, color, dasharray }).fill({ color: 'none' })
export default Style }
// 概要连线
generalizationLine(node) {
node
.stroke({
width: this.merge('generalizationLineWidth', true),
color: this.merge('generalizationLineColor', true)
})
.fill({ color: 'none' })
}
// 按钮
iconBtn(node, fillNode) {
node.fill({ color: '#808080' })
fillNode.fill({ color: '#fff' })
}
}
export default Style

View File

@ -1,142 +1,147 @@
// 默认主题 // 默认主题
export default {
// 节点内边距 export default {
paddingX: 15, // 节点内边距
paddingY: 5, paddingX: 15,
// 图片显示的最大宽度 paddingY: 5,
imgMaxWidth: 100, // 图片显示的最大宽度
// 图片显示的最大高度 imgMaxWidth: 100,
imgMaxHeight: 100, // 图片显示的最大高度
// icon的大小 imgMaxHeight: 100,
iconSize: 20, // icon的大小
// 连线的粗细 iconSize: 20,
lineWidth: 1, // 连线的粗细
// 连线的颜色 lineWidth: 1,
lineColor: '#549688', // 连线的颜色
// 连线样式 lineColor: '#549688',
lineDasharray: 'none', // 连线样式
// 连线风格 lineDasharray: 'none',
lineStyle: 'straight', // 针对logicalStructure、mindMap两种结构。曲线curve、直线straight、直连direct // 连线风格
// 概要连线的粗细 lineStyle: 'straight', // 针对logicalStructure、mindMap两种结构。曲线curve、直线straight、直连direct
generalizationLineWidth: 1, // 概要连线的粗细
// 概要连线的颜色 generalizationLineWidth: 1,
generalizationLineColor: '#549688', // 概要连线的颜色
// 概要曲线距节点的距离 generalizationLineColor: '#549688',
generalizationLineMargin: 0, // 概要曲线距节点的距离
// 概要节点距节点的距离 generalizationLineMargin: 0,
generalizationNodeMargin: 20, // 概要节点距节点的距离
// 背景颜色 generalizationNodeMargin: 20,
backgroundColor: '#fafafa', // 背景颜色
// 背景图片 backgroundColor: '#fafafa',
backgroundImage: 'none', // 背景图片
// 背景重复 backgroundImage: 'none',
backgroundRepeat: 'no-repeat', // 背景重复
// 节点使用横线样式 backgroundRepeat: 'no-repeat',
nodeUseLineStyle: false, // 设置背景图像的起始位置
// 根节点样式 backgroundPosition: 'center center',
root: { // 设置背景图片大小
shape: 'rectangle', backgroundSize: 'cover',
fillColor: '#549688', // 节点使用横线样式
fontFamily: '微软雅黑, Microsoft YaHei', nodeUseLineStyle: false,
color: '#fff', // 根节点样式
fontSize: 16, root: {
fontWeight: 'bold', shape: 'rectangle',
fontStyle: 'normal', fillColor: '#549688',
lineHeight: 1.5, fontFamily: '微软雅黑, Microsoft YaHei',
borderColor: 'transparent', color: '#fff',
borderWidth: 0, fontSize: 16,
borderDasharray: 'none', fontWeight: 'bold',
borderRadius: 5, fontStyle: 'normal',
textDecoration: 'none', lineHeight: 1.5,
active: { borderColor: 'transparent',
borderColor: 'rgb(57, 80, 96)', borderWidth: 0,
borderWidth: 3, borderDasharray: 'none',
borderDasharray: 'none' borderRadius: 5,
} textDecoration: 'none',
}, active: {
// 二级节点样式 borderColor: 'rgb(57, 80, 96)',
second: { borderWidth: 3,
shape: 'rectangle', borderDasharray: 'none'
marginX: 100, }
marginY: 40, },
fillColor: '#fff', // 二级节点样式
fontFamily: '微软雅黑, Microsoft YaHei', second: {
color: '#565656', shape: 'rectangle',
fontSize: 16, marginX: 100,
fontWeight: 'noraml', marginY: 40,
fontStyle: 'normal', fillColor: '#fff',
lineHeight: 1.5, fontFamily: '微软雅黑, Microsoft YaHei',
borderColor: '#549688', color: '#565656',
borderWidth: 1, fontSize: 16,
borderDasharray: 'none', fontWeight: 'noraml',
borderRadius: 5, fontStyle: 'normal',
textDecoration: 'none', lineHeight: 1.5,
active: { borderColor: '#549688',
borderColor: 'rgb(57, 80, 96)', borderWidth: 1,
borderWidth: 3, borderDasharray: 'none',
borderDasharray: 'none' borderRadius: 5,
} textDecoration: 'none',
}, active: {
// 三级及以下节点样式 borderColor: 'rgb(57, 80, 96)',
node: { borderWidth: 3,
shape: 'rectangle', borderDasharray: 'none'
marginX: 50, }
marginY: 0, },
fillColor: 'transparent', // 三级及以下节点样式
fontFamily: '微软雅黑, Microsoft YaHei', node: {
color: '#6a6d6c', shape: 'rectangle',
fontSize: 14, marginX: 50,
fontWeight: 'noraml', marginY: 0,
fontStyle: 'normal', fillColor: 'transparent',
lineHeight: 1.5, fontFamily: '微软雅黑, Microsoft YaHei',
borderColor: 'transparent', color: '#6a6d6c',
borderWidth: 0, fontSize: 14,
borderRadius: 5, fontWeight: 'noraml',
borderDasharray: 'none', fontStyle: 'normal',
textDecoration: 'none', lineHeight: 1.5,
active: { borderColor: 'transparent',
borderColor: 'rgb(57, 80, 96)', borderWidth: 0,
borderWidth: 3, borderRadius: 5,
borderDasharray: 'none' borderDasharray: 'none',
} textDecoration: 'none',
}, active: {
// 概要节点样式 borderColor: 'rgb(57, 80, 96)',
generalization: { borderWidth: 3,
shape: 'rectangle', borderDasharray: 'none'
marginX: 100, }
marginY: 40, },
fillColor: '#fff', // 概要节点样式
fontFamily: '微软雅黑, Microsoft YaHei', generalization: {
color: '#565656', shape: 'rectangle',
fontSize: 16, marginX: 100,
fontWeight: 'noraml', marginY: 40,
fontStyle: 'normal', fillColor: '#fff',
lineHeight: 1.5, fontFamily: '微软雅黑, Microsoft YaHei',
borderColor: '#549688', color: '#565656',
borderWidth: 1, fontSize: 16,
borderDasharray: 'none', fontWeight: 'noraml',
borderRadius: 5, fontStyle: 'normal',
textDecoration: 'none', lineHeight: 1.5,
active: { borderColor: '#549688',
borderColor: 'rgb(57, 80, 96)', borderWidth: 1,
borderWidth: 3, borderDasharray: 'none',
borderDasharray: 'none' borderRadius: 5,
} textDecoration: 'none',
} active: {
} borderColor: 'rgb(57, 80, 96)',
borderWidth: 3,
// 支持激活样式的属性 borderDasharray: 'none'
// 简单来说,会改变节点大小的都不支持在激活时设置,为了性能考虑,节点切换激活态时不会重新计算节点大小 }
export const supportActiveStyle = [ }
'fillColor', }
'color',
'fontWeight', // 支持激活样式的属性
'fontStyle', // 简单来说,会改变节点大小的都不支持在激活时设置,为了性能考虑,节点切换激活态时不会重新计算节点大小
'borderColor', export const supportActiveStyle = [
'borderWidth', 'fillColor',
'borderDasharray', 'color',
'borderRadius', 'fontWeight',
'textDecoration' 'fontStyle',
] 'borderColor',
'borderWidth',
export const lineStyleProps = ['lineColor', 'lineDasharray', 'lineWidth'] 'borderDasharray',
'borderRadius',
'textDecoration'
]
export const lineStyleProps = ['lineColor', 'lineDasharray', 'lineWidth']

View File

@ -166,6 +166,22 @@ export const backgroundPositionList = [
} }
] ]
// 背景图片大小
export const backgroundSizeList = [
{
name: 'Auto',
value: 'auto'
},
{
name: 'Cover',
value: 'cover'
},
{
name: 'Contain',
value: 'contain'
}
]
// 快捷键列表 // 快捷键列表
export const shortcutKeyList = [ export const shortcutKeyList = [
{ {

View File

@ -14,7 +14,8 @@ import {
backgroundPositionList as backgroundPositionListZh, backgroundPositionList as backgroundPositionListZh,
shortcutKeyList as shortcutKeyListZh, shortcutKeyList as shortcutKeyListZh,
shapeList as shapeListZh, shapeList as shapeListZh,
sidebarTriggerList as sidebarTriggerListZh sidebarTriggerList as sidebarTriggerListZh,
backgroundSizeList as backgroundSizeListZh
} from './zh' } from './zh'
import { import {
fontFamilyList as fontFamilyListEn, fontFamilyList as fontFamilyListEn,
@ -24,7 +25,8 @@ import {
backgroundPositionList as backgroundPositionListEn, backgroundPositionList as backgroundPositionListEn,
shortcutKeyList as shortcutKeyListEn, shortcutKeyList as shortcutKeyListEn,
shapeList as shapeListEn, shapeList as shapeListEn,
sidebarTriggerList as sidebarTriggerListEn sidebarTriggerList as sidebarTriggerListEn,
backgroundSizeList as backgroundSizeListEn
} from './en' } from './en'
const fontFamilyList = { const fontFamilyList = {
@ -52,6 +54,11 @@ const backgroundPositionList = {
en: backgroundPositionListEn en: backgroundPositionListEn
} }
const backgroundSizeList = {
zh: backgroundSizeListZh,
en: backgroundSizeListEn
}
const shortcutKeyList = { const shortcutKeyList = {
zh: shortcutKeyListZh, zh: shortcutKeyListZh,
en: shortcutKeyListEn en: shortcutKeyListEn
@ -81,6 +88,7 @@ export {
lineStyleList, lineStyleList,
backgroundRepeatList, backgroundRepeatList,
backgroundPositionList, backgroundPositionList,
backgroundSizeList,
shortcutKeyList, shortcutKeyList,
shapeList, shapeList,
sidebarTriggerList sidebarTriggerList

View File

@ -221,6 +221,22 @@ export const backgroundPositionList = [
} }
] ]
// 背景图片大小
export const backgroundSizeList = [
{
name: '自动',
value: 'auto'
},
{
name: '覆盖',
value: 'cover'
},
{
name: '保持',
value: 'contain'
}
]
// 数据存储 // 数据存储
export const store = { export const store = {
sidebarZIndex: 1 //侧边栏zIndex sidebarZIndex: 1 //侧边栏zIndex

View File

@ -5,6 +5,8 @@ export default {
color: 'Color', color: 'Color',
image: 'Image', image: 'Image',
imageRepeat: 'Image repeat', imageRepeat: 'Image repeat',
imagePosition: 'Image position',
imageSize: 'Image size',
line: 'Line', line: 'Line',
width: 'Width', width: 'Width',
style: 'Style', style: 'Style',

View File

@ -5,6 +5,8 @@ export default {
color: '颜色', color: '颜色',
image: '图片', image: '图片',
imageRepeat: '图片重复', imageRepeat: '图片重复',
imagePosition: '图片位置',
imageSize: '图片大小',
line: '连线', line: '连线',
width: '粗细', width: '粗细',
style: '风格', style: '风格',

View File

@ -25,6 +25,7 @@
} }
" "
></ImgUpload> ></ImgUpload>
<!-- 图片重复方式 -->
<div class="rowItem"> <div class="rowItem">
<span class="name">{{ $t('baseStyle.imageRepeat') }}</span> <span class="name">{{ $t('baseStyle.imageRepeat') }}</span>
<el-select <el-select
@ -47,6 +48,52 @@
</el-option> </el-option>
</el-select> </el-select>
</div> </div>
<!-- 图片位置 -->
<div class="rowItem">
<span class="name">{{ $t('baseStyle.imagePosition') }}</span>
<el-select
size="mini"
style="width: 120px"
v-model="style.backgroundPosition"
placeholder=""
@change="
value => {
update('backgroundPosition', value)
}
"
>
<el-option
v-for="item in backgroundPositionList"
:key="item.value"
:label="item.name"
:value="item.value"
>
</el-option>
</el-select>
</div>
<!-- 图片大小 -->
<div class="rowItem">
<span class="name">{{ $t('baseStyle.imageSize') }}</span>
<el-select
size="mini"
style="width: 120px"
v-model="style.backgroundSize"
placeholder=""
@change="
value => {
update('backgroundSize', value)
}
"
>
<el-option
v-for="item in backgroundSizeList"
:key="item.value"
:label="item.name"
:value="item.value"
>
</el-option>
</el-select>
</div>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</div> </div>
@ -389,7 +436,7 @@
<script> <script>
import Sidebar from './Sidebar' import Sidebar from './Sidebar'
import Color from './Color' import Color from './Color'
import { lineWidthList, lineStyleList, backgroundRepeatList } from '@/config' import { lineWidthList, lineStyleList, backgroundRepeatList, backgroundPositionList, backgroundSizeList } from '@/config'
import ImgUpload from '@/components/ImgUpload' import ImgUpload from '@/components/ImgUpload'
import { storeConfig } from '@/api' import { storeConfig } from '@/api'
import { mapState } from 'vuex' import { mapState } from 'vuex'
@ -434,6 +481,8 @@ export default {
iconSize: 0, iconSize: 0,
backgroundImage: '', backgroundImage: '',
backgroundRepeat: 'no-repeat', backgroundRepeat: 'no-repeat',
backgroundPosition: '',
backgroundSize: '',
marginX: 0, marginX: 0,
marginY: 0, marginY: 0,
nodeUseLineStyle: false nodeUseLineStyle: false
@ -464,7 +513,13 @@ export default {
}, },
backgroundRepeatList() { backgroundRepeatList() {
return backgroundRepeatList[this.$i18n.locale] || backgroundRepeatList.zh return backgroundRepeatList[this.$i18n.locale] || backgroundRepeatList.zh
} },
backgroundPositionList() {
return backgroundPositionList[this.$i18n.locale] || backgroundPositionList.zh
},
backgroundSizeList() {
return backgroundSizeList[this.$i18n.locale] || backgroundSizeList.zh
},
}, },
watch: { watch: {
activeSidebar(val) { activeSidebar(val) {
@ -499,6 +554,8 @@ export default {
'iconSize', 'iconSize',
'backgroundImage', 'backgroundImage',
'backgroundRepeat', 'backgroundRepeat',
'backgroundPosition',
'backgroundSize',
'nodeUseLineStyle' 'nodeUseLineStyle'
].forEach(key => { ].forEach(key => {
this.style[key] = this.mindMap.getThemeConfig(key) this.style[key] = this.mindMap.getThemeConfig(key)