This commit is contained in:
Alex-larget
2026-03-26 21:29:18 +08:00
parent 7bf301a9c8
commit 159ce035f2
5 changed files with 109 additions and 18 deletions

View File

@@ -136,6 +136,8 @@ Page({
readingProgress: 0,
/** 未解锁付费墙:合并 data.previewPercent章节与顶层 previewPercent全局 */
previewPercent: 20,
/** 未解锁时:按 previewPercent 裁切可见高度px。0 表示未启用/待测量 */
previewMaxHeightPx: 0,
/** mpUi.readPage.beforeLoginHint未登录付费墙上方说明文案 */
readBeforeLoginHint: '',
/** 朋友圈单页标题与说明mpUi.readPage.singlePageTitle / singlePagePaywallHint */
@@ -185,6 +187,53 @@ Page({
momentsPaywallExpanded: false,
},
_isLockedState(state) {
return state === 'locked_not_login' || state === 'locked_not_purchased'
},
/**
* 未解锁时:先渲染完整正文,再测量其高度,按 previewPercent 计算可见高度并裁切。
* 这样后端无需截断,避免 HTML/段落被截断导致的渲染失败。
*/
_updatePreviewClipHeight() {
const state = this.data.accessState
if (!this._isLockedState(state)) {
if (this.data.previewMaxHeightPx !== 0) this.setData({ previewMaxHeightPx: 0 })
return
}
const percent = Number(this.data.previewPercent) || 20
// 先关闭裁切,确保测到“完整高度”
if (this.data.previewMaxHeightPx !== 0) {
this.setData({ previewMaxHeightPx: 0 })
}
const measure = () =>
new Promise((resolve) => {
wx.createSelectorQuery()
.in(this)
.select('#previewContentMeasure')
.boundingClientRect((rect) => resolve(rect || null))
.exec()
})
const apply = async () => {
// nextTick 让 setData 的 DOM 更新生效
await new Promise((r) => (typeof wx.nextTick === 'function' ? wx.nextTick(r) : setTimeout(r, 0)))
const rect = await measure()
const fullH = rect && rect.height ? rect.height : 0
if (!fullH || fullH < 20) return
const clipped = Math.max(120, Math.floor((fullH * Math.min(Math.max(percent, 1), 100)) / 100))
// 只在仍处于锁定态时应用,避免切换状态后误裁切
if (this._isLockedState(this.data.accessState)) {
this.setData({ previewMaxHeightPx: clipped })
}
}
apply().catch(() => {})
},
/**
* 是否处于朋友圈等「单页预览」环境。
* 兼容:部分机型/基础库首帧 getSystemInfoSync().mode 未就绪,需结合 launch/enter scene 1154、getWindowInfo。
@@ -480,11 +529,12 @@ Page({
}
this.setData({ section })
// 已解锁用 data.content完整内容未解锁用 content预览先 determineAccessState 再 loadContent 保证顺序正确
const displayContent = accessManager.canAccessFullContent(accessState) ? (res.data?.content ?? res.content) : res.content
// 规则更新:小程序端始终使用“完整正文”渲染;未解锁时通过前端裁切可见比例实现预览
// 兼容老返回:完整正文可能在 res.data.content老链路只有 res.content
const displayContent = (res.data?.content ?? res.content)
if (res && displayContent) {
const { lines, segments } = contentParser.parseContent(displayContent, parseCfg)
// 预览内容由后端统一截取比例,这里展示全部预览内容
// 预览比例由前端按高度裁切,这里渲染完整内容(避免后端截断导致渲染失败)
const previewCount = lines.length
const updates = {
content: displayContent,
@@ -496,7 +546,9 @@ Page({
previewPercent: normalizePreviewPercent(res),
}
if (res.mid) updates.sectionMid = res.mid
this.setData(updates)
// 先关闭裁切,等 DOM 更新后再测量并按比例裁切
this.setData({ ...updates, previewMaxHeightPx: 0 })
this._updatePreviewClipHeight()
// 写入本地缓存(存 displayContent供离线/重试降级使用)
try { wx.setStorageSync(cacheKey, { ...res, content: displayContent }) } catch (_) {}
if (accessManager.canAccessFullContent(accessState)) {
@@ -511,7 +563,7 @@ Page({
if (cached && cached.content) {
await app.getReadExtras()
const { lines, segments } = contentParser.parseContent(cached.content, getContentParseConfig())
// 预览内容由后端统一截取比例,这里展示全部预览内容
// 预览比例由前端按高度裁切,这里渲染完整内容
const previewCount = lines.length
this.setData({
content: cached.content,
@@ -521,7 +573,9 @@ Page({
partTitle: cached.partTitle || '',
chapterTitle: cached.chapterTitle || '',
previewPercent: normalizePreviewPercent(cached),
previewMaxHeightPx: 0,
})
this._updatePreviewClipHeight()
app.touchRecentSection(id)
console.log('[Read] 从本地缓存加载成功')
return