开始
在 Ant Design Pro 中,官方推荐使用 Ant Design Chart 这个 React 图表库,当然这个 也是基于 G2 的高交互可视化图形语法
在这里,我将常用的图表进行封装成一个组件,并保持原有的属性,然后通过一个参数来控制:type
共有 column(柱状图) line(折线图) dualAxes(双轴图) bar(条形图) area(面积图) pie(饼图) 六种图表
干了什么
有人可能会问,在 G2 中结构已经非常简便了,为何还需要封装?
是的,我也认为在原本的属性上已经封装的非常简便,没必要进行二次封装,同时,属性也确实非常多,我在这边写示例的时候只能列举一些常见的属性,那么封装的意义在于什么呢? 最为主要的一点就是:数据源(data)
我们先来看看 官网给的数据源是什么样的格式
const data = [{ name: '中国', value: '123', time: '2021' },{ name: '美国', value: '69', time: '2021' },{ name: '中国', value: '223', time: '2020' },{ name: '美国', value: '73', time: '2020' },...]
我们大概可以看出,所有的表格基本上分为三个参数 name, value, time(三个参数可以自由设置),以 time 为横轴, value为纵轴,name 为区分字段。
乍一看,这个数据源非常简单,能够清楚的看到数据结构,似乎没什么问题。
那么运用在实际的项目中,我们来看看后端提供的数据源是如何的4
const data = [{ a: '123', b: '69', start: '2021' },{ a: '223', b: '73', start: '2020' },...]
我相信大部分接口都是这样提供数据的,所以我主要就是将这种数据源转化为 Ant Design Charts的数据源,并结合接口,让他自动转化,自动匹配字段,转化为上述的格式—-这个是最主要的原因
其次,遵循一个系统一种类型,统一更改,样式匹配,我们可以根据具体项目进行对应的封装
注意事项
数据源提供两种方式,一共分为两种,第一种是直接将接口的值匹配给 data, 第二种则是 直接将接口放入onRequest,建议使用 onRequest
主要注意下这两个参数 fields 和 payload
- fields:返回接口匹配字段,用于实现 自定义匹配的功能
- payload:接口参数,请求接口时所带的参数
- 此外,还需要注意 legend tooltip label 三个参数,分别对应 配置图例 提示语 文本标签 三个字段
演示示例
做了一些相对的参数,与操作






卡片图表(Charts.Card)
通常而言,数据看板需要看不同维度,时间段的统计,为了简便起来,我们将结合 Charts、ProCard 做成卡片图标,并结合 DatePicker, DatePicker.RangePicker, Radio 形成限定条件
我们先说说需要注意的几点:
- 首先全部包含 Charts、ProCard的属性,并支持全局自定义配置
- 目前卡片图表只接受 onRequest(接口) 的方式,传输数据,不能通过 data 直接渲染
- 特别注意 condition, 目前设置三种查询状态,日期、日期时间段、单选按钮 三个查询以供图表查询
- 其次 要注意 payload 参数,它也 Charts 不同,他会返回一个查询条件的集合,方便作为接口参数
- 有什么好的建议,欢迎讨论,感谢~~
核心代码
这个最主要的功能就是数据转化,因此也没有太多的介绍,感兴趣可以在gitHub上参观~~
export const calcData = (listAll: Object, { xField, fields, fieldsLine, type, ...props}: ChartProps) => {const list = Array.isArray(listAll) ? listAll : [listAll]if(type === 'pie' && Array.isArray(fields)){if(fields.length !== 2){message.error('请输入对应的名称和值')return []}let res:any = [];list.map((item) => {res = [...res, { ...item, label: item[fields[0]], value: item[fields[1]]}]})if(props?.pie?.zero){res = res.filter((item:any) => item.value !== 0)}return res} else if(type === 'dualAxes'){if(!fieldsLine){message.error('请传入对应的折线图数据')return [[], []]}const keys = Object.keys(fields)const values = Object.values(fields)const keys1 = Object.keys(fieldsLine)const values1 = Object.values(fieldsLine)let res:any = []let res1:any = []list.map((item) => {keys.map((ele, index) => {if((item[ele] || item[ele] === 0) && xField){res = [...res, { ...item, label: values[index], value: item[ele], time: item[xField] || index }]}})keys1.map((ele, index) => {if((item[ele] || item[ele] === 0) && xField){res1 = [...res1, { ...item, label: values1[index], value: item[ele], time: item[xField] || index }]}})})return [res, res1]} else {const keys = Object.keys(fields)const values = Object.values(fields)let res:any = []list.map((item) => {keys.map((ele, index) => {if((item[ele] || item[ele] === 0) && xField){res = [...res, { ...item, label: values[index], value: item[ele], time: type ==='pie' ? undefined : item[xField] || index }]}})})if(props?.pie?.zero){res = res.filter((item:any) => item.value !== 0)}return res}}
具体代码
文件位置:src/components/Charts
全局配置文件:src/utils/Setting/ChartsSy
如何使用
主要以柱状图使用为例
import React, { useEffect } from 'react';import { Charts } from '@/components';import { Switch, Tooltip, Select } from 'antd';import { InfoCircleOutlined } from '@ant-design/icons';import { queryData } from './services';import { positionData, positionLabel, positionTooltip } from './test'import { useReactive } from 'ahooks';const TextShow: React.FC<{text: string, title: string}> = ({text='', title='', children}) => {return <span style={{marginTop: 8, fontWeight: 'bolder'}}>{text} <Tooltip title={title}><InfoCircleOutlined /></Tooltip> : {children}</span>}const { Option } = Select;const Mock: React.FC<any> = () => {const state = useReactive<any>({show: true,data: [],isRequest: true,legend: true,layout: false,position: 'top-left',labelPosition: 'middle',noSelect: false,label: true,labelContent: false,color: false,slider: true,sliderValue: false,tooltipCustom: false,tooltipTitle: false,tooltipPosition: 'right',})useEffect(() => {if(!state.isRequest){queryData({detail: 'data'}).then((res) => {state.data = [...res]})}}, [state.isRequest])const switchShow = (label:string, name:string, flag?: boolean) => {return <><span style={{marginLeft: 12, fontWeight: 'normal'}}>{label}:</span><Switch checked={state[name]} onChange={(e) => { if(flag){state.show = false;setTimeout(() => {state.show = true}, 200)} state[name] = e }}/></>}const selectShow = (list: Array<any>, label:string, name:string) => {return <><span style={{ marginLeft: 8}} >{label}:</span><Select value={state[name]} style={{ width: 120,marginLeft:8, marginTop:8 }} onChange={(e) => { state[name] = e }}>{list.map((data, i) => <Option key={i} value={data.value}>{data.name}</Option>)}</Select></>}return (<><div><TextShow text={'数据请求onRequest'} title="是否直接传入接口获取数据" ><Switch checked={state.isRequest} onChange={(e) => {state.isRequest = e }}/></TextShow></div><div style={{marginTop: 4}}><TextShow text={'图例'} title="legend的属性" >{ switchShow('是否展示', 'legend') }{ switchShow('是否垂直', 'layout') }{ selectShow(positionData, '位置', 'position') }{ switchShow('是否置灰', 'noSelect', true) }</TextShow></div><div style={{marginTop: 4}}><TextShow text={'文本标签'} title="label的属性" >{ switchShow('是否展示', 'label') }{ selectShow(positionLabel, '位置', 'labelPosition') }{ switchShow('是否改变文字', 'labelContent') }</TextShow></div><div style={{marginTop: 4}}><TextShow text={'提示语'} title="tooltip的属性" >{ switchShow('更改title', 'tooltipTitle') }{ selectShow(positionTooltip, '位置', 'tooltipPosition') }{ switchShow('是否自定义', 'tooltipCustom') }</TextShow></div><div style={{marginTop: 4}}><TextShow text={'其他'} title="有关的表格其余属性都在 colum" >{ switchShow('改变颜色', 'color') }{ switchShow('是否启动缩略轴', 'slider') }{ switchShow('改变缩略的值', 'sliderValue') }</TextShow></div>{state.show && <Chartsfields={{ a: '北方人口', b: '南方人口'}}type='column'onRequest={state.isRequest ? queryData : undefined}payload={state.isRequest ? () => ({ detail: 'data' }) : undefined}data={state.isRequest ? undefined : state.data}legend={ state.legend ? {layout: state.layout ? 'vertical' : 'horizontal',position: state.position,noSelect: state.noSelect ? ['北方人口'] : undefined,} : false}label={ state.label ? {position: state.labelPosition,content: state.labelContent ? (data:any) => {return data.name} : undefined} : false}tooltip={{title: state.tooltipTitle ? 'address' : undefined,position: state.tooltipPosition,customContent: state.tooltipCustom ? (title:any, data:any) => {return “<div style="padding: 8px 0px"><div>title</div><div style="margin-top: 8px"><p>data[0]?.data?.label : data[0]?.data?.name</p><p>data[1]?.data?.label : data[1]?.data?.name</p></div></div>“} : undefined,}}colum={{color: state.color ? ['red', 'yellow'] : undefined,slider: state.slider ? state.sliderValue ? {start: 0.1,end: 0.5} : {} : undefined,}}></Charts>}</>);};
此外,配置的相对较少,后续会根据具体项目逐渐迭代~~
