Feat:1.支持定义标签样式;2.新增标签显示位置的实例化选项;
This commit is contained in:
parent
ba9a6e501a
commit
f79918ec6f
@ -235,6 +235,10 @@ export const CONSTANTS = {
|
|||||||
DEFAULT: 'default',
|
DEFAULT: 'default',
|
||||||
NOT_ACTIVE: 'notActive',
|
NOT_ACTIVE: 'notActive',
|
||||||
ACTIVE_ONLY: 'activeOnly'
|
ACTIVE_ONLY: 'activeOnly'
|
||||||
|
},
|
||||||
|
TAG_POSITION: {
|
||||||
|
RIGHT: 'right',
|
||||||
|
BOTTOM: 'bottom'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,8 @@ export const defaultOpt = {
|
|||||||
mouseScaleCenterUseMousePosition: true,
|
mouseScaleCenterUseMousePosition: true,
|
||||||
// 最多显示几个标签
|
// 最多显示几个标签
|
||||||
maxTag: 5,
|
maxTag: 5,
|
||||||
|
// 标签显示的位置,相对于节点文本,bottom(下方)、right(右侧)
|
||||||
|
tagPosition: CONSTANTS.TAG_POSITION.RIGHT,
|
||||||
// 展开收缩按钮尺寸
|
// 展开收缩按钮尺寸
|
||||||
expandBtnSize: 20,
|
expandBtnSize: 20,
|
||||||
// 节点里图片和文字的间距
|
// 节点里图片和文字的间距
|
||||||
|
|||||||
@ -253,11 +253,15 @@ class Node {
|
|||||||
height: rect.height
|
height: rect.height
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const { tagPosition } = this.mindMap.opt
|
||||||
|
const tagIsBottom = tagPosition === CONSTANTS.TAG_POSITION.BOTTOM
|
||||||
// 宽高
|
// 宽高
|
||||||
let imgContentWidth = 0
|
let imgContentWidth = 0
|
||||||
let imgContentHeight = 0
|
let imgContentHeight = 0
|
||||||
let textContentWidth = 0
|
let textContentWidth = 0
|
||||||
let textContentHeight = 0
|
let textContentHeight = 0
|
||||||
|
let tagContentWidth = 0
|
||||||
|
let tagContentHeight = 0
|
||||||
// 存在图片
|
// 存在图片
|
||||||
if (this._imgData) {
|
if (this._imgData) {
|
||||||
this._rectInfo.imgContentWidth = imgContentWidth = this._imgData.width
|
this._rectInfo.imgContentWidth = imgContentWidth = this._imgData.width
|
||||||
@ -290,10 +294,20 @@ class Node {
|
|||||||
}
|
}
|
||||||
// 标签
|
// 标签
|
||||||
if (this._tagData.length > 0) {
|
if (this._tagData.length > 0) {
|
||||||
textContentWidth += this._tagData.reduce((sum, cur) => {
|
let maxTagHeight = 0
|
||||||
textContentHeight = Math.max(textContentHeight, cur.height)
|
const totalTagWidth = this._tagData.reduce((sum, cur) => {
|
||||||
|
maxTagHeight = Math.max(maxTagHeight, cur.height)
|
||||||
return (sum += cur.width + this.textContentItemMargin)
|
return (sum += cur.width + this.textContentItemMargin)
|
||||||
}, 0)
|
}, 0)
|
||||||
|
if (tagIsBottom) {
|
||||||
|
// 文字下方
|
||||||
|
tagContentWidth = totalTagWidth
|
||||||
|
tagContentHeight = maxTagHeight
|
||||||
|
} else {
|
||||||
|
// 否则在右侧
|
||||||
|
textContentWidth += totalTagWidth
|
||||||
|
textContentHeight = Math.max(textContentHeight, maxTagHeight)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 备注
|
// 备注
|
||||||
if (this._noteData) {
|
if (this._noteData) {
|
||||||
@ -325,6 +339,15 @@ class Node {
|
|||||||
// 纯内容宽高
|
// 纯内容宽高
|
||||||
let _width = Math.max(imgContentWidth, textContentWidth)
|
let _width = Math.max(imgContentWidth, textContentWidth)
|
||||||
let _height = imgContentHeight + textContentHeight
|
let _height = imgContentHeight + textContentHeight
|
||||||
|
// 如果标签在文字下方
|
||||||
|
if (tagIsBottom && tagContentHeight > 0 && textContentHeight > 0) {
|
||||||
|
// 那么文字和标签之间也需要间距
|
||||||
|
margin += this.blockContentMargin
|
||||||
|
// 整体高度要考虑标签宽度
|
||||||
|
_width = Math.max(_width, tagContentWidth)
|
||||||
|
// 整体高度要加上标签的高度
|
||||||
|
_height += tagContentHeight
|
||||||
|
}
|
||||||
// 计算节点形状需要的附加内边距
|
// 计算节点形状需要的附加内边距
|
||||||
let { paddingX: shapePaddingX, paddingY: shapePaddingY } =
|
let { paddingX: shapePaddingX, paddingY: shapePaddingY } =
|
||||||
this.shapeInstance.getShapePadding(_width, _height, paddingX, paddingY)
|
this.shapeInstance.getShapePadding(_width, _height, paddingX, paddingY)
|
||||||
@ -342,7 +365,7 @@ class Node {
|
|||||||
layout() {
|
layout() {
|
||||||
// 清除之前的内容
|
// 清除之前的内容
|
||||||
this.group.clear()
|
this.group.clear()
|
||||||
const { hoverRectPadding } = this.mindMap.opt
|
const { hoverRectPadding, tagPosition } = this.mindMap.opt
|
||||||
let { width, height, textContentItemMargin } = this
|
let { width, height, textContentItemMargin } = this
|
||||||
let { paddingY } = this.getPaddingVale()
|
let { paddingY } = this.getPaddingVale()
|
||||||
const halfBorderWidth = this.getBorderWidth() / 2
|
const halfBorderWidth = this.getBorderWidth() / 2
|
||||||
@ -382,6 +405,8 @@ class Node {
|
|||||||
addHoverNode()
|
addHoverNode()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
const tagIsBottom = tagPosition === CONSTANTS.TAG_POSITION.BOTTOM
|
||||||
|
const { textContentHeight } = this._rectInfo
|
||||||
// 图片节点
|
// 图片节点
|
||||||
let imgHeight = 0
|
let imgHeight = 0
|
||||||
if (this._imgData) {
|
if (this._imgData) {
|
||||||
@ -401,7 +426,7 @@ class Node {
|
|||||||
})
|
})
|
||||||
foreignObject
|
foreignObject
|
||||||
.x(textContentOffsetX)
|
.x(textContentOffsetX)
|
||||||
.y((this._rectInfo.textContentHeight - this._prefixData.height) / 2)
|
.y((textContentHeight - this._prefixData.height) / 2)
|
||||||
textContentNested.add(foreignObject)
|
textContentNested.add(foreignObject)
|
||||||
textContentOffsetX += this._prefixData.width + textContentItemMargin
|
textContentOffsetX += this._prefixData.width + textContentItemMargin
|
||||||
}
|
}
|
||||||
@ -412,7 +437,7 @@ class Node {
|
|||||||
this._iconData.forEach(item => {
|
this._iconData.forEach(item => {
|
||||||
item.node
|
item.node
|
||||||
.x(textContentOffsetX + iconLeft)
|
.x(textContentOffsetX + iconLeft)
|
||||||
.y((this._rectInfo.textContentHeight - item.height) / 2)
|
.y((textContentHeight - item.height) / 2)
|
||||||
iconNested.add(item.node)
|
iconNested.add(item.node)
|
||||||
iconLeft += item.width + textContentItemMargin
|
iconLeft += item.width + textContentItemMargin
|
||||||
})
|
})
|
||||||
@ -427,7 +452,7 @@ class Node {
|
|||||||
;(this._textData.nodeContent || this._textData.node)
|
;(this._textData.nodeContent || this._textData.node)
|
||||||
.x(-oldX) // 修复非富文本模式下同时存在图标和换行的文本时,被收起和展开时图标与文字距离会逐渐拉大的问题
|
.x(-oldX) // 修复非富文本模式下同时存在图标和换行的文本时,被收起和展开时图标与文字距离会逐渐拉大的问题
|
||||||
.x(textContentOffsetX)
|
.x(textContentOffsetX)
|
||||||
.y((this._rectInfo.textContentHeight - this._textData.height) / 2)
|
.y((textContentHeight - this._textData.height) / 2)
|
||||||
textContentNested.add(this._textData.node)
|
textContentNested.add(this._textData.node)
|
||||||
textContentOffsetX += this._textData.width + textContentItemMargin
|
textContentOffsetX += this._textData.width + textContentItemMargin
|
||||||
}
|
}
|
||||||
@ -435,29 +460,50 @@ class Node {
|
|||||||
if (this._hyperlinkData) {
|
if (this._hyperlinkData) {
|
||||||
this._hyperlinkData.node
|
this._hyperlinkData.node
|
||||||
.x(textContentOffsetX)
|
.x(textContentOffsetX)
|
||||||
.y((this._rectInfo.textContentHeight - this._hyperlinkData.height) / 2)
|
.y((textContentHeight - this._hyperlinkData.height) / 2)
|
||||||
textContentNested.add(this._hyperlinkData.node)
|
textContentNested.add(this._hyperlinkData.node)
|
||||||
textContentOffsetX += this._hyperlinkData.width + textContentItemMargin
|
textContentOffsetX += this._hyperlinkData.width + textContentItemMargin
|
||||||
}
|
}
|
||||||
// 标签
|
// 标签
|
||||||
let tagNested = new G()
|
let tagNested = new G()
|
||||||
if (this._tagData && this._tagData.length > 0) {
|
if (this._tagData && this._tagData.length > 0) {
|
||||||
let tagLeft = 0
|
if (tagIsBottom) {
|
||||||
this._tagData.forEach(item => {
|
// 标签显示在文字下方
|
||||||
item.node
|
let tagLeft = 0
|
||||||
.x(textContentOffsetX + tagLeft)
|
this._tagData.forEach(item => {
|
||||||
.y((this._rectInfo.textContentHeight - item.height) / 2)
|
item.node.x(tagLeft).y(0)
|
||||||
tagNested.add(item.node)
|
tagNested.add(item.node)
|
||||||
tagLeft += item.width + textContentItemMargin
|
tagLeft += item.width + textContentItemMargin
|
||||||
})
|
})
|
||||||
textContentNested.add(tagNested)
|
tagNested.cx(width / 2).y(
|
||||||
textContentOffsetX += tagLeft
|
paddingY + // 内边距
|
||||||
|
imgHeight + // 图片高度
|
||||||
|
textContentHeight + // 文本区域高度
|
||||||
|
(imgHeight > 0 && textContentHeight > 0
|
||||||
|
? this.blockContentMargin
|
||||||
|
: 0) + // 图片和文本之间的间距
|
||||||
|
this.blockContentMargin // 标签和文本之间的间距
|
||||||
|
)
|
||||||
|
this.group.add(tagNested)
|
||||||
|
} else {
|
||||||
|
// 标签显示在文字右侧
|
||||||
|
let tagLeft = 0
|
||||||
|
this._tagData.forEach(item => {
|
||||||
|
item.node
|
||||||
|
.x(textContentOffsetX + tagLeft)
|
||||||
|
.y((textContentHeight - item.height) / 2)
|
||||||
|
tagNested.add(item.node)
|
||||||
|
tagLeft += item.width + textContentItemMargin
|
||||||
|
})
|
||||||
|
textContentNested.add(tagNested)
|
||||||
|
textContentOffsetX += tagLeft
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 备注
|
// 备注
|
||||||
if (this._noteData) {
|
if (this._noteData) {
|
||||||
this._noteData.node
|
this._noteData.node
|
||||||
.x(textContentOffsetX)
|
.x(textContentOffsetX)
|
||||||
.y((this._rectInfo.textContentHeight - this._noteData.height) / 2)
|
.y((textContentHeight - this._noteData.height) / 2)
|
||||||
textContentNested.add(this._noteData.node)
|
textContentNested.add(this._noteData.node)
|
||||||
textContentOffsetX += this._noteData.width
|
textContentOffsetX += this._noteData.width
|
||||||
}
|
}
|
||||||
@ -465,7 +511,7 @@ class Node {
|
|||||||
if (this._attachmentData) {
|
if (this._attachmentData) {
|
||||||
this._attachmentData.node
|
this._attachmentData.node
|
||||||
.x(textContentOffsetX)
|
.x(textContentOffsetX)
|
||||||
.y((this._rectInfo.textContentHeight - this._attachmentData.height) / 2)
|
.y((textContentHeight - this._attachmentData.height) / 2)
|
||||||
textContentNested.add(this._attachmentData.node)
|
textContentNested.add(this._attachmentData.node)
|
||||||
textContentOffsetX += this._attachmentData.width
|
textContentOffsetX += this._attachmentData.width
|
||||||
}
|
}
|
||||||
@ -478,18 +524,16 @@ class Node {
|
|||||||
})
|
})
|
||||||
foreignObject
|
foreignObject
|
||||||
.x(textContentOffsetX)
|
.x(textContentOffsetX)
|
||||||
.y((this._rectInfo.textContentHeight - this._postfixData.height) / 2)
|
.y((textContentHeight - this._postfixData.height) / 2)
|
||||||
textContentNested.add(foreignObject)
|
textContentNested.add(foreignObject)
|
||||||
textContentOffsetX += this._postfixData.width
|
textContentOffsetX += this._postfixData.width
|
||||||
}
|
}
|
||||||
// 文字内容整体
|
// 文字内容整体
|
||||||
textContentNested.translate(
|
textContentNested.translate(
|
||||||
width / 2 - textContentNested.bbox().width / 2,
|
width / 2 - textContentNested.bbox().width / 2,
|
||||||
imgHeight +
|
paddingY + // 内边距
|
||||||
paddingY +
|
imgHeight + // 图片高度
|
||||||
(imgHeight > 0 && this._rectInfo.textContentHeight > 0
|
(imgHeight > 0 && textContentHeight > 0 ? this.blockContentMargin : 0) // 和图片的间距
|
||||||
? this.blockContentMargin
|
|
||||||
: 0)
|
|
||||||
)
|
)
|
||||||
this.group.add(textContentNested)
|
this.group.add(textContentNested)
|
||||||
addHoverNode()
|
addHoverNode()
|
||||||
|
|||||||
@ -1,8 +1,4 @@
|
|||||||
import {
|
import { checkIsNodeStyleDataKey } from '../../../utils/index'
|
||||||
checkIsNodeStyleDataKey,
|
|
||||||
generateColorByContent
|
|
||||||
} from '../../../utils/index'
|
|
||||||
import { Gradient } from '@svgdotjs/svg.js'
|
|
||||||
|
|
||||||
const rootProp = ['paddingX', 'paddingY']
|
const rootProp = ['paddingX', 'paddingY']
|
||||||
const backgroundStyleProps = [
|
const backgroundStyleProps = [
|
||||||
@ -182,21 +178,24 @@ class Style {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 标签文字
|
// 标签文字
|
||||||
tagText(node) {
|
tagText(node, style) {
|
||||||
node
|
node
|
||||||
.fill({
|
.fill({
|
||||||
color: '#fff'
|
color: '#fff'
|
||||||
})
|
})
|
||||||
.css({
|
.css({
|
||||||
'font-size': '12px'
|
'font-size': style.fontSize + 'px'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 标签矩形
|
// 标签矩形
|
||||||
tagRect(node, text, color) {
|
tagRect(node, style) {
|
||||||
node.fill({
|
node.fill({
|
||||||
color: color || generateColorByContent(text.node.textContent)
|
color: style.fill
|
||||||
})
|
})
|
||||||
|
if (style.radius) {
|
||||||
|
node.radius(style.radius)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 内置图标
|
// 内置图标
|
||||||
|
|||||||
@ -6,12 +6,23 @@ import {
|
|||||||
checkIsRichText,
|
checkIsRichText,
|
||||||
isUndef,
|
isUndef,
|
||||||
createForeignObjectNode,
|
createForeignObjectNode,
|
||||||
addXmlns
|
addXmlns,
|
||||||
|
generateColorByContent
|
||||||
} from '../../../utils'
|
} from '../../../utils'
|
||||||
import { Image as SVGImage, SVG, A, G, Rect, Text } from '@svgdotjs/svg.js'
|
import { Image as SVGImage, SVG, A, G, Rect, Text } from '@svgdotjs/svg.js'
|
||||||
import iconsSvg from '../../../svg/icons'
|
import iconsSvg from '../../../svg/icons'
|
||||||
import { CONSTANTS } from '../../../constants/constant'
|
import { CONSTANTS } from '../../../constants/constant'
|
||||||
|
|
||||||
|
// 标签默认的样式
|
||||||
|
const defaultTagStyle = {
|
||||||
|
radius: 3, // 标签矩形的圆角大小
|
||||||
|
fontSize: 12, // 字号,建议文字高度不要大于height
|
||||||
|
fill: '', // 标签矩形的背景颜色
|
||||||
|
height: 20, // 标签矩形的高度
|
||||||
|
paddingX: 8 // 水平内边距,如果设置了width,将忽略该配置
|
||||||
|
//width: 30 // 标签矩形的宽度,如果不设置,默认以文字的宽度+paddingX*2为宽度
|
||||||
|
}
|
||||||
|
|
||||||
// 创建图片节点
|
// 创建图片节点
|
||||||
function createImgNode() {
|
function createImgNode() {
|
||||||
const img = this.getData('image')
|
const img = this.getData('image')
|
||||||
@ -284,31 +295,69 @@ function createHyperlinkNode() {
|
|||||||
|
|
||||||
// 创建标签节点
|
// 创建标签节点
|
||||||
function createTagNode() {
|
function createTagNode() {
|
||||||
let tagData = this.getData('tag')
|
const tagData = this.getData('tag')
|
||||||
if (!tagData || tagData.length <= 0) {
|
if (!tagData || tagData.length <= 0) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
let nodes = []
|
let { maxTag, tagsColorMap } = this.mindMap.opt
|
||||||
tagData.slice(0, this.mindMap.opt.maxTag).forEach((item, index) => {
|
tagsColorMap = tagsColorMap || {}
|
||||||
let tag = new G()
|
const nodes = []
|
||||||
|
tagData.slice(0, maxTag).forEach(item => {
|
||||||
|
let str = ''
|
||||||
|
let style = {
|
||||||
|
...defaultTagStyle
|
||||||
|
}
|
||||||
|
// 旧版只支持字符串类型
|
||||||
|
if (typeof item === 'string') {
|
||||||
|
str = item
|
||||||
|
} else {
|
||||||
|
// v0.10.3+版本支持对象类型
|
||||||
|
str = item.text
|
||||||
|
style = { ...defaultTagStyle, ...item.style }
|
||||||
|
}
|
||||||
|
// 是否手动设置了标签宽度
|
||||||
|
const hasCustomWidth = typeof style.width !== 'undefined'
|
||||||
|
// 创建容器节点
|
||||||
|
const tag = new G()
|
||||||
tag.on('click', () => {
|
tag.on('click', () => {
|
||||||
this.mindMap.emit('node_tag_click', this, item)
|
this.mindMap.emit('node_tag_click', this, item)
|
||||||
})
|
})
|
||||||
// 标签文本
|
// 标签文本
|
||||||
let text = new Text().text(item).x(8).cy(8)
|
const text = new Text().text(str)
|
||||||
this.style.tagText(text, index)
|
this.style.tagText(text, style)
|
||||||
let { width } = text.bbox()
|
// 获取文本宽高
|
||||||
|
const { width: textWidth, height: textHeight } = text.bbox()
|
||||||
|
// 矩形宽度
|
||||||
|
const rectWidth = hasCustomWidth
|
||||||
|
? style.width
|
||||||
|
: textWidth + style.paddingX * 2
|
||||||
|
// 取文本和矩形最大宽高作为标签宽高
|
||||||
|
const maxWidth = hasCustomWidth ? Math.max(rectWidth, textWidth) : rectWidth
|
||||||
|
const maxHeight = Math.max(style.height, textHeight)
|
||||||
|
// 文本居中
|
||||||
|
if (hasCustomWidth) {
|
||||||
|
text.x((maxWidth - textWidth) / 2)
|
||||||
|
} else {
|
||||||
|
text.x(hasCustomWidth ? 0 : style.paddingX)
|
||||||
|
}
|
||||||
|
text.cy(-maxHeight / 2)
|
||||||
// 标签矩形
|
// 标签矩形
|
||||||
let rect = new Rect().size(width + 16, 20)
|
const rect = new Rect().size(rectWidth, style.height).cy(-maxHeight / 2)
|
||||||
// 先从自定义的颜色中获取颜色,没有的话就按照内容生成
|
if (hasCustomWidth) {
|
||||||
const tagsColorList = this.mindMap.opt.tagsColorMap || {}
|
rect.x((maxWidth - rectWidth) / 2)
|
||||||
const color = tagsColorList[text.node.textContent]
|
}
|
||||||
this.style.tagRect(rect, text, color)
|
this.style.tagRect(rect, {
|
||||||
|
...style,
|
||||||
|
fill:
|
||||||
|
style.fill || // 优先节点自身配置
|
||||||
|
tagsColorMap[text.node.textContent] || // 否则尝试从实例化选项tagsColorMap映射中获取颜色
|
||||||
|
generateColorByContent(text.node.textContent) // 否则按照标签内容生成
|
||||||
|
})
|
||||||
tag.add(rect).add(text)
|
tag.add(rect).add(text)
|
||||||
nodes.push({
|
nodes.push({
|
||||||
node: tag,
|
node: tag,
|
||||||
width: width + 16,
|
width: maxWidth,
|
||||||
height: 20
|
height: maxHeight
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return nodes
|
return nodes
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user