/** * 文本工具:HTML 剥离为纯文本 + 摘要截断 * 跨端实现(小程序无 DOM),仅用正则处理,不依赖 DOMParser */ /** HTML 实体解码(覆盖常见实体即可) */ const HTML_ENTITY_MAP: Record = { ' ': ' ', '<': '<', '>': '>', '"': '"', ''': '\'', '&': '&', } /** 行首 markdown 标记(# 标题 / * - 列表 / 数字序号 / 引用 > / 代码块 ``` 等) */ const MD_PREFIX_REG = /^\s{0,3}(#{1,6}[ \t]|>|[+*-][ \t]|\d+[.、)][ \t]|```|~~~|!?\[)/gm /** * 将 HTML/Markdown 源文本剥离为压缩空白后的纯文本 * @param source 原文(可为空) */ export function htmlToPlainText(source?: string): string { if (!source) return '' let text = source // 剥离脚本/样式块 text = text.replace(/<(script|style)[\s\S]*?<\/\1>/gi, ' ') // 块级/换行标签替换为空格,其余标签整体剥离 text = text .replace(/<\/(p|div|br|li|h[1-6]|blockquote|pre|tr|section|article)>/gi, ' ') .replace(/<[^>]+>/g, ' ') // markdown 行首符号清理 text = text.replace(MD_PREFIX_REG, '') // 实体解码 text = text.replace(/&[a-z]+;|&#\d+;/gi, match => HTML_ENTITY_MAP[match.toLowerCase()] ?? ' ') // 压缩空白(含换行) return text.replace(/\s+/g, ' ').trim() } /** 截断文本,超长追加省略号 */ export function truncateText(text: string, max: number, ellipsis = '…'): string { if (!text || text.length <= max) return text return `${text.slice(0, max).trimEnd()}${ellipsis}` } /** 从 HTML/Markdown 源提取纯文本摘要(去标签 + 截断) */ export function extractPlainExcerpt(source?: string, max = 120): string { return truncateText(htmlToPlainText(source), max) }