399 lines
12 KiB
Vue
399 lines
12 KiB
Vue
<template>
|
||
<view class="scroll-container theme-light">
|
||
<view class="card-detail-page">
|
||
<view v-if="card" class="card-detail-page__body">
|
||
<view class="cd-hero">
|
||
<view class="cd-hero__card">
|
||
<view class="cd-hero__card-header">
|
||
<text class="cd-hero__card-name">{{ card.memberCardName || card.cardName }}</text>
|
||
<view class="cd-hero__card-tag" :class="'cd-hero__card-tag--' + getTagClass(card.memberCardType || card.cardType)">
|
||
<text class="cd-hero__card-tag-text">{{ getCardTypeName(card.memberCardType || card.cardType) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="cd-hero__card-body">
|
||
<view class="cd-hero__card-item" v-if="card.memberCardValidityDays || card.validityDays">
|
||
<text class="cd-hero__card-label">有效期</text>
|
||
<text class="cd-hero__card-value">{{ card.memberCardValidityDays || card.validityDays }}天</text>
|
||
</view>
|
||
<view class="cd-hero__card-item" v-if="(card.memberCardType === 'COUNT_CARD' || card.cardType === 'COUNT') && (card.memberCardTotalTimes || card.totalTimes)">
|
||
<text class="cd-hero__card-label">总次数</text>
|
||
<text class="cd-hero__card-value">{{ card.memberCardTotalTimes || card.totalTimes }}次</text>
|
||
</view>
|
||
<view class="cd-hero__card-item" v-if="(card.memberCardType === 'STORED_VALUE_CARD' || card.cardType === 'BALANCE') && (card.memberCardAmount || card.amount)">
|
||
<text class="cd-hero__card-label">储值金额</text>
|
||
<text class="cd-hero__card-value">{{ card.memberCardAmount || card.amount }}元</text>
|
||
</view>
|
||
</view>
|
||
<view class="cd-hero__card-footer">
|
||
<text class="cd-hero__card-price">¥{{ card.memberCardPrice || card.price }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="cd-section">
|
||
<text class="cd-section__title">会员卡权益</text>
|
||
<view class="cd-benefits">
|
||
<view class="cd-benefit">
|
||
<view class="cd-benefit__icon">💪</view>
|
||
<view class="cd-benefit__content">
|
||
<text class="cd-benefit__title">全场器械使用</text>
|
||
<text class="cd-benefit__desc">免费使用所有健身器械和设备</text>
|
||
</view>
|
||
</view>
|
||
<view class="cd-benefit">
|
||
<view class="cd-benefit__icon">🏋️</view>
|
||
<view class="cd-benefit__content">
|
||
<text class="cd-benefit__title">免费团课</text>
|
||
<text class="cd-benefit__desc">预约参加各类团课(次卡除外)</text>
|
||
</view>
|
||
</view>
|
||
<view class="cd-benefit">
|
||
<view class="cd-benefit__icon">🧴</view>
|
||
<view class="cd-benefit__content">
|
||
<text class="cd-benefit__title">淋浴服务</text>
|
||
<text class="cd-benefit__desc">免费使用淋浴间和储物柜</text>
|
||
</view>
|
||
</view>
|
||
<view class="cd-benefit">
|
||
<view class="cd-benefit__icon">📱</view>
|
||
<view class="cd-benefit__content">
|
||
<text class="cd-benefit__title">在线预约</text>
|
||
<text class="cd-benefit__desc">APP在线预约课程和签到</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="cd-section" v-if="card.extraConfig">
|
||
<text class="cd-section__title">详细解读</text>
|
||
<view class="cd-desc">
|
||
<text class="cd-desc__text">{{ parseExtraConfig(card.extraConfig) }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="cd-section">
|
||
<text class="cd-section__title">购买须知</text>
|
||
<view class="cd-notes">
|
||
<text class="cd-notes__item">1. 会员卡一经购买,非特殊情况不予退款</text>
|
||
<text class="cd-notes__item">2. 请在有效期内使用,过期自动失效</text>
|
||
<text class="cd-notes__item">3. 会员卡仅限本人使用,不得转借他人</text>
|
||
<text class="cd-notes__item">4. 支付订单有效时间为15分钟,超时自动取消</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="cd-bottom">
|
||
<view class="cd-bottom__total">
|
||
<text class="cd-bottom__total-label">价格</text>
|
||
<text class="cd-bottom__total-price">¥{{ card.memberCardPrice || card.price }}</text>
|
||
</view>
|
||
<view class="cd-bottom__btn" @tap="handleSelectCard">
|
||
<text class="cd-bottom__btn-text">选择该卡</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-else class="cd-loading">
|
||
<text>{{ loading ? '加载中...' : '暂无数据' }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted, onUnmounted } from 'vue'
|
||
import { getMemberCardById } from '@/api/main.js'
|
||
|
||
const card = ref(null)
|
||
const loading = ref(false)
|
||
|
||
function getCardTypeName(type) {
|
||
const map = {
|
||
TIME_CARD: '时长卡',
|
||
DURATION: '时长卡',
|
||
COUNT_CARD: '次卡',
|
||
COUNT: '次卡',
|
||
STORED_VALUE_CARD: '储值卡',
|
||
BALANCE: '储值卡'
|
||
}
|
||
return map[type] || type
|
||
}
|
||
|
||
function getTagClass(type) {
|
||
if (type === 'TIME_CARD' || type === 'DURATION') return 'time'
|
||
if (type === 'COUNT_CARD' || type === 'COUNT') return 'count'
|
||
if (type === 'STORED_VALUE_CARD' || type === 'BALANCE') return 'value'
|
||
return 'default'
|
||
}
|
||
|
||
function parseExtraConfig(config) {
|
||
if (!config) return ''
|
||
try {
|
||
const obj = JSON.parse(config)
|
||
return obj.description || obj.desc || config
|
||
} catch (e) {
|
||
return config
|
||
}
|
||
}
|
||
|
||
function handleSelectCard() {
|
||
if (!card.value) return
|
||
uni.navigateTo({ url: `/pages/memberInfo/purchaseCard?cardId=${card.value.memberCardId || card.value.id}` })
|
||
}
|
||
|
||
async function loadCardDetail(cardId) {
|
||
if (!cardId) return
|
||
loading.value = true
|
||
try {
|
||
const res = await getMemberCardById(cardId)
|
||
if (res && res.memberCardId) {
|
||
card.value = res
|
||
} else if (res && res.code === 200 && res.data) {
|
||
card.value = res.data
|
||
} else {
|
||
uni.showToast({ title: res?.message || '加载失败', icon: 'none' })
|
||
}
|
||
} catch (e) {
|
||
console.error('加载会员卡详情失败:', e)
|
||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
const pages = getCurrentPages()
|
||
const currentPage = pages[pages.length - 1]
|
||
const options = currentPage.options || {}
|
||
|
||
const cardId = options.cardId || options.id
|
||
if (cardId) {
|
||
loadCardDetail(cardId)
|
||
}
|
||
})
|
||
|
||
onUnmounted(() => {
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
@import '@/common/style/base.css';
|
||
@import '@/common/style/memberInfo/pages/page-reset.css';
|
||
@import '@/common/style/memberInfo/pages/sub-page-base.css';
|
||
@import '@/common/style/memberInfo/member-info-component-reset.css';
|
||
@import '@/common/style/memberInfo/member-info-sub-nav.css';
|
||
|
||
.card-detail-page {
|
||
min-height: 100vh;
|
||
background: #F5F7FA;
|
||
}
|
||
|
||
.card-detail-page__body {
|
||
padding-bottom: 120px;
|
||
}
|
||
|
||
.cd-loading {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
height: 60vh;
|
||
font-size: 14px;
|
||
color: #718096;
|
||
}
|
||
|
||
.cd-hero {
|
||
background: linear-gradient(135deg, #1A365D 0%, #2C5282 100%);
|
||
padding: 32px 24px;
|
||
}
|
||
|
||
.cd-hero__card {
|
||
background: rgba(255, 255, 255, 0.1);
|
||
border-radius: 16px;
|
||
padding: 24px;
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.cd-hero__card-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.cd-hero__card-name {
|
||
font-size: 22px;
|
||
font-weight: 700;
|
||
color: #FFFFFF;
|
||
}
|
||
|
||
.cd-hero__card-tag {
|
||
padding: 4px 12px;
|
||
border-radius: 20px;
|
||
font-size: 12px;
|
||
&--time { background: rgba(66, 153, 225, 0.3); color: #90CDF4; }
|
||
&--count { background: rgba(237, 137, 54, 0.3); color: #FBD38D; }
|
||
&--value { background: rgba(56, 161, 105, 0.3); color: #9AE6B4; }
|
||
&--default { background: rgba(160, 174, 192, 0.3); color: #CBD5E0; }
|
||
}
|
||
|
||
.cd-hero__card-body {
|
||
display: flex;
|
||
gap: 24px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.cd-hero__card-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
.cd-hero__card-label {
|
||
font-size: 12px;
|
||
color: rgba(255, 255, 255, 0.6);
|
||
}
|
||
|
||
.cd-hero__card-value {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #FFFFFF;
|
||
}
|
||
|
||
.cd-hero__card-footer {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 8px;
|
||
padding-top: 16px;
|
||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||
}
|
||
|
||
.cd-hero__card-price {
|
||
font-size: 32px;
|
||
font-weight: 700;
|
||
color: #F6E05E;
|
||
}
|
||
|
||
.cd-hero__card-original {
|
||
font-size: 14px;
|
||
color: rgba(255, 255, 255, 0.5);
|
||
text-decoration: line-through;
|
||
}
|
||
|
||
.cd-section {
|
||
margin: 16px;
|
||
background: #FFFFFF;
|
||
border-radius: 16px;
|
||
padding: 20px;
|
||
}
|
||
|
||
.cd-section__title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #1A202C;
|
||
margin-bottom: 16px;
|
||
display: block;
|
||
}
|
||
|
||
.cd-benefits {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
}
|
||
|
||
.cd-benefit {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 12px;
|
||
}
|
||
|
||
.cd-benefit__icon {
|
||
font-size: 24px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.cd-benefit__content {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
.cd-benefit__title {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: #2D3748;
|
||
}
|
||
|
||
.cd-benefit__desc {
|
||
font-size: 12px;
|
||
color: #718096;
|
||
}
|
||
|
||
.cd-desc__text {
|
||
font-size: 14px;
|
||
color: #4A5568;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.cd-notes {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.cd-notes__item {
|
||
font-size: 13px;
|
||
color: #718096;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.cd-bottom {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 16px 24px;
|
||
padding-bottom: calc(16px + env(safe-area-inset-bottom));
|
||
background: #FFFFFF;
|
||
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.05);
|
||
z-index: 100;
|
||
}
|
||
|
||
.cd-bottom__total {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 8px;
|
||
}
|
||
|
||
.cd-bottom__total-label {
|
||
font-size: 14px;
|
||
color: #718096;
|
||
}
|
||
|
||
.cd-bottom__total-price {
|
||
font-size: 28px;
|
||
font-weight: 700;
|
||
color: #E53E3E;
|
||
}
|
||
|
||
.cd-bottom__btn {
|
||
background: linear-gradient(135deg, #1677FF 0%, #0958D9 100%);
|
||
border-radius: 12px;
|
||
padding: 16px 48px;
|
||
transition: all 0.2s;
|
||
|
||
&:active {
|
||
transform: scale(0.98);
|
||
opacity: 0.9;
|
||
}
|
||
}
|
||
|
||
.cd-bottom__btn-text {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #FFFFFF;
|
||
}
|
||
</style>
|