import { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' import { Avatar, Button, List, NavBar, ProgressBar, Toast } from 'antd-mobile' import { useAuth } from '../auth/AuthContext.jsx' import { APP_VERSION, BUILD_ID } from '../version.js' import { applyOtaUpdate, checkOtaUpdate, getCurrentVersionInfo, getOtaVersionInfo, reloadApp, rollbackOta, } from '../api/ota.js' // "我" 页: 顶部 NavBar + 用户头像卡片 + 信息列表 + OTA 升级区 // OTA 区: // - 显示当前 APP_VERSION (前端包, 对应内置 dist) // - 显示当前 hotswap 状态 (是否已应用覆盖包) // - [检查更新]: 走 checkOtaUpdate() // - [立即升级]: 走 applyOtaUpdate(), 完成后 reload // - [回滚]: 清除覆盖包, 回到内置 dist export default function MePage() { const { user, logout } = useAuth() const navigate = useNavigate() // OTA 状态 const [otaState, setOtaState] = useState('idle') // idle | checking | available | none | downloading | ready | error const [otaInfo, setOtaInfo] = useState(null) // 检查结果中的 info const [otaVersion, setOtaVersion] = useState(null) // hotswap 当前激活的覆盖包版本 const [progress, setProgress] = useState({ received: 0, total: 0 }) const [error, setError] = useState('') // 进入页面时拉一次 hotswap 状态 (Tauri 内才需要, web 下 null 也无所谓) useEffect(() => { let cancelled = false getOtaVersionInfo().then((v) => { if (!cancelled) setOtaVersion(v) }) return () => { cancelled = true } }, []) async function onCheck() { setError('') setOtaState('checking') try { const r = await checkOtaUpdate() if (r.available) { setOtaInfo(r.info) setOtaState('available') Toast.show({ icon: 'success', content: '发现新版本 v' + (r.info?.version || '') }) } else { setOtaInfo(null) setOtaState('none') if (r.reason === 'same_version') { Toast.show({ content: '已是最新版本' }) } else if (r.reason === 'no_package') { Toast.show({ content: '当前没有可用更新' }) } else { Toast.show({ content: '当前没有可用更新' }) } } } catch (e) { setError(e?.message || '检查失败') setOtaState('error') Toast.show({ icon: 'fail', content: '检查失败: ' + (e?.message || '') }) } } async function onApply() { setError('') setOtaState('downloading') setProgress({ received: 0, total: 0 }) try { const newVer = await applyOtaUpdate((received, total) => { setProgress({ received, total }) }) Toast.show({ icon: 'success', content: '已下载 v' + newVer + ', 即将重启' }) setOtaState('ready') // 给用户 1s 看提示, 然后 reload setTimeout(() => reloadApp(), 1000) } catch (e) { setError(e?.message || '升级失败') setOtaState('error') Toast.show({ icon: 'fail', content: '升级失败: ' + (e?.message || '') }) } } async function onRollback() { if (!window.confirm('确认回滚到内置版本?')) return try { await rollbackOta() Toast.show({ icon: 'success', content: '已回滚, 正在重启' }) setTimeout(() => reloadApp(), 800) } catch (e) { Toast.show({ icon: 'fail', content: '回滚失败: ' + (e?.message || '') }) } } async function onLogout() { const confirmed = window.confirm('确定退出登录?') if (!confirmed) return await logout() Toast.show({ icon: 'success', content: '已退出' }) navigate('/login', { replace: true }) } const cur = getCurrentVersionInfo() const isTauri = typeof window !== 'undefined' && '__TAURI_INTERNALS__' in window const showProgress = otaState === 'downloading' && progress.total > 0 const progressPct = showProgress ? Math.min(100, Math.round((progress.received / progress.total) * 100)) : 0 return (