feat:新增动画,修复一番赏的逻辑错误,无限赏和一番赏目前按照权重升序排列
This commit is contained in:
parent
28e0721e3f
commit
a634c6caac
@ -1,251 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view v-if="visible" class="blessing-container">
|
|
||||||
<view class="blessing-animation" :class="currentBlessing.type">
|
|
||||||
<view class="blessing-emoji">{{ currentBlessing.emoji }}</view>
|
|
||||||
<view v-if="currentBlessing.type === 'sheep'" class="blessing-subtitle">小羊祝你</view>
|
|
||||||
<view class="blessing-text">
|
|
||||||
<text v-for="(char, index) in currentBlessing.chars"
|
|
||||||
:key="index"
|
|
||||||
class="char"
|
|
||||||
:class="{ 'from-left': index % 2 === 0, 'from-right': index % 2 === 1 }"
|
|
||||||
:style="{ animationDelay: index * 0.15 + 's' }">
|
|
||||||
{{ char }}
|
|
||||||
</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, onMounted, onUnmounted } from 'vue'
|
|
||||||
import { registerBlessing, unregisterBlessing } from '@/utils/blessing.js'
|
|
||||||
|
|
||||||
const visible = ref(false)
|
|
||||||
const blessings = [
|
|
||||||
{
|
|
||||||
emoji: '🐏',
|
|
||||||
chars: ['三', '羊', '开', '泰'],
|
|
||||||
type: 'sheep'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
emoji: '🐴',
|
|
||||||
chars: ['马', '到', '功', '成'],
|
|
||||||
type: 'horse'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
const currentBlessing = ref(blessings[0])
|
|
||||||
|
|
||||||
const handleShow = ({ type }) => {
|
|
||||||
console.log('[BlessingAnimation] handleShow 被调用, type:', type)
|
|
||||||
|
|
||||||
// 根据类型选择祝福语
|
|
||||||
let index = 0
|
|
||||||
if (type === 'random') {
|
|
||||||
index = Math.floor(Math.random() * blessings.length)
|
|
||||||
} else if (type === 'sheep') {
|
|
||||||
index = 0
|
|
||||||
} else if (type === 'horse') {
|
|
||||||
index = 1
|
|
||||||
} else {
|
|
||||||
index = Math.floor(Math.random() * blessings.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
currentBlessing.value = blessings[index]
|
|
||||||
visible.value = true
|
|
||||||
|
|
||||||
console.log('[BlessingAnimation] 显示祝福:', currentBlessing.value)
|
|
||||||
|
|
||||||
// 3秒后自动隐藏
|
|
||||||
setTimeout(() => {
|
|
||||||
visible.value = false
|
|
||||||
console.log('[BlessingAnimation] 隐藏祝福')
|
|
||||||
}, 3000)
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
console.log('[BlessingAnimation] 组件已挂载,准备注册监听器')
|
|
||||||
registerBlessing(handleShow)
|
|
||||||
console.log('[BlessingAnimation] 监听器注册完成')
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
console.log('[BlessingAnimation] 组件即将卸载')
|
|
||||||
unregisterBlessing()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.blessing-container {
|
|
||||||
position: fixed;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
z-index: 10000; // 提高到比支付弹窗(999)更高的层级
|
|
||||||
pointer-events: none;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 20rpx;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 600rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blessing-animation {
|
|
||||||
text-align: center;
|
|
||||||
animation: blessingFadeIn 0.5s ease-out;
|
|
||||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.98), rgba(255, 248, 243, 0.98));
|
|
||||||
padding: 40rpx 30rpx;
|
|
||||||
border-radius: 24rpx;
|
|
||||||
box-shadow: 0 12rpx 48rpx rgba(255, 107, 0, 0.3);
|
|
||||||
backdrop-filter: blur(20rpx);
|
|
||||||
border: 2rpx solid rgba(255, 159, 67, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes blessingFadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(-30rpx);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.blessing-emoji {
|
|
||||||
font-size: 100rpx;
|
|
||||||
line-height: 1;
|
|
||||||
margin-bottom: 16rpx;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 小羊动画 - 弹跳出现
|
|
||||||
.blessing-animation.sheep .blessing-emoji {
|
|
||||||
animation: emojiBounce 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 小马动画 - 从左边跑步进场
|
|
||||||
.blessing-animation.horse .blessing-emoji {
|
|
||||||
animation: emojiRun 0.8s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes emojiBounce {
|
|
||||||
0% {
|
|
||||||
transform: scale(0) rotate(-180deg);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
transform: scale(1.2) rotate(10deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: scale(1) rotate(0deg);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes emojiRun {
|
|
||||||
0% {
|
|
||||||
transform: translateX(-300rpx) scale(0.8);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
transform: translateX(30rpx) scale(1.1);
|
|
||||||
}
|
|
||||||
80% {
|
|
||||||
transform: translateX(-15rpx) scale(0.95);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: translateX(0) scale(1);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.blessing-subtitle {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #FF9500;
|
|
||||||
font-weight: 700;
|
|
||||||
margin-top: 12rpx;
|
|
||||||
margin-bottom: 8rpx;
|
|
||||||
opacity: 0;
|
|
||||||
animation: subtitleFadeIn 0.5s ease-out 0.3s forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes subtitleFadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(10rpx);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.blessing-text {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 12rpx;
|
|
||||||
margin-top: 16rpx;
|
|
||||||
|
|
||||||
.char {
|
|
||||||
font-size: 48rpx;
|
|
||||||
font-weight: 900;
|
|
||||||
color: #FF6B00;
|
|
||||||
text-shadow: 0 4rpx 12rpx rgba(255, 107, 0, 0.3);
|
|
||||||
opacity: 0;
|
|
||||||
animation: charAppear 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从左边出现
|
|
||||||
.char.from-left {
|
|
||||||
animation-name: charAppearFromLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从右边出现
|
|
||||||
.char.from-right {
|
|
||||||
animation-name: charAppearFromRight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes charAppear {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(30rpx) scale(0.5);
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
transform: translateY(-8rpx) scale(1.1);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0) scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes charAppearFromLeft {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateX(-80rpx) scale(0.5);
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
transform: translateX(10rpx) scale(1.1);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateX(0) scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes charAppearFromRight {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateX(80rpx) scale(0.5);
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
transform: translateX(-10rpx) scale(1.1);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateX(0) scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,233 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view v-if="visible" class="blessing-float" :class="{ 'hide': shouldHide }" @tap="handleClose">
|
|
||||||
<view class="blessing-content">
|
|
||||||
<view class="blessing-emoji">{{ currentBlessing.emoji }}</view>
|
|
||||||
<view v-if="currentBlessing.type === 'sheep'" class="blessing-subtitle">小羊祝你</view>
|
|
||||||
<view class="blessing-text">
|
|
||||||
<text v-for="(char, index) in currentBlessing.chars"
|
|
||||||
:key="index"
|
|
||||||
class="char"
|
|
||||||
:class="{ 'from-left': index % 2 === 0, 'from-right': index % 2 === 1 }"
|
|
||||||
:style="{ animationDelay: index * 0.15 + 's' }">
|
|
||||||
{{ char }}
|
|
||||||
</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, onMounted, onUnmounted } from 'vue'
|
|
||||||
import { registerBlessing, unregisterBlessing } from '@/utils/blessing.js'
|
|
||||||
|
|
||||||
const visible = ref(false)
|
|
||||||
const shouldHide = ref(false)
|
|
||||||
const blessings = [
|
|
||||||
{
|
|
||||||
emoji: '🐏',
|
|
||||||
chars: ['三', '羊', '开', '泰'],
|
|
||||||
type: 'sheep'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
emoji: '🐴',
|
|
||||||
chars: ['马', '到', '功', '成'],
|
|
||||||
type: 'horse'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
const currentBlessing = ref(blessings[0])
|
|
||||||
|
|
||||||
const handleShow = ({ type }) => {
|
|
||||||
console.log('[BlessingFloat] handleShow 被调用, type:', type)
|
|
||||||
|
|
||||||
// 根据类型选择祝福语
|
|
||||||
let index = 0
|
|
||||||
if (type === 'random') {
|
|
||||||
index = Math.floor(Math.random() * blessings.length)
|
|
||||||
} else if (type === 'sheep') {
|
|
||||||
index = 0
|
|
||||||
} else if (type === 'horse') {
|
|
||||||
index = 1
|
|
||||||
} else {
|
|
||||||
index = Math.floor(Math.random() * blessings.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
currentBlessing.value = blessings[index]
|
|
||||||
visible.value = true
|
|
||||||
shouldHide.value = false
|
|
||||||
|
|
||||||
console.log('[BlessingFloat] 显示祝福:', currentBlessing.value)
|
|
||||||
|
|
||||||
// 3秒后自动隐藏
|
|
||||||
setTimeout(() => {
|
|
||||||
shouldHide.value = true
|
|
||||||
setTimeout(() => {
|
|
||||||
visible.value = false
|
|
||||||
console.log('[BlessingFloat] 隐藏祝福')
|
|
||||||
}, 300)
|
|
||||||
}, 3000)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
shouldHide.value = true
|
|
||||||
setTimeout(() => {
|
|
||||||
visible.value = false
|
|
||||||
console.log('[BlessingFloat] 用户点击关闭')
|
|
||||||
}, 300)
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
console.log('[BlessingFloat] 组件已挂载,准备注册监听器')
|
|
||||||
registerBlessing(handleShow)
|
|
||||||
console.log('[BlessingFloat] 监听器注册完成')
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
console.log('[BlessingFloat] 组件即将卸载')
|
|
||||||
unregisterBlessing()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.blessing-float {
|
|
||||||
position: fixed;
|
|
||||||
top: 200rpx;
|
|
||||||
right: 30rpx;
|
|
||||||
z-index: 99999;
|
|
||||||
animation: floatIn 0.5s ease-out;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
|
|
||||||
&.hide {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(-50rpx) scale(0.8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes floatIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(-50rpx) scale(0.8);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0) scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.blessing-content {
|
|
||||||
background: linear-gradient(135deg, #FF6B00 0%, #FF9500 100%);
|
|
||||||
padding: 24rpx 20rpx;
|
|
||||||
border-radius: 20rpx;
|
|
||||||
box-shadow: 0 8rpx 24rpx rgba(255, 107, 0, 0.4);
|
|
||||||
text-align: center;
|
|
||||||
min-width: 200rpx;
|
|
||||||
backdrop-filter: blur(10rpx);
|
|
||||||
}
|
|
||||||
|
|
||||||
.blessing-emoji {
|
|
||||||
font-size: 60rpx;
|
|
||||||
line-height: 1;
|
|
||||||
margin-bottom: 8rpx;
|
|
||||||
display: block;
|
|
||||||
animation: emojiBounce 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes emojiBounce {
|
|
||||||
0% {
|
|
||||||
transform: scale(0) rotate(-180deg);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
transform: scale(1.2) rotate(10deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: scale(1) rotate(0deg);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.blessing-subtitle {
|
|
||||||
font-size: 20rpx;
|
|
||||||
color: #FFFFFF;
|
|
||||||
font-weight: 700;
|
|
||||||
margin-bottom: 6rpx;
|
|
||||||
opacity: 0;
|
|
||||||
animation: subtitleFadeIn 0.5s ease-out 0.3s forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes subtitleFadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(10rpx);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.blessing-text {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 8rpx;
|
|
||||||
margin-top: 8rpx;
|
|
||||||
|
|
||||||
.char {
|
|
||||||
font-size: 32rpx;
|
|
||||||
font-weight: 900;
|
|
||||||
color: #FFFFFF;
|
|
||||||
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.2);
|
|
||||||
opacity: 0;
|
|
||||||
animation: charAppear 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
.char.from-left {
|
|
||||||
animation-name: charAppearFromLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
.char.from-right {
|
|
||||||
animation-name: charAppearFromRight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes charAppear {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(20rpx) scale(0.5);
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
transform: translateY(-4rpx) scale(1.1);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0) scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes charAppearFromLeft {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateX(-40rpx) scale(0.5);
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
transform: translateX(6rpx) scale(1.1);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateX(0) scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes charAppearFromRight {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateX(40rpx) scale(0.5);
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
transform: translateX(-6rpx) scale(1.1);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateX(0) scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,267 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view v-if="visible" class="blessing-popup-mask" @tap="handleClose">
|
|
||||||
<view class="blessing-popup-content" @tap.stop>
|
|
||||||
<view class="blessing-emoji">{{ currentBlessing.emoji }}</view>
|
|
||||||
<view v-if="currentBlessing.type === 'sheep'" class="blessing-subtitle">小羊祝你</view>
|
|
||||||
<view class="blessing-text">
|
|
||||||
<text v-for="(char, index) in currentBlessing.chars"
|
|
||||||
:key="index"
|
|
||||||
class="char"
|
|
||||||
:class="{ 'from-left': index % 2 === 0, 'from-right': index % 2 === 1 }"
|
|
||||||
:style="{ animationDelay: index * 0.15 + 's' }">
|
|
||||||
{{ char }}
|
|
||||||
</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, onMounted, onUnmounted } from 'vue'
|
|
||||||
import { registerBlessing, unregisterBlessing } from '@/utils/blessing.js'
|
|
||||||
|
|
||||||
const visible = ref(false)
|
|
||||||
const blessings = [
|
|
||||||
{
|
|
||||||
emoji: '🐏',
|
|
||||||
chars: ['三', '羊', '开', '泰'],
|
|
||||||
type: 'sheep'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
emoji: '🐴',
|
|
||||||
chars: ['马', '到', '功', '成'],
|
|
||||||
type: 'horse'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
const currentBlessing = ref(blessings[0])
|
|
||||||
|
|
||||||
const handleShow = ({ type }) => {
|
|
||||||
console.log('[BlessingPopup] handleShow 被调用, type:', type)
|
|
||||||
|
|
||||||
// 根据类型选择祝福语
|
|
||||||
let index = 0
|
|
||||||
if (type === 'random') {
|
|
||||||
index = Math.floor(Math.random() * blessings.length)
|
|
||||||
} else if (type === 'sheep') {
|
|
||||||
index = 0
|
|
||||||
} else if (type === 'horse') {
|
|
||||||
index = 1
|
|
||||||
} else {
|
|
||||||
index = Math.floor(Math.random() * blessings.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
currentBlessing.value = blessings[index]
|
|
||||||
visible.value = true
|
|
||||||
|
|
||||||
console.log('[BlessingPopup] 显示祝福:', currentBlessing.value)
|
|
||||||
|
|
||||||
// 3秒后自动隐藏
|
|
||||||
setTimeout(() => {
|
|
||||||
visible.value = false
|
|
||||||
console.log('[BlessingPopup] 隐藏祝福')
|
|
||||||
}, 3000)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
visible.value = false
|
|
||||||
console.log('[BlessingPopup] 用户点击关闭')
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
console.log('[BlessingPopup] 组件已挂载,准备注册监听器')
|
|
||||||
registerBlessing(handleShow)
|
|
||||||
console.log('[BlessingPopup] 监听器注册完成')
|
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
console.log('[BlessingPopup] 组件即将卸载')
|
|
||||||
unregisterBlessing()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.blessing-popup-mask {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: rgba(0, 0, 0, 0.4);
|
|
||||||
z-index: 10000; // 比支付弹窗(999)高
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
animation: maskFadeIn 0.3s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes maskFadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.blessing-popup-content {
|
|
||||||
width: 600rpx;
|
|
||||||
max-width: 90%;
|
|
||||||
background: rgba(255, 255, 255, 0.98);
|
|
||||||
border-radius: 32rpx;
|
|
||||||
padding: 60rpx 40rpx;
|
|
||||||
text-align: center;
|
|
||||||
animation: popupScaleIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
||||||
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.2);
|
|
||||||
backdrop-filter: blur(20rpx);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes popupScaleIn {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: scale(0.8);
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
transform: scale(1.05);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.blessing-emoji {
|
|
||||||
font-size: 140rpx;
|
|
||||||
line-height: 1;
|
|
||||||
margin-bottom: 24rpx;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 小羊动画 - 弹跳出现
|
|
||||||
.blessing-popup-content:has(.blessing-subtitle) .blessing-emoji {
|
|
||||||
animation: emojiBounce 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 小马动画 - 从左边跑步进场
|
|
||||||
.blessing-popup-content:not(:has(.blessing-subtitle)) .blessing-emoji {
|
|
||||||
animation: emojiRun 0.8s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes emojiBounce {
|
|
||||||
0% {
|
|
||||||
transform: scale(0) rotate(-180deg);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
transform: scale(1.2) rotate(10deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: scale(1) rotate(0deg);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes emojiRun {
|
|
||||||
0% {
|
|
||||||
transform: translateX(-300rpx) scale(0.8);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
transform: translateX(30rpx) scale(1.1);
|
|
||||||
}
|
|
||||||
80% {
|
|
||||||
transform: translateX(-15rpx) scale(0.95);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: translateX(0) scale(1);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.blessing-subtitle {
|
|
||||||
font-size: 32rpx;
|
|
||||||
color: #FF9500;
|
|
||||||
font-weight: 700;
|
|
||||||
margin-bottom: 16rpx;
|
|
||||||
opacity: 0;
|
|
||||||
animation: subtitleFadeIn 0.5s ease-out 0.3s forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes subtitleFadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(10rpx);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.blessing-text {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 16rpx;
|
|
||||||
margin-top: 24rpx;
|
|
||||||
|
|
||||||
.char {
|
|
||||||
font-size: 56rpx;
|
|
||||||
font-weight: 900;
|
|
||||||
color: #FF6B00;
|
|
||||||
text-shadow: 0 4rpx 12rpx rgba(255, 107, 0, 0.3);
|
|
||||||
opacity: 0;
|
|
||||||
animation: charAppear 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从左边出现
|
|
||||||
.char.from-left {
|
|
||||||
animation-name: charAppearFromLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从右边出现
|
|
||||||
.char.from-right {
|
|
||||||
animation-name: charAppearFromRight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes charAppear {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(30rpx) scale(0.5);
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
transform: translateY(-8rpx) scale(1.1);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0) scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes charAppearFromLeft {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateX(-80rpx) scale(0.5);
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
transform: translateX(10rpx) scale(1.1);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateX(0) scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes charAppearFromRight {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateX(80rpx) scale(0.5);
|
|
||||||
}
|
|
||||||
60% {
|
|
||||||
transform: translateX(-10rpx) scale(1.1);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateX(0) scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -2,7 +2,7 @@
|
|||||||
<view>
|
<view>
|
||||||
<!-- 祝福动画 -->
|
<!-- 祝福动画 -->
|
||||||
<view v-if="showBlessing" class="blessing-container">
|
<view v-if="showBlessing" class="blessing-container">
|
||||||
<view class="blessing-animation">
|
<view class="blessing-animation" :class="currentBlessing.type">
|
||||||
<view class="blessing-emoji">{{ currentBlessing.emoji }}</view>
|
<view class="blessing-emoji">{{ currentBlessing.emoji }}</view>
|
||||||
<view v-if="currentBlessing.type === 'sheep'" class="blessing-subtitle">小羊祝你</view>
|
<view v-if="currentBlessing.type === 'sheep'" class="blessing-subtitle">小羊祝你</view>
|
||||||
<view class="blessing-text">
|
<view class="blessing-text">
|
||||||
@ -117,8 +117,33 @@ const blessings = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
emoji: '🐴',
|
emoji: '🐴',
|
||||||
chars: ['马', '到', '功', '成'],
|
chars: ['一', '马', '当', '先'],
|
||||||
type: 'horse'
|
type: 'horse'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
emoji: '🍊',
|
||||||
|
chars: ['心', '想', '事', '橙'],
|
||||||
|
type: 'orange'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
emoji: '🐵',
|
||||||
|
chars: ['财', '源', '广', '进'],
|
||||||
|
type: 'monkey'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
emoji: '🐮',
|
||||||
|
chars: ['牛', '气', '冲', '天'],
|
||||||
|
type: 'ox'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
emoji: '🐶',
|
||||||
|
chars: ['旺', '旺', '旺', '旺'],
|
||||||
|
type: 'dog'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
emoji: '🐔',
|
||||||
|
chars: ['吉', '祥', '如', '意'],
|
||||||
|
type: 'chicken'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
const currentBlessing = ref(blessings[0])
|
const currentBlessing = ref(blessings[0])
|
||||||
@ -242,17 +267,16 @@ function handleConfirm() {
|
|||||||
|
|
||||||
.blessing-container {
|
.blessing-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 50%;
|
top: 0;
|
||||||
left: 50%;
|
left: 0;
|
||||||
transform: translate(-50%, -50%);
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
z-index: 10000;
|
z-index: 10000;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 20rpx;
|
padding: 20rpx;
|
||||||
width: 100%;
|
|
||||||
max-width: 600rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.blessing-animation {
|
.blessing-animation {
|
||||||
@ -285,13 +309,38 @@ function handleConfirm() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 小羊动画 - 弹跳出现
|
// 小羊动画 - 弹跳出现
|
||||||
.blessing-animation:has(.blessing-subtitle) .blessing-emoji {
|
.blessing-animation.sheep .blessing-emoji {
|
||||||
animation: emojiBounce 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
animation: emojiBounce 1.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 小马动画 - 从左边跑步进场
|
// 小马动画 - 从左边跑步进场
|
||||||
.blessing-animation:not(:has(.blessing-subtitle)) .blessing-emoji {
|
.blessing-animation.horse .blessing-emoji {
|
||||||
animation: emojiRun 0.8s ease-out;
|
animation: emojiRun 1.5s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 橙子动画 - 缩放旋转出现(微信小程序优化版)
|
||||||
|
.blessing-animation.orange .blessing-emoji {
|
||||||
|
animation: emojiRotate 1.5s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 猴子动画 - 跳跃摇摆出现
|
||||||
|
.blessing-animation.monkey .blessing-emoji {
|
||||||
|
animation: emojiSwing 1.5s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 牛动画 - 冲撞弹跳出现
|
||||||
|
.blessing-animation.ox .blessing-emoji {
|
||||||
|
animation: emojiCharge 1.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 狗动画 - 摇尾巴跳动出现
|
||||||
|
.blessing-animation.dog .blessing-emoji {
|
||||||
|
animation: emojiWag 1.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 鸡动画 - 啄米点头出现
|
||||||
|
.blessing-animation.chicken .blessing-emoji {
|
||||||
|
animation: emojiPeck 1.5s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes emojiBounce {
|
@keyframes emojiBounce {
|
||||||
@ -325,6 +374,120 @@ function handleConfirm() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes emojiRotate {
|
||||||
|
0% {
|
||||||
|
transform: scale(0) rotate(0deg);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
transform: scale(1.2) rotate(180deg);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
transform: scale(0.95) rotate(360deg);
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
transform: scale(1.05) rotate(360deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1) rotate(360deg);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes emojiSwing {
|
||||||
|
0% {
|
||||||
|
transform: scale(0) translateY(-50rpx) rotate(-30deg);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
transform: scale(1.15) translateY(10rpx) rotate(20deg);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
transform: scale(0.9) translateY(-5rpx) rotate(-10deg);
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
transform: scale(1.05) translateY(3rpx) rotate(5deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1) translateY(0) rotate(0deg);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes emojiCharge {
|
||||||
|
0% {
|
||||||
|
transform: scale(0) translateX(-100rpx) rotate(45deg);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.3) translateX(20rpx) rotate(-20deg);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
transform: scale(0.85) translateX(-10rpx) rotate(10deg);
|
||||||
|
}
|
||||||
|
85% {
|
||||||
|
transform: scale(1.08) translateX(5rpx) rotate(-5deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1) translateX(0) rotate(0deg);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes emojiWag {
|
||||||
|
0% {
|
||||||
|
transform: scale(0) translateY(-30rpx) rotate(-15deg);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
30% {
|
||||||
|
transform: scale(1.2) translateY(0) rotate(15deg);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(0.9) translateY(-15rpx) rotate(-15deg);
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
transform: scale(1.1) translateY(0) rotate(15deg);
|
||||||
|
}
|
||||||
|
85% {
|
||||||
|
transform: scale(0.95) translateY(-5rpx) rotate(-5deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1) translateY(0) rotate(0deg);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes emojiPeck {
|
||||||
|
0% {
|
||||||
|
transform: scale(0) translateY(-40rpx) rotate(0deg);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
transform: scale(1.15) translateY(10rpx) rotate(10deg);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
transform: scale(0.85) translateY(-5rpx) rotate(-10deg);
|
||||||
|
}
|
||||||
|
55% {
|
||||||
|
transform: scale(1.1) translateY(8rpx) rotate(8deg);
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
transform: scale(0.9) translateY(-3rpx) rotate(-8deg);
|
||||||
|
}
|
||||||
|
85% {
|
||||||
|
transform: scale(1.05) translateY(2rpx) rotate(3deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1) translateY(0) rotate(0deg);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.blessing-subtitle {
|
.blessing-subtitle {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: #FF9500;
|
color: #FF9500;
|
||||||
|
|||||||
@ -39,22 +39,14 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 支付弹窗 -->
|
<!-- 支付弹窗已移至父组件,避免在 scroll-view 内导致定位问题 -->
|
||||||
<PaymentPopup
|
|
||||||
v-model:visible="paymentVisible"
|
|
||||||
:amount="totalAmount"
|
|
||||||
:coupons="coupons"
|
|
||||||
:showCards="false"
|
|
||||||
@confirm="onPaymentConfirm"
|
|
||||||
/>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, watch } from 'vue'
|
import { ref, computed, onMounted, watch } from 'vue'
|
||||||
import { getIssueChoices, getUserCoupons, joinLottery, createWechatOrder, getLotteryResult } from '@/api/appUser'
|
import { getIssueChoices, getUserCoupons, joinLottery, createWechatOrder, getLotteryResult } from '@/api/appUser'
|
||||||
import PaymentPopup from '@/components/PaymentPopup.vue'
|
|
||||||
import { requestLotterySubscription } from '@/utils/subscribe'
|
import { requestLotterySubscription } from '@/utils/subscribe'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -66,20 +58,35 @@ const props = defineProps({
|
|||||||
hideActionBar: { type: Boolean, default: false } // 支持隐藏内置操作栏
|
hideActionBar: { type: Boolean, default: false } // 支持隐藏内置操作栏
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['payment-success', 'selection-change'])
|
const emit = defineEmits(['payment-success', 'selection-change', 'payment-visible-change', 'payment-amount-change', 'payment-coupons-change'])
|
||||||
|
|
||||||
const choices = ref([])
|
const choices = ref([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const selectedItems = ref([])
|
const selectedItems = ref([])
|
||||||
const paymentVisible = ref(false)
|
const paymentVisible = ref(false)
|
||||||
|
|
||||||
|
// 监听支付弹窗状态变化,通知父组件
|
||||||
|
watch(paymentVisible, (newVal) => {
|
||||||
|
emit('payment-visible-change', newVal)
|
||||||
|
})
|
||||||
|
|
||||||
// 模拟优惠券和道具卡数据,实际项目中可能需要从接口获取
|
// 模拟优惠券和道具卡数据,实际项目中可能需要从接口获取
|
||||||
const coupons = ref([])
|
const coupons = ref([])
|
||||||
|
|
||||||
const totalAmount = computed(() => {
|
const totalAmount = computed(() => {
|
||||||
return (selectedItems.value.length * props.pricePerDraw).toFixed(2)
|
return (selectedItems.value.length * props.pricePerDraw).toFixed(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 监听支付金额变化,通知父组件
|
||||||
|
watch(totalAmount, (newVal) => {
|
||||||
|
emit('payment-amount-change', newVal)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听优惠券变化,通知父组件
|
||||||
|
watch(coupons, (newVal) => {
|
||||||
|
emit('payment-coupons-change', newVal)
|
||||||
|
})
|
||||||
|
|
||||||
const disabled = computed(() => !!props.disabled)
|
const disabled = computed(() => !!props.disabled)
|
||||||
const disabledMessage = computed(() => props.disabledText || '暂不可下单')
|
const disabledMessage = computed(() => props.disabledText || '暂不可下单')
|
||||||
|
|
||||||
@ -168,39 +175,43 @@ function handleSelect(item) {
|
|||||||
emit('selection-change', [...selectedItems.value])
|
emit('selection-change', [...selectedItems.value])
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBuy() {
|
async function handleBuy() {
|
||||||
if (disabled.value) {
|
if (disabled.value) {
|
||||||
uni.showToast({ title: disabledMessage.value, icon: 'none' })
|
uni.showToast({ title: disabledMessage.value, icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (selectedItems.value.length === 0) return
|
if (selectedItems.value.length === 0) return
|
||||||
|
|
||||||
|
// 主动发送金额和优惠券数据
|
||||||
|
emit('payment-amount-change', totalAmount.value)
|
||||||
|
await fetchCoupons()
|
||||||
paymentVisible.value = true
|
paymentVisible.value = true
|
||||||
fetchCoupons()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRandomOne() {
|
async function handleRandomOne() {
|
||||||
if (disabled.value) {
|
if (disabled.value) {
|
||||||
uni.showToast({ title: disabledMessage.value, icon: 'none' })
|
uni.showToast({ title: disabledMessage.value, icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const available = choices.value.filter(item =>
|
const available = choices.value.filter(item =>
|
||||||
!item.is_sold && item.status !== 'sold' && !isSelected(item)
|
!item.is_sold && item.status !== 'sold' && !isSelected(item)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (available.length === 0) {
|
if (available.length === 0) {
|
||||||
uni.showToast({ title: '没有可选位置了', icon: 'none' })
|
uni.showToast({ title: '没有可选位置了', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const randomIndex = Math.floor(Math.random() * available.length)
|
const randomIndex = Math.floor(Math.random() * available.length)
|
||||||
const randomItem = available[randomIndex]
|
const randomItem = available[randomIndex]
|
||||||
|
|
||||||
// 选中该位置
|
// 选中该位置
|
||||||
selectedItems.value.push(randomItem)
|
selectedItems.value.push(randomItem)
|
||||||
|
|
||||||
// 立即弹出支付
|
// 主动发送金额和优惠券数据
|
||||||
|
emit('payment-amount-change', totalAmount.value)
|
||||||
|
await fetchCoupons()
|
||||||
paymentVisible.value = true
|
paymentVisible.value = true
|
||||||
fetchCoupons()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -222,9 +233,12 @@ async function fetchCoupons() {
|
|||||||
amount: Number(yuan).toFixed(2)
|
amount: Number(yuan).toFixed(2)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
// 主动发送优惠券数据给父组件
|
||||||
|
emit('payment-coupons-change', coupons.value)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('fetchCoupons error', e)
|
console.error('fetchCoupons error', e)
|
||||||
coupons.value = []
|
coupons.value = []
|
||||||
|
emit('payment-coupons-change', [])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,6 +343,10 @@ async function onPaymentConfirm(paymentData) {
|
|||||||
defineExpose({
|
defineExpose({
|
||||||
handleRandomOne,
|
handleRandomOne,
|
||||||
handleBuy,
|
handleBuy,
|
||||||
|
onPaymentConfirm,
|
||||||
|
setPaymentVisible: (visible) => {
|
||||||
|
paymentVisible.value = visible
|
||||||
|
},
|
||||||
selectedItems: () => selectedItems.value
|
selectedItems: () => selectedItems.value
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -111,10 +111,14 @@ defineProps({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.bg-image {
|
.bg-image {
|
||||||
width: 100%;
|
width: 115%;
|
||||||
height: 100%;
|
height: 115%;
|
||||||
|
max-width: 115%;
|
||||||
|
max-height: 115%;
|
||||||
|
position: absolute;
|
||||||
|
top: -7.5%;
|
||||||
|
left: -7.5%;
|
||||||
filter: blur(40rpx) brightness(0.85) saturate(1.1);
|
filter: blur(40rpx) brightness(0.85) saturate(1.1);
|
||||||
transform: scale(1.15);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-mask {
|
.bg-mask {
|
||||||
|
|||||||
@ -43,10 +43,10 @@
|
|||||||
:tabs="[{key: 'pool', label: '本机奖池'}, {key: 'records', label: '购买记录'}]"
|
:tabs="[{key: 'pool', label: '本机奖池'}, {key: 'records', label: '购买记录'}]"
|
||||||
>
|
>
|
||||||
<!-- 奖池预览 -->
|
<!-- 奖池预览 -->
|
||||||
<RewardsPreview
|
<RewardsPreview
|
||||||
v-if="tabActive === 'pool'"
|
v-if="tabActive === 'pool'"
|
||||||
:rewards="currentIssueRewards"
|
:rewards="previewRewards"
|
||||||
:grouped="true"
|
:grouped="detail?.play_type !== 'match'"
|
||||||
@view-all="openRewardsPopup"
|
@view-all="openRewardsPopup"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -196,6 +196,7 @@ import CabinetPreviewPopup from '@/components/activity/CabinetPreviewPopup.vue'
|
|||||||
import LotteryResultPopup from '@/components/activity/LotteryResultPopup.vue'
|
import LotteryResultPopup from '@/components/activity/LotteryResultPopup.vue'
|
||||||
import { getActivityDetail, getActivityIssues, getActivityIssueRewards, getUserCoupons, getItemCards, createWechatOrder, getMatchingCardTypes, createMatchingPreorder, checkMatchingGame, getIssueDrawLogs, getMatchingGameCards } from '../../../api/appUser'
|
import { getActivityDetail, getActivityIssues, getActivityIssueRewards, getUserCoupons, getItemCards, createWechatOrder, getMatchingCardTypes, createMatchingPreorder, checkMatchingGame, getIssueDrawLogs, getMatchingGameCards } from '../../../api/appUser'
|
||||||
import { levelToAlpha } from '@/utils/activity'
|
import { levelToAlpha } from '@/utils/activity'
|
||||||
|
import { vibrateShort } from '@/utils/vibrate.js'
|
||||||
|
|
||||||
const detail = ref({})
|
const detail = ref({})
|
||||||
const statusText = ref('')
|
const statusText = ref('')
|
||||||
@ -314,27 +315,64 @@ const currentIssueRewards = computed(() => {
|
|||||||
return (iid && Array.isArray(m[iid])) ? m[iid] : []
|
return (iid && Array.isArray(m[iid])) ? m[iid] : []
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 用于奖池预览的 rewards(已排序)
|
||||||
|
const previewRewards = computed(() => {
|
||||||
|
const isMatchType = detail.value?.play_type === 'match'
|
||||||
|
|
||||||
|
if (isMatchType) {
|
||||||
|
// 对对碰模式:按 min_score 升序
|
||||||
|
return [...currentIssueRewards.value].sort((a, b) => (a.min_score - b.min_score))
|
||||||
|
} else {
|
||||||
|
// 普通模式:返回原数组
|
||||||
|
return currentIssueRewards.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const rewardGroups = computed(() => {
|
const rewardGroups = computed(() => {
|
||||||
|
const isMatchType = detail.value?.play_type === 'match'
|
||||||
|
|
||||||
|
// 对对碰模式:不分组,直接按 min_score 平铺所有奖品
|
||||||
|
if (isMatchType) {
|
||||||
|
// 先按 min_score 升序排序
|
||||||
|
const sortedRewards = [...currentIssueRewards.value].sort((a, b) => (a.min_score - b.min_score))
|
||||||
|
|
||||||
|
// 将每个奖品作为一个单独的分组
|
||||||
|
return sortedRewards.map(item => ({
|
||||||
|
level: `${item.min_score}对子`,
|
||||||
|
rewards: [item],
|
||||||
|
totalPercent: item.percent.toFixed(1)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 普通模式:按原来的分组逻辑
|
||||||
const groups = {}
|
const groups = {}
|
||||||
currentIssueRewards.value.forEach(item => {
|
currentIssueRewards.value.forEach(item => {
|
||||||
let level = item.level || '赏'
|
let level = item.level || '赏'
|
||||||
|
|
||||||
|
// 普通模式:只显示 min_score > 0 的奖品
|
||||||
if (item.min_score > 0 && level !== 'BOSS') {
|
if (item.min_score > 0 && level !== 'BOSS') {
|
||||||
level = `${item.min_score}对子`
|
level = `${item.min_score}对子`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!groups[level]) groups[level] = []
|
if (!groups[level]) groups[level] = []
|
||||||
groups[level].push(item)
|
groups[level].push(item)
|
||||||
})
|
})
|
||||||
|
|
||||||
return Object.keys(groups).sort((a, b) => {
|
return Object.keys(groups).sort((a, b) => {
|
||||||
|
// 普通模式:Last 和 BOSS 优先
|
||||||
if (a === 'Last' || a === 'BOSS') return -1
|
if (a === 'Last' || a === 'BOSS') return -1
|
||||||
if (b === 'Last' || b === 'BOSS') return 1
|
if (b === 'Last' || b === 'BOSS') return 1
|
||||||
// 分组之间按该组最小 weight 排序(升序)
|
|
||||||
|
// 普通模式:分组之间按该组最小 weight 排序(升序)
|
||||||
const minWeightA = Math.min(...groups[a].map(item => item.weight || 0))
|
const minWeightA = Math.min(...groups[a].map(item => item.weight || 0))
|
||||||
const minWeightB = Math.min(...groups[b].map(item => item.weight || 0))
|
const minWeightB = Math.min(...groups[b].map(item => item.weight || 0))
|
||||||
return minWeightA - minWeightB
|
return minWeightA - minWeightB
|
||||||
}).map(key => {
|
}).map(key => {
|
||||||
const rewards = groups[key]
|
const rewards = groups[key]
|
||||||
// 分组内按 weight 升序排列
|
|
||||||
|
// 普通模式:分组内按 weight 升序排列
|
||||||
rewards.sort((a, b) => (a.weight - b.weight))
|
rewards.sort((a, b) => (a.weight - b.weight))
|
||||||
|
|
||||||
const total = rewards.reduce((sum, item) => sum + (Number(item.percent) || 0), 0)
|
const total = rewards.reduce((sum, item) => sum + (Number(item.percent) || 0), 0)
|
||||||
return {
|
return {
|
||||||
level: key,
|
level: key,
|
||||||
@ -511,7 +549,7 @@ function normalizeIssues(list) {
|
|||||||
status_text: i.status_text ?? (i.status === 1 ? '进行中' : i.status === 0 ? '未开始' : i.status === 2 ? '已结束' : '')
|
status_text: i.status_text ?? (i.status === 1 ? '进行中' : i.status === 0 ? '未开始' : i.status === 2 ? '已结束' : '')
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
function normalizeRewards(list) {
|
function normalizeRewards(list, playType = 'normal') {
|
||||||
const arr = unwrap(list)
|
const arr = unwrap(list)
|
||||||
const items = arr.map((i, idx) => ({
|
const items = arr.map((i, idx) => ({
|
||||||
...i, // Spread original properties first
|
...i, // Spread original properties first
|
||||||
@ -528,8 +566,16 @@ function normalizeRewards(list) {
|
|||||||
...it,
|
...it,
|
||||||
percent: total > 0 ? Math.round((it.weight / total) * 1000) / 10 : 0
|
percent: total > 0 ? Math.round((it.weight / total) * 1000) / 10 : 0
|
||||||
}))
|
}))
|
||||||
// 按 weight 升序排列(从小到大)
|
|
||||||
enriched.sort((a, b) => (a.weight - b.weight))
|
// 根据 play_type 决定排序方式
|
||||||
|
if (playType === 'match') {
|
||||||
|
// 对对碰:按 min_score 升序排列,不过滤 min_score=0 的奖品
|
||||||
|
enriched.sort((a, b) => (a.min_score - b.min_score))
|
||||||
|
} else {
|
||||||
|
// 普通活动:按 weight 升序排列(从小到大)
|
||||||
|
enriched.sort((a, b) => (a.weight - b.weight))
|
||||||
|
}
|
||||||
|
|
||||||
return enriched
|
return enriched
|
||||||
}
|
}
|
||||||
async function fetchRewardsForIssues(activityId) {
|
async function fetchRewardsForIssues(activityId) {
|
||||||
@ -537,10 +583,13 @@ async function fetchRewardsForIssues(activityId) {
|
|||||||
const promises = list.map(it => getActivityIssueRewards(activityId, it.id))
|
const promises = list.map(it => getActivityIssueRewards(activityId, it.id))
|
||||||
const results = await Promise.allSettled(promises)
|
const results = await Promise.allSettled(promises)
|
||||||
|
|
||||||
|
// 获取 play_type
|
||||||
|
const playType = detail.value?.play_type || 'normal'
|
||||||
|
|
||||||
results.forEach((res, i) => {
|
results.forEach((res, i) => {
|
||||||
const issueId = list[i] && list[i].id
|
const issueId = list[i] && list[i].id
|
||||||
if (!issueId) return
|
if (!issueId) return
|
||||||
const value = res.status === 'fulfilled' ? normalizeRewards(res.value) : []
|
const value = res.status === 'fulfilled' ? normalizeRewards(res.value, playType) : []
|
||||||
rewardsMap.value = { ...(rewardsMap.value || {}), [issueId]: value }
|
rewardsMap.value = { ...(rewardsMap.value || {}), [issueId]: value }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -847,7 +896,7 @@ function drawOne() {
|
|||||||
function manualDraw() {
|
function manualDraw() {
|
||||||
if (gameLoading.value) return
|
if (gameLoading.value) return
|
||||||
if (!canManualDraw.value) return
|
if (!canManualDraw.value) return
|
||||||
uni.vibrateShort({ type: 'light' })
|
vibrateShort()
|
||||||
drawOne()
|
drawOne()
|
||||||
chance.value = Math.max(0, Number(chance.value || 0) - 1)
|
chance.value = Math.max(0, Number(chance.value || 0) - 1)
|
||||||
pickedHandIndex.value = -1
|
pickedHandIndex.value = -1
|
||||||
@ -879,7 +928,7 @@ async function autoDrawIfStuck() {
|
|||||||
async function onCellTap(cell) {
|
async function onCellTap(cell) {
|
||||||
if (gameLoading.value) return
|
if (gameLoading.value) return
|
||||||
if (!cell || cell.empty) return
|
if (!cell || cell.empty) return
|
||||||
uni.vibrateShort({ type: 'light' })
|
vibrateShort()
|
||||||
const hi = Number(cell.handIndex)
|
const hi = Number(cell.handIndex)
|
||||||
if (!Number.isFinite(hi) || hi < 0) return
|
if (!Number.isFinite(hi) || hi < 0) return
|
||||||
|
|
||||||
@ -1019,7 +1068,7 @@ function onResultClose() {
|
|||||||
|
|
||||||
async function advanceOne() {
|
async function advanceOne() {
|
||||||
if (gameLoading.value) return
|
if (gameLoading.value) return
|
||||||
uni.vibrateShort({ type: 'light' })
|
vibrateShort()
|
||||||
const entry = gameEntry.value || null
|
const entry = gameEntry.value || null
|
||||||
const gameId = entry && entry.game_id ? String(entry.game_id) : ''
|
const gameId = entry && entry.game_id ? String(entry.game_id) : ''
|
||||||
if (!gameId) return
|
if (!gameId) return
|
||||||
@ -1077,7 +1126,7 @@ async function autoRun() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function onParticipate() {
|
async function onParticipate() {
|
||||||
uni.vibrateShort({ type: 'medium' })
|
vibrateShort()
|
||||||
const aid = activityId.value || ''
|
const aid = activityId.value || ''
|
||||||
const iid = currentIssueId.value || ''
|
const iid = currentIssueId.value || ''
|
||||||
if (!aid || !iid) { uni.showToast({ title: '期数未选择', icon: 'none' }); return }
|
if (!aid || !iid) { uni.showToast({ title: '期数未选择', icon: 'none' }); return }
|
||||||
@ -1123,7 +1172,7 @@ async function applyResumeEntry(entry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function onResumeGame() {
|
async function onResumeGame() {
|
||||||
uni.vibrateShort({ type: 'medium' })
|
vibrateShort()
|
||||||
const aid = activityId.value || ''
|
const aid = activityId.value || ''
|
||||||
const latest = syncResumeGame(aid)
|
const latest = syncResumeGame(aid)
|
||||||
if (!latest || !latest.entry || !latest.entry.game_id) return
|
if (!latest || !latest.entry || !latest.entry.game_id) return
|
||||||
|
|||||||
@ -51,16 +51,19 @@
|
|||||||
|
|
||||||
<!-- 选号组件 - 隐藏内置操作栏 -->
|
<!-- 选号组件 - 隐藏内置操作栏 -->
|
||||||
<view class="selector-body" v-if="activityId && currentIssueId">
|
<view class="selector-body" v-if="activityId && currentIssueId">
|
||||||
<YifanSelector
|
<YifanSelector
|
||||||
ref="yifanSelectorRef"
|
ref="yifanSelectorRef"
|
||||||
:activity-id="activityId"
|
:activity-id="activityId"
|
||||||
:issue-id="currentIssueId"
|
:issue-id="currentIssueId"
|
||||||
:price-per-draw="Number(detail.price_draw || 0) / 100"
|
:price-per-draw="Number(detail.price_draw || 0) / 100"
|
||||||
:disabled="!isOrderAllowed"
|
:disabled="!isOrderAllowed"
|
||||||
:disabled-text="orderBlockedReason"
|
:disabled-text="orderBlockedReason"
|
||||||
:hide-action-bar="true"
|
:hide-action-bar="true"
|
||||||
@payment-success="onPaymentSuccess"
|
@payment-success="onPaymentSuccess"
|
||||||
@selection-change="onSelectionChange"
|
@selection-change="onSelectionChange"
|
||||||
|
@payment-visible-change="onPaymentVisibleChange"
|
||||||
|
@payment-amount-change="onPaymentAmountChange"
|
||||||
|
@payment-coupons-change="onPaymentCouponsChange"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@ -68,7 +71,7 @@
|
|||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<!-- 固定底部操作栏 -->
|
<!-- 固定底部操作栏 -->
|
||||||
<view class="float-bar">
|
<view class="float-bar" v-show="!isPaymentVisible">
|
||||||
<view class="float-bar-inner">
|
<view class="float-bar-inner">
|
||||||
<view class="selection-info" v-if="selectedCount > 0">
|
<view class="selection-info" v-if="selectedCount > 0">
|
||||||
已选 <text class="highlight">{{ selectedCount }}</text> 个位置
|
已选 <text class="highlight">{{ selectedCount }}</text> 个位置
|
||||||
@ -112,6 +115,16 @@
|
|||||||
v-model:visible="cabinetVisible"
|
v-model:visible="cabinetVisible"
|
||||||
:activity-id="activityId"
|
:activity-id="activityId"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- 支付弹窗(从 YifanSelector 提升到这里,确保祝福动画位置正确) -->
|
||||||
|
<PaymentPopup
|
||||||
|
v-model:visible="paymentVisible"
|
||||||
|
:amount="paymentAmount"
|
||||||
|
:coupons="paymentCoupons"
|
||||||
|
:showCards="false"
|
||||||
|
@confirm="onPaymentConfirm"
|
||||||
|
@cancel="onPaymentCancel"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</ActivityPageLayout>
|
</ActivityPageLayout>
|
||||||
</template>
|
</template>
|
||||||
@ -130,6 +143,7 @@ import RulesPopup from '@/components/activity/RulesPopup.vue'
|
|||||||
import CabinetPreviewPopup from '@/components/activity/CabinetPreviewPopup.vue'
|
import CabinetPreviewPopup from '@/components/activity/CabinetPreviewPopup.vue'
|
||||||
import FlipGrid from '@/components/FlipGrid.vue'
|
import FlipGrid from '@/components/FlipGrid.vue'
|
||||||
import YifanSelector from '@/components/YifanSelector.vue'
|
import YifanSelector from '@/components/YifanSelector.vue'
|
||||||
|
import PaymentPopup from '@/components/PaymentPopup.vue'
|
||||||
// Composables
|
// Composables
|
||||||
import { useActivity, useIssues, useRewards, useRecords } from '../../composables'
|
import { useActivity, useIssues, useRewards, useRecords } from '../../composables'
|
||||||
// Utils
|
// Utils
|
||||||
@ -174,12 +188,53 @@ const showFlip = ref(false)
|
|||||||
const flipRef = ref(null)
|
const flipRef = ref(null)
|
||||||
const yifanSelectorRef = ref(null)
|
const yifanSelectorRef = ref(null)
|
||||||
const selectedCount = ref(0) // 从外部追踪选中数量
|
const selectedCount = ref(0) // 从外部追踪选中数量
|
||||||
|
const isPaymentVisible = ref(false) // 支付弹窗是否显示
|
||||||
|
const paymentVisible = ref(false) // 控制支付弹窗显示
|
||||||
|
const paymentAmount = ref('0') // 支付金额
|
||||||
|
const paymentCoupons = ref([]) // 可用优惠券
|
||||||
|
|
||||||
// 接收选中变化事件
|
// 接收选中变化事件
|
||||||
function onSelectionChange(items) {
|
function onSelectionChange(items) {
|
||||||
selectedCount.value = Array.isArray(items) ? items.length : 0
|
selectedCount.value = Array.isArray(items) ? items.length : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 接收支付弹窗显示状态变化(从 YifanSelector)
|
||||||
|
function onPaymentVisibleChange(visible) {
|
||||||
|
isPaymentVisible.value = visible
|
||||||
|
paymentVisible.value = visible
|
||||||
|
}
|
||||||
|
|
||||||
|
// 接收支付金额变化
|
||||||
|
function onPaymentAmountChange(amount) {
|
||||||
|
paymentAmount.value = amount
|
||||||
|
}
|
||||||
|
|
||||||
|
// 接收优惠券变化
|
||||||
|
function onPaymentCouponsChange(coupons) {
|
||||||
|
paymentCoupons.value = coupons
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支付确认处理(委托给 YifanSelector)
|
||||||
|
async function onPaymentConfirm(paymentData) {
|
||||||
|
if (yifanSelectorRef.value && yifanSelectorRef.value.onPaymentConfirm) {
|
||||||
|
await yifanSelectorRef.value.onPaymentConfirm(paymentData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支付取消处理
|
||||||
|
function onPaymentCancel() {
|
||||||
|
// PaymentPopup 会通过 v-model 自动更新 paymentVisible
|
||||||
|
// watch 会监听到变化并同步给 YifanSelector
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听支付弹窗状态变化,同步给 YifanSelector
|
||||||
|
watch(paymentVisible, (newVal) => {
|
||||||
|
// 当支付弹窗关闭时,通知 YifanSelector 更新内部状态
|
||||||
|
if (!newVal && yifanSelectorRef.value && yifanSelectorRef.value.setPaymentVisible) {
|
||||||
|
yifanSelectorRef.value.setPaymentVisible(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// 触发随机选号
|
// 触发随机选号
|
||||||
function handleRandomDraw() {
|
function handleRandomDraw() {
|
||||||
if (yifanSelectorRef.value && yifanSelectorRef.value.handleRandomOne) {
|
if (yifanSelectorRef.value && yifanSelectorRef.value.handleRandomOne) {
|
||||||
|
|||||||
@ -124,6 +124,7 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
|
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
|
||||||
import { getUserCoupons } from '../../api/appUser'
|
import { getUserCoupons } from '../../api/appUser'
|
||||||
|
import { vibrateShort } from '@/utils/vibrate.js'
|
||||||
|
|
||||||
const list = ref([])
|
const list = ref([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
@ -214,7 +215,7 @@ function getCouponClass() {
|
|||||||
// 切换Tab
|
// 切换Tab
|
||||||
function switchTab(tab) {
|
function switchTab(tab) {
|
||||||
if (currentTab.value === tab) return
|
if (currentTab.value === tab) return
|
||||||
uni.vibrateShort({ type: 'light' })
|
vibrateShort()
|
||||||
currentTab.value = tab
|
currentTab.value = tab
|
||||||
list.value = []
|
list.value = []
|
||||||
page.value = 1
|
page.value = 1
|
||||||
@ -271,7 +272,7 @@ async function fetchData(append = false) {
|
|||||||
|
|
||||||
// 去使用优惠券
|
// 去使用优惠券
|
||||||
function onUseCoupon(item) {
|
function onUseCoupon(item) {
|
||||||
uni.vibrateShort({ type: 'medium' })
|
vibrateShort()
|
||||||
// 通常跳转到首页或抽盒页
|
// 通常跳转到首页或抽盒页
|
||||||
uni.switchTab({
|
uni.switchTab({
|
||||||
url: '/pages/index/index'
|
url: '/pages/index/index'
|
||||||
|
|||||||
@ -125,6 +125,7 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { onLoad } from '@dcloudio/uni-app'
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
import { getItemCards } from '../../api/appUser'
|
import { getItemCards } from '../../api/appUser'
|
||||||
|
import { vibrateShort } from '@/utils/vibrate.js'
|
||||||
|
|
||||||
const list = ref([])
|
const list = ref([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
@ -193,7 +194,7 @@ function getCardIcon(type) {
|
|||||||
// 切换Tab
|
// 切换Tab
|
||||||
function switchTab(tab) {
|
function switchTab(tab) {
|
||||||
if (currentTab.value === tab) return
|
if (currentTab.value === tab) return
|
||||||
uni.vibrateShort({ type: 'light' })
|
vibrateShort()
|
||||||
currentTab.value = tab
|
currentTab.value = tab
|
||||||
list.value = []
|
list.value = []
|
||||||
page.value = 1
|
page.value = 1
|
||||||
@ -251,7 +252,7 @@ async function fetchData(append = false) {
|
|||||||
|
|
||||||
// 去使用道具卡
|
// 去使用道具卡
|
||||||
function onUseCard(item) {
|
function onUseCard(item) {
|
||||||
uni.vibrateShort({ type: 'medium' })
|
vibrateShort()
|
||||||
// 道具卡通常去首页或指定的活动页
|
// 道具卡通常去首页或指定的活动页
|
||||||
uni.switchTab({
|
uni.switchTab({
|
||||||
url: '/pages/index/index'
|
url: '/pages/index/index'
|
||||||
|
|||||||
@ -129,6 +129,7 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
|
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
|
||||||
import { getOrders, cancelOrder as cancelOrderApi, createWechatOrder } from '../../api/appUser'
|
import { getOrders, cancelOrder as cancelOrderApi, createWechatOrder } from '../../api/appUser'
|
||||||
|
import { vibrateShort } from '@/utils/vibrate.js'
|
||||||
|
|
||||||
const currentTab = ref('pending')
|
const currentTab = ref('pending')
|
||||||
const orders = ref([])
|
const orders = ref([])
|
||||||
@ -289,7 +290,7 @@ function getStatusClass(item) {
|
|||||||
|
|
||||||
function switchTab(tab) {
|
function switchTab(tab) {
|
||||||
if (currentTab.value === tab) return
|
if (currentTab.value === tab) return
|
||||||
uni.vibrateShort({ type: 'light' })
|
vibrateShort()
|
||||||
currentTab.value = tab
|
currentTab.value = tab
|
||||||
fetchOrders(false)
|
fetchOrders(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -128,6 +128,7 @@
|
|||||||
import { ref, reactive, computed } from 'vue'
|
import { ref, reactive, computed } from 'vue'
|
||||||
import { onLoad } from '@dcloudio/uni-app'
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
import { getTasks, getTaskProgress, claimTaskReward } from '../../api/appUser'
|
import { getTasks, getTaskProgress, claimTaskReward } from '../../api/appUser'
|
||||||
|
import { vibrateShort } from '@/utils/vibrate.js'
|
||||||
|
|
||||||
const tasks = ref([])
|
const tasks = ref([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
@ -315,8 +316,8 @@ function getTierProgressText(task, tier) {
|
|||||||
async function claimReward(task, tier) {
|
async function claimReward(task, tier) {
|
||||||
const key = `${task.id}_${tier.id}`
|
const key = `${task.id}_${tier.id}`
|
||||||
if (claiming[key]) return
|
if (claiming[key]) return
|
||||||
|
|
||||||
uni.vibrateShort({ type: 'medium' })
|
vibrateShort()
|
||||||
claiming[key] = true
|
claiming[key] = true
|
||||||
try {
|
try {
|
||||||
const userId = getUserId()
|
const userId = getUserId()
|
||||||
|
|||||||
@ -164,6 +164,7 @@
|
|||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { onShow, onReachBottom, onShareAppMessage, onPullDownRefresh } from '@dcloudio/uni-app'
|
import { onShow, onReachBottom, onShareAppMessage, onPullDownRefresh } from '@dcloudio/uni-app'
|
||||||
import { getInventory, getProductDetail, redeemInventory, requestShipping, cancelShipping, listAddresses, getShipments, createAddressShare } from '@/api/appUser'
|
import { getInventory, getProductDetail, redeemInventory, requestShipping, cancelShipping, listAddresses, getShipments, createAddressShare } from '@/api/appUser'
|
||||||
|
import { vibrateShort } from '@/utils/vibrate.js'
|
||||||
|
|
||||||
const currentTab = ref(0)
|
const currentTab = ref(0)
|
||||||
const aggregatedList = ref([])
|
const aggregatedList = ref([])
|
||||||
@ -514,7 +515,7 @@ async function loadInventory(uid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toggleSelect(item) {
|
function toggleSelect(item) {
|
||||||
uni.vibrateShort({ type: 'light' })
|
vibrateShort()
|
||||||
item.selected = !item.selected
|
item.selected = !item.selected
|
||||||
if (item.selected) {
|
if (item.selected) {
|
||||||
// 选中时默认数量为最大值
|
// 选中时默认数量为最大值
|
||||||
@ -529,7 +530,7 @@ function toggleSelect(item) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toggleSelectAll() {
|
function toggleSelectAll() {
|
||||||
uni.vibrateShort({ type: 'light' })
|
vibrateShort()
|
||||||
const newState = !isAllSelected.value
|
const newState = !isAllSelected.value
|
||||||
aggregatedList.value.forEach(item => {
|
aggregatedList.value.forEach(item => {
|
||||||
item.selected = newState
|
item.selected = newState
|
||||||
@ -554,7 +555,7 @@ function changeCount(item, delta) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function onRedeem() {
|
async function onRedeem() {
|
||||||
uni.vibrateShort({ type: 'medium' })
|
vibrateShort()
|
||||||
const user_id = uni.getStorageSync('user_id')
|
const user_id = uni.getStorageSync('user_id')
|
||||||
if (!user_id) return
|
if (!user_id) return
|
||||||
|
|
||||||
@ -602,7 +603,7 @@ async function onRedeem() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function onShip() {
|
async function onShip() {
|
||||||
uni.vibrateShort({ type: 'medium' })
|
vibrateShort()
|
||||||
const user_id = uni.getStorageSync('user_id')
|
const user_id = uni.getStorageSync('user_id')
|
||||||
if (!user_id) return
|
if (!user_id) return
|
||||||
|
|
||||||
@ -685,7 +686,7 @@ onShareAppMessage((res) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
async function onInvite(item) {
|
async function onInvite(item) {
|
||||||
uni.vibrateShort({ type: 'medium' })
|
vibrateShort()
|
||||||
const user_id = uni.getStorageSync('user_id')
|
const user_id = uni.getStorageSync('user_id')
|
||||||
if (!user_id) {
|
if (!user_id) {
|
||||||
uni.navigateTo({ url: '/pages/login/index' })
|
uni.navigateTo({ url: '/pages/login/index' })
|
||||||
|
|||||||
@ -149,6 +149,7 @@ import { ref, computed, onMounted, onUnmounted } from 'vue'
|
|||||||
import { onLoad } from '@dcloudio/uni-app'
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
import { request } from '../../utils/request'
|
import { request } from '../../utils/request'
|
||||||
import { wechatLogin, bindPhone, getUserStats, getPointsBalance, sendSmsCode, smsLogin } from '../../api/appUser'
|
import { wechatLogin, bindPhone, getUserStats, getPointsBalance, sendSmsCode, smsLogin } from '../../api/appUser'
|
||||||
|
import { vibrateShort } from '@/utils/vibrate.js'
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const agreementChecked = ref(false)
|
const agreementChecked = ref(false)
|
||||||
@ -235,7 +236,7 @@ function toPurchaseAgreement() {
|
|||||||
async function handleSendCode() {
|
async function handleSendCode() {
|
||||||
if (!agreementChecked.value) {
|
if (!agreementChecked.value) {
|
||||||
uni.showToast({ title: '请先同意用户协议', icon: 'none' })
|
uni.showToast({ title: '请先同意用户协议', icon: 'none' })
|
||||||
uni.vibrateShort()
|
vibrateShort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,7 +277,7 @@ async function handleSendCode() {
|
|||||||
async function handleSmsLogin() {
|
async function handleSmsLogin() {
|
||||||
if (!agreementChecked.value) {
|
if (!agreementChecked.value) {
|
||||||
uni.showToast({ title: '请先同意用户协议', icon: 'none' })
|
uni.showToast({ title: '请先同意用户协议', icon: 'none' })
|
||||||
uni.vibrateShort()
|
vibrateShort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,7 +316,7 @@ async function handleSmsLogin() {
|
|||||||
function onGetPhoneNumber(e) {
|
function onGetPhoneNumber(e) {
|
||||||
if (!agreementChecked.value) {
|
if (!agreementChecked.value) {
|
||||||
uni.showToast({ title: '请先同意用户协议', icon: 'none' })
|
uni.showToast({ title: '请先同意用户协议', icon: 'none' })
|
||||||
uni.vibrateShort()
|
vibrateShort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,40 +0,0 @@
|
|||||||
/**
|
|
||||||
* 祝福动画工具
|
|
||||||
* 用于在支付弹窗等场景显示祝福动画
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 使用简单的全局变量来存储回调函数
|
|
||||||
let blessingCallback = null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 显示祝福动画
|
|
||||||
* @param {Object} options - 配置选项
|
|
||||||
* @param {string} options.type - 祝福类型 'sheep' | 'horse' | 'random'
|
|
||||||
*/
|
|
||||||
export function showBlessing(options = {}) {
|
|
||||||
const type = options.type || 'random'
|
|
||||||
console.log('[showBlessing] 触发祝福动画, type:', type)
|
|
||||||
|
|
||||||
if (blessingCallback) {
|
|
||||||
blessingCallback({ type })
|
|
||||||
} else {
|
|
||||||
console.warn('[showBlessing] 没有注册的监听器')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册祝福动画监听
|
|
||||||
* @param {Function} callback - 回调函数
|
|
||||||
*/
|
|
||||||
export function registerBlessing(callback) {
|
|
||||||
blessingCallback = callback
|
|
||||||
console.log('[registerBlessing] 祝福动画监听器已注册')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 移除祝福动画监听
|
|
||||||
*/
|
|
||||||
export function unregisterBlessing() {
|
|
||||||
blessingCallback = null
|
|
||||||
console.log('[unregisterBlessing] 祝福动画监听器已移除')
|
|
||||||
}
|
|
||||||
52
utils/vibrate.js
Normal file
52
utils/vibrate.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* 震动工具函数
|
||||||
|
* 统一处理不同平台的震动API兼容性
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短震动
|
||||||
|
* 微信小程序不支持 type 参数,会忽略该参数
|
||||||
|
* @param {Object} options - 配置项
|
||||||
|
* @param {string} options.type - 震动类型 'light' | 'medium' | 'heavy'(仅在部分平台有效)
|
||||||
|
*/
|
||||||
|
export function vibrateShort(options = {}) {
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
// 微信小程序不支持 type 参数,直接调用
|
||||||
|
uni.vibrateShort({
|
||||||
|
fail: (err) => {
|
||||||
|
console.warn('[vibrateShort] 震动失败:', err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef H5 || APP-PLUS
|
||||||
|
// H5和App可能支持 type 参数
|
||||||
|
uni.vibrateShort({
|
||||||
|
...options,
|
||||||
|
fail: (err) => {
|
||||||
|
console.warn('[vibrateShort] 震动失败:', err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef MP-ALIPAY || MP-BAIDU || MP-TOUTIAO
|
||||||
|
// 其他小程序平台,尝试传递参数
|
||||||
|
uni.vibrateShort({
|
||||||
|
...options,
|
||||||
|
fail: (err) => {
|
||||||
|
console.warn('[vibrateShort] 震动失败:', err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 长震动
|
||||||
|
*/
|
||||||
|
export function vibrateLong() {
|
||||||
|
uni.vibrateLong({
|
||||||
|
fail: (err) => {
|
||||||
|
console.warn('[vibrateLong] 震动失败:', err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user