重构:Wails v3 迁移 + 前端目录规范化 + Sidebar滚动优化
- web/ → frontend/ 目录重命名(Wails v3 标准结构) - main.go: Middleware 修复 custom.js 404 + DevTools 延迟启动 - Sidebar: 收藏夹内部独立滚动 + 帮助区块固定底部 - useFavorites.ts: longPressTimer const→let 修复 TypeError - App.vue: Arco Tabs padding-top 覆盖 - build: config.yml / Taskfile.yml 对齐官方模板,devtools build tag - 新增 v3 bindings、vite.config.js、跨平台构建配置 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,286 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { formatBytes as formatFileSize } from '@/utils/fileUtils'
|
||||
import {
|
||||
CheckUpdate, GetUpdateConfig, DownloadUpdate, InstallUpdate
|
||||
} from '../wailsjs/v3-bindings/u-desk/app'
|
||||
import { On, Off } from '@wailsio/events'
|
||||
|
||||
/**
|
||||
* 更新管理 Store
|
||||
* 统一管理版本检查、下载、安装等更新相关逻辑
|
||||
*/
|
||||
export const useUpdateStore = defineStore('update', () => {
|
||||
// ==================== 状态 ====================
|
||||
const updateInfo = ref<UpdateInfo | null>(null)
|
||||
const showUpdate = ref(false)
|
||||
const checking = ref(false)
|
||||
const downloading = ref(false)
|
||||
const installing = ref(false)
|
||||
const downloadProgress = ref(0)
|
||||
const downloadStatus = ref<'active' | 'exception' | 'success'>('active')
|
||||
const progressInfo = ref({
|
||||
speed: 0,
|
||||
downloaded: 0,
|
||||
total: 0
|
||||
})
|
||||
|
||||
// 节流:防止过度更新
|
||||
let lastUpdateTime = 0
|
||||
const UPDATE_THROTTLE = 100 // 100ms 最小更新间隔
|
||||
|
||||
// 最小显示时间:确保进度条至少显示 5 秒
|
||||
let downloadStartTime = 0
|
||||
const MIN_DISPLAY_TIME = 5000 // 5 秒最小显示时间
|
||||
|
||||
// ==================== 工具函数 ====================
|
||||
const parseEventData = (event: unknown) => {
|
||||
try {
|
||||
return typeof event === 'string' ? JSON.parse(event) : (event as Record<string, unknown>)
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
const formatSpeed = (bytesPerSecond: number): string => {
|
||||
return formatFileSize(bytesPerSecond) + '/s'
|
||||
}
|
||||
|
||||
// ==================== 核心方法 ====================
|
||||
/**
|
||||
* 检查更新
|
||||
* @param silent 是否静默模式(不显示消息)
|
||||
*/
|
||||
const checkForUpdates = async (silent = false) => {
|
||||
if (checking.value) return
|
||||
|
||||
checking.value = true
|
||||
|
||||
try {
|
||||
const configResult = await GetUpdateConfig()
|
||||
if (!configResult.success) return
|
||||
|
||||
const { auto_check_enabled } = configResult.data || {}
|
||||
if (!auto_check_enabled) return
|
||||
|
||||
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 { /* 通知不可用时忽略 */ }
|
||||
|
||||
if (!silent) {
|
||||
Message.success('发现新版本!')
|
||||
}
|
||||
} else if (!silent) {
|
||||
Message.success('已是最新版本')
|
||||
}
|
||||
} catch (error) {
|
||||
if (!silent) {
|
||||
console.error('检查更新失败:', error)
|
||||
Message.error('检查更新失败:' + (error as Error).message)
|
||||
}
|
||||
} finally {
|
||||
checking.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载更新
|
||||
*/
|
||||
const downloadUpdate = async () => {
|
||||
const url = updateInfo.value?.download_url
|
||||
if (!url) {
|
||||
Message.warning('下载地址不存在')
|
||||
return
|
||||
}
|
||||
|
||||
// 记录开始时间
|
||||
downloadStartTime = Date.now()
|
||||
|
||||
// 重置下载状态
|
||||
downloading.value = true
|
||||
downloadProgress.value = 1 // 设置为 1 而不是 0,确保进度条显示
|
||||
downloadStatus.value = 'active'
|
||||
progressInfo.value = { speed: 0, downloaded: 0, total: 0 }
|
||||
|
||||
try {
|
||||
const result = await DownloadUpdate(url)
|
||||
if (!result.success) {
|
||||
downloadStatus.value = 'exception'
|
||||
downloading.value = false
|
||||
Message.error(result.message || '下载启动失败')
|
||||
return
|
||||
}
|
||||
Message.success('下载请求已发送,等待后端发送进度事件...')
|
||||
} catch (error) {
|
||||
console.error('下载失败:', error)
|
||||
downloadStatus.value = 'exception'
|
||||
downloading.value = false
|
||||
Message.error('下载失败:' + (error as Error).message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装更新
|
||||
*/
|
||||
const installUpdate = async (filePath: string) => {
|
||||
if (!filePath) {
|
||||
Message.warning('请先下载更新包')
|
||||
return
|
||||
}
|
||||
|
||||
installing.value = true
|
||||
|
||||
try {
|
||||
const result = await InstallUpdate(filePath, true)
|
||||
if (result.success) {
|
||||
Message.success({
|
||||
content: '安装成功!应用将在几秒后重启...',
|
||||
duration: 3000
|
||||
})
|
||||
return
|
||||
}
|
||||
Message.error(result.message || '安装失败')
|
||||
} catch (error) {
|
||||
Message.error('安装失败:' + (error as Error).message)
|
||||
} finally {
|
||||
installing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载进度处理
|
||||
*/
|
||||
const onDownloadProgress = (event: unknown) => {
|
||||
const now = Date.now()
|
||||
if (now - lastUpdateTime < UPDATE_THROTTLE) {
|
||||
return
|
||||
}
|
||||
|
||||
lastUpdateTime = now
|
||||
const data = parseEventData(event)
|
||||
|
||||
progressInfo.value = {
|
||||
speed: (data.speed as number) || 0,
|
||||
downloaded: (data.downloaded as number) || 0,
|
||||
total: (data.total as number) || 0
|
||||
}
|
||||
|
||||
const rawProgress = Number(data.progress) || 0
|
||||
const safeProgress = Math.min(100, Math.max(0, Math.round(rawProgress)))
|
||||
downloadProgress.value = safeProgress
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载完成处理
|
||||
*/
|
||||
const onDownloadComplete = (event: unknown) => {
|
||||
const data = parseEventData(event)
|
||||
|
||||
// 错误处理
|
||||
if (data.error) {
|
||||
console.error('下载失败:', data.error)
|
||||
downloadStatus.value = 'exception'
|
||||
downloading.value = false
|
||||
Message.error('下载失败:' + data.error)
|
||||
return
|
||||
}
|
||||
|
||||
// 数据验证
|
||||
if (!data.success || !data.file_path) {
|
||||
console.error('下载数据不完整:', data)
|
||||
downloadStatus.value = 'exception'
|
||||
downloading.value = false
|
||||
Message.error('下载完成但数据不完整')
|
||||
return
|
||||
}
|
||||
|
||||
// 完成下载
|
||||
downloadProgress.value = 100
|
||||
downloadStatus.value = 'success'
|
||||
|
||||
const fileSize = (data.file_size as number) || 0
|
||||
|
||||
// 系统通知:下载完成
|
||||
try {
|
||||
window.runtime?.SendNotification?.({
|
||||
title: 'U-Desk',
|
||||
body: `更新包下载完成 (${formatFileSize(fileSize)}),正在安装...`
|
||||
})
|
||||
} catch { /* 通知不可用时忽略 */ }
|
||||
progressInfo.value = {
|
||||
speed: 0,
|
||||
downloaded: fileSize,
|
||||
total: fileSize
|
||||
}
|
||||
|
||||
// 计算已经显示的时间
|
||||
const elapsed = Date.now() - downloadStartTime
|
||||
const remainingTime = Math.max(0, MIN_DISPLAY_TIME - elapsed)
|
||||
|
||||
// 确保进度条至少显示 3 秒
|
||||
setTimeout(() => {
|
||||
downloading.value = false // 安装前才关闭下载状态
|
||||
installUpdate(data.file_path as string)
|
||||
}, remainingTime)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置事件监听
|
||||
*/
|
||||
const setupEventListeners = () => {
|
||||
On('download-progress', onDownloadProgress)
|
||||
On('download-complete', onDownloadComplete)
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除事件监听
|
||||
*/
|
||||
const removeEventListeners = () => {
|
||||
Off('download-progress')
|
||||
Off('download-complete')
|
||||
}
|
||||
|
||||
// ==================== 返回 ====================
|
||||
return {
|
||||
// 状态
|
||||
updateInfo,
|
||||
showUpdate,
|
||||
checking,
|
||||
downloading,
|
||||
installing,
|
||||
downloadProgress,
|
||||
downloadStatus,
|
||||
progressInfo,
|
||||
|
||||
// 方法
|
||||
checkForUpdates,
|
||||
downloadUpdate,
|
||||
installUpdate,
|
||||
setupEventListeners,
|
||||
removeEventListeners,
|
||||
formatFileSize,
|
||||
formatSpeed
|
||||
}
|
||||
})
|
||||
|
||||
// ==================== 类型定义 ====================
|
||||
interface UpdateInfo {
|
||||
has_update: boolean
|
||||
current_version: string
|
||||
latest_version: string
|
||||
download_url: string
|
||||
changelog: string
|
||||
force_update: boolean
|
||||
release_date: string
|
||||
file_size: number
|
||||
}
|
||||
Reference in New Issue
Block a user