GridCard效果图
- 支持上下调整高度
- 支持左右调整列宽
- 图表自适应拖拽宽高
- 支持拖拽后的排序
GridLayoutCard
import React, {useMemo} from 'react';
import {array, number} from 'prop-types';
import {Card, Empty} from 'antd';
import {WidthProvider, Responsive} from 'react-grid-layout';
import 'react-grid-layout/css/styles.css';
import 'react-resizable/css/styles.css';
import {LineChart} from '@/components';
const WithGridLayout = WidthProvider(Responsive);
const GridItemComponent = React.forwardRef(({item, style, children, ...rest}, ref) => {
const height = window.parseInt(style.height) - 60 || 200;
return (
<Card
{...rest}
title={item.title}
size='small'
style={style}
ref={ref}
>
<LineChart
dataSource={item.dataSource}
xAxisData={item.xAxisData}
height={height}
/>
{children}
</Card>
);
});
GridLayoutCard.propTypes = {
dataSource: array.isRequired,
gutter: array,
rowHeight: number,
};
GridLayoutCard.defaultProps = {
gutter: [16, 24],
rowHeight: 80,
}
function GridLayoutCard({dataSource, gutter, rowHeight}) {
const layout = useMemo(() => {
return dataSource.map((it, index) => ({
i: String(it.id), // 组件key值
x: (index % 3) * 4, // 组件在x轴坐标
y: Math.floor(index / 3), // 组件在y轴坐标
w: 4, // 组件宽度,占多少列
h: 3, // 组件高度 = rowHeight的倍数,3倍
minW: 3, // 最小宽度
// placeholder: index,
}));
}, [dataSource])
if (!dataSource?.length) {
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/>
}
return (
<WithGridLayout
layouts={{lg: layout, md: layout, sm: layout}}
cols={{lg: 12, md: 12, sm: 6, xs: 4, xxs: 2}}
breakpoints={{lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0}}
rowHeight={rowHeight}
margin={gutter}
containerPadding={[0, 0]}
resizeHandles={['ne', 'se']}
// innerRef={ref}
// compactType="horizontal"
>
{dataSource.map(it => {
return (
<GridItemComponent
key={it.id}
item={it}
/>
);
})}
</WithGridLayout>
);
}
export default GridLayoutCard;
使用组件
import GridLayout from '@/components';
const mockData = [
{
id: 10,
title: '00',
dataSource: [{label: 'ONE', value: [150, 230, 224, 218, 135, 147, 260]}],
xAxisData: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
{
id: 11,
title: '11',
dataSource: [{label: 'ONE', value: [150, 230, 224, 218, 135, 147, 260]}],
xAxisData: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
{
id: 22,
title: '22',
dataSource: [{label: 'ONE', value: [150, 230, 224, 218, 135, 147, 260]}],
xAxisData: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
{
id: 33,
title: '33',
dataSource: [{label: 'ONE', value: [150, 230, 224, 218, 135, 147, 260]}],
xAxisData: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
{
id: 44,
title: '44',
dataSource: [{label: 'ONE', value: [150, 230, 224, 218, 135, 147, 260]}],
xAxisData: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
{
id: 55,
title: '55',
dataSource: [{label: 'ONE', value: [150, 230, 224, 218, 135, 147, 260]}],
xAxisData: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
{
id: 66,
title: '66',
dataSource: [{label: 'ONE', value: [150, 230, 224, 218, 135, 147, 260]}],
xAxisData: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
];
function App()
return (
<Spin spinning={loading}>
<GridCard
dataSource={mockData}
onDragEnd={onDragEnd}
gutter={[16, 24]}
/>
</Spin>
)
}
GridLayoutComponent
import React, {useMemo} from 'react';
import {array, number} from 'prop-types';
import {Empty} from "antd";
import {WidthProvider, Responsive} from 'react-grid-layout';
import 'react-grid-layout/css/styles.css';
import 'react-resizable/css/styles.css';
import GridLayoutItem from './GridLayoutItem'
// import styles from './style.module.less';
const WithGridLayout = WidthProvider(Responsive);
GridLayoutCard.propTypes = {
dataSource: array.isRequired,
gutter: array,
rowHeight: number,
};
GridLayoutCard.defaultProps = {
gutter: [16, 24],
rowHeight: 80,
}
function GridLayoutCard({dataSource, gutter, rowHeight}) {
const layout = useMemo(() => {
return dataSource.map((it, index) => ({
i: String(it.id), // 组件key值,唯一的
x: (index % 3) * 4, // 组件在x轴坐标
y: Math.floor(index / 3), // 组件在y轴坐标
w: 4, // 组件宽度,占多少列
h: 3, // 组件高度 = rowHeight的倍数,3就是 rowHeight的3倍
minW: 3, // 最小宽度
minH: 2, // 最小高度
// placeholder: index,
}));
}, [dataSource])
if (!dataSource?.length) {
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/>
}
return (
<WithGridLayout
layouts={{lg: layout, md: layout, sm: layout}}
cols={{lg: 12, md: 12, sm: 6, xs: 4, xxs: 2}}
breakpoints={{lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0}}
rowHeight={rowHeight}
margin={gutter}
containerPadding={[0, 0]}
resizeHandles={['ne', 'se']}
// className={styles.grid}
// compactType="horizontal"
>
{dataSource.map(it => {
return (
<GridLayoutItem
extra
key={it.id}
item={it}
onEdit={() => {
}}
onDelete={() => {
}}
/>
);
})}
</WithGridLayout>
);
}
export default GridLayoutCard;
GridLayoutItem
import {useMemo, forwardRef} from 'react';
import {string, bool, object} from 'prop-types';
import {Card} from 'antd';
import moment from 'moment';
import {LineChart, BarChart, PieChart} from '@/components';
import Table from '../SortableHocCard/Table'
import DropdownMenu from '../SortableHocCard/DropdownMenu'
const COMPONENT = {
LINE: LineChart,
BAR: BarChart,
PIE: PieChart,
TABLE: Table,
}
function GridLayoutItem(props, ref) {
const {extra, item, theme, style, children, onEdit, onDelete, ...rest} = props;
const {name, type, source} = item;
const Component = COMPONENT[type];
const height = useMemo(() => {
return window.parseInt(style.height) - 60 || 200;
}, [style.height]);
const chartProps = useMemo(initSource, [source]);
function initSource() {
const _props = {xAxisData: [], dataSource: []}
if (!source) return _props;
if (type === 'TABLE') {
_props.dataSource = source;
} else if (type === 'PIE') {
_props.dataSource = source.map(it => {
return {name: it.name, value: it.metricValue}
});
} else if (['BAR', 'LINE'].includes(type)) {
_props.dataSource = source.map(it => {
return {label: it.name, value: it.values[1]}
});
const {values} = source[0];
_props.xAxisData = values[0].map(it => moment(it).format('HH:mm[\n]MM-DD'))
}
return _props;
}
return (
<Card
{...rest}
size='small'
style={style}
ref={ref}
title={name}
bodyStyle={{padding: (type === 'TABLE') ? 0 : 12}}
extra={extra && <DropdownMenu onEdit={onEdit} onDelete={onDelete}/>}
>
{Component && (
<Component
{...chartProps}
height={height}
theme={theme}
/>
)}
{children}
</Card>
);
}
const GridLayoutItemRef = forwardRef(GridLayoutItem)
GridLayoutItemRef.propTypes = {
item: object.isRequired,
extra: bool,
theme: string,
};
export default GridLayoutItemRef;
antd 3x Card组件报错
react-grid-layout Error:
不是 react版本的问题;是:antd 3x版本的 Card组件引起的 React.forwardRef,无法获取 ref
解决:
- 升级 antd到 4x版本
- 用 div代替 Card组件
�