背景
实现对指定数据的「指标拆解图」 渲染,并且可以根据筛选项进行变化。
任务分解
- antv 的图表,以及请求后端的载荷对传入的数据结构有严格要求
- 一个工具函数将后端接口返回的数据格式化成 antv 图表要求的格式
- 一个工具函数将前端提交的请求数据格式化后端提供的接口规范形式。
- 刚进入页面图表需要有个初始状态的渲染
- 可以用 useModel 实现初始化的值
- 完成筛选、重置功能
- 结合 form 以及 umi 的 request 实现
代码实现
import { fetchXXXGroup } from '@/services/XXX';
import { DecompositionTreeGraph } from '@ant-design/graphs';
import { Button, Card, Form } from 'antd';
import { useState } from 'react';
import { useModel } from 'umi';
interface XXXGraphType {
id: string;
value?: {
title?: string;
items?: {
text?: string;
value?: string;
}[];
};
children?: XXXGraphType[];
}
// 格式化处理后端接口返回值 使其符合 Antv 基本要求
const toGraphData = (data: any[]) => {
const basicData: any = data.map(
({
name,
XXX_id,
XXX_level,
children,
name,
count,
}) => ({
id: XXX_level + '#' + XXX_id,
value: {
title: XXX_name,
items: [{ text: name, value: count + '人' }],
},
children: toGraphData(children),
}),
);
return basicData;
};
const DepartmentOverviewTree = () => {
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
const rawData: any = useModel('useXXX');
const dataXXX: object[] = rawData.XXX;
const formattedData:XXXGraphType = toGraphData(dataXXX);
const initialData = {
id: 'root',
value: {
title: ' XXX概览图',
},
children: formattedData,
};
const [data, setData] = useState<any>(initialData);
const onReset = () => {
form.resetFields();
onFinish()
};
// 格式化前端提交数据
const formatForm = (params: any) => {
const { address, xxx, ...props } = params || {};
return {
address: address ? address.join(',') : undefined,
xxx: xxx ? xxx.map((arr: any) => arr.join('#')).join(',') : undefined,
...props,
};
};
const onFinish = async () => {
setLoading(true);
try {
const { data } = await fetchXXXGroup({
...formatForm(form.getFieldsValue()),
});
const { depts } = data || {};
if (Array.isArray(depts)) {
const basicData = toGraphData(depts)
const formattedData = {
id: 'root',
value: {
title: 'XXX概览图',
},
children: basicData,
}
setData(formattedData);
}
} finally {
setLoading(false);
}
};
const config: any = {
data: data,
layout: {
direction: 'TB',
ranksepFunc: () => 20,
},
nodeCfg: {
size: [120, 20],
type: 'indicator-card',
anchorPoints: [
[0.5, 0],
[0.5, 1],
],
autoWidth: true,
items: {
style: (cfg: any, group: any, type: string | number) => {
const styles = {
value: {
padding: 2,
},
};
// @ts-ignore
return styles[type];
},
},
},
markerCfg: (cfg: any) => {
const { children, id } = cfg;
return {
show: id !== 'root' && children?.length,
position: 'bottom',
animate: false,
};
},
edgeCfg: {
type: 'polyline',
},
animate: false,
autoFit: true,
fitCenter: true,
};
return (
<>
<Card style={{ marginBottom: '20px' }}>
<Form layout="inline" onFinish={onFinish} form={form}>
<Form.Item label="address:" name="address">
<AddressSelect mode="multiple" style={{ width: '200px' }} />
</Form.Item>
<Form.Item label="XXX:" name="XXX">
<DepartmentSelect style={{ width: '200px' }} />
</Form.Item>
<Form.Item labelAlign="right">
<Button onClick={onReset} style={{ marginLeft: '300px' }}>
重置
</Button>
</Form.Item>
<Form.Item labelAlign="right">
<Button type="primary" htmlType="submit">
查询
</Button>
</Form.Item>
</Form>
</Card>
<Card>
<DecompositionTreeGraph {...config} />
</Card>
</>
);
};
export default DepartmentOverviewTree;
其他
获取图表实例
一、onReady 回调
import React from 'react';
import { Line } from '@ant-design/charts';
const Page: React.FC = () => {
const data = [];
const config = {};
return <Line {...config} onReady={(chart) => console.log(chart)} />;
};
export default Page;
二、挂载到 ref 上
import React from 'react';
import { Bar } from '@ant-design/charts';
const Page: React.FC = () => {
const data = [];
const config = {};
const ref = React.useRef();
React.useEffect(() => {
console.log(ref.current.getChart());
}, []);
return <Bar {...config} ref={ref} />;
};
export default Page;
注意
Antv DecompositionTreeGraph 指标拆解图的 data 数据源是「对象」不是「对象数组」,后端接口返回的值一般是带有多个字段的对象数组,处理时注意最后要变成严格遵循下面格式的
「对象」类型
interface XXXGraphType {
id: string;
value: {
title: string;
items: {
text: string;
value?: string;
}[];
};
children: XXXGraphType[];
}