diff --git a/components/BlessingAnimation.vue b/components/BlessingAnimation.vue
deleted file mode 100644
index 01c093e..0000000
--- a/components/BlessingAnimation.vue
+++ /dev/null
@@ -1,251 +0,0 @@
-
-
-
- {{ currentBlessing.emoji }}
- 小羊祝你
-
-
- {{ char }}
-
-
-
-
-
-
-
-
-
diff --git a/components/BlessingFloat.vue b/components/BlessingFloat.vue
deleted file mode 100644
index bbcaccb..0000000
--- a/components/BlessingFloat.vue
+++ /dev/null
@@ -1,233 +0,0 @@
-
-
-
- {{ currentBlessing.emoji }}
- 小羊祝你
-
-
- {{ char }}
-
-
-
-
-
-
-
-
-
diff --git a/components/BlessingPopup.vue b/components/BlessingPopup.vue
deleted file mode 100644
index 7cc58b5..0000000
--- a/components/BlessingPopup.vue
+++ /dev/null
@@ -1,267 +0,0 @@
-
-
-
-
-
-
-
diff --git a/components/PaymentPopup.vue b/components/PaymentPopup.vue
index 29ee05b..518ab75 100644
--- a/components/PaymentPopup.vue
+++ b/components/PaymentPopup.vue
@@ -2,7 +2,7 @@
-
+
{{ currentBlessing.emoji }}
小羊祝你
@@ -117,8 +117,33 @@ const blessings = [
},
{
emoji: '🐴',
- chars: ['马', '到', '功', '成'],
+ chars: ['一', '马', '当', '先'],
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])
@@ -242,17 +267,16 @@ function handleConfirm() {
.blessing-container {
position: fixed;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
z-index: 10000;
pointer-events: none;
display: flex;
justify-content: center;
align-items: center;
padding: 20rpx;
- width: 100%;
- max-width: 600rpx;
}
.blessing-animation {
@@ -285,13 +309,38 @@ function handleConfirm() {
}
// 小羊动画 - 弹跳出现
-.blessing-animation:has(.blessing-subtitle) .blessing-emoji {
- animation: emojiBounce 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
+.blessing-animation.sheep .blessing-emoji {
+ animation: emojiBounce 1.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
// 小马动画 - 从左边跑步进场
-.blessing-animation:not(:has(.blessing-subtitle)) .blessing-emoji {
- animation: emojiRun 0.8s ease-out;
+.blessing-animation.horse .blessing-emoji {
+ 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 {
@@ -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 {
font-size: 28rpx;
color: #FF9500;
diff --git a/components/YifanSelector.vue b/components/YifanSelector.vue
index 3b192c1..23ebbc3 100644
--- a/components/YifanSelector.vue
+++ b/components/YifanSelector.vue
@@ -39,22 +39,14 @@
-
-
-
+
+
diff --git a/components/activity/ActivityPageLayout.vue b/components/activity/ActivityPageLayout.vue
index 46c26ce..5bf1e5c 100644
--- a/components/activity/ActivityPageLayout.vue
+++ b/components/activity/ActivityPageLayout.vue
@@ -111,10 +111,14 @@ defineProps({
}
.bg-image {
- width: 100%;
- height: 100%;
+ width: 115%;
+ 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);
- transform: scale(1.15);
}
.bg-mask {
diff --git a/pages-activity/activity/duiduipeng/index.vue b/pages-activity/activity/duiduipeng/index.vue
index ccd3e63..f595ea9 100644
--- a/pages-activity/activity/duiduipeng/index.vue
+++ b/pages-activity/activity/duiduipeng/index.vue
@@ -43,10 +43,10 @@
:tabs="[{key: 'pool', label: '本机奖池'}, {key: 'records', label: '购买记录'}]"
>
-
@@ -196,6 +196,7 @@ import CabinetPreviewPopup from '@/components/activity/CabinetPreviewPopup.vue'
import LotteryResultPopup from '@/components/activity/LotteryResultPopup.vue'
import { getActivityDetail, getActivityIssues, getActivityIssueRewards, getUserCoupons, getItemCards, createWechatOrder, getMatchingCardTypes, createMatchingPreorder, checkMatchingGame, getIssueDrawLogs, getMatchingGameCards } from '../../../api/appUser'
import { levelToAlpha } from '@/utils/activity'
+import { vibrateShort } from '@/utils/vibrate.js'
const detail = ref({})
const statusText = ref('')
@@ -314,27 +315,64 @@ const currentIssueRewards = computed(() => {
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 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 = {}
currentIssueRewards.value.forEach(item => {
let level = item.level || '赏'
+
+ // 普通模式:只显示 min_score > 0 的奖品
if (item.min_score > 0 && level !== 'BOSS') {
level = `${item.min_score}对子`
}
+
if (!groups[level]) groups[level] = []
groups[level].push(item)
})
+
return Object.keys(groups).sort((a, b) => {
+ // 普通模式:Last 和 BOSS 优先
if (a === 'Last' || a === 'BOSS') return -1
if (b === 'Last' || b === 'BOSS') return 1
- // 分组之间按该组最小 weight 排序(升序)
+
+ // 普通模式:分组之间按该组最小 weight 排序(升序)
const minWeightA = Math.min(...groups[a].map(item => item.weight || 0))
const minWeightB = Math.min(...groups[b].map(item => item.weight || 0))
return minWeightA - minWeightB
}).map(key => {
const rewards = groups[key]
- // 分组内按 weight 升序排列
+
+ // 普通模式:分组内按 weight 升序排列
rewards.sort((a, b) => (a.weight - b.weight))
+
const total = rewards.reduce((sum, item) => sum + (Number(item.percent) || 0), 0)
return {
level: key,
@@ -511,7 +549,7 @@ function normalizeIssues(list) {
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 items = arr.map((i, idx) => ({
...i, // Spread original properties first
@@ -528,8 +566,16 @@ function normalizeRewards(list) {
...it,
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
}
async function fetchRewardsForIssues(activityId) {
@@ -537,10 +583,13 @@ async function fetchRewardsForIssues(activityId) {
const promises = list.map(it => getActivityIssueRewards(activityId, it.id))
const results = await Promise.allSettled(promises)
+ // 获取 play_type
+ const playType = detail.value?.play_type || 'normal'
+
results.forEach((res, i) => {
const issueId = list[i] && list[i].id
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 }
})
}
@@ -847,7 +896,7 @@ function drawOne() {
function manualDraw() {
if (gameLoading.value) return
if (!canManualDraw.value) return
- uni.vibrateShort({ type: 'light' })
+ vibrateShort()
drawOne()
chance.value = Math.max(0, Number(chance.value || 0) - 1)
pickedHandIndex.value = -1
@@ -879,7 +928,7 @@ async function autoDrawIfStuck() {
async function onCellTap(cell) {
if (gameLoading.value) return
if (!cell || cell.empty) return
- uni.vibrateShort({ type: 'light' })
+ vibrateShort()
const hi = Number(cell.handIndex)
if (!Number.isFinite(hi) || hi < 0) return
@@ -1019,7 +1068,7 @@ function onResultClose() {
async function advanceOne() {
if (gameLoading.value) return
- uni.vibrateShort({ type: 'light' })
+ vibrateShort()
const entry = gameEntry.value || null
const gameId = entry && entry.game_id ? String(entry.game_id) : ''
if (!gameId) return
@@ -1077,7 +1126,7 @@ async function autoRun() {
}
async function onParticipate() {
- uni.vibrateShort({ type: 'medium' })
+ vibrateShort()
const aid = activityId.value || ''
const iid = currentIssueId.value || ''
if (!aid || !iid) { uni.showToast({ title: '期数未选择', icon: 'none' }); return }
@@ -1123,7 +1172,7 @@ async function applyResumeEntry(entry) {
}
async function onResumeGame() {
- uni.vibrateShort({ type: 'medium' })
+ vibrateShort()
const aid = activityId.value || ''
const latest = syncResumeGame(aid)
if (!latest || !latest.entry || !latest.entry.game_id) return
diff --git a/pages-activity/activity/yifanshang/index.vue b/pages-activity/activity/yifanshang/index.vue
index 54f7356..a1c728c 100644
--- a/pages-activity/activity/yifanshang/index.vue
+++ b/pages-activity/activity/yifanshang/index.vue
@@ -51,16 +51,19 @@
-
@@ -68,7 +71,7 @@
-
+
已选 {{ selectedCount }} 个位置
@@ -112,6 +115,16 @@
v-model:visible="cabinetVisible"
:activity-id="activityId"
/>
+
+
+
@@ -130,6 +143,7 @@ import RulesPopup from '@/components/activity/RulesPopup.vue'
import CabinetPreviewPopup from '@/components/activity/CabinetPreviewPopup.vue'
import FlipGrid from '@/components/FlipGrid.vue'
import YifanSelector from '@/components/YifanSelector.vue'
+import PaymentPopup from '@/components/PaymentPopup.vue'
// Composables
import { useActivity, useIssues, useRewards, useRecords } from '../../composables'
// Utils
@@ -174,12 +188,53 @@ const showFlip = ref(false)
const flipRef = ref(null)
const yifanSelectorRef = ref(null)
const selectedCount = ref(0) // 从外部追踪选中数量
+const isPaymentVisible = ref(false) // 支付弹窗是否显示
+const paymentVisible = ref(false) // 控制支付弹窗显示
+const paymentAmount = ref('0') // 支付金额
+const paymentCoupons = ref([]) // 可用优惠券
// 接收选中变化事件
function onSelectionChange(items) {
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() {
if (yifanSelectorRef.value && yifanSelectorRef.value.handleRandomOne) {
diff --git a/pages-user/coupons/index.vue b/pages-user/coupons/index.vue
index 859e05e..11596a9 100644
--- a/pages-user/coupons/index.vue
+++ b/pages-user/coupons/index.vue
@@ -124,6 +124,7 @@
import { ref } from 'vue'
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
import { getUserCoupons } from '../../api/appUser'
+import { vibrateShort } from '@/utils/vibrate.js'
const list = ref([])
const loading = ref(false)
@@ -214,7 +215,7 @@ function getCouponClass() {
// 切换Tab
function switchTab(tab) {
if (currentTab.value === tab) return
- uni.vibrateShort({ type: 'light' })
+ vibrateShort()
currentTab.value = tab
list.value = []
page.value = 1
@@ -271,7 +272,7 @@ async function fetchData(append = false) {
// 去使用优惠券
function onUseCoupon(item) {
- uni.vibrateShort({ type: 'medium' })
+ vibrateShort()
// 通常跳转到首页或抽盒页
uni.switchTab({
url: '/pages/index/index'
diff --git a/pages-user/item-cards/index.vue b/pages-user/item-cards/index.vue
index ed5bd52..1617a05 100644
--- a/pages-user/item-cards/index.vue
+++ b/pages-user/item-cards/index.vue
@@ -125,6 +125,7 @@
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getItemCards } from '../../api/appUser'
+import { vibrateShort } from '@/utils/vibrate.js'
const list = ref([])
const loading = ref(false)
@@ -193,7 +194,7 @@ function getCardIcon(type) {
// 切换Tab
function switchTab(tab) {
if (currentTab.value === tab) return
- uni.vibrateShort({ type: 'light' })
+ vibrateShort()
currentTab.value = tab
list.value = []
page.value = 1
@@ -251,7 +252,7 @@ async function fetchData(append = false) {
// 去使用道具卡
function onUseCard(item) {
- uni.vibrateShort({ type: 'medium' })
+ vibrateShort()
// 道具卡通常去首页或指定的活动页
uni.switchTab({
url: '/pages/index/index'
diff --git a/pages-user/orders/index.vue b/pages-user/orders/index.vue
index 4f3a551..ac0f557 100644
--- a/pages-user/orders/index.vue
+++ b/pages-user/orders/index.vue
@@ -129,6 +129,7 @@
import { ref } from 'vue'
import { onLoad, onReachBottom } from '@dcloudio/uni-app'
import { getOrders, cancelOrder as cancelOrderApi, createWechatOrder } from '../../api/appUser'
+import { vibrateShort } from '@/utils/vibrate.js'
const currentTab = ref('pending')
const orders = ref([])
@@ -289,7 +290,7 @@ function getStatusClass(item) {
function switchTab(tab) {
if (currentTab.value === tab) return
- uni.vibrateShort({ type: 'light' })
+ vibrateShort()
currentTab.value = tab
fetchOrders(false)
}
diff --git a/pages-user/tasks/index.vue b/pages-user/tasks/index.vue
index 5858bf5..a547ff4 100644
--- a/pages-user/tasks/index.vue
+++ b/pages-user/tasks/index.vue
@@ -128,6 +128,7 @@
import { ref, reactive, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getTasks, getTaskProgress, claimTaskReward } from '../../api/appUser'
+import { vibrateShort } from '@/utils/vibrate.js'
const tasks = ref([])
const loading = ref(false)
@@ -315,8 +316,8 @@ function getTierProgressText(task, tier) {
async function claimReward(task, tier) {
const key = `${task.id}_${tier.id}`
if (claiming[key]) return
-
- uni.vibrateShort({ type: 'medium' })
+
+ vibrateShort()
claiming[key] = true
try {
const userId = getUserId()
diff --git a/pages/cabinet/index.vue b/pages/cabinet/index.vue
index 65c9f7c..5757d7c 100644
--- a/pages/cabinet/index.vue
+++ b/pages/cabinet/index.vue
@@ -164,6 +164,7 @@
import { ref, computed } from 'vue'
import { onShow, onReachBottom, onShareAppMessage, onPullDownRefresh } from '@dcloudio/uni-app'
import { getInventory, getProductDetail, redeemInventory, requestShipping, cancelShipping, listAddresses, getShipments, createAddressShare } from '@/api/appUser'
+import { vibrateShort } from '@/utils/vibrate.js'
const currentTab = ref(0)
const aggregatedList = ref([])
@@ -514,7 +515,7 @@ async function loadInventory(uid) {
}
function toggleSelect(item) {
- uni.vibrateShort({ type: 'light' })
+ vibrateShort()
item.selected = !item.selected
if (item.selected) {
// 选中时默认数量为最大值
@@ -529,7 +530,7 @@ function toggleSelect(item) {
}
function toggleSelectAll() {
- uni.vibrateShort({ type: 'light' })
+ vibrateShort()
const newState = !isAllSelected.value
aggregatedList.value.forEach(item => {
item.selected = newState
@@ -554,7 +555,7 @@ function changeCount(item, delta) {
}
async function onRedeem() {
- uni.vibrateShort({ type: 'medium' })
+ vibrateShort()
const user_id = uni.getStorageSync('user_id')
if (!user_id) return
@@ -602,7 +603,7 @@ async function onRedeem() {
}
async function onShip() {
- uni.vibrateShort({ type: 'medium' })
+ vibrateShort()
const user_id = uni.getStorageSync('user_id')
if (!user_id) return
@@ -685,7 +686,7 @@ onShareAppMessage((res) => {
})
async function onInvite(item) {
- uni.vibrateShort({ type: 'medium' })
+ vibrateShort()
const user_id = uni.getStorageSync('user_id')
if (!user_id) {
uni.navigateTo({ url: '/pages/login/index' })
diff --git a/pages/login/index.vue b/pages/login/index.vue
index 2b55baa..92c7292 100644
--- a/pages/login/index.vue
+++ b/pages/login/index.vue
@@ -149,6 +149,7 @@ import { ref, computed, onMounted, onUnmounted } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { request } from '../../utils/request'
import { wechatLogin, bindPhone, getUserStats, getPointsBalance, sendSmsCode, smsLogin } from '../../api/appUser'
+import { vibrateShort } from '@/utils/vibrate.js'
const loading = ref(false)
const agreementChecked = ref(false)
@@ -235,7 +236,7 @@ function toPurchaseAgreement() {
async function handleSendCode() {
if (!agreementChecked.value) {
uni.showToast({ title: '请先同意用户协议', icon: 'none' })
- uni.vibrateShort()
+ vibrateShort()
return
}
@@ -276,7 +277,7 @@ async function handleSendCode() {
async function handleSmsLogin() {
if (!agreementChecked.value) {
uni.showToast({ title: '请先同意用户协议', icon: 'none' })
- uni.vibrateShort()
+ vibrateShort()
return
}
@@ -315,7 +316,7 @@ async function handleSmsLogin() {
function onGetPhoneNumber(e) {
if (!agreementChecked.value) {
uni.showToast({ title: '请先同意用户协议', icon: 'none' })
- uni.vibrateShort()
+ vibrateShort()
return
}
diff --git a/utils/blessing.js b/utils/blessing.js
deleted file mode 100644
index f78007a..0000000
--- a/utils/blessing.js
+++ /dev/null
@@ -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] 祝福动画监听器已移除')
-}
diff --git a/utils/vibrate.js b/utils/vibrate.js
new file mode 100644
index 0000000..5cd0c26
--- /dev/null
+++ b/utils/vibrate.js
@@ -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)
+ }
+ })
+}