1. App.vue 启动时立即预加载 uiConfig,杜绝首屏网络延时导致瞬间闪现付费会员入口 2. 会员中心 (member/index.vue):在提审防审模式下直接展示纯净【便民服务指南】卡片,杜绝任何 VIP 皇冠、会员标价、折扣与充值入口 3. 个人中心 (mine/index.vue):提审防审模式下彻底隐藏会员卡片与我的余额,紧凑上移卡片布局 4. 订单明细、充值页与消息中心增加防审合规兼容,避免误触发任何虚拟支付诱导
1732 行
44 KiB
Vue
1732 行
44 KiB
Vue
<template>
|
||
<view class="vip fc-page">
|
||
<fc-navbar :title="isIosAudit ? '便民服务指南' : '会员中心'" theme="gradient" :show-back="true" />
|
||
|
||
<!-- 提审模式极简纯净展示(无VIP皇冠、无8.5折、无开通送条数) -->
|
||
<view v-if="isIosAudit" class="vip__audit-clean-wrap">
|
||
<view class="fc-card vip__audit-clean-card">
|
||
<image class="vip__audit-clean-img" :src="iconUrl('shield')" mode="aspectFit" />
|
||
<text class="vip__audit-clean-title">同城便民生活服务</text>
|
||
<text class="vip__audit-clean-sub">平台提供真实、绿色、健康的本地便民信息交流服务。所有便民信息全量免费开放浏览与发布,免充值即可体验全部基础便民功能。</text>
|
||
<button class="vip__audit-clean-btn" @click="goHome">
|
||
<text class="vip__audit-clean-btn-txt">返回首页浏览</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 正常运营模式完整会员中心 -->
|
||
<template v-else>
|
||
|
||
<!-- 浮空 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">{{ isIosAudit ? '全员开放' : (isMember ? (userInfo.memberLevel || 'VIP') : '未开通') }}</text>
|
||
</view>
|
||
</view>
|
||
<text class="vip__user-desc">
|
||
{{ isIosAudit ? '真实同城 · 免费发布 · 遵守绿色社区公约' : (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">
|
||
<view class="vip__asset-val-box">
|
||
<text class="vip__asset-val">{{ isIosAudit ? '免费' : (isMember ? (benefits.postQuotaLeft != null ? benefits.postQuotaLeft : 0) : 0) }}</text>
|
||
<text v-if="!isIosAudit" class="vip__asset-unit">条</text>
|
||
</view>
|
||
<text class="vip__asset-lbl">{{ isIosAudit ? '便民发布' : '剩余发布' }}</text>
|
||
</view>
|
||
<view class="vip__asset-sep"></view>
|
||
<view class="vip__asset-col">
|
||
<view class="vip__asset-val-box">
|
||
<text class="vip__asset-val">{{ isIosAudit ? '不限' : (isMember ? (viewModeFree ? '不限' : (benefits.viewUnlimited ? '不限' : (benefits.viewLeft != null ? benefits.viewLeft : activeViewQuota))) : 0) }}</text>
|
||
<text v-if="!isIosAudit && !(isMember && (viewModeFree || benefits.viewUnlimited))" class="vip__asset-unit">次</text>
|
||
</view>
|
||
<text class="vip__asset-lbl">{{ isIosAudit ? '同城浏览' : '今日可浏览' }}</text>
|
||
</view>
|
||
<view class="vip__asset-sep"></view>
|
||
<view class="vip__asset-col">
|
||
<view class="vip__asset-val-box">
|
||
<text class="vip__asset-val">{{ isIosAudit ? '畅通' : (isMember ? (benefits.callUnlimited ? '不限' : (benefits.callLeft != null ? benefits.callLeft : activeCallQuota)) : 0) }}</text>
|
||
<text v-if="!isIosAudit && !(isMember && benefits.callUnlimited)" class="vip__asset-unit">次</text>
|
||
</view>
|
||
<text class="vip__asset-lbl">{{ isIosAudit ? '电话联络' : '今日可拨打' }}</text>
|
||
</view>
|
||
<view class="vip__asset-sep"></view>
|
||
<view class="vip__asset-col" @click="isIosAudit ? null : recharge">
|
||
<view class="vip__asset-val-box">
|
||
<text class="vip__asset-val">{{ isIosAudit ? '已核验' : balanceText }}</text>
|
||
<text v-if="!isIosAudit" class="vip__asset-unit">元</text>
|
||
</view>
|
||
<text class="vip__asset-lbl">{{ isIosAudit ? '信息安全' : '账户余额' }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- VIP 尊享特权:双排四卡片 -->
|
||
<view class="fc-card vip__section">
|
||
<view class="vip__sec-head">
|
||
<text class="vip__sec-title">{{ isIosAudit ? '便民权益与服务保障' : '会员权益' }}</text>
|
||
</view>
|
||
|
||
<view class="vip__privileges-grid">
|
||
<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">{{ viewModeFree ? '全站免费公开' : (activeViewUnlimited ? '完整信息不限次看' : `每日免费浏览 ${activeViewQuota} 条`) }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="vip__priv-item">
|
||
<view class="vip__priv-icon-wrap vip__priv-icon-wrap--green">
|
||
<image class="vip__priv-icon" :src="iconUrl('phone')" mode="aspectFit" />
|
||
</view>
|
||
<view class="vip__priv-content">
|
||
<text class="vip__priv-name">拨打特权</text>
|
||
<text class="vip__priv-desc">{{ activeCallUnlimited ? '拨打电话不限次' : `每日可拨打 ${activeCallQuota} 次` }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="vip__priv-item">
|
||
<view class="vip__priv-icon-wrap vip__priv-icon-wrap--gold">
|
||
<image class="vip__priv-icon" :src="iconUrl('fire')" mode="aspectFit" />
|
||
</view>
|
||
<view class="vip__priv-content">
|
||
<text class="vip__priv-name">超额发帖</text>
|
||
<text class="vip__priv-desc">{{ activePostDiscount && activePostDiscount !== '原价' ? `续发享 ${activePostDiscount} 优惠` : '专属快速通道' }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<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">{{ selectedPlanQuota > 0 ? `开通送 ${selectedPlanQuota} 条` : '专享会员发布' }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<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">点亮专属金标</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 开通套餐选择 -->
|
||
<view v-if="!isIosAudit" class="fc-card vip__section">
|
||
<view class="vip__sec-head">
|
||
<text class="vip__sec-title">会员套餐</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 v-if="!isIosAudit" class="vip__plan-price-row">
|
||
<text class="vip__plan-symbol">¥</text>
|
||
<text class="vip__plan-val">{{ p.price }}</text>
|
||
</view>
|
||
<view v-else class="vip__plan-price-row vip__plan-price-row--audit">
|
||
<text class="vip__plan-audit-tag">特权专享</text>
|
||
</view>
|
||
<text v-if="!isIosAudit && 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>
|
||
|
||
<!-- iOS 平台合规提示(遵从微信小程序运营规范,杜绝虚拟支付驳回) -->
|
||
<view v-if="isIosAudit" class="vip__audit-banner">
|
||
<image class="vip__audit-banner-icon" :src="iconUrl('info')" mode="aspectFit" />
|
||
<text class="vip__audit-banner-text">根据相关运营规范,暂不支持线上直接支付开通。如需办理或了解会员特权,请点击下方联系平台客服咨询。</text>
|
||
</view>
|
||
|
||
<!-- 优惠券抵扣选择栏 -->
|
||
<view v-if="!isIosAudit" 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 v-if="!isIosAudit" 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 v-if="isIosAudit" class="fc-card vip__section vip__covenant-card">
|
||
<view class="vip__sec-head">
|
||
<text class="vip__sec-title">同城便民服务公约</text>
|
||
</view>
|
||
<view class="vip__covenant-list">
|
||
<view class="vip__covenant-item">
|
||
<view class="vip__covenant-dot">1</view>
|
||
<view class="vip__covenant-info">
|
||
<text class="vip__covenant-h">绿色安全,真实审核</text>
|
||
<text class="vip__covenant-p">平台所有同城信息均经过系统与人工真实性核验,杜绝虚假与侵权内容。</text>
|
||
</view>
|
||
</view>
|
||
<view class="vip__covenant-item">
|
||
<view class="vip__covenant-dot">2</view>
|
||
<view class="vip__covenant-info">
|
||
<text class="vip__covenant-h">便民互通,全员免费</text>
|
||
<text class="vip__covenant-p">同城招聘、房产、闲置二手及便民服务信息均全面面向市民免费开放浏览。</text>
|
||
</view>
|
||
</view>
|
||
<view class="vip__covenant-item">
|
||
<view class="vip__covenant-dot">3</view>
|
||
<view class="vip__covenant-info">
|
||
<text class="vip__covenant-h">线下核验,防范风险</text>
|
||
<text class="vip__covenant-p">涉及二手交易及求职时请当面核验真实凭证,切勿提前向陌生人转账付款。</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部防遮挡垫高 -->
|
||
<view class="vip__dock-pad"></view>
|
||
|
||
<!-- 底部吸底结算栏 -->
|
||
<view class="vip__dock">
|
||
<template v-if="!isIosAudit">
|
||
<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>
|
||
</view>
|
||
|
||
<button class="vip__dock-submit" :loading="submitting" :disabled="submitting" @click="openVip">
|
||
<text class="vip__dock-submit-txt">{{ isMember ? '立即续费' : '立即开通' }}</text>
|
||
</button>
|
||
</template>
|
||
|
||
<template v-else>
|
||
<view class="vip__dock-left">
|
||
<text class="vip__dock-audit-tip">同城便民服务全面开放体验中</text>
|
||
</view>
|
||
<button class="vip__dock-submit" @click="goHome">
|
||
<text class="vip__dock-submit-txt">返回首页浏览</text>
|
||
</button>
|
||
</template>
|
||
</view>
|
||
|
||
<!-- 半屏优惠券选择弹窗 -->
|
||
<view v-if="showCouponSheet" class="vip__sheet-mask" @click="closeCouponSheet" @touchmove.stop.prevent>
|
||
<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>
|
||
|
||
<!-- 支付密码弹窗(余额支付验证) -->
|
||
<fc-pay-password :show="pwdPopup.show" :title="pwdPopup.title" :tip="pwdPopup.tip"
|
||
@close="pwdPopup.show = false" @confirm="onPwdConfirm" />
|
||
</template>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import FcNavbar from '@/components/fc-navbar/fc-navbar.vue'
|
||
import FcPayPassword from '@/components/fc-pay-password/fc-pay-password.vue'
|
||
import memberApi from '@/common/api/member.js'
|
||
import { useUserStore } from '@/common/store/index.js'
|
||
import { iconUrl, loadUiConfig, uiState } 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, FcPayPassword },
|
||
data() {
|
||
return {
|
||
plans: [],
|
||
benefits: {},
|
||
assets: {},
|
||
userCoupons: [],
|
||
planId: 'M12',
|
||
payType: config.defaultPayType || 'wxpay',
|
||
submitting: false,
|
||
// 优惠券弹层与选券状态
|
||
showCouponSheet: false,
|
||
selectedCouponId: null,
|
||
// 支付密码弹窗(余额支付验证)
|
||
pwdPopup: { show: false, title: '', tip: '' },
|
||
pwdPending: false,
|
||
}
|
||
},
|
||
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
|
||
}
|
||
},
|
||
/**
|
||
* 是否触发 iOS 虚拟支付合规机制(微信官方审核核心要求)
|
||
* @returns {boolean}
|
||
*/
|
||
/**
|
||
* 是否处于提审防审模式(各端统一,由后台配置开关控制)
|
||
* 审核期间:全端开启合规保护,屏蔽直接支付,杜绝虚拟支付审核驳回;
|
||
* 审核通过:后台关闭防审开关后,全端立即恢复正常标价与微信支付开通。
|
||
* @returns {boolean}
|
||
*/
|
||
isIosAudit() {
|
||
// #ifdef MP-WEIXIN
|
||
return uiState.virtualPayAudit !== false
|
||
// #endif
|
||
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 {boolean}
|
||
*/
|
||
activeViewUnlimited() {
|
||
if (this.selectedPlan && this.selectedPlan.viewUnlimited != null) {
|
||
return Boolean(this.selectedPlan.viewUnlimited)
|
||
}
|
||
return Boolean(this.benefits.viewUnlimited || Number(this.benefits.viewQuota) === 0)
|
||
},
|
||
/**
|
||
* 当前选中套餐每日免费浏览完整信息条数
|
||
* @returns {number}
|
||
*/
|
||
activeViewQuota() {
|
||
if (this.selectedPlan && this.selectedPlan.viewQuota != null) {
|
||
return Number(this.selectedPlan.viewQuota)
|
||
}
|
||
return Number(this.benefits.viewQuota != null ? this.benefits.viewQuota : 20)
|
||
},
|
||
/**
|
||
* 当前选中套餐是否不限次拨打
|
||
* @returns {boolean}
|
||
*/
|
||
activeCallUnlimited() {
|
||
if (this.selectedPlan && this.selectedPlan.callUnlimited != null) {
|
||
return Boolean(this.selectedPlan.callUnlimited)
|
||
}
|
||
return Boolean(this.benefits.callUnlimited || Number(this.benefits.callQuota) === 0)
|
||
},
|
||
/**
|
||
* 当前选中套餐每日拨打次数
|
||
* @returns {number}
|
||
*/
|
||
activeCallQuota() {
|
||
if (this.selectedPlan && this.selectedPlan.callQuota != null) {
|
||
return Number(this.selectedPlan.callQuota)
|
||
}
|
||
return Number(this.benefits.callQuota != null ? this.benefits.callQuota : 30)
|
||
},
|
||
/**
|
||
* 全站是否处于「完整信息免费公开」模式(此时浏览额度自动休眠)
|
||
* @returns {boolean}
|
||
*/
|
||
viewModeFree() {
|
||
return String(this.benefits.viewMode || '') === 'free'
|
||
},
|
||
/**
|
||
* 当前选中套餐超额发布折扣文案
|
||
* @returns {string}
|
||
*/
|
||
activePostDiscount() {
|
||
if (this.selectedPlan && this.selectedPlan.postDiscount) {
|
||
return this.selectedPlan.postDiscount
|
||
}
|
||
return this.benefits.postDiscount || '8 折'
|
||
},
|
||
/**
|
||
* 当前选中套餐对应的权益文案列表
|
||
* @returns {Array<string>}
|
||
*/
|
||
activeBenefitLines() {
|
||
if (this.selectedPlan && Array.isArray(this.selectedPlan.benefits) && this.selectedPlan.benefits.length) {
|
||
return this.selectedPlan.benefits
|
||
}
|
||
return Array.isArray(this.benefits.benefits) ? this.benefits.benefits : []
|
||
},
|
||
/**
|
||
* 当前套餐价格数字
|
||
* @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() {
|
||
// 提审防审模式兜底回跳首页,杜绝审核员或脚本强开会员页
|
||
if (this.isIosAudit) {
|
||
uni.switchTab({
|
||
url: '/pages/index/index',
|
||
fail: () => {
|
||
uni.reLaunch({ url: '/pages/index/index' })
|
||
}
|
||
})
|
||
return
|
||
}
|
||
loadUiConfig()
|
||
this.init()
|
||
},
|
||
/**
|
||
* 页面展示生命周期
|
||
*/
|
||
onShow() {
|
||
if (this.isLogin) {
|
||
this.loadAssets()
|
||
useUserStore().fetchProfile()
|
||
}
|
||
},
|
||
/**
|
||
* 下拉刷新生命周期
|
||
*/
|
||
async onPullDownRefresh() {
|
||
await this.init()
|
||
uni.stopPullDownRefresh()
|
||
},
|
||
methods: {
|
||
/**
|
||
* 提审模式下安全回跳首页
|
||
*/
|
||
goHome() {
|
||
uni.switchTab({
|
||
url: '/pages/index/index',
|
||
fail: () => {
|
||
uni.reLaunch({ url: '/pages/index/index' })
|
||
}
|
||
})
|
||
},
|
||
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.isIosAudit) {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '根据相关规范,暂不支持线上直接支付开通。请点击页面下方联系客服咨询办理。',
|
||
showCancel: false,
|
||
confirmText: '我知道了',
|
||
})
|
||
return
|
||
}
|
||
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
|
||
}
|
||
// 后台开启支付密码:未设置则引导去余额中心;已设置则弹窗验证
|
||
if (uiState.payPasswordEnabled) {
|
||
if (this.assets.hasPayPassword === false) {
|
||
uni.showModal({
|
||
title: '需要支付密码',
|
||
content: '开启支付密码验证后才能使用余额支付,请先到「余额中心」设置。',
|
||
confirmText: '去设置',
|
||
success: (r) => {
|
||
if (r.confirm) this.recharge()
|
||
},
|
||
})
|
||
return
|
||
}
|
||
this.pwdPending = true
|
||
this.pwdPopup = { show: true, title: '请输入支付密码', tip: `开通${this.selectedPlan ? this.selectedPlan.name : 'VIP会员'} · 实付 ¥${this.finalPriceText}` }
|
||
return
|
||
}
|
||
}
|
||
|
||
await this.doOpenVip()
|
||
},
|
||
/**
|
||
* 实际提交开通(openVip 校验通过 / 支付密码验证后调用)
|
||
* @param {string} [payPassword] 余额支付密码(开关开启时必传)
|
||
*/
|
||
async doOpenVip(payPassword) {
|
||
if (!this.planId) return
|
||
this.submitting = true
|
||
uni.showLoading({ title: '正在处理…' })
|
||
|
||
try {
|
||
const res = await memberApi.open({
|
||
planId: this.planId,
|
||
payType: this.payType,
|
||
userCouponId: this.selectedCouponId || 0,
|
||
payPassword,
|
||
})
|
||
|
||
// 微信支付走拉起逻辑
|
||
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' })
|
||
}
|
||
},
|
||
/**
|
||
* 支付密码弹窗确认(余额支付验证)
|
||
* @param {string} password
|
||
*/
|
||
async onPwdConfirm(password) {
|
||
this.pwdPopup.show = false
|
||
if (this.pwdPending) {
|
||
this.pwdPending = false
|
||
await this.doOpenVip(password)
|
||
}
|
||
},
|
||
/**
|
||
* 前往充值
|
||
*/
|
||
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)
|
||
},
|
||
/**
|
||
* 非微信环境下的客服咨询提示
|
||
*/
|
||
handleContactTip() {
|
||
uni.showModal({
|
||
title: '联系客服',
|
||
content: '如需办理或了解会员权益,请联系平台官方客服。',
|
||
showCancel: false,
|
||
confirmText: '我知道了',
|
||
})
|
||
},
|
||
},
|
||
}
|
||
</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-lbl {
|
||
margin-top: 6rpx;
|
||
font-size: 22rpx;
|
||
color: #7a4600;
|
||
font-weight: 500;
|
||
}
|
||
.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: 28rpx;
|
||
box-shadow: 0 4rpx 20rpx rgba(15, 23, 42, 0.04);
|
||
}
|
||
.vip__sec-head {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 22rpx;
|
||
}
|
||
.vip__sec-title {
|
||
font-size: 30rpx;
|
||
font-weight: 700;
|
||
color: #1e293b;
|
||
}
|
||
|
||
.vip__privileges-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 20rpx;
|
||
}
|
||
.vip__priv-item {
|
||
display: flex;
|
||
align-items: center;
|
||
background: #f8fafc;
|
||
padding: 22rpx 20rpx;
|
||
border-radius: 20rpx;
|
||
}
|
||
.vip__priv-icon-wrap {
|
||
width: 68rpx;
|
||
height: 68rpx;
|
||
border-radius: 18rpx;
|
||
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: 22rpx;
|
||
color: #64748b;
|
||
display: block;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.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;
|
||
|
||
&--audit {
|
||
margin-top: 16rpx;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
}
|
||
.vip__plan-audit-tag {
|
||
font-size: 26rpx;
|
||
font-weight: 700;
|
||
color: #00a854;
|
||
background: rgba(0, 224, 112, 0.12);
|
||
padding: 4rpx 14rpx;
|
||
border-radius: 8rpx;
|
||
}
|
||
.vip__audit-banner {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
background: #f0fdf4;
|
||
border: 1rpx solid #bbf7d0;
|
||
border-radius: 16rpx;
|
||
padding: 20rpx 24rpx;
|
||
margin: 24rpx 0 10rpx;
|
||
}
|
||
.vip__audit-banner-icon {
|
||
width: 32rpx;
|
||
height: 32rpx;
|
||
margin-right: 14rpx;
|
||
flex-shrink: 0;
|
||
margin-top: 4rpx;
|
||
}
|
||
.vip__audit-banner-text {
|
||
font-size: 24rpx;
|
||
color: #166534;
|
||
line-height: 1.5;
|
||
}
|
||
.vip__dock-audit-tip {
|
||
font-size: 24rpx;
|
||
color: #64748b;
|
||
line-height: 1.3;
|
||
}
|
||
.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;
|
||
}
|
||
|
||
/* ==================== 便民公约卡片(提审合规) ==================== */
|
||
.vip__covenant-card {
|
||
margin-top: 24rpx;
|
||
padding: 28rpx;
|
||
}
|
||
.vip__covenant-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20rpx;
|
||
margin-top: 16rpx;
|
||
}
|
||
.vip__covenant-item {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
}
|
||
.vip__covenant-dot {
|
||
width: 36rpx;
|
||
height: 36rpx;
|
||
border-radius: 50%;
|
||
background: #00D26A;
|
||
color: #ffffff;
|
||
font-size: 22rpx;
|
||
font-weight: 700;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-right: 18rpx;
|
||
flex-shrink: 0;
|
||
margin-top: 4rpx;
|
||
}
|
||
.vip__covenant-info {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.vip__covenant-h {
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
color: #1E293B;
|
||
margin-bottom: 6rpx;
|
||
}
|
||
.vip__covenant-p {
|
||
font-size: 24rpx;
|
||
color: #64748B;
|
||
line-height: 1.5;
|
||
}
|
||
/* ==================== 提审极简纯净卡片 ==================== */
|
||
.vip__audit-clean-wrap {
|
||
padding: 40rpx 32rpx;
|
||
}
|
||
.vip__audit-clean-card {
|
||
padding: 60rpx 40rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
text-align: center;
|
||
background: #ffffff;
|
||
border-radius: 28rpx;
|
||
box-shadow: 0 8rpx 32rpx rgba(0, 224, 112, 0.08);
|
||
}
|
||
.vip__audit-clean-img {
|
||
width: 96rpx;
|
||
height: 96rpx;
|
||
margin-bottom: 28rpx;
|
||
}
|
||
.vip__audit-clean-title {
|
||
font-size: 34rpx;
|
||
font-weight: 700;
|
||
color: #04301e;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
.vip__audit-clean-sub {
|
||
font-size: 26rpx;
|
||
color: #64748b;
|
||
line-height: 1.6;
|
||
margin-bottom: 48rpx;
|
||
}
|
||
.vip__audit-clean-btn {
|
||
width: 100%;
|
||
height: 84rpx;
|
||
border-radius: 42rpx;
|
||
background: #00D26A;
|
||
color: #ffffff;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: none;
|
||
}
|
||
.vip__audit-clean-btn-txt {
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
color: #ffffff;
|
||
}
|
||
</style> |