文件
fenleixinxi/uniapp/pages/member/recharge.vue

269 行
6.8 KiB
Vue

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<template>
<view class="recharge fc-page">
<!-- 规范化顶部导航 -->
<fc-navbar title="余额充值" theme="gradient" :show-back="true" />
<!-- 余额卡 -->
<view class="recharge__hero">
<text class="recharge__hero-label">账户余额(元)</text>
<text class="recharge__hero-num">{{ assets.balance != null ? assets.balance : '0.00' }}</text>
<text class="recharge__hero-tip">充值后可用于付费发布、会员开通与联系方式解锁</text>
</view>
<!-- 充值金额 -->
<view class="fc-card">
<text class="recharge__title">选择充值金额</text>
<view class="recharge__tiers">
<view v-for="t in tiers" :key="t.value" class="recharge__tier"
:class="{ 'recharge__tier--active': Number(amount) === t.value }" @click="pickTier(t.value)">
<text class="recharge__tier-amount">¥{{ t.value }}</text>
<text v-if="t.tag" class="recharge__tier-tag">{{ t.tag }}</text>
</view>
</view>
<view class="recharge__custom">
<text class="recharge__custom-symbol">¥</text>
<input v-model="amount" class="recharge__custom-input" type="digit" placeholder="自定义充值金额(元)" />
</view>
</view>
<!-- 支付方式:充值只支持微信支付(余额「充值」= 用余额给余额加钱,服务端已禁止,防资损) -->
<view class="fc-card">
<text class="recharge__title">支付方式</text>
<view class="recharge__pay">
<view class="recharge__pay-type recharge__pay-type--active">
<image class="fc-icon fc-icon--sm" :src="iconUrl('paywechat')" mode="aspectFit" />
<text class="recharge__pay-text">微信支付</text>
</view>
</view>
<view class="recharge__btn" @click="submit">
<text>立即充值 ¥{{ payAmount }}</text>
</view>
<text class="recharge__note">充值仅支持微信支付,支付成功后余额由微信回调自动入账(通常几秒内到账)。</text>
</view>
</view>
</template>
<script>
import FcNavbar from '@/components/fc-navbar/fc-navbar.vue'
import memberApi from '@/common/api/member.js'
import orderApi from '@/common/api/order.js'
import { iconUrl, loadUiConfig } from '@/common/store/ui.js'
import { pay } from '@/common/utils/pay.js'
import config from '@/common/config/index.js'
export default {
components: { FcNavbar },
data() {
return {
assets: {},
tiers: [
{ value: 50, tag: '' },
{ value: 100, tag: '推荐' },
{ value: 200, tag: '' },
{ value: 500, tag: '' },
{ value: 1000, tag: '' },
],
amount: '',
// 充值只允许微信支付(服务端 /order/recharge 已禁止 payType=balance,防「用余额给余额加钱」资损)
payType: config.constants.PAY_TYPE.WXPAY,
submitting: false,
}
},
computed: {
/** 待充值金额(展示用,保留两位小数) */
payAmount() {
const n = Number(this.amount)
return !n || n <= 0 ? '0.00' : n.toFixed(2)
},
},
onLoad() {
loadUiConfig()
this.loadAssets()
},
/** 从收银台返回时重新拉余额:微信回调入账后这里能刷新到最新值 */
onShow() {
this.loadAssets()
},
methods: {
iconUrl,
async loadAssets() {
try {
this.assets = await memberApi.assets()
} catch (e) {
this.assets = { balance: 0 }
}
},
pickTier(v) {
this.amount = String(v)
},
async submit() {
if (this.submitting) return
const num = Number(this.amount)
if (!num || num <= 0) {
uni.showToast({ title: '请输入正确的充值金额', icon: 'none' })
return
}
if (num > 100000) {
uni.showToast({ title: '单次充值不能超过 100000 元', icon: 'none' })
return
}
this.submitting = true
try {
const res = await orderApi.recharge({ amount: num, payType: this.payType })
// 微信支付:下单只落 pending,付款后由 /pay/notify 回调入账(余额不会立刻变化)
await pay({ orderNo: res.orderNo, payType: this.payType })
await this.loadAssets()
this.amount = ''
uni.showToast({ title: '支付成功,到账略有延迟', icon: 'none' })
} catch (e) {
uni.showToast({ title: e.message || '充值失败', icon: 'none' })
} finally {
this.submitting = false
}
},
},
}
</script>
<style lang="scss" scoped>
.recharge {
padding-bottom: 60rpx;
}
.recharge__hero {
background: $fc-gradient-hero;
padding: 48rpx 40rpx 60rpx;
border-radius: 0 0 40rpx 40rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.recharge__hero-label {
color: $fc-text-secondary;
font-weight: 500;
font-size: $fc-font-sm;
}
.recharge__hero-num {
margin-top: 16rpx;
color: $fc-text;
letter-spacing: -1rpx;
font-size: 72rpx;
font-weight: 700;
}
.recharge__hero-tip {
margin-top: 16rpx;
color: $fc-text-placeholder;
font-size: $fc-font-xs;
}
.recharge__title {
font-size: $fc-font-lg;
font-weight: 700;
color: $fc-text;
}
.recharge__tiers {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16rpx;
margin-top: 24rpx;
}
.recharge__tier {
position: relative;
padding: 28rpx 0;
border: 2rpx solid $fc-border;
border-radius: $fc-radius;
background: $fc-bg;
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
transition: all 0.2s ease;
&--active {
border-color: $fc-primary-text;
background: $fc-primary-lighter;
box-shadow: 0 4rpx 14rpx rgba(4, 135, 74, 0.15);
}
}
.recharge__tier-amount {
font-size: $fc-font-lg;
font-weight: 700;
color: $fc-text;
}
.recharge__tier-tag {
position: absolute;
top: -14rpx;
right: 10rpx;
padding: 2rpx 12rpx;
border-radius: $fc-radius-pill;
background: $fc-gradient-warm;
color: #ffffff;
font-size: 18rpx;
}
.recharge__custom {
margin-top: 8rpx;
height: 96rpx;
border-radius: $fc-radius;
background: $fc-bg;
display: flex;
align-items: center;
padding: 0 28rpx;
}
.recharge__custom-symbol {
font-size: 40rpx;
font-weight: 700;
color: $fc-price;
}
.recharge__custom-input {
flex: 1;
margin-left: 12rpx;
font-size: $fc-font-lg;
color: $fc-text;
}
.recharge__pay {
display: flex;
margin-top: 24rpx;
}
.recharge__pay-type {
flex: 1;
height: 80rpx;
border-radius: $fc-radius;
border: 2rpx solid $fc-border;
background: $fc-bg;
display: flex;
align-items: center;
justify-content: center;
font-size: $fc-font-sm;
color: $fc-text-regular;
&:first-child {
margin-right: 16rpx;
}
&--active {
border-color: $fc-primary-text;
background: $fc-primary-lighter;
color: $fc-primary-text;
font-weight: 600;
}
}
.recharge__pay-text {
margin-left: 10rpx;
}
.recharge__btn {
margin-top: 28rpx;
height: 90rpx;
border-radius: $fc-radius-pill;
background: $fc-gradient-primary;
color: $fc-primary-ink;
font-size: 32rpx;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 10rpx 28rpx rgba(4, 48, 30, 0.3);
}
.recharge__note {
display: block;
margin-top: 20rpx;
font-size: $fc-font-xs;
color: $fc-text-placeholder;
line-height: $fc-leading;
}
</style>