import React, { useState, useEffect, useCallback, useRef } from 'react'; import { View, Text, ScrollView } from '@tarojs/components'; import Taro, { useRouter, usePullDownRefresh } from '@tarojs/taro'; import styles from './index.module.scss'; import { getRoomById } from '@/api/rooms'; import { getDevices } from '@/api/devices'; import { getLatestTelemetry, fetchTrend } from '@/api/telemetry'; import { getTrays, getBatches, batchStatusLabel, type Tray, type Batch } from '@/api/trayBatch'; import MiniChart from '@/components/MiniChart'; import { roomStatusLabel, onlineStatusLabel, deviceKindLabel, metricLabel, metricUnit, formatNumber, formatRelativeTime, } from '@/utils/format'; import type { Room, Device, TelemetryRecord, TrendPoint } from '@/types'; const RoomDetailPage: React.FC = () => { const router = useRouter(); const roomId = router.params.id || ''; const [room, setRoom] = useState(null); const [devices, setDevices] = useState([]); const [telemetryMap, setTelemetryMap] = useState>({}); const [trend, setTrend] = useState([]); const [trays, setTrays] = useState([]); const [batches, setBatches] = useState([]); const [trendLoading, setTrendLoading] = useState(false); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const intervalRef = useRef | null>(null); const fetchData = useCallback(async () => { if (!roomId) { setError('缺少蚕房ID'); setLoading(false); return; } try { setError(''); const [roomRes, devicesRes, traysRes, batchesRes] = await Promise.all([ getRoomById(roomId).catch((e) => { console.error('[RoomDetail] 获取蚕房失败:', e); return null; }), getDevices().catch(() => [] as Device[]), getTrays(roomId).catch(() => [] as Tray[]), getBatches(roomId).catch(() => [] as Batch[]), ]); if (roomRes) { setRoom(roomRes); } const roomDevices = devicesRes.filter((d) => d.roomId === roomId); setDevices(roomDevices); setTrays(traysRes); setBatches(batchesRes); // 获取每个设备的最新遥测 const telemetryEntries = await Promise.all( roomDevices.map(async (device) => { try { const telemetry = await getLatestTelemetry(device.deviceKey).catch( () => [] as TelemetryRecord[] ); return [device.deviceKey, telemetry] as [string, TelemetryRecord[]]; } catch { return [device.deviceKey, []] as [string, TelemetryRecord[]]; } }) ); const map: Record = {}; telemetryEntries.forEach(([key, val]) => { map[key] = val; }); setTelemetryMap(map); // 加载24小时趋势数据 setTrendLoading(true); const trendData = await fetchTrend(24).catch(() => [] as TrendPoint[]); setTrend(trendData); } catch (err) { console.error('[RoomDetail] 数据加载失败:', err); setError(err instanceof Error ? err.message : '数据加载失败'); } finally { setLoading(false); setTrendLoading(false); } }, [roomId]); useEffect(() => { fetchData(); intervalRef.current = setInterval(() => { fetchData(); }, 30000); return () => { if (intervalRef.current) clearInterval(intervalRef.current); }; }, [fetchData]); usePullDownRefresh(() => { fetchData().then(() => { Taro.stopPullDownRefresh(); }); }); if (loading) { return ( 加载中... ); } if (error && !room) { return ( ⚠️ {error} ); } if (!room) { return ( 🏠 未找到蚕房信息 ); } return ( {/* 蚕房信息卡片 */} {room.name} {roomStatusLabel(room.status)} {room.code && ( 编码: {room.code} )} {room.location && ( 📍 {room.location} )} 📡 设备: {devices.length} {room.description && ( {room.description} )} {/* 最新遥测数据 */} {devices.length > 0 && ( 实时环境数据 {devices.map((device) => { const telemetry = telemetryMap[device.deviceKey] || []; if (telemetry.length === 0) return null; return ( {device.name} {telemetry.map((item) => ( {metricLabel(item.metric)} {item.metric === 'temperature' ? '🌡' : item.metric === 'humidity' ? '💧' : '📈'} {formatNumber(item.value)} {metricUnit(item.metric)} {formatRelativeTime(item.timestamp)} ))} ); })} )} {/* 24小时趋势图 */} 24小时趋势 {trendLoading && trend.length === 0 ? ( 趋势数据加载中... ) : trend.length > 0 ? ( <> 温度 (°C) 湿度 (%) 温度 湿度 ) : ( 暂无趋势数据 )} {/* 设备列表 */} 蚕房设备 {devices.length > 0 ? ( {devices.map((device) => ( {device.kind === 'camera' ? '📹' : device.kind === 'sensor' ? '🌡' : '🔌'} {device.name} {deviceKindLabel(device.kind)} · {device.deviceKey} {onlineStatusLabel(device.onlineStatus)} ))} ) : ( 📭 该蚕房暂无设备 )} {/* 蚕匾 */} 蚕匾 {trays.length > 0 ? ( {trays.map((t) => ( 🪵 {t.name} {[t.code, t.position].filter(Boolean).join(' · ') || '无位置信息'} ))} ) : ( 📭 该蚕房暂无蚕匾 )} {/* 当前批次 */} 当前批次 {batches.length > 0 ? ( {batches.map((b) => ( 🐛 {b.name} {batchStatusLabel(b.status)} · {b.instar ? `${b.instar}龄` : '龄期未设置'} {b.variety ? ` · ${b.variety}` : ''} ))} ) : ( 📭 该蚕房暂无批次 )} ); }; export default RoomDetailPage;