- 移除只写不读的 verifyCodeCache(无界内存增长) - 大神收益走势由 20 次逐日/逐周/逐月查询改为 3 次 GROUP BY 聚合 - 派单池地理编码改为并行,避免串行等待外部接口 - 补齐 orders/activities/participants/user_notifications/finance/withdrawals/posts 组合索引;修复 withdrawals 使用不存在 created_at 的失效索引 - 数据库连接池支持 MYSQL_CONNECTION_LIMIT/MYSQL_QUEUE_LIMIT - 首页/弹窗定时器卸载清理,活动列表参与者按活动 id 查询,大厅组局按页取数,图片 lazy-load - 121 套件/792 用例全绿;已部署并验证新索引与 9ms 走势查询
253 行
5.8 KiB
Vue
253 行
5.8 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, onUnmounted } from 'vue';
|
|
import { ICONS } from '@/common/icons.js';
|
|
import { getCurrentThemeKey } from '@/common/theme.js';
|
|
import { getPlatformConfigs } from '@/common/api.js';
|
|
|
|
const visible = ref(false);
|
|
let popupTimer = null;
|
|
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 展示,保证首屏骨架屏转场顺畅
|
|
if (popupTimer) clearTimeout(popupTimer);
|
|
popupTimer = 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();
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
if (popupTimer) {
|
|
clearTimeout(popupTimer);
|
|
popupTimer = null;
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
.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>
|