Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 

96 строки
3.1 KiB

  1. /**
  2. * 后台富文本清洗(协议、公告等静态内容)
  3. *
  4. * 背景:html 表里的正文是 Word / 旧编辑器导出的,存在两类问题会破坏移动端排版:
  5. * 1. 大量 `<p><span>&nbsp;</span></p>` 空段落,叠加段间距后出现大片空白;
  6. * 2. 几乎每个 span 都带内联 `font-family:宋体;font-size:14px;color:#000`,
  7. * 内联样式优先级最高,会盖掉端上给的 tagStyle,导致字号偏小、暗色模式不可读。
  8. *
  9. * 处理方式:删掉空段落,剥掉会影响排版与主题的内联属性,保留加粗、下划线等语义样式。
  10. */
  11. // 会破坏排版 / 主题的内联属性,统一剥掉,交给端上的 tagStyle 决定
  12. const DROP_STYLE_PROPS = [
  13. 'font-family',
  14. 'font-size',
  15. 'color',
  16. 'background',
  17. 'background-color',
  18. 'line-height',
  19. 'margin',
  20. 'margin-top',
  21. 'margin-bottom',
  22. 'margin-left',
  23. 'margin-right',
  24. 'padding',
  25. 'padding-top',
  26. 'padding-bottom',
  27. 'width',
  28. 'height'
  29. ]
  30. /** 判断一段 html 是否只有空白内容(空格、&nbsp;、<br>、空标签) */
  31. function isBlankFragment(html) {
  32. const text = String(html || '')
  33. .replace(/<br\s*\/?>/gi, '')
  34. .replace(/<[^>]+>/g, '')
  35. .replace(/&nbsp;|&#160;|\u00a0/gi, '')
  36. .replace(/\s/g, '')
  37. return text.length === 0
  38. }
  39. /** 过滤单个 style 属性里的危险声明 */
  40. function cleanStyleValue(style) {
  41. return String(style || '')
  42. .split(';')
  43. .map((item) => item.trim())
  44. .filter(Boolean)
  45. .filter((item) => {
  46. const prop = item.split(':')[0].trim().toLowerCase()
  47. return prop && !DROP_STYLE_PROPS.includes(prop)
  48. })
  49. .join('; ')
  50. }
  51. /**
  52. * @param {string} html 后台返回的富文本
  53. * @return {string} 清洗后的富文本
  54. */
  55. export function cleanRichText(html) {
  56. if (!html) return ''
  57. let text = String(html)
  58. // 1. Word 导出常见的无用标签
  59. text = text
  60. .replace(/<\/?o:p[^>]*>/gi, '')
  61. .replace(/<!--[\s\S]*?-->/g, '')
  62. .replace(/<meta[^>]*>/gi, '')
  63. .replace(/<style[\s\S]*?<\/style>/gi, '')
  64. // 2. 剥掉内联的字体/颜色/间距声明,style 清空后连属性一起去掉
  65. text = text.replace(/\sstyle\s*=\s*"([^"]*)"/gi, (match, style) => {
  66. const kept = cleanStyleValue(style)
  67. return kept ? ` style="${kept}"` : ''
  68. })
  69. text = text.replace(/\sstyle\s*=\s*'([^']*)'/gi, (match, style) => {
  70. const kept = cleanStyleValue(style)
  71. return kept ? ` style="${kept}"` : ''
  72. })
  73. // 3. Word 会把字体信息塞进 font 标签和 class,一并清掉
  74. text = text.replace(/<\/?font[^>]*>/gi, '')
  75. text = text.replace(/\sclass\s*=\s*"(?:Mso|xl)[^"]*"/gi, '')
  76. // 4. 删掉只剩空白的段落 / 标题(连续空段落是大片空白的主因)
  77. text = text.replace(/<(p|div|h[1-6])\b[^>]*>([\s\S]*?)<\/\1>/gi, (match, tag, inner) => {
  78. return isBlankFragment(inner) ? '' : match
  79. })
  80. // 5. 连续 <br> 压缩为一个,避免用 br 堆出的空行
  81. text = text.replace(/(?:<br\s*\/?>\s*){2,}/gi, '<br>')
  82. return text.trim()
  83. }
  84. export default cleanRichText