fix(图表): 修复柱形图没有横轴长度限制的问题,添加超过长度限制后能够鼠标悬浮显示全部内容,以及优化数据标签的显示策略

This commit is contained in:
jianneng-fit2cloud
2026-05-28 18:58:34 +08:00
parent 03e3e1b4cc
commit 4e00239e81
9 changed files with 215 additions and 8 deletions

View File

@@ -122,6 +122,10 @@ const isBidirectionalBar = computed(() => {
return props.chart.type === 'bidirectional-bar'
})
const showLengthLimit = computed(() => {
return isBidirectionalBar.value || showProperty('showLengthLimit')
})
const isBulletGraph = computed(() => {
return ['bullet-graph'].includes(props.chart.type)
})
@@ -527,7 +531,7 @@ onMounted(() => {
class="form-item"
:class="'form-item-' + themes"
:label="t('chart.length_limit')"
v-if="isBidirectionalBar"
v-if="showLengthLimit"
>
<el-input-number
:disabled="!state.axisForm.axisLabel.show"

View File

@@ -15,6 +15,8 @@ import { flow, hexColorToRGBA, hexToRgba, parseJson } from '@/views/chart/compon
import { cloneDeep, defaultsDeep, filter, find, isEmpty } from 'lodash-es'
import { valueFormatter } from '@/views/chart/components/js/formatter'
import {
configAxisLengthLimit,
formatAxisLabelWithLengthLimit,
getLineDash,
handleChartDashboardHidden,
setGradientColor,
@@ -49,6 +51,7 @@ export class Bar extends G2ChartView<ViewSpec, G2Column> {
properties = BAR_EDITOR_PROPERTY
propertyInner = {
...BAR_EDITOR_PROPERTY_INNER,
'x-axis-selector': [...BAR_EDITOR_PROPERTY_INNER['x-axis-selector'], 'showLengthLimit'],
'basic-style-selector': [...BAR_EDITOR_PROPERTY_INNER['basic-style-selector'], 'seriesColor'],
'label-selector': ['vPosition', 'seriesLabelFormatter', 'showExtremum'],
'tooltip-selector': [
@@ -141,9 +144,20 @@ export class Bar extends G2ChartView<ViewSpec, G2Column> {
newChart.on('interval:click', action)
new G2TooltipCarousel(newChart, chart, data).start()
extremumEvt(newChart, chart, options.children[0], container, scale, this.name === 'bar')
this.configLengthLimitTooltip(chart, newChart)
return newChart
}
protected configLengthLimitTooltip(chart: Chart, chartObj: G2Column): void {
if (this.supportAxisLengthLimit('xAxis')) {
configAxisLengthLimit(chart, chartObj, 'xAxis')
}
}
protected supportAxisLengthLimit(axisType: string): boolean {
return axisType === 'xAxis'
}
protected configLabel(chart: Chart, options: ViewSpec): ViewSpec {
const customAttr = parseJson(chart.customAttr)
const { label: l } = customAttr
@@ -208,7 +222,9 @@ export class Bar extends G2ChartView<ViewSpec, G2Column> {
}
} as any
if (!l.fullDisplay) {
newLabel.transform = [{ type: 'overlapHide' }]
newLabel.transform = [{ type: 'exceedAdjust' }, { type: 'overlapHide' }]
} else {
newLabel.transform = [{ type: 'exceedAdjust' }]
}
return {
...options,
@@ -651,6 +667,9 @@ export class Bar extends G2ChartView<ViewSpec, G2Column> {
if (axisType === 'yAxis') {
return valueFormatter(value, axis.axisLabelFormatter)
}
if (this.supportAxisLengthLimit(axisType)) {
return formatAxisLabelWithLengthLimit(value, axis.axisLabel.lengthLimit)
}
return value
}
}

View File

@@ -12,6 +12,8 @@ import { valueFormatter } from '@/views/chart/components/js/formatter'
import { useI18n } from '@/hooks/web/useI18n'
import { ChartEvent, Chart as G2Chart, G2Spec } from '@antv/g2'
import {
configXAxisLengthLimit,
formatAxisLabelWithLengthLimit,
handleChartDashboardHidden,
setGradientColor,
TOOLTIP_ITEM_TPL,
@@ -240,6 +242,7 @@ export class BidirectionalHorizontalBar extends G2ChartView {
}
})
newChart.on('interval:click', action)
configXAxisLengthLimit(chart, newChart)
// 开始渲染
handleChartDashboardHidden(chart, options)
newChart.options(options)
@@ -400,13 +403,17 @@ export class BidirectionalHorizontalBar extends G2ChartView {
}
// G2 默认轴组件会按完整标签宽度预留空间,横向对称条形图只需要按文本宽度估算中间轴占位
const labelFontSize = xAxis.axisLabel.fontSize ?? 12
const formatXAxisLabel = value => {
const formatXAxisLabelText = value => {
const label = `${value ?? ''}`
const lengthLimit = xAxis.axisLabel.lengthLimit
return lengthLimit && label.length > lengthLimit
? label.substring(0, lengthLimit) + '...'
: label
}
const formatXAxisLabel = value => {
const originLabel = `${value ?? ''}`
return formatAxisLabelWithLengthLimit(originLabel, xAxis.axisLabel.lengthLimit)
}
const getLabelTextWidth = text => {
return Array.from(`${text ?? ''}`).reduce((width, char) => {
return width + (char.charCodeAt(0) > 255 ? labelFontSize : labelFontSize * 0.6)
@@ -416,7 +423,7 @@ export class BidirectionalHorizontalBar extends G2ChartView {
if (basicStyle.layout === 'horizontal' && position === 'right' && xAxis.axisLabel.show) {
const fields = (firstMark.data?.value || []).map(item => item.field)
const maxLabelWidth = fields.reduce((maxWidth, field) => {
return Math.max(maxWidth, getLabelTextWidth(formatXAxisLabel(field)))
return Math.max(maxWidth, getLabelTextWidth(formatXAxisLabelText(field)))
}, 0)
// 中间维度轴只需要左右各预留半个标签宽度和少量间距,避免 G2 默认轴宽把两侧空白撑大
centerAxisSize = Math.ceil(Math.max(labelFontSize + 8, maxLabelWidth / 2 + 8))
@@ -873,7 +880,7 @@ export class BidirectionalHorizontalBar extends G2ChartView {
}
},
transform: label.fullDisplay
? []
? [{ type: 'exceedAdjust' }]
: [{ type: 'overlapDodgeY' }, { type: 'exceedAdjust' }, { type: 'overlapHide' }],
fontFamily: chart.fontFamily
}

View File

@@ -10,6 +10,8 @@ import { flow, parseJson } from '@/views/chart/components/js/util'
import { RuntimeOptions } from '@antv/g2/lib/api/runtime'
import { formatterItem, valueFormatter } from '@/views/chart/components/js/formatter'
import {
configAxisLengthLimit,
formatAxisLabelWithLengthLimit,
getLineDash,
handleChartDashboardHidden,
TOOLTIP_ITEM_TPL,
@@ -118,6 +120,7 @@ export class BulletGraph extends G2ChartView<RuntimeOptions, G2Bullet> {
action(actionParams)
})
listenerTooltipShow(newChart, chart)
configAxisLengthLimit(chart, newChart, 'xAxis')
return newChart
}
@@ -356,6 +359,9 @@ export class BulletGraph extends G2ChartView<RuntimeOptions, G2Bullet> {
if (axisType === 'yAxis') {
return valueFormatter(value, axis.axisLabelFormatter)
}
if (axisType === 'xAxis') {
return formatAxisLabelWithLengthLimit(value, axis.axisLabel.lengthLimit)
}
return value
}
}

View File

@@ -10,12 +10,17 @@ import {
setUpStackSeriesColor
} from '@/views/chart/components/js/util'
import {
ViewSpec,
handleEmptyDataStrategy
handleEmptyDataStrategy,
ViewSpec
} from '@/views/chart/components/js/panel/charts/g2/bar/barUtil'
import { useI18n } from '@/hooks/web/useI18n'
import { Bar } from '@/views/chart/components/js/panel/charts/g2/bar/bar'
import { getLineDash, setGradientColor } from '@/views/chart/components/js/panel/common/common_antv'
import {
configAxisLengthLimit,
formatAxisLabelWithLengthLimit,
getLineDash,
setGradientColor
} from '@/views/chart/components/js/panel/common/common_antv'
import { valueFormatter } from '@/views/chart/components/js/formatter'
import { DEFAULT_BASIC_STYLE } from '@/views/chart/components/editor/util/chart'
import { defaultsDeep } from 'lodash-es'
@@ -194,6 +199,10 @@ export class HorizontalBar extends Bar {
return tmpOptions
}
protected configLengthLimitTooltip(chart: Chart, chartObj): void {
configAxisLengthLimit(chart, chartObj, 'yAxis')
}
protected getAxisConfig(chart: Chart, axisType: string): any {
const customStyle = parseJson(chart.customStyle)
const axis = JSON.parse(JSON.stringify(customStyle[axisType]))
@@ -231,6 +240,11 @@ export class HorizontalBar extends Bar {
if (axisType === 'xAxis') {
return valueFormatter(value, axis.axisLabelFormatter)
}
const lengthLimit = axis.axisLabel.lengthLimit
const valueText = value === null || value === undefined ? value : `${value}`
if (axisType === 'yAxis' && lengthLimit && valueText?.length > lengthLimit) {
return formatAxisLabelWithLengthLimit(valueText, lengthLimit)
}
return value
}
}

View File

@@ -14,6 +14,7 @@ import { G2DrawOptions } from '@/views/chart/components/js/panel/types/impl/g2'
import { Chart as G2Column } from '@antv/g2'
import { useI18n } from '@/hooks/web/useI18n'
import {
configAxisLengthLimit,
handleChartDashboardHidden,
setGradientColor,
toLinearGradient,
@@ -195,6 +196,7 @@ export class ProgressBar extends HorizontalStackBar {
newChart.options(newOptions)
newChart.on('interval:click', action)
listenerTooltipShow(newChart, chart)
configAxisLengthLimit(chart, newChart, 'yAxis')
return newChart
}

View File

@@ -15,6 +15,7 @@ import { HorizontalBar } from '@/views/chart/components/js/panel/charts/g2/bar/h
import { G2DrawOptions } from '@/views/chart/components/js/panel/types/impl/g2'
import { cloneDeep, isEmpty } from 'lodash-es'
import {
configAxisLengthLimit,
handleChartDashboardHidden,
TOOLTIP_ITEM_TPL,
TOOLTIP_TITLE_TPL
@@ -130,6 +131,7 @@ export class RangeBar extends HorizontalBar {
newChart.options(options)
newChart.on('interval:click', action)
listenerTooltipShow(newChart, chart)
configAxisLengthLimit(chart, newChart, 'yAxis')
return newChart
}

View File

@@ -8,6 +8,8 @@ import {
ViewSpec
} from '@/views/chart/components/js/panel/charts/g2/bar/barUtil'
import {
configAxisLengthLimit,
formatAxisLabelWithLengthLimit,
handleChartDashboardHidden,
setGradientColor
} from '@/views/chart/components/js/panel/common/common_antv'
@@ -178,9 +180,31 @@ export class Waterfall extends Bar {
newChart.options(options)
newChart.on('interval:click', action)
listenerTooltipShow(newChart, chart)
configAxisLengthLimit(chart, newChart, 'yAxis')
return newChart
}
protected getAxisConfig(chart: Chart, axisType: string): any {
const axisConfig = super.getAxisConfig(chart, axisType)
if (!axisConfig || axisType !== 'yAxis') {
return axisConfig
}
const { yAxis } = parseJson(chart.customStyle)
const originLabelFormatter = axisConfig.labelFormatter
return {
...axisConfig,
labelFormatter: value => {
const label =
typeof originLabelFormatter === 'function' ? originLabelFormatter(value) : value
return formatAxisLabelWithLengthLimit(label, yAxis.axisLabel.lengthLimit)
}
}
}
protected supportAxisLengthLimit(axisType: string): boolean {
return axisType === 'yAxis'
}
protected configBasicStyle(chart: Chart, options: ViewSpec): ViewSpec {
const { children } = options
const customAttr = parseJson(chart.customAttr)

View File

@@ -31,6 +31,8 @@ import { centroid } from '@turf/centroid'
import { defaults, find, groupBy, map, uniq } from 'lodash-es'
import { useI18n } from '@/hooks/web/useI18n'
import { isMobile } from '@/utils/utils'
import { ChartEvent } from '@antv/g2'
import { Text } from '@antv/g'
import { GaodeMap, TMap, TencentMap } from '@antv/l7-maps'
import {
gaodeMapStyleOptions,
@@ -2070,6 +2072,133 @@ export function configAxisLabelLengthLimit(chart, plot, triggerObjName) {
})
}
export function configXAxisLengthLimit(
chart: Chart,
chartObj: any,
formatOriginText?: (originText: string, event: any) => string
): void {
configAxisLengthLimit(chart, chartObj, 'xAxis', formatOriginText)
}
export function configAxisLengthLimit(
chart: Chart,
chartObj: any,
axisType = 'xAxis',
formatOriginText?: (originText: string, event: any) => string
): void {
const axis = parseJson(chart.customStyle)?.[axisType]
if (!axis?.show || !axis.axisLabel?.show || !axis.axisLabel.lengthLimit) {
return
}
let hideTimer: ReturnType<typeof setTimeout>
const { tooltip = {} } = parseJson(chart.customAttr)
const tooltipFontSize = tooltip.fontSize ?? 12
const tooltipId = `AXIS_LABEL_TIP-${chart.container || chart.id || 'default'}-${axisType}`
const getOriginText = (event: any) => {
const originTextRaw = event.target?.attributes?.originValue
if (originTextRaw === undefined || originTextRaw === null) {
return ''
}
return String(
(formatOriginText ? formatOriginText(String(originTextRaw), event) : originTextRaw) ?? ''
)
}
const getTooltipDom = () => {
let parentDom = document.getElementById('G2-TOOLTIP-WRAPPER')
if (!parentDom) {
parentDom = document.createElement('div')
parentDom.id = 'G2-TOOLTIP-WRAPPER'
parentDom.style.position = 'absolute'
parentDom.style.pointerEvents = 'none'
parentDom.style.zIndex = '9999'
document.body.appendChild(parentDom)
}
let tooltipDom = document.getElementById(tooltipId)
if (!tooltipDom) {
tooltipDom = document.createElement('div')
tooltipDom.id = tooltipId
tooltipDom.className = 'g2-axis-label-tooltip'
tooltipDom.style.position = 'fixed'
tooltipDom.style.display = 'none'
tooltipDom.style.color = tooltip.color ?? '#333333'
tooltipDom.style.backgroundColor = tooltip.backgroundColor ?? '#ffffff'
tooltipDom.style.fontSize = `${tooltipFontSize}px`
tooltipDom.style.padding = '4px 8px'
tooltipDom.style.boxShadow = 'rgba(0, 0, 0, 0.1) 0px 4px 8px 0px'
tooltipDom.style.borderRadius = '4px'
tooltipDom.style.cursor = 'default'
tooltipDom.style.pointerEvents = 'none'
tooltipDom.style.transition =
'left 0.4s cubic-bezier(0.23, 1, 0.32, 1), top 0.4s cubic-bezier(0.23, 1, 0.32, 1)'
parentDom.appendChild(tooltipDom)
}
return tooltipDom
}
const hideTooltip = () => {
const tooltipDom = document.getElementById(tooltipId)
if (tooltipDom) {
tooltipDom.style.display = 'none'
}
}
chartObj?.on(`axis-label-item:${ChartEvent.POINTER_MOVE}`, event => {
const showText = event.target?.attributes?.text
if (!showText?.endsWith('...')) {
hideTooltip()
return
}
const originText = getOriginText(event)
if (!originText || originText === showText) {
hideTooltip()
return
}
if (hideTimer) {
clearTimeout(hideTimer)
}
const tooltipDom = getTooltipDom()
tooltipDom.innerText = originText
tooltipDom.style.display = 'block'
const { width, height } = tooltipDom.getBoundingClientRect()
const clientX = event.client?.x ?? event.x ?? 0
const clientY = event.client?.y ?? event.y ?? 0
const gap = 10
let left = clientX + gap
let top = clientY + gap
if (left + width > window.innerWidth) {
left = Math.max(gap, clientX - width - gap)
}
if (top + height > window.innerHeight) {
top = Math.max(gap, clientY - height - gap)
}
tooltipDom.style.left = `${left}px`
tooltipDom.style.top = `${top}px`
})
chartObj?.on(`axis-label-item:${ChartEvent.POINTER_OUT}`, () => {
hideTimer = setTimeout(hideTooltip, 200)
})
}
export function formatAxisLabelWithLengthLimit(value: unknown, lengthLimit?: number) {
const label = value === null || value === undefined ? '' : `${value}`
if (lengthLimit && label.length > lengthLimit) {
return new Text({
style: {
text: label.substring(0, lengthLimit) + '...',
originValue: label
}
})
}
return label
}
/**
* y轴标题截取
* @param chart