Files
autoAiWorkSys/app/components/UpdateDialog.vue
张成 e17d5610f5 1
2025-12-22 16:26:59 +08:00

201 lines
5.0 KiB
Vue

<template>
<Dialog
v-model:visible="dialogVisible"
:modal="true"
:closable="!updateInfo?.forceUpdate"
header="发现新版本"
:style="{ width: '500px' }"
@hide="handleClose"
>
<div class="update-content">
<div class="update-info">
<p><strong>新版本号:</strong> {{ updateInfo?.version || '未知' }}</p>
<p v-if="updateInfo?.fileSize > 0"><strong>文件大小:</strong> {{ formatFileSize(updateInfo.fileSize) }}</p>
<div v-if="updateInfo?.releaseNotes" class="release-notes">
<p><strong>更新内容:</strong></p>
<pre>{{ updateInfo.releaseNotes }}</pre>
</div>
</div>
<!-- 下载进度 -->
<div v-if="isDownloading" class="update-progress">
<ProgressBar :value="updateProgress" />
<div class="progress-text">
<span>下载进度: {{ updateProgress }}%</span>
<span v-if="downloadState?.totalBytes > 0">
({{ formatFileSize(downloadState.downloadedBytes) }} / {{ formatFileSize(downloadState.totalBytes) }})
</span>
</div>
</div>
</div>
<template #footer>
<Button
v-if="!isDownloading && updateProgress === 0"
label="立即更新"
@click="handleStartDownload"
/>
<Button
v-if="!isDownloading && updateProgress === 100"
label="立即安装"
@click="handleInstallUpdate"
/>
<Button
v-if="!updateInfo?.forceUpdate && !isDownloading && updateProgress === 0"
label="稍后提醒"
severity="secondary"
@click="handleClose"
/>
<Button
v-if="!updateInfo?.forceUpdate && !isDownloading && updateProgress === 100"
label="稍后安装"
severity="secondary"
@click="handleClose"
/>
</template>
</Dialog>
</template>
<script>
import { mapState, mapActions } from 'vuex';
import { Dialog, Button, ProgressBar } from '../components/PrimeVue';
export default {
name: 'UpdateDialog',
components: {
Dialog,
Button,
ProgressBar
},
computed: {
...mapState('update', [
'updateDialogVisible',
'updateInfo',
'updateProgress',
'isDownloading',
'downloadState'
]),
dialogVisible: {
get() {
return this.updateDialogVisible && !!this.updateInfo;
},
set(value) {
if (!value) {
this.handleClose();
}
}
}
},
methods: {
...mapActions('update', [
'hideUpdateDialog'
]),
handleClose() {
this.hideUpdateDialog();
},
async handleStartDownload() {
try {
const updateInfoData = this.updateInfo;
if (!updateInfoData) {
console.error('更新信息不存在');
return;
}
if (this.$store) {
this.$store.dispatch('update/setDownloading', true);
this.$store.dispatch('update/setUpdateProgress', 0);
}
if (!window.electronAPI || !window.electronAPI.update) {
throw new Error('Electron API不可用');
}
const result = await window.electronAPI.invoke('update:download', updateInfoData.downloadUrl);
if (!result.success) {
throw new Error(result.error || '下载失败');
}
} catch (error) {
console.error('开始下载更新失败:', error);
if (this.$store) {
this.$store.dispatch('update/setDownloading', false);
}
}
},
async handleInstallUpdate() {
try {
if (!window.electronAPI || !window.electronAPI.update) {
throw new Error('Electron API不可用');
}
if (this.$store) {
this.$store.dispatch('update/setDownloading', true);
}
const result = await window.electronAPI.invoke('update:install');
if (result.success) {
if (this.$store) {
this.$store.dispatch('update/hideUpdateDialog');
}
} else {
throw new Error(result.error || '安装失败');
}
} catch (error) {
console.error('安装更新失败:', error);
if (this.$store) {
this.$store.dispatch('update/setDownloading', false);
}
}
},
formatFileSize(bytes) {
if (!bytes || bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
}
}
};
</script>
<style lang="less" scoped>
.update-content {
padding: 10px 0;
}
.update-info {
margin-bottom: 20px;
p {
margin: 8px 0;
}
}
.release-notes {
margin-top: 15px;
pre {
background: #f5f5f5;
padding: 10px;
border-radius: 4px;
white-space: pre-wrap;
word-break: break-word;
font-size: 13px;
line-height: 1.5;
}
}
.update-progress {
margin-top: 20px;
.progress-text {
margin-top: 10px;
text-align: center;
font-size: 13px;
color: #666;
}
}
</style>