使用react-grid-layout和react-full-screen实现一个可自定义和全屏展示的dashboard页面
Posted c.
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用react-grid-layout和react-full-screen实现一个可自定义和全屏展示的dashboard页面相关的知识,希望对你有一定的参考价值。
文章目录
使用react-grid-layout和react-full-screen实现一个可自定义和全屏展示的dashboard页面
之前的博文中已经讲了如何使用react-grid-layout
和echarts-for-react
实现一个支持拖拽的自定义响应式dashboard页面, 还有使用react-sizeme
解决了侧栏(抽屉)展开或隐藏时不会自适应容器大小的问题
参考:
《使用react-grid-layout和echarts-for-react实现一个支持拖拽的自定义响应式dashboard页面》
《使用react-sizeme解决react-grid-layout中侧栏(抽屉)展开或隐藏时不会自适应容器大小的问题》
接下来我们需要做的功能如下:
- 我们可以全局锁定和解锁dashboard中的图表组件,让其不允许拖动和缩放。
- 懒加载对应的图表组件显示到dashboard中
- 实时保存当前界面的布局到localstorage,以便下次进入页面可以展示上一次编辑的dashboard布局
- 实现dashboard全屏展示,这里会用到
react-full-screen
具体实现代码展示
主展示页面
import React, useLayoutEffect, useState from "react";
import 'react-grid-layout/css/styles.css'
import 'react-resizable/css/styles.css'
import findIndex from "lodash";
import './dashboard.css'
import CloseOutlined, LockOutlined, QuestionCircleOutlined, UnlockOutlined from "@ant-design/icons";
import ReactGridLayout from "react-grid-layout";
import isEmpty from "lodash-es";
import withSize from 'react-sizeme';
import LazyWidget from "@/pages/Dashboard/Detail/Widget/LazyWidget";
import DashboardMenuButton from "@/pages/Dashboard/Detail/DashboardMenuButton";
import FullScreen, useFullScreenHandle from "react-full-screen";
interface DashboardWidgetInfo
widgetName: string,
layout: ReactGridLayout.Layout
function DashboardGird(size: width: any)
const [widgets, setWidgets] = useState<DashboardWidgetInfo[]>([]);
const handle = useFullScreenHandle();
useLayoutEffect(() =>
const layoutJson = localStorage.getItem("dashboard_layout");
if (isEmpty(layoutJson))
return;
setWidgets(JSON.parse(layoutJson as string));
, []);
const getLayouts: any = () =>
return widgets.map(item =>
return
...item.layout
);
const setLayoutStatic = (widget: DashboardWidgetInfo, staticFlag: boolean) =>
const index = findIndex(widgets, (w: any) => w.widgetName === widget.widgetName);
if (index !== -1)
const updateWidget = widgets[index];
updateWidget.layout.static = staticFlag;
widgets.splice(index, 1, ...updateWidget);
const newWidgets = [...widgets];
setWidgets(newWidgets);
const lockWidget = (widget: DashboardWidgetInfo) =>
setLayoutStatic(widget, true);
const unlockWidget = (widget: DashboardWidgetInfo) =>
setLayoutStatic(widget, false);
const onRemoveWidget = (widget: DashboardWidgetInfo) =>
const widgetIndex = findIndex(widgets, (w: any) => w.layout.i === widget.layout.i);
if (widgetIndex !== -1)
widgets.splice(widgetIndex, 1);
const newWidgets = [...widgets];
setWidgets(newWidgets);
const getWidgetComponent = (widgetName: string) =>
return (<LazyWidget widgetName=widgetName/>)
const createWidget = (widget: DashboardWidgetInfo) =>
return (
<div className='dashboard-widget-wrapper' key=widget.layout.i data-grid=widget.layout>
<span className='dashboard-widget-header'>
<QuestionCircleOutlined className='dashboard-widget-header-icon'/>
widget.layout.static ? <LockOutlined className='dashboard-widget-header-icon' onClick=() => unlockWidget(widget)/> : (
<UnlockOutlined className='dashboard-widget-header-icon' onClick=() => lockWidget(widget)/>)
<CloseOutlined className='dashboard-widget-header-icon' onClick=() => onRemoveWidget(widget)/>
</span>
getWidgetComponent(widget.widgetName)
</div>
);
const onAddWidget = () =>
const x = (widgets.length * 3) % 12;
const widgetName = x % 2 == 0 ? 'BarChartWidget' : 'PieChartWidget';
const index = findIndex(widgets, (w) => w.widgetName === widgetName);
if (index !== -1)
return;
const newWidgets = [...widgets,
widgetName: widgetName,
layout: i: widgetName, x: x, y: Infinity, w: 3, h: 2, static: false
] as DashboardWidgetInfo[];
setWidgets(newWidgets);
const onLayoutChange = (layouts: any[]) =>
for (const layout of layouts)
const updateIndex = findIndex(widgets, (w) => w.layout.i === layout.i);
if (updateIndex !== -1)
const updateWidget = widgets[updateIndex];
updateWidget.layout =
...layout,
;
widgets.splice(updateIndex, 1, ...updateWidget);
const newWidgets = [...widgets];
setWidgets(newWidgets);
localStorage.setItem("dashboard_layout", JSON.stringify(widgets));
const lockAllWidgets = () =>
setWidgets(widgets.map(w =>
return
...w,
layout:
...w.layout,
static: true
) as DashboardWidgetInfo[]);
const unlockAllWidgets = () =>
setWidgets(widgets.map(w =>
return
...w,
layout:
...w.layout,
static: false
) as DashboardWidgetInfo[]);
const enterFullScreen = () =>
handle.enter();
const menuConfig =
addWidget: onAddWidget,
lockAllWidgets: lockAllWidgets,
unlockAllWidgets: unlockAllWidgets,
enterFullScreen: enterFullScreen
return (
<FullScreen handle=handle>
<div>
<DashboardMenuButton ...menuConfig/>
<ReactGridLayout
cols=12
rowHeight=100
width=width
autoSize=true
isDraggable=true
isResizable=true
isBounded=true
layout=getLayouts()
className='layouts'
onLayoutChange=onLayoutChange>
widgets?.map(item => createWidget(item))
</ReactGridLayout>
</div>
</FullScreen>
);
export default withSize(refreshMode: 'debounce', refreshRate: 60)(DashboardGird);
懒加载组件
import React, useMemo from "react";
import WidgetLoadingSpin from "@/pages/Dashboard/Detail/WidgetLoadingSpin";
function LazyWidget(widgetName: any)
const LazyComponent = React.lazy(() => import('@/pages/Dashboard/Detail/Widget/' + widgetName));
return useMemo(() => (<React.Suspense fallback=<WidgetLoadingSpin/>>
<LazyComponent/>
</React.Suspense>), [widgetName]);
export default LazyWidget
组件加载时展示的组件
import Spin from "antd";
import React from "react";
import './dashboard.css'
function WidgetLoadingSpin()
return (
<div className='dashboard-widget-loading'><Spin tip='Loading...'/></div>
)
export default WidgetLoadingSpin;
dashboard菜单组件
import React, useState from "react";
import Button, Dropdown, MenuProps from "antd";
import
AppstoreAddOutlined,
AppstoreOutlined,
FullscreenOutlined,
LockOutlined,
RedoOutlined,
UnlockOutlined
from "@ant-design/icons";
import Draggable, DraggableBounds, DraggableData, DraggableEvent from 'react-draggable'
import './dashboard.css'
interface DashboardMenuProps
addWidget: () => void,
lockAllWidgets: () => void,
unlockAllWidgets: () => void,
enterFullScreen: () => void
const DashboardMenuButton = (props: DashboardMenuProps) =>
const [bound, setBound] = useState<DraggableBounds>(left: 0, top: 0, bottom: 0, right: 0)
const onStart = (event: DraggableEvent, draggableData: DraggableData) =>
const clientWidth, clientHeight = window?.document?.documentElement;
const targetRect = document.getElementById("draggable-dashboard-menu-button")?.getBoundingClientRect();
const rightSpaceWidth = 5;
if (targetRect)
setBound(
left: -targetRect?.left + draggableData?.x,
right: clientWidth - (targetRect?.right - draggableData?.x) - rightSpaceWidth,
top: -targetRect?.top + draggableData?.y,
bottom: clientHeight - (targetRect?.bottom - draggableData?.y)
)
;
const items: MenuProps['items'] = [
key: 'addWidget',
label: (
<AppstoreAddOutlined onClick=props.addWidget/>
),
,
key: 'lockAll',
label: (
<LockOutlined onClick=props.lockAllWidgets/>
),
,
key: 'unlockAll',
label: (
<UnlockOutlined onClick=props.unlockAllWidgets/>
),
,
key: 'refreshAll',
label: (
<RedoOutlined/>
),
,
key: 'fullScreen',
label: (
<FullscreenOutlined onClick=props.enterFullScreen/>
),
];
return (<Draggable bounds=bound handle='.draggable-dashboard-button'
onStart=(event, uiData) => onStart(event, uiData)>
<div className='dashboard-menu-button'
id='draggable-dashboard-menu-button'>
<Dropdown menu=items placement="topRight" arrow trigger=['click'] overlayClassName='dashboard-menu-button-dropdown'>
<Button type="primary" icon=<AppstoreOutlined/> size='large' className='draggable-dashboard-button'/>
</Dropdown>
</div>
</Draggable>)
export default DashboardMenuButton;
具体的图表组件
import React from "react";
import WidgetLoadingSpin from "@/pages/Dashboard/Detail/WidgetLoadingSpin";
const ReactEchartsLazy = React.lazy(() => import('echarts-for-react'));
function BarChartWidget()
const getBarChart = () =>
return
tooltip使用react-sizeme解决react-grid-layout中侧栏(抽屉)展开或隐藏时不会自适应容器大小的问题
使用react-sizeme解决react-grid-layout中侧栏(抽屉)展开或隐藏时不会自适应容器大小的问题
使用react-grid-layout和echarts-for-react实现一个支持拖拽的自定义响应式dashboard页面
使用react-grid-layout和echarts-for-react实现一个支持拖拽的自定义响应式dashboard页面