import React, { useEffect, useState, useCallback } from 'react'; import { Row, Col, Card, Tag, Select, Statistic, Modal, Spin, Badge, Tooltip, DatePicker, Alert, Button } from 'antd'; import { HomeOutlined, UserOutlined, CalendarOutlined, BankOutlined, HistoryOutlined, ShopOutlined } from '@ant-design/icons'; import dayjs, { Dayjs } from 'dayjs'; import api from '../../api'; function getCardStyle(room: any): React.CSSProperties { let base: React.CSSProperties; if (room.status === 'maintenance') base = { background: '#f5f5f5', borderColor: '#d9d9d9' }; else if (room.currentCount === 0) base = { background: '#f6ffed', borderColor: '#b7eb8f' }; else if (room.currentCount >= room.capacity) base = { background: '#fff2f0', borderColor: '#ffccc7' }; else base = { background: '#e6f4ff', borderColor: '#91caff' }; if (room.tenantColor) { return { ...base, background: `color-mix(in srgb, ${room.tenantColor} 15%, ${base.background || '#fff'} 85%)` }; } return base; } function getStatusLabel(room: any) { if (room.status === 'maintenance') return 维修中; if (room.currentCount === 0) return 空闲; if (room.currentCount >= room.capacity) return 满员; return 部分入住; } function getTenantTags(occupants: any[]) { const tenantList = [ ...new Map( occupants .filter((o: any) => o.tenantName) .map((o: any) => [o.tenantId, { name: o.tenantName, color: o.tenantColor }]), ).values(), ] as { name: string; color: string | null }[]; if (tenantList.length === 0) return null; return (
{tenantList.map((t) => ( } > {t.name} ))}
); } const RoomVisualPage: React.FC = () => { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [selectedBuilding, setSelectedBuilding] = useState('all'); const [selectedTenant, setSelectedTenant] = useState('all'); const [detailRoom, setDetailRoom] = useState(null); const [asOf, setAsOf] = useState(null); const isHistorical = !!asOf && !asOf.isSame(dayjs(), 'day'); const fetchData = useCallback(async () => { setLoading(true); try { const params = isHistorical ? { asOf: asOf!.format('YYYY-MM-DD') } : undefined; const res: any = await api.get('/rooms/visual', { params }); setData(res); } catch (e) { console.error(e); } setLoading(false); }, [isHistorical, asOf]); useEffect(() => { fetchData(); }, [fetchData]); if (!data) return ; const rooms = data.rooms.filter((r: any) => { if (selectedBuilding !== 'all' && r.building !== selectedBuilding) return false; if (selectedTenant !== 'all' && !(r.tenantIds || []).includes(selectedTenant)) return false; return true; }); const totalRooms = rooms.length; const emptyRooms = rooms.filter( (r: any) => r.currentCount === 0 && r.status !== 'maintenance', ).length; const availableBeds = rooms.reduce( (sum: number, r: any) => r.status !== 'maintenance' ? sum + (r.capacity - r.currentCount) : sum, 0, ); const fullRooms = rooms.filter((r: any) => r.currentCount >= r.capacity).length; /* getCardStyle, getStatusLabel, getTenantTags are now standalone functions outside the component */ return (

宿舍总览

} disabledDate={(d) => d && d.isAfter(dayjs(), 'day')} style={{ width: 180 }} /> ({ value: t.id, label: ( {t.color && ( )} {t.name} ), })), ]} />
{isHistorical && ( } style={{ marginBottom: 16 }} title={`正在查看 ${asOf!.format('YYYY年M月D日')} 的历史入住情况(含当日已归档房间),非实时数据`} action={ } /> )} {loading && } {/* 统计栏 */} } /> {/* 房态网格 */} {rooms.map((room: any) => ( setDetailRoom(room)} >
{room.tenantColor && ( )} {room.roomNumber} {getStatusLabel(room)}
{room.building && {room.building} } {room.floor && {room.floor}F}
= room.capacity ? '#FF3B30' : room.currentCount > 0 ? '#007AFF' : '#34C759', fontSize: 12, }} />
{room.orgLabel && (
}> {room.orgLabel}
)} {getTenantTags(room.occupants)} {room.occupants.length > 0 && (
{room.occupants.slice(0, 4).map((o: any) => ( }> {o.studentName} ))} {room.occupants.length > 4 && +{room.occupants.length - 4}}
)}
))}
{/* 详情弹窗 */} setDetailRoom(null)} footer={null} width={500} > {detailRoom && (
位置:{detailRoom.building || '-'} {detailRoom.floor ? `${detailRoom.floor}F` : ''}
{detailRoom.tenantColor && (
{detailRoom.occupants[0]?.tenantName || '租户'}
)}
{getStatusLabel(detailRoom)}
{detailRoom.occupants.length > 0 ? (

当前住户

{detailRoom.occupants.map((o: any) => (
{o.studentName} {o.organization && ( {o.organization} )}
{o.days} 天
入住:{o.checkInDate} | 计费起:{o.billingStartDate} {o.supervisor && ( 负责人:{o.supervisor} )}
))}
) : (
当前无住户,可安排入住
)}
)}
); }; export default RoomVisualPage;