124 lines
6.3 KiB
Vue
124 lines
6.3 KiB
Vue
|
|
<template>
|
||
|
|
<div v-if="isElectronEnv && status !== 'idle'" class="fixed bottom-4 right-4 z-50 animate-slideUp">
|
||
|
|
<div class="bg-white rounded-xl shadow-2xl border border-gray-200 overflow-hidden w-80">
|
||
|
|
<!-- 头部 -->
|
||
|
|
<div class="bg-gradient-to-r from-blue-500 to-blue-600 px-4 py-3 flex items-center justify-between">
|
||
|
|
<div class="flex items-center gap-2">
|
||
|
|
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"
|
||
|
|
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
||
|
|
</svg>
|
||
|
|
<span class="text-white font-medium">应用更新</span>
|
||
|
|
</div>
|
||
|
|
<button @click="dismissUpdate" class="text-white/70 hover:text-white transition-colors">
|
||
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
|
||
|
|
</svg>
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- 内容 -->
|
||
|
|
<div class="p-4">
|
||
|
|
<!-- 检查中 -->
|
||
|
|
<div v-if="status === 'checking'" class="flex items-center gap-3">
|
||
|
|
<div class="w-5 h-5 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" />
|
||
|
|
<span class="text-gray-600">正在检查更新...</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- 发现新版本 -->
|
||
|
|
<div v-if="status === 'available' && updateInfo" class="space-y-3">
|
||
|
|
<div class="flex items-center justify-between">
|
||
|
|
<span class="text-gray-500 text-sm">当前版本</span>
|
||
|
|
<span class="text-gray-700 font-mono text-sm">{{ currentVersion }}</span>
|
||
|
|
</div>
|
||
|
|
<div class="flex items-center justify-between">
|
||
|
|
<span class="text-gray-500 text-sm">最新版本</span>
|
||
|
|
<span class="text-green-600 font-mono text-sm font-medium">{{ updateInfo.version }}</span>
|
||
|
|
</div>
|
||
|
|
<p v-if="updateInfo.releaseNotes" class="text-gray-500 text-xs bg-gray-50 p-2 rounded-lg line-clamp-3">
|
||
|
|
{{ updateInfo.releaseNotes }}
|
||
|
|
</p>
|
||
|
|
<button @click="downloadUpdate"
|
||
|
|
class="w-full py-2 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-lg font-medium hover:from-blue-600 hover:to-blue-700 transition-all shadow-md hover:shadow-lg">
|
||
|
|
下载更新
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- 下载中 -->
|
||
|
|
<div v-if="status === 'downloading' && progress" class="space-y-3">
|
||
|
|
<div class="flex items-center justify-between text-sm">
|
||
|
|
<span class="text-gray-600">下载中...</span>
|
||
|
|
<span class="text-blue-600 font-medium">{{ progress.percent.toFixed(1) }}%</span>
|
||
|
|
</div>
|
||
|
|
<div class="h-2 bg-gray-200 rounded-full overflow-hidden">
|
||
|
|
<div class="h-full bg-gradient-to-r from-blue-500 to-blue-600 transition-all duration-300"
|
||
|
|
:style="{ width: `${progress.percent}%` }" />
|
||
|
|
</div>
|
||
|
|
<div class="flex items-center justify-between text-xs text-gray-500">
|
||
|
|
<span>{{ formatBytes(progress.transferred) }} / {{ formatBytes(progress.total) }}</span>
|
||
|
|
<span>{{ formatBytes(progress.bytesPerSecond) }}/s</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- 下载完成 -->
|
||
|
|
<div v-if="status === 'downloaded'" class="space-y-3">
|
||
|
|
<div class="flex items-center gap-2 text-green-600">
|
||
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M5 13l4 4L19 7" />
|
||
|
|
</svg>
|
||
|
|
<span class="font-medium">下载完成</span>
|
||
|
|
</div>
|
||
|
|
<p class="text-gray-500 text-sm">点击下方按钮重启应用以完成更新</p>
|
||
|
|
<button @click="installUpdate"
|
||
|
|
class="w-full py-2 bg-gradient-to-r from-green-500 to-green-600 text-white rounded-lg font-medium hover:from-green-600 hover:to-green-700 transition-all shadow-md hover:shadow-lg">
|
||
|
|
🚀 立即重启并安装
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- 错误 -->
|
||
|
|
<div v-if="status === 'error'" class="space-y-3">
|
||
|
|
<div class="flex items-center gap-2 text-red-600">
|
||
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"
|
||
|
|
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||
|
|
</svg>
|
||
|
|
<span class="font-medium">更新失败</span>
|
||
|
|
</div>
|
||
|
|
<p class="text-gray-500 text-sm">{{ error }}</p>
|
||
|
|
<button @click="checkForUpdates"
|
||
|
|
class="w-full py-2 bg-gray-100 text-gray-700 rounded-lg font-medium hover:bg-gray-200 transition-all">
|
||
|
|
重试
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script setup>
|
||
|
|
import { useUpdate } from '../hooks/useUpdate'
|
||
|
|
import { isElectron } from '../utils/electronBridge'
|
||
|
|
|
||
|
|
const isElectronEnv = isElectron()
|
||
|
|
|
||
|
|
const {
|
||
|
|
status,
|
||
|
|
updateInfo,
|
||
|
|
progress,
|
||
|
|
error,
|
||
|
|
currentVersion,
|
||
|
|
checkForUpdates,
|
||
|
|
downloadUpdate,
|
||
|
|
installUpdate,
|
||
|
|
dismissUpdate
|
||
|
|
} = useUpdate()
|
||
|
|
|
||
|
|
function formatBytes(bytes) {
|
||
|
|
if (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 `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`
|
||
|
|
}
|
||
|
|
</script>
|