feat: 重构各业务模块管理页面与服务
This commit is contained in:
85
apps/admin/src/pages/Dashboard/DashboardLazyCards.tsx
Normal file
85
apps/admin/src/pages/Dashboard/DashboardLazyCards.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user