From 87daccdc4864c2999918d6327da2835e4bc5cfcb Mon Sep 17 00:00:00 2001 From: catlog22 Date: Sun, 8 Feb 2026 15:21:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=E5=8A=9F=E8=83=BD=E5=8F=8A=20Header=20?= =?UTF-8?q?=E5=B8=83=E5=B1=80=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SettingsPage 新增 VersionCheckSection(自动/手动检查更新) - 添加版本检查相关中英文 i18n 翻译 - 修复 Header 历史链接的 flex 布局对齐 --- ccw/frontend/src/components/layout/Header.tsx | 2 +- ccw/frontend/src/locales/en/settings.json | 16 ++ ccw/frontend/src/locales/zh/settings.json | 16 ++ ccw/frontend/src/pages/SettingsPage.tsx | 180 +++++++++++++++++- 4 files changed, 212 insertions(+), 2 deletions(-) diff --git a/ccw/frontend/src/components/layout/Header.tsx b/ccw/frontend/src/components/layout/Header.tsx index f8ce1d79..f5083e34 100644 --- a/ccw/frontend/src/components/layout/Header.tsx +++ b/ccw/frontend/src/components/layout/Header.tsx @@ -84,7 +84,7 @@ export function Header({ asChild className="gap-2" > - + {formatMessage({ id: 'navigation.main.history' })} diff --git a/ccw/frontend/src/locales/en/settings.json b/ccw/frontend/src/locales/en/settings.json index 021252fa..bd586299 100644 --- a/ccw/frontend/src/locales/en/settings.json +++ b/ccw/frontend/src/locales/en/settings.json @@ -114,6 +114,22 @@ "on": "On", "off": "Off" }, + "versionCheck": { + "title": "Version Update", + "currentVersion": "Current Version", + "latestVersion": "Latest Version", + "checkNow": "Check Now", + "checking": "Checking...", + "autoCheck": "Auto-check for updates", + "autoCheckDesc": "Automatically check for new versions every hour", + "upToDate": "Up to date", + "updateAvailable": "Update available", + "updateCommand": "Update command", + "viewRelease": "View Release", + "lastChecked": "Last checked", + "checkFailed": "Check failed", + "never": "Never" + }, "about": { "title": "About", "version": "Version", diff --git a/ccw/frontend/src/locales/zh/settings.json b/ccw/frontend/src/locales/zh/settings.json index 177c67a3..7fbfcaf6 100644 --- a/ccw/frontend/src/locales/zh/settings.json +++ b/ccw/frontend/src/locales/zh/settings.json @@ -114,6 +114,22 @@ "on": "开启", "off": "关闭" }, + "versionCheck": { + "title": "版本更新", + "currentVersion": "当前版本", + "latestVersion": "最新版本", + "checkNow": "立即检查", + "checking": "检查中...", + "autoCheck": "自动检查更新", + "autoCheckDesc": "每小时自动检查是否有新版本", + "upToDate": "已是最新版本", + "updateAvailable": "有新版本可用", + "updateCommand": "更新命令", + "viewRelease": "查看更新", + "lastChecked": "上次检查", + "checkFailed": "检查失败", + "never": "从未" + }, "about": { "title": "关于", "version": "版本", diff --git a/ccw/frontend/src/pages/SettingsPage.tsx b/ccw/frontend/src/pages/SettingsPage.tsx index dfee073e..f0410322 100644 --- a/ccw/frontend/src/pages/SettingsPage.tsx +++ b/ccw/frontend/src/pages/SettingsPage.tsx @@ -3,7 +3,7 @@ // ======================================== // Application settings and configuration with CLI tools management -import { useState, useCallback } from 'react'; +import { useState, useCallback, useEffect } from 'react'; import { useIntl } from 'react-intl'; import { Settings, @@ -727,6 +727,181 @@ function ResponseLanguageSection() { ); } +// ========== Version Check Section ========== + +interface VersionData { + currentVersion: string; + latestVersion: string; + hasUpdate: boolean; + packageName: string; + updateCommand: string; + checkedAt: string; +} + +function VersionCheckSection() { + const { formatMessage } = useIntl(); + const [versionData, setVersionData] = useState(null); + const [checking, setChecking] = useState(false); + const [error, setError] = useState(null); + const [lastChecked, setLastChecked] = useState(null); + const [autoCheck, setAutoCheck] = useState(() => { + try { + const saved = localStorage.getItem('ccw.autoUpdate'); + return saved === null ? true : JSON.parse(saved); + } catch { + return true; + } + }); + + const checkVersion = async (silent = false) => { + if (!silent) setChecking(true); + setError(null); + try { + const response = await fetch('/api/version-check'); + if (!response.ok) throw new Error(`HTTP ${response.status}`); + + const data: VersionData = await response.json(); + if (!data.currentVersion) throw new Error('Invalid response'); + + setVersionData(data); + setLastChecked(new Date()); + } catch (err) { + if (!silent) setError(err instanceof Error ? err.message : 'Unknown error'); + } finally { + setChecking(false); + } + }; + + useEffect(() => { + // Initial check + checkVersion(true); + + if (!autoCheck) return; + const interval = setInterval(() => checkVersion(true), 60 * 60 * 1000); + return () => clearInterval(interval); + }, [autoCheck]); + + const toggleAutoCheck = (enabled: boolean) => { + setAutoCheck(enabled); + localStorage.setItem('ccw.autoUpdate', JSON.stringify(enabled)); + }; + + return ( + +
+

+ + {formatMessage({ id: 'settings.versionCheck.title' })} +

+ +
+ +
+ {/* Version info */} +
+
+ + {formatMessage({ id: 'settings.versionCheck.currentVersion' })} + + + {versionData?.currentVersion ?? '...'} + +
+
+ + {formatMessage({ id: 'settings.versionCheck.latestVersion' })} + + + {versionData?.latestVersion ?? '...'} + +
+ + {/* Status */} + {versionData && ( +
+ + {versionData.hasUpdate + ? formatMessage({ id: 'settings.versionCheck.updateAvailable' }) + : formatMessage({ id: 'settings.versionCheck.upToDate' })} + + +
+ )} + + {error && ( +
+ + + {formatMessage({ id: 'settings.versionCheck.checkFailed' })}: {error} + +
+ )} +
+ + {/* Update action */} + {versionData?.hasUpdate && ( +
+
+

+ {formatMessage({ id: 'settings.versionCheck.updateCommand' })} +

+ + {versionData.updateCommand} + +
+ +
+ )} + + {/* Auto check toggle + last checked */} +
+ + + {formatMessage({ id: 'settings.versionCheck.lastChecked' })}:{' '} + {lastChecked ? lastChecked.toLocaleTimeString() : formatMessage({ id: 'settings.versionCheck.never' })} + +
+
+
+ ); +} + // ========== System Status Section ========== function SystemStatusSection() { @@ -1118,6 +1293,9 @@ export function SettingsPage() { {/* System Status */} + {/* Version Check */} + + {/* CLI Tools Configuration */}