feat(客户端更新): 登录页/首页更新面板显示更新包下载进度条
- 新增 shared/utils/update-progress.ts:进度事件解析/百分比钳制/文案格式化(纯函数) - 新增 shared/components/UpdateProgressBar.vue:两页共用,失败态琥珀色并停在断点 百分比、总大小未知走不确定态 - useVersionUpdate:先挂监听再发桥调用(避免丢首帧),监听 pywebview-update-progress (Python 侧 evaluate_js 推送,同 pywebview-download-progress 约定);失败保留上次进度 - 测试:tests/update-progress.test.ts(8 用例) 兼容:老客户端没有该事件源,进度条不出现,其余行为不变(优雅降级)。 注:本仓库 modules-withdraw / modules-image-video 有 3 个既存失败用例(隔离复现), 与本次改动无关;全仓库仅新增测试引用本次新增模块。
This commit is contained in:
@@ -6,8 +6,31 @@
|
||||
* 提供);旧客户端无此桥时降级:本机版本为空 → 只要线上有包就允许手动更新。
|
||||
*/
|
||||
|
||||
import { ref } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { getPywebviewApi } from '@/shared/bridges/pywebview'
|
||||
import {
|
||||
normalizeUpdateProgress,
|
||||
type UpdateProgress,
|
||||
} from '@/shared/utils/update-progress'
|
||||
|
||||
export type { UpdateProgress, UpdateProgressStatus } from '@/shared/utils/update-progress'
|
||||
|
||||
const updateProgress = ref<UpdateProgress | null>(null)
|
||||
let progressListenerBound = false
|
||||
|
||||
/**
|
||||
* 绑定一次全局监听:下载进度由 Python 侧主动推事件(同 pywebview-download-progress 约定),
|
||||
* JS 侧不轮询——桥调用虽然并发安全,但轮询会给主进程平白加请求。
|
||||
*/
|
||||
function bindProgressListener() {
|
||||
if (progressListenerBound || typeof window === 'undefined') return
|
||||
progressListenerBound = true
|
||||
window.addEventListener('pywebview-update-progress', (event) => {
|
||||
const progress = normalizeUpdateProgress((event as CustomEvent<unknown>).detail)
|
||||
if (!progress) return
|
||||
updateProgress.value = progress
|
||||
})
|
||||
}
|
||||
|
||||
function normVersion(value: unknown): string {
|
||||
return String(value ?? '').trim().replace(/^[vV]/, '')
|
||||
@@ -124,19 +147,57 @@ export function useVersionUpdate() {
|
||||
updating.value = false
|
||||
return
|
||||
}
|
||||
// 先挂监听再发桥调用:进度事件在下载开始后立刻开推,晚绑会丢最前面的进度
|
||||
bindProgressListener()
|
||||
updateProgress.value = {
|
||||
status: 'downloading',
|
||||
message: '正在准备下载更新包...',
|
||||
downloaded: 0,
|
||||
total: 0,
|
||||
percent: -1,
|
||||
}
|
||||
hint.value = '正在下载并准备更新,程序将自动退出...'
|
||||
try {
|
||||
const result = await doUpdateApp!(fileUrl.value)
|
||||
hint.value = result?.success
|
||||
? '更新已启动,程序即将退出...'
|
||||
: (result?.error || '更新启动失败,请重试')
|
||||
if (result?.success) {
|
||||
const last = updateProgress.value
|
||||
updateProgress.value = {
|
||||
status: 'ready',
|
||||
message: '安装包已就绪,正在启动更新程序,程序即将退出...',
|
||||
downloaded: last?.downloaded || 0,
|
||||
total: last?.total || 0,
|
||||
percent: 100,
|
||||
}
|
||||
hint.value = '更新已启动,程序即将退出...'
|
||||
} else {
|
||||
// 失败:进度条停在断点处,重试将从断点继续(Python 侧保留断点文件)
|
||||
const message = result?.error || '更新启动失败,请重试'
|
||||
updateProgress.value = {
|
||||
status: 'failed',
|
||||
message,
|
||||
downloaded: updateProgress.value?.downloaded || 0,
|
||||
total: updateProgress.value?.total || 0,
|
||||
percent: updateProgress.value?.percent ?? -1,
|
||||
}
|
||||
hint.value = message
|
||||
}
|
||||
} catch {
|
||||
updateProgress.value = {
|
||||
status: 'failed',
|
||||
message: '请求更新失败,请重试',
|
||||
downloaded: updateProgress.value?.downloaded || 0,
|
||||
total: updateProgress.value?.total || 0,
|
||||
percent: updateProgress.value?.percent ?? -1,
|
||||
}
|
||||
hint.value = '请求更新失败,请重试'
|
||||
} finally {
|
||||
updating.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 进度条的百分比/文案由 UpdateProgressBar 组件从 progress 自行派生(见 shared/utils/update-progress)
|
||||
const progress = computed(() => updateProgress.value)
|
||||
|
||||
return {
|
||||
checking,
|
||||
updating,
|
||||
@@ -147,6 +208,7 @@ export function useVersionUpdate() {
|
||||
canDownload,
|
||||
hint,
|
||||
checked,
|
||||
progress,
|
||||
loadCurrentVersion,
|
||||
runCheck,
|
||||
doUpdate,
|
||||
|
||||
Reference in New Issue
Block a user