refactor: update API base URL and enhance read page functionality
- Changed API base URL from localhost:8080 to https://soulapi.quwanzhi.com for production. - Added new read page configuration for "阅读详情" with specific query parameters. - Implemented a fallback mechanism for read page UI to handle cases where cloud data is unavailable. - Updated read page content and clipboard text formatting for better user experience. - Removed deprecated gift sharing UI elements to streamline the interface.
This commit is contained in:
@@ -28,6 +28,7 @@ const mpPagePopups = require('../../utils/mpPagePopups.js')
|
||||
const READ_UI_DEFAULTS = {
|
||||
singlePageUnlockTitle: '解锁完整内容',
|
||||
singlePagePayButtonText: '支付 ¥{price} 解锁全文',
|
||||
/** 单页展开区首行:付款引导;其下为分隔线 + shareTipLine + fullPaywallTip */
|
||||
singlePageExpandedHint: '预览页不能直接付款,务必先点底栏「前往小程序」。',
|
||||
payTapModalTitle: '解锁说明',
|
||||
payTapModalContent:
|
||||
@@ -613,7 +614,8 @@ Page({
|
||||
const parseCfg = getContentParseConfig()
|
||||
const sectionPrice = this.data.sectionPrice ?? 1
|
||||
let res = prefetchedChapter
|
||||
if (!res || !res.content) {
|
||||
const hasChapterBody = (r) => r && String(r.data?.content ?? r.content ?? '').length > 0
|
||||
if (!res || !hasChapterBody(res)) {
|
||||
res = await app.request({ url: this._getChapterUrl({ id }), silent: true })
|
||||
}
|
||||
this._applyChapterMetaFromResponse(res)
|
||||
@@ -656,14 +658,15 @@ Page({
|
||||
console.error('[Read] 加载内容失败,尝试本地缓存:', e)
|
||||
try {
|
||||
const cached = wx.getStorageSync(cacheKey)
|
||||
if (cached && cached.content) {
|
||||
const cachedDisplay = cached && (cached.data?.content ?? cached.content)
|
||||
if (cached && String(cachedDisplay || '').length) {
|
||||
await app.getReadExtras()
|
||||
this._applyChapterMetaFromResponse(cached)
|
||||
const { lines, segments } = contentParser.parseContent(cached.content, getContentParseConfig())
|
||||
const { lines, segments } = contentParser.parseContent(cachedDisplay, getContentParseConfig())
|
||||
// 预览比例由前端按高度裁切,这里渲染完整内容
|
||||
const previewCount = lines.length
|
||||
this.setData({
|
||||
content: cached.content,
|
||||
content: cachedDisplay,
|
||||
contentParagraphs: lines,
|
||||
contentSegments: normalizeMentionSegments(segments),
|
||||
previewParagraphs: lines.slice(0, previewCount),
|
||||
|
||||
@@ -149,7 +149,12 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="paywall-audit-tip" wx:if="{{auditMode}}">审核中,暂不支持购买</view>
|
||||
<text class="paywall-desc paywall-desc--moments-expanded" wx:if="{{momentsPaywallExpanded}}">{{readUi.singlePageExpandedHint || '预览不可付款,请点底部「前往小程序」。'}}</text>
|
||||
<view class="paywall-expanded-stack" wx:if="{{momentsPaywallExpanded}}">
|
||||
<text class="paywall-expanded-lead">{{readUi.singlePageExpandedHint || '预览页不能直接付款,务必先点底栏「前往小程序」。'}}</text>
|
||||
<view class="paywall-expanded-divider"></view>
|
||||
<text class="paywall-expanded-line">{{readUi.shareTipLine || '好友经你分享购买,你可获得约 90% 收益'}}</text>
|
||||
<text class="paywall-expanded-line">{{readUi.fullPaywallTip || '转发给需要的人,一起学习还能赚佣金'}}</text>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:else>
|
||||
@@ -236,7 +241,12 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="paywall-audit-tip" wx:if="{{auditMode}}">审核中,暂不支持购买</view>
|
||||
<text class="paywall-desc paywall-desc--moments-expanded" wx:if="{{momentsPaywallExpanded}}">{{readUi.singlePageExpandedHint || '预览不可付款,请点底部「前往小程序」。'}}</text>
|
||||
<view class="paywall-expanded-stack" wx:if="{{momentsPaywallExpanded}}">
|
||||
<text class="paywall-expanded-lead">{{readUi.singlePageExpandedHint || '预览页不能直接付款,务必先点底栏「前往小程序」。'}}</text>
|
||||
<view class="paywall-expanded-divider"></view>
|
||||
<text class="paywall-expanded-line">{{readUi.shareTipLine || '好友经你分享购买,你可获得约 90% 收益'}}</text>
|
||||
<text class="paywall-expanded-line">{{readUi.fullPaywallTip || '转发给需要的人,一起学习还能赚佣金'}}</text>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:else>
|
||||
|
||||
@@ -299,6 +299,13 @@
|
||||
}
|
||||
.paywall--single-preview .paywall-title {
|
||||
margin-bottom: 20rpx;
|
||||
font-size: 34rpx;
|
||||
}
|
||||
.paywall--single-preview .paywall-desc {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.paywall--single-preview .purchase-section .btn-label {
|
||||
font-size: 26rpx;
|
||||
}
|
||||
/* 朋友圈单页图二:首段说明 + 与购买行间距收紧 */
|
||||
.paywall-sp-lead {
|
||||
@@ -328,12 +335,35 @@
|
||||
text-align: center;
|
||||
color: rgba(255, 255, 255, 0.58);
|
||||
}
|
||||
.paywall-desc--moments-expanded {
|
||||
margin-top: 28rpx !important;
|
||||
margin-bottom: 0 !important;
|
||||
font-size: 26rpx !important;
|
||||
line-height: 1.45;
|
||||
/* 单页:点「解锁说明」后展开 — 首行引导 / 分隔线 / 两行分润说明(与运营稿一致) */
|
||||
.paywall-expanded-stack {
|
||||
margin-top: 28rpx;
|
||||
padding: 0 8rpx;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.paywall-expanded-lead {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.45;
|
||||
color: rgba(255, 255, 255, 0.55);
|
||||
text-align: center;
|
||||
}
|
||||
.paywall-expanded-divider {
|
||||
height: 0;
|
||||
border-top: 1rpx solid rgba(255, 255, 255, 0.08);
|
||||
margin: 20rpx 0;
|
||||
}
|
||||
.paywall-expanded-line {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.45;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
text-align: center;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
.paywall-expanded-line:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* 朋友圈单页:未点解锁前的一行轻提示 */
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"pathName": "pages/read/read",
|
||||
"query": "mid=233",
|
||||
"scene": null,
|
||||
"launchMode": "default"
|
||||
"launchMode": "singlePage"
|
||||
},
|
||||
{
|
||||
"name": "个人资料",
|
||||
|
||||
@@ -644,7 +644,8 @@ func getUnpaidPreviewPercent(db *gorm.DB) int {
|
||||
|
||||
// findChapterAndRespond 按条件查章节并返回统一格式
|
||||
// 免费判断优先级:system_config.free_chapters / chapter_config.freeChapters > chapters.is_free/price
|
||||
// 付费章节:若请求携带 userId 且有购买权限则返回完整 content,否则返回 previewContent
|
||||
// data.content 始终返回完整 HTML;未解锁时的「试读比例」由小程序对渲染后高度做 overflow 裁切(见 miniprogram read _updatePreviewClipHeight)。
|
||||
// 纯文本截取 previewContent 仅用于 H5 落地页等,不再用于小程序读章节响应。
|
||||
// content 缓存:优先 Redis,命中时仅查元数据(不含 LONGTEXT),未命中时查全量并回填缓存
|
||||
func findChapterAndRespond(c *gin.Context, whereFn func(*gorm.DB) *gorm.DB) {
|
||||
var ch model.Chapter
|
||||
@@ -679,27 +680,18 @@ func findChapterAndRespond(c *gin.Context, whereFn func(*gorm.DB) *gorm.DB) {
|
||||
isFree = true
|
||||
}
|
||||
|
||||
// 确定返回的 content:免费直接返回,付费须校验购买权限
|
||||
userID := c.Query("userId")
|
||||
isPremium := ch.EditionPremium != nil && *ch.EditionPremium
|
||||
hasFullAccess := isFree || checkUserChapterAccess(db, userID, ch.ID, isPremium)
|
||||
var returnContent string
|
||||
// 未解锁:正文截取见 chapter_preview.go(HTML 剥标签后与 H5 一致)
|
||||
if hasFullAccess {
|
||||
returnContent = ch.Content
|
||||
} else {
|
||||
percent := chapterPreviewPercent(db, &ch)
|
||||
returnContent = previewContent(ch.Content, percent)
|
||||
}
|
||||
effectivePct := chapterPreviewPercent(db, &ch)
|
||||
returnContent := ch.Content
|
||||
|
||||
// data 中的 content 必须与外层 content 一致,避免泄露完整内容给未授权用户
|
||||
chForResponse := ch
|
||||
chForResponse.Content = returnContent
|
||||
chForResponse.PartTitle = sanitizeChapterTitleField(ch.PartTitle)
|
||||
chForResponse.ChapterTitle = sanitizeChapterTitleField(ch.ChapterTitle)
|
||||
chForResponse.SectionTitle = sanitizeChapterTitleField(ch.SectionTitle)
|
||||
|
||||
effectivePct := chapterPreviewPercent(db, &ch)
|
||||
out := gin.H{
|
||||
"success": true,
|
||||
"data": chForResponse,
|
||||
@@ -715,7 +707,7 @@ func findChapterAndRespond(c *gin.Context, whereFn func(*gorm.DB) *gorm.DB) {
|
||||
"readPreviewUi": mergeReadPreviewUI(db),
|
||||
}
|
||||
if !hasFullAccess {
|
||||
out["previewPercent"] = getUnpaidPreviewPercent(db)
|
||||
out["previewPercent"] = effectivePct
|
||||
}
|
||||
// 文章详情内直接输出上一篇/下一篇,省去单独请求
|
||||
if list := getOrderedChapterList(); len(list) > 0 {
|
||||
|
||||
@@ -44,7 +44,7 @@ func chapterPreviewPercent(db *gorm.DB, ch *model.Chapter) int {
|
||||
return g
|
||||
}
|
||||
|
||||
// previewContent 取内容的前 percent%(按纯文本 rune 计;原 HTML 先剥标签),与 h5 落地页一致
|
||||
// previewContent 取内容的前 percent%(按纯文本 rune 计;原 HTML 先剥标签)——用于 H5 落地页等;小程序读章节接口应返回全文并由端上按渲染高度裁切。
|
||||
func previewContent(content string, percent int) string {
|
||||
work := content
|
||||
if strings.Contains(content, "<") && strings.Contains(content, ">") {
|
||||
|
||||
Reference in New Issue
Block a user