import React, { useEffect, useState } from 'react'; import { Row, Col, Card, Statistic, DatePicker, Spin, Grid } from 'antd'; import { TeamOutlined, HomeOutlined, DollarOutlined, CheckCircleOutlined } from '@ant-design/icons'; import ReactECharts from 'echarts-for-react'; import dayjs from 'dayjs'; import api from '../../api'; const { RangePicker } = DatePicker; const COLORS = [ '#007AFF', '#34C759', '#FF9500', '#FF3B30', '#5AC8FA', '#AF52DE', '#FF2D55', '#FFCC00', ]; const DashboardPage: React.FC = () => { const screens = Grid.useBreakpoint(); const isMobile = !screens.sm; const [stats, setStats] = useState(null); const [ganttData, setGanttData] = useState([]); const [expenseStats, setExpenseStats] = useState([]); const [roomRanking, setRoomRanking] = useState([]); const [loading, setLoading] = useState(true); const [period, setPeriod] = useState<[string, string]>([ dayjs().startOf('month').format('YYYY-MM-DD'), dayjs().endOf('month').format('YYYY-MM-DD'), ]); const fetchData = async () => { setLoading(true); try { const [s, g, e, r] = await Promise.all([ api.get('/dashboard/stats'), api.get('/dashboard/gantt', { params: { periodStart: period[0], periodEnd: period[1] } }), api.get('/dashboard/expense-stats', { params: { periodStart: period[0], periodEnd: period[1] }, }), api.get('/dashboard/room-ranking', { params: { periodStart: period[0], periodEnd: period[1] }, }), ]); setStats(s); setGanttData(g as any); setExpenseStats(e as any); setRoomRanking(r as any); } catch (e) { console.error(e); } setLoading(false); }; useEffect(() => { fetchData(); }, [period]); const expenseTypeMap: Record = { water: '水费', electricity: '电费', cleaning: '保洁费', damage: '损坏赔偿', penalty: '罚款', key: '钥匙费', remote: '空调遥控器', deposit_deduction: '押金扣除', other: '其他', }; // 甘特图配置 const ganttOption = () => { if (!ganttData.length) return {}; const rooms = ganttData.map((d) => d.roomNumber); const pStart = new Date(period[0]).getTime(); const pEnd = new Date(period[1]).getTime(); const data: any[] = []; ganttData.forEach((room, roomIdx) => { room.occupancies.forEach((occ: any, i: number) => { const start = Math.max(new Date(occ.checkInDate).getTime(), pStart); const end = occ.checkOutDate ? Math.min(new Date(occ.checkOutDate).getTime(), pEnd) : pEnd; data.push({ name: occ.studentName, value: [roomIdx, start, end, occ.studentName], itemStyle: { color: COLORS[(occ.studentId || i) % COLORS.length] }, }); }); }); return { tooltip: { formatter: (p: any) => { const v = p.value; return `${p.name}
宿舍: ${rooms[v[0]]}
入住: ${dayjs(v[1]).format('MM-DD')} ~ ${dayjs(v[2]).format('MM-DD')}`; }, }, grid: { left: 80, right: 30, top: 20, bottom: 30 }, xAxis: { type: 'time', min: pStart, max: pEnd }, yAxis: { type: 'category', data: rooms, inverse: true }, series: [ { type: 'custom', renderItem: (_params: any, api: any) => { const catIdx = api.value(0); const start = api.coord([api.value(1), catIdx]); const end = api.coord([api.value(2), catIdx]); const height = api.size([0, 1])[1] * 0.6; return { type: 'rect', shape: { x: start[0], y: start[1] - height / 2, width: end[0] - start[0], height }, style: { ...api.style(), fill: api.visual('color'), stroke: '#fff', lineWidth: 1 }, }; }, encode: { x: [1, 2], y: 0 }, data, }, ], }; }; // 费用饼图 const pieOption = { tooltip: { trigger: 'item' }, legend: { bottom: 0 }, series: [ { type: 'pie', radius: ['40%', '70%'], data: expenseStats.map((e) => ({ name: expenseTypeMap[e.type] || e.type, value: Number(e.total), })), }, ], }; // 宿舍费用排行 const barOption = { tooltip: {}, grid: { left: 80, right: 20, bottom: 30, top: 10 }, xAxis: { type: 'value' }, yAxis: { type: 'category', data: roomRanking.map((r) => r.roomNumber).reverse(), inverse: false, }, series: [ { type: 'bar', data: roomRanking.map((r) => Number(r.total)).reverse(), itemStyle: { color: '#007AFF', borderRadius: [0, 4, 4, 0] }, }, ], }; if (loading && !stats) return ; return (

数据面板

{ if (dates) setPeriod([dates[0]!.format('YYYY-MM-DD'), dates[1]!.format('YYYY-MM-DD')]); }} />
} /> } /> } /> } /> {ganttData.length > 0 ? ( ) : (
暂无入住数据
)}
{expenseStats.length > 0 ? ( ) : (
暂无费用数据
)}
{roomRanking.length > 0 ? ( ) : (
暂无费用数据
)}
); }; export default DashboardPage;