Files
gym-manage/gym-manage-uniapp/pages/memberInfo/rechargeStoredCard.vue
T

406 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
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="recharge-page">
<MemberInfoSubNav title="储值卡充值" @back="goBack" />
<view class="recharge-page__body">
<!-- 当前余额 -->
<view class="rc-balance-card">
<view class="rc-balance-card__inner">
<text class="rc-balance-card__label">当前余额</text>
<view class="rc-balance-card__amount">
<text class="rc-balance-card__symbol">¥</text>
<text class="rc-balance-card__num">{{ currentBalance }}</text>
</view>
<text class="rc-balance-card__card-name">{{ currentCardName }}</text>
</view>
</view>
<!-- 充值档位 -->
<view class="rc-section">
<text class="rc-section__title">选择充值金额</text>
<view class="rc-grid">
<view
v-for="(item, index) in rechargeOptions"
:key="index"
class="rc-grid-item"
:class="{ 'rc-grid-item--selected': selectedIndex === index }"
@tap="selectRecharge(index)"
>
<view class="rc-grid-item__top">
<text class="rc-grid-item__amount">¥{{ item.amount }}</text>
</view>
<view class="rc-grid-item__bottom">
<text class="rc-grid-item__bonus">¥{{ item.bonus }}</text>
</view>
<view class="rc-grid-item__check" v-if="selectedIndex === index">
<text></text>
</view>
</view>
</view>
</view>
<!-- 充值说明 -->
<view class="rc-rules">
<text class="rc-rules__title">充值说明</text>
<view class="rc-rules__list">
<view class="rc-rules__item">
<text>1. 充值金额实时到账赠送金额同步到账</text>
</view>
<view class="rc-rules__item">
<text>2. 余额可用于购买会员卡消费等</text>
</view>
<view class="rc-rules__item">
<text>3. 充值金额一经到账不予退还</text>
</view>
</view>
</view>
<!-- 底部按钮 -->
<view class="rc-bottom">
<view class="rc-bottom__info">
<text class="rc-bottom__label">实付金额</text>
<view class="rc-bottom__amount">
<text class="rc-bottom__symbol">¥</text>
<text class="rc-bottom__num">{{ selectedAmount }}</text>
</view>
</view>
<view
class="rc-bottom__btn"
:class="{ 'rc-bottom__btn--disabled': loading }"
@tap="handleRecharge"
>
<text>{{ loading ? '处理中...' : '立即充值' }}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import MemberInfoSubNav from '@/components/memberInfo/MemberInfoSubNav.vue'
import { getMemberId } from '@/utils/request.js'
import { checkPayPasswordSet, getStoredCardInfo } from '@/api/main.js'
import { navigateToPage, PAGE } from '@/common/constants/routes.js'
const currentBalance = ref('0.00')
const selectedIndex = ref(0)
const loading = ref(false)
const rechargeOptions = ref([
{ amount: 100, bonus: 10, payAmount: 100 },
{ amount: 200, bonus: 20, payAmount: 200 },
{ amount: 300, bonus: 30, payAmount: 300 }
])
const selectedAmount = computed(() => {
return rechargeOptions.value[selectedIndex.value]?.payAmount || 0
})
onLoad(() => {
loadBalance()
})
async function loadBalance() {
const memberId = getMemberId()
if (!memberId) return
try {
const res = await getStoredCardInfo(memberId)
const data = res.data || res
currentBalance.value = ((data.balance || 0).toFixed(2))
} catch (e) {
console.error('获取储值卡余额失败:', e)
}
}
function selectRecharge(index) {
selectedIndex.value = index
}
async function handleRecharge() {
if (loading.value) return
const memberId = getMemberId()
if (!memberId) {
uni.showToast({ title: '用户未登录', icon: 'none' })
return
}
// 检查支付密码
uni.showLoading({ title: '检查中...' })
try {
const checkRes = await checkPayPasswordSet(memberId)
uni.hideLoading()
const isSet = checkRes.isSet || checkRes.data?.isSet || false
if (!isSet) {
uni.showModal({
title: '提示',
content: '您尚未设置支付密码,请先设置支付密码后再进行充值',
confirmText: '去设置',
success: (res) => {
if (res.confirm) {
navigateToPage(PAGE.SET_PAY_PASSWORD)
}
}
})
return
}
// 确认充值
const option = rechargeOptions.value[selectedIndex.value]
uni.showModal({
title: '确认充值',
content: `确认充值 ${option.amount} 元?\n到账金额:${option.amount + option.bonus}\n(含赠送 ${option.bonus} 元)`,
confirmText: '确认充值',
success: async (res) => {
if (res.confirm) {
await initiatePayment(option)
}
}
})
} catch (e) {
uni.hideLoading()
console.error('检查支付密码失败:', e)
uni.showToast({ title: '检查失败,请稍后重试', icon: 'none' })
}
}
async function initiatePayment(option) {
const amount = option.payAmount
const desc = `储值卡充值:充${option.amount}${option.amount + option.bonus}`
const remark = `rechargeAmount=${option.amount}&bonus=${option.bonus}`
uni.navigateTo({
url: `/pages/memberInfo/confirmPayment?orderType=STORED_CARD_RECHARGE&amount=${amount}&desc=${encodeURIComponent(desc)}&remark=${encodeURIComponent(remark)}&returnPage=memberCard&cardType=stored`,
fail: (err) => {
console.error('跳转支付页面失败:', err)
uni.showToast({ title: '跳转支付页面失败', icon: 'none' })
}
})
}
function goBack() {
uni.navigateBack()
}
</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';
.recharge-page {
min-height: 100vh;
background: #F5F7FA;
padding-bottom: 100px;
}
.recharge-page__body {
padding: 16px;
}
.rc-balance-card {
margin-bottom: 20px;
}
.rc-balance-card__inner {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 16px;
padding: 24px;
color: #FFFFFF;
}
.rc-balance-card__label {
font-size: 13px;
opacity: 0.8;
margin-bottom: 8px;
display: block;
}
.rc-balance-card__amount {
display: flex;
align-items: baseline;
margin-bottom: 8px;
}
.rc-balance-card__symbol {
font-size: 18px;
font-weight: 600;
margin-right: 4px;
}
.rc-balance-card__num {
font-size: 36px;
font-weight: 700;
}
.rc-balance-card__card-name {
font-size: 13px;
opacity: 0.9;
}
.rc-section {
background: #FFFFFF;
border-radius: 12px;
padding: 16px;
margin-bottom: 16px;
}
.rc-section__title {
font-size: 15px;
font-weight: 600;
color: #1A202C;
margin-bottom: 16px;
display: block;
}
.rc-grid {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.rc-grid-item {
width: calc(33.33% - 8px);
background: #F7FAFC;
border: 2px solid transparent;
border-radius: 12px;
padding: 16px 8px;
text-align: center;
position: relative;
transition: all 0.2s;
&--selected {
background: #F0F4FF;
border-color: #1677FF;
}
}
.rc-grid-item__top {
margin-bottom: 4px;
}
.rc-grid-item__amount {
font-size: 20px;
font-weight: 700;
color: #1A202C;
}
.rc-grid-item__bottom {
margin-bottom: 0;
}
.rc-grid-item__bonus {
font-size: 12px;
color: #E53E3E;
font-weight: 500;
}
.rc-grid-item__check {
position: absolute;
top: -1px;
right: -1px;
width: 20px;
height: 20px;
background: #1677FF;
border-radius: 0 10px 0 10px;
display: flex;
align-items: center;
justify-content: center;
text {
color: #FFFFFF;
font-size: 12px;
font-weight: 700;
}
}
.rc-rules {
background: #FFFFFF;
border-radius: 12px;
padding: 16px;
}
.rc-rules__title {
font-size: 14px;
font-weight: 600;
color: #1A202C;
margin-bottom: 12px;
display: block;
}
.rc-rules__list {
display: flex;
flex-direction: column;
gap: 8px;
}
.rc-rules__item {
font-size: 13px;
color: #718096;
line-height: 1.5;
}
.rc-bottom {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #FFFFFF;
padding: 12px 16px;
padding-bottom: calc(12px + env(safe-area-inset-bottom));
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
}
.rc-bottom__info {
display: flex;
align-items: baseline;
gap: 8px;
}
.rc-bottom__label {
font-size: 13px;
color: #718096;
}
.rc-bottom__amount {
display: flex;
align-items: baseline;
}
.rc-bottom__symbol {
font-size: 14px;
font-weight: 600;
color: #E53E3E;
margin-right: 2px;
}
.rc-bottom__num {
font-size: 24px;
font-weight: 700;
color: #E53E3E;
}
.rc-bottom__btn {
padding: 12px 32px;
background: #1677FF;
border-radius: 24px;
color: #FFFFFF;
font-size: 15px;
font-weight: 600;
&--disabled {
opacity: 0.6;
}
}
</style>