|
- /**
- * 后台富文本清洗(协议、公告等静态内容)
- *
- * 背景:html 表里的正文是 Word / 旧编辑器导出的,直接渲染有两类问题:
- * 1. 大量 `<p><span> </span></p>` 空段落,叠加段间距后出现大片空白;
- * 2. 几乎每个 span 都带内联 `font-family:宋体;font-size:14px;color:#000`,
- * 内联样式优先级最高,会盖掉页面样式,暗色模式下黑字配深底完全不可读。
- *
- * 处理方式:删掉空段落,剥掉会影响排版与主题的内联属性,保留加粗、下划线等语义样式。
- * 与 ai_uniapp_v2/utils/richtext.js 保持同一套规则(两个仓库独立,无法共享模块)。
- */
-
- const DROP_STYLE_PROPS = [
- 'font-family',
- 'font-size',
- 'color',
- 'background',
- 'background-color',
- 'line-height',
- 'margin',
- 'margin-top',
- 'margin-bottom',
- 'margin-left',
- 'margin-right',
- 'padding',
- 'padding-top',
- 'padding-bottom',
- 'width',
- 'height',
- ]
-
- /** 判断一段 html 是否只有空白内容(空格、 、<br>、空标签) */
- function isBlankFragment(html) {
- const text = String(html || '')
- .replace(/<br\s*\/?>/gi, '')
- .replace(/<[^>]+>/g, '')
- .replace(/ | |\u00a0/gi, '')
- .replace(/\s/g, '')
- return text.length === 0
- }
-
- function cleanStyleValue(style) {
- return String(style || '')
- .split(';')
- .map(item => item.trim())
- .filter(Boolean)
- .filter((item) => {
- const prop = item.split(':')[0].trim().toLowerCase()
- return prop && !DROP_STYLE_PROPS.includes(prop)
- })
- .join('; ')
- }
-
- /**
- * @param {string} html 后台返回的富文本
- * @return {string} 清洗后的富文本
- */
- export function cleanRichText(html) {
- if (!html) return ''
- let text = String(html)
-
- // Word 导出的无用标签与内嵌样式表
- text = text
- .replace(/<\/?o:p[^>]*>/gi, '')
- .replace(/<!--[\s\S]*?-->/g, '')
- .replace(/<meta[^>]*>/gi, '')
- .replace(/<style[\s\S]*?<\/style>/gi, '')
- .replace(/<script[\s\S]*?<\/script>/gi, '')
-
- // 剥掉内联的字体/颜色/间距声明,style 清空后连属性一起去掉
- text = text.replace(/\sstyle\s*=\s*"([^"]*)"/gi, (match, style) => {
- const kept = cleanStyleValue(style)
- return kept ? ` style="${kept}"` : ''
- })
- text = text.replace(/\sstyle\s*=\s*'([^']*)'/gi, (match, style) => {
- const kept = cleanStyleValue(style)
- return kept ? ` style="${kept}"` : ''
- })
-
- text = text.replace(/<\/?font[^>]*>/gi, '')
- text = text.replace(/\sclass\s*=\s*"(?:Mso|xl)[^"]*"/gi, '')
-
- // 只剩空白的段落 / 标题直接删掉
- text = text.replace(/<(p|div|h[1-6])\b[^>]*>([\s\S]*?)<\/\1>/gi, (match, tag, inner) => {
- return isBlankFragment(inner) ? '' : match
- })
-
- // 连续 <br> 压缩为一个
- text = text.replace(/(?:<br\s*\/?>\s*){2,}/gi, '<br>')
-
- return text.trim()
- }
-
- export default cleanRichText
|