forked from wangziqi/gongxue-base
feat(dashboard): add classroom utilization stats endpoint and UI
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Row, Col, Card, Statistic, DatePicker, Spin, Grid } from 'antd';
|
||||
import { TeamOutlined, HomeOutlined, CheckCircleOutlined, BankOutlined, PercentageOutlined, UserSwitchOutlined, SolutionOutlined, FileProtectOutlined } from '@ant-design/icons';
|
||||
import { TeamOutlined, HomeOutlined, CheckCircleOutlined, BankOutlined, PercentageOutlined, UserSwitchOutlined, SolutionOutlined, FileProtectOutlined, ReadOutlined, CalendarOutlined } from '@ant-design/icons';
|
||||
import ReactECharts from 'echarts-for-react';
|
||||
import dayjs from 'dayjs';
|
||||
import api from '../../api';
|
||||
@@ -68,7 +68,14 @@ const DashboardPage: React.FC = () => {
|
||||
const [classRanking, setClassRanking] = useState<{ top: ClassAttendanceRank[]; bottom: ClassAttendanceRank[] }>({ top: [], bottom: [] });
|
||||
const [classroomOccupancy, setClassroomOccupancy] = useState<ClassroomOccupancy[]>([]);
|
||||
const [ganttData, setGanttData] = useState<GanttRoom[]>([]);
|
||||
const [roomRanking] = useState<Array<{ roomNumber: string; total: string }>>([]);
|
||||
const [roomRanking, setRoomRanking] = useState<Array<{ roomNumber: string; total: string }>>([]);
|
||||
const [classroomUtil, setClassroomUtil] = useState<{
|
||||
totalClassrooms: number;
|
||||
inUseCount: number;
|
||||
utilizationRate: string;
|
||||
scheduleCount: number;
|
||||
rentalCount: number;
|
||||
} | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [period, setPeriod] = useState<[string, string]>([
|
||||
dayjs().startOf('month').format('YYYY-MM-DD'),
|
||||
@@ -78,9 +85,9 @@ const DashboardPage: React.FC = () => {
|
||||
const fetchData = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const [s, , cr, g] = await Promise.all([
|
||||
const [s, rr, cr, g] = await Promise.all([
|
||||
api.get<DashboardStats>('/dashboard/stats'),
|
||||
api.get('/dashboard/room-ranking', {
|
||||
api.get<Array<{ roomNumber: string; total: string }>>('/dashboard/room-ranking', {
|
||||
params: { periodStart: period[0], periodEnd: period[1] },
|
||||
}),
|
||||
api.get<{ top: ClassAttendanceRank[]; bottom: ClassAttendanceRank[] }>('/dashboard/class-attendance-ranking'),
|
||||
@@ -89,10 +96,13 @@ const DashboardPage: React.FC = () => {
|
||||
}),
|
||||
]);
|
||||
setStats(s);
|
||||
setRoomRanking(rr);
|
||||
setClassRanking(cr);
|
||||
setGanttData(g);
|
||||
const co = await api.get<ClassroomOccupancy[]>('/dashboard/classroom-occupancy');
|
||||
setClassroomOccupancy(co);
|
||||
const cu = await api.get('/dashboard/classroom-utilization');
|
||||
setClassroomUtil(cu);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
@@ -434,6 +444,29 @@ const DashboardPage: React.FC = () => {
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Card title="教室利用率" style={{ marginBottom: 16 }}>
|
||||
<Row gutter={[24, 16]}>
|
||||
<Col xs={12} sm={6}>
|
||||
<Statistic title="教室总数" value={classroomUtil?.totalClassrooms ?? '-'} prefix={<ReadOutlined />} />
|
||||
</Col>
|
||||
<Col xs={12} sm={6}>
|
||||
<Statistic title="今日使用" value={classroomUtil?.inUseCount ?? '-'} prefix={<CheckCircleOutlined />} />
|
||||
</Col>
|
||||
<Col xs={12} sm={6}>
|
||||
<Statistic
|
||||
title="利用率"
|
||||
value={classroomUtil?.utilizationRate ?? '-'}
|
||||
suffix="%"
|
||||
prefix={<PercentageOutlined />}
|
||||
valueStyle={{ color: Number(classroomUtil?.utilizationRate ?? 0) > 70 ? '#34C759' : '#FF9500' }}
|
||||
/>
|
||||
</Col>
|
||||
<Col xs={12} sm={6}>
|
||||
<Statistic title="内部排课" value={classroomUtil?.scheduleCount ?? '-'} prefix={<CalendarOutlined />} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
|
||||
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
|
||||
<Col xs={24} sm={12}>
|
||||
<Card title="考勤趋势(近30天)">
|
||||
|
||||
Reference in New Issue
Block a user