【Uniapp】【rich-text】富文本展示以及图片预览功能解决方案

前言

在使用uniapp的富文本展示组件的时候会遇到几个问题:

  1. 在后台编辑的图片文字,在移动端展示效果不好,比如文字大小不统一,图片太长导致超出手机屏幕
  2. 点击富文本中的图片需要图片预览。

富文本展示问题

这边可以使用正则表达式,对富文本的数据进行一个格式化,对p标签和img标签设置对应css样式保证移动端的展示效果。

这边写了一个格式化的函数如下

js 复制代码
export function formatRichText(html) {
  if (!html) return '';
  let formattedHtml = html;
	let imgStyle = "max-width: 100%; height: auto; display: block;"
	let pStyle = "font-size: 28rpx;"

  // 1. 处理 img 标签:移除干扰属性,添加自适应样式
  formattedHtml = formattedHtml.replace(/<img[^>]*>/gi, (match) => {
    // 移除 style、width、height 属性(支持双引号和单引号)
    let cleaned = match
      .replace(/style="[^"]*"/gi, '')
      .replace(/style='[^']*'/gi, '')
      .replace(/width="[^"]*"/gi, '')
      .replace(/width='[^']*'/gi, '')
      .replace(/height="[^"]*"/gi, '')
      .replace(/height='[^']*'/gi, '');
    // 添加新样式(插入到 <img 之后)
    return cleaned.replace(/<img/i, `<img style="${imgStyle}"`);
  });

  // 2. 处理 p 标签:设置字体大小为 28rpx,保留原有样式
  formattedHtml = formattedHtml.replace(/<p(\s[^>]*)?>/gi, (match) => {
    const styleRegex = /style=["']([^"']*)["']/i;
    const hasStyle = match.match(styleRegex);
    if (hasStyle) {
      // 已有 style 属性:追加 font-size
      const oldStyle = hasStyle[1];
      const newStyle = oldStyle + `; ${pStyle}`;
      return match.replace(styleRegex, `style="${newStyle}"`);
    } else {
      // 无 style 属性:新增 style
      return match.replace(/<p/i, `<p style="${pStyle}"`);
    }
  });

  return formattedHtml;
}

富文本图片预览问题-方案一

这边重点是要使用 @itemclick 事件,这样可以精确的获取到点击的是哪张图片,这个事件只有点击到富文本中的a标签img标签才会触发。且在nvue 中是不支持的。

并且我们在rich-text的还存储了 富文本的源数据 在 :data-nodes中。js中可以使用 e.target.dataset.nodes 来获取到我们存储的数据

图片预览需要两个数据,一个是图片列表,一个是当前图片url

html 复制代码
<rich-text :nodes="formatRichText(content)" @itemclick="showPreview" :data-nodes="content"></rich-text>
js 复制代码
import {ref} from 'vue'
import {formatRichText} from '/common/utils/utils.js'

const content = ref(`<p>1.测试数据。</p><p>2.测试数据。</p><p>3.测试数据。</p><p><img src='https://picsum.photos/800/600'></p><p><img src='https://picsum.photos/600/800'></p><p>a标签测试</p><p><a href="https://www.baidu.com">https://www.baidu.com</a></p>`) 

// 从html中提取所有img src
function matchImgUrlList(html){
	const imgReg = /<img[^>]+src=["']([^"']+)["'][^>]*>/gi
	const imgList = []
	let match
	while ((match = imgReg.exec(html)) !== null) {
		imgList.push(match[1])
	}
	return imgList
}

function showPreview(e){
	console.log(e)
	if(e.detail.node.name !== 'img') {
		return
	}
	let nodes = e.target.dataset.nodes;
	let imgList = matchImgUrlList(nodes)
	let current = e.detail.node.attrs.src
	uni.previewImage({
		current: current,
		urls:imgList,
	})
}

最后我们来看一下@itemclick事件到底给我们传了哪些数据:

json 复制代码
{
    "defaultPrevented": false,
    "timeStamp": 4127.700000047684,
    "_stop": false,
    "_end": false,
    "type": "onItemclick",
    "bubbles": false,
    "cancelable": false,
    "target": {
        "dataset": {
            "nodes": "<p>1.测试数据。</p><p>2.测试数据。</p><p>3.测试数据。</p><p><img src='https://picsum.photos/800/600'></p><p><img src='https://picsum.photos/600/800'></p><p>a标签测试</p><p><a href=\"https://www.baidu.com\">https://www.baidu.com</a></p>"
        },
        "id": "",
        "offsetLeft": 0,
        "offsetTop": 19
    },
    "detail": {
        "node": {
            "attrs": {
                "style": "max-width: 100%; height: auto; display: block;",
                "src": "https://picsum.photos/800/600"
            },
            "name": "img"
        }
    },
    "currentTarget": {
        "dataset": {
            "nodes": "<p>1.测试数据。</p><p>2.测试数据。</p><p>3.测试数据。</p><p><img src='https://picsum.photos/800/600'></p><p><img src='https://picsum.photos/600/800'></p><p>a标签测试</p><p><a href=\"https://www.baidu.com\">https://www.baidu.com</a></p>"
        },
        "id": "",
        "offsetLeft": 0,
        "offsetTop": 19
    }
}

富文本图片预览问题-方案二

由于@itemclick的兼容性问题,这边又提出了另一个解决方案。

将富文本解析成如下的数据结构,然后v-for循环即可。区分为文本与图片,遇到文本则用rich-text进行展示,遇到图片则直接用原生的 <image> 标签展示

json 复制代码
[
    {
        "type": "text",
        "content": "<p>1.测试数据。</p><p>2.测试数据。</p><p>3.测试数据。</p><p>"
    },
    {
        "type": "image",
        "src": "https://picsum.photos/800/600"
    },
]

代码如下

html 复制代码
<template v-for="(seg, idx) in parseHtmlSegments(content)" :key="idx">
	<rich-text v-if="seg.type === 'text'" :nodes="formatRichText(seg.content)"></rich-text>
	<image v-else :src="seg.src" mode="widthFix" class="preview-image" @tap="onImageTap(seg.src, content)"></image>
</template>

解析富文本为上述json格式的工具方法如下

js 复制代码
export function parseHtmlSegments(html){
	console.log(html)
	if (!html) return []
	const segments = []
	const imgReg = /<img[^>]+src=["']([^"']+)["'][^>]*>/gi
	let lastIndex = 0
	let match
	while ((match = imgReg.exec(html)) !== null) {
		if (match.index > lastIndex) {
			const textContent = html.substring(lastIndex, match.index)
			if (textContent.trim()) {
				segments.push({ type: 'text', content: textContent })
			}
		}
		segments.push({ type: 'image', src: match[1] })
		lastIndex = match.index + match[0].length
	}
	if (lastIndex < html.length) {
		const textContent = html.substring(lastIndex)
		if (textContent.trim()) {
			segments.push({ type: 'text', content: textContent })
		}
	}
	return segments
}
js 复制代码
import {ref} from 'vue'
import {formatRichText, parseHtmlSegments,matchImgUrlList} from '/common/utils/utils.js'

function onImageTap(currentSrc, html){
	const imgList = matchImgUrlList(html)
	if (rawUrls.length === 0) return
	uni.previewImage({
		urls: imgList,
		current: currentSrc,
	})
}
相关推荐
随风一样自由1 小时前
【前端独角兽】刚进入前端领域的前端开发用jQuery还是Vue3?
前端·javascript·vue3·jquery
IMPYLH2 小时前
HTML 的 <data> 元素
前端·html
YHHLAI2 小时前
[特殊字符] 面试中的 Promise —— 从入门到精通
前端·javascript·面试
weixin_BYSJ19873 小时前
SpringBoot + MySQL 乒乓球运动员信息管理系统项目实战--附源码04954
java·javascript·spring boot·python·django·flask·php
沪飘大军4 小时前
ll-llm:一个零依赖、可插拔的 OpenAI 兼容 LLM 代理
javascript
kyriewen4 小时前
我扒了 GPT-5.6 的全部编程跑分——有一项 OpenAI 没敢放出来
前端·gpt·ai编程
前端H5 小时前
微前端 v3 架构升级:从加载隔离到状态协同
前端·架构·状态模式
山河木马5 小时前
GPU自动处理专题3-NDC怎么映射成屏幕像素坐标(视口变换)
javascript·webgl·图形学
陆枫Larry6 小时前
小程序包体积优化:用 SVG 图片替换 iconfont,并保留 CSS 控色能力
前端
Appthing6 小时前
在 TanStack Start 中使用 VitePWA 的正确配置
javascript