文件
shanghaipeiwan/pwuniapp/components/HomePopupModal.vue

244 行
5.6 KiB
Vue

<template>
<view v-if="visible" class="popup-mask" @touchmove.stop.prevent>
<view class="popup-card" :class="{ 'theme-warm': currentThemeKey === 'warm' }">
<!-- 弹窗顶图海报 (可选) -->
<view class="popup-image-wrap" v-if="config.imageUrl">
<image class="popup-img" :src="resolvedImageUrl" mode="aspectFill" />
<view class="popup-image-overlay"></view>
</view>
<!-- 弹窗文字与操作主体 -->
<view class="popup-content">
<view class="popup-badge">官方活动福利</view>
<view class="popup-title">{{ config.title || '新人专享同城好礼' }}</view>
<view class="popup-sub">{{ config.subtitle || '多重优惠券与高颜值陪玩搭子已就绪' }}</view>
<view class="popup-actions">
<button class="btn-primary-action btn-active" @click="handleAction">
{{ config.btnText || '立即领券组局' }}
</button>
</view>
</view>
<!-- 悬浮关闭圆圈按钮 -->
<view class="popup-close-btn btn-active" @click="handleClose">
<image class="close-icon" :src="ICONS.close" mode="aspectFit" />
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
import { ICONS } from '@/common/icons.js';
import { getCurrentThemeKey } from '@/common/theme.js';
import { getPlatformConfigs } from '@/common/api.js';
const visible = ref(false);
const config = ref({
enabled: false,
title: '',
subtitle: '',
imageUrl: '',
link: '/pages/activity/list',
btnText: '立即参与',
frequency: 'once_daily'
});
const currentThemeKey = ref(getCurrentThemeKey());
const resolvedImageUrl = computed(() => {
const url = config.value.imageUrl;
if (!url) return '';
if (url.startsWith('/uploads/')) {
return `https://peiww.gynhc.com${url}`;
}
return url;
});
const checkAndDisplayPopup = async () => {
try {
const configs = await getPlatformConfigs();
if (!configs || !configs.home_popup) return;
const pConfig = configs.home_popup;
if (!pConfig.enabled) return;
config.value = { ...config.value, ...pConfig };
// 根据频次判断是否展示
const storageKey = 'peiwan_home_popup_last_shown';
const lastShown = uni.getStorageSync(storageKey);
const now = Date.now();
if (pConfig.frequency === 'once_ever') {
if (lastShown) return;
} else if (pConfig.frequency === 'once_daily') {
if (lastShown) {
const lastDate = new Date(Number(lastShown)).toDateString();
const nowDate = new Date(now).toDateString();
if (lastDate === nowDate) return;
}
}
// 延迟 600ms 展示,保证首屏骨架屏转场顺畅
setTimeout(() => {
visible.value = true;
uni.setStorageSync(storageKey, String(now));
}, 600);
} catch (err) {
console.warn('[Popup] 读取弹窗配置异常:', err);
}
};
const handleClose = () => {
visible.value = false;
};
const handleAction = () => {
visible.value = false;
const link = config.value.link || '/pages/activity/list';
if (link.startsWith('/pages/activity/list') || link.startsWith('/pages/order/list') || link.startsWith('/pages/user/index')) {
uni.switchTab({ url: link });
} else {
uni.navigateTo({ url: link });
}
};
onMounted(() => {
checkAndDisplayPopup();
});
</script>
<style scoped>
.popup-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(15, 23, 42, 0.7);
backdrop-filter: blur(4px);
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
padding: 0 40rpx;
animation: fadeIn 0.3s ease-out;
}
.popup-card {
position: relative;
width: 100%;
max-width: 600rpx;
background: #FFFFFF;
border-radius: 36rpx;
overflow: visible;
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.3);
animation: scaleUp 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}
.popup-image-wrap {
position: relative;
width: 100%;
height: 320rpx;
border-radius: 36rpx 36rpx 0 0;
overflow: hidden;
}
.popup-img {
width: 100%;
height: 100%;
}
.popup-image-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 80rpx;
background: linear-gradient(to top, #FFFFFF, transparent);
}
.popup-content {
padding: 36rpx 40rpx 44rpx;
text-align: center;
}
.popup-badge {
display: inline-block;
padding: 6rpx 20rpx;
background: rgba(99, 102, 241, 0.1);
color: #6366F1;
font-size: 22rpx;
font-weight: 600;
border-radius: 20rpx;
margin-bottom: 16rpx;
}
.popup-title {
font-size: 38rpx;
font-weight: 800;
color: #0F172A;
margin-bottom: 12rpx;
line-height: 1.3;
}
.popup-sub {
font-size: 26rpx;
color: #64748B;
line-height: 1.5;
margin-bottom: 36rpx;
}
.btn-primary-action {
width: 100%;
height: 88rpx;
background: linear-gradient(135deg, #6366F1 0%, #4F46E5 100%);
color: #FFFFFF;
font-size: 30rpx;
font-weight: 700;
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 10rpx 24rpx rgba(99, 102, 241, 0.35);
border: none;
}
.popup-close-btn {
position: absolute;
bottom: -96rpx;
left: 50%;
transform: translateX(-50%);
width: 72rpx;
height: 72rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.25);
border: 2rpx solid rgba(255, 255, 255, 0.5);
backdrop-filter: blur(8px);
display: flex;
align-items: center;
justify-content: center;
}
.close-icon {
width: 32rpx;
height: 32rpx;
}
.btn-active:active {
transform: scale(0.96);
opacity: 0.9;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes scaleUp {
from { opacity: 0; transform: scale(0.85); }
to { opacity: 1; transform: scale(1); }
}
</style>