mirror of
https://github.com/Moe-Sakura/frontend.git
synced 2026-03-15 04:53:18 +08:00
refactor: update PWA registration and StatsCorner component for improved functionality
- Changed PWA registration type to 'autoUpdate' for immediate updates upon new version availability. - Removed obsolete busuanzi elements and replaced the data fetching method with a new API call for site statistics. - Simplified the UpdateToast component to trigger immediate updates without countdown, enhancing user experience.
This commit is contained in:
@@ -27,12 +27,6 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 隐藏的 busuanzi 元素(让 busuanzi 脚本更新) -->
|
|
||||||
<div id="busuanzi_container_site_pv" style="display: none !important; visibility: hidden !important; position: absolute; left: -9999px;">
|
|
||||||
<span id="busuanzi_value_site_pv" />
|
|
||||||
<span id="busuanzi_value_site_uv" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 左下角统计(Vue 控制显示) -->
|
<!-- 左下角统计(Vue 控制显示) -->
|
||||||
<div
|
<div
|
||||||
class="fixed bottom-4 left-4 z-40 transition-all duration-300"
|
class="fixed bottom-4 left-4 z-40 transition-all duration-300"
|
||||||
@@ -62,8 +56,6 @@ import { useStatsStore } from '@/stores/stats'
|
|||||||
|
|
||||||
const statsStore = useStatsStore()
|
const statsStore = useStatsStore()
|
||||||
const showStats = ref(false)
|
const showStats = ref(false)
|
||||||
let checkInterval: number | null = null
|
|
||||||
let observer: MutationObserver | null = null
|
|
||||||
|
|
||||||
// 计算属性 - 从 statsStore 获取状态
|
// 计算属性 - 从 statsStore 获取状态
|
||||||
const apiService = computed(() => statsStore.serviceStatuses.get('api'))
|
const apiService = computed(() => statsStore.serviceStatuses.get('api'))
|
||||||
@@ -100,48 +92,28 @@ const statusIconClass = computed(() => {
|
|||||||
return 'text-red-500'
|
return 'text-red-500'
|
||||||
})
|
})
|
||||||
|
|
||||||
// 检查不蒜子数据是否加载
|
// 使用新的 busuanzi API 获取统计
|
||||||
function checkBusuanziData() {
|
async function fetchBusuanziStats() {
|
||||||
const pvElement = document.getElementById('busuanzi_value_site_pv')
|
try {
|
||||||
const uvElement = document.getElementById('busuanzi_value_site_uv')
|
const res = await fetch('https://bsz.saop.cc/api', {
|
||||||
|
method: 'POST',
|
||||||
if (pvElement && uvElement) {
|
credentials: 'include',
|
||||||
const pvValue = parseInt(pvElement.textContent || '0', 10)
|
headers: { 'x-bsz-referer': window.location.href },
|
||||||
const uvValue = parseInt(uvElement.textContent || '0', 10)
|
})
|
||||||
|
|
||||||
if (pvValue > 0 && uvValue > 0) {
|
if (!res.ok) {return}
|
||||||
// 更新 statsStore
|
|
||||||
statsStore.updateVisitorStats(pvValue, uvValue)
|
const { success, data } = await res.json()
|
||||||
|
|
||||||
|
if (success && data) {
|
||||||
|
statsStore.updateVisitorStats(data.site_pv, data.site_uv)
|
||||||
showStats.value = true
|
showStats.value = true
|
||||||
|
|
||||||
if (checkInterval !== null) {
|
|
||||||
clearInterval(checkInterval)
|
|
||||||
checkInterval = null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (observer) {
|
|
||||||
observer.disconnect()
|
|
||||||
observer = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[Busuanzi] Failed to fetch stats:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用 MutationObserver 监听不蒜子元素的变化
|
|
||||||
function setupObserver() {
|
|
||||||
const pvElement = document.getElementById('busuanzi_value_site_pv')
|
|
||||||
const uvElement = document.getElementById('busuanzi_value_site_uv')
|
|
||||||
|
|
||||||
if (!pvElement || !uvElement) {return}
|
|
||||||
|
|
||||||
observer = new MutationObserver(() => {
|
|
||||||
checkBusuanziData()
|
|
||||||
})
|
|
||||||
|
|
||||||
observer.observe(pvElement, { childList: true, characterData: true, subtree: true })
|
|
||||||
observer.observe(uvElement, { childList: true, characterData: true, subtree: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听 visitorStats 变化,自动显示统计
|
// 监听 visitorStats 变化,自动显示统计
|
||||||
watch(() => statsStore.visitorStats.pv, (pv) => {
|
watch(() => statsStore.visitorStats.pv, (pv) => {
|
||||||
if (pv > 0) {
|
if (pv > 0) {
|
||||||
@@ -153,39 +125,13 @@ onMounted(() => {
|
|||||||
// 使用 statsStore 进行状态检测
|
// 使用 statsStore 进行状态检测
|
||||||
statsStore.startStatusCheck(30000)
|
statsStore.startStatusCheck(30000)
|
||||||
|
|
||||||
// 不蒜子统计
|
// 获取不蒜子统计
|
||||||
setupObserver()
|
void fetchBusuanziStats()
|
||||||
checkBusuanziData()
|
|
||||||
|
|
||||||
let attempts = 0
|
|
||||||
const maxAttempts = 40
|
|
||||||
|
|
||||||
checkInterval = window.setInterval(() => {
|
|
||||||
attempts++
|
|
||||||
checkBusuanziData()
|
|
||||||
|
|
||||||
if (showStats.value || attempts >= maxAttempts) {
|
|
||||||
if (checkInterval !== null) {
|
|
||||||
clearInterval(checkInterval)
|
|
||||||
checkInterval = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 500)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
// 停止状态检测
|
// 停止状态检测
|
||||||
statsStore.stopStatusCheck()
|
statsStore.stopStatusCheck()
|
||||||
|
|
||||||
if (checkInterval !== null) {
|
|
||||||
clearInterval(checkInterval)
|
|
||||||
checkInterval = null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (observer) {
|
|
||||||
observer.disconnect()
|
|
||||||
observer = null
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -13,49 +13,24 @@
|
|||||||
>
|
>
|
||||||
<!-- 图标 -->
|
<!-- 图标 -->
|
||||||
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-white/20 flex items-center justify-center">
|
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-white/20 flex items-center justify-center">
|
||||||
<RefreshCw :size="18" :class="{ 'animate-spin': isUpdating }" />
|
<RefreshCw :size="18" class="animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 文字内容 -->
|
<!-- 文字内容 -->
|
||||||
<div class="flex-1 min-w-0">
|
<div class="flex-1 min-w-0">
|
||||||
<p class="font-semibold text-sm">发现新版本</p>
|
<p class="font-semibold text-sm">发现新版本</p>
|
||||||
<p class="text-xs text-white/80 truncate">
|
<p class="text-xs text-white/80 truncate">正在更新...</p>
|
||||||
<template v-if="isUpdating">
|
|
||||||
正在更新...
|
|
||||||
</template>
|
|
||||||
<template v-else-if="countdown > 0">
|
|
||||||
{{ countdown }} 秒后自动更新...
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
准备更新...
|
|
||||||
</template>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 立即更新按钮 -->
|
|
||||||
<button
|
|
||||||
v-if="!isUpdating && countdown > 0"
|
|
||||||
class="flex-shrink-0 px-3 py-1.5 rounded-full bg-white/20 hover:bg-white/30 text-xs font-medium transition-colors"
|
|
||||||
@click="handleUpdate"
|
|
||||||
>
|
|
||||||
立即更新
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch, onUnmounted } from 'vue'
|
import { watch } from 'vue'
|
||||||
import { RefreshCw } from 'lucide-vue-next'
|
import { RefreshCw } from 'lucide-vue-next'
|
||||||
import { useRegisterSW } from 'virtual:pwa-register/vue'
|
import { useRegisterSW } from 'virtual:pwa-register/vue'
|
||||||
import { playNotification } from '@/composables/useSound'
|
import { playNotification } from '@/composables/useSound'
|
||||||
|
|
||||||
const UPDATE_COUNTDOWN = 5 // 秒
|
|
||||||
|
|
||||||
const countdown = ref(0)
|
|
||||||
const isUpdating = ref(false)
|
|
||||||
let countdownTimer: number | null = null
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
needRefresh,
|
needRefresh,
|
||||||
updateServiceWorker,
|
updateServiceWorker,
|
||||||
@@ -75,46 +50,12 @@ const {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// 当有更新时启动倒计时
|
// 当有更新时立即更新
|
||||||
watch(needRefresh, (need) => {
|
watch(needRefresh, (need) => {
|
||||||
if (need) {
|
if (need) {
|
||||||
playNotification()
|
playNotification()
|
||||||
startCountdown()
|
// 立即更新 SW 并刷新页面
|
||||||
}
|
void updateServiceWorker(true)
|
||||||
})
|
|
||||||
|
|
||||||
function startCountdown() {
|
|
||||||
countdown.value = UPDATE_COUNTDOWN
|
|
||||||
|
|
||||||
if (countdownTimer) {
|
|
||||||
clearInterval(countdownTimer)
|
|
||||||
}
|
|
||||||
|
|
||||||
countdownTimer = window.setInterval(() => {
|
|
||||||
countdown.value--
|
|
||||||
if (countdown.value <= 0) {
|
|
||||||
if (countdownTimer) {
|
|
||||||
clearInterval(countdownTimer)
|
|
||||||
countdownTimer = null
|
|
||||||
}
|
|
||||||
handleUpdate()
|
|
||||||
}
|
|
||||||
}, 1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleUpdate() {
|
|
||||||
if (countdownTimer) {
|
|
||||||
clearInterval(countdownTimer)
|
|
||||||
countdownTimer = null
|
|
||||||
}
|
|
||||||
isUpdating.value = true
|
|
||||||
// 更新 SW 并刷新页面
|
|
||||||
void updateServiceWorker(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
if (countdownTimer) {
|
|
||||||
clearInterval(countdownTimer)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export default defineConfig({
|
|||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
// PWA 配置
|
// PWA 配置
|
||||||
VitePWA({
|
VitePWA({
|
||||||
registerType: 'prompt', // 提示用户更新
|
registerType: 'autoUpdate', // 收到新版本立即更新
|
||||||
includeAssets: ['logo.svg', 'robots.txt'],
|
includeAssets: ['logo.svg', 'robots.txt'],
|
||||||
manifest: {
|
manifest: {
|
||||||
name: 'SearchGal - Galgame 聚合搜索',
|
name: 'SearchGal - Galgame 聚合搜索',
|
||||||
|
|||||||
Reference in New Issue
Block a user