feat(electron): 实现系统托盘和关闭行为配置功能

- 添加系统托盘创建和销毁逻辑- 实现窗口关闭行为配置(退出/最小化/托盘)
- 添加配置文件读写功能
- 实现下载取消和清理功能
- 添加待更新文件检查机制
- 优化文件下载进度和错误处理
- 添加自动更新配置选项- 实现平滑滚动动画效果
- 添加试用期过期类型检查
-优化VIP状态刷新逻辑
This commit is contained in:
2025-10-17 14:17:47 +08:00
parent 6e1b4d00de
commit 07e34c35c8
19 changed files with 1545 additions and 467 deletions

View File

@@ -84,6 +84,7 @@
<span style="font-weight: 500" v-if="prog.current !== '0 MB' && prog.total !== '0 MB'">{{ prog.current }} / {{ prog.total }}</span>
<span style="font-weight: 500" v-else>下载完成</span>
<div class="action-buttons">
<el-button size="small" @click="clearDownloadedFiles">清除下载</el-button>
<el-button size="small" type="primary" @click="installUpdate">立即重启</el-button>
</div>
</div>
@@ -99,10 +100,18 @@
import {ref, computed, onMounted, onUnmounted, watch} from 'vue'
import {ElMessage, ElMessageBox} from 'element-plus'
import {updateApi} from '../../api/update'
import {getSettings} from '../../utils/settings'
const props = defineProps<{ modelValue: boolean }>()
const props = defineProps<{
modelValue: boolean
}>()
const emit = defineEmits<{ 'update:modelValue': [value: boolean] }>()
// 暴露方法给父组件调用
defineExpose({
checkForUpdatesNow
})
const show = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
@@ -119,7 +128,7 @@ const info = ref({
downloadUrl: '',
asarUrl: '',
jarUrl: '',
updateNotes: '• 优化了用户界面体验\n• 修复了已知问题\n• 提升了系统稳定性',
updateNotes: '',
currentVersion: '',
hasUpdate: false
})
@@ -135,12 +144,10 @@ async function autoCheck(silent = false) {
if (!result.needUpdate) {
hasNewVersion.value = false
if (!silent) {
ElMessage.info('当前已是最新版本')
}
if (!silent) ElMessage.info('当前已是最新版本')
return
}
// 发现新版本,更新信息并显示小红点
info.value = {
currentVersion: result.currentVersion,
@@ -148,52 +155,50 @@ async function autoCheck(silent = false) {
downloadUrl: result.downloadUrl || '',
asarUrl: result.asarUrl || '',
jarUrl: result.jarUrl || '',
updateNotes: '• 优化了用户界面体验\n• 修复了已知问题\n• 提升了系统稳定性\n• 同步更新前端和后端',
updateNotes: result.updateNotes || '',
hasUpdate: true
}
hasNewVersion.value = true
// 检查是否跳过此版本
const skippedVersion = localStorage.getItem(SKIP_VERSION_KEY)
if (skippedVersion === result.latestVersion) {
// 跳过的版本:显示小红点,但不弹框
return
}
if (skippedVersion === result.latestVersion) return
// 检查是否在稍后提醒时间内
const remindLater = localStorage.getItem(REMIND_LATER_KEY)
if (remindLater && Date.now() < parseInt(remindLater)) {
// 稍后提醒期间:显示小红点,但不弹框
if (remindLater && Date.now() < parseInt(remindLater)) return
const settings = getSettings()
if (settings.autoUpdate) {
await startAutoDownload()
return
}
// 首次发现新版本:显示小红点并弹框
show.value = true
stage.value = 'check'
if (!silent) {
ElMessage.success('发现新版本')
}
if (!silent) ElMessage.success('发现新版本')
} catch (error) {
console.error('检查更新失败:', error)
if (!silent) {
ElMessage.error('检查更新失败')
}
if (!silent) ElMessage.error('检查更新失败')
}
}
function handleVersionClick() {
// 如果有新版本,直接显示更新对话框
if (stage.value === 'downloading' || stage.value === 'completed') {
show.value = true
return
}
if (hasNewVersion.value) {
// 重置状态确保从检查阶段开始
stage.value = 'check'
prog.value = {percentage: 0, current: '0 MB', total: '0 MB'}
show.value = true
} else {
// 没有新版本,执行检查更新
autoCheck(false)
checkForUpdatesNow()
}
}
// 立即检查更新(供外部调用)
async function checkForUpdatesNow() {
await autoCheck(false)
}
function skipVersion() {
localStorage.setItem(SKIP_VERSION_KEY, info.value.latestVersion)
show.value = false
@@ -206,14 +211,27 @@ function remindLater() {
}
async function start() {
// 如果已经在下载或已完成,不重复执行
if (stage.value === 'downloading') {
show.value = true
return
}
if (stage.value === 'completed') {
show.value = true
return
}
if (!info.value.asarUrl && !info.value.jarUrl) {
ElMessage.error('下载链接不可用')
return
}
stage.value = 'downloading'
show.value = true
prog.value = {percentage: 0, current: '0 MB', total: '0 MB'}
// 设置新的进度监听器(会自动清理旧的)
;(window as any).electronAPI.onDownloadProgress((progress: any) => {
prog.value = {
percentage: progress.percentage || 0,
@@ -231,32 +249,73 @@ async function start() {
if (response.success) {
stage.value = 'completed'
prog.value.percentage = 100
// 如果没有有效的进度信息,设置默认值
if (prog.value.current === '0 MB' && prog.value.total === '0 MB') {
// 保持原有的"0 MB"值,让模板中的条件判断来处理显示
}
ElMessage.success('下载完成')
show.value = true
} else {
ElMessage.error('下载失败: ' + (response.error || '未知错误'))
stage.value = 'check'
prog.value = {percentage: 0, current: '0 MB', total: '0 MB'}
;(window as any).electronAPI.removeDownloadProgressListener()
}
} catch (error) {
console.error('下载失败:', error)
ElMessage.error('下载失败')
stage.value = 'check'
prog.value = {percentage: 0, current: '0 MB', total: '0 MB'}
;(window as any).electronAPI.removeDownloadProgressListener()
}
}
async function startAutoDownload() {
if (!info.value.asarUrl && !info.value.jarUrl) return
stage.value = 'downloading'
prog.value = {percentage: 0, current: '0 MB', total: '0 MB'}
;(window as any).electronAPI.onDownloadProgress((progress: any) => {
prog.value = {
percentage: progress.percentage || 0,
current: progress.current || '0 MB',
total: progress.total || '0 MB'
}
})
try {
const response = await (window as any).electronAPI.downloadUpdate({
asarUrl: info.value.asarUrl,
jarUrl: info.value.jarUrl
})
if (response.success) {
stage.value = 'completed'
prog.value.percentage = 100
show.value = true
ElMessage.success('更新已下载完成,可以安装了')
} else {
stage.value = 'check'
;(window as any).electronAPI.removeDownloadProgressListener()
}
} catch (error) {
stage.value = 'check'
;(window as any).electronAPI.removeDownloadProgressListener()
}
}
async function cancelDownload() {
try {
(window as any).electronAPI.removeDownloadProgressListener()
;(window as any).electronAPI.removeDownloadProgressListener()
await (window as any).electronAPI.cancelDownload()
show.value = false
stage.value = 'check'
prog.value = {percentage: 0, current: '0 MB', total: '0 MB'}
hasNewVersion.value = false
show.value = false
ElMessage.info('已取消下载')
} catch (error) {
console.error('取消下载失败:', error)
show.value = false
stage.value = 'check'
prog.value = {percentage: 0, current: '0 MB', total: '0 MB'}
hasNewVersion.value = false
show.value = false
}
}
@@ -281,18 +340,46 @@ async function installUpdate() {
}
}
async function clearDownloadedFiles() {
try {
await ElMessageBox.confirm(
'确定要清除已下载的更新文件吗?清除后需要重新下载。',
'确认清除',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
const response = await (window as any).electronAPI.clearUpdateFiles()
if (response.success) {
ElMessage.success('已清除下载文件')
// 重置状态
stage.value = 'check'
prog.value = {percentage: 0, current: '0 MB', total: '0 MB'}
hasNewVersion.value = false
show.value = false
} else {
ElMessage.error('清除失败: ' + (response.error || '未知错误'))
}
} catch (error) {
if (error !== 'cancel') ElMessage.error('清除失败')
}
}
onMounted(async () => {
version.value = await (window as any).electronAPI.getJarVersion()
await autoCheck(true)
})
// 监听对话框关闭,重置状态
watch(show, (newValue) => {
if (!newValue) {
// 对话框关闭时重置状态
stage.value = 'check'
prog.value = {percentage: 0, current: '0 MB', total: '0 MB'}
const pendingUpdate = await (window as any).electronAPI.checkPendingUpdate()
if (pendingUpdate && pendingUpdate.hasPendingUpdate) {
stage.value = 'completed'
prog.value.percentage = 100
return
}
await autoCheck(true)
})
onUnmounted(() => {
@@ -315,8 +402,10 @@ onUnmounted(() => {
z-index: 1000;
cursor: pointer;
user-select: none;
transition: all 0.3s ease;
}
.update-badge {
position: absolute;
top: -2px;