重构: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:
lxy
2026-05-01 11:03:53 +08:00
co-authored by Claude Opus 4.7
parent 44847e0d40
commit f54bf1c28d
185 changed files with 7768 additions and 914 deletions
+286
View File
@@ -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
}