- Added AutoUpdater class to manage update checks and downloads. - Integrated auto-updater in the main process after the main window is created. - Enhanced preload script to expose update-related IPC channels. - Created UpdateDialog component to handle user interactions for updates. - Implemented state management for update status using Pinia store. - Added localization support for update messages in multiple languages. - Documented auto-update configuration steps for private GitHub repositories. - Created .env.example for GitHub token configuration.
196 lines
5.3 KiB
Vue
196 lines
5.3 KiB
Vue
<template>
|
|
<v-dialog
|
|
v-model="dialogVisible"
|
|
max-width="500"
|
|
persistent
|
|
:scrim="false"
|
|
>
|
|
<v-card v-if="updateStatus.status === 'available'">
|
|
<v-card-title class="headline">
|
|
{{ t('update.newVersionAvailable') }}
|
|
</v-card-title>
|
|
<v-card-text>
|
|
<div>{{ t('update.currentVersion') }}: {{ updateStatus.currentVersion }}</div>
|
|
<div>{{ t('update.latestVersion') }}: {{ latestVersion }}</div>
|
|
<div class="mt-3">{{ t('update.updateDescription') }}</div>
|
|
</v-card-text>
|
|
<v-card-actions>
|
|
<v-spacer></v-spacer>
|
|
<v-btn
|
|
color="primary"
|
|
variant="text"
|
|
@click="skipUpdate"
|
|
>
|
|
{{ t('update.skipUpdate') }}
|
|
</v-btn>
|
|
<v-btn
|
|
color="primary"
|
|
variant="flat"
|
|
@click="downloadUpdate"
|
|
:loading="isDownloading"
|
|
>
|
|
{{ t('update.downloadUpdate') }}
|
|
</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
|
|
<v-card v-else-if="updateStatus.status === 'downloading'">
|
|
<v-card-title class="headline">
|
|
{{ t('update.downloadingUpdate') }}
|
|
</v-card-title>
|
|
<v-card-text>
|
|
<v-progress-linear
|
|
:model-value="downloadProgress"
|
|
color="primary"
|
|
height="25"
|
|
rounded
|
|
>
|
|
<strong>{{ Math.round(downloadProgress) }}%</strong>
|
|
</v-progress-linear>
|
|
<div class="mt-3">{{ t('update.downloadProgressText') }}</div>
|
|
</v-card-text>
|
|
<v-card-actions>
|
|
<v-spacer></v-spacer>
|
|
<v-btn
|
|
color="primary"
|
|
variant="text"
|
|
@click="cancelDownload"
|
|
:disabled="isDownloadComplete"
|
|
>
|
|
{{ t('common.cancel') }}
|
|
</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
|
|
<v-card v-else-if="updateStatus.status === 'downloaded'">
|
|
<v-card-title class="headline">
|
|
{{ t('update.updateDownloaded') }}
|
|
</v-card-title>
|
|
<v-card-text>
|
|
{{ t('update.updateReadyToInstall') }}
|
|
</v-card-text>
|
|
<v-card-actions>
|
|
<v-spacer></v-spacer>
|
|
<v-btn
|
|
color="primary"
|
|
variant="text"
|
|
@click="laterInstall"
|
|
>
|
|
{{ t('update.laterInstall') }}
|
|
</v-btn>
|
|
<v-btn
|
|
color="primary"
|
|
variant="flat"
|
|
@click="installUpdate"
|
|
>
|
|
{{ t('update.installNow') }}
|
|
</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
|
|
<v-card v-else-if="updateStatus.status === 'error'">
|
|
<v-card-title class="headline">
|
|
{{ t('update.updateError') }}
|
|
</v-card-title>
|
|
<v-card-text>
|
|
{{ updateStatus.error }}
|
|
</v-card-text>
|
|
<v-card-actions>
|
|
<v-spacer></v-spacer>
|
|
<v-btn
|
|
color="primary"
|
|
variant="flat"
|
|
@click="closeDialog"
|
|
>
|
|
{{ t('common.ok') }}
|
|
</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-dialog>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, onMounted, onUnmounted } from 'vue'
|
|
import { useAppStore, UpdateStatus } from '@/renderer/store/app'
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
const { t } = useI18n()
|
|
const appStore = useAppStore()
|
|
|
|
// 从store获取状态
|
|
const updateStatus = computed(() => appStore.updateStatus)
|
|
const downloadProgress = computed(() => appStore.downloadProgress)
|
|
|
|
// 对话框显示状态
|
|
const dialogVisible = computed({
|
|
get: () => {
|
|
return appStore.updateDialogVisible ||
|
|
updateStatus.value.status === 'available' ||
|
|
updateStatus.value.status === 'downloading' ||
|
|
updateStatus.value.status === 'downloaded' ||
|
|
(updateStatus.value.status === 'error' && !!updateStatus.value.error)
|
|
},
|
|
set: (value: boolean) => {
|
|
if (value) {
|
|
appStore.showUpdateDialog()
|
|
} else {
|
|
appStore.hideUpdateDialog()
|
|
}
|
|
}
|
|
})
|
|
|
|
const latestVersion = computed(() => {
|
|
// 这里应该从API获取最新版本号,暂时使用固定值
|
|
return 'v' + (updateStatus.value.currentVersion || '1.0.0').replace(/^v?/, '').split('.').map((n, i) =>
|
|
i === 2 ? (parseInt(n) + 1).toString() : n
|
|
).join('.')
|
|
})
|
|
|
|
const isDownloading = computed(() => updateStatus.value.status === 'downloading')
|
|
const isDownloadComplete = computed(() => downloadProgress.value >= 100)
|
|
|
|
// 方法
|
|
const downloadUpdate = async () => {
|
|
await appStore.startUpdateDownload()
|
|
}
|
|
|
|
const skipUpdate = () => {
|
|
appStore.setUpdateStatus('not-available', updateStatus.value.currentVersion)
|
|
dialogVisible.value = false
|
|
}
|
|
|
|
const cancelDownload = () => {
|
|
// 取消下载逻辑
|
|
appStore.setUpdateStatus('not-available', updateStatus.value.currentVersion)
|
|
dialogVisible.value = false
|
|
}
|
|
|
|
const laterInstall = () => {
|
|
dialogVisible.value = false
|
|
}
|
|
|
|
const installUpdate = () => {
|
|
// 安装更新,这需要触发quitAndInstall
|
|
window.mainApi.invoke('msgInstallUpdate')
|
|
dialogVisible.value = false
|
|
}
|
|
|
|
const closeDialog = () => {
|
|
appStore.setUpdateStatus('idle')
|
|
dialogVisible.value = false
|
|
}
|
|
|
|
// 监听更新下载进度
|
|
onMounted(() => {
|
|
// 监听主进程发来的更新相关事件
|
|
window.mainApi.on('update-download-progress', (_, progress) => {
|
|
appStore.setDownloadProgress(progress.percent)
|
|
})
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
// 移除事件监听器
|
|
window.mainApi.off('update-download-progress', () => {})
|
|
})
|
|
</script>
|
|
</template> |