import { useState, useEffect } from 'react' import { Card, Button, Spin, Progress, Alert, Space, Tag, Modal, message } from 'antd' import { CloudUploadOutlined, ReloadOutlined, CheckCircleOutlined, ExclamationCircleOutlined } from '@ant-design/icons' import { useTranslation } from 'react-i18next' import { apiClient } from '../services/api' import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' interface UpdateInfo { hasUpdate: boolean currentVersion: string latestVersion: string latestTag: string releaseNotes: string publishedAt: string prerelease: boolean } interface UpdateStatus { updating: boolean progress: number message: string error: string | null } const SystemUpdate: React.FC = () => { const { t, i18n } = useTranslation() const [currentVersion, setCurrentVersion] = useState('') const [updateChecking, setUpdateChecking] = useState(false) const [updateInfo, setUpdateInfo] = useState(null) const [updateStatus, setUpdateStatus] = useState({ updating: false, progress: 0, message: '', error: null }) useEffect(() => { fetchCurrentVersion() fetchUpdateStatus() }, []) const fetchCurrentVersion = async () => { try { const response = await apiClient.get('/update/version') if (response.data.code === 0 && response.data.data) { setCurrentVersion(response.data.data.version) } } catch (error: any) { console.error('获取版本失败:', error) } } const fetchUpdateStatus = async () => { try { const response = await apiClient.get('/update/status') if (response.data.code === 0 && response.data.data) { setUpdateStatus({ updating: response.data.data.updating, progress: response.data.data.progress || 0, message: response.data.data.message || '', error: response.data.data.error || null }) } } catch (error: any) { console.error('获取更新状态失败:', error) } } const handleCheckUpdate = async () => { setUpdateChecking(true) setUpdateInfo(null) try { const response = await apiClient.get('/update/check') const data = response.data if (data.code === 0 && data.data) { setUpdateInfo(data.data) if (data.data.hasUpdate) { message.success(t('systemUpdate.hasNewVersion', { version: data.data.latestVersion })) } else { message.info(t('systemUpdate.alreadyLatest')) } } else { message.error(data.message || t('systemUpdate.checkFailed')) } } catch (error: any) { message.error(error.message || t('systemUpdate.checkFailed')) } finally { setUpdateChecking(false) } } const handleExecuteUpdate = () => { Modal.confirm({ title: t('systemUpdate.confirmTitle'), icon: , content: (

{t('systemUpdate.confirmContent1', { version: updateInfo?.latestVersion })}

{t('systemUpdate.confirmContent2')}

{t('systemUpdate.confirmContent3')}

), okText: t('systemUpdate.okText'), okType: 'primary', cancelText: t('systemUpdate.cancelText'), onOk: async () => { try { const response = await apiClient.post('/update/update', {}) const data = response.data if (data.code === 0) { message.success(t('systemUpdate.updateStarted')) // 开始轮询更新状态 const pollInterval = setInterval(async () => { try { const statusResponse = await apiClient.get('/update/status') const statusData = statusResponse.data if (statusData.code === 0 && statusData.data) { setUpdateStatus({ updating: statusData.data.updating, progress: statusData.data.progress || 0, message: statusData.data.message || '', error: statusData.data.error || null }) // 更新完成 if (!statusData.data.updating) { clearInterval(pollInterval) if (statusData.data.error) { message.error(t('systemUpdate.updateFailedWithMessage', { message: statusData.data.error })) } else if (statusData.data.progress === 100) { message.success(t('systemUpdate.updateSuccessRefresh')) setTimeout(() => window.location.reload(), 3000) } } } } catch (error) { console.error('获取更新状态失败:', error) } }, 2000) // 每2秒轮询一次 // 5分钟后停止轮询 setTimeout(() => clearInterval(pollInterval), 5 * 60 * 1000) } else if (data.code === 403) { message.error(t('systemUpdate.needAdmin')) } else { message.error(data.message || t('systemUpdate.startFailed')) } } catch (error: any) { message.error(error.message || t('systemUpdate.startFailed')) } } }) } const formatDate = (dateString: string) => { return new Date(dateString).toLocaleString(i18n.language === 'zh-CN' ? 'zh-CN' : i18n.language === 'zh-TW' ? 'zh-TW' : 'en') } return ( {t('systemUpdate.title')} } style={{ marginBottom: '16px', borderRadius: '8px', boxShadow: '0 2px 8px rgba(0,0,0,0.06)' }} > {/* 当前版本信息 */}
{t('systemUpdate.currentVersion')}
v{currentVersion || 'unknown'}
{/* 更新状态 */} {updateStatus.updating && ( {t('systemUpdate.updating')} } description={
{updateStatus.message || t('systemUpdate.ready')}
`${percent}%`} />
} type="info" showIcon icon={} style={{ borderRadius: '8px', border: '1px solid #91d5ff' }} /> )} {updateStatus.error && ( {t('systemUpdate.updateFailedTitle')}} description={
{updateStatus.error}
} type="error" showIcon closable onClose={() => setUpdateStatus(prev => ({ ...prev, error: null }))} style={{ borderRadius: '8px' }} /> )} {/* 检查更新 */} {!updateStatus.updating && (
{updateInfo && !updateInfo.hasUpdate && ( {t('systemUpdate.alreadyLatest')} } type="success" showIcon icon={} style={{ marginTop: '12px', borderRadius: '8px', border: '1px solid #b7eb8f' }} /> )}
)} {/* 更新信息 */} {updateInfo && updateInfo.hasUpdate && !updateStatus.updating && (
{t('systemUpdate.newVersionFound')}
v{updateInfo.latestVersion} {updateInfo.prerelease && ( {t('systemUpdate.prerelease')} )}
{t('systemUpdate.publishedAt')}
{formatDate(updateInfo.publishedAt)}
{updateInfo.releaseNotes && (
{t('systemUpdate.releaseNotes')}

, h2: ({node, ...props}) =>

, h3: ({node, ...props}) =>

, h4: ({node, ...props}) =>

, p: ({node, ...props}) =>

, ul: ({node, ...props}) =>