handleCdkChange(e.target.value)}
@@ -607,6 +614,7 @@ export function UpdateSection() {
startDownload();
}}
progressBgClass="bg-bg-secondary"
+ onSlowDownloadHintClick={handleScrollToCdkInput}
/>
)}
diff --git a/src/i18n/locales/en-US.ts b/src/i18n/locales/en-US.ts
index c652b261..188dbb37 100644
--- a/src/i18n/locales/en-US.ts
+++ b/src/i18n/locales/en-US.ts
@@ -690,6 +690,7 @@ export default {
' is an independent third-party accelerated download service that requires a paid subscription, not a fee charged by "{{projectName}}". Its operating costs are covered by subscription revenue, with a portion supporting project developers. Subscribe for high-speed downloads while supporting ongoing development. Without a CDK, downloads will fall back to GitHub. If that fails, please configure a network proxy.',
getCdk: 'No CDKey? Subscribe Now',
cdkHint: 'Please check if your CDK is correct or has expired',
+ slowDownloadHint: 'Other channels',
checkUpdate: 'Check for Updates',
checking: 'Checking...',
upToDate: 'You are up to date ({{version}})',
diff --git a/src/i18n/locales/ja-JP.ts b/src/i18n/locales/ja-JP.ts
index 71541523..f6c0d3f8 100644
--- a/src/i18n/locales/ja-JP.ts
+++ b/src/i18n/locales/ja-JP.ts
@@ -689,6 +689,7 @@ export default {
' は独立したサードパーティの高速ダウンロードサービスで、有料サブスクリプションが必要です。これは「{{projectName}}」の料金ではありません。運営費はサブスクリプション収入で賄われ、一部は開発者に還元されます。CDK を購読して高速ダウンロードをお楽しみください。CDK を入力しない場合、GitHub からダウンロードします。失敗した場合は、ネットワークプロキシを設定してください。',
getCdk: 'CDKをお持ちでない方はこちら',
cdkHint: 'CDK が正しいか、または有効期限が切れていないか確認してください',
+ slowDownloadHint: '他の入手先',
checkUpdate: '更新を確認',
checking: '確認中...',
upToDate: '最新バージョンです ({{version}})',
diff --git a/src/i18n/locales/ko-KR.ts b/src/i18n/locales/ko-KR.ts
index f1d3c891..c36f6381 100644
--- a/src/i18n/locales/ko-KR.ts
+++ b/src/i18n/locales/ko-KR.ts
@@ -678,6 +678,7 @@ export default {
'는 독립적인 서드파티 고속 다운로드 서비스이며 유료 구독이 필요합니다. 이것은 "{{projectName}}"의 요금이 아닙니다. 운영비는 구독 수익으로 충당되며 일부는 개발자에게 환원됩니다. CDK를 구독하여 고속 다운로드를 즐기세요. CDK가 없으면 GitHub에서 다운로드됩니다. 실패하면 네트워크 프록시를 설정하세요.',
getCdk: 'CDK가 없으신가요? 지금 구독하세요',
cdkHint: 'CDK가 올바른지 또는 만료되지 않았는지 확인하세요',
+ slowDownloadHint: '다른 채널',
checkUpdate: '업데이트 확인',
checking: '확인 중...',
upToDate: '최신 버전입니다 ({{version}})',
diff --git a/src/i18n/locales/zh-CN.ts b/src/i18n/locales/zh-CN.ts
index 18bd1d33..032dfce7 100644
--- a/src/i18n/locales/zh-CN.ts
+++ b/src/i18n/locales/zh-CN.ts
@@ -666,6 +666,7 @@ export default {
' 是独立的第三方加速下载服务,需要付费使用,并非「{{projectName}}」收费。其运营成本由订阅收入支撑,部分收益将回馈项目开发者。欢迎订阅 CDK 享受高速下载,同时支持项目持续开发。未填写 CDK 时将自动通过 GitHub 下载,若失败请尝试配置网络代理。',
getCdk: '没有CDK?立即订阅',
cdkHint: '请检查您的 CDK 是否正确或已过期',
+ slowDownloadHint: '其他渠道',
checkUpdate: '检查更新',
checking: '正在检查...',
upToDate: '当前已是最新版本 ({{version}})',
diff --git a/src/i18n/locales/zh-TW.ts b/src/i18n/locales/zh-TW.ts
index dd33d810..028a461b 100644
--- a/src/i18n/locales/zh-TW.ts
+++ b/src/i18n/locales/zh-TW.ts
@@ -662,6 +662,7 @@ export default {
' 是獨立的第三方加速下載服務,需要付費使用,並非「{{projectName}}」收費。其營運成本由訂閱收入支撐,部分收益將回饋專案開發者。歡迎訂閱 CDK 享受高速下載,同時支援專案持續開發。未填寫 CDK 時將自動透過 GitHub 下載,若失敗請嘗試設定網路代理。',
getCdk: '沒有CDK?立即訂閱',
cdkHint: '請檢查您的 CDK 是否正確或已過期',
+ slowDownloadHint: '其他管道',
checkUpdate: '檢查更新',
checking: '正在檢查...',
upToDate: '目前已是最新版本 ({{version}})',
diff --git a/src/services/updateService.ts b/src/services/updateService.ts
index 473da55a..b98f5552 100644
--- a/src/services/updateService.ts
+++ b/src/services/updateService.ts
@@ -733,9 +733,14 @@ interface DownloadUpdateOptions {
// 当前下载的保存路径,用于取消时清理临时文件
let currentDownloadPath: string | null = null;
-// 进度事件数据(包含 session_id 用于区分不同下载任务)
-interface DownloadProgressEventPayload extends DownloadProgress {
+// 进度事件数据。Rust 侧 DownloadProgressEvent 走 serde 默认命名,
+// 字段是 snake_case,不能直接套用前端 camelCase 的 DownloadProgress
+interface DownloadProgressEventPayload {
session_id: number;
+ downloaded_size: number;
+ total_size: number;
+ speed: number;
+ progress: number;
}
/**
@@ -809,8 +814,8 @@ export async function downloadUpdate(
// 如果已被取消,忽略进度更新
if (downloadCancelled) return;
onProgress({
- downloadedSize: event.payload.downloadedSize,
- totalSize: event.payload.totalSize,
+ downloadedSize: event.payload.downloaded_size,
+ totalSize: event.payload.total_size,
speed: event.payload.speed,
progress: event.payload.progress,
});
diff --git a/src/stores/appStore.ts b/src/stores/appStore.ts
index ad88b722..f9c05ddb 100644
--- a/src/stores/appStore.ts
+++ b/src/stores/appStore.ts
@@ -79,6 +79,12 @@ import { cacheTaskEnabledForController } from '@/utils/taskControllerCache';
// 从独立模块导入类型和辅助函数
import type { AppState, LogEntry, TaskRunStatus } from './types';
+/** 低于该速度(字节/秒)视为慢速下载 */
+const SLOW_DOWNLOAD_SPEED_BPS = 1024 * 1024;
+
+/** 慢速需要持续这么久才认定为「确实慢」,避免下载刚开始时误报 */
+export const SLOW_DOWNLOAD_DURATION_MS = 5000;
+
/**
* 规范化定时策略:仅保留 times(分钟精度)字段,丢弃旧版整点 hours 字段。
* 不做新旧数据迁移——旧配置中基于 hours 的时间点不会被转换为 times,加载后时间点为空,需用户重新配置。
@@ -351,6 +357,9 @@ export const useAppStore = create
()(
currentPage: 'main',
setCurrentPage: (page) => set({ currentPage: page }),
+ settingsTargetSection: null,
+ setSettingsTargetSection: (section) => set({ settingsTargetSection: section }),
+
// 调试选项(不落盘,每次启动默认关闭)
saveDraw: false,
setSaveDraw: async (enabled) => {
@@ -2036,14 +2045,26 @@ export const useAppStore = create()(
downloadStatus: 'idle',
downloadProgress: null,
downloadSavePath: null,
- setDownloadStatus: (status) => set({ downloadStatus: status }),
- setDownloadProgress: (progress) => set({ downloadProgress: progress }),
+ slowDownloadSince: null,
+ // 每次状态变化都重置慢速计时:'downloading' 表示新一轮下载开始,其余状态表示下载已结束
+ setDownloadStatus: (status) => set({ downloadStatus: status, slowDownloadSince: null }),
+ // 要求 downloadedSize > 0 才起算:下载开始前先写入的那条 speed 为 0 的占位进度,
+ // 以及 Rust 建连/重定向期间(进度事件还没开始推送)都不应计入慢速时长
+ setDownloadProgress: (progress) =>
+ set((state) => ({
+ downloadProgress: progress,
+ slowDownloadSince:
+ progress && progress.downloadedSize > 0 && progress.speed < SLOW_DOWNLOAD_SPEED_BPS
+ ? (state.slowDownloadSince ?? Date.now())
+ : null,
+ })),
setDownloadSavePath: (path) => set({ downloadSavePath: path }),
resetDownloadState: () =>
set({
downloadStatus: 'idle',
downloadProgress: null,
downloadSavePath: null,
+ slowDownloadSince: null,
}),
// 安装状态
diff --git a/src/stores/types.ts b/src/stores/types.ts
index 2805883f..42c3fda0 100644
--- a/src/stores/types.ts
+++ b/src/stores/types.ts
@@ -136,6 +136,10 @@ export interface AppState {
currentPage: PageView;
setCurrentPage: (page: PageView) => void;
+ /** 进入设置页后需要自动滚动到的分区 id(如 'update'),由 SettingsPage 消费后清空 */
+ settingsTargetSection: string | null;
+ setSettingsTargetSection: (section: string | null) => void;
+
// 调试选项(不落盘,每次启动默认关闭)
saveDraw: boolean;
setSaveDraw: (enabled: boolean) => void;
@@ -456,6 +460,8 @@ export interface AppState {
downloadStatus: DownloadStatus;
downloadProgress: DownloadProgress | null;
downloadSavePath: string | null;
+ /** 下载速度持续低于阈值的起始时间戳,速度回升或下载结束时置为 null */
+ slowDownloadSince: number | null;
setDownloadStatus: (status: DownloadStatus) => void;
setDownloadProgress: (progress: DownloadProgress | null) => void;
setDownloadSavePath: (path: string | null) => void;