From a634c6caac10669fc489be59a7a5c5ea5b56b3f2 Mon Sep 17 00:00:00 2001 From: tsui110 Date: Mon, 29 Dec 2025 20:06:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=96=B0=E5=A2=9E=E5=8A=A8=E7=94=BB?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E5=A4=8D=E4=B8=80=E7=95=AA=E8=B5=8F=E7=9A=84?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E9=94=99=E8=AF=AF=EF=BC=8C=E6=97=A0=E9=99=90?= =?UTF-8?q?=E8=B5=8F=E5=92=8C=E4=B8=80=E7=95=AA=E8=B5=8F=E7=9B=AE=E5=89=8D?= =?UTF-8?q?=E6=8C=89=E7=85=A7=E6=9D=83=E9=87=8D=E5=8D=87=E5=BA=8F=E6=8E=92?= =?UTF-8?q?=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/BlessingAnimation.vue | 251 ----------------- components/BlessingFloat.vue | 233 ---------------- components/BlessingPopup.vue | 267 ------------------- components/PaymentPopup.vue | 185 ++++++++++++- components/YifanSelector.vue | 62 +++-- components/activity/ActivityPageLayout.vue | 10 +- pages-activity/activity/duiduipeng/index.vue | 77 +++++- pages-activity/activity/yifanshang/index.vue | 63 ++++- pages-user/coupons/index.vue | 5 +- pages-user/item-cards/index.vue | 5 +- pages-user/orders/index.vue | 3 +- pages-user/tasks/index.vue | 5 +- pages/cabinet/index.vue | 11 +- pages/login/index.vue | 7 +- utils/blessing.js | 40 --- utils/vibrate.js | 52 ++++ 16 files changed, 416 insertions(+), 860 deletions(-) delete mode 100644 components/BlessingAnimation.vue delete mode 100644 components/BlessingFloat.vue delete mode 100644 components/BlessingPopup.vue delete mode 100644 utils/blessing.js create mode 100644 utils/vibrate.js 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 @@ - - - - - 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 @@ - - - - - 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 @@ @@ -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) + } + }) +}