| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125 |
- <template>
- <Transition name="slide-down">
- <div
- v-if="showUpdatePrompt"
- class="fixed top-4 left-4 right-4 md:left-auto md:right-4 md:w-96 z-50"
- >
- <div
- class="bg-white dark:bg-slate-800 rounded-2xl shadow-2xl border border-slate-200 dark:border-slate-700 p-4"
- >
- <div class="flex items-start gap-3">
- <!-- 图标 -->
- <div class="flex-shrink-0">
- <div
- class="w-10 h-10 rounded-full bg-blue-500/10 flex items-center justify-center"
- >
- <svg
- class="w-5 h-5 text-blue-500"
- fill="none"
- stroke="currentColor"
- viewBox="0 0 24 24"
- >
- <path
- stroke-linecap="round"
- stroke-linejoin="round"
- stroke-width="2"
- d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
- />
- </svg>
- </div>
- </div>
- <!-- 内容 -->
- <div class="flex-1 min-w-0">
- <h3 class="text-sm font-semibold text-slate-900 dark:text-white mb-1">
- 发现新版本
- </h3>
- <p class="text-xs text-slate-600 dark:text-slate-400">
- 应用有更新,点击刷新以获取最新功能
- </p>
- <!-- 按钮 -->
- <div class="mt-3 flex gap-2">
- <button
- @click="updateApp"
- :disabled="updating"
- class="flex-1 bg-blue-500 hover:bg-blue-600 disabled:bg-blue-400 text-white text-xs font-medium py-2 px-3 rounded-lg transition"
- >
- {{ updating ? '更新中...' : '立即更新' }}
- </button>
- <button
- @click="dismissUpdate"
- class="px-3 py-2 text-xs text-slate-600 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-700 rounded-lg transition"
- >
- 稍后
- </button>
- </div>
- </div>
- </div>
- </div>
- </div>
- </Transition>
- </template>
- <script setup lang="ts">
- import { ref, onMounted } from 'vue';
- import { useRegisterSW } from 'virtual:pwa-register/vue';
- const showUpdatePrompt = ref(false);
- const updating = ref(false);
- const { needRefresh, updateServiceWorker } = useRegisterSW({
- onRegistered(r) {
- console.log('Service Worker 已注册');
- // 每小时检查一次更新
- r && setInterval(() => {
- r.update();
- }, 60 * 60 * 1000);
- },
- onRegisterError(error) {
- console.error('Service Worker 注册失败:', error);
- },
- });
- // 更新应用
- const updateApp = async () => {
- updating.value = true;
- try {
- await updateServiceWorker(true);
- // 更新后刷新页面
- window.location.reload();
- } catch (error) {
- console.error('更新失败:', error);
- updating.value = false;
- }
- };
- // 关闭更新提示
- const dismissUpdate = () => {
- showUpdatePrompt.value = false;
- };
- // 监听更新
- onMounted(() => {
- if (needRefresh.value) {
- showUpdatePrompt.value = true;
- }
- });
- </script>
- <style scoped>
- .slide-down-enter-active,
- .slide-down-leave-active {
- transition: all 0.3s ease-out;
- }
- .slide-down-enter-from {
- opacity: 0;
- transform: translateY(-20px);
- }
- .slide-down-leave-to {
- opacity: 0;
- transform: translateY(-20px);
- }
- </style>
|