hookehuyr

feat(广告): 添加广告遮罩组件并在欢迎页和首页集成

添加 AdOverlay 组件用于显示全屏广告,支持点击跳转和关闭功能
在 Welcome 和 Dashboard 页面集成该组件,并添加相关逻辑处理
......@@ -7,6 +7,7 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
AdOverlay: typeof import('./src/components/AdOverlay.vue')['default']
AppHeader: typeof import('./src/components/AppHeader.vue')['default']
BackToTop: typeof import('./src/components/BackToTop.vue')['default']
BottomNav: typeof import('./src/components/BottomNav.vue')['default']
......@@ -19,6 +20,7 @@ declare module 'vue' {
NutDialog: typeof import('@nutui/nutui-taro')['Dialog']
NutImagePreview: typeof import('@nutui/nutui-taro')['ImagePreview']
NutInput: typeof import('@nutui/nutui-taro')['Input']
NutOverlay: typeof import('@nutui/nutui-taro')['Overlay']
NutPicker: typeof import('@nutui/nutui-taro')['Picker']
NutPopup: typeof import('@nutui/nutui-taro')['Popup']
NutRow: typeof import('@nutui/nutui-taro')['Row']
......
<template>
<nut-overlay
v-model:visible="visible"
:z-index="9999"
:close-on-click-overlay="false"
:lock-scroll="true"
>
<view class="overlay-body">
<view class="overlay-content">
<!-- 广告图片 -->
<view class="ad-image-container" @click="handleAdClick">
<image
:src="adImageUrl"
mode="aspectFill"
class="ad-image"
alt="广告图片"
/>
</view>
<!-- 关闭按钮 -->
<view class="close-button" @click="handleClose">
<Close size="24" class="close-icon" />
</view>
</view>
</view>
</nut-overlay>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import Taro from '@tarojs/taro'
import { Close } from '@nutui/icons-vue-taro'
// Props
const props = defineProps({
// 广告图片URL
adImageUrl: {
type: String,
default: ''
},
// 点击跳转的页面路径
targetPage: {
type: String,
default: '/pages/Dashboard/index'
},
// 存储键名(用于区分不同的广告)
storageKey: {
type: String,
default: 'ad_overlay_shown'
}
})
// Emits
const emit = defineEmits(['close', 'click'])
// 响应式数据
const visible = ref(false)
/**
* 获取今天的日期字符串(YYYY-MM-DD格式)
* @returns {string} 今天的日期
*/
const getTodayDateString = () => {
const today = new Date()
const year = today.getFullYear()
const month = String(today.getMonth() + 1).padStart(2, '0')
const day = String(today.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
/**
* 检查今天是否已经显示过广告
* @returns {boolean} 是否已显示
*/
const hasShownToday = () => {
try {
const lastShownDate = Taro.getStorageSync(props.storageKey)
const today = getTodayDateString()
return lastShownDate === today
} catch (error) {
console.error('获取存储数据失败:', error)
return false
}
}
/**
* 记录今天已显示广告
*/
const markAsShownToday = () => {
try {
const today = getTodayDateString()
Taro.setStorageSync(props.storageKey, today)
} catch (error) {
console.error('保存存储数据失败:', error)
}
}
/**
* 显示广告遮罩
*/
const show = () => {
if (!hasShownToday()) {
visible.value = true
}
}
/**
* 隐藏广告遮罩
*/
const hide = () => {
visible.value = false
}
/**
* 处理广告图片点击事件
*/
const handleAdClick = () => {
// 记录已显示
markAsShownToday()
// 隐藏遮罩
hide()
// 跳转到目标页面
Taro.navigateTo({
url: props.targetPage
}).catch(error => {
console.error('页面跳转失败:', error)
Taro.showToast({
title: '页面跳转失败',
icon: 'none'
})
})
// 触发点击事件
emit('click', props.targetPage)
}
/**
* 处理关闭按钮点击事件
*/
const handleClose = () => {
// 记录已显示
markAsShownToday()
// 隐藏遮罩
hide()
// 触发关闭事件
emit('close')
}
// 暴露方法给父组件
defineExpose({
show,
hide,
hasShownToday
})
// 组件挂载时检查是否需要显示
onMounted(() => {
// 延迟一点显示,避免页面加载时的闪烁
setTimeout(() => {
show()
}, 500)
})
</script>
<style lang="less">
.overlay-body {
display: flex;
height: 100%;
align-items: center;
justify-content: center;
}
.overlay-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 80vw;
max-width: 600rpx;
}
.ad-image-container {
width: 100%;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.3);
cursor: pointer;
margin-bottom: 40rpx;
}
.ad-image {
width: 100%;
height: 50vh;
display: block;
border-radius: 16rpx;
}
.close-button {
width: 60rpx;
height: 60rpx;
// background-color: rgba(0, 0, 0, 0.6);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.close-icon {
color: white;
}
/* 点击效果 */
.ad-image-container:active {
transform: scale(0.98);
transition: transform 0.1s ease;
}
.close-button:active {
transform: scale(0.9);
transition: transform 0.1s ease;
}
</style>
......@@ -169,6 +169,15 @@
<BottomNav />
<!-- 广告遮罩层 -->
<AdOverlay
:ad-image-url="adObj.adImageUrl"
:target-page="adObj.targetPage"
:storage-key="adObj.storageKey"
@close="handleAdClose"
@click="handleAdClick"
/>
<!-- 图片预览 -->
<nut-image-preview
v-model:show="previewVisible"
......@@ -226,6 +235,7 @@ import TotalPointsDisplay from '@/components/TotalPointsDisplay.vue';
import PointsCollector from '@/components/PointsCollector.vue'
import WeRunAuth from '@/components/WeRunAuth.vue'
import RankingCard from '@/components/RankingCard.vue'
import AdOverlay from '@/components/AdOverlay.vue'
import { useMediaPreview } from '@/composables/useMediaPreview';
// 默认家庭封面图
const defaultFamilyCover = 'https://cdn.ipadbiz.cn/lls_prog/images/default-family-cover.png';
......@@ -277,6 +287,9 @@ const albumData = ref([
}
]);
// 广告遮罩层数据
const adObj = ref({})
/**
* 触发积分收集组件的一键收取
*/
......@@ -375,6 +388,28 @@ const handleGoToPointsRule = () => {
})
}
/**
* 处理广告遮罩层关闭事件
*/
const handleAdClose = () => {
console.log('广告遮罩层已关闭')
}
/**
* 处理广告遮罩层点击事件
* @param {string} targetPage - 跳转的目标页面
*/
const handleAdClick = (targetPage) => {
console.log('广告被点击,跳转到:', targetPage)
// 如果跳转路径是欢迎页和首页,不跳转直接关闭弹框
if (targetPage === '/pages/Dashboard/index' || targetPage === '/pages/Welcome/index') {
handleAdClose()
return
}
// 跳转到目标页面
Taro.navigateTo({ url: targetPage });
}
const family_id = ref('');
const totalFamilySteps = ref(0);
......@@ -406,6 +441,13 @@ useDidShow(async () => {
Taro.redirectTo({ url: '/pages/Welcome/index' });
return; // 直接返回,不执行后续逻辑
}
// TODO: 获取广告信息
adObj.value = {
adImageUrl: 'https://cdn.ipadbiz.cn/lls_prog/images/%E5%8D%97%E4%BA%AC%E8%B7%AF%E5%95%86%E5%9C%88.jpeg',
targetPage: '/pages/Dashboard/index',
storageKey: 'dashboard_ad_overlay',
}
})
useReady(async () => {
......
......@@ -132,6 +132,15 @@
</nut-row>
</template>
</nut-dialog>
<!-- 广告遮罩层 -->
<AdOverlay
:ad-image-url="adObj.adImageUrl"
:target-page="adObj.targetPage"
:storage-key="adObj.storageKey"
@close="handleAdClose"
@click="handleAdClick"
/>
</view>
</template>
......@@ -139,6 +148,7 @@
import { ref } from 'vue';
import Taro, { useDidShow } from '@tarojs/taro';
import BottomNav from '../../components/BottomNav.vue'; // 假设BottomNav组件已转换
import AdOverlay from '@/components/AdOverlay.vue'
// 获取接口信息
import { getUserProfileAPI } from '@/api/user';
......@@ -161,6 +171,31 @@ const navigateTo = (url) => {
Taro.navigateTo({ url });
};
// 广告遮罩层数据
const adObj = ref({})
/**
* 处理广告遮罩层关闭事件
*/
const handleAdClose = () => {
console.log('广告遮罩层已关闭')
}
/**
* 处理广告遮罩层点击事件
* @param {string} targetPage - 跳转的目标页面
*/
const handleAdClick = (targetPage) => {
console.log('广告被点击,跳转到:', targetPage)
// 如果跳转路径是欢迎页和首页,不跳转直接关闭弹框
if (targetPage === '/pages/Dashboard/index' || targetPage === '/pages/Welcome/index') {
handleAdClose()
return
}
// 跳转到目标页面
Taro.navigateTo({ url: targetPage });
}
useDidShow(async () => {
// 获取用户的个人信息
const { code, data } = await getUserProfileAPI();
......@@ -179,6 +214,13 @@ useDidShow(async () => {
hasProfile.value = userInfo.value.nickname && userInfo.value.birth_date && userInfo.value.wheelchair_needed !== null;
userAge.value = userInfo.value.birthday;
}
// TODO: 获取广告信息
adObj.value = {
adImageUrl: 'https://cdn.ipadbiz.cn/lls_prog/images/%E5%8D%97%E4%BA%AC%E8%B7%AF%E5%95%86%E5%9C%88.jpeg',
targetPage: '/pages/Dashboard/index',
storageKey: 'dashboard_ad_overlay',
}
});
const handleNavigate = (url) => {
......