diff --git a/core/core-frontend/src/views/chart/components/editor/editor-style/components/XAxisSelector.vue b/core/core-frontend/src/views/chart/components/editor/editor-style/components/XAxisSelector.vue index ede662b287..60442c7672 100644 --- a/core/core-frontend/src/views/chart/components/editor/editor-style/components/XAxisSelector.vue +++ b/core/core-frontend/src/views/chart/components/editor/editor-style/components/XAxisSelector.vue @@ -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" > { 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 { 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 { } } 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 { if (axisType === 'yAxis') { return valueFormatter(value, axis.axisLabelFormatter) } + if (this.supportAxisLengthLimit(axisType)) { + return formatAxisLabelWithLengthLimit(value, axis.axisLabel.lengthLimit) + } return value } } diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/bidirectional-bar.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/bidirectional-bar.ts index 0721f7fd1d..1161a5b88f 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/bidirectional-bar.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/bidirectional-bar.ts @@ -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 } diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/bullet-graph.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/bullet-graph.ts index 1b5d057c59..abf8a4a677 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/bullet-graph.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/bullet-graph.ts @@ -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 { action(actionParams) }) listenerTooltipShow(newChart, chart) + configAxisLengthLimit(chart, newChart, 'xAxis') return newChart } @@ -356,6 +359,9 @@ export class BulletGraph extends G2ChartView { if (axisType === 'yAxis') { return valueFormatter(value, axis.axisLabelFormatter) } + if (axisType === 'xAxis') { + return formatAxisLabelWithLengthLimit(value, axis.axisLabel.lengthLimit) + } return value } } diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/horizontal-bar.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/horizontal-bar.ts index 36e60693e9..9697fbc6de 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/horizontal-bar.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/horizontal-bar.ts @@ -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 } } diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/progress-bar.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/progress-bar.ts index 2c23a61aa0..367435148e 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/progress-bar.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/progress-bar.ts @@ -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 } diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/range-bar.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/range-bar.ts index 9500bec0d3..d612ef61bb 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/range-bar.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/range-bar.ts @@ -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 } diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/waterfall.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/waterfall.ts index 7d351415cc..099e9227e8 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/waterfall.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/g2/bar/waterfall.ts @@ -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) diff --git a/core/core-frontend/src/views/chart/components/js/panel/common/common_antv.ts b/core/core-frontend/src/views/chart/components/js/panel/common/common_antv.ts index e2102a7b75..b3fef1c2a8 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/common/common_antv.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/common/common_antv.ts @@ -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 + 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