完成一键登录和支付功能

This commit is contained in:
future
2026-06-28 14:49:21 +08:00
parent 74876e0923
commit 09e587cb65
94 changed files with 10284 additions and 2666 deletions
@@ -0,0 +1,405 @@
<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>