mirror of
https://github.com/dataease/dataease.git
synced 2026-06-16 20:42:07 +08:00
fix(图表): 修复柱形图没有横轴长度限制的问题,添加超过长度限制后能够鼠标悬浮显示全部内容,以及优化数据标签的显示策略
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user