Commit 58a3fa49 authored by lg's avatar lg

chore: init

parent e0d7053e
const version = 'v1'
// 915活动页面配置
export const act915Config = {
// 页面状态
states: {
NORMAL: 'normal',
INVITED: 'invited',
COMPLETED: 'completed'
},
// 图片配置
images: {
background: `Act915Page/${version}/Act915PageBg.png`,
goods1: `Act915Page/${version}/Act915PageGoods1.png`,
goods3: `Act915Page/${version}/Act915PageGoods3.png`,
goodslist2: `Act915Page/${version}/Act915PageGoodsList2.png`,
goodslist1: `Act915Page/${version}/Act915PageGoodsList1.png`,
inviteConBg: `Act915Page/${version}/Act915PageInviteConBg.png`,
inviteConBtn: `Act915Page/${version}/Act915PageInviteConBtn.png`,
inviteConStatusNo2: `Act915Page/${version}/Act915PageInviteConStatusNo2.png`,
inviteConStatusNo1: `Act915Page/${version}/Act915PageInviteConStatusNo1.png`,
inviteConStatusYes2: `Act915Page/${version}/Act915PageInviteConStatusYes2.png`,
inviteConStatusYes1: `Act915Page/${version}/Act915PageInviteConStatusYes1.png`,
inviteConImg: `Act915Page/${version}/Act915PageInviteConImg.png`,
lotteryBanner: `Act915Page/${version}/Act915PageLotteryBanner.png`,
banner: `Act915Page/${version}/Act915PageBanner.png`,
banner2: `Act915Page/${version}/Act915PageBanner.png`,
banner3: `Act915Page/${version}/Act915PageBanner.png`
},
// 文本配置
texts: {
inviteTitle: '邀请好友助力得XXXXX',
inviteBtnText: '立即邀请',
lotteryTitle: '抽奖活动',
goodsTitle: '商品展示'
},
// 活动配置
activity: {
maxInvites: 2, // 最大邀请人数
reward: 'XXXXX', // 奖励内容
startTime: '2024-09-15 00:00:00',
endTime: '2024-09-30 23:59:59'
}
}
// 获取图片URL的工具函数
export const getImageUrl = (imagePath) => {
return imagePath
}
// 加载状态样式
.loading-container {
width: 750rpx;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-color: #f5f5f5;
text {
font-size: 32rpx;
color: #666;
}
}
// 915活动页面样式
.act915page {
width: 750rpx;
height: 1624rpx;
left: 0rpx;
top: 0rpx;
position: absolute;
max-width: 100vw; // 确保不超过视口宽度
overflow-x: hidden; // 防止水平滚动
// 返回按钮样式
.nav-left {
position: fixed;
top: 100rpx;
left: 30rpx;
z-index: 999;
.back-btn {
width: 60rpx;
height: 60rpx;
}
}
.act915pagebg {
width: 750rpx;
height: 4238rpx;
left: 0rpx;
top: 0rpx;
position: absolute;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.act915pagegoods1 {
width: 685rpx;
height: 244rpx;
left: 32rpx;
top: 1370rpx;
position: absolute;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.act915pagegoods3-container {
width: 701rpx;
height: 370rpx;
left: 24rpx;
top: 1648rpx;
position: absolute;
.act915pagegoods3bg {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.act915pagegoods3products {
position: absolute;
top: 13rpx;
left: 10rpx;
width: 203rpx;
height: 286rpx;
display: flex;
gap: 14rpx; // 商品之间的间距
padding: 25rpx; // 容器内边距
pointer-events: none; // 让子元素可以点击
.act915pagegoods3product {
flex: 1;
height: 286rpx; // 固定高度,确保商品项有足够空间
position: relative;
pointer-events: auto; // 恢复点击事件
transition: all 0.3s ease;
min-width: 200rpx; // 最小宽度确保可点击性
&:hover {
opacity: 0.8;
transform: scale(1.02);
}
// 为不同位置的商品设置不同的点击区域
&.product-left {
// 左侧商品点击区域
}
&.product-center {
// 中间商品点击区域
}
&.product-right {
// 右侧商品点击区域
}
}
}
}
.act915pagegoodslist2 {
width: 701rpx;
height: 791rpx;
left: 24rpx;
top: 3369rpx;
position: absolute;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.act915pagegoodslist2-container {
width: 701rpx;
height: 791rpx;
left: 24rpx;
top: 3369rpx;
position: absolute;
.act915pagegoodslist2bg {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.act915pagegoodslist2products {
position: absolute;
top: 110rpx;
left: 10rpx;
width: 654rpx;
height: 316 * 2rpx;
display: grid;
grid-template-columns: 1fr 1fr 1fr; // 3列等宽
grid-template-rows: 1fr 1fr; // 2行等高
gap: 0rpx; // 网格间距
padding: 20rpx; // 容器内边距
pointer-events: none; // 让子元素可以点击
.act915pagegoodslist2product {
position: relative;
pointer-events: auto; // 恢复点击事件
transition: all 0.3s ease;
width: 204rpx;
height: 295rpx;
&:hover {
opacity: 0.8;
transform: scale(1.02);
}
// 为不同位置的商品设置不同的点击区域
&.product-1 {
// 第一行左侧商品
}
&.product-2 {
// 第一行中间商品
}
&.product-3 {
// 第一行右侧商品
}
&.product-4 {
// 第二行左侧商品
}
&.product-5 {
// 第二行中间商品
}
&.product-6 {
// 第二行右侧商品
}
}
}
}
.act915pagegoodslist1-container {
width: 701rpx;
height: 795rpx;
left: 24rpx;
top: 2546rpx;
position: absolute;
.act915pagegoodslist1bg {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.act915pagegoodslist1products {
position: absolute;
top: 113rpx;
left: 10rpx;
width: 654rpx;
height: 316 * 2rpx;
display: grid;
grid-template-columns: 1fr 1fr 1fr; // 3列等宽
grid-template-rows: 1fr 1fr; // 2行等高
gap: 0rpx; // 网格间距
padding: 20rpx; // 容器内边距
pointer-events: none; // 让子元素可以点击
.act915pagegoodslist1product {
position: relative;
pointer-events: auto; // 恢复点击事件
transition: all 0.3s ease;
width: 204rpx;
height: 295rpx;
&:hover {
opacity: 0.8;
transform: scale(1.02);
}
// 为不同位置的商品设置不同的点击区域
&.product-1 {
// 第一行左侧商品
}
&.product-2 {
// 第一行中间商品
}
&.product-3 {
// 第一行右侧商品
}
&.product-4 {
// 第二行左侧商品
}
&.product-5 {
// 第二行中间商品
}
&.product-6 {
// 第二行右侧商品
}
}
}
}
.act915pageinvitecon {
width: 686rpx;
height: 210rpx;
left: 31rpx;
top: 2045rpx;
position: absolute;
.act915pageinviteconbg {
width: 686rpx;
height: 210rpx;
left: 0rpx;
top: 0rpx;
position: absolute;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
// 重置button默认样式
border: none;
outline: none;
padding: 0;
margin: 0;
background-color: transparent;
// 移除button的默认样式
&::after {
border: none;
}
// 移除button的默认边框
&::before {
border: none;
}
}
.act915pageinviteconbtn {
width: 164rpx;
height: 63rpx;
left: 205rpx;
top: 107rpx;
position: absolute;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
transition: opacity 0.3s ease;
&:hover {
opacity: 0.8;
}
}
.act915pageinviteconstatus {
width: 148rpx;
height: 67rpx;
left: 28rpx;
top: 104rpx;
pointer-events: none;
position: absolute;
.act915pageinviteconstatusno2 {
width: 65rpx;
height: 66rpx;
left: 83rpx;
top: 0rpx;
position: absolute;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.act915pageinviteconstatusno1 {
width: 65rpx;
height: 66rpx;
left: 0rpx;
top: 1rpx;
position: absolute;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.act915pageinviteconstatusyes2 {
width: 64rpx;
height: 65rpx;
left: 84rpx;
top: 0rpx;
position: absolute;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.act915pageinviteconstatusyes1 {
width: 64rpx;
height: 65rpx;
left: 2rpx;
top: 1rpx;
position: absolute;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
}
.act915pageinvitecontitle {
width: 347rpx;
height: 31rpx;
left: 30rpx;
top: 36rpx;
position: absolute;
font-size: 32rpx;
line-height: 31rpx;
color: rgba(255, 255, 255, 1);
}
.act915pageinviteconimg {
width: 155rpx;
height: 155rpx;
left: 497rpx;
top: 28rpx;
position: absolute;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
}
.act915pagelotterybanner {
width: 685rpx;
height: 210rpx;
left: 32rpx;
top: 2294rpx;
position: absolute;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.mask-background {
width: 750rpx;
height: 459rpx;
left: 0rpx;
top: 2045rpx;
position: absolute;
.auth-button {
width: 750rpx;
height: 459rpx;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
border: none !important;
outline: none !important;
padding: 0;
margin: 0;
background-color: transparent !important;
}
.auth-button::after {
border: none !important;
outline: none !important;
border-radius: 0 !important;
}
}
.act915pagebanner {
width: 750rpx;
height: 1332rpx;
left: 0rpx;
top: 0rpx;
position: absolute;
.act915pagebanneritem {
width: 100%;
height: 100%;
display: block;
}
}
}
<template>
<view class="act915page" v-if="!loading">
<!-- 返回按钮 -->
<view class="nav-left">
<image class="back-btn" :src="$baseUrl + 'xingmaLab/1001/backBtn.png'" mode="aspectFit"
@click="handleBack" />
</view>
<span class="act915pagebg" :style="getBackgroundStyle()"></span>
<span class="act915pagegoods1" :style="{ backgroundImage: `url(${$baseUrl}${getImageUrl('goods1')})` }"
@click="handleGoods1Click"></span>
<!-- 商品3区域 - 分成3个点击区域 -->
<view class="act915pagegoods3-container">
<span class="act915pagegoods3bg"
:style="{ backgroundImage: `url(${$baseUrl}${getGoodsImage('goods3')})` }"></span>
<!-- 三个商品点击区域 -->
<view class="act915pagegoods3products">
<!-- 商品1 - 左侧 -->
<view class="act915pagegoods3product product-left" @click="handleGoods3Click(1)"></view>
<!-- 商品2 - 中间 -->
<view class="act915pagegoods3product product-center" @click="handleGoods3Click(2)"></view>
<!-- 商品3 - 右侧 -->
<view class="act915pagegoods3product product-right" @click="handleGoods3Click(3)"></view>
</view>
</view>
<!-- 商品列表2区域 - 分成6个点击区域 -->
<view class="act915pagegoodslist2-container">
<span class="act915pagegoodslist2bg"
:style="{ backgroundImage: `url(${$baseUrl}${getGoodsImage('goodslist2')})` }"></span>
<!-- 6个商品点击区域 -->
<view class="act915pagegoodslist2products">
<!-- 商品1 - 第一行左侧 -->
<view class="act915pagegoodslist2product product-1" @click="handleGoodsList2Click(1)"></view>
<!-- 商品2 - 第一行中间 -->
<view class="act915pagegoodslist2product product-2" @click="handleGoodsList2Click(2)"></view>
<!-- 商品3 - 第一行右侧 -->
<view class="act915pagegoodslist2product product-3" @click="handleGoodsList2Click(3)"></view>
<!-- 商品4 - 第二行左侧 -->
<view class="act915pagegoodslist2product product-4" @click="handleGoodsList2Click(4)"></view>
<!-- 商品5 - 第二行中间 -->
<view class="act915pagegoodslist2product product-5" @click="handleGoodsList2Click(5)"></view>
<!-- 商品6 - 第二行右侧 -->
<view class="act915pagegoodslist2product product-6" @click="handleGoodsList2Click(6)"></view>
</view>
</view>
<!-- 商品列表1区域 - 分成6个点击区域 -->
<view class="act915pagegoodslist1-container">
<span class="act915pagegoodslist1bg"
:style="{ backgroundImage: `url(${$baseUrl}${getGoodsImage('goodslist1')})` }"></span>
<!-- 6个商品点击区域 -->
<view class="act915pagegoodslist1products">
<!-- 商品1 - 第一行左侧 -->
<view class="act915pagegoodslist1product product-1" @click="handleGoodsList1Click(1)"></view>
<!-- 商品2 - 第一行中间 -->
<view class="act915pagegoodslist1product product-2" @click="handleGoodsList1Click(2)"></view>
<!-- 商品3 - 第一行右侧 -->
<view class="act915pagegoodslist1product product-3" @click="handleGoodsList1Click(3)"></view>
<!-- 商品4 - 第二行左侧 -->
<view class="act915pagegoodslist1product product-4" @click="handleGoodsList1Click(4)"></view>
<!-- 商品5 - 第二行中间 -->
<view class="act915pagegoodslist1product product-5" @click="handleGoodsList1Click(5)"></view>
<!-- 商品6 - 第二行右侧 -->
<view class="act915pagegoodslist1product product-6" @click="handleGoodsList1Click(6)"></view>
</view>
</view>
<div class="act915pageinvitecon">
<button class="act915pageinviteconbg" @click="handleInviteClick"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl('inviteConBg')})` }"
open-type="share"></button>
<div class="act915pageinviteconstatus">
<!-- 根据邀请人数动态显示状态 -->
<span v-if="nextRewardNeedCount == 1" class="act915pageinviteconstatusyes1"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl('inviteConStatusYes1')})` }"></span>
<span v-else class="act915pageinviteconstatusno1"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl('inviteConStatusNo1')})` }"></span>
<span v-if="nextRewardNeedCount == 0" class="act915pageinviteconstatusyes2"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl('inviteConStatusYes2')})` }"></span>
<span v-else class="act915pageinviteconstatusno2"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl('inviteConStatusNo2')})` }"></span>
</div>
</div>
<span class="act915pagelotterybanner"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl('lotteryBanner')})` }"
@click="handleLotteryClick"></span>
<!-- Banner轮播图 -->
<swiper class="act915pagebanner" :indicator-dots="true" :autoplay="true" :circular="true" :interval="3000"
:duration="500">
<swiper-item v-for="(banner, index) in bannerList" :key="index" @click="handleBannerClick(index)">
<image class="act915pagebanneritem" :src="$baseUrl + getBannerImage(banner)" mode="aspectFill"></image>
</swiper-item>
</swiper>
<!-- Full screen mask for new users -->
<view class="mask-background" v-if="showMask">
<button class="auth-button" open-type="getPhoneNumber" @getphonenumber="onGetPhoneNumber">
</button>
</view>
<!-- 抽奖成功弹窗 -->
<DrawSucModal :visible="showDrawSucModal" :award-name="currentAwardName" :award-image-url="currentAwardImageUrl"
@close="handleDrawSucModalClose" @happy-get="handleHappyGet" />
</view>
</template>
<script setup>
import { ref, onMounted, computed, getCurrentInstance } from 'vue'
import { fetchAct915JSON, getAct915Home } from '@/api/activity0915/index.js'
import { useHomeStore } from '@/stores/home'; // Assuming homeStore path
import { useUserStore } from '@/stores/user'; // Import userStore
import { invitationAssist } from '@/api/activity0915/index.js'; // Import invitationAssist
import { jump } from '@/utils/index.js'; // Import jump function
import DrawSucModal from '../lottery/components/drawSucModal/drawSucModal.vue'; // Import DrawSucModal if needed
import md from '@/md.js';
// 组件名称
defineOptions({
name: 'Act915Page'
})
// 定义分享函数
const getShareData = (type) => {
console.log('getShareData 被调用', type)
const { proxy } = getCurrentInstance()
const $baseUrl = proxy.$baseUrl
const shareTitle = '星妈会超级品牌周来啦!666积分兑换爱本纤纤3日装!'
const shareImage = $baseUrl + 'Act915Page/v2/act915ShareImg.png'
const sharePath = type.from == 'button' ? `/activity0915/act0915/act0915?invitationCode=${invitationCode.value}` : `/activity0915/act0915/act0915`
console.log('分享参数:', {
title: shareTitle,
imageUrl: shareImage,
path: sharePath,
invitationCode: invitationCode.value
})
return {
title: shareTitle,
path: sharePath,
imageUrl: shareImage
}
}
// 响应式数据
const currentState = ref('normal') // 页面状态
const config = ref({}) // 配置对象
const bannerList = ref([]) // Banner列表
const loading = ref(true) // 加载状态
// 邀请相关数据
const showMask = ref(true);
const showDrawSucModal = ref(false);
const currentAwardName = ref('');
const currentAwardImageUrl = ref('');
const invitationCode = ref('');
const invitationText = ref('');
const invitationImageUrl = ref('');
const invitedCount = ref(0);
const nextRewardNeedCount = ref(2);
const unclaimedPrizes = ref([]);
const receivedPrizes = ref([]);
const isLogin = ref(false);
const isAssist = ref(false);
// 获取store实例
const homeStore = useHomeStore();
const userStore = useUserStore();
// 获取背景样式的辅助函数
const getBackgroundStyle = () => {
const backgroundImage = getImageUrl('background')
if (backgroundImage) {
// 获取全局属性
const { proxy } = getCurrentInstance()
const $baseUrl = proxy.$baseUrl
return {
backgroundImage: `url(${$baseUrl}${backgroundImage})`
}
} else {
// 如果背景图片不存在,使用渐变背景
return {
background: `linear-gradient(to bottom, ${config.value?.background?.color}, ${config.value?.background?.color2})`
}
}
}
// 返回按钮处理
const handleBack = () => {
console.log('点击返回按钮')
// md.sensorComponentLogTake({
// xcxComponentClick: "true",
// pageName: "915专题活动首页",
// componentName: "返回按钮",
// componentContent: "返回"
// });
try {
uni.navigateBack({
delta: 1,
success: () => {
console.log('返回成功')
},
fail: () => {
// 如果返回失败,跳转到首页
uni.redirectTo({
url: '/pages/index/index'
})
}
})
} catch (error) {
console.log('返回失败:', error)
uni.redirectTo({
url: '/pages/index/index'
})
}
}
// 商品1区域点击处理
const handleGoods1Click = () => {
console.log('点击商品1区域')
md.sensorComponentLogTake({
xcxComponentClick: "true",
pageName: "915专题活动首页",
componentName: "商品单列图",
componentContent: config.value?.goods?.goods1?.title || "商品单列图"
});
jump(config.value?.goods?.goods1?.link)
}
// 获取图片URL的辅助函数
const getImageUrl = (imageKey) => {
// 根据不同的图片类型从对应的结构中获取
switch (imageKey) {
case 'background':
return config.value?.background?.image || ''
case 'goods1':
return config.value?.goods?.goods1?.image || ''
case 'inviteConBg':
return config.value?.invite?.image || ''
case 'inviteConStatusNo2':
return config.value?.invite?.inviteConStatusNo2Img || ''
case 'inviteConStatusNo1':
return config.value?.invite?.inviteConStatusNo1Img || ''
case 'inviteConStatusYes2':
return config.value?.invite?.inviteConStatusYes2Img || ''
case 'inviteConStatusYes1':
return config.value?.invite?.inviteConStatusYes1Img || ''
case 'lotteryBanner':
return config.value?.lottery?.image || ''
default:
// 兼容原有的结构
if (config.value?.images?.[imageKey]) {
return config.value.images[imageKey]
}
if (config.value?.config?.images?.[imageKey]) {
return config.value.config.images[imageKey]
}
if (config.value && config.value[imageKey]) {
return config.value[imageKey]
}
return ''
}
}
// 获取Banner图片的辅助函数
const getBannerImage = (banner) => {
// 如果是融合结构(包含 image 和 link)
if (banner && typeof banner === 'object' && banner.image) {
return banner.image
}
// 兼容原有的字符串结构
return banner || ''
}
// 获取商品图片的辅助函数
const getGoodsImage = (goodsKey) => {
// 从商品数据结构中获取图片
const goodsData = config.value.goods?.[goodsKey]
if (goodsData && goodsData.image) {
return goodsData.image
}
// 兼容原有的 config.images 结构
return getImageUrl(goodsKey)
}
// 获取配置数据
const fetchConfig = async () => {
try {
loading.value = true
const response = await fetchAct915JSON()
console.log('915活动配置数据:', response)
if (response && response.data) {
config.value = response.data
// 设置Banner列表
if (config.value.act915pagebanner && Array.isArray(config.value.act915pagebanner)) {
// 检查是否是融合结构(包含 image 和 link)
if (config.value.act915pagebanner[0] && typeof config.value.act915pagebanner[0] === 'object' && config.value.act915pagebanner[0].image) {
bannerList.value = config.value.act915pagebanner
} else {
// 兼容原有的字符串数组结构
bannerList.value = config.value.act915pagebanner
}
} else if (config.value.config?.images) {
bannerList.value = [
config.value.config.images.banner,
config.value.config.images.banner2,
config.value.config.images.banner3
].filter(Boolean)
}
}
} catch (error) {
} finally {
loading.value = false
}
}
// 获取915活动首页信息
const fetchAct915HomeData = async () => {
try {
console.log('开始请求 getAct915Home 接口...')
const response = await getAct915Home()
console.log('getAct915Home 接口返回结果:', response)
if (response && response.ok && response.data) {
const data = response.data
// 更新邀请相关数据
invitationCode.value = data.invitationCode
invitationText.value = data.invitationText
invitationImageUrl.value = data.invitationImageUrl
invitedCount.value = data.invitedCount || 0
nextRewardNeedCount.value = data.nextRewardNeedCount || 2
unclaimedPrizes.value = data.unclaimedPrizes || []
receivedPrizes.value = data.receivedPrizes || []
// 判断是否登录(根据notLogin字段或邀请码)
isLogin.value = !data.notLogin
// 检查是否有未领取的奖品需要弹窗
if (unclaimedPrizes.value?.length > 0) {
showPrizeModal()
}
// 检查URL参数中的邀请码
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
const options = currentPage.options;
const incomingCode = options.invitationCode;
if (incomingCode && !isAssist.value) {
console.log('检测到邀请码:', incomingCode)
// 根据登录状态决定助力处理方式
if (isLogin.value) {
showMask.value = false
// 已登录用户直接调用助力接口
console.log('用户已登录,直接调用助力接口')
await handleInvitationAssist(incomingCode);
} else {
// 未登录用户需要先授权
console.log('用户未登录,显示授权弹窗')
showMask.value = true
}
}
if (isLogin.value) {
showMask.value = false
}else{
showMask.value = true
}
}
return response
} catch (error) {
console.error('getAct915Home 接口请求失败:', error)
return null
}
}
// 显示奖品弹窗
const showPrizeModal = () => {
if (unclaimedPrizes.value.length > 0) {
// 计算总奖品数量
const totalQuantity = unclaimedPrizes.value.reduce((sum, prize) => sum + (prize.quantity || 1), 0)
const firstPrize = unclaimedPrizes.value[0]
currentAwardName.value = `${firstPrize.prizeName} × ${totalQuantity}`
currentAwardImageUrl.value = firstPrize.prizeImageUrl
showDrawSucModal.value = true
}
}
const handleLotteryClick = () => {
md.sensorComponentLogTake({
xcxComponentClick: "true",
pageName: "915专题活动首页",
componentName: "中部抽奖banner",
componentContent: "中部抽奖banner"
});
// 从配置中获取抽奖banner链接
const lotteryLink = config.value?.lottery?.link
if (lotteryLink) {
jump(lotteryLink)
} else {
uni.navigateTo({
url: '/activity0915/lottery/lottery'
})
}
}
// 商品3区域点击处理
const handleGoods3Click = (productId) => {
console.log('点击商品3区域商品:', productId)
// 从配置中获取商品信息
const goods3Data = config.value.goods?.goods3
const goods3List = goods3Data?.items || []
const product = goods3List.find(item => item.id === productId)
// 添加埋点
md.sensorComponentLogTake({
xcxComponentClick: "true",
pageName: "915专题活动首页",
componentName: "商品三列图",
componentContent: product?.title || `商品三列图-商品${productId}`
});
if (product && product.link) {
// 直接处理跳转链接
jump(product.link)
} else {
// 使用默认处理
// uni.showToast({
// title: `商品${productId}暂无跳转链接`,
// icon: 'none'
// })
}
}
// 商品列表1区域点击处理
const handleGoodsList1Click = (productId) => {
console.log('点击商品列表1区域商品:', productId)
// 从配置中获取商品信息
const goodsList1Data = config.value.goods?.goodslist1
const goodsList1 = goodsList1Data?.items || []
const product = goodsList1.find(item => item.id === productId)
// 添加埋点
md.sensorComponentLogTake({
xcxComponentClick: "true",
pageName: "915专题活动首页",
componentName: "爆品不错过商品专区",
componentContent: product?.title || `爆品不错过商品专区-商品${productId}`
});
if (product && product.link) {
// 直接处理跳转链接
jump(product.link)
} else {
// // 使用默认处理
// uni.showToast({
// title: `商品列表1-商品${productId}暂无跳转链接`,
// icon: 'none'
// })
}
}
// 商品列表2区域点击处理
const handleGoodsList2Click = (productId) => {
console.log('点击商品列表2区域商品:', productId)
// 从配置中获取商品信息
const goodsList2Data = config.value.goods?.goodslist2
const goodsList2 = goodsList2Data?.items || []
const product = goodsList2.find(item => item.id === productId)
// 添加埋点
md.sensorComponentLogTake({
xcxComponentClick: "true",
pageName: "915专题活动首页",
componentName: "好物等你来商品专区",
componentContent: product?.title || `好物等你来商品专区-商品${productId}`
});
if (product && product.link) {
// 直接处理跳转链接
jump(product.link)
} else {
// 使用默认处理
// uni.showToast({
// title: `商品列表2-商品${productId}暂无跳转链接`,
// icon: 'none'
// })
}
}
// Banner点击处理
const handleBannerClick = (index) => {
console.log('点击Banner:', index)
// 获取当前Banner数据
const currentBanner = bannerList.value[index]
// 如果是融合结构(包含 image 和 link)
if (currentBanner && typeof currentBanner === 'object' && currentBanner.link) {
jump(currentBanner.link)
} else {
// 兼容原有的分离结构
const bannerLinks = config.value.links?.banner
if (bannerLinks && bannerLinks[index]) {
jump(bannerLinks[index])
} else {
// uni.showToast({
// title: `Banner ${index + 1} 暂无跳转链接`,
// icon: 'none'
// })
}
}
}
// 处理邀请助力的通用函数
const handleInvitationAssist = async (incomingCode) => {
try {
console.log('处理助力邀请码:', incomingCode)
const unionId = uni.getStorageSync('unionId');
const res = await invitationAssist(incomingCode, unionId);
if (res && res.success) {
uni.showToast({
title: '助力成功',
icon: 'none',
duration: 2000
});
isAssist.value = true;
// 助力成功后重新获取数据
await fetchAct915HomeData();
return Promise.resolve();
} else {
uni.showToast({
title: res.message,
icon: 'none',
duration: 2000
});
return Promise.reject();
}
} catch (error) {
console.error('助力请求失败:', error)
uni.showToast({
title: error.message,
icon: 'none',
duration: 2000
});
return Promise.reject();
}
}
// 邀请按钮点击处理
const handleInviteClick = () => {
md.sensorComponentLogTake({
xcxComponentClick: "true",
pageName: "915专题活动首页",
componentName: "邀请得好礼",
componentContent: "立即邀请"
});
console.log('点击邀请按钮')
// 检查是否登录
if (!isLogin.value) {
// 未登录用户需要先授权
showMask.value = true
return
}
}
// 页面初始化逻辑
const initPage = async () => {
console.log('开始初始化页面...')
// 获取页面配置
await fetchConfig()
md.sensorComponentLogTake({
xcxComponentExposure: "true",
pageName: "915专题活动首页",
componentName: "banner头图",
componentContent: "banner头图"
});
md.sensorComponentLogTake({
xcxComponentExposure: "true",
pageName: "915专题活动首页",
componentName: "商品单列图",
componentContent: config.value?.goods?.goods1?.title || "商品单列图"
});
// 商品三列图埋点 - 遍历每个商品
if (config.value?.goods?.goods3?.items) {
config.value.goods.goods3.items.forEach((item, index) => {
md.sensorComponentLogTake({
xcxComponentExposure: "true",
pageName: "915专题活动首页",
componentName: "商品三列图",
componentContent: item.title || `商品三列图-商品${index + 1}`
});
});
}
md.sensorComponentLogTake({
xcxComponentExposure: "true",
pageName: "915专题活动首页",
componentName: "邀请得好礼",
componentContent: "立即邀请"
});
md.sensorComponentLogTake({
xcxComponentExposure: "true",
pageName: "915专题活动首页",
componentName: "中部抽奖banner",
componentContent: "中部抽奖banner"
});
// 爆品不错过商品专区埋点 - 遍历每个商品
if (config.value?.goods?.goodslist1?.items) {
config.value.goods.goodslist1.items.forEach((item, index) => {
md.sensorComponentLogTake({
xcxComponentExposure: "true",
pageName: "915专题活动首页",
componentName: "爆品不错过商品专区",
componentContent: item.title || `爆品不错过商品专区-商品${index + 1}`
});
});
}
// 好物等你来商品专区埋点 - 遍历每个商品
if (config.value?.goods?.goodslist2?.items) {
config.value.goods.goodslist2.items.forEach((item, index) => {
md.sensorComponentLogTake({
xcxComponentExposure: "true",
pageName: "915专题活动首页",
componentName: "好物等你来商品专区",
componentContent: item.title || `好物等你来商品专区-商品${index + 1}`
});
});
}
console.log('Banner列表:', bannerList.value)
console.log('配置数据:', config.value)
// 请求 getAct915Home 接口(包含助力逻辑处理)
await fetchAct915HomeData();
}
// 生命周期
onMounted(async () => {
console.log('915活动页面已加载')
// 先进行自动登录获取基础信息,登录成功后执行页面初始化
try {
console.log('开始执行 normalAutoLogin...')
await userStore.normalAutoLogin(initPage)
console.log('normalAutoLogin 完成')
} catch (error) {
console.error('normalAutoLogin 失败:', error)
// 即使登录失败也执行页面初始化
await initPage()
}
// 先隐藏朋友圈分享
uni.hideShareMenu({
menus: ['shareTimeline']
})
// 显示分享菜单
uni.showShareMenu({
withShareTicket: true,
menus: ['shareAppMessage']
});
});
// Phone authorization
const onGetPhoneNumber = async (e) => {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
const options = currentPage.options;
const incomingCode = options.invitationCode;
if (e.detail.errMsg === 'getPhoneNumber:ok') {
try {
// console.warn('onGetPhoneNumber', e.detail)
// 隐藏授权弹窗
userStore.phoneCallback(e.detail, null, null, async () => {
showMask.value = false;
// 重新获取用户信息(登录后)
await fetchAct915HomeData();
console.warn('授权后重新获取用户信息')
// 检查是否有邀请码需要处理助力
// if (incomingCode) {
// // 使用统一的助力处理函数
// await handleInvitationAssist(incomingCode);
// }
}, {
wxLoginBusinessDTO: {
activityUserInvite915Code: incomingCode
}
})
} catch (error) {
console.error('授权后处理失败:', error)
uni.showToast({ title: '授权失败', icon: 'none' });
}
} else {
uni.showToast({ title: '授权失败', icon: 'none' });
}
};
// 处理蒙层点击
const handleMaskClick = () => {
showMask.value = false;
};
// 抽奖成功弹窗关闭处理
const handleDrawSucModalClose = () => {
console.log('关闭抽奖成功弹窗');
showDrawSucModal.value = false;
};
// 开心领取处理
const handleHappyGet = () => {
console.log('点击开心领取');
showDrawSucModal.value = false;
// uni.showToast({
// title: '奖品领取成功',
// icon: 'success'
// });
};
// 暴露给 Options API 使用
defineExpose({
getShareData
})
</script>
<style lang="less" scoped>
@import 'index.less';
</style>
<script>
// 微信小程序分享配置 - 使用 Options API
export default {
// 自定义分享内容
onShareAppMessage(type) {
console.log('onShareAppMessage 被调用', type)
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
const pageInstance = currentPage.$vm
return pageInstance.getShareData(type)
},
// 朋友圈分享已禁用
// onShareTimeline() {
// console.log('onShareTimeline 被调用')
// // 朋友圈分享功能已禁用
// return null
// }
}
</script>
# 抽奖页面弹窗组件
## 组件结构
```
activity0915/lottery/
├── lottery.vue # 主抽奖页面
├── config.js # 主页面配置
├── lotterypage.less # 主页面样式
└── components/ # 弹窗组件目录
├── ruleModal/ # 规则弹窗
│ ├── ruleModal.vue
│ ├── config.js
│ └── rulemodal.less
├── drawSucModal/ # 抽奖成功弹窗
│ ├── drawSucModal.vue
│ ├── config.js
│ └── drawsucmodal.less
└── drawFailModal/ # 抽奖失败弹窗
├── drawFailModal.vue
├── config.js
└── drawfailmodal.less
```
## 组件说明
### 1. RuleModal (规则弹窗)
- **功能**: 显示活动规则说明
- **Props**:
- `visible`: Boolean - 控制弹窗显示/隐藏
- **Events**:
- `close`: 关闭弹窗事件
### 2. DrawSucModal (抽奖成功弹窗)
- **功能**: 显示抽奖成功和奖品信息
- **Props**:
- `visible`: Boolean - 控制弹窗显示/隐藏
- `awardName`: String - 中奖奖品名称
- **Events**:
- `close`: 关闭弹窗事件
- `happy-get`: 点击"开心领取"按钮事件
### 3. DrawFailModal (抽奖失败弹窗)
- **功能**: 显示抽奖失败提示
- **Props**:
- `visible`: Boolean - 控制弹窗显示/隐藏
- **Events**:
- `close`: 关闭弹窗事件
- `i-know`: 点击"我知道了"按钮事件
## 使用方法
### 在主页面中引入组件
```vue
<template>
<view class="lotterypage">
<!-- 主页面内容 -->
<!-- 规则弹窗 -->
<RuleModal
:visible="showRuleModal"
@close="handleRuleModalClose"
/>
<!-- 抽奖成功弹窗 -->
<DrawSucModal
:visible="showDrawSucModal"
:award-name="currentAwardName"
@close="handleDrawSucModalClose"
@happy-get="handleHappyGet"
/>
<!-- 抽奖失败弹窗 -->
<DrawFailModal
:visible="showDrawFailModal"
@close="handleDrawFailModalClose"
@i-know="handleIKnow"
/>
</view>
</template>
<script setup>
import RuleModal from './components/ruleModal/ruleModal.vue'
import DrawSucModal from './components/drawSucModal/drawSucModal.vue'
import DrawFailModal from './components/drawFailModal/drawFailModal.vue'
// 弹窗状态
const showRuleModal = ref(false)
const showDrawSucModal = ref(false)
const showDrawFailModal = ref(false)
const currentAwardName = ref('')
// 事件处理
const handleRuleModalClose = () => {
showRuleModal.value = false
}
const handleDrawSucModalClose = () => {
showDrawSucModal.value = false
}
const handleHappyGet = () => {
showDrawSucModal.value = false
// 处理奖品领取逻辑
}
const handleDrawFailModalClose = () => {
showDrawFailModal.value = false
}
const handleIKnow = () => {
showDrawFailModal.value = false
// 处理失败确认逻辑
}
</script>
```
## 抽奖逻辑
抽奖页面会根据奖品配置中的概率进行随机抽奖:
```javascript
// 根据奖品概率判断是否中奖
const isWin = Math.random() < award.probability
if (isWin) {
// 中奖 - 显示成功弹窗
currentAwardName.value = award.name
showDrawSucModal.value = true
} else {
// 未中奖 - 显示失败弹窗
showDrawFailModal.value = true
}
```
## 样式说明
所有弹窗组件都使用绝对定位,覆盖在主页面之上。弹窗背景使用半透明遮罩,确保用户注意力集中在弹窗内容上。
每个弹窗组件都有独立的样式文件,可以根据设计需求进行调整。
const version = 'v1'
// 抽奖失败弹窗配置
export const drawFailModalConfig = {
// 图片配置
images: {
background: `Act915Component/DrawFailModal/${version}/DrawFailModalBg.png`,
iKnowBtn: `Act915Component/DrawFailModal/${version}/DrawFailModalBgIKnowBtn.png`,
closeBtn: `Act915Component/DrawFailModal/${version}/DrawFailModalBgCloseBtn.png`
},
// 文本配置
texts: {
iKnowButton: '我知道了',
closeButton: '关闭'
},
// 弹窗配置
modal: {
width: 550,
height: 396,
animationDuration: 300
}
}
// 获取图片URL的工具函数
export const getImageUrl = (imagePath) => {
return imagePath
}
<template>
<view class="drawfailmodal" v-if="visible">
<!-- 蒙层 -->
<view class="drawfailmodal-mask" @click="handleCloseClick"></view>
<!-- 弹窗内容 -->
<view class="drawfailmodal-content">
<span class="drawfailmodalbg"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.background)})` }"></span>
<span class="drawfailmodalbgiknowbtn"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.iKnowBtn)})` }"
@click="handleIKnowClick"></span>
<span class="drawfailmodalbgclosebtn"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.closeBtn)})` }"
@click="handleCloseClick"></span>
</view>
</view>
</template>
<script setup>
import { drawFailModalConfig, getImageUrl } from './config.js'
// 组件名称
defineOptions({
name: 'DrawFailModal'
})
// Props
defineProps({
visible: {
type: Boolean,
default: false
}
})
// Emits
const emit = defineEmits(['close', 'i-know'])
// 配置对象
const config = drawFailModalConfig
// 我知道了按钮点击处理
const handleIKnowClick = () => {
console.log('点击我知道了按钮')
emit('i-know')
}
// 关闭按钮点击处理
const handleCloseClick = () => {
console.log('关闭抽奖失败弹窗')
emit('close')
}
</script>
<style lang="less" scoped>
@import './drawfailmodal.less';
</style>
@import "@/common.less";
.drawfailmodal {
width: 100vw;
height: 100vh;
left: 0rpx;
top: 0rpx;
position: fixed;
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
// 蒙层
.drawfailmodal-mask {
width: 100%;
height: 100%;
position: absolute;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1;
}
// 弹窗内容
.drawfailmodal-content {
width: 550rpx;
height: 396rpx;
position: relative;
z-index: 2;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.drawfailmodalbg {
width: 550rpx;
height: 396rpx;
position: absolute;
top: 0;
left: 0;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
}
.drawfailmodalbgiknowbtn {
width: 465rpx;
height: 97rpx;
position: absolute;
top: 243rpx;
left: 42rpx;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
transition: opacity 0.3s ease;
&:hover {
opacity: 0.8;
}
}
.drawfailmodalbgclosebtn {
width: 59rpx;
height: 59rpx;
position: absolute;
top: 437rpx;
left: 245rpx;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
transition: opacity 0.3s ease;
&:hover {
opacity: 0.8;
}
}
}
const version = 'v1'
// 抽奖成功弹窗配置
export const drawSucModalConfig = {
// 图片配置
images: {
background: `Act915Component/DrawSucModal/${version}/DrawSucModalBg.png`,
happyGetBtn: `Act915Component/DrawSucModal/${version}/DrawSucModalHappyGetBtn.png`,
closeBtn: `Act915Component/DrawSucModal/${version}/DrawSucModalCloseBtn.png`
},
// 文本配置
texts: {
defaultAwardName: '奖品名称奖品名称',
happyGetButton: '开心领取',
closeButton: '关闭'
},
// 弹窗配置
modal: {
width: 550,
height: 616,
animationDuration: 300
}
}
// 获取图片URL的工具函数
export const getImageUrl = (imagePath) => {
return imagePath
}
<template>
<view class="drawsucmodal" v-if="visible">
<!-- 蒙层 -->
<view class="drawsucmodal-mask" @click="handleCloseClick"></view>
<!-- 弹窗内容 -->
<view class="drawsucmodal-content">
<span class="drawsucmodalbg"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.background)})` }"></span>
<image class="drawsucmodalawardimg" :src="`${$baseUrl}${awardImageUrl}`" mode="aspectFit"
v-if="awardImageUrl" @error="console.log('奖品图片加载失败')"></image>
<span class="drawsucmodalawardname">{{ awardName }}</span>
<span class="drawsucmodalhappygetbtn"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.happyGetBtn)})` }"
@click="handleHappyGetClick"></span>
<span class="drawsucmodalclosebtn"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.closeBtn)})` }"
@click="handleCloseClick"></span>
</view>
</view>
</template>
<script setup>
import { ref, getCurrentInstance } from 'vue'
import { drawSucModalConfig, getImageUrl } from './config.js'
// 组件名称
defineOptions({
name: 'DrawSucModal'
})
// Props
defineProps({
visible: {
type: Boolean,
default: false
},
awardName: {
type: String,
default: ''
},
awardImageUrl: {
type: String,
default: ''
}
})
// Emits
const emit = defineEmits(['close', 'happy-get'])
// 配置对象
const config = drawSucModalConfig
// 获取$baseUrl
const instance = getCurrentInstance()
const $baseUrl = instance.proxy.$baseUrl
// 开心领取按钮点击处理
const handleHappyGetClick = () => {
console.log('点击开心领取按钮')
emit('happy-get')
}
// 关闭按钮点击处理
const handleCloseClick = () => {
console.log('关闭抽奖成功弹窗')
emit('close')
}
</script>
<style lang="less" scoped>
@import './drawsucmodal.less';
</style>
@import "@/common.less";
.drawsucmodal {
width: 100vw;
height: 100vh;
left: 0rpx;
top: 0rpx;
position: fixed;
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
// 蒙层
.drawsucmodal-mask {
width: 100%;
height: 100%;
position: absolute;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1;
}
// 弹窗内容
.drawsucmodal-content {
width: 550rpx;
height: 616rpx;
position: relative;
z-index: 2;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.drawsucmodalbg {
width: 550rpx;
height: 616rpx;
position: absolute;
top: 0;
left: 0;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
}
.drawsucmodalawardimg {
width: 180rpx;
height: 180rpx;
position: absolute;
top: 150rpx;
left: 185rpx;
object-fit: contain;
z-index: 3;
}
.drawsucmodalawardname {
width: 348rpx;
height: 30rpx;
position: absolute;
top: 365rpx;
left: 101rpx;
font-size: 29rpx;
line-height: 30rpx;
color: rgba(0, 0, 0, 1);
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.drawsucmodalhappygetbtn {
width: 465rpx;
height: 97rpx;
position: absolute;
top: 434rpx;
left: 42rpx;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
transition: opacity 0.3s ease;
&:hover {
opacity: 0.8;
}
}
.drawsucmodalclosebtn {
width: 60rpx;
height: 59rpx;
position: absolute;
top: 660rpx;
left: 245rpx;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
transition: opacity 0.3s ease;
&:hover {
opacity: 0.8;
}
}
}
const version = 'v1'
// 活动规则弹窗配置
export const act915RuleModalConfig = {
// 图片配置
images: {
background: `Act915Component/Act915RuleModal/${version}/Act915RuleModalBg.png`,
textImg: `Act915Component/Act915RuleModal/${version}/Act915RuleModalTextImg.png`,
closeBtn: `Act915Component/Act915RuleModal/${version}/Act915RuleModalCloseBtn.png`
},
// 文本配置
texts: {
closeButton: '关闭',
ruleTitle: '活动规则'
},
// 弹窗配置
modal: {
width: 550,
height: 667,
animationDuration: 300
}
}
// 获取图片URL的工具函数
export const getImageUrl = (imagePath) => {
return imagePath
}
<template>
<view class="rulemodal" v-if="visible">
<!-- 蒙层 -->
<view class="rulemodal-mask" @click="handleCloseClick"></view>
<!-- 弹窗内容 -->
<view class="rulemodal-content">
<span class="rulemodalbg"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.background)})` }"></span>
<!-- 规则内容区域 - 支持滚动 -->
<view class="rulemodal-text-container">
<image class="rulemodaltextimg"
:src="`${$baseUrl}${getRuleImgUrl()}`"
mode="widthFix"
@load="console.log('规则图片加载成功')"
@error="console.log('规则图片加载失败')"></image>
</view>
<span class="rulemodalclosebtn"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.closeBtn)})` }"
@click="handleCloseClick"></span>
</view>
</view>
</template>
<script setup>
import { act915RuleModalConfig, getImageUrl } from './config.js'
import { getCurrentInstance } from 'vue'
// 组件名称
defineOptions({
name: 'RuleModal'
})
// 获取全局属性
const { proxy } = getCurrentInstance()
const $baseUrl = proxy.$baseUrl
// Props
const props = defineProps({
visible: {
type: Boolean,
default: false
},
rules: {
type: String,
default: ''
},
config: {
type: Object,
default: () => ({})
}
})
// Emits
const emit = defineEmits(['close'])
// 配置对象
const config = act915RuleModalConfig
// 获取规则图片URL
const getRuleImgUrl = () => {
// 优先使用接口返回的 ruleImg
if (props.config && props.config.ruleImg) {
console.log('使用接口返回的 ruleImg:', props.config.ruleImg)
return props.config.ruleImg
}
// 回退到默认配置
console.log('使用默认配置的 textImg:', config.images.textImg)
const defaultUrl = getImageUrl(config.images.textImg)
console.log('默认图片URL:', defaultUrl)
return defaultUrl
}
// 关闭按钮点击处理
const handleCloseClick = () => {
console.log('关闭规则弹窗')
emit('close')
}
</script>
<style lang="less" scoped>
@import './rulemodal.less';
</style>
@import "@/common.less";
.rulemodal {
width: 100vw;
height: 100vh;
left: 0rpx;
top: 0rpx;
position: fixed;
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden; // 禁用弹窗内的滚动
// 蒙层
.rulemodal-mask {
width: 100%;
height: 100%;
position: absolute;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1;
}
// 弹窗内容
.rulemodal-content {
width: 550rpx;
height: 667rpx;
position: relative;
z-index: 2;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.rulemodalbg {
width: 550rpx;
height: 667rpx;
position: absolute;
top: 0;
left: 0;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
}
// 规则内容滚动容器
.rulemodal-text-container {
width: 100%;
height: 471rpx;
position: absolute;
// top: 154rpx;
// left: 35rpx;
overflow-y: auto;
overflow-x: hidden;
z-index: 3;
// 自定义滚动条样式
&::-webkit-scrollbar {
width: 8rpx;
}
&::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.1);
border-radius: 4rpx;
}
&::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.3);
border-radius: 4rpx;
&:hover {
background: rgba(0, 0, 0, 0.5);
}
}
// 规则图片样式
.rulemodaltextimg {
position: relative;
top: 0;
left: 0;
width: 100%;
height: auto;
object-fit: contain;
display: block;
}
}
.rulemodalclosebtn {
width: 60rpx;
height: 59rpx;
position: absolute;
top: 728rpx;
left: 245rpx;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
transition: opacity 0.3s ease;
&:hover {
opacity: 0.8;
}
}
}
const version = 'v1'
// 抽奖页面配置
export const lotteryConfig = {
// 页面状态
states: {
NORMAL: 'normal',
DRAWING: 'drawing',
COMPLETED: 'completed'
},
// 图片配置
images: {
background: `LotteryPage/${version}/LotteryPageBg.png`,
turnTableBg: `LotteryPage/${version}/LotteryPageTurnTableBg.png`,
turnTableLogo: `LotteryPage/${version}/LotteryPageTurnTableLogo.png`,
awards6Bg: `LotteryPage/${version}/LotteryPageTurnTableAwards6Bg.png`,
awards5Bg: `LotteryPage/${version}/LotteryPageTurnTableAwards5Bg.png`,
awards4Bg: `LotteryPage/${version}/LotteryPageTurnTableAwards4Bg.png`,
awards3Bg: `LotteryPage/${version}/LotteryPageTurnTableAwards3Bg.png`,
awards2Img: `LotteryPage/${version}/LotteryPageTurnTableAwards2Img.png`,
awards1Img: `LotteryPage/${version}/LotteryPageTurnTableAwards1Img.png`,
title: `LotteryPage/${version}/LotteryPageTitle.png`,
ruleBtn: `LotteryPage/${version}/LotteryPageRuleBtn.png`,
awardBtn: `LotteryPage/${version}/LotteryPageAwardBtn.png`,
noticeConBg: `LotteryPage/${version}/LotteryPageNoticeConBg.png`,
noticeConSoundIcon: `LotteryPage/${version}/LotteryPageNoticeConSoundIcon.png`,
drawBtn: `LotteryPage/${version}/LotteryPageDrawBtn.png`,
lotterypageturntableawardsSelBg: `LotteryPage/${version}/lotterypageturntableawardsSelBg.png`,
},
// 奖品配置
awards: [
{
id: 1,
name: 'N47度 水果玉米汁',
type: 'product',
probability: 0.1
},
{
id: 2,
name: 'N47度 水果玉米汁',
type: 'product',
probability: 0.15
},
{
id: 3,
name: 'N47度 水果玉米汁',
type: 'product',
probability: 0.2
},
{
id: 4,
name: 'N47度 水果玉米汁',
type: 'product',
probability: 0.25
},
{
id: 5,
name: '感谢参与',
type: 'thanks',
probability: 0.2
},
{
id: 6,
name: 'N47度 水果玉米汁',
type: 'product',
probability: 0.1
}
],
// 文本配置
texts: {
title: '幸运大转盘',
ruleTitle: '活动规则',
awardTitle: '奖品列表',
drawButton: '立即抽奖',
remainingTimes: '剩余次数:',
noTimesLeft: '抽奖次数已用完',
drawingText: '抽奖中...',
congratulations: '恭喜中奖',
youGot: '您获得了:'
},
// 抽奖配置
lottery: {
maxTimes: 1, // 最大抽奖次数
animationDuration: 2000, // 抽奖动画时长
rotationSpeed: 5 // 转盘旋转速度
}
}
// 获取图片URL的工具函数
export const getImageUrl = (imagePath) => {
return imagePath
}
// 获取随机公告
export const getRandomNotice = () => {
const notices = [
'用户152****8839 获得了 XXXXXX',
'用户138****5678 获得了 YYYYYY',
'用户159****1234 获得了 ZZZZZZ',
'恭喜用户获得大奖!',
'又有用户中奖了!'
]
return notices[Math.floor(Math.random() * notices.length)]
}
// 根据概率获取奖品
export const getRandomAward = (awards) => {
const random = Math.random()
let cumulativeProbability = 0
for (const award of awards) {
cumulativeProbability += award.probability
if (random <= cumulativeProbability) {
return award
}
}
// 默认返回最后一个奖品
return awards[awards.length - 1]
}
// 抽奖页面样式
.lotterypage {
width: 750rpx;
height: 100vh;
left: 0rpx;
top: 0rpx;
overflow: hidden;
position: absolute;
// 返回按钮样式
.nav-left {
position: fixed;
top: 100rpx;
left: 30rpx;
z-index: 999;
.back-btn {
width: 60rpx;
height: 60rpx;
}
}
.lotterypageCon {
width: 750rpx;
height: 1624rpx;
top: 0rpx;
bottom: 0rpx;
margin: auto;
position: absolute;
}
.lotterypagebg {
width: 750rpx;
height: 1624rpx;
left: 0rpx;
top: 0rpx;
position: absolute;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
}
.lotterypageturntable {
width: 719rpx;
height: 718rpx;
left: 15rpx;
top: 476rpx;
position: absolute;
.lotterypageturntablebg {
width: 719rpx;
height: 718rpx;
left: 0rpx;
top: 0rpx;
position: absolute;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
}
.lotterypageturntablelogo {
width: 209rpx;
height: 204rpx;
left: 255rpx;
top: 259rpx;
position: absolute;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
}
.lotterypageturntableawards {
width: 448rpx;
height: 553rpx;
left: 130rpx;
top: 79rpx;
position: absolute;
.lotterypageturntableawards6 {
width: 90rpx;
height: 164rpx;
left: 0rpx;
top: 95rpx;
position: absolute;
.lotterypageturntableawards6selbg {
width: 272rpx;
height: 214rpx;
left: -71rpx;
top: -18rpx;
position: absolute;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
transform: rotate(300deg);
transform-origin: center;
}
.lotterypageturntableawards6img {
width: 100rpx;
height: 100rpx;
left: 10rpx;
top: 10rpx;
position: absolute;
object-fit: contain;
}
.lotterypageturntableawards6name {
width: 100rpx;
height: 50rpx;
left: 0rpx;
top: 115rpx;
position: absolute;
font-size: 16rpx;
line-height: 25rpx;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
color: rgba(0, 0, 0, 1);
text-align: center;
}
}
.lotterypageturntableawards5 {
width: 78rpx;
height: 100rpx;
left: 359rpx;
top: 138rpx;
position: absolute;
.lotterypageturntableawards5selbg {
width: 272rpx;
height: 214rpx;
left: -104rpx;
top: -59rpx;
position: absolute;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
transform: rotate(60deg);
transform-origin: center;
}
.lotterypageturntableawards5img {
width: 100rpx;
height: 100rpx;
left: 0rpx;
top: -35rpx;
position: absolute;
object-fit: contain;
}
.lotterypageturntableawards5name {
width: 81rpx;
height: 36rpx;
left: 16rpx;
top: 70rpx;
position: absolute;
font-size: 16rpx;
line-height: 18rpx;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
color: rgba(0, 0, 0, 1);
text-align: center;
}
}
.lotterypageturntableawards4 {
width: 90rpx;
height: 164rpx;
left: 22rpx;
top: 296rpx;
position: absolute;
.lotterypageturntableawards4selbg {
width: 272rpx;
height: 214rpx;
left: -94rpx;
top: -30rpx;
position: absolute;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
transform: rotate(240deg);
transform-origin: center;
}
.lotterypageturntableawards4img {
width: 100rpx;
height: 100rpx;
left: 0rpx;
top: 0rpx;
position: absolute;
object-fit: contain;
}
.lotterypageturntableawards4name {
width: 100rpx;
height: 50rpx;
left: 0rpx;
top: 115rpx;
position: absolute;
font-size: 16rpx;
line-height: 25rpx;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
color: rgba(0, 0, 0, 1);
text-align: center;
}
}
.lotterypageturntableawards3 {
width: 90rpx;
height: 164rpx;
left: 358rpx;
top: 296rpx;
position: absolute;
.lotterypageturntableawards3selbg {
width: 272rpx;
height: 214rpx;
left: -105rpx;
top: -29rpx;
position: absolute;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
transform: rotate(120deg);
transform-origin: center;
}
.lotterypageturntableawards3img {
width: 100rpx;
height: 100rpx;
left: 0rpx;
top: 0rpx;
position: absolute;
object-fit: contain;
}
.lotterypageturntableawards3name {
width: 100rpx;
height: 50rpx;
left: 0rpx;
top: 115rpx;
position: absolute;
font-size: 16rpx;
line-height: 25rpx;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
color: rgba(0, 0, 0, 1);
text-align: center;
}
}
.lotterypageturntableawards2 {
width: 90rpx;
height: 164rpx;
left: 182rpx;
top: 389rpx;
position: absolute;
.lotterypageturntableawards2selbg {
width: 272rpx;
height: 214rpx;
left: -90rpx;
top: -29rpx;
position: absolute;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
transform: rotate(180deg);
transform-origin: center;
}
.lotterypageturntableawards2img {
width: 100rpx;
height: 100rpx;
left: 0rpx;
top: 5rpx;
position: absolute;
object-fit: contain;
}
.lotterypageturntableawards2name {
width: 100rpx;
height: 50rpx;
left: 0rpx;
top: 115rpx;
position: absolute;
font-size: 16rpx;
line-height: 25rpx;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
color: rgba(0, 0, 0, 1);
text-align: center;
}
}
.lotterypageturntableawards1 {
width: 90rpx;
height: 164rpx;
left: 183rpx;
top: 0rpx;
position: absolute;
.lotterypageturntableawards1selbg {
width: 272rpx;
height: 214rpx;
left: -90rpx;
top: -18rpx;
position: absolute;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
transform: rotate(0deg);
transform-origin: center;
}
.lotterypageturntableawards1img {
width: 100rpx;
height: 100rpx;
left: 0rpx;
top: 5rpx;
position: absolute;
object-fit: contain;
}
.lotterypageturntableawards1name {
width: 100rpx;
height: 50rpx;
left: 0rpx;
top: 115rpx;
position: absolute;
font-size: 16rpx;
line-height: 25rpx;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
color: rgba(0, 0, 0, 1);
text-align: center;
}
}
}
}
.lotterypagetitle {
width: 531rpx;
height: 99rpx;
left: 111rpx;
top: 257rpx;
position: absolute;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
}
.lotterypagerulebtn {
width: 51rpx;
height: 93rpx;
left: 0rpx;
top: 309rpx;
position: absolute;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
transition: opacity 0.3s ease;
&:hover {
opacity: 0.8;
}
}
.lotterypageawardbtn {
width: 48rpx;
height: 93rpx;
left: 702rpx;
top: 309rpx;
position: absolute;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
transition: opacity 0.3s ease;
&:hover {
opacity: 0.8;
}
}
.lotterypagenoticecon {
width: 701rpx;
height: 49rpx;
left: 25rpx;
top: 372rpx;
position: absolute;
.lotterypagenoticeconbg {
width: 701rpx;
height: 49rpx;
left: 0rpx;
top: 0rpx;
position: absolute;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
}
.lotterypagenoticecontext-wrapper {
width: 482rpx;
height: 40rpx;
left: 135rpx;
top: 5rpx;
position: absolute;
overflow: hidden;
}
.lotterypagenoticecontext-container {
transition: transform 0.5s ease-in-out;
&.no-transition {
transition: none;
}
}
.lotterypagenoticecontext {
width: 502rpx;
height: 40rpx;
position: absolute;
top: 0;
left: 0;
font-size: 24rpx;
line-height: 40rpx;
color: rgba(255, 255, 255, 1);
display: block;
margin: 0;
padding: 0;
white-space: nowrap;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
}
.lotterypagenoticeconsoundicon {
width: 23rpx;
height: 22rpx;
left: 98rpx;
top: 14rpx;
position: absolute;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
transition: opacity 0.3s ease;
&:hover {
opacity: 0.8;
}
}
}
.lotterypagedrawbtn {
width: 481rpx;
height: 113rpx;
left: 135rpx;
top: 1256rpx;
position: absolute;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
transition: opacity 0.3s ease;
&:hover {
opacity: 0.8;
}
// 禁用状态样式
&.disabled-draw-btn {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
&:hover {
opacity: 0.5;
}
}
}
.lotterypagelefttimes {
width: 136rpx;
height: 23rpx;
left: 0rpx;
right: 0rpx;
margin: auto;
top: 1374rpx;
text-align: center;
white-space: nowrap;
position: absolute;
font-size: 24rpx;
line-height: 23rpx;
color: rgba(255, 255, 255, 1);
}
}
<template>
<view class="lotterypage">
<!-- 返回按钮 -->
<view class="nav-left">
<image class="back-btn" :src="$baseUrl + 'xingmaLab/1001/backBtn.png'" mode="aspectFit"
@click="handleBack" />
</view>
<view class="lotterypageCon">
<span class="lotterypagebg"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.background)})` }"></span>
<div class="lotterypageturntable">
<span class="lotterypageturntablebg"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.turnTableBg)})` }"></span>
<span class="lotterypageturntablelogo"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.turnTableLogo)})` }"></span>
<div class="lotterypageturntableawards" :style="turntableRotationStyle">
<div class="lotterypageturntableawards6" v-if="turntablePrizes.length > 5">
<span class="lotterypageturntableawards6selbg"
v-if="isPrizeSelected(5) || isPrizeHighlighted(5)"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.lotterypageturntableawardsSelBg)})` }"></span>
<image class="lotterypageturntableawards6img"
:src="`${$baseUrl}${turntablePrizes[5].prizeImageUrl}`" mode="aspectFit"
@error="console.log('奖品6图片加载失败')"></image>
<span class="lotterypageturntableawards6name">{{ turntablePrizes[5].prizeName }}</span>
</div>
<div class="lotterypageturntableawards5" v-if="turntablePrizes.length > 4">
<span class="lotterypageturntableawards5selbg"
v-if="isPrizeSelected(4) || isPrizeHighlighted(4)"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.lotterypageturntableawardsSelBg)})` }"></span>
<image class="lotterypageturntableawards5img"
:src="`${$baseUrl}${turntablePrizes[4].prizeImageUrl}`" mode="aspectFit"
@error="console.log('奖品5图片加载失败')"></image>
<span class="lotterypageturntableawards5name">{{ turntablePrizes[4].prizeName }}</span>
</div>
<div class="lotterypageturntableawards4" v-if="turntablePrizes.length > 3">
<span class="lotterypageturntableawards4selbg"
v-if="isPrizeSelected(3) || isPrizeHighlighted(3)"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.lotterypageturntableawardsSelBg)})` }"></span>
<image class="lotterypageturntableawards4img"
:src="`${$baseUrl}${turntablePrizes[3].prizeImageUrl}`" mode="aspectFit"
@error="console.log('奖品4图片加载失败')"></image>
<span class="lotterypageturntableawards4name">{{ turntablePrizes[3].prizeName }}</span>
</div>
<div class="lotterypageturntableawards3" v-if="turntablePrizes.length > 2">
<span class="lotterypageturntableawards3selbg"
v-if="isPrizeSelected(2) || isPrizeHighlighted(2)"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.lotterypageturntableawardsSelBg)})` }"></span>
<image class="lotterypageturntableawards3img"
:src="`${$baseUrl}${turntablePrizes[2].prizeImageUrl}`" mode="aspectFit"
@error="console.log('奖品3图片加载失败')"></image>
<span class="lotterypageturntableawards3name">{{ turntablePrizes[2].prizeName }}</span>
</div>
<div class="lotterypageturntableawards2" v-if="turntablePrizes.length > 1">
<span class="lotterypageturntableawards2selbg"
v-if="isPrizeSelected(1) || isPrizeHighlighted(1)"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.lotterypageturntableawardsSelBg)})` }"></span>
<image class="lotterypageturntableawards2img"
:src="`${$baseUrl}${turntablePrizes[1].prizeImageUrl}`" mode="aspectFit"
@error="console.log('奖品2图片加载失败')"></image>
<span class="lotterypageturntableawards2name">{{ turntablePrizes[1].prizeName }}</span>
</div>
<div class="lotterypageturntableawards1" v-if="turntablePrizes.length > 0">
<span class="lotterypageturntableawards1selbg"
v-if="isPrizeSelected(0) || isPrizeHighlighted(0)"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.lotterypageturntableawardsSelBg)})` }"></span>
<image class="lotterypageturntableawards1img"
:src="`${$baseUrl}${turntablePrizes[0].prizeImageUrl}`" mode="aspectFit"
@error="console.log('奖品1图片加载失败')"></image>
<span class="lotterypageturntableawards1name">{{ turntablePrizes[0].prizeName }}</span>
</div>
</div>
</div>
<span class="lotterypagetitle"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.title)})` }"></span>
<span class="lotterypagerulebtn"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.ruleBtn)})` }"
@click="handleRuleClick"></span>
<span class="lotterypageawardbtn"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.awardBtn)})` }"
@click="handleAwardClick"></span>
<div class="lotterypagenoticecon">
<span class="lotterypagenoticeconbg"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.noticeConBg)})` }"></span>
<div class="lotterypagenoticecontext-wrapper">
<div class="lotterypagenoticecontext-container" :class="{ 'no-transition': isResetting }"
:style="{ transform: `translateY(-${currentNoticeIndex * 40}rpx)` }">
<span class="lotterypagenoticecontext" v-for="(notice, index) in displayNotices"
:key="`${notice.userNickname}-${index}`" :style="{ top: `${index * 40}rpx` }">{{
formatNotice(notice.userNickname, notice.prizeName)
}}</span>
</div>
</div>
<span class="lotterypagenoticeconsoundicon"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.noticeConSoundIcon)})` }"></span>
</div>
<span class="lotterypagedrawbtn"
:style="{ backgroundImage: `url(${$baseUrl}${getImageUrl(config.images.drawBtn)})` }"
@click="handleDrawClick" :class="{ 'disabled-draw-btn': !canDraw }"></span>
<span class="lotterypagelefttimes">剩余次数:{{ remainingTimes }}</span>
<!-- 规则弹窗 -->
<RuleModal :visible="showRuleModal" :rules="activityRules" :config="ruleModalConfig"
@close="handleRuleModalClose" />
<!-- 抽奖成功弹窗 -->
<DrawSucModal :visible="showDrawSucModal" :award-name="currentAwardName"
:award-image-url="currentAwardImageUrl" @close="handleDrawSucModalClose" @happy-get="handleHappyGet" />
<!-- 抽奖失败弹窗 -->
<DrawFailModal :visible="showDrawFailModal" @close="handleDrawFailModalClose" @i-know="handleIKnow" />
</view>
</view>
</template>
<script setup>
import { ref, onMounted, computed, getCurrentInstance, onUnmounted } from 'vue'
import { lotteryConfig, getImageUrl } from './config.js'
import RuleModal from './components/ruleModal/ruleModal.vue'
import DrawSucModal from './components/drawSucModal/drawSucModal.vue'
import DrawFailModal from './components/drawFailModal/drawFailModal.vue'
import { getAct915LotteryDraw, getAct915LotteryInfo, fetchAct915LotteryJSON } from '@/api/activity0915/index.js'
import md from '@/md.js'
import { jump, JumpType } from '@/utils/index.js'
// 组件名称
defineOptions({
name: 'LotteryPage'
})
// 响应式数据
const currentState = ref('normal') // 页面状态
const remainingTimes = ref(0) // 剩余抽奖次数
const currentNotice = ref('') // 当前公告
const turntablePrizes = ref([]) // 转盘奖品列表
const winningCarousel = ref([]) // 中奖轮播列表
const userWinningPrizes = ref([]) // 用户中奖记录
const currentNoticeIndex = ref(0) // 当前公告索引
const ruleModalConfig = ref({}) // 规则弹窗配置
const carouselTimer = ref(null) // 轮播定时器
const activityRules = ref('') // 活动规则内容
const isDrawing = ref(false) // 是否正在抽奖
const selectedPrizeIndex = ref(-1) // 当前选中的奖品索引
const currentHighlightIndex = ref(-1) // 当前高亮的奖品索引(用于轮流亮起效果)
const isResetting = ref(false) // 是否正在重置轮播位置
// 弹窗状态
const showRuleModal = ref(false) // 规则弹窗
const showDrawSucModal = ref(false) // 抽奖成功弹窗
const showDrawFailModal = ref(false) // 抽奖失败弹窗
const currentAwardName = ref('') // 当前中奖奖品名称
const currentAwardImageUrl = ref('') // 当前中奖奖品图片URL
// 配置对象
const config = lotteryConfig
// 获取$baseUrl
const instance = getCurrentInstance()
const $baseUrl = instance.proxy.$baseUrl
// 计算属性 - 是否可以抽奖
const canDraw = computed(() => {
return remainingTimes.value > 0 && currentState.value === 'normal' && !isDrawing.value
})
// 转盘不需要旋转,保持静态
const turntableRotationStyle = computed(() => {
return {
transform: 'rotate(0deg)',
transition: 'none'
}
})
// 检查奖品是否被选中(用于最终结果)
const isPrizeSelected = (index) => {
return selectedPrizeIndex.value === index
}
// 检查奖品是否正在高亮(用于轮流亮起效果)
const isPrizeHighlighted = (index) => {
return currentHighlightIndex.value === index
}
// 获取最近的20条公告
const recentNotices = computed(() => {
let notices = [];
if (winningCarousel.value.length > 0) {
notices = winningCarousel.value.slice(0, 20);
} else {
// 提供默认数据确保轮播能正常工作
notices = [
{ userNickname: '用户1', prizeName: '奖品A' },
{ userNickname: '用户2', prizeName: '奖品B' },
{ userNickname: '用户3', prizeName: '奖品C' }
];
}
// 如果只有1条数据,复制成3条以确保轮播效果
if (notices.length === 1) {
const singleNotice = notices[0];
notices = [singleNotice, singleNotice, singleNotice];
console.log('轮播数据只有1条,已复制成3条:', notices);
}
return notices;
});
// 用于轮播显示的公告列表(重复数据以实现无缝循环)
const displayNotices = computed(() => {
const notices = recentNotices.value;
if (notices.length === 0) return [];
// 重复数据以实现无缝循环
return [...notices, ...notices];
});
// 格式化公告内容,限制为20个字符
const formatNotice = (nickname, prize) => {
const fullText = `${nickname} 获得了 ${prize}`;
return fullText;
};
// 返回按钮处理
const handleBack = () => {
console.log('点击返回按钮')
// md.sensorComponentLogTake({
// xcxComponentClick: "true",
// pageName: "915专题活动抽奖页",
// componentName: "返回按钮",
// componentContent: "返回"
// });
try {
uni.navigateBack({
delta: 1,
success: () => {
console.log('返回成功')
},
fail: () => {
// 如果返回失败,跳转到首页
uni.redirectTo({
url: '/activity0915/act0915/act0915'
})
}
})
} catch (error) {
console.log('返回失败:', error)
uni.redirectTo({
url: '/activity0915/act0915/act0915'
})
}
}
// 规则按钮点击处理
const handleRuleClick = () => {
console.log('点击规则按钮')
md.sensorComponentLogTake({
xcxComponentClick: "true",
pageName: "915专题活动抽奖页",
componentName: "抽奖板块",
componentContent: "规则"
});
showRuleModal.value = true
}
// 规则弹窗关闭处理
const handleRuleModalClose = () => {
showRuleModal.value = false
}
// 奖品按钮点击处理
const handleAwardClick = () => {
console.log('点击奖品按钮')
md.sensorComponentLogTake({
xcxComponentClick: "true",
pageName: "915专题活动抽奖页",
componentName: "抽奖板块",
componentContent: "奖品"
});
// // 根据环境获取对应的appId
// const getAppId = () => {
// // 可以根据实际环境判断
// // uat2: wxabebc35e71e66795
// // test2: wx18428fc8a569a3c7
// // 生产: wx4205ec55b793245e
// // 这里可以根据实际需求调整环境判断逻辑
// // 例如:根据域名、配置等判断当前环境
// const currentEnv = 'prod' // 可以动态获取
// const appIdMap = {
// 'uat2': 'wxabebc35e71e66795',
// 'test2': 'wx18428fc8a569a3c7',
// 'prod': 'wx4205ec55b793245e'
// }
// return appIdMap[currentEnv] || appIdMap['uat2'] // 默认uat2环境
// }
jump({
type: JumpType.MINI,
url: '/subPackages/xmhMainProcess/mine/index',
extra: {
appId: 'wx4205ec55b793245e',
envVersion: 'release'
}
})
// const appId = getAppId()
// const path = '/subPackages/xmhMainProcess/mine/index'
// uni.navigateToMiniProgram({
// appId: appId,
// path: path,
// extraData: {},
// envVersion: 'release', // 可以根据环境调整:develop, trial, release
// success: (res) => {
// console.log('跳转小程序成功:', res)
// },
// fail: (err) => {
// console.error('跳转小程序失败:', err)
// // uni.showToast({
// // title: '跳转失败',
// // icon: 'none'
// // })
// }
// })
}
// 加载规则弹窗配置
const loadRuleModalConfig = async () => {
try {
console.log('开始请求 fetchAct915LotteryJSON 接口...')
const response = await fetchAct915LotteryJSON()
console.log('fetchAct915LotteryJSON 接口返回结果:', response)
if (response && response.ok && response.data) {
ruleModalConfig.value = response.data
console.log('规则弹窗配置加载成功:', ruleModalConfig.value)
} else {
console.warn('规则弹窗配置接口返回异常:', response)
}
} catch (error) {
console.error('fetchAct915LotteryJSON 接口请求失败:', error)
}
}
// 加载抽奖信息
const loadLotteryInfo = async () => {
try {
console.log('开始请求 getAct915LotteryInfo 接口...')
const response = await getAct915LotteryInfo()
console.log('getAct915LotteryInfo 接口返回结果:', response)
if (response && response.ok) {
remainingTimes.value = response.data.remainingChances
turntablePrizes.value = response.data.turntablePrizes
winningCarousel.value = response.data.winningCarousel
userWinningPrizes.value = response.data.userWinningPrizes
activityRules.value = response.data.activityRules || ''
// 启动公告轮播(无论是否有API数据)
startNoticeCarousel()
} else {
// uni.showToast({
// title: response.msg || '获取抽奖信息失败',
// icon: 'none'
// })
// 即使API失败也启动轮播
startNoticeCarousel()
}
} catch (error) {
console.error('getAct915LotteryInfo 接口请求失败:', error)
uni.showToast({
title: '网络错误,请稍后重试',
icon: 'none'
})
// 即使网络错误也启动轮播
startNoticeCarousel()
}
}
// 公告轮播
const startNoticeCarousel = () => {
// 确保有数据可以轮播
if (recentNotices.value.length > 0) {
carouselTimer.value = setInterval(() => {
currentNoticeIndex.value = currentNoticeIndex.value + 1
console.log('当前公告索引:', currentNoticeIndex.value)
// 当滚动到重复数据的开始位置时,立即重置到原始位置(无动效)
if (currentNoticeIndex.value >= recentNotices.value.length) {
// 禁用过渡动效
isResetting.value = true
// 立即重置到原始位置
currentNoticeIndex.value = 0
// 下一帧恢复过渡动效
setTimeout(() => {
isResetting.value = false
}, 100) // 确保重置完成
}
}, 3000) // 每3秒切换一次
}
}
// 停止轮播
const stopNoticeCarousel = () => {
if (carouselTimer.value) {
clearInterval(carouselTimer.value)
carouselTimer.value = null
}
}
// 抽奖按钮点击处理
const handleDrawClick = async () => {
md.sensorComponentLogTake({
xcxComponentClick: "true",
pageName: "915专题活动抽奖页",
componentName: "抽奖板块",
componentContent: "立即抽奖"
});
if (!canDraw.value) {
uni.showToast({
title: '抽奖次数已用完',
icon: 'none'
})
return
}
console.log('开始抽奖')
isDrawing.value = true
selectedPrizeIndex.value = -1 // 重置选中状态
currentHighlightIndex.value = -1 // 重置高亮状态
// uni.showLoading({
// title: '抽奖中...',
// mask: true
// })
try {
const response = await getAct915LotteryDraw()
console.log('getAct915LotteryDraw 接口返回结果:', response)
if (response && response.ok) {
remainingTimes.value = response.data.remainingChances
// 根据接口返回的prizeId找到对应的奖品索引
const prizeIndex = turntablePrizes.value.findIndex(prize => prize.prizeId === response.data.prizeId)
console.warn("prizeIndex", prizeIndex)
if (prizeIndex !== -1) {
// 开始轮流亮起效果
startHighlightAnimation(prizeIndex, response.data.isWin)
} else {
// 如果找不到对应奖品,显示失败
// uni.hideLoading()
isDrawing.value = false
showDrawFailModal.value = true
}
} else {
// uni.hideLoading()
isDrawing.value = false
uni.showToast({
title: response.msg || '抽奖失败',
icon: 'none'
})
}
} catch (error) {
console.error('getAct915LotteryDraw 接口请求失败:', error)
uni.hideLoading()
isDrawing.value = false
uni.showToast({
title: '网络错误,请稍后重试',
icon: 'none'
})
}
}
// 轮流亮起动画
const startHighlightAnimation = (targetIndex, isWin) => {
console.log(`开始轮流亮起动画,目标奖品索引: ${targetIndex}, 是否中奖: ${isWin}`)
// 按照指定顺序:1 -> 5 -> 3 -> 2 -> 4 -> 6
// 对应索引:0 -> 4 -> 2 -> 1 -> 3 -> 5
const turntableOrder = [0, 4, 2, 1, 3, 5] // 按照指定顺序
const totalPrizes = turntableOrder.length
let currentOrderIndex = 0
let roundCount = 0
const maxRounds = 2 // 最多转2圈
const highlightSpeed = 200 // 每个奖品高亮200ms
const highlightTimer = setInterval(() => {
// 获取当前转盘顺序中的奖品索引
const currentPrizeIndex = turntableOrder[currentOrderIndex]
currentHighlightIndex.value = currentPrizeIndex
// 检查是否到达目标位置
if (roundCount >= maxRounds && currentPrizeIndex === targetIndex) {
clearInterval(highlightTimer)
// 停止高亮,显示最终选中状态
setTimeout(() => {
currentHighlightIndex.value = -1
selectedPrizeIndex.value = targetIndex
uni.hideLoading()
// 显示结果弹窗
setTimeout(() => {
isDrawing.value = false
if (isWin) {
currentAwardName.value = turntablePrizes.value[targetIndex].prizeName
currentAwardImageUrl.value = turntablePrizes.value[targetIndex].prizeImageUrl
showDrawSucModal.value = true
} else {
showDrawFailModal.value = true
}
// 延迟重置选中状态
setTimeout(() => {
selectedPrizeIndex.value = -1
}, 2000)
}, 1000)
}, highlightSpeed)
} else {
// 移动到下一个奖品(按转盘顺序)
currentOrderIndex = (currentOrderIndex + 1) % totalPrizes
// 如果回到起点,增加圈数
if (currentOrderIndex === 0) {
roundCount++
}
}
}, highlightSpeed)
}
// 抽奖成功弹窗关闭处理
const handleDrawSucModalClose = () => {
showDrawSucModal.value = false
}
// 开心领取处理
const handleHappyGet = () => {
console.log('点击开心领取')
showDrawSucModal.value = false
// uni.showToast({
// title: '奖品领取成功',
// icon: 'success'
// })
}
// 抽奖失败弹窗关闭处理
const handleDrawFailModalClose = () => {
showDrawFailModal.value = false
}
// 我知道了处理
const handleIKnow = () => {
console.log('点击我知道了')
showDrawFailModal.value = false
// uni.showToast({
// title: '下次再来试试吧',
// icon: 'none'
// })
}
// 生命周期
onMounted(() => {
console.log('抽奖页面已加载')
loadRuleModalConfig() // 先加载规则弹窗配置
loadLotteryInfo()
md.sensorComponentLogTake({
xcxComponentExposure: "true",
pageName: "915专题活动抽奖页",
componentName: "抽奖板块",
componentContent: "规则、奖品、立即抽奖"
});
})
onUnmounted(() => {
stopNoticeCarousel()
})
</script>
<style lang="less" scoped>
@import 'index.less';
</style>
......@@ -274,6 +274,27 @@
}
}
]
},
{
"root": "activities/1015",
"pages": [
{
"path": "home/index",
"style": {
"navigationBarTitleText": "",
"shareAppMessage": true,
"shareTimeline": false,
"navigationStyle": "custom"
}
},
{
"path": "lottery/index",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}
]
}
],
"globalStyle": {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment