修复: 版本更新与设置面板走查问题
- 更新链路: 检查记录远端SHA256,下载完成事件回传,安装时带哈希校验 - 续传: 服务器忽略Range返回200时截断重下防追加损坏,文件名去query - 定时器: 自动检查可重入,配置变更免重启生效,关闭即停,下载加互斥 - 前端: 支持跳过此版本并按版本去重,手动检查不受门禁限制,启动未就绪延时重试 - 更新面板: 新增自动检查开关/间隔/地址配置与进度条状态映射 - 版本历史: cache参数强制刷新与加载超时重试兜底 - 配置: 侧边栏分区全链路持久化,保存请求复用api类型防字段丢失 - 侧边栏: 区块定位改显式类名,不再依赖排序位置 - 绑定: 重新生成并与镜像目录同步
This commit is contained in:
@@ -4,10 +4,15 @@ import { Message } from '@arco-design/web-vue'
|
||||
import { formatBytes as formatFileSize } from '@/utils/fileUtils'
|
||||
import { parseWailsEventData } from '@/utils/wailsEvent'
|
||||
import {
|
||||
CheckUpdate, GetUpdateConfig, DownloadUpdate, InstallUpdate
|
||||
CheckUpdate, GetUpdateConfig, DownloadUpdate, InstallUpdate, InstallUpdateWithHash
|
||||
} from '../wailsjs/v3-bindings/u-desk/app'
|
||||
import { On, Off } from '@wailsio/events'
|
||||
|
||||
// 本次应用运行内已自动弹过的版本(升级后自动恢复提醒)
|
||||
let sessionNotifiedVersion = ''
|
||||
// 下载完成事件携带的远端声明哈希,安装时用于完整性校验
|
||||
let lastExpectedSha256 = ''
|
||||
|
||||
/**
|
||||
* 更新管理 Store
|
||||
* 统一管理版本检查、下载、安装等更新相关逻辑
|
||||
@@ -41,34 +46,65 @@ export const useUpdateStore = defineStore('update', () => {
|
||||
}
|
||||
|
||||
// ==================== 核心方法 ====================
|
||||
// 用户显式跳过的版本(localStorage):该版本不再自动弹窗,手动检查不受限
|
||||
const SKIPPED_VERSION_KEY = 'udesk-update-skipped-version'
|
||||
|
||||
/**
|
||||
* 展示更新弹窗。自动路径(静默检查/后端广播)按版本去重(用户已跳过或本次运行已弹过则不弹),手动检查强制弹
|
||||
*/
|
||||
const tryShowUpdate = (data: UpdateInfo, force = false) => {
|
||||
const version = String(data?.latest_version || '')
|
||||
if (!force && version) {
|
||||
if (localStorage.getItem(SKIPPED_VERSION_KEY) === version) return
|
||||
if (sessionNotifiedVersion === version) return
|
||||
}
|
||||
updateInfo.value = data
|
||||
showUpdate.value = true
|
||||
if (version) {
|
||||
sessionNotifiedVersion = version
|
||||
}
|
||||
if (!force) {
|
||||
// 自动发现时的系统通知
|
||||
try {
|
||||
window.runtime?.SendNotification?.({
|
||||
title: 'U-Desk',
|
||||
body: `发现新版本 ${version},点击查看`
|
||||
})
|
||||
} catch { /* 通知不可用时忽略 */ }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 稍后提醒:记录用户显式跳过的版本,该版本不再自动弹窗(手动检查仍强制弹)
|
||||
*/
|
||||
const skipVersion = (version: string) => {
|
||||
if (version) {
|
||||
localStorage.setItem(SKIPPED_VERSION_KEY, version)
|
||||
}
|
||||
showUpdate.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查更新
|
||||
* @param silent 是否静默模式(不显示消息)
|
||||
* @param manual 是否手动触发(跳过 auto_check_enabled 门禁)
|
||||
* @returns 检查是否实际完成(false=后端未就绪或网络失败,调用方可重试)
|
||||
*/
|
||||
const checkForUpdates = async (silent = false) => {
|
||||
if (checking.value) return
|
||||
const checkForUpdates = async (silent = false, manual = false): Promise<boolean> => {
|
||||
if (checking.value) return true
|
||||
|
||||
checking.value = true
|
||||
|
||||
try {
|
||||
const configResult = await GetUpdateConfig()
|
||||
if (!configResult.success) return
|
||||
if (!configResult.success) return false
|
||||
|
||||
const { auto_check_enabled } = configResult.data || {}
|
||||
if (!auto_check_enabled) return
|
||||
if (!manual && !auto_check_enabled) return true
|
||||
|
||||
const result = await CheckUpdate()
|
||||
if (result.success && result.data?.has_update) {
|
||||
updateInfo.value = result.data
|
||||
showUpdate.value = true
|
||||
|
||||
// 系统通知:发现新版本
|
||||
try {
|
||||
window.runtime?.SendNotification?.({
|
||||
title: 'U-Desk',
|
||||
body: `发现新版本 ${result.data.latest_version},点击查看`
|
||||
})
|
||||
} catch { /* 通知不可用时忽略 */ }
|
||||
tryShowUpdate(result.data, !silent)
|
||||
|
||||
if (!silent) {
|
||||
Message.success('发现新版本!')
|
||||
@@ -76,11 +112,13 @@ export const useUpdateStore = defineStore('update', () => {
|
||||
} else if (!silent) {
|
||||
Message.success('已是最新版本')
|
||||
}
|
||||
return result.success === true
|
||||
} catch (error) {
|
||||
if (!silent) {
|
||||
console.error('检查更新失败:', error)
|
||||
Message.error('检查更新失败:' + (error as Error).message)
|
||||
}
|
||||
return false
|
||||
} finally {
|
||||
checking.value = false
|
||||
}
|
||||
@@ -134,7 +172,10 @@ export const useUpdateStore = defineStore('update', () => {
|
||||
installing.value = true
|
||||
|
||||
try {
|
||||
const result = await InstallUpdate(filePath, true)
|
||||
// 优先带哈希校验安装(期望哈希来自下载完成事件的远端声明)
|
||||
const result = lastExpectedSha256
|
||||
? await InstallUpdateWithHash(filePath, true, lastExpectedSha256, 'sha256')
|
||||
: await InstallUpdate(filePath, true)
|
||||
if (result.success) {
|
||||
Message.success({
|
||||
content: '安装成功!应用将在几秒后重启...',
|
||||
@@ -200,6 +241,8 @@ export const useUpdateStore = defineStore('update', () => {
|
||||
// 完成下载
|
||||
downloadProgress.value = 100
|
||||
downloadStatus.value = 'success'
|
||||
// 记录远端声明的期望哈希,安装时用于校验
|
||||
lastExpectedSha256 = String(data.expected_sha256 || '')
|
||||
|
||||
const fileSize = (data.file_size as number) || 0
|
||||
|
||||
@@ -227,12 +270,24 @@ export const useUpdateStore = defineStore('update', () => {
|
||||
}, remainingTime)
|
||||
}
|
||||
|
||||
/**
|
||||
* 后端广播"发现新版本"处理(启动即查与周期检查命中时后端都会发)
|
||||
*/
|
||||
const onUpdateAvailable = (event: unknown) => {
|
||||
const data = parseWailsEventData(event) as UpdateInfo
|
||||
if (!data?.latest_version) {
|
||||
return
|
||||
}
|
||||
tryShowUpdate(data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置事件监听
|
||||
*/
|
||||
const setupEventListeners = () => {
|
||||
On('download-progress', onDownloadProgress)
|
||||
On('download-complete', onDownloadComplete)
|
||||
On('update-available', onUpdateAvailable)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -241,6 +296,7 @@ export const useUpdateStore = defineStore('update', () => {
|
||||
const removeEventListeners = () => {
|
||||
Off('download-progress')
|
||||
Off('download-complete')
|
||||
Off('update-available')
|
||||
}
|
||||
|
||||
// ==================== 返回 ====================
|
||||
@@ -259,6 +315,7 @@ export const useUpdateStore = defineStore('update', () => {
|
||||
checkForUpdates,
|
||||
downloadUpdate,
|
||||
installUpdate,
|
||||
skipVersion,
|
||||
setupEventListeners,
|
||||
removeEventListeners,
|
||||
formatFileSize,
|
||||
|
||||
Reference in New Issue
Block a user