feat: enhance link handling in reading page

- Added functionality to open article links in a mini program web-view, allowing users to preview external links.
- Implemented mobile hints for external links to improve user experience on mobile devices.
- Updated content parser to support new link types, including regular hyperlinks.
- Enhanced styling for article links to improve visibility and interaction.

Made-with: Cursor
This commit is contained in:
乘风
2026-04-24 16:17:07 +08:00
parent 51d38cff0f
commit 73d56a225a
6 changed files with 95 additions and 14 deletions

View File

@@ -6,6 +6,7 @@
* { type: 'text', text }
* { type: 'mention', userId, nickname } — @某人,点击加好友(提交存客宝见 utils/soulBridge.submitCkbLead
* { type: 'linkTag', label, url, ... } — #链接标签,点击跳转(阅读页 onLinkTagTap外链→link-preview、小程序→navigateToMiniProgram
* { type: 'link', text, url } — 普通超链接,点击外链预览(阅读页 onArticleLinkTap
* { type: 'image', src, alt } — 图片
* { type: 'video', src } — 内嵌视频(管理端 rich-video-wrap / <video>
*/
@@ -100,7 +101,7 @@ function stripTrailingAtForMention(before) {
/**
* 将一个 HTML block 字符串解析为 segments 数组
* 处理内联元素mention / linkTag(span) / linkTag(a) / img / video
* 处理内联元素mention / linkTag(span) / linkTag(a) / link(a) / img / video
*/
function parseBlockToSegments(block, config) {
const segs = []
@@ -111,7 +112,7 @@ function parseBlockToSegments(block, config) {
if (token) personTokenSet.add(token)
}
// 合并匹配所有内联元素
const tokenRe = /<span[^>]*data-type="mention"[^>]*>[\s\S]*?<\/span>|<span[^>]*data-type="linkTag"[^>]*>[\s\S]*?<\/span>|<a[^>]*href="([^"]*)"[^>]*>(#[^<]*)<\/a>|<video[^>]*>[\s\S]*?<\/video>|<video[^>]*\/>|<img[^>]*\/?>/gi
const tokenRe = /<span[^>]*data-type="mention"[^>]*>[\s\S]*?<\/span>|<span[^>]*data-type="linkTag"[^>]*>[\s\S]*?<\/span>|<a[^>]*href=(?:"[^"]*"|'[^']*')[^>]*>[\s\S]*?<\/a>|<video[^>]*>[\s\S]*?<\/video>|<video[^>]*\/>|<img[^>]*\/?>/gi
let lastEnd = 0
let m
@@ -162,12 +163,21 @@ function parseBlockToSegments(block, config) {
segs.push({ type: 'linkTag', label: innerText || '#', url, tagType, pagePath, tagId, appId, mpKey, passPhone, phoneParamName })
} else if (/^<a /i.test(tag)) {
// #linkTag — 旧格式 <a href>insertLinkTag 旧版产生url 可能为空)
// m[1] = href, m[2] = innerText以 # 开头)
const url = m[1] || ''
const label = cleanSingleLineField((m[2] || '').replace(/^#/, ''))
// 旧格式没有 tagType在 onLinkTagTap 中会按 label 匹配缓存的 linkTags 配置降级处理
segs.push({ type: 'linkTag', label: label || '#', url, tagType: '', pagePath: '', tagId: '' })
// <a href>:兼容旧 #linkTag 与普通超链接
const hrefMatch =
tag.match(/href\s*=\s*"([^"]*)"/i) ||
tag.match(/href\s*=\s*'([^']*)'/i)
const url = hrefMatch ? decodeEntities((hrefMatch[1] || '').trim()) : ''
const anchorText = decodeEntities((tag.replace(/<[^>]+>/g, '') || '').trim())
// 旧格式:#标签仍走 linkTag 逻辑,复用现有 onLinkTagTap
if (/^[#]/u.test(anchorText)) {
const label = cleanSingleLineField(anchorText.replace(/^[#]/u, ''))
segs.push({ type: 'linkTag', label: label || '#', url, tagType: '', pagePath: '', tagId: '' })
} else {
// 普通链接:展示锚文本;无锚文本则回退展示 url
segs.push({ type: 'link', text: anchorText || url, url })
}
} else if (/^<video/i.test(tag)) {
const rawSrc = extractVideoSrcFromTag(tag)