From 49b56b4576c387cd38447865aed66e993278eaa1 Mon Sep 17 00:00:00 2001 From: chenluli Date: Mon, 17 Jun 2024 21:33:54 +0800 Subject: [PATCH] fix(web): sort line chart data according to time order (#1639) --- .../chart/autoChart/advisor/pipeline.ts | 14 +++++----- .../autoChart/charts/multi-line-chart.ts | 9 +++--- .../charts/multi-measure-line-chart.ts | 7 +++-- web/components/chart/autoChart/charts/util.ts | 28 +++++++++++++++---- web/components/chart/autoChart/index.tsx | 10 ++++++- 5 files changed, 47 insertions(+), 21 deletions(-) diff --git a/web/components/chart/autoChart/advisor/pipeline.ts b/web/components/chart/autoChart/advisor/pipeline.ts index 54eea96ae..9cf5eda9f 100644 --- a/web/components/chart/autoChart/advisor/pipeline.ts +++ b/web/components/chart/autoChart/advisor/pipeline.ts @@ -73,8 +73,8 @@ export const getVisAdvices = (props: { data: Datum[]; myChartAdvisor: Advisor; d */ const customDataProps = dataMetaMap ? Object.keys(dataMetaMap).map((item) => { - return { name: item, ...dataMetaMap[item] }; - }) + return { name: item, ...dataMetaMap[item] }; + }) : null; // 可根据需要选择是否使用全部 fields 进行推荐 @@ -84,11 +84,11 @@ export const getVisAdvices = (props: { data: Datum[]; myChartAdvisor: Advisor; d const selectedFields = size(allFieldsInfo) > 2 ? allFieldsInfo?.filter((field) => { - if (field.recommendation === 'string' || field.recommendation === 'date') { - return field.distinct && field.distinct > 1; - } - return true; - }) + if (field.recommendation === 'string' || field.recommendation === 'date') { + return field.distinct && field.distinct > 1; + } + return true; + }) : allFieldsInfo; const allAdvices = myChartAdvisor?.adviseWithLog({ diff --git a/web/components/chart/autoChart/charts/multi-line-chart.ts b/web/components/chart/autoChart/charts/multi-line-chart.ts index 4440c9ba5..051f17e67 100644 --- a/web/components/chart/autoChart/charts/multi-line-chart.ts +++ b/web/components/chart/autoChart/charts/multi-line-chart.ts @@ -1,8 +1,9 @@ -import { hasSubset, intersects } from '../advisor/utils'; -import { findOrdinalField, processDateEncode, findNominalField, isUniqueXValue, getLineSize } from './util'; +import { hasSubset } from '../advisor/utils'; +import { findOrdinalField, processDateEncode, findNominalField, getLineSize, sortData } from './util'; import type { ChartKnowledge, CustomChart, GetChartConfigProps, Specification } from '../types'; import type { Datum } from '@antv/ava'; +const MULTI_LINE_CHART = 'multi_line_chart' const getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConfigProps['dataProps']) => { const ordinalField = findOrdinalField(dataProps); const nominalField = findNominalField(dataProps); @@ -18,7 +19,7 @@ const getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConf const spec: Specification = { type: 'view', autoFit: true, - data, + data: sortData({ data, chartType: MULTI_LINE_CHART, xField: field4X }), children: [], }; @@ -43,7 +44,7 @@ const getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConf }; const ckb: ChartKnowledge = { - id: 'multi_line_chart', + id: MULTI_LINE_CHART, name: 'multi_line_chart', alias: ['multi_line_chart'], family: ['LineCharts'], diff --git a/web/components/chart/autoChart/charts/multi-measure-line-chart.ts b/web/components/chart/autoChart/charts/multi-measure-line-chart.ts index aee99842a..8f6904592 100644 --- a/web/components/chart/autoChart/charts/multi-measure-line-chart.ts +++ b/web/components/chart/autoChart/charts/multi-measure-line-chart.ts @@ -1,8 +1,9 @@ import { hasSubset } from '../advisor/utils'; -import { findNominalField, findOrdinalField, getLineSize, processDateEncode } from './util'; +import { findNominalField, findOrdinalField, getLineSize, processDateEncode, sortData } from './util'; import type { ChartKnowledge, CustomChart, GetChartConfigProps, Specification } from '../types'; import { Datum } from '@antv/ava'; +const MULTI_MEASURE_LINE_CHART = 'multi_measure_line_chart' const getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConfigProps['dataProps']) => { try { // @ts-ignore @@ -12,7 +13,7 @@ const getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConf const spec: Specification = { type: 'view', - data, + data: sortData({ data, chartType: MULTI_MEASURE_LINE_CHART, xField: field4Nominal }), children: [], }; @@ -40,7 +41,7 @@ const getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConf }; const ckb: ChartKnowledge = { - id: 'multi_measure_line_chart', + id: MULTI_MEASURE_LINE_CHART, name: 'multi_measure_line_chart', alias: ['multi_measure_line_chart'], family: ['LineCharts'], diff --git a/web/components/chart/autoChart/charts/util.ts b/web/components/chart/autoChart/charts/util.ts index 7128763e9..6e07b9f58 100644 --- a/web/components/chart/autoChart/charts/util.ts +++ b/web/components/chart/autoChart/charts/util.ts @@ -1,8 +1,6 @@ -import { Datum, FieldInfo } from '@antv/ava'; -import { hasSubset, intersects } from '../advisor/utils'; -import { uniq } from 'lodash'; - -type BasicDataPropertyForAdvice = any; +import type { Datum, FieldInfo } from "@antv/ava"; +import { hasSubset, intersects } from "../advisor/utils"; +import { cloneDeep, uniq } from "lodash"; /** * Process date column to new Date(). @@ -10,7 +8,7 @@ type BasicDataPropertyForAdvice = any; * @param dataProps * @returns */ -export function processDateEncode(field: string, dataProps: BasicDataPropertyForAdvice[]) { +export function processDateEncode(field: string, dataProps: FieldInfo[]) { const dp = dataProps.find((dataProp) => dataProp.name === field); if (dp?.recommendation === 'date') { @@ -50,3 +48,21 @@ export const getLineSize = ( } return field4X?.name && isUniqueXValue({ data: allData, xField: field4X.name }) ? 5 : undefined; }; + +export const sortData = ({ data, chartType, xField }: { + data: Datum[]; + xField?: FieldInfo; + chartType: string; +}) => { + const sortedData = cloneDeep(data) + try { + // 折线图绘制需要将数据点按照日期从小到大的顺序排序和连线 + if (chartType.includes('line') && xField?.name && xField.recommendation === 'date') { + sortedData.sort((datum1, datum2) => new Date(datum1[xField.name as string]).getTime() - new Date(datum2[xField.name as string]).getTime()) + return sortedData + } + } catch (err) { + console.error(err) + } + return sortedData +} diff --git a/web/components/chart/autoChart/index.tsx b/web/components/chart/autoChart/index.tsx index 1ec84c8ee..063fb74c9 100644 --- a/web/components/chart/autoChart/index.tsx +++ b/web/components/chart/autoChart/index.tsx @@ -9,6 +9,7 @@ import { AutoChartProps, ChartType, CustomAdvisorConfig, CustomChart, Specificat import { customCharts } from './charts'; import { ChatContext } from '@/app/chat-context'; import { compact, concat, uniq } from 'lodash'; +import { sortData } from './charts/util'; const { Option } = Select; @@ -81,7 +82,7 @@ export const AutoChart = (props: AutoChartProps) => { setAdvices(allAdvices); setRenderChartType(allAdvices[0]?.type as ChartType); } - }, [data, advisor, chartType]); + }, [JSON.stringify(data), advisor, chartType]); const visComponent = useMemo(() => { /* Advices exist, render the chart. */ @@ -89,6 +90,13 @@ export const AutoChart = (props: AutoChartProps) => { const chartTypeInput = renderChartType ?? advices[0].type; const spec: Specification = advices?.find((item: Advice) => item.type === chartTypeInput)?.spec ?? undefined; if (spec) { + if (spec.data && ['line_chart', 'step_line_chart'].includes(chartTypeInput)) { + // 处理 ava 内置折线图的排序问题 + const dataAnalyzerOutput = advisor?.dataAnalyzer.execute({ data }) + if (dataAnalyzerOutput && 'dataProps' in dataAnalyzerOutput) { + spec.data = sortData({ data: spec.data, xField: dataAnalyzerOutput.dataProps?.find(field => field.recommendation === 'date'), chartType: chartTypeInput }); + } + } return (