优化小程序阅读页面,新增好友优惠展示逻辑,支持通过推荐人获取折扣。调整价格计算方式,确保用户在购买章节时能看到实际优惠。更新相关样式以提升用户体验。

This commit is contained in:
乘风
2026-02-12 17:08:46 +08:00
parent f1dad89434
commit 543a465682
20 changed files with 352 additions and 139 deletions

View File

@@ -7,9 +7,9 @@ App({
globalData: {
// API基础地址 - 连接真实后端
// baseUrl: 'https://soulapi.quwanzhi.com',
// baseUrl: 'https://souldev.quwanzhi.com',
baseUrl: 'https://souldev.quwanzhi.com',
// baseUrl: 'http://localhost:3006',
baseUrl: 'http://localhost:8080',
// baseUrl: 'http://localhost:8080',
// 小程序配置 - 真实AppID
appId: 'wxb8bbb2b10dec74aa',

View File

@@ -56,6 +56,12 @@ Page({
sectionPrice: 1,
fullBookPrice: 9.9,
totalSections: 62,
// 好友优惠展示
userDiscount: 5,
hasReferralDiscount: false,
showDiscountHint: false,
displaySectionPrice: 1,
displayFullBookPrice: 9.9,
// 弹窗
showShareModal: false,
@@ -131,11 +137,34 @@ Page({
}
try {
const config = await accessManager.fetchLatestConfig()
const userId = app.globalData.userInfo?.id
const [config, purchaseRes] = await Promise.all([
accessManager.fetchLatestConfig(),
userId ? app.request(`/api/miniprogram/user/purchase-status?userId=${userId}`) : Promise.resolve(null)
])
const sectionPrice = config.prices?.section ?? 1
const fullBookPrice = config.prices?.fullbook ?? 9.9
const userDiscount = config.userDiscount ?? 5
// 有推荐人 = ref/ referral_code 或 用户信息中有推荐人绑定
const hasReferral = !!(wx.getStorageSync('referral_code') || ref || purchaseRes?.data?.hasReferrer)
const hasReferralDiscount = hasReferral && userDiscount > 0
const showDiscountHint = userDiscount > 0
const displaySectionPrice = hasReferralDiscount
? Math.round(sectionPrice * (1 - userDiscount / 100) * 100) / 100
: sectionPrice
const displayFullBookPrice = hasReferralDiscount
? Math.round(fullBookPrice * (1 - userDiscount / 100) * 100) / 100
: fullBookPrice
this.setData({
freeIds: config.freeChapters,
sectionPrice: config.prices?.section ?? 1,
fullBookPrice: config.prices?.fullbook ?? 9.9
sectionPrice,
fullBookPrice,
userDiscount,
hasReferralDiscount,
showDiscountHint: userDiscount > 0,
displaySectionPrice,
displayFullBookPrice,
purchasedCount: purchaseRes?.data?.purchasedSections?.length ?? this.data.purchasedCount ?? 0
})
// 先拉取章节获取 idmid 时必需id 时可直接用)
@@ -157,7 +186,8 @@ Page({
accessState,
canAccess,
isLoggedIn: !!app.globalData.userInfo?.id,
showPaywall: !canAccess
showPaywall: !canAccess,
purchasedCount: purchaseRes?.data?.purchasedSections?.length ?? 0
})
await this.loadContent(mid, resolvedId, accessState, prefetchedChapter)
@@ -621,9 +651,34 @@ Page({
// 1. 刷新用户购买状态(从 orders 表拉取最新)
await accessManager.refreshUserPurchaseStatus()
// 2. 重新拉取免费列表(极端情况:刚登录时当前章节可能改免费了)
const config = await accessManager.fetchLatestConfig()
this.setData({ freeIds: config.freeChapters })
// 2. 重新拉取免费列表、价格与用户推荐人状态
const userId = app.globalData.userInfo?.id
const [config, purchaseRes] = await Promise.all([
accessManager.fetchLatestConfig(),
userId ? app.request(`/api/miniprogram/user/purchase-status?userId=${userId}`) : Promise.resolve(null)
])
const sectionPrice = config.prices?.section ?? this.data.sectionPrice ?? 1
const fullBookPrice = config.prices?.fullbook ?? this.data.fullBookPrice ?? 9.9
const userDiscount = config.userDiscount ?? 5
const hasReferral = !!(wx.getStorageSync('referral_code') || purchaseRes?.data?.hasReferrer)
const hasReferralDiscount = hasReferral && userDiscount > 0
const displaySectionPrice = hasReferralDiscount
? Math.round(sectionPrice * (1 - userDiscount / 100) * 100) / 100
: sectionPrice
const displayFullBookPrice = hasReferralDiscount
? Math.round(fullBookPrice * (1 - userDiscount / 100) * 100) / 100
: fullBookPrice
this.setData({
freeIds: config.freeChapters,
sectionPrice,
fullBookPrice,
userDiscount,
hasReferralDiscount,
showDiscountHint: userDiscount > 0,
displaySectionPrice,
displayFullBookPrice,
purchasedCount: purchaseRes?.data?.purchasedSections?.length ?? this.data.purchasedCount ?? 0
})
// 3. 重新判断当前章节权限
const newAccessState = await accessManager.determineAccessState(
@@ -1235,9 +1290,33 @@ Page({
wx.showLoading({ title: '重试中...', mask: true })
try {
// 重新拉取配置
const config = await accessManager.fetchLatestConfig()
this.setData({ freeIds: config.freeChapters })
const userId = app.globalData.userInfo?.id
const [config, purchaseRes] = await Promise.all([
accessManager.fetchLatestConfig(),
userId ? app.request(`/api/miniprogram/user/purchase-status?userId=${userId}`) : Promise.resolve(null)
])
const sectionPrice = config.prices?.section ?? this.data.sectionPrice ?? 1
const fullBookPrice = config.prices?.fullbook ?? this.data.fullBookPrice ?? 9.9
const userDiscount = config.userDiscount ?? 5
const hasReferral = !!(wx.getStorageSync('referral_code') || purchaseRes?.data?.hasReferrer)
const hasReferralDiscount = hasReferral && userDiscount > 0
const displaySectionPrice = hasReferralDiscount
? Math.round(sectionPrice * (1 - userDiscount / 100) * 100) / 100
: sectionPrice
const displayFullBookPrice = hasReferralDiscount
? Math.round(fullBookPrice * (1 - userDiscount / 100) * 100) / 100
: fullBookPrice
this.setData({
freeIds: config.freeChapters,
sectionPrice,
fullBookPrice,
userDiscount,
hasReferralDiscount,
showDiscountHint: userDiscount > 0,
displaySectionPrice,
displayFullBookPrice,
purchasedCount: purchaseRes?.data?.purchasedSections?.length ?? this.data.purchasedCount ?? 0
})
// 重新判断权限
const newAccessState = await accessManager.determineAccessState(

View File

@@ -166,7 +166,16 @@
<!-- 购买本章 - 直接调起支付 -->
<view class="purchase-btn purchase-section" bindtap="handlePurchaseSection">
<text class="btn-label">购买本章</text>
<text class="btn-price brand-color">¥{{section && section.price != null ? section.price : sectionPrice}}</text>
<view class="btn-price-row" wx:if="{{hasReferralDiscount}}">
<text class="btn-original-price">¥{{section && section.price != null ? section.price : sectionPrice}}</text>
<text class="btn-price brand-color">¥{{displaySectionPrice}}</text>
<text class="btn-discount-tag">省{{userDiscount}}%</text>
</view>
<view class="btn-price-row" wx:elif="{{showDiscountHint}}">
<text class="btn-price brand-color">¥{{section && section.price != null ? section.price : sectionPrice}}</text>
<text class="btn-discount-tag">好友链接立省{{userDiscount}}%</text>
</view>
<text wx:else class="btn-price brand-color">¥{{section && section.price != null ? section.price : sectionPrice}}</text>
</view>
<!-- 解锁全书 - 只有购买超过3章才显示 -->
@@ -176,8 +185,9 @@
<text class="btn-label">解锁全部 {{totalSections}} 章</text>
</view>
<view class="btn-right">
<text class="btn-price">¥{{fullBookPrice || 9.9}}</text>
<text class="btn-discount">省82%</text>
<text class="btn-original-price" wx:if="{{hasReferralDiscount}}">¥{{fullBookPrice || 9.9}}</text>
<text class="btn-price">¥{{hasReferralDiscount ? displayFullBookPrice : (fullBookPrice || 9.9)}}</text>
<text class="btn-discount">{{hasReferralDiscount ? '省' + userDiscount + '%' : '省82%'}}</text>
</view>
</view>
</view>

View File

@@ -273,6 +273,23 @@
font-weight: 600;
}
.purchase-section .btn-price-row {
display: flex;
align-items: center;
gap: 8rpx;
}
.btn-original-price {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.55);
text-decoration: line-through;
}
.btn-discount-tag {
font-size: 22rpx;
color: #00CED1;
}
.brand-color {
color: #00CED1;
}

View File

@@ -32,7 +32,8 @@ Page({
pendingEarnings: 0, // 待结算收益(保留兼容)
shareRate: 90, // 分成比例90%),从 referral/data 或 config 获取
minWithdrawAmount: 10, // 最低提现金额,从 referral/data 获取
bindingDays: 30, // 绑定期天数,从 referral/data 获取
withdrawFee: 5, // 提现手续费%,从 referral/data 获取
bindingDays: 30, // 绑定期天数,从 referral/data 获取
userDiscount: 5, // 好友购买优惠%,从 referral/data 获取
hasWechatId: false, // 是否已绑定微信号(未绑定时需引导去设置)
@@ -199,6 +200,7 @@ Page({
pendingEarnings: formatMoney(realData?.pendingEarnings || 0),
shareRate: realData?.shareRate ?? 90,
minWithdrawAmount: minWithdrawAmount,
withdrawFee: realData?.withdrawFee ?? 5,
bindingDays: realData?.bindingDays ?? 30,
userDiscount: realData?.userDiscount ?? 5,
@@ -565,9 +567,16 @@ Page({
return
}
const withdrawFee = this.data.withdrawFee ?? 5
const actualAmount = withdrawFee > 0
? Math.round(availableEarnings * (1 - withdrawFee / 100) * 100) / 100
: availableEarnings
const feeText = withdrawFee > 0
? `\n扣除 ${withdrawFee}% 手续费后,实际到账 ¥${actualAmount.toFixed(2)}`
: ''
wx.showModal({
title: '确认提现',
content: `提现 ¥${availableEarnings.toFixed(2)} 到您的微信零钱`,
content: `申请提现 ¥${availableEarnings.toFixed(2)} 到您的微信零钱${feeText}`,
confirmText: '立即提现',
success: async (res) => {
if (!res.confirm) return

View File

@@ -49,6 +49,7 @@
<view class="withdraw-btn {{availableEarningsNum < minWithdrawAmount || !hasWechatId ? 'btn-disabled' : ''}}" bindtap="handleWithdraw">
{{availableEarningsNum < minWithdrawAmount ? '满' + minWithdrawAmount + '元可提现' : !hasWechatId ? '请先绑定微信号' : '申请提现 ¥' + availableEarnings}}
</view>
<text class="withdraw-fee-tip" wx:if="{{withdrawFee > 0}}">提现将扣除 {{withdrawFee}}% 手续费</text>
<text class="wechat-tip" wx:if="{{availableEarningsNum > 0 && !hasWechatId}}">为便于提现到账,请先到「设置」中绑定微信号</text>
<view class="withdraw-records-link" bindtap="goToWithdrawRecords">查看提现记录</view>
</view>

View File

@@ -37,6 +37,7 @@
.withdraw-btn { padding: 28rpx; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); color: #fff; font-size: 32rpx; font-weight: 600; text-align: center; border-radius: 24rpx; box-shadow: 0 8rpx 24rpx rgba(0,206,209,0.3); }
.withdraw-btn.btn-disabled { background: rgba(0,206,209,0.2); color: rgba(255,255,255,0.3); box-shadow: none; }
.withdraw-fee-tip { display: block; font-size: 24rpx; color: rgba(255,255,255,0.5); margin-top: 12rpx; text-align: center; }
.wechat-tip { display: block; font-size: 24rpx; color: rgba(255,165,0,0.9); margin-top: 16rpx; text-align: center; }
.withdraw-records-link { display: block; margin-top: 16rpx; text-align: center; font-size: 26rpx; color: #00CED1; }

View File

@@ -23,12 +23,33 @@
"condition": {
"miniprogram": {
"list": [
{
"name": "pages/referral/referral",
"pathName": "pages/referral/referral",
"query": "",
"scene": null,
"launchMode": "default"
},
{
"name": "pages/read/read",
"pathName": "pages/read/read",
"query": "mid=11",
"launchMode": "default",
"scene": null
},
{
"name": "pages/referral/referral",
"pathName": "pages/referral/referral",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "ces",
"pathName": "pages/read/read",
"query": "scene=mid%3D3&ref%3DogpTW5a9ex",
"scene": null,
"launchMode": "default"
"launchMode": "default",
"scene": null
},
{
"name": "pages/chapters/chapters",

View File

@@ -26,17 +26,18 @@ class ChapterAccessManager {
if (res.success && res.freeChapters) {
return {
freeChapters: res.freeChapters,
prices: res.prices || { section: 1, fullbook: 9.9 }
prices: res.prices || { section: 1, fullbook: 9.9 },
userDiscount: (typeof res.userDiscount === 'number' ? res.userDiscount : 5)
}
}
} catch (e) {
console.warn('[AccessManager] 获取配置失败,使用默认配置:', e)
}
// 默认配置
return {
freeChapters: ['preface', 'epilogue', '1.1', 'appendix-1', 'appendix-2', 'appendix-3'],
prices: { section: 1, fullbook: 9.9 }
prices: { section: 1, fullbook: 9.9 },
userDiscount: 5
}
}