1385 行
33 KiB
Vue
1385 行
33 KiB
Vue
<template>
|
||
<view class="vip fc-page">
|
||
<!-- 自定义导航栏:深绿渐变沉浸式 -->
|
||
<fc-navbar title="会员中心" theme="gradient" :show-back="true" />
|
||
|
||
<!-- 顶部深绿沉浸底座 -->
|
||
<view class="vip__backdrop">
|
||
<view class="vip__backdrop-ambient"></view>
|
||
</view>
|
||
|
||
<!-- 浮空 VIP 尊享卡片 -->
|
||
<view class="vip__card-wrap">
|
||
<view class="vip__card">
|
||
<!-- 卡片装饰流光 -->
|
||
<view class="vip__card-sheen"></view>
|
||
|
||
<!-- 用户身份信息 -->
|
||
<view class="vip__card-user" @click="handleUserClick">
|
||
<view class="vip__avatar">
|
||
<text class="vip__avatar-txt">{{ userInitial }}</text>
|
||
</view>
|
||
<view class="vip__user-meta">
|
||
<view class="vip__user-title-row">
|
||
<text class="vip__user-name fc-ellipsis">{{ nickName }}</text>
|
||
<view class="vip__badge" :class="{ 'vip__badge--active': isMember }">
|
||
<image class="vip__badge-icon" :src="iconUrl('crown')" mode="aspectFit" />
|
||
<text class="vip__badge-txt">{{ isMember ? (userInfo.memberLevel || 'VIP 会员') : '未开通' }}</text>
|
||
</view>
|
||
</view>
|
||
<text class="vip__user-desc">
|
||
{{ isMember ? '有效期至 ' + expireText : '开通立享多项专属特权,尊享优质服务' }}
|
||
</text>
|
||
</view>
|
||
<image class="vip__card-crown" :src="iconUrl('home-crown')" mode="aspectFit" />
|
||
</view>
|
||
|
||
<!-- 资产数据三栏(通透呼吸感) -->
|
||
<view class="vip__assets">
|
||
<view class="vip__asset-col" @click="recharge">
|
||
<view class="vip__asset-val-box">
|
||
<text class="vip__asset-val">{{ balanceText }}</text>
|
||
<text class="vip__asset-unit">元</text>
|
||
</view>
|
||
<view class="vip__asset-label-row">
|
||
<text class="vip__asset-lbl">账户余额</text>
|
||
<text class="vip__asset-act">充值 ›</text>
|
||
</view>
|
||
</view>
|
||
<view class="vip__asset-sep"></view>
|
||
<view class="vip__asset-col" @click="goCoupons">
|
||
<view class="vip__asset-val-box">
|
||
<text class="vip__asset-val">{{ myCouponCount }}</text>
|
||
<text class="vip__asset-unit">张</text>
|
||
</view>
|
||
<view class="vip__asset-label-row">
|
||
<text class="vip__asset-lbl">优惠券</text>
|
||
<text class="vip__asset-act">查看 ›</text>
|
||
</view>
|
||
</view>
|
||
<view class="vip__asset-sep"></view>
|
||
<view class="vip__asset-col">
|
||
<view class="vip__asset-val-box">
|
||
<text class="vip__asset-val">{{ assets.points || 0 }}</text>
|
||
<text class="vip__asset-unit">分</text>
|
||
</view>
|
||
<view class="vip__asset-label-row">
|
||
<text class="vip__asset-lbl">可用积分</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- VIP 尊享特权:2 列精致大卡片 -->
|
||
<view class="fc-card vip__section">
|
||
<view class="vip__sec-head">
|
||
<text class="vip__sec-title">VIP 尊享专属特权</text>
|
||
<text class="vip__sec-tip">开通即可享有以下权益</text>
|
||
</view>
|
||
|
||
<view class="vip__privileges-grid">
|
||
<!-- 特权 1:每日免费看(真实呈现已用与剩余) -->
|
||
<view class="vip__priv-item">
|
||
<view class="vip__priv-icon-wrap vip__priv-icon-wrap--green">
|
||
<image class="vip__priv-icon" :src="iconUrl('eye')" mode="aspectFit" />
|
||
</view>
|
||
<view class="vip__priv-content">
|
||
<text class="vip__priv-name">每日免费看</text>
|
||
<text class="vip__priv-desc">
|
||
{{ isMember ? `今日已看 ${benefits.freeViewUsed || 0} / 剩 ${benefits.freeViewLeft != null ? benefits.freeViewLeft : benefits.freeView} 次` : `每日享 ${benefits.freeView || 20} 条免费看` }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 特权 2:发布折扣 -->
|
||
<view class="vip__priv-item">
|
||
<view class="vip__priv-icon-wrap vip__priv-icon-wrap--gold">
|
||
<image class="vip__priv-icon" :src="iconUrl('mine-post')" mode="aspectFit" />
|
||
</view>
|
||
<view class="vip__priv-content">
|
||
<text class="vip__priv-name">全场发布优惠</text>
|
||
<text class="vip__priv-desc">发布享 {{ benefits.postDiscount || '8折' }} 专属优惠</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 特权 3:赠送发布额度(真实呈现剩余额度) -->
|
||
<view class="vip__priv-item">
|
||
<view class="vip__priv-icon-wrap vip__priv-icon-wrap--green">
|
||
<image class="vip__priv-icon" :src="iconUrl('edit')" mode="aspectFit" />
|
||
</view>
|
||
<view class="vip__priv-content">
|
||
<text class="vip__priv-name">赠送发布额度</text>
|
||
<text class="vip__priv-desc">
|
||
{{ isMember ? `当前剩余 ${benefits.postQuotaLeft != null ? benefits.postQuotaLeft : 0} 条额度` : `开通套餐送 ${selectedPlanQuota} 条发布` }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 特权 4:专属身份标识 -->
|
||
<view class="vip__priv-item">
|
||
<view class="vip__priv-icon-wrap vip__priv-icon-wrap--gold">
|
||
<image class="vip__priv-icon" :src="iconUrl('crown')" mode="aspectFit" />
|
||
</view>
|
||
<view class="vip__priv-content">
|
||
<text class="vip__priv-name">专属身份勋章</text>
|
||
<text class="vip__priv-desc">全站彰显 VIP 尊贵皇冠标</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 开通套餐选择 -->
|
||
<view class="fc-card vip__section">
|
||
<view class="vip__sec-head">
|
||
<text class="vip__sec-title">{{ isMember ? '续费套餐' : '选择开通套餐' }}</text>
|
||
<text class="vip__sec-tip">支持随时续期无缝顺延</text>
|
||
</view>
|
||
|
||
<!-- 套餐 3 栏卡片 -->
|
||
<view class="vip__plans-row">
|
||
<view
|
||
v-for="p in plans"
|
||
:key="p.planId"
|
||
class="vip__plan-card"
|
||
:class="{ 'vip__plan-card--active': planId === p.planId }"
|
||
@click="selectPlan(p)"
|
||
>
|
||
<view v-if="p.tag" class="vip__plan-badge">
|
||
<text>{{ p.tag }}</text>
|
||
</view>
|
||
<text class="vip__plan-name">{{ p.name }}</text>
|
||
<view class="vip__plan-price-row">
|
||
<text class="vip__plan-symbol">¥</text>
|
||
<text class="vip__plan-val">{{ p.price }}</text>
|
||
</view>
|
||
<text v-if="p.originPrice && p.originPrice > p.price" class="vip__plan-del">
|
||
¥{{ p.originPrice }}
|
||
</text>
|
||
<view v-if="p.postQuota && p.postQuota > 0" class="vip__plan-quota-chip">
|
||
<text>送{{ p.postQuota }}条发布</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 优惠券抵扣选择栏 -->
|
||
<view class="vip__coupon-row" @click="openCouponSheet">
|
||
<view class="vip__coupon-lbl-box">
|
||
<text class="vip__coupon-lbl">优惠券</text>
|
||
</view>
|
||
<view class="vip__coupon-act-box">
|
||
<text v-if="selectedCoupon" class="vip__coupon-tag vip__coupon-tag--active">
|
||
已选 1 张,立省 ¥{{ couponDiscountText }}
|
||
</text>
|
||
<text v-else-if="usableCoupons.length > 0" class="vip__coupon-tag vip__coupon-tag--has">
|
||
{{ usableCoupons.length }} 张可用
|
||
</text>
|
||
<text v-else class="vip__coupon-tag vip__coupon-tag--none">
|
||
暂无可用券
|
||
</text>
|
||
<text class="vip__coupon-arrow">›</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 支付方式切换 -->
|
||
<view class="vip__pay-toggle">
|
||
<text class="vip__pay-label">支付方式</text>
|
||
<view class="vip__pay-options">
|
||
<view
|
||
class="vip__pay-opt"
|
||
:class="{ 'vip__pay-opt--active': payType === 'wxpay' }"
|
||
@click="selectPayType('wxpay')"
|
||
>
|
||
<image class="vip__pay-opt-icon" :src="iconUrl('paywechat')" mode="aspectFit" />
|
||
<text class="vip__pay-opt-txt">微信支付</text>
|
||
<view class="vip__radio" :class="{ 'vip__radio--active': payType === 'wxpay' }">
|
||
<text v-if="payType === 'wxpay'" class="vip__radio-dot"></text>
|
||
</view>
|
||
</view>
|
||
|
||
<view
|
||
class="vip__pay-opt"
|
||
:class="{ 'vip__pay-opt--active': payType === 'balance' }"
|
||
@click="selectPayType('balance')"
|
||
>
|
||
<image class="vip__pay-opt-icon" :src="iconUrl('wallet')" mode="aspectFit" />
|
||
<text class="vip__pay-opt-txt">余额支付 (¥{{ balanceText }})</text>
|
||
<view class="vip__radio" :class="{ 'vip__radio--active': payType === 'balance' }">
|
||
<text v-if="payType === 'balance'" class="vip__radio-dot"></text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部防遮挡垫高 -->
|
||
<view class="vip__dock-pad"></view>
|
||
|
||
<!-- 底部吸底结算栏 -->
|
||
<view class="vip__dock">
|
||
<view class="vip__dock-left">
|
||
<view class="vip__dock-cost-line">
|
||
<text class="vip__dock-tag">实付</text>
|
||
<text class="vip__dock-sym">¥</text>
|
||
<text class="vip__dock-amount">{{ finalPriceText }}</text>
|
||
<text v-if="savedMoneyText" class="vip__dock-saved-pill">{{ savedMoneyText }}</text>
|
||
</view>
|
||
<text class="vip__dock-meta">{{ quotaTip }}</text>
|
||
</view>
|
||
|
||
<button class="vip__dock-submit" :loading="submitting" :disabled="submitting" @click="openVip">
|
||
<text class="vip__dock-submit-txt">{{ isMember ? '立即续费' : '立即开通' }}</text>
|
||
</button>
|
||
</view>
|
||
|
||
<!-- 半屏优惠券选择弹窗 -->
|
||
<view v-if="showCouponSheet" class="vip__sheet-mask" @click="closeCouponSheet">
|
||
<view class="vip__sheet-panel" @click.stop>
|
||
<view class="vip__sheet-head">
|
||
<text class="vip__sheet-title">选择优惠券</text>
|
||
<view class="vip__sheet-close-btn" @click="closeCouponSheet">
|
||
<text class="vip__sheet-close-txt">✕</text>
|
||
</view>
|
||
</view>
|
||
<scroll-view class="vip__sheet-scroll" scroll-y>
|
||
<view class="vip__sheet-list">
|
||
<!-- 不使用优惠券选项 -->
|
||
<view
|
||
class="vip__sheet-item vip__sheet-item--opt"
|
||
:class="{ 'vip__sheet-item--active': !selectedCouponId }"
|
||
@click="chooseCoupon(null)"
|
||
>
|
||
<text class="vip__sheet-opt-txt">不使用优惠券</text>
|
||
<view class="vip__radio" :class="{ 'vip__radio--active': !selectedCouponId }">
|
||
<text v-if="!selectedCouponId" class="vip__radio-dot"></text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 可用券列表 -->
|
||
<view
|
||
v-for="c in usableCoupons"
|
||
:key="c.id"
|
||
class="vip__sheet-item"
|
||
:class="{ 'vip__sheet-item--active': selectedCouponId === c.id }"
|
||
@click="chooseCoupon(c)"
|
||
>
|
||
<view class="vip__sheet-card-left">
|
||
<view class="vip__sheet-card-price">
|
||
<text class="vip__sheet-card-sym">¥</text>
|
||
<text class="vip__sheet-card-val">{{ formatAmount(c.amount) }}</text>
|
||
</view>
|
||
<text class="vip__sheet-card-cond">{{ c.minAmount > 0 ? `满${c.minAmount}可用` : '无门槛' }}</text>
|
||
</view>
|
||
<view class="vip__sheet-card-info">
|
||
<text class="vip__sheet-card-name fc-ellipsis">{{ c.name || '通用优惠券' }}</text>
|
||
<text class="vip__sheet-card-date">
|
||
{{ c.expireAt ? `有效期至 ${formatDate(c.expireAt)}` : '永久有效' }}
|
||
</text>
|
||
</view>
|
||
<view class="vip__radio" :class="{ 'vip__radio--active': selectedCouponId === c.id }">
|
||
<text v-if="selectedCouponId === c.id" class="vip__radio-dot"></text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
<view class="vip__sheet-bottom">
|
||
<view class="vip__sheet-btn" @click="closeCouponSheet">
|
||
<text class="vip__sheet-btn-txt">确定</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import FcNavbar from '@/components/fc-navbar/fc-navbar.vue'
|
||
import memberApi from '@/common/api/member.js'
|
||
import { useUserStore } from '@/common/store/index.js'
|
||
import { iconUrl, loadUiConfig } from '@/common/store/ui.js'
|
||
import { formatTime } from '@/common/utils/format.js'
|
||
import { pay } from '@/common/utils/pay.js'
|
||
import config from '@/common/config/index.js'
|
||
|
||
export default {
|
||
components: { FcNavbar },
|
||
data() {
|
||
return {
|
||
plans: [],
|
||
benefits: {},
|
||
assets: {},
|
||
userCoupons: [],
|
||
planId: 'M12',
|
||
payType: config.defaultPayType || 'wxpay',
|
||
submitting: false,
|
||
// 优惠券弹层与选券状态
|
||
showCouponSheet: false,
|
||
selectedCouponId: null,
|
||
}
|
||
},
|
||
computed: {
|
||
/**
|
||
* 当前登录用户信息
|
||
* @returns {Object}
|
||
*/
|
||
userInfo() {
|
||
try {
|
||
return useUserStore().state.userInfo || {}
|
||
} catch (e) {
|
||
return {}
|
||
}
|
||
},
|
||
/**
|
||
* 当前是否已登录
|
||
* @returns {boolean}
|
||
*/
|
||
isLogin() {
|
||
try {
|
||
return useUserStore().isLogin.value
|
||
} catch (e) {
|
||
return false
|
||
}
|
||
},
|
||
/**
|
||
* 昵称展示
|
||
* @returns {string}
|
||
*/
|
||
nickName() {
|
||
if (!this.isLogin) return '点击登录'
|
||
return this.userInfo.nickname || this.userInfo.phone || '同城居民'
|
||
},
|
||
/**
|
||
* 头像首字母
|
||
* @returns {string}
|
||
*/
|
||
userInitial() {
|
||
if (!this.isLogin) return '登'
|
||
const n = this.userInfo.nickname || this.userInfo.phone || 'V'
|
||
return n.slice(0, 1).toUpperCase()
|
||
},
|
||
/**
|
||
* 是否为会员状态
|
||
* @returns {boolean}
|
||
*/
|
||
isMember() {
|
||
try {
|
||
return useUserStore().isMember.value
|
||
} catch (e) {
|
||
return false
|
||
}
|
||
},
|
||
/**
|
||
* 到期时间文字
|
||
* @returns {string}
|
||
*/
|
||
expireText() {
|
||
if (!this.userInfo.memberExpireAt) return '--'
|
||
return formatTime(this.userInfo.memberExpireAt).slice(0, 10)
|
||
},
|
||
/**
|
||
* 余额格式化
|
||
* @returns {string}
|
||
*/
|
||
balanceText() {
|
||
const b = this.assets.balance
|
||
return b != null ? Number(b).toFixed(2) : '0.00'
|
||
},
|
||
/**
|
||
* 选中的套餐对象
|
||
* @returns {Object|null}
|
||
*/
|
||
selectedPlan() {
|
||
return this.plans.find((x) => x.planId === this.planId) || null
|
||
},
|
||
/**
|
||
* 当前套餐价格数字
|
||
* @returns {number}
|
||
*/
|
||
currentPlanPrice() {
|
||
return this.selectedPlan ? (Number(this.selectedPlan.price) || 0) : 0
|
||
},
|
||
/**
|
||
* 我的可用卡券总数
|
||
* @returns {number}
|
||
*/
|
||
myCouponCount() {
|
||
if (this.assets.couponCount != null) {
|
||
return Number(this.assets.couponCount)
|
||
}
|
||
return this.userCoupons.filter((c) => c.status === 'unused').length
|
||
},
|
||
/**
|
||
* 针对当前套餐金额适用的优惠券列表
|
||
* @returns {Array}
|
||
*/
|
||
usableCoupons() {
|
||
const price = this.currentPlanPrice
|
||
if (!Array.isArray(this.userCoupons)) return []
|
||
const now = Math.floor(Date.now() / 1000)
|
||
return this.userCoupons.filter((c) => {
|
||
// 状态必须为 unused
|
||
if (c.status !== 'unused') return false
|
||
// 检查过期时间
|
||
if (c.expiretime && Number(c.expiretime) < now) return false
|
||
// 检查门槛金额 min_amount
|
||
const min = Number(c.minAmount != null ? c.minAmount : (c.min_amount || 0))
|
||
return price >= min
|
||
})
|
||
},
|
||
/**
|
||
* 当前选中的优惠券
|
||
* @returns {Object|null}
|
||
*/
|
||
selectedCoupon() {
|
||
if (!this.selectedCouponId) return null
|
||
return this.usableCoupons.find((c) => c.id === this.selectedCouponId) || null
|
||
},
|
||
/**
|
||
* 优惠券抵扣金额(数字)
|
||
* @returns {number}
|
||
*/
|
||
couponDiscountAmount() {
|
||
if (!this.selectedCoupon) return 0
|
||
const c = this.selectedCoupon
|
||
const price = this.currentPlanPrice
|
||
let discount = 0
|
||
if (c.type === 'discount') {
|
||
const rate = Number(c.amount) / 10
|
||
discount = price * (1 - rate)
|
||
} else {
|
||
discount = Number(c.amount) || 0
|
||
}
|
||
// 最多减到实付 0.01 元
|
||
return Math.min(discount, Math.max(0, price - 0.01))
|
||
},
|
||
/**
|
||
* 优惠券抵扣金额文本
|
||
* @returns {string}
|
||
*/
|
||
couponDiscountText() {
|
||
return this.couponDiscountAmount.toFixed(2)
|
||
},
|
||
/**
|
||
* 最终实付金额
|
||
* @returns {string}
|
||
*/
|
||
finalPriceText() {
|
||
const price = this.currentPlanPrice
|
||
const discount = this.couponDiscountAmount
|
||
const finalVal = Math.max(0.01, price - discount)
|
||
return finalVal.toFixed(2)
|
||
},
|
||
/**
|
||
* 立省文案
|
||
* @returns {string}
|
||
*/
|
||
savedMoneyText() {
|
||
let totalSave = 0
|
||
if (this.selectedPlan && this.selectedPlan.originPrice && this.selectedPlan.originPrice > this.selectedPlan.price) {
|
||
totalSave += (Number(this.selectedPlan.originPrice) - Number(this.selectedPlan.price))
|
||
}
|
||
totalSave += this.couponDiscountAmount
|
||
return totalSave > 0 ? `省 ¥${totalSave.toFixed(0)}` : ''
|
||
},
|
||
/**
|
||
* 当前选中套餐的发布赠送条数
|
||
* @returns {number}
|
||
*/
|
||
selectedPlanQuota() {
|
||
return this.selectedPlan && this.selectedPlan.postQuota ? this.selectedPlan.postQuota : 30
|
||
},
|
||
/**
|
||
* 额度提示文案
|
||
* @returns {string}
|
||
*/
|
||
quotaTip() {
|
||
if (this.selectedPlan && this.selectedPlan.postQuota > 0) {
|
||
return `当前套餐送 ${this.selectedPlan.postQuota} 条发布`
|
||
}
|
||
return '开通即享专属会员全部特权'
|
||
},
|
||
},
|
||
/**
|
||
* 页面加载生命周期
|
||
*/
|
||
onLoad() {
|
||
loadUiConfig()
|
||
this.init()
|
||
},
|
||
/**
|
||
* 页面展示生命周期
|
||
*/
|
||
onShow() {
|
||
if (this.isLogin) {
|
||
this.loadAssets()
|
||
useUserStore().fetchProfile()
|
||
}
|
||
},
|
||
/**
|
||
* 下拉刷新生命周期
|
||
*/
|
||
async onPullDownRefresh() {
|
||
await this.init()
|
||
uni.stopPullDownRefresh()
|
||
},
|
||
methods: {
|
||
iconUrl,
|
||
/**
|
||
* 初始化数据
|
||
* @returns {Promise<void>}
|
||
*/
|
||
async init() {
|
||
try {
|
||
const [plans, benefits, assets] = await Promise.all([
|
||
memberApi.plans(),
|
||
memberApi.benefits(),
|
||
memberApi.assets(),
|
||
])
|
||
this.plans = Array.isArray(plans) ? plans : []
|
||
this.benefits = benefits || {}
|
||
this.assets = assets || {}
|
||
this.userCoupons = (assets && Array.isArray(assets.coupons)) ? assets.coupons : []
|
||
|
||
if (this.plans.length > 0) {
|
||
const rec = this.plans.find((p) => p.tag && p.tag.includes('推荐'))
|
||
this.planId = rec ? rec.planId : this.plans[0].planId
|
||
}
|
||
this.autoSelectBestCoupon()
|
||
} catch (e) {
|
||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||
}
|
||
},
|
||
/**
|
||
* 加载最新资产与优惠券
|
||
* @returns {Promise<void>}
|
||
*/
|
||
async loadAssets() {
|
||
try {
|
||
const res = await memberApi.assets()
|
||
this.assets = res || {}
|
||
this.userCoupons = (res && Array.isArray(res.coupons)) ? res.coupons : []
|
||
this.autoSelectBestCoupon()
|
||
} catch (e) {}
|
||
},
|
||
/**
|
||
* 选择套餐
|
||
* @param {Object} plan
|
||
*/
|
||
selectPlan(plan) {
|
||
if (!plan) return
|
||
this.planId = plan.planId
|
||
this.$nextTick(() => {
|
||
this.autoSelectBestCoupon()
|
||
})
|
||
},
|
||
/**
|
||
* 自动优选减免面额最大的一张可用券
|
||
*/
|
||
autoSelectBestCoupon() {
|
||
if (!this.usableCoupons || this.usableCoupons.length === 0) {
|
||
this.selectedCouponId = null
|
||
return
|
||
}
|
||
// 若当前已选券仍在可用范围内,则保留
|
||
if (this.selectedCouponId && this.usableCoupons.some((c) => c.id === this.selectedCouponId)) {
|
||
return
|
||
}
|
||
// 按面额从大到小排序,默认选中第一张
|
||
const sorted = [...this.usableCoupons].sort((a, b) => Number(b.amount || 0) - Number(a.amount || 0))
|
||
this.selectedCouponId = sorted[0].id
|
||
},
|
||
/**
|
||
* 打开优惠券选择弹窗
|
||
*/
|
||
openCouponSheet() {
|
||
if (this.usableCoupons.length === 0) {
|
||
uni.showToast({ title: '暂无当前套餐可适用的优惠券', icon: 'none' })
|
||
return
|
||
}
|
||
this.showCouponSheet = true
|
||
},
|
||
/**
|
||
* 关闭优惠券选择弹窗
|
||
*/
|
||
closeCouponSheet() {
|
||
this.showCouponSheet = false
|
||
},
|
||
/**
|
||
* 选中某张优惠券
|
||
* @param {Object|null} c
|
||
*/
|
||
chooseCoupon(c) {
|
||
this.selectedCouponId = c ? c.id : null
|
||
this.showCouponSheet = false
|
||
},
|
||
/**
|
||
* 切换支付类型
|
||
* @param {string} type
|
||
*/
|
||
selectPayType(type) {
|
||
this.payType = type
|
||
},
|
||
/**
|
||
* 检查登录状态
|
||
* @returns {boolean}
|
||
*/
|
||
checkLogin() {
|
||
if (!this.isLogin) {
|
||
uni.navigateTo({ url: '/pages/auth/login' })
|
||
return false
|
||
}
|
||
return true
|
||
},
|
||
/**
|
||
* 用户区域点击
|
||
*/
|
||
handleUserClick() {
|
||
if (!this.isLogin) {
|
||
uni.navigateTo({ url: '/pages/auth/login' })
|
||
}
|
||
},
|
||
/**
|
||
* 跳转到优惠券中心
|
||
*/
|
||
goCoupons() {
|
||
if (!this.checkLogin()) return
|
||
uni.navigateTo({ url: '/pages/member/coupons' })
|
||
},
|
||
/**
|
||
* 提交开通或续费
|
||
* @returns {Promise<void>}
|
||
*/
|
||
async openVip() {
|
||
if (!this.checkLogin()) return
|
||
if (!this.planId) {
|
||
uni.showToast({ title: '请选择套餐', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
// 余额支付前进行实付金额判定
|
||
const finalCost = Number(this.finalPriceText)
|
||
if (this.payType === 'balance') {
|
||
const bal = Number(this.assets.balance || 0)
|
||
if (bal < finalCost) {
|
||
uni.showModal({
|
||
title: '余额不足',
|
||
content: `当前账户余额 (¥${this.balanceText}) 不足支付实付金额 (¥${this.finalPriceText}),是否前往充值?`,
|
||
confirmText: '去充值',
|
||
success: (r) => {
|
||
if (r.confirm) this.recharge()
|
||
},
|
||
})
|
||
return
|
||
}
|
||
}
|
||
|
||
this.submitting = true
|
||
uni.showLoading({ title: '正在处理…' })
|
||
|
||
try {
|
||
const res = await memberApi.open({
|
||
planId: this.planId,
|
||
payType: this.payType,
|
||
userCouponId: this.selectedCouponId || 0,
|
||
})
|
||
|
||
// 微信支付走拉起逻辑
|
||
if (this.payType === config.constants.PAY_TYPE.WXPAY && res && res.orderNo) {
|
||
await pay({ orderNo: res.orderNo, payType: this.payType })
|
||
}
|
||
|
||
uni.hideLoading()
|
||
this.submitting = false
|
||
|
||
// 同步刷新本地用户信息与资产
|
||
useUserStore().fetchProfile()
|
||
this.loadAssets()
|
||
|
||
// 支付成功直接平滑跳转至专业的支付结果页
|
||
uni.redirectTo({
|
||
url: `/pages/order/success?orderNo=${res.orderNo || ''}&type=member&title=${encodeURIComponent(this.selectedPlan ? this.selectedPlan.name : 'VIP会员')}&amount=${this.finalPriceText}&originAmount=${this.currentPlanPrice}&discount=${this.couponDiscountText}&payType=${this.payType}`,
|
||
})
|
||
} catch (e) {
|
||
uni.hideLoading()
|
||
this.submitting = false
|
||
uni.showToast({ title: e.message || '开通未完成', icon: 'none' })
|
||
}
|
||
},
|
||
/**
|
||
* 前往充值
|
||
*/
|
||
recharge() {
|
||
if (!this.checkLogin()) return
|
||
uni.navigateTo({ url: '/pages/member/recharge' })
|
||
},
|
||
/**
|
||
* 格式化金额数值
|
||
* @param {number|string} val
|
||
* @returns {string}
|
||
*/
|
||
formatAmount(val) {
|
||
const n = Number(val)
|
||
if (isNaN(n)) return '0'
|
||
return Number.isInteger(n) ? String(n) : n.toFixed(1)
|
||
},
|
||
/**
|
||
* 格式化时间字符串至年月日
|
||
* @param {string} dateStr
|
||
* @returns {string}
|
||
*/
|
||
formatDate(dateStr) {
|
||
if (!dateStr) return ''
|
||
return String(dateStr).slice(0, 10)
|
||
},
|
||
},
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.vip {
|
||
min-height: 100vh;
|
||
background: $fc-bg;
|
||
position: relative;
|
||
padding-bottom: 40rpx;
|
||
}
|
||
|
||
.vip__backdrop {
|
||
background: $fc-gradient-hero;
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: 380rpx;
|
||
overflow: hidden;
|
||
z-index: 0;
|
||
}
|
||
.vip__backdrop-ambient {
|
||
position: absolute;
|
||
top: -60rpx;
|
||
right: -60rpx;
|
||
width: 320rpx;
|
||
height: 320rpx;
|
||
border-radius: 50%;
|
||
background: radial-gradient(circle, rgba(0, 224, 112, 0.28) 0%, rgba(0, 224, 112, 0) 70%);
|
||
filter: blur(20px);
|
||
}
|
||
|
||
.vip__card-wrap {
|
||
position: relative;
|
||
z-index: 2;
|
||
padding: 16rpx 28rpx 0;
|
||
}
|
||
.vip__card {
|
||
/* 尊享高定暖金黑金渐变:彻底告别老式墨绿发黑,与 mine 会员卡片视觉基因统一 */
|
||
background: linear-gradient(135deg, #ffe7a6 0%, #ffd06a 45%, #f5a623 100%);
|
||
border-radius: 28rpx;
|
||
padding: 36rpx 32rpx 28rpx;
|
||
position: relative;
|
||
overflow: hidden;
|
||
box-shadow: 0 16rpx 36rpx rgba(245, 166, 35, 0.22);
|
||
border: 1rpx solid rgba(255, 255, 255, 0.6);
|
||
}
|
||
.vip__card-sheen {
|
||
position: absolute;
|
||
top: -90rpx;
|
||
right: -90rpx;
|
||
width: 280rpx;
|
||
height: 280rpx;
|
||
border-radius: 50%;
|
||
background: radial-gradient(circle, rgba(255, 255, 255, 0.55) 0%, rgba(255, 255, 255, 0) 70%);
|
||
pointer-events: none;
|
||
}
|
||
.vip__card-user {
|
||
display: flex;
|
||
align-items: center;
|
||
position: relative;
|
||
z-index: 2;
|
||
}
|
||
.vip__avatar {
|
||
width: 88rpx;
|
||
height: 88rpx;
|
||
border-radius: 50%;
|
||
background: #ffffff;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: 3rpx solid rgba(255, 255, 255, 0.95);
|
||
box-shadow: 0 4rpx 14rpx rgba(74, 38, 0, 0.12);
|
||
flex-shrink: 0;
|
||
}
|
||
.vip__avatar-txt {
|
||
font-size: 38rpx;
|
||
font-weight: 800;
|
||
color: #4a2600;
|
||
}
|
||
.vip__user-meta {
|
||
margin-left: 20rpx;
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
.vip__user-title-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
}
|
||
.vip__user-name {
|
||
font-size: 32rpx;
|
||
font-weight: 800;
|
||
color: #3e2000;
|
||
}
|
||
.vip__badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: 4rpx 14rpx;
|
||
border-radius: 20rpx;
|
||
background: rgba(74, 38, 0, 0.08);
|
||
border: 1rpx solid rgba(74, 38, 0, 0.12);
|
||
}
|
||
.vip__badge--active {
|
||
background: #3e2000;
|
||
border-color: #3e2000;
|
||
}
|
||
.vip__badge-icon {
|
||
width: 22rpx;
|
||
height: 22rpx;
|
||
margin-right: 6rpx;
|
||
}
|
||
.vip__badge-txt {
|
||
font-size: 20rpx;
|
||
color: #4a2600;
|
||
font-weight: 700;
|
||
}
|
||
.vip__badge--active .vip__badge-txt {
|
||
color: #ffe7a6;
|
||
}
|
||
.vip__user-desc {
|
||
margin-top: 8rpx;
|
||
font-size: 22rpx;
|
||
color: #7a4600;
|
||
font-weight: 500;
|
||
}
|
||
.vip__card-crown {
|
||
width: 72rpx;
|
||
height: 72rpx;
|
||
opacity: 0.18;
|
||
}
|
||
|
||
.vip__assets {
|
||
margin-top: 32rpx;
|
||
padding-top: 24rpx;
|
||
border-top: 1rpx solid rgba(74, 38, 0, 0.1);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
.vip__asset-col {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
.vip__asset-val-box {
|
||
display: flex;
|
||
align-items: baseline;
|
||
}
|
||
.vip__asset-val {
|
||
font-size: 40rpx;
|
||
font-weight: 800;
|
||
color: #3e2000;
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
}
|
||
.vip__asset-unit {
|
||
font-size: 20rpx;
|
||
color: #7a4600;
|
||
margin-left: 4rpx;
|
||
font-weight: 600;
|
||
}
|
||
.vip__asset-label-row {
|
||
margin-top: 6rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6rpx;
|
||
}
|
||
.vip__asset-lbl {
|
||
font-size: 22rpx;
|
||
color: #7a4600;
|
||
font-weight: 500;
|
||
}
|
||
.vip__asset-act {
|
||
font-size: 20rpx;
|
||
color: #4a2600;
|
||
font-weight: 700;
|
||
background: rgba(255, 255, 255, 0.45);
|
||
padding: 2rpx 10rpx;
|
||
border-radius: 10rpx;
|
||
}
|
||
.vip__asset-sep {
|
||
width: 1rpx;
|
||
height: 36rpx;
|
||
background: rgba(74, 38, 0, 0.12);
|
||
}
|
||
|
||
.vip__section {
|
||
margin: 24rpx 28rpx 0;
|
||
background: #ffffff;
|
||
border-radius: 28rpx;
|
||
padding: 32rpx 28rpx;
|
||
box-shadow: 0 4rpx 20rpx rgba(15, 23, 42, 0.04);
|
||
}
|
||
.vip__sec-head {
|
||
display: flex;
|
||
align-items: baseline;
|
||
justify-content: space-between;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
.vip__sec-title {
|
||
font-size: 32rpx;
|
||
font-weight: 700;
|
||
color: #1e293b;
|
||
}
|
||
.vip__sec-tip {
|
||
font-size: 22rpx;
|
||
color: #64748b;
|
||
}
|
||
|
||
.vip__privileges-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 20rpx;
|
||
}
|
||
.vip__priv-item {
|
||
display: flex;
|
||
align-items: center;
|
||
background: #f8fafc;
|
||
padding: 20rpx 18rpx;
|
||
border-radius: 20rpx;
|
||
}
|
||
.vip__priv-icon-wrap {
|
||
width: 64rpx;
|
||
height: 64rpx;
|
||
border-radius: 16rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
|
||
&--green {
|
||
background: #eaf8f0;
|
||
}
|
||
&--gold {
|
||
background: #fff4e0;
|
||
}
|
||
}
|
||
.vip__priv-icon {
|
||
width: 36rpx;
|
||
height: 36rpx;
|
||
}
|
||
.vip__priv-content {
|
||
margin-left: 16rpx;
|
||
min-width: 0;
|
||
flex: 1;
|
||
}
|
||
.vip__priv-name {
|
||
font-size: 26rpx;
|
||
font-weight: 700;
|
||
color: #1e293b;
|
||
display: block;
|
||
}
|
||
.vip__priv-desc {
|
||
margin-top: 4rpx;
|
||
font-size: 20rpx;
|
||
color: #64748b;
|
||
display: block;
|
||
}
|
||
|
||
.vip__plans-row {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 16rpx;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
.vip__plan-card {
|
||
background: #ffffff;
|
||
border: 2rpx solid #e2e8f0;
|
||
border-radius: 20rpx;
|
||
padding: 28rpx 10rpx 24rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
position: relative;
|
||
transition: all 0.2s ease;
|
||
|
||
&--active {
|
||
border-color: #00e070;
|
||
background: linear-gradient(180deg, #f2fef7 0%, #ffffff 100%);
|
||
box-shadow: 0 6rpx 20rpx rgba(0, 224, 112, 0.15);
|
||
}
|
||
}
|
||
.vip__plan-badge {
|
||
position: absolute;
|
||
top: -12rpx;
|
||
right: -6rpx;
|
||
background: #00e070;
|
||
color: #0f2f1d;
|
||
font-size: 18rpx;
|
||
font-weight: 700;
|
||
padding: 2rpx 12rpx;
|
||
border-radius: 12rpx 0 12rpx 0;
|
||
}
|
||
.vip__plan-name {
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
color: #1e293b;
|
||
}
|
||
.vip__plan-price-row {
|
||
display: flex;
|
||
align-items: baseline;
|
||
color: #f5a623;
|
||
margin-top: 10rpx;
|
||
}
|
||
.vip__plan-symbol {
|
||
font-size: 24rpx;
|
||
font-weight: 700;
|
||
}
|
||
.vip__plan-val {
|
||
font-size: 44rpx;
|
||
font-weight: 800;
|
||
line-height: 1;
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
}
|
||
.vip__plan-del {
|
||
margin-top: 4rpx;
|
||
font-size: 20rpx;
|
||
color: #94a3b8;
|
||
text-decoration: line-through;
|
||
}
|
||
.vip__plan-quota-chip {
|
||
margin-top: 12rpx;
|
||
font-size: 18rpx;
|
||
color: #00a854;
|
||
background: #eaf8f0;
|
||
padding: 2rpx 10rpx;
|
||
border-radius: 8rpx;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.vip__coupon-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 24rpx 0;
|
||
border-top: 1rpx solid #f1f5f9;
|
||
border-bottom: 1rpx solid #f1f5f9;
|
||
margin: 24rpx 0 16rpx;
|
||
}
|
||
.vip__coupon-lbl {
|
||
font-size: 26rpx;
|
||
color: #1e293b;
|
||
font-weight: 700;
|
||
}
|
||
.vip__coupon-act-box {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10rpx;
|
||
}
|
||
.vip__coupon-tag {
|
||
font-size: 24rpx;
|
||
font-weight: 600;
|
||
padding: 6rpx 18rpx;
|
||
border-radius: 20rpx;
|
||
|
||
&--active {
|
||
color: #e65100;
|
||
background: #fff4e0;
|
||
}
|
||
&--has {
|
||
color: #00a854;
|
||
background: #eaf8f0;
|
||
}
|
||
&--none {
|
||
color: #64748b;
|
||
font-weight: normal;
|
||
}
|
||
}
|
||
.vip__coupon-arrow {
|
||
font-size: 28rpx;
|
||
color: #64748b;
|
||
}
|
||
|
||
.vip__pay-toggle {
|
||
margin-top: 16rpx;
|
||
}
|
||
.vip__pay-label {
|
||
font-size: 26rpx;
|
||
font-weight: 700;
|
||
color: #1e293b;
|
||
margin-bottom: 16rpx;
|
||
display: block;
|
||
}
|
||
.vip__pay-options {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 16rpx;
|
||
}
|
||
.vip__pay-opt {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 20rpx 18rpx;
|
||
border-radius: 18rpx;
|
||
background: #f8fafc;
|
||
border: 2rpx solid transparent;
|
||
transition: all 0.2s ease;
|
||
|
||
&--active {
|
||
background: #f0fdf6;
|
||
border-color: #00e070;
|
||
}
|
||
}
|
||
.vip__pay-opt-icon {
|
||
width: 36rpx;
|
||
height: 36rpx;
|
||
margin-right: 12rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
.vip__pay-opt-txt {
|
||
flex: 1;
|
||
font-size: 24rpx;
|
||
color: #1e293b;
|
||
font-weight: 600;
|
||
}
|
||
.vip__radio {
|
||
width: 32rpx;
|
||
height: 32rpx;
|
||
border-radius: 50%;
|
||
border: 2rpx solid #cbd5e1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
|
||
&--active {
|
||
border-color: #00e070;
|
||
background: #00e070;
|
||
}
|
||
}
|
||
.vip__radio-dot {
|
||
width: 12rpx;
|
||
height: 12rpx;
|
||
border-radius: 50%;
|
||
background: #0f2f1d;
|
||
}
|
||
|
||
.vip__dock-pad {
|
||
height: 180rpx;
|
||
}
|
||
.vip__dock {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
background: #ffffff;
|
||
box-shadow: 0 -8rpx 28rpx rgba(15, 23, 42, 0.06);
|
||
padding: 20rpx 32rpx;
|
||
padding-bottom: calc(20rpx + constant(safe-area-inset-bottom));
|
||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
z-index: 100;
|
||
}
|
||
.vip__dock-left {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.vip__dock-cost-line {
|
||
display: flex;
|
||
align-items: baseline;
|
||
}
|
||
.vip__dock-tag {
|
||
font-size: 24rpx;
|
||
color: #64748b;
|
||
margin-right: 8rpx;
|
||
}
|
||
.vip__dock-sym {
|
||
font-size: 28rpx;
|
||
color: #f5a623;
|
||
font-weight: 700;
|
||
}
|
||
.vip__dock-amount {
|
||
font-size: 48rpx;
|
||
color: #f5a623;
|
||
font-weight: 800;
|
||
line-height: 1;
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
}
|
||
.vip__dock-saved-pill {
|
||
margin-left: 12rpx;
|
||
padding: 2rpx 12rpx;
|
||
border-radius: 12rpx;
|
||
background: #fff4e0;
|
||
color: #e65100;
|
||
font-size: 20rpx;
|
||
font-weight: 700;
|
||
}
|
||
.vip__dock-meta {
|
||
margin-top: 6rpx;
|
||
font-size: 20rpx;
|
||
color: #64748b;
|
||
}
|
||
.vip__dock-submit {
|
||
height: 84rpx;
|
||
padding: 0 48rpx;
|
||
border-radius: 42rpx;
|
||
background: $fc-gradient-primary;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 8rpx 24rpx rgba(0, 224, 112, 0.28);
|
||
margin: 0;
|
||
border: none;
|
||
}
|
||
.vip__dock-submit-txt {
|
||
font-size: 30rpx;
|
||
font-weight: 700;
|
||
color: #0f2f1d;
|
||
}
|
||
|
||
.vip__sheet-mask {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.55);
|
||
z-index: 999;
|
||
display: flex;
|
||
align-items: flex-end;
|
||
}
|
||
.vip__sheet-panel {
|
||
width: 100%;
|
||
background: #ffffff;
|
||
border-radius: 32rpx 32rpx 0 0;
|
||
max-height: 75vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding-bottom: constant(safe-area-inset-bottom);
|
||
padding-bottom: env(safe-area-inset-bottom);
|
||
}
|
||
.vip__sheet-head {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 32rpx 36rpx 20rpx;
|
||
border-bottom: 1rpx solid #f1f5f9;
|
||
}
|
||
.vip__sheet-title {
|
||
font-size: 32rpx;
|
||
font-weight: 700;
|
||
color: #1e293b;
|
||
}
|
||
.vip__sheet-close-btn {
|
||
padding: 8rpx 16rpx;
|
||
}
|
||
.vip__sheet-close-txt {
|
||
font-size: 32rpx;
|
||
color: #64748b;
|
||
}
|
||
.vip__sheet-scroll {
|
||
max-height: 55vh;
|
||
padding: 20rpx 32rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
.vip__sheet-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16rpx;
|
||
padding-bottom: 20rpx;
|
||
}
|
||
.vip__sheet-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 20rpx 24rpx;
|
||
border-radius: 18rpx;
|
||
background: #f8fafc;
|
||
border: 2rpx solid transparent;
|
||
transition: all 0.2s ease;
|
||
|
||
&--active {
|
||
background: #f0fdf6;
|
||
border-color: #00e070;
|
||
}
|
||
&--opt {
|
||
justify-content: space-between;
|
||
padding: 24rpx;
|
||
}
|
||
}
|
||
.vip__sheet-opt-txt {
|
||
font-size: 28rpx;
|
||
color: #334155;
|
||
font-weight: 500;
|
||
}
|
||
.vip__sheet-card-left {
|
||
width: 140rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
border-right: 2rpx dashed #cbd5e1;
|
||
padding-right: 16rpx;
|
||
margin-right: 18rpx;
|
||
}
|
||
.vip__sheet-card-price {
|
||
display: flex;
|
||
align-items: baseline;
|
||
color: #f5a623;
|
||
}
|
||
.vip__sheet-card-sym {
|
||
font-size: 22rpx;
|
||
font-weight: 700;
|
||
}
|
||
.vip__sheet-card-val {
|
||
font-size: 38rpx;
|
||
font-weight: 800;
|
||
line-height: 1;
|
||
}
|
||
.vip__sheet-card-cond {
|
||
font-size: 18rpx;
|
||
color: #b45309;
|
||
margin-top: 4rpx;
|
||
}
|
||
.vip__sheet-card-info {
|
||
flex: 1;
|
||
min-width: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6rpx;
|
||
}
|
||
.vip__sheet-card-name {
|
||
font-size: 26rpx;
|
||
font-weight: 700;
|
||
color: #1e293b;
|
||
}
|
||
.vip__sheet-card-date {
|
||
font-size: 20rpx;
|
||
color: #64748b;
|
||
}
|
||
.vip__sheet-bottom {
|
||
padding: 20rpx 36rpx 24rpx;
|
||
border-top: 1rpx solid #f1f5f9;
|
||
}
|
||
.vip__sheet-btn {
|
||
height: 84rpx;
|
||
border-radius: 42rpx;
|
||
background: $fc-gradient-primary;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 6rpx 20rpx rgba(0, 224, 112, 0.25);
|
||
}
|
||
.vip__sheet-btn-txt {
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
color: #0f2f1d;
|
||
}
|
||
</style> |