Files
gym-manage/gym-manage-uniapp/pages/memberInfo/cardDetail.vue
T
2026-06-28 14:49:21 +08:00

399 lines
12 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>