feat: 优化UI
This commit is contained in:
+111
-16
@@ -37,7 +37,6 @@ Page({
|
||||
const quarterly = (parseFloat(options.quarterlyPrice) || 0) / 100
|
||||
const annual = (parseFloat(options.annualPrice) || 0) / 100
|
||||
|
||||
// 默认选中包年,否则最合算的
|
||||
let defaultPlan = 'monthly'
|
||||
let defaultPrice = monthly
|
||||
if (annual > 0) { defaultPlan = 'annual'; defaultPrice = annual }
|
||||
@@ -59,15 +58,42 @@ Page({
|
||||
})
|
||||
} else {
|
||||
// ─── VIP 会员模式 ───
|
||||
const gd = app.globalData
|
||||
this.setData({
|
||||
mode: 'vip',
|
||||
isVip: app.globalData.isVip,
|
||||
isVip: gd.isVip,
|
||||
selectedPlan: 'vip-all',
|
||||
currentPrice: '19.9'
|
||||
currentPrice: '--',
|
||||
vipExpireAt: gd.vipExpireAt ? gd.vipExpireAt.substring(0, 10) : ''
|
||||
})
|
||||
// 从后端拉 VIP 配置
|
||||
this._loadVipConfig()
|
||||
}
|
||||
},
|
||||
|
||||
_loadVipConfig() {
|
||||
const self = this
|
||||
api.getVipConfig().then(function (res) {
|
||||
if (res.code === 200 && res.data) {
|
||||
var cfg = res.data
|
||||
// 后端单位:分 → 元
|
||||
var price = cfg.discountedPrice > 0 ? (cfg.discountedPrice / 100).toFixed(2) : (cfg.price / 100).toFixed(2)
|
||||
var originalPrice = cfg.price > 0 ? (cfg.price / 100).toFixed(2) : ''
|
||||
var hasDiscount = cfg.discountedPrice > 0 && cfg.discountedPrice < cfg.price
|
||||
self.setData({
|
||||
currentPrice: price,
|
||||
vipPrice: price,
|
||||
vipOriginalPrice: hasDiscount ? originalPrice : '',
|
||||
vipRemark: cfg.remark || ''
|
||||
})
|
||||
}
|
||||
}).catch(function (err) {
|
||||
console.error('[VIP] 获取配置失败:', err)
|
||||
// 容错:使用默认价格
|
||||
self.setData({ currentPrice: '19.90', vipPrice: '19.90', vipOriginalPrice: '29.90' })
|
||||
})
|
||||
},
|
||||
|
||||
onShow() {
|
||||
if (this.data.mode === 'vip') {
|
||||
this.setData({ isVip: app.globalData.isVip })
|
||||
@@ -95,20 +121,42 @@ Page({
|
||||
const { mode, selectedPlan, currentPrice, channelId } = this.data
|
||||
|
||||
if (mode === 'vip') {
|
||||
// ── VIP 全频道(模拟,后续接入时替换) ──
|
||||
wx.showModal({
|
||||
title: '确认支付',
|
||||
content: `即将支付 ¥${currentPrice} 开通全频道会员`,
|
||||
success(res) {
|
||||
if (res.confirm) {
|
||||
app.upgradeVip()
|
||||
wx.showToast({ title: '开通成功!', icon: 'success' })
|
||||
setTimeout(function () {
|
||||
self.setData({ isVip: true })
|
||||
}, 500)
|
||||
// ── VIP 永久会员:调后端预支付接口 ──
|
||||
wx.showLoading({ title: '获取支付信息...' })
|
||||
|
||||
api.initiateVipPayment()
|
||||
.then(function (res) {
|
||||
if (res.code !== 200 || !res.data || !res.data.payments) {
|
||||
wx.hideLoading()
|
||||
wx.showToast({ title: res.msg || '获取支付信息失败', icon: 'none' })
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const payments = res.data.payments
|
||||
const outTradeNo = res.data.outTradeNo
|
||||
wx.hideLoading()
|
||||
|
||||
wx.requestPayment({
|
||||
timeStamp: payments.timeStamp,
|
||||
nonceStr: payments.nonceStr,
|
||||
package: payments.package,
|
||||
signType: payments.signType || 'RSA',
|
||||
paySign: payments.paySign,
|
||||
success() {
|
||||
self._pollVipStatus(outTradeNo, 3, 2000)
|
||||
},
|
||||
fail(err) {
|
||||
if (err.errMsg && err.errMsg.indexOf('cancel') > -1) return
|
||||
wx.showToast({ title: '支付失败,请重试', icon: 'none' })
|
||||
console.error('[VIP支付] wx.requestPayment 失败:', err)
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(function (err) {
|
||||
wx.hideLoading()
|
||||
console.error('[VIP支付] 接口请求失败:', err)
|
||||
wx.showToast({ title: '网络异常,请重试', icon: 'none' })
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -216,6 +264,53 @@ Page({
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 轮询 VIP 支付状态
|
||||
* 成功后更新全局 isVip + 触发 vipChange 事件
|
||||
*/
|
||||
_pollVipStatus(outTradeNo, retries, interval) {
|
||||
const self = this
|
||||
wx.showLoading({ title: '验证中...' })
|
||||
|
||||
api.queryPayStatus(outTradeNo)
|
||||
.then(function (paid) {
|
||||
if (paid) {
|
||||
// ✅ VIP 开通确认成功
|
||||
wx.hideLoading()
|
||||
// 更新全局状态
|
||||
app.globalData.isVip = true
|
||||
app.emit('vipChange', { isVip: true })
|
||||
self.setData({ isVip: true })
|
||||
wx.showToast({ title: '🎉 VIP 开通成功!', icon: 'none' })
|
||||
setTimeout(function () { wx.navigateBack() }, 1500)
|
||||
} else if (retries > 1) {
|
||||
// 🔄 尚未到账,等待后重试
|
||||
setTimeout(function () {
|
||||
self._pollVipStatus(outTradeNo, retries - 1, interval)
|
||||
}, interval)
|
||||
} else {
|
||||
// ⏳ 重试耗尽,乐观提示
|
||||
wx.hideLoading()
|
||||
wx.showModal({
|
||||
title: '支付处理中',
|
||||
content: 'VIP 开通已提交,正在确认中,稍后请重新进入查看',
|
||||
showCancel: false,
|
||||
success: function () { wx.navigateBack() }
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(function (err) {
|
||||
wx.hideLoading()
|
||||
console.error('[VIP支付] 查询状态失败:', err)
|
||||
wx.showModal({
|
||||
title: '支付处理中',
|
||||
content: 'VIP 开通已提交,稍后请刷新查看是否生效',
|
||||
showCancel: false,
|
||||
success: function () { wx.navigateBack() }
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
goBack() {
|
||||
wx.navigateBack()
|
||||
}
|
||||
|
||||
+55
-25
@@ -2,12 +2,56 @@
|
||||
<page-meta page-style="overflow: hidden;" />
|
||||
<view class="vip-page">
|
||||
|
||||
<!-- ── 已是VIP,直接返回 ── -->
|
||||
<!-- ── 已是 VIP,展示会员权益页 ── -->
|
||||
<view wx:if="{{isVip && mode === 'vip'}}" class="vip-done">
|
||||
<text class="vip-done-icon">👑</text>
|
||||
<text class="vip-done-title">您已经是全频道会员</text>
|
||||
<text class="vip-done-desc">畅享全部频道,尊享专属权益</text>
|
||||
<button class="done-back-btn" bindtap="goBack">返回</button>
|
||||
|
||||
<!-- 头部云光装饰 -->
|
||||
<view class="vip-glow"></view>
|
||||
|
||||
<!-- 安全区域占位 -->
|
||||
<view style="height: {{statusBarHeight}}px;"></view>
|
||||
|
||||
<!-- 嵇章区 -->
|
||||
<view class="vip-done-hero">
|
||||
<view class="vip-crown-wrap">
|
||||
<text class="vip-crown">👑</text>
|
||||
<view class="vip-crown-ring ring-1"></view>
|
||||
<view class="vip-crown-ring ring-2"></view>
|
||||
</view>
|
||||
<text class="vip-done-title">全频道会员</text>
|
||||
<view wx:if="{{vipExpireAt}}" class="vip-expire-badge">
|
||||
<text class="vip-expire-text">永久有效 · 不限期限</text>
|
||||
</view>
|
||||
<view wx:else class="vip-expire-badge">
|
||||
<text class="vip-expire-text">永久有效 · 不限期限</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 权益卡片网格 -->
|
||||
<view class="vip-done-benefits">
|
||||
<view class="vip-benefit-item">
|
||||
<text class="vb-icon">🔓</text>
|
||||
<text class="vb-name">全频道解锁</text>
|
||||
</view>
|
||||
<view class="vip-benefit-item">
|
||||
<text class="vb-icon">🎧</text>
|
||||
<text class="vb-name">免广告收听</text>
|
||||
</view>
|
||||
<view class="vip-benefit-item">
|
||||
<text class="vb-icon">⭐</text>
|
||||
<text class="vb-name">优先推送</text>
|
||||
</view>
|
||||
<view class="vip-benefit-item">
|
||||
<text class="vb-icon">💬</text>
|
||||
<text class="vb-name">互动评论</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 返回按钮 -->
|
||||
<view class="vip-done-actions">
|
||||
<button class="done-back-btn" bindtap="goBack">返回收听</button>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<!-- ── 主Scroll区域 ── -->
|
||||
@@ -23,7 +67,7 @@
|
||||
<view class="vip-hero">
|
||||
<text class="vip-hero-title">{{mode === 'channel' ? channelName : '开通全频道会员'}}</text>
|
||||
<text class="vip-hero-desc">
|
||||
{{mode === 'channel' ? '选择适合你的订阅方案,随时随地收听' : '解锁全部频道,告别无聊早晨'}}
|
||||
{{mode === 'channel' ? '选择适合你的订阅方案,随时随地收听' : '一次开通,永久解锁全部频道'}}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
@@ -51,20 +95,6 @@
|
||||
<text class="benefit-desc">收听无任何打扰</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="benefit-item">
|
||||
<text class="benefit-check">⬇️</text>
|
||||
<view class="benefit-info">
|
||||
<text class="benefit-name">音频全量下载</text>
|
||||
<text class="benefit-desc">支持离线随时听</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="benefit-item">
|
||||
<text class="benefit-check">⏰</text>
|
||||
<view class="benefit-info">
|
||||
<text class="benefit-name">晨间定时播</text>
|
||||
<text class="benefit-desc">专属智能闹钟</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -77,14 +107,14 @@
|
||||
bindtap="selectPlan"
|
||||
data-plan="vip-all"
|
||||
>
|
||||
<view wx:if="{{selectedPlan === 'vip-all'}}" class="plan-badge">限时特惠</view>
|
||||
<view wx:if="{{vipOriginalPrice}}" class="plan-badge">限时特惠</view>
|
||||
<view class="plan-info">
|
||||
<text class="plan-name">全频道连续包月</text>
|
||||
<text class="plan-desc">自动续费,随时可取消</text>
|
||||
<text class="plan-name">永久会员</text>
|
||||
<text class="plan-desc">{{vipRemark || '一次购买,永久畅听全部频道'}}</text>
|
||||
</view>
|
||||
<view class="plan-price">
|
||||
<text class="price-amount"><text class="price-symbol">¥</text>19.9</text>
|
||||
<text class="price-original">¥29.9</text>
|
||||
<text class="price-amount"><text class="price-symbol">¥</text>{{vipPrice || currentPrice}}</text>
|
||||
<text class="price-original" wx:if="{{vipOriginalPrice}}">¥{{vipOriginalPrice}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
+134
-21
@@ -9,39 +9,152 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 已是VIP */
|
||||
/* 已是VIP 页面整体 */
|
||||
.vip-done {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
padding: 40rpx;
|
||||
background: #FFFFFF;
|
||||
background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.vip-done-icon {
|
||||
font-size: 120rpx;
|
||||
margin-bottom: 40rpx;
|
||||
|
||||
/* 头部云光装饰 */
|
||||
.vip-glow {
|
||||
position: absolute;
|
||||
top: -150rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 1000rpx;
|
||||
height: 1000rpx;
|
||||
background: radial-gradient(circle, rgba(251, 191, 36, 0.15) 0%, rgba(0,0,0,0) 60%);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* 勋章区 */
|
||||
.vip-done-hero {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: 120rpx;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.vip-crown-wrap {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
background: linear-gradient(135deg, #FFDF8A 0%, #D99B22 100%);
|
||||
border-radius: 50%;
|
||||
border: 4rpx solid rgba(255, 255, 255, 0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
box-shadow: 0 16rpx 40rpx rgba(217, 155, 34, 0.4);
|
||||
margin-bottom: 60rpx;
|
||||
}
|
||||
|
||||
.vip-crown {
|
||||
font-size: 80rpx;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.vip-crown-ring {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
border: 2rpx solid rgba(255, 223, 138, 0.4);
|
||||
}
|
||||
.ring-1 {
|
||||
width: 240rpx;
|
||||
height: 240rpx;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
.ring-2 {
|
||||
width: 300rpx;
|
||||
height: 300rpx;
|
||||
animation: pulse 2s infinite 1s;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(0.8); opacity: 1; }
|
||||
100% { transform: scale(1.2); opacity: 0; }
|
||||
}
|
||||
|
||||
.vip-done-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
margin-bottom: 16rpx;
|
||||
font-size: 44rpx;
|
||||
font-weight: 800;
|
||||
color: #FFF;
|
||||
letter-spacing: 4rpx;
|
||||
margin-bottom: 24rpx;
|
||||
text-shadow: 0 4rpx 16rpx rgba(0,0,0,0.5);
|
||||
}
|
||||
.vip-done-desc {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
margin-bottom: 48rpx;
|
||||
}
|
||||
.done-back-btn {
|
||||
padding: 16rpx 48rpx;
|
||||
background: #F5F5F5;
|
||||
|
||||
.vip-expire-badge {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 12rpx 32rpx;
|
||||
border-radius: 999rpx;
|
||||
font-size: 28rpx;
|
||||
border: 1rpx solid rgba(251, 191, 36, 0.3);
|
||||
}
|
||||
|
||||
.vip-expire-text {
|
||||
font-size: 24rpx;
|
||||
color: #FCD34D;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 权益网格 */
|
||||
.vip-done-benefits {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
gap: 24rpx 0;
|
||||
margin-top: 80rpx;
|
||||
padding: 0 50rpx;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.vip-benefit-item {
|
||||
width: 48%;
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.vb-icon {
|
||||
font-size: 40rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.vb-name {
|
||||
font-size: 28rpx;
|
||||
color: #FFF;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 按钮操作区 */
|
||||
.vip-done-actions {
|
||||
margin-top: auto;
|
||||
margin-bottom: 120rpx;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.done-back-btn {
|
||||
background: linear-gradient(90deg, #FCD34D 0%, #F59E0B 100%);
|
||||
color: #5F370E;
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
padding: 24rpx 120rpx;
|
||||
border-radius: 999rpx;
|
||||
box-shadow: 0 8rpx 32rpx rgba(245, 158, 11, 0.4);
|
||||
border: none;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.done-back-btn::after { border: none; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user