vending-machine/mobile/pages/membership/membership.vue
18631081161 fa0cf7e41c
All checks were successful
continuous-integration/drone/push Build is passing
bug修复
2026-04-21 04:08:37 +08:00

220 lines
5.7 KiB
Vue

<template>
<view class="membership-page">
<!-- 自定义导航栏 -->
<view class="custom-nav" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="nav-inner">
<view class="nav-back" @click="goBack">
<image class="back-icon" src="/static/ic_back2.png" mode="aspectFit" />
</view>
<text class="nav-title">{{ t('membership.title') }}</text>
<view class="nav-placeholder" />
</view>
</view>
<!-- 可滚动内容区域 -->
<view class="scroll-body" :style="{ paddingTop: (statusBarHeight + 44) + 'px', paddingBottom: '200rpx' }">
<!-- 会员宣传长图 -->
<image
v-if="bannerUrl"
class="membership-banner"
:src="bannerUrl"
mode="widthFix"
/>
</view>
<!-- 底部悬浮按钮区域 -->
<view class="fixed-bottom">
<view v-for="product in products" :key="product.productId" class="bottom-btn-wrap">
<template v-if="product.type === 'monthly'">
<view
v-if="!isMember || membershipType !== 'monthly'"
class="bottom-btn monthly-btn"
@click="onPurchase(product)"
>
<text class="bottom-btn-text">{{ product.name || t('membership.joinBtn') }}</text>
</view>
<view v-else class="bottom-btn disabled-btn">
<text class="bottom-btn-text">{{ t('membership.joinedBtn') }}</text>
</view>
</template>
<template v-if="product.type === 'subscription'">
<view
v-if="!isSubscribed"
class="bottom-btn subscribe-btn"
@click="onSubscribe(product)"
>
<text class="bottom-btn-text">{{ product.name || t('membership.subscribeBtn') }}</text>
<text class="bottom-btn-sub">每月支付 长期有效</text>
</view>
<view v-else class="bottom-btn disabled-btn">
<text class="bottom-btn-text">{{ t('membership.subscribedBtn') }}</text>
</view>
</template>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { useMembershipStore } from '../../stores/membership.js'
import { getMembershipBanner } from '../../api/content.js'
import { resolveImageUrl } from '../../utils/image.js'
const { t } = useI18n()
const membershipStore = useMembershipStore()
// 状态栏高度
const statusBarHeight = ref(0)
const safeBottom = ref(0)
try {
const sysInfo = uni.getSystemInfoSync()
statusBarHeight.value = sysInfo.statusBarHeight || 0
safeBottom.value = sysInfo.safeAreaInsets?.bottom || 0
} catch (e) {}
const bannerUrl = ref('')
const products = computed(() => membershipStore.products)
const isMember = computed(() => !!membershipStore.membershipInfo?.isMember)
const membershipType = computed(() => membershipStore.membershipInfo?.membershipType || '')
const isSubscribed = computed(() => membershipType.value === 'subscription')
function goBack() {
uni.navigateBack()
}
async function onPurchase(product) {
try {
await membershipStore.purchase(product.productId, product.price)
uni.showToast({ title: t('membership.joinedBtn'), icon: 'success' })
} catch (e) {
if (e.message === 'cancelled') return
if (e.message?.includes('purchase')) {
uni.showToast({ title: t('membership.payConfirmPending'), icon: 'none' })
}
}
}
async function onSubscribe(product) {
try {
await membershipStore.subscribe(product.productId, product.price)
uni.showToast({ title: t('membership.subscribedBtn'), icon: 'success' })
} catch (e) {
if (e.message === 'cancelled') return
if (e.message?.includes('subscribe')) {
uni.showToast({ title: t('membership.payConfirmPending'), icon: 'none' })
}
}
}
async function loadData() {
try {
const res = await getMembershipBanner()
bannerUrl.value = resolveImageUrl(res.data?.imageUrl || res.data || '')
} catch (e) {}
try { await membershipStore.fetchProducts() } catch (e) {}
try { await membershipStore.fetchMembershipInfo() } catch (e) {}
try { await membershipStore.fetchSubscriptionStatus() } catch (e) {}
}
onMounted(() => { loadData() })
</script>
<style scoped>
.membership-page {
min-height: 100vh;
background-color: #f5f5f5;
}
/* 自定义导航栏 */
.custom-nav {
position: fixed;
top: 0; left: 0; right: 0;
z-index: 100;
background-color: #DBDBDB;
}
.nav-inner {
height: 44px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16rpx;
}
.nav-back {
width: 60rpx;
height: 44px;
display: flex;
align-items: center;
justify-content: center;
}
.back-icon {
width: 40rpx;
height: 40rpx;
}
.nav-title {
font-size: 34rpx;
font-weight: 600;
color: #333;
}
.nav-placeholder {
width: 60rpx;
}
/* 宣传长图 */
.membership-banner {
width: 100%;
}
/* 底部悬浮按钮 */
.fixed-bottom {
position: fixed;
bottom: 0; left: 0; right: 0;
z-index: 99;
background-color: #ffffff;
padding: 20rpx 32rpx 48rpx;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.08);
}
.bottom-btn-wrap {
margin-bottom: 12rpx;
}
.bottom-btn-wrap:last-child {
margin-bottom: 0;
}
.bottom-btn {
width: 100%;
height: 88rpx;
border-radius: 44rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.monthly-btn {
background-color: #ff8c00;
}
.subscribe-btn {
background-color: #ffffff;
border: 2rpx solid #ff8c00;
}
.subscribe-btn .bottom-btn-text {
color: #ff8c00;
}
.subscribe-btn .bottom-btn-sub {
color: #999;
font-size: 20rpx;
margin-top: 2rpx;
}
.disabled-btn {
background-color: #cccccc;
}
.bottom-btn-text {
font-size: 30rpx;
color: #ffffff;
font-weight: 500;
}
.bottom-btn-sub {
display: none;
}
</style>