Files
qnc-webview-v3/src/layouts/GlobalLayout.vue

167 lines
4.8 KiB
Vue
Raw Normal View History

2025-12-16 12:33:02 +08:00
<template>
<router-view />
<van-popup v-model:show="showPopup" round @click-overlay="onClickOverlay">
<div class="popup-content text-center p-8">
2026-05-13 14:43:38 +08:00
<div v-if="currentNotify?.title" class="text-lg font-bold mb-4">{{ currentNotify.title }}</div>
2025-12-16 12:33:02 +08:00
<div v-html="currentNotify?.content"></div>
<div class="flex justify-center">
2026-05-13 14:43:38 +08:00
<van-button type="primary" @click="onClosePopup" class="w-24">关闭</van-button>
2025-12-16 12:33:02 +08:00
</div>
</div>
</van-popup>
</template>
<script setup>
import { ref, onMounted, watch } from 'vue'
import { useRoute } from 'vue-router'
// 响应式变量
const showPopup = ref(false)
const notify = ref([])
const currentNotify = ref(null)
2026-05-13 14:43:38 +08:00
const pendingNotifyQueue = ref([])
const shownNotificationKeys = ref(new Set())
const SESSION_KEY = 'qnc_webview_shown_notifications'
2025-12-16 12:33:02 +08:00
// 获取当前页面路径
const route = useRoute()
// 获取通知数据
onMounted(() => {
2026-05-13 14:43:38 +08:00
loadShownNotificationKeys()
2025-12-16 12:33:02 +08:00
getGlobalNotify()
})
// 获取通知数据
const getGlobalNotify = async () => {
const { data, error } = await useApiFetch("/notification/list")
.get()
.json()
2026-05-13 14:43:38 +08:00
if (data.value && !error.value && data.value.code === 200 && data.value.data) {
notify.value = data.value.data.notifications ?? []
checkNotification()
2025-12-16 12:33:02 +08:00
}
}
2026-05-13 14:43:38 +08:00
/** 与后台「展示时间」一致:未配置或起止相同视为「全天」,任意时刻都算在范围内 */
2025-12-16 12:33:02 +08:00
const isWithinTimeRange = (startTime, endTime) => {
2026-05-13 14:43:38 +08:00
const s = (startTime || '').trim()
const e = (endTime || '').trim()
if (!s && !e) return true
if (s === e) return true
2025-12-16 12:33:02 +08:00
2026-05-13 14:43:38 +08:00
const now = new Date()
2025-12-16 12:33:02 +08:00
const currentMinutes = now.getHours() * 60 + now.getMinutes()
2026-05-13 14:43:38 +08:00
const toMinutes = (t) => {
const parts = t.split(':').map(Number)
const h = parts[0] ?? 0
const m = parts[1] ?? 0
return h * 60 + m
}
const startMinutes = toMinutes(s)
const endMinutes = toMinutes(e)
2025-12-16 12:33:02 +08:00
if (endMinutes < startMinutes) {
2026-05-13 14:43:38 +08:00
return currentMinutes >= startMinutes || currentMinutes <= endMinutes
2025-12-16 12:33:02 +08:00
}
return currentMinutes >= startMinutes && currentMinutes <= endMinutes
}
2026-05-13 14:43:38 +08:00
/** 当前路由是否与通知配置的页面一致 */
const matchesNotificationPage = (page) => {
const p = (page || '').trim()
const cur = route.path || ''
if (p === cur) return true
if (p === '/' && (cur === '/' || cur === '')) return true
if (cur.startsWith('/app/') && p === cur.replace('/app/', '/')) return true
if (p.startsWith('/app/') && cur === p.replace('/app/', '/')) return true
return false
}
const buildNotificationKey = (notification) => {
return [
notification.title ?? '',
notification.content ?? '',
notification.notificationPage ?? '',
notification.startDate ?? '',
notification.endDate ?? '',
notification.startTime ?? '',
notification.endTime ?? ''
].join('|')
}
const loadShownNotificationKeys = () => {
const raw = sessionStorage.getItem(SESSION_KEY)
if (!raw) return
try {
const parsed = JSON.parse(raw)
if (Array.isArray(parsed)) {
shownNotificationKeys.value = new Set(parsed)
}
} catch {
shownNotificationKeys.value = new Set()
}
}
const saveShownNotificationKeys = () => {
sessionStorage.setItem(SESSION_KEY, JSON.stringify([...shownNotificationKeys.value]))
}
const hasShownNotification = (notification) => {
return shownNotificationKeys.value.has(buildNotificationKey(notification))
}
const markNotificationShown = (notification) => {
shownNotificationKeys.value.add(buildNotificationKey(notification))
saveShownNotificationKeys()
}
const showNextNotification = () => {
const next = pendingNotifyQueue.value.shift()
if (!next) {
currentNotify.value = null
showPopup.value = false
return
}
currentNotify.value = next
showPopup.value = true
markNotificationShown(next)
}
2025-12-16 12:33:02 +08:00
// 检查通知并更新showPopup
const checkNotification = () => {
2026-05-13 14:43:38 +08:00
if (showPopup.value) return
const matchedNotifications = []
2025-12-16 12:33:02 +08:00
for (let notification of notify.value) {
const isTimeValid = isWithinTimeRange(notification.startTime, notification.endTime)
2026-05-13 14:43:38 +08:00
const isPageValid = matchesNotificationPage(notification.notificationPage)
const isShown = hasShownNotification(notification)
2025-12-16 12:33:02 +08:00
2026-05-13 14:43:38 +08:00
if (isTimeValid && isPageValid && !isShown) {
matchedNotifications.push(notification)
2025-12-16 12:33:02 +08:00
}
}
2026-05-13 14:43:38 +08:00
pendingNotifyQueue.value = matchedNotifications
showNextNotification()
2025-12-16 12:33:02 +08:00
}
// 监听路由变化
watch(() => route.path, () => {
2026-05-13 14:43:38 +08:00
checkNotification()
2025-12-16 12:33:02 +08:00
})
// 关闭弹窗
2026-05-13 14:43:38 +08:00
const onClosePopup = () => {
showNextNotification()
}
2025-12-16 12:33:02 +08:00
const onClickOverlay = () => {
2026-05-13 14:43:38 +08:00
onClosePopup()
2025-12-16 12:33:02 +08:00
}
</script>
<style lang="scss" scoped></style>