shop-wx/doc/wot-design-uni/docs/.vitepress/theme/components/Banner.vue

272 lines
5.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import { ref, watch, computed, onMounted } from 'vue'
import { useBanner } from '../composables/banner'
const open = ref(false) // 默认不显示,避免闪烁
const BANNER_STORAGE_KEY = 'wot-banner-dismissed-time'
const TWENTY_FOUR_HOURS = 24 * 60 * 60 * 1000 // 24小时的毫秒数
// 使用 banner composable 获取远程数据
const { data: bannerData } = useBanner()
// 计算当前要显示的 banner 信息(取第一个)
const currentBanner = computed(() => {
return bannerData.value && bannerData.value.length > 0 ? bannerData.value[0] : null
})
/**
* 检查是否应该显示横幅
*/
function checkShouldShowBanner() {
if (typeof window === 'undefined') return true
const dismissedTime = localStorage.getItem(BANNER_STORAGE_KEY)
if (!dismissedTime) {
// 首次访问,添加 banner-show class 以显示横幅
document.documentElement.classList.add('banner-show')
return true
}
const dismissedTimestamp = parseInt(dismissedTime, 10)
const currentTime = Date.now()
// 如果超过24小时清除记录并显示横幅
if (currentTime - dismissedTimestamp > TWENTY_FOUR_HOURS) {
localStorage.removeItem(BANNER_STORAGE_KEY)
document.documentElement.classList.add('banner-show')
return true
}
// 未超过24小时确保不显示横幅
document.documentElement.classList.remove('banner-show')
return false
}
function dismiss() {
open.value = false
document.documentElement.classList.remove('banner-show')
// 存储当前时间戳到 localStorage
if (typeof window !== 'undefined') {
localStorage.setItem(BANNER_STORAGE_KEY, Date.now().toString())
}
}
// 监听 banner 数据变化,只有当有数据时才进行展示逻辑校验
watch(currentBanner, (newBanner) => {
if (newBanner) {
// 有 banner 数据时,检查是否应该显示
const shouldShow = checkShouldShowBanner()
open.value = shouldShow
} else {
// 没有 banner 数据时,不显示横幅
open.value = false
}
}, { immediate: true })
</script>
<template>
<div class="banner" v-if="open && currentBanner">
<div class="vt-banner-text">
<p class="vt-banner-title">{{ currentBanner.title }}</p>
<a
target="_blank"
class="vt-primary-action"
:href="currentBanner.link"
>
{{ currentBanner.action }}
</a>
</div>
<button aria-label="close" @click="dismiss">
<svg
class="close"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M18.9,10.9h-6v-6c0-0.6-0.4-1-1-1s-1,0.4-1,1v6h-6c-0.6,0-1,0.4-1,1s0.4,1,1,1h6v6c0,0.6,0.4,1,1,1s1-0.4,1-1v-6h6c0.6,0,1-0.4,1-1S19.5,10.9,18.9,10.9z"
/>
</svg>
</button>
<div class="glow glow--purple"></div>
<div class="glow glow--blue"></div>
</div>
</template>
<style>
html.banner-show {
--vp-layout-top-height: 64px;
}
</style>
<style scoped>
.banner {
position: fixed;
z-index: 100;
box-sizing: border-box;
top: 0;
left: 0;
right: 0;
height: var(--vp-layout-top-height, 64px);
line-height: var(--vp-layout-top-height, 64px);
text-align: center;
font-size: 18px;
font-weight: 600;
color: white;
background: #131A24;
display: none;
justify-content: center;
align-items: center;
overflow: hidden;
}
html.banner-show .banner {
display: flex;
}
.glow.glow--purple {
position: absolute;
bottom: -15%;
left: -75%;
width: 80%;
aspect-ratio: 1.5;
pointer-events: none;
border-radius: 100%;
background: linear-gradient(270deg, #7a23a1, #715ebde6 60% 80%, #bd34fe00);
filter: blur(15vw);
transform: none;
opacity: 0.6;
}
.glow.glow--blue {
position: absolute;
bottom: -15%;
right: -40%;
width: 80%;
aspect-ratio: 1.5;
pointer-events: none;
border-radius: 100%;
background: linear-gradient(180deg, #61d9ff, #0000);
filter: blur(15vw);
transform: none;
opacity: 0.3;
}
@media (min-width: 768px) {
.glow.glow--blue {
top: -15%;
right: -40%;
width: 80%;
}
.glow.glow--purple {
bottom: -15%;
left: -40%;
width: 80%;
}
}
@media (min-width: 1025px) {
.glow.glow--blue {
top: -15%;
right: -40%;
width: 80%;
}
.glow.glow--purple {
bottom: -15%;
left: -40%;
width: 80%;
}
}
button {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
padding: 5px 5px;
}
.close {
width: 32px;
height: 32px;
fill: #fff;
transform: rotate(45deg);
}
.vt-banner-text {
color: #fff;
font-size: 20px;
margin-left: 0.75rem;
}
.vt-banner-title {
display: inline-block;
background: linear-gradient(90deg, #bd34fe 0%, #41d1ff 100%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
color: transparent;
text-align: center;
font-size: 18px;
font-style: normal;
font-weight: 700;
line-height: normal;
}
.vt-primary-action {
background:
radial-gradient(140.35% 140.35% at 175% 94.74%, #2bfdd2, #bd34fe00),
radial-gradient(89.94% 89.94% at 18.42% 15.79%, #4d80f0, #41d1ff00);
color: #fff;
padding: 4px 8px;
border-radius: 5px;
font-size: 18px;
text-decoration: none;
margin: 0 0.75rem;
transition: all 0.2s ease-in-out;
}
@media (max-width: 1280px) {
.banner .vt-banner-text,
.banner .vt-primary-action {
font-size: 10px;
}
.vt-tagline {
display: none;
}
}
@media (max-width: 780px) {
.vt-tagline {
display: none;
}
.vt-coupon {
display: none;
}
.vt-primary-action {
margin: 0 10px;
padding: 4px 8px;
}
.vt-time-now {
display: none;
}
}
@media (max-width: 560px) {
.vt-place {
display: none;
}
}
</style>