All checks were successful
continuous-integration/drone/push Build is passing
220 lines
5.7 KiB
Vue
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>
|