diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/g2/line/area.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/g2/line/area.ts index 0437bb1596..12910e41aa 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/g2/line/area.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/g2/line/area.ts @@ -15,7 +15,14 @@ import { } from '@/views/chart/components/js/util' import { cloneDeep, defaultsDeep, isEmpty, merge } from 'lodash-es' import { valueFormatter } from '@/views/chart/components/js/formatter' -import { LINE_AXIS_TYPE, LINE_EDITOR_PROPERTY, LINE_EDITOR_PROPERTY_INNER } from './common' +import { + configStackOrderByYAxis, + configYAxisSeriesLegendDomain, + LINE_AXIS_TYPE, + LINE_EDITOR_PROPERTY, + LINE_EDITOR_PROPERTY_INNER, + sortTooltipItemsByYAxis +} from './common' import { useI18n } from '@/hooks/web/useI18n' import { addExtremumText, extremumEvt } from '@/views/chart/components/js/extremumUitl' import { Chart as G2Chart, G2Spec } from '@antv/g2' @@ -501,7 +508,7 @@ export class Area extends G2ChartView { } } defaultsDeep(options, tmpLegend) - return options + return configYAxisSeriesLegendDomain(chart, options) } protected configAssistLine(chart: Chart, options: G2Spec): G2Spec { @@ -616,7 +623,7 @@ export class Area extends G2ChartView { } const result = [] const head = originalItems[0] - tooltipItems.forEach(item => { + sortTooltipItemsByYAxis(chart, tooltipItems).forEach(item => { if (item.value === null || item.value === undefined) { return } @@ -855,6 +862,10 @@ export class StackArea extends Area { return options } + protected configLegend(chart: Chart, options: G2Spec): G2Spec { + return super.configLegend(chart, configStackOrderByYAxis(chart, options)) + } + protected configTooltip(chart: Chart, options: G2Spec): G2Spec { const customAttr: DeepPartial = parseJson(chart.customAttr) const tooltipAttr = customAttr.tooltip @@ -874,7 +885,7 @@ export class StackArea extends Area { render: (e, { title, items }) => { const titleHtml = TOOLTIP_TITLE_TPL.replace('{title}', title) const result = [] - items.forEach(item => { + sortTooltipItemsByYAxis(chart, items).forEach(item => { if (item.value === null || item.value === undefined) { return } diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/g2/line/common.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/g2/line/common.ts index ffc424174c..02435fdd30 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/g2/line/common.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/g2/line/common.ts @@ -1,3 +1,5 @@ +import type { G2Spec } from '@antv/g2' + export const LINE_EDITOR_PROPERTY: EditorProperty[] = [ 'background-overall-component', 'border-style', @@ -72,3 +74,125 @@ export const LINE_AXIS_TYPE: AxisType[] = [ 'extLabel', 'extTooltip' ] + +const Y_AXIS_SERIES_ORDER_NOT_FOUND = Number.MAX_SAFE_INTEGER + +/** + * 只有系列来自值轴指标时才按指标顺序处理;有分组/堆叠维度时,系列来自维度值,保留原逻辑 + */ +export const isYAxisSeriesChart = (chart: Chart) => { + return !!chart.yAxis?.length && !chart.xAxisExt?.length && !chart.extStack?.length +} + +/** + * 指标系列在图表数据里的名称来自字段展示名,未设置展示名时使用字段名 + */ +export const getYAxisSeriesName = axis => axis.chartShowName || axis.name + +/** + * 为 legend 生成值轴指标顺序的 color domain,同时保留数据里未匹配到的新系列 + */ +export const getYAxisSeriesDomain = (chart: Chart, data: any[] = []) => { + const domain = [] + chart.yAxis?.forEach(axis => { + const name = getYAxisSeriesName(axis) + if (name && !domain.includes(name)) { + domain.push(name) + } + }) + data.forEach(item => { + if (item.category && !domain.includes(item.category)) { + domain.push(item.category) + } + }) + return domain +} + +/** + * 建立指标 id/显示名到字段下标的映射,供 tooltip 和堆叠层级排序复用 + */ +export const getYAxisOrderMap = (chart: Chart) => { + const orderMap = new Map() + chart.yAxis?.forEach((axis, index) => { + orderMap.set(axis.id, index) + const name = getYAxisSeriesName(axis) + if (name) { + orderMap.set(name, index) + } + }) + return orderMap +} + +/** + * 根据指标字段顺序获取当前系列排序值,未命中时放到最后并保持原相对顺序 + */ +export const getYAxisSeriesOrder = (orderMap: Map, item) => { + return ( + orderMap.get(item.quotaList?.[0]?.id) ?? + orderMap.get(item.category) ?? + Y_AXIS_SERIES_ORDER_NOT_FOUND + ) +} + +/** + * 对 tooltip items 按右侧指标字段自上而下排序 + */ +export const sortTooltipItemsByYAxis = (chart: Chart, items: any[]) => { + if (!isYAxisSeriesChart(chart)) { + return items + } + const orderMap = getYAxisOrderMap(chart) + return items + .map((item, index) => ({ + item, + index, + order: getYAxisSeriesOrder(orderMap, item) + })) + .sort((a, b) => a.order - b.order || a.index - b.index) + .map(({ item }) => item) +} + +/** + * 对 legend 的 color domain 按右侧指标字段自上而下排序,不覆盖已有颜色映射等样式配置 + */ +export const configYAxisSeriesLegendDomain = (chart: Chart, options: G2Spec) => { + if (!isYAxisSeriesChart(chart)) { + return options + } + const domain = getYAxisSeriesDomain(chart, options.data?.value) + if (!domain.length) { + return options + } + const scale = options.scale ?? {} + options.scale = { + ...scale, + color: { + ...(scale.color ?? {}), + domain + } + } + return options +} + +/** + * G2 正值堆叠是从下往上累加,堆叠折线需要反向使用指标下标来实现视觉自上而下对齐 + */ +export const configStackOrderByYAxis = (chart: Chart, options: G2Spec) => { + if (!isYAxisSeriesChart(chart)) { + return options + } + const orderMap = getYAxisOrderMap(chart) + options.transform = (options.transform ?? []).map(transform => { + if (transform.type !== 'stackY') { + return transform + } + return { + ...transform, + orderBy: item => { + const order = getYAxisSeriesOrder(orderMap, item) + return order === Y_AXIS_SERIES_ORDER_NOT_FOUND ? order : -order + } + } + }) + return options +} diff --git a/core/core-frontend/src/views/chart/components/js/panel/charts/g2/line/line.ts b/core/core-frontend/src/views/chart/components/js/panel/charts/g2/line/line.ts index 474750717a..235a5a051a 100644 --- a/core/core-frontend/src/views/chart/components/js/panel/charts/g2/line/line.ts +++ b/core/core-frontend/src/views/chart/components/js/panel/charts/g2/line/line.ts @@ -14,7 +14,13 @@ import { } from '@/views/chart/components/js/util' import { cloneDeep, defaultsDeep, isEmpty, merge } from 'lodash-es' import { valueFormatter } from '@/views/chart/components/js/formatter' -import { LINE_AXIS_TYPE, LINE_EDITOR_PROPERTY, LINE_EDITOR_PROPERTY_INNER } from './common' +import { + configYAxisSeriesLegendDomain, + LINE_AXIS_TYPE, + LINE_EDITOR_PROPERTY, + LINE_EDITOR_PROPERTY_INNER, + sortTooltipItemsByYAxis +} from './common' import { useI18n } from '@/hooks/web/useI18n' import { Chart as G2Chart, G2Spec } from '@antv/g2' import { DEFAULT_YAXIS_STYLE } from '@/views/chart/components/editor/util/chart' @@ -566,7 +572,7 @@ export class Line extends G2ChartView { defaultsDeep(options, scaleOpt) } } - return options + return configYAxisSeriesLegendDomain(chart, options) } protected configAssistLine(chart: Chart, options: G2Spec): G2Spec { @@ -683,7 +689,7 @@ export class Line extends G2ChartView { } const result = [] const head = originalItems[0] - tooltipItems.forEach(item => { + sortTooltipItemsByYAxis(chart, tooltipItems).forEach(item => { if (item.value === null || item.value === undefined) { return }