更新管理端迁移Mycontent-temp的菜单与布局规范,确保主导航收敛并优化隐藏页面入口。新增相关会议记录与文档,反映团队讨论的最新决策与实施建议。
This commit is contained in:
@@ -77,6 +77,16 @@ Page({
|
||||
|
||||
async onLoad(options) {
|
||||
wx.showShareMenu({ withShareTimeline: true })
|
||||
|
||||
// 预加载 linkTags 配置(供 onLinkTagTap 旧格式降级匹配 type 用)
|
||||
if (!app.globalData.linkTagsConfig) {
|
||||
app.request({ url: '/api/miniprogram/config', silent: true }).then(cfg => {
|
||||
if (cfg && Array.isArray(cfg.linkTags)) {
|
||||
app.globalData.linkTagsConfig = cfg.linkTags
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// 支持 scene(扫码)、mid、id、ref
|
||||
const sceneStr = (options && options.scene) || ''
|
||||
const parsed = parseScene(sceneStr)
|
||||
@@ -196,9 +206,9 @@ Page({
|
||||
})
|
||||
},
|
||||
|
||||
// 【重构】加载章节内容(专注于内容加载,权限判断已在 onLoad 中由 accessManager 完成)
|
||||
// prefetchedChapter:若已有章节数据(含 content)则复用,避免二次请求
|
||||
// 加载章节内容:优先复用 prefetchedChapter 避免二次请求,失败时降级本地缓存
|
||||
async loadContent(id, accessState, prefetchedChapter) {
|
||||
const cacheKey = `chapter_${id}`
|
||||
try {
|
||||
const sectionPrice = this.data.sectionPrice ?? 1
|
||||
let res = prefetchedChapter
|
||||
@@ -212,7 +222,7 @@ Page({
|
||||
price: res.price ?? sectionPrice
|
||||
}
|
||||
this.setData({ section })
|
||||
|
||||
|
||||
if (res && res.content) {
|
||||
const { lines, segments } = contentParser.parseContent(res.content)
|
||||
const previewCount = Math.ceil(lines.length * 0.2)
|
||||
@@ -226,29 +236,29 @@ Page({
|
||||
}
|
||||
if (res.mid) updates.sectionMid = res.mid
|
||||
this.setData(updates)
|
||||
|
||||
// 如果有权限,标记为已读
|
||||
// 写入本地缓存,供离线/重试降级使用
|
||||
try { wx.setStorageSync(cacheKey, res) } catch (_) {}
|
||||
if (accessManager.canAccessFullContent(accessState)) {
|
||||
app.markSectionAsRead(id)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[Read] 加载内容失败:', e)
|
||||
// 尝试从本地缓存加载
|
||||
const cacheKey = `chapter_${id}`
|
||||
console.error('[Read] 加载内容失败,尝试本地缓存:', e)
|
||||
try {
|
||||
const cached = wx.getStorageSync(cacheKey)
|
||||
if (cached && cached.content) {
|
||||
const { lines, segments } = contentParser.parseContent(cached.content)
|
||||
const previewCount = Math.ceil(lines.length * 0.2)
|
||||
|
||||
this.setData({
|
||||
content: cached.content,
|
||||
contentParagraphs: lines,
|
||||
contentSegments: segments,
|
||||
previewParagraphs: lines.slice(0, previewCount)
|
||||
previewParagraphs: lines.slice(0, previewCount),
|
||||
partTitle: cached.partTitle || '',
|
||||
chapterTitle: cached.chapterTitle || ''
|
||||
})
|
||||
console.log('[Read] 从本地缓存加载成功')
|
||||
return
|
||||
}
|
||||
} catch (cacheErr) {
|
||||
console.warn('[Read] 本地缓存也失败:', cacheErr)
|
||||
@@ -315,48 +325,6 @@ Page({
|
||||
return `/api/miniprogram/book/chapter/${finalId}`
|
||||
},
|
||||
|
||||
// 加载内容 - 三级降级方案:API → 本地缓存 → 备用API
|
||||
async loadContent(id) {
|
||||
const cacheKey = `chapter_${id}`
|
||||
|
||||
// 1. 优先从API获取
|
||||
try {
|
||||
const res = await this.fetchChapterWithTimeout(id, 5000)
|
||||
if (res && res.content) {
|
||||
this.setChapterContent(res)
|
||||
// 成功后缓存到本地
|
||||
wx.setStorageSync(cacheKey, res)
|
||||
console.log('[Read] 从API加载成功:', id)
|
||||
return
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[Read] API加载失败,尝试本地缓存:', e.message)
|
||||
}
|
||||
|
||||
// 2. API失败,尝试从本地缓存读取
|
||||
try {
|
||||
const cached = wx.getStorageSync(cacheKey)
|
||||
if (cached && cached.content) {
|
||||
this.setChapterContent(cached)
|
||||
console.log('[Read] 从本地缓存加载成功:', id)
|
||||
// 后台静默刷新
|
||||
this.silentRefresh(id)
|
||||
return
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[Read] 本地缓存读取失败')
|
||||
}
|
||||
|
||||
// 3. 都失败,显示加载中并持续重试
|
||||
this.setData({
|
||||
contentParagraphs: ['章节内容加载中...', '正在尝试连接服务器,请稍候...'],
|
||||
contentSegments: contentParser.parseContent('章节内容加载中...\n正在尝试连接服务器,请稍候...').segments,
|
||||
previewParagraphs: ['章节内容加载中...']
|
||||
})
|
||||
|
||||
// 延迟重试(最多3次)
|
||||
this.retryLoadContent(id, 3)
|
||||
},
|
||||
|
||||
// 带超时的章节请求
|
||||
fetchChapterWithTimeout(id, timeout = 5000) {
|
||||
@@ -476,6 +444,69 @@ Page({
|
||||
getApp().goBackOrToHome()
|
||||
},
|
||||
|
||||
// 点击正文中的 #链接标签:外链复制到剪贴板,小程序内页直接跳转
|
||||
onLinkTagTap(e) {
|
||||
let url = (e.currentTarget.dataset.url || '').trim()
|
||||
const label = (e.currentTarget.dataset.label || '').trim()
|
||||
let tagType = (e.currentTarget.dataset.tagType || '').trim()
|
||||
let pagePath = (e.currentTarget.dataset.pagePath || '').trim()
|
||||
|
||||
// 旧格式(<a href>)tagType 为空 → 按 label 从缓存 linkTags 补充类型信息
|
||||
if (!tagType && label) {
|
||||
const cached = (app.globalData.linkTagsConfig || []).find(t => t.label === label)
|
||||
if (cached) {
|
||||
tagType = cached.type || 'url'
|
||||
pagePath = cached.pagePath || ''
|
||||
if (!url) url = cached.url || ''
|
||||
}
|
||||
}
|
||||
|
||||
// CKB 类型:复用 @mention 加好友流程,弹出留资表单
|
||||
if (tagType === 'ckb') {
|
||||
// 触发通用加好友(无特定 personId,使用全局 CKB Key)
|
||||
this.onMentionTap({ currentTarget: { dataset: { userId: '', nickname: label } } })
|
||||
return
|
||||
}
|
||||
|
||||
// 小程序内部路径(pagePath 或 url 以 /pages/ 开头)
|
||||
const internalPath = pagePath || (url.startsWith('/pages/') ? url : '')
|
||||
if (internalPath) {
|
||||
wx.navigateTo({ url: internalPath, fail: () => wx.switchTab({ url: internalPath }) })
|
||||
return
|
||||
}
|
||||
|
||||
// 外部 URL:优先用 wx.openLink 在浏览器打开,旧版微信降级复制
|
||||
if (url) {
|
||||
if (typeof wx.openLink === 'function') {
|
||||
wx.openLink({
|
||||
url,
|
||||
fail: () => {
|
||||
// openLink 不支持(如不在微信内),降级复制
|
||||
wx.setClipboardData({
|
||||
data: url,
|
||||
success: () => wx.showToast({ title: `链接已复制`, icon: 'none', duration: 2000 })
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
wx.setClipboardData({
|
||||
data: url,
|
||||
success: () => wx.showToast({ title: `链接已复制`, icon: 'none', duration: 2000 })
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
wx.showToast({ title: '暂无跳转地址', icon: 'none' })
|
||||
},
|
||||
|
||||
// 点击正文图片 → 全屏预览
|
||||
onImageTap(e) {
|
||||
const src = e.currentTarget.dataset.src
|
||||
if (!src) return
|
||||
wx.previewImage({ current: src, urls: [src] })
|
||||
},
|
||||
|
||||
// 点击正文中的 @某人:确认弹窗 → 登录/资料校验 → 调用 ckb/lead 加好友留资
|
||||
onMentionTap(e) {
|
||||
const userId = e.currentTarget.dataset.userId
|
||||
|
||||
@@ -42,12 +42,14 @@
|
||||
<view class="skeleton skeleton-5"></view>
|
||||
</view>
|
||||
|
||||
<!-- 完整内容 - 免费或已购买(支持 @ 高亮可点) -->
|
||||
<!-- 完整内容 - 免费或已购买(支持 @ mention / #linkTag / 图片) -->
|
||||
<view class="article" wx:if="{{accessState === 'free' || accessState === 'unlocked_purchased'}}">
|
||||
<view class="paragraph" wx:for="{{contentSegments}}" wx:key="index">
|
||||
<block wx:for="{{item}}" wx:key="index" wx:for-item="seg">
|
||||
<text wx:if="{{seg.type === 'text'}}">{{seg.text}}</text>
|
||||
<text wx:elif="{{seg.type === 'mention'}}" class="mention" bindtap="onMentionTap" data-user-id="{{seg.userId}}" data-nickname="{{seg.nickname}}">@{{seg.nickname}}</text>
|
||||
<text wx:elif="{{seg.type === 'linkTag'}}" class="link-tag" bindtap="onLinkTagTap" data-url="{{seg.url}}" data-label="{{seg.label}}" data-tag-type="{{seg.tagType}}" data-page-path="{{seg.pagePath}}" data-tag-id="{{seg.tagId}}">#{{seg.label}}</text>
|
||||
<image wx:elif="{{seg.type === 'image'}}" class="content-image" src="{{seg.src}}" mode="widthFix" show-menu-by-longpress bindtap="onImageTap" data-src="{{seg.src}}"></image>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
|
||||
@@ -189,6 +189,21 @@
|
||||
padding: 0 4rpx;
|
||||
}
|
||||
|
||||
/* 正文内 #链接标签 高亮可点 */
|
||||
.paragraph .link-tag {
|
||||
color: #FFD700;
|
||||
font-weight: 500;
|
||||
padding: 0 4rpx;
|
||||
}
|
||||
|
||||
/* 正文内图片 */
|
||||
.content-image {
|
||||
width: 100%;
|
||||
border-radius: 8rpx;
|
||||
margin: 16rpx 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.preview {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user