feat: 首页弹窗广告(HOME_POPUP 类型,每次冷启动弹一次,点击跳内部页面,tabBar 页自动 switchTab,失败 toast 兜底)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
bba2571f25
commit
32f3db7e85
+175
-2
@@ -125,12 +125,34 @@
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 首页弹窗广告:每次冷启动弹一次,取「首页弹窗」类型排序最前一条 -->
|
||||
<view
|
||||
v-if="homePopupVisible"
|
||||
class="popup-overlay"
|
||||
:class="{ visible: homePopupAnim }"
|
||||
@click="closeHomePopup"
|
||||
>
|
||||
<image
|
||||
class="popup-img"
|
||||
:src="homePopupData?.imageUrl || ''"
|
||||
mode="widthFix"
|
||||
@click.stop="onHomePopupClick"
|
||||
@error="onHomePopupImgError"
|
||||
/>
|
||||
<view class="popup-close" @click.stop="closeHomePopup">
|
||||
<view class="close-x">
|
||||
<view class="close-line line-a" />
|
||||
<view class="close-line line-b" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
|
||||
import { ref, computed, onMounted, nextTick } from 'vue'
|
||||
import { onShow, onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
|
||||
import { post } from '../../api/request'
|
||||
import { BASE_URL } from '../../config'
|
||||
import type { BannerItem } from '../../api/types'
|
||||
@@ -224,6 +246,71 @@ async function fetchSpots() {
|
||||
}
|
||||
}
|
||||
|
||||
/* ====== 首页弹窗广告(HOME_POPUP) ====== */
|
||||
// xuchao: 首页为 tabBar 页,实例与进程同生命周期,实例级标志即「每次冷启动弹一次」
|
||||
let homePopupHandled = false
|
||||
let homePopupFetching = false
|
||||
const homePopupData = ref<BannerItem | null>(null)
|
||||
const homePopupVisible = ref(false)
|
||||
const homePopupAnim = ref(false)
|
||||
|
||||
onShow(() => { checkHomePopup() })
|
||||
|
||||
/** 首页每次显示时检查;本次启动已弹过/请求中则跳过;接口失败不置标志,下次进首页重试 */
|
||||
async function checkHomePopup() {
|
||||
if (homePopupHandled || homePopupFetching) return
|
||||
homePopupFetching = true
|
||||
try {
|
||||
const list = await post<BannerItem[]>('/mpContent/queryList', { channel: 'pingxiang', type: 'HOME_POPUP' })
|
||||
homePopupHandled = true
|
||||
const item = (list || []).find(c => c.imageUrl)
|
||||
if (item) {
|
||||
homePopupData.value = item
|
||||
homePopupVisible.value = true
|
||||
nextTick(() => { homePopupAnim.value = true })
|
||||
}
|
||||
} catch {
|
||||
// 静默失败,下次进首页再试
|
||||
} finally {
|
||||
homePopupFetching = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 点击广告图:先关弹窗,再跳配置的内部页面 */
|
||||
function onHomePopupClick() {
|
||||
const url = homePopupData.value?.linkUrl
|
||||
homePopupAnim.value = false
|
||||
homePopupVisible.value = false
|
||||
if (url) navigateInnerPage(url)
|
||||
}
|
||||
|
||||
/** 关闭弹窗(离场动画结束后移除节点) */
|
||||
function closeHomePopup() {
|
||||
homePopupAnim.value = false
|
||||
setTimeout(() => { homePopupVisible.value = false }, 280)
|
||||
}
|
||||
|
||||
/** 图片加载失败直接移除弹窗 */
|
||||
function onHomePopupImgError() {
|
||||
homePopupVisible.value = false
|
||||
}
|
||||
|
||||
// xuchao: tabBar 页 navigateTo 会失败,必须走 switchTab
|
||||
const TAB_PAGE_PATHS = ['/pages/index/index', '/pages/qrcode/index', '/pages/profile/index']
|
||||
|
||||
/** xuchao: 小程序内部页面智能跳转(tabBar 走 switchTab,其余 navigateTo,失败兜底提示) */
|
||||
function navigateInnerPage(url: string) {
|
||||
const path = url.startsWith('/') ? url : `/${url}`
|
||||
if (TAB_PAGE_PATHS.includes(path.split('?')[0])) {
|
||||
uni.switchTab({ url: path })
|
||||
return
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: path,
|
||||
fail: () => uni.showToast({ title: '页面暂未开放', icon: 'none' }),
|
||||
})
|
||||
}
|
||||
|
||||
onShareAppMessage(() => ({
|
||||
title: '爱上萍乡旅游年卡',
|
||||
path: '/pages/index/index',
|
||||
@@ -823,4 +910,90 @@ function goMore() {
|
||||
.main-scroll {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
首页弹窗广告
|
||||
============================================ */
|
||||
.popup-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: $z-modal;
|
||||
background: rgba(0, 0, 0, 0.45);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
transition: opacity 280ms cubic-bezier(0.16, 1, 0.3, 1);
|
||||
|
||||
&.visible {
|
||||
opacity: 1;
|
||||
|
||||
.popup-img {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.popup-close {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 广告图:宽 600rpx 固定,高按原图比例(widthFix)自适应 */
|
||||
.popup-img {
|
||||
width: 600rpx;
|
||||
border-radius: $radius-2xl;
|
||||
overflow: hidden;
|
||||
transform: scale(0.88);
|
||||
opacity: 0;
|
||||
transition: transform 320ms cubic-bezier(0.16, 1, 0.3, 1),
|
||||
opacity 260ms cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.popup-close {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
border-radius: $radius-full;
|
||||
border: 2rpx solid rgba(255, 255, 255, 0.35);
|
||||
background: transparent;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: $spacing-4;
|
||||
flex-shrink: 0;
|
||||
opacity: 0;
|
||||
transform: scale(0.8);
|
||||
transition: opacity 320ms cubic-bezier(0.16, 1, 0.3, 1) 120ms,
|
||||
transform 320ms cubic-bezier(0.16, 1, 0.3, 1) 120ms;
|
||||
|
||||
&:active {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.close-x {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.close-line {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 28rpx;
|
||||
height: 2rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 2rpx;
|
||||
|
||||
&.line-a {
|
||||
transform: translate(-50%, -50%) rotate(45deg);
|
||||
}
|
||||
|
||||
&.line-b {
|
||||
transform: translate(-50%, -50%) rotate(-45deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user