Files
gongxue-base/apps/admin/src/pages/Dashboard/DashboardLazyCards.tsx

86 lines
2.4 KiB
TypeScript

import React, { type CSSProperties } from 'react';
import { Card, Col, Row } from 'antd';
import { useIntersectionObserver } from 'usehooks-ts';
import ReactECharts from '../../components/ECharts';
import type { ClassroomOccupancy, GanttRoom } from './Dashboard.types';
import { buildClassroomHeatmapOption, buildGanttOption } from './DashboardCharts';
const useInViewport = (rootMargin = '200px') => {
const { ref, isIntersecting } = useIntersectionObserver({
rootMargin,
freezeOnceVisible: true,
});
return { ref, inView: isIntersecting };
};
const LazySection: React.FC<{
title: string;
vp: { ref: (node?: Element | null) => void; inView: boolean };
minHeight: number;
style?: CSSProperties;
children: React.ReactNode;
}> = ({ title, vp, minHeight, style, children }) => {
return (
<div ref={vp.ref} style={style}>
{vp.inView ? (
<Row gutter={[16, 16]}>
<Col xs={24}>
<Card title={title}>{children}</Card>
</Col>
</Row>
) : (
<Card title={title} style={{ minHeight }}>
<div style={{ textAlign: 'center', padding: 40, color: '#999' }}></div>
</Card>
)}
</div>
);
};
export const ClassroomHeatmapCard: React.FC<{
data: ClassroomOccupancy[];
isMobile: boolean;
}> = ({ data, isMobile }) => {
const vp = useInViewport('200px');
return (
<LazySection
title="教室占用热力图"
vp={vp}
minHeight={isMobile ? 340 : 440}
style={{ marginBottom: 24 }}
>
{data.length > 0 ? (
<ReactECharts
option={buildClassroomHeatmapOption(data)}
style={{ width: '100%', height: isMobile ? 300 : 400 }}
/>
) : (
<div style={{ textAlign: 'center', padding: 40, color: '#999' }}></div>
)}
</LazySection>
);
};
export const GanttCard: React.FC<{ data: GanttRoom[]; isMobile: boolean }> = ({
data,
isMobile,
}) => {
const vp = useInViewport('200px');
return (
<LazySection
title="入住时间线(甘特图)"
vp={vp}
minHeight={isMobile ? 340 : 490}
>
{data.length > 0 ? (
<ReactECharts
option={buildGanttOption(data)}
style={{ width: '100%', height: isMobile ? 300 : 450 }}
/>
) : (
<div style={{ textAlign: 'center', padding: 40, color: '#999' }}></div>
)}
</LazySection>
);
};