fix(data-vi): charts flickers (#3836)

This commit is contained in:
YANG QIA 2024-03-27 15:43:11 +08:00 committed by GitHub
parent e95f4ab123
commit 996e89fb14
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 48 additions and 56 deletions

View File

@ -11,38 +11,38 @@ describe('api', () => {
}); });
test('setGroup', () => { test('setGroup', () => {
const charts1 = [new Chart({ name: 'test1', title: 'Test1', component: null })]; const charts1 = [new Chart({ name: 'test1', title: 'Test1', Component: null })];
plugin.charts.setGroup('group', charts1); plugin.charts.setGroup('group', charts1);
expect(plugin.charts.charts.get('group')).toEqual(charts1); expect(plugin.charts.charts.get('group')).toEqual(charts1);
const charts2 = [new Chart({ name: 'test2', title: 'Test2', component: null })]; const charts2 = [new Chart({ name: 'test2', title: 'Test2', Component: null })];
plugin.charts.setGroup('group', charts2); plugin.charts.setGroup('group', charts2);
expect(plugin.charts.charts.get('group')).toEqual(charts2); expect(plugin.charts.charts.get('group')).toEqual(charts2);
}); });
test('addGroup', () => { test('addGroup', () => {
const charts1 = [new Chart({ name: 'test1', title: 'Test1', component: null })]; const charts1 = [new Chart({ name: 'test1', title: 'Test1', Component: null })];
plugin.charts.setGroup('group1', charts1); plugin.charts.setGroup('group1', charts1);
const charts2 = [new Chart({ name: 'test2', title: 'Test2', component: null })]; const charts2 = [new Chart({ name: 'test2', title: 'Test2', Component: null })];
plugin.charts.addGroup('group2', charts2); plugin.charts.addGroup('group2', charts2);
expect(plugin.charts.charts.get('group1')).toEqual(charts1); expect(plugin.charts.charts.get('group1')).toEqual(charts1);
expect(plugin.charts.charts.get('group2')).toEqual(charts2); expect(plugin.charts.charts.get('group2')).toEqual(charts2);
}); });
test('add', () => { test('add', () => {
const charts1 = [new Chart({ name: 'test1', title: 'Test1', component: null })]; const charts1 = [new Chart({ name: 'test1', title: 'Test1', Component: null })];
plugin.charts.setGroup('group', charts1); plugin.charts.setGroup('group', charts1);
const chart = new Chart({ name: 'test2', title: 'Test2', component: null }); const chart = new Chart({ name: 'test2', title: 'Test2', Component: null });
plugin.charts.add('group', chart); plugin.charts.add('group', chart);
expect(plugin.charts.charts.get('group').length).toEqual(2); expect(plugin.charts.charts.get('group').length).toEqual(2);
expect(plugin.charts.charts.get('group')[1].name).toEqual('test2'); expect(plugin.charts.charts.get('group')[1].name).toEqual('test2');
}); });
test('getChartTypes', () => { test('getChartTypes', () => {
const charts1 = [new Chart({ name: 'test1', title: 'Test1', component: null })]; const charts1 = [new Chart({ name: 'test1', title: 'Test1', Component: null })];
plugin.charts.setGroup('group1', charts1); plugin.charts.setGroup('group1', charts1);
const charts2 = [new Chart({ name: 'test2', title: 'Test2', component: null })]; const charts2 = [new Chart({ name: 'test2', title: 'Test2', Component: null })];
plugin.charts.setGroup('group2', charts2); plugin.charts.setGroup('group2', charts2);
expect(plugin.charts.getChartTypes()).toEqual([ expect(plugin.charts.getChartTypes()).toEqual([
{ {
@ -69,9 +69,9 @@ describe('api', () => {
}); });
test('getCharts', () => { test('getCharts', () => {
const charts1 = [new Chart({ name: 'test1', title: 'Test1', component: null })]; const charts1 = [new Chart({ name: 'test1', title: 'Test1', Component: null })];
plugin.charts.setGroup('group1', charts1); plugin.charts.setGroup('group1', charts1);
const charts2 = [new Chart({ name: 'test2', title: 'Test2', component: null })]; const charts2 = [new Chart({ name: 'test2', title: 'Test2', Component: null })];
plugin.charts.setGroup('group2', charts2); plugin.charts.setGroup('group2', charts2);
expect(plugin.charts.getCharts()).toEqual({ expect(plugin.charts.getCharts()).toEqual({
'group1.test1': charts1[0], 'group1.test1': charts1[0],
@ -80,9 +80,9 @@ describe('api', () => {
}); });
test('getChart', () => { test('getChart', () => {
const charts1 = [new Chart({ name: 'test1', title: 'Test1', component: null })]; const charts1 = [new Chart({ name: 'test1', title: 'Test1', Component: null })];
plugin.charts.setGroup('group1', charts1); plugin.charts.setGroup('group1', charts1);
const charts2 = [new Chart({ name: 'test2', title: 'Test2', component: null })]; const charts2 = [new Chart({ name: 'test2', title: 'Test2', Component: null })];
plugin.charts.setGroup('group2', charts2); plugin.charts.setGroup('group2', charts2);
expect(plugin.charts.getChart('group1.test1')).toEqual(charts1[0]); expect(plugin.charts.getChart('group1.test1')).toEqual(charts1[0]);
expect(plugin.charts.getChart('group2.test2')).toEqual(charts2[0]); expect(plugin.charts.getChart('group2.test2')).toEqual(charts2[0]);
@ -90,7 +90,7 @@ describe('api', () => {
}); });
describe('auto infer', () => { describe('auto infer', () => {
const chart = new Chart({ name: 'test', title: 'Test', component: null }); const chart = new Chart({ name: 'test', title: 'Test', Component: null });
const fields = [ const fields = [
{ {
name: 'price', name: 'price',

View File

@ -8,7 +8,7 @@ export class Statistic extends AntdChart {
super({ super({
name: 'statistic', name: 'statistic',
title: 'Statistic', title: 'Statistic',
component: AntdStatistic, Component: AntdStatistic,
config: [ config: [
{ {
property: 'field', property: 'field',

View File

@ -4,7 +4,7 @@ import { Table as AntdTable } from 'antd';
export class Table extends AntdChart { export class Table extends AntdChart {
constructor() { constructor() {
super({ name: 'table', title: 'Table', component: AntdTable }); super({ name: 'table', title: 'Table', Component: AntdTable });
} }
getProps({ data, fieldProps, general, advanced }: RenderProps) { getProps({ data, fieldProps, general, advanced }: RenderProps) {

View File

@ -22,7 +22,7 @@ export type RenderProps = {
export interface ChartType { export interface ChartType {
name: string; name: string;
title: string; title: string;
component: React.FC<any>; Component: React.FC<any>;
schema: ISchema; schema: ISchema;
init?: ( init?: (
fields: FieldOption[], fields: FieldOption[],
@ -34,7 +34,7 @@ export interface ChartType {
general?: any; general?: any;
advanced?: any; advanced?: any;
}; };
render: (props: RenderProps) => React.FC<any>; getProps(props: RenderProps): any;
getReference?: () => { getReference?: () => {
title: string; title: string;
link: string; link: string;
@ -44,21 +44,21 @@ export interface ChartType {
export type ChartProps = { export type ChartProps = {
name: string; name: string;
title: string; title: string;
component: React.FC<any>; Component: React.FC<any>;
config?: Config[]; config?: Config[];
}; };
export class Chart implements ChartType { export class Chart implements ChartType {
name: string; name: string;
title: string; title: string;
component: React.FC<any>; Component: React.FC<any>;
config: Config[]; config: Config[];
configs = new Map<string, Function>(); configs = new Map<string, Function>();
constructor({ name, title, component, config }: ChartProps) { constructor({ name, title, Component, config }: ChartProps) {
this.name = name; this.name = name;
this.title = title; this.title = title;
this.component = component; this.Component = Component;
this.config = config; this.config = config;
this.addConfigs(configs); this.addConfigs(configs);
} }
@ -170,11 +170,4 @@ export class Chart implements ChartType {
getProps(props: RenderProps): any { getProps(props: RenderProps): any {
return props; return props;
} }
render({ data, general, advanced, fieldProps }: RenderProps) {
return () =>
React.createElement(this.component, {
...this.getProps({ data, general, advanced, fieldProps }),
});
}
} }

View File

@ -4,7 +4,7 @@ import { ChartType } from '../chart';
export class Bar extends G2PlotChart { export class Bar extends G2PlotChart {
constructor() { constructor() {
super({ name: 'bar', title: 'Bar Chart', component: G2PlotBar, config: ['isGroup', 'isStack', 'isPercent'] }); super({ name: 'bar', title: 'Bar Chart', Component: G2PlotBar, config: ['isGroup', 'isStack', 'isPercent'] });
} }
init: ChartType['init'] = (fields, { measures, dimensions }) => { init: ChartType['init'] = (fields, { measures, dimensions }) => {

View File

@ -6,7 +6,7 @@ import lodash from 'lodash';
export class DualAxes extends G2PlotChart { export class DualAxes extends G2PlotChart {
constructor() { constructor() {
super({ name: 'dualAxes', title: 'Dual Axes Chart', component: G2DualAxes }); super({ name: 'dualAxes', title: 'Dual Axes Chart', Component: G2DualAxes });
this.config = [ this.config = [
'xField', 'xField',
{ {

View File

@ -2,11 +2,11 @@ import { Chart, ChartProps, ChartType, RenderProps } from '../chart';
import configs from './configs'; import configs from './configs';
export class G2PlotChart extends Chart { export class G2PlotChart extends Chart {
constructor({ name, title, component, config }: ChartProps) { constructor({ name, title, Component, config }: ChartProps) {
super({ super({
name, name,
title, title,
component, Component,
config: ['xField', 'yField', 'seriesField', ...(config || [])], config: ['xField', 'yField', 'seriesField', ...(config || [])],
}); });
this.addConfigs(configs); this.addConfigs(configs);

View File

@ -7,13 +7,13 @@ export default [
new G2PlotChart({ new G2PlotChart({
name: 'line', name: 'line',
title: 'Line Chart', title: 'Line Chart',
component: Line, Component: Line,
config: ['smooth', 'isStack'], config: ['smooth', 'isStack'],
}), }),
new G2PlotChart({ new G2PlotChart({
name: 'area', name: 'area',
title: 'Area Chart', title: 'Area Chart',
component: Area, Component: Area,
config: [ config: [
'smooth', 'smooth',
{ {
@ -26,16 +26,16 @@ export default [
new G2PlotChart({ new G2PlotChart({
name: 'column', name: 'column',
title: 'Column Chart', title: 'Column Chart',
component: Column, Component: Column,
config: ['isGroup', 'isStack', 'isPercent'], config: ['isGroup', 'isStack', 'isPercent'],
}), }),
new G2PlotChart({ new G2PlotChart({
name: 'bar', name: 'bar',
title: 'Bar Chart', title: 'Bar Chart',
component: Bar, Component: Bar,
config: ['isGroup', 'isStack', 'isPercent'], config: ['isGroup', 'isStack', 'isPercent'],
}), }),
new Pie(), new Pie(),
new DualAxes(), new DualAxes(),
new G2PlotChart({ name: 'scatter', title: 'Scatter Chart', component: Scatter }), new G2PlotChart({ name: 'scatter', title: 'Scatter Chart', Component: Scatter }),
]; ];

View File

@ -4,7 +4,7 @@ import { ChartType, RenderProps } from '../chart';
export class Pie extends G2PlotChart { export class Pie extends G2PlotChart {
constructor() { constructor() {
super({ name: 'pie', title: 'Pie Chart', component: G2Pie }); super({ name: 'pie', title: 'Pie Chart', Component: G2Pie });
this.config = [ this.config = [
{ {
property: 'field', property: 'field',

View File

@ -12,7 +12,7 @@ import {
useDesignable, useDesignable,
} from '@nocobase/client'; } from '@nocobase/client';
import { Empty, Result, Spin, Typography } from 'antd'; import { Empty, Result, Spin, Typography } from 'antd';
import React, { useContext } from 'react'; import React, { memo, useContext } from 'react';
import { ErrorBoundary } from 'react-error-boundary'; import { ErrorBoundary } from 'react-error-boundary';
import { ChartConfigContext } from '../configure'; import { ChartConfigContext } from '../configure';
import { useData, useFieldTransformer, useFieldsWithAssociation } from '../hooks'; import { useData, useFieldTransformer, useFieldsWithAssociation } from '../hooks';
@ -22,6 +22,8 @@ import { ChartRendererContext } from './ChartRendererProvider';
import { useChart } from '../chart/group'; import { useChart } from '../chart/group';
import { ChartDataContext } from '../block/ChartDataProvider'; import { ChartDataContext } from '../block/ChartDataProvider';
const { Paragraph, Text } = Typography; const { Paragraph, Text } = Typography;
import lodash from 'lodash';
import { Line } from '@ant-design/plots';
export const ChartRenderer: React.FC & { export const ChartRenderer: React.FC & {
Designer: React.FC; Designer: React.FC;
@ -38,7 +40,7 @@ export const ChartRenderer: React.FC & {
const chart = useChart(config?.chartType); const chart = useChart(config?.chartType);
const locale = api.auth.getLocale(); const locale = api.auth.getLocale();
const transformers = useFieldTransformer(transform, locale); const transformers = useFieldTransformer(transform, locale);
const Component = chart?.render({ const chartProps = chart?.getProps({
data, data,
general, general,
advanced, advanced,
@ -51,30 +53,27 @@ export const ChartRenderer: React.FC & {
return props; return props;
}, {}), }, {}),
}); });
const C = chart?.Component;
const C = () => if (!chart) {
chart ? ( return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={t('Please configure chart')} />;
}
if (!(data && data.length) && !service.loading) {
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={t('No data')} />;
}
return (
<Spin spinning={service.loading}>
<ErrorBoundary <ErrorBoundary
onError={(error) => { onError={(error) => {
console.error(error); console.error(error);
}} }}
FallbackComponent={ErrorFallback} FallbackComponent={ErrorFallback}
> >
<Component /> <C {...chartProps} />
</ErrorBoundary> </ErrorBoundary>
) : ( </Spin>
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={t('Please configure chart')} /> );
);
if (service.loading) {
return <Spin />;
}
if (!(data && data.length)) {
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={t('No data')} />;
}
return <C />;
}; };
ChartRenderer.Designer = function Designer() { ChartRenderer.Designer = function Designer() {