import React, { useState, useEffect, useCallback, useRef } from 'react'; import { View, Text, ScrollView, Switch, Input } from '@tarojs/components'; import Taro, { useRouter } from '@tarojs/taro'; import styles from './index.module.scss'; import { sendControl } from '@/api/devices'; import { getTelemetry, normalizeRealtime } from '@/api/telemetry'; import { formatNumber } from '@/utils/format'; import type { RealtimeMetric } from '@/types'; const MODES = [ { value: 'cool', label: '制冷' }, { value: 'heat', label: '制热' }, { value: 'fan', label: '通风' }, { value: 'auto', label: '自动' }, ]; const SPEEDS = [ { value: '0', label: '自动' }, { value: '1', label: '低' }, { value: '2', label: '中' }, { value: '3', label: '高' }, ]; const ControlPage: React.FC = () => { const router = useRouter(); const deviceKey = router.params.deviceKey || ''; const deviceName = router.params.deviceName || ''; const [loading, setLoading] = useState(true); const [metrics, setMetrics] = useState([]); const [isOn, setIsOn] = useState(false); const [sending, setSending] = useState(false); const [mode, setMode] = useState('cool'); const [speed, setSpeed] = useState('1'); const [tempValue, setTempValue] = useState('26'); const timerRef = useRef | null>(null); const loadData = useCallback(async () => { try { const telemetryData = await getTelemetry({ deviceKey, limit: 50 }).catch( () => [] as never[] ); setMetrics(normalizeRealtime(telemetryData)); } catch { // ignore } finally { setLoading(false); } }, [deviceKey]); useEffect(() => { loadData(); timerRef.current = setInterval(loadData, 30000); return () => { if (timerRef.current) { clearInterval(timerRef.current); timerRef.current = null; } }; }, [loadData]); const handleSend = async (action: string, value?: string | number) => { setSending(true); Taro.showLoading({ title: '发送指令...' }); try { await sendControl({ deviceKey, action, value }); if (action === 'power') { setIsOn(value === 'on'); } Taro.showToast({ title: `命令已发送: ${action}`, icon: 'success' }); } catch (err) { Taro.showToast({ title: err instanceof Error ? `发送失败: ${err.message}` : '发送失败', icon: 'none', }); } finally { setSending(false); Taro.hideLoading(); } }; const handleToggle = (event: { detail: { value: boolean } }) => { handleSend('power', event.detail.value ? 'on' : 'off'); }; const getMetricColor = (status: string) => { if (status === 'danger') return '#f53f3f'; if (status === 'warn') return '#ff7d00'; return '#10b981'; }; if (loading) { return ( 加载中... ); } return ( {/* 头部卡片 */} {deviceName} Key: {deviceKey} {isOn ? '开启' : '关闭'} {/* 控制面板 */} 控制面板 运行模式 {MODES.map((m) => ( !sending && setMode(m.value)} > {m.label} ))} 风速 {SPEEDS.map((s) => ( !sending && setSpeed(s.value)} > {s.label} ))} 目标温度 setTempValue(e.detail.value)} placeholder="26" /> !sending && handleSend('set_temp', parseInt(tempValue, 10) || 26)} > 设置 !sending && handleSend('fan_on')} > 开启风扇 !sending && handleSend('fan_off')} > 关闭风扇 !sending && handleSend('dehumidifier_on')} > 开启除湿 !sending && handleSend('dehumidifier_off')} > 关闭除湿 {/* 实时数据 */} 实时数据 {metrics.length === 0 ? ( 📊 暂无遥测数据 ) : ( {metrics.map((m) => { const color = getMetricColor(m.status); return ( {m.name} {formatNumber(m.value, 1)} {m.unit} ); })} )} ); }; export default ControlPage;