diff --git a/api/appUser.js b/api/appUser.js index 7eac8e3..682c5d9 100644 --- a/api/appUser.js +++ b/api/appUser.js @@ -150,6 +150,14 @@ export function redeemProductByPoints(user_id, product_id, quantity) { return authRequest({ url: `/api/app/users/${user_id}/points/redeem-product`, method: 'POST', data: { product_id, quantity } }) } +export function redeemItemCardByPoints(user_id, item_card_id, quantity = 1) { + return authRequest({ url: `/api/app/users/${user_id}/points/redeem-item-card`, method: 'POST', data: { item_card_id, quantity } }) +} + +export function getStoreItems(kind = 'product', page = 1, page_size = 20) { + return authRequest({ url: '/api/app/store/items', method: 'GET', data: { kind, page, page_size } }) +} + export function getTasks(page = 1, page_size = 20) { return authRequest({ url: '/api/app/task-center/tasks', method: 'GET', data: { page, page_size } }) } @@ -158,6 +166,10 @@ export function getTaskProgress(task_id, user_id) { return authRequest({ url: `/api/app/task-center/tasks/${task_id}/progress/${user_id}`, method: 'GET' }) } +export function claimTaskReward(task_id, user_id, tier_id) { + return authRequest({ url: `/api/app/task-center/tasks/${task_id}/claim/${user_id}`, method: 'POST', data: { tier_id } }) +} + export function getShipments(user_id, page = 1, page_size = 20) { return authRequest({ url: `/api/app/users/${user_id}/shipments`, method: 'GET', data: { page, page_size } }) } @@ -229,3 +241,15 @@ export function checkMatchingGame(game_id, total_pairs) { data: { game_id, total_pairs } }) } + +/** + * 支付成功后获取游戏数据 + * @param {string} game_id - 游戏ID + */ +export function getMatchingGameCards(game_id) { + return authRequest({ + url: '/api/app/matching/cards', + method: 'GET', + data: { game_id } + }) +} diff --git a/components/YifanSelector.vue b/components/YifanSelector.vue index f551815..3b192c1 100644 --- a/components/YifanSelector.vue +++ b/components/YifanSelector.vue @@ -25,7 +25,7 @@ - + 已选 {{ selectedItems.length }} 个位置 @@ -62,10 +62,11 @@ const props = defineProps({ issueId: { type: [String, Number], required: true }, pricePerDraw: { type: Number, default: 0 }, disabled: { type: Boolean, default: false }, - disabledText: { type: String, default: '' } + disabledText: { type: String, default: '' }, + hideActionBar: { type: Boolean, default: false } // 支持隐藏内置操作栏 }) -const emit = defineEmits(['payment-success']) +const emit = defineEmits(['payment-success', 'selection-change']) const choices = ref([]) const loading = ref(false) @@ -162,6 +163,9 @@ function handleSelect(item) { } else { selectedItems.value.push(item) } + + // 通知父组件选中变化 + emit('selection-change', [...selectedItems.value]) } function handleBuy() { @@ -320,6 +324,13 @@ async function onPaymentConfirm(paymentData) { } } } + +// 暴露方法供外部调用 +defineExpose({ + handleRandomOne, + handleBuy, + selectedItems: () => selectedItems.value +}) diff --git a/components/activity/RecordsList.vue b/components/activity/RecordsList.vue index 5b8a54c..89cae0f 100644 --- a/components/activity/RecordsList.vue +++ b/components/activity/RecordsList.vue @@ -1,5 +1,5 @@ @@ -87,21 +90,40 @@ defineProps({ color: $text-sub; } -.empty-state { +/* 紧凑优雅的空状态 */ +.empty-state-compact { display: flex; flex-direction: column; align-items: center; justify-content: center; - padding: $spacing-xl; - color: $text-sub; + padding: $spacing-lg $spacing-xl; + min-height: 200rpx; +} + +.empty-icon-wrap { + width: 80rpx; + height: 80rpx; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, rgba($brand-primary, 0.1) 0%, rgba($accent-gold, 0.1) 100%); + border-radius: 50%; + margin-bottom: $spacing-md; } .empty-icon { - font-size: 64rpx; - margin-bottom: $spacing-sm; + font-size: 40rpx; } -.empty-text { - font-size: $font-sm; +.empty-title { + font-size: $font-md; + color: $text-sub; + font-weight: 600; + margin-bottom: 8rpx; +} + +.empty-hint { + font-size: $font-xs; + color: $text-tertiary; } diff --git a/components/activity/RewardsPreview.vue b/components/activity/RewardsPreview.vue index e049896..973c8ff 100644 --- a/components/activity/RewardsPreview.vue +++ b/components/activity/RewardsPreview.vue @@ -223,6 +223,7 @@ const rewardGroups = computed(() => { justify-content: center; padding: $spacing-xl; color: $text-sub; + min-height: 300rpx; /* 防止切换时布局跳动 */ } .empty-icon { diff --git a/components/activity/RulesPopup.vue b/components/activity/RulesPopup.vue new file mode 100644 index 0000000..d8b76e6 --- /dev/null +++ b/components/activity/RulesPopup.vue @@ -0,0 +1,125 @@ + + + + + + diff --git a/pages.json b/pages.json index 81a4b4f..ce1e536 100644 --- a/pages.json +++ b/pages.json @@ -42,6 +42,30 @@ "navigationBarTitleText": "积分记录" } }, + { + "path": "pages/coupons/index", + "style": { + "navigationBarTitleText": "我的优惠券" + } + }, + { + "path": "pages/item-cards/index", + "style": { + "navigationBarTitleText": "我的道具卡" + } + }, + { + "path": "pages/invites/index", + "style": { + "navigationBarTitleText": "邀请记录" + } + }, + { + "path": "pages/tasks/index", + "style": { + "navigationBarTitleText": "任务中心" + } + }, { "path": "pages/orders/index", "style": { diff --git a/pages/activity/duiduipeng/index.vue b/pages/activity/duiduipeng/index.vue index 5f520f6..042a1d2 100644 --- a/pages/activity/duiduipeng/index.vue +++ b/pages/activity/duiduipeng/index.vue @@ -1,5 +1,6 @@ @@ -86,6 +95,8 @@ import ActivityTabs from '@/components/activity/ActivityTabs.vue' import RewardsPreview from '@/components/activity/RewardsPreview.vue' import RewardsPopup from '@/components/activity/RewardsPopup.vue' import RecordsList from '@/components/activity/RecordsList.vue' +import RulesPopup from '@/components/activity/RulesPopup.vue' +import CabinetPreviewPopup from '@/components/activity/CabinetPreviewPopup.vue' import FlipGrid from '@/components/FlipGrid.vue' import PaymentPopup from '@/components/PaymentPopup.vue' // Composables @@ -126,6 +137,8 @@ const { // ============ 本地状态 ============ const tabActive = ref('pool') const rewardsVisible = ref(false) +const rulesVisible = ref(false) +const cabinetVisible = ref(false) const showFlip = ref(false) const flipRef = ref(null) const drawLoading = ref(false) @@ -141,15 +154,11 @@ const selectedCard = ref(null) // ============ 业务方法 ============ function showRules() { - uni.showModal({ - title: '活动规则', - content: detail.value.rules || '1. 选择档位进行抽赏\n2. 每次抽赏随机获得奖品\n3. 奖池与概率以页面展示为准', - showCancel: false - }) + rulesVisible.value = true } function goCabinet() { - uni.switchTab({ url: '/pages/cabinet/index' }) + cabinetVisible.value = true } function closeFlip() { diff --git a/pages/activity/yifanshang/index.vue b/pages/activity/yifanshang/index.vue index d9fa3e3..005e7f2 100644 --- a/pages/activity/yifanshang/index.vue +++ b/pages/activity/yifanshang/index.vue @@ -49,20 +49,41 @@ {{ orderBlockedReason }} - + + + @@ -93,6 +126,8 @@ import ActivityTabs from '@/components/activity/ActivityTabs.vue' import RewardsPreview from '@/components/activity/RewardsPreview.vue' import RewardsPopup from '@/components/activity/RewardsPopup.vue' import RecordsList from '@/components/activity/RecordsList.vue' +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' // Composables @@ -133,8 +168,31 @@ const { // ============ 本地状态 ============ const tabActive = ref('pool') const rewardsVisible = ref(false) +const rulesVisible = ref(false) +const cabinetVisible = ref(false) const showFlip = ref(false) const flipRef = ref(null) +const yifanSelectorRef = ref(null) +const selectedCount = ref(0) // 从外部追踪选中数量 + +// 接收选中变化事件 +function onSelectionChange(items) { + selectedCount.value = Array.isArray(items) ? items.length : 0 +} + +// 触发随机选号 +function handleRandomDraw() { + if (yifanSelectorRef.value && yifanSelectorRef.value.handleRandomOne) { + yifanSelectorRef.value.handleRandomOne() + } +} + +// 触发支付 +function handlePayment() { + if (yifanSelectorRef.value && yifanSelectorRef.value.handleBuy) { + yifanSelectorRef.value.handleBuy() + } +} // ============ 倒计时相关(一番赏专属) ============ const nowMs = ref(Date.now()) @@ -179,15 +237,11 @@ const orderBlockedReason = computed(() => { // ============ 业务方法 ============ function showRules() { - uni.showModal({ - title: '活动规则', - content: detail.value.rules || '1. 选择号码进行抽选\n2. 每个号码对应一个奖品\n3. 已售号码不可再选\n4.未满足开赏条件,将自动为所有参与用户退款,款项将原路返回', - showCancel: false - }) + rulesVisible.value = true } function goCabinet() { - uni.switchTab({ url: '/pages/cabinet/index' }) + cabinetVisible.value = true } function closeFlip() { @@ -426,4 +480,77 @@ watch(currentIssueId, (newId) => { border: none; } } + +/* ============= 底部固定操作栏 ============= */ +.float-bar { + position: fixed; + left: 32rpx; + right: 32rpx; + bottom: calc(40rpx + env(safe-area-inset-bottom)); + z-index: 100; + animation: slideUp 0.4s cubic-bezier(0.23, 1, 0.32, 1) backwards; +} + +.float-bar-inner { + background: rgba(255, 255, 255, 0.85); + backdrop-filter: blur(30rpx); + padding: 24rpx 40rpx; + border-radius: 999rpx; + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.12); + border: 1rpx solid rgba(255, 255, 255, 0.6); +} + +.selection-info { + font-size: 28rpx; + color: $text-main; + display: flex; + align-items: baseline; + font-weight: 800; +} + +.highlight { + color: $brand-primary; + font-weight: 900; + font-size: 40rpx; + margin: 0 8rpx; + font-family: 'DIN Alternate', sans-serif; +} + +.action-buttons { + display: flex; + gap: 20rpx; +} + +.action-btn { + height: 88rpx; + line-height: 88rpx; + padding: 0 56rpx; + border-radius: 999rpx; + font-size: 30rpx; + font-weight: 900; + margin: 0; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); + border: none; + + &::after { + border: none; + } + + &:active { + transform: scale(0.92); + } + + &.primary { + background: $gradient-brand !important; + color: #FFFFFF !important; + box-shadow: 0 12rpx 32rpx rgba($brand-primary, 0.35); + } +} diff --git a/pages/cabinet/index.vue b/pages/cabinet/index.vue index 01b35d7..f0b457e 100644 --- a/pages/cabinet/index.vue +++ b/pages/cabinet/index.vue @@ -1,7 +1,9 @@