feat(bi): make more config visualizable (#2386)
* feat(bi): make more config visualizable * refactor: improve config schema of chart * chore: set default isStack of area chart as true
This commit is contained in:
parent
dcee24a1b0
commit
1ea1e903ed
@ -7,28 +7,28 @@ import { QueryProps } from '../../renderer';
|
||||
import { RenderProps } from '../chart';
|
||||
|
||||
export class Statistic extends AntdChart {
|
||||
schema: ISchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
field: {
|
||||
title: lang('Field'),
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
'x-reactions': '{{ useChartFields }}',
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
title: lang('Title'),
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Input',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super('statistic', 'Statistic', AntdStatistic);
|
||||
super({
|
||||
name: 'statistic',
|
||||
title: 'Statistic',
|
||||
component: AntdStatistic,
|
||||
config: [
|
||||
{
|
||||
property: 'field',
|
||||
name: 'field',
|
||||
title: 'Field',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
title: {
|
||||
title: lang('Title'),
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Input',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
init(
|
||||
|
@ -4,7 +4,7 @@ import { Table as AntdTable } from 'antd';
|
||||
|
||||
export class Table extends AntdChart {
|
||||
constructor() {
|
||||
super('table', 'Table', AntdTable);
|
||||
super({ name: 'table', title: 'Table', component: AntdTable });
|
||||
}
|
||||
|
||||
getProps({ data, fieldProps, general, advanced }: RenderProps) {
|
||||
|
@ -3,6 +3,7 @@ import { FieldOption } from '../hooks';
|
||||
import { QueryProps } from '../renderer';
|
||||
import { parseField } from '../utils';
|
||||
import { ISchema } from '@formily/react';
|
||||
import configs, { AnySchemaProperties, ConfigProps } from './configs';
|
||||
|
||||
export type RenderProps = {
|
||||
data: any[];
|
||||
@ -58,16 +59,78 @@ export interface ChartType {
|
||||
render: (props: RenderProps) => React.FC<any>;
|
||||
}
|
||||
|
||||
type Config = (
|
||||
| (ConfigProps & {
|
||||
property?: string;
|
||||
})
|
||||
| string
|
||||
)[];
|
||||
|
||||
export type ChartProps = {
|
||||
name: string;
|
||||
title: string;
|
||||
component: React.FC<any>;
|
||||
config?: Config;
|
||||
};
|
||||
|
||||
export class Chart implements ChartType {
|
||||
name: string;
|
||||
title: string;
|
||||
component: React.FC<any>;
|
||||
schema = {};
|
||||
config: Config;
|
||||
configs = new Map<string, Function>();
|
||||
|
||||
constructor(name: string, title: string, component: React.FC<any>) {
|
||||
constructor({ name, title, component, config }: ChartProps) {
|
||||
this.name = name;
|
||||
this.title = title;
|
||||
this.component = component;
|
||||
this.config = config;
|
||||
this.addConfigs(configs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate config schema according to this.config
|
||||
* How to set up this.config:
|
||||
* 1. string - the config function name in config.ts
|
||||
* 2. object - { property: string, ...props }
|
||||
* - property is the config function name in config.ts, and the other props are the arguments of the function
|
||||
* 3. object - use the object directly as the properties of the schema
|
||||
* 4. function - use the custom function to return the properties of the schema
|
||||
*/
|
||||
get schema() {
|
||||
if (!this.config) {
|
||||
return {};
|
||||
}
|
||||
const properties = this.config.reduce((properties, conf) => {
|
||||
let schema: AnySchemaProperties = {};
|
||||
if (typeof conf === 'string') {
|
||||
const func = this.configs.get(conf);
|
||||
schema = func?.() || {};
|
||||
} else if (typeof conf === 'function') {
|
||||
schema = conf();
|
||||
} else {
|
||||
if (conf.property) {
|
||||
const func = this.configs.get(conf.property);
|
||||
schema = func?.(conf) || {};
|
||||
} else {
|
||||
schema = conf as AnySchemaProperties;
|
||||
}
|
||||
}
|
||||
return {
|
||||
...properties,
|
||||
...schema,
|
||||
};
|
||||
}, {} as AnySchemaProperties);
|
||||
return {
|
||||
type: 'object',
|
||||
properties,
|
||||
};
|
||||
}
|
||||
|
||||
addConfigs(configs: { [key: string]: Function }) {
|
||||
Object.entries(configs).forEach(([key, func]) => {
|
||||
this.configs.set(key, func);
|
||||
});
|
||||
}
|
||||
|
||||
infer(
|
||||
|
@ -0,0 +1,43 @@
|
||||
import { SchemaProperties } from '@formily/react';
|
||||
import { lang } from '../locale';
|
||||
|
||||
export type FieldConfigProps = Partial<{
|
||||
name: string;
|
||||
title: string;
|
||||
required: boolean;
|
||||
defaultValue: any;
|
||||
}>;
|
||||
|
||||
export type AnySchemaProperties = SchemaProperties<any, any, any, any, any, any, any, any>;
|
||||
export type ConfigProps = FieldConfigProps | AnySchemaProperties | (() => AnySchemaProperties);
|
||||
|
||||
const selectField = ({ name, title, required, defaultValue }: FieldConfigProps) => {
|
||||
return {
|
||||
[name]: {
|
||||
title: lang(title),
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
'x-reactions': '{{ useChartFields }}',
|
||||
required,
|
||||
default: defaultValue,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const booleanField = ({ name, title, defaultValue = false }: FieldConfigProps) => {
|
||||
return {
|
||||
[name]: {
|
||||
'x-content': lang(title),
|
||||
type: 'boolean',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Checkbox',
|
||||
default: defaultValue,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default {
|
||||
field: selectField,
|
||||
booleanField,
|
||||
};
|
@ -5,7 +5,7 @@ import { G2PlotChart } from './g2plot';
|
||||
|
||||
export class Bar extends G2PlotChart {
|
||||
constructor() {
|
||||
super('bar', 'Bar Chart', G2PlotBar);
|
||||
super({ name: 'bar', title: 'Bar Chart', component: G2PlotBar, config: ['isGroup', 'isStack', 'isPercent'] });
|
||||
}
|
||||
|
||||
init(
|
||||
|
@ -0,0 +1,12 @@
|
||||
import config, { FieldConfigProps } from '../configs';
|
||||
const { field, booleanField } = config;
|
||||
|
||||
export default {
|
||||
xField: (props: FieldConfigProps) => field({ name: 'xField', title: 'xField', required: true, ...props }),
|
||||
yField: (props: FieldConfigProps) => field({ name: 'yField', title: 'yField', required: true, ...props }),
|
||||
seriesField: (props: FieldConfigProps) => field({ name: 'seriesField', title: 'seriesField', ...props }),
|
||||
isStack: (props: FieldConfigProps) => booleanField({ name: 'isStack', title: 'isStack', ...props }),
|
||||
smooth: (props: FieldConfigProps) => booleanField({ name: 'smooth', title: 'smooth', ...props }),
|
||||
isPercent: (props: FieldConfigProps) => booleanField({ name: 'isPercent', title: 'isPercent', ...props }),
|
||||
isGroup: (props: FieldConfigProps) => booleanField({ name: 'isGroup', title: 'isGroup', ...props }),
|
||||
};
|
@ -7,63 +7,54 @@ import { FieldOption } from '../../hooks';
|
||||
import { QueryProps } from '../../renderer';
|
||||
|
||||
export class DualAxes extends G2PlotChart {
|
||||
schema: ISchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
xField: {
|
||||
title: '{{t("xField")}}',
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
'x-reactions': '{{ useChartFields }}',
|
||||
required: true,
|
||||
},
|
||||
yField: {
|
||||
title: '{{t("yField")}}',
|
||||
type: 'array',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'ArrayItems',
|
||||
items: {
|
||||
type: 'void',
|
||||
'x-component': 'Space',
|
||||
properties: {
|
||||
sort: {
|
||||
type: 'void',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'ArrayItems.SortHandle',
|
||||
},
|
||||
input: {
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
'x-reactions': '{{ useChartFields }}',
|
||||
'x-component-props': {
|
||||
style: {
|
||||
minWidth: '200px',
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
remove: {
|
||||
type: 'void',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'ArrayItems.Remove',
|
||||
},
|
||||
},
|
||||
},
|
||||
properties: {
|
||||
add: {
|
||||
type: 'void',
|
||||
title: '{{t("Add")}}',
|
||||
'x-component': 'ArrayItems.Addition',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super('dualAxes', 'Dual Axes Chart', G2DualAxes);
|
||||
super({ name: 'dualAxes', title: 'Dual Axes Chart', component: G2DualAxes });
|
||||
this.config = [
|
||||
'xField',
|
||||
{
|
||||
yField: {
|
||||
title: '{{t("yField")}}',
|
||||
type: 'array',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'ArrayItems',
|
||||
items: {
|
||||
type: 'void',
|
||||
'x-component': 'Space',
|
||||
properties: {
|
||||
sort: {
|
||||
type: 'void',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'ArrayItems.SortHandle',
|
||||
},
|
||||
input: {
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
'x-reactions': '{{ useChartFields }}',
|
||||
'x-component-props': {
|
||||
style: {
|
||||
minWidth: '200px',
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
remove: {
|
||||
type: 'void',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'ArrayItems.Remove',
|
||||
},
|
||||
},
|
||||
},
|
||||
properties: {
|
||||
add: {
|
||||
type: 'void',
|
||||
title: '{{t("Add")}}',
|
||||
'x-component': 'ArrayItems.Addition',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
init(
|
||||
|
@ -1,37 +1,18 @@
|
||||
import { Chart, RenderProps } from '../chart';
|
||||
import { Chart, ChartProps, RenderProps } from '../chart';
|
||||
import { FieldOption } from '../../hooks';
|
||||
import { QueryProps } from '../../renderer';
|
||||
import { ISchema } from '@formily/react';
|
||||
import configs from './configs';
|
||||
|
||||
export class G2PlotChart extends Chart {
|
||||
schema: ISchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
xField: {
|
||||
title: '{{t("xField")}}',
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
'x-reactions': '{{ useChartFields }}',
|
||||
required: true,
|
||||
},
|
||||
yField: {
|
||||
title: '{{t("yField")}}',
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
'x-reactions': '{{ useChartFields }}',
|
||||
required: true,
|
||||
},
|
||||
seriesField: {
|
||||
title: '{{t("seriesField")}}',
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
'x-reactions': '{{ useChartFields }}',
|
||||
},
|
||||
},
|
||||
};
|
||||
constructor({ name, title, component, config }: ChartProps) {
|
||||
super({
|
||||
name,
|
||||
title,
|
||||
component,
|
||||
config: ['xField', 'yField', 'seriesField', ...(config || [])],
|
||||
});
|
||||
this.addConfigs(configs);
|
||||
}
|
||||
|
||||
init(
|
||||
fields: FieldOption[],
|
||||
@ -73,7 +54,7 @@ export class G2PlotChart extends Chart {
|
||||
obj[key] = value;
|
||||
if (key.includes('Field')) {
|
||||
if (Array.isArray(value)) {
|
||||
obj[key] = value.map((v) => (v.includes('.') ? replace(v) : v));
|
||||
obj[key] = value.map((v) => (v?.includes('.') ? replace(v) : v));
|
||||
} else if (typeof value === 'string' && value.includes('.')) {
|
||||
obj[key] = replace(value);
|
||||
}
|
||||
@ -101,7 +82,7 @@ export class G2PlotChart extends Chart {
|
||||
getReference() {
|
||||
return {
|
||||
title: this.title,
|
||||
link: `https://g2plot.antv.antgroup.com/api/plots/${this.name}`,
|
||||
link: `https://charts.ant.design/api/plots/${this.name}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,37 @@
|
||||
import { Area, Column, Line, Scatter } from '@ant-design/plots';
|
||||
import { Chart } from '../chart';
|
||||
import { Bar } from './bar';
|
||||
import { Pie } from './pie';
|
||||
import { DualAxes } from './dualAxes';
|
||||
import { G2PlotChart } from './g2plot';
|
||||
|
||||
export default [
|
||||
new G2PlotChart('line', 'Line Chart', Line),
|
||||
new G2PlotChart('area', 'Area Chart', Area),
|
||||
new G2PlotChart('column', 'Column Chart', Column),
|
||||
new G2PlotChart({
|
||||
name: 'line',
|
||||
title: 'Line Chart',
|
||||
component: Line,
|
||||
config: ['smooth', 'isStack'],
|
||||
}),
|
||||
new G2PlotChart({
|
||||
name: 'area',
|
||||
title: 'Area Chart',
|
||||
component: Area,
|
||||
config: [
|
||||
'smooth',
|
||||
{
|
||||
property: 'isStack',
|
||||
defaultValue: true,
|
||||
},
|
||||
'isPercent',
|
||||
],
|
||||
}),
|
||||
new G2PlotChart({
|
||||
name: 'column',
|
||||
title: 'Column Chart',
|
||||
component: Column,
|
||||
config: ['isGroup', 'isStack', 'isPercent'],
|
||||
}),
|
||||
new Bar(),
|
||||
new Pie(),
|
||||
new DualAxes(),
|
||||
new G2PlotChart('scatter', 'Scatter Chart', Scatter),
|
||||
new G2PlotChart({ name: 'scatter', title: 'Scatter Chart', component: Scatter }),
|
||||
];
|
||||
|
@ -5,30 +5,22 @@ import { FieldOption } from '../../hooks';
|
||||
import { QueryProps } from '../../renderer';
|
||||
|
||||
export class Pie extends G2PlotChart {
|
||||
schema: ISchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
angleField: {
|
||||
title: '{{t("angleField")}}',
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
'x-reactions': '{{ useChartFields }}',
|
||||
required: true,
|
||||
},
|
||||
colorField: {
|
||||
title: '{{t("colorField")}}',
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'Select',
|
||||
'x-reactions': '{{ useChartFields }}',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super('pie', 'Pie Chart', G2Pie);
|
||||
super({ name: 'pie', title: 'Pie Chart', component: G2Pie });
|
||||
this.config = [
|
||||
{
|
||||
property: 'field',
|
||||
name: 'angleField',
|
||||
title: 'angleField',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
property: 'field',
|
||||
name: 'colorField',
|
||||
title: 'colorField',
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
init(
|
||||
|
@ -69,4 +69,8 @@ export default {
|
||||
Max: '最大值',
|
||||
'Please select a chart type.': '请选择图表类型',
|
||||
Collection: '数据表',
|
||||
isStack: '堆叠',
|
||||
isPercent: '显示为百分比',
|
||||
isGroup: '分组',
|
||||
smooth: '平滑曲线',
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user