在uniapp中实现富文本的编辑,上传与回显

1.前言

富文本的使用十分频繁,我简单讲一讲关于在小程序端实现富文本的编辑,上传与回显。

本文借助官方提供的富文本编辑插件editor来实现富文本的编辑

本文将会提供两种实现回显方式,一种是rich-text,这是官方提供的插件,另一种是uv-ui插件中的uv-parse,这是比较热门的uniapp插件,并说明两者的区别和使用场景。

另外,本文不仅提供前端(vue3)的实现方式,并且会提供node.js的后端实现。

2.使用rich-texteditor插件实现富文本的编辑,上传与回显

2.1 富文本的编辑与上传

editor是官方提供的一个简单的富文本编辑器,虽然简单,但也能够满足很多需求了。

具体的用法如下:

下面是Edit.vue文件

借鉴了王二红大佬的实现

html 复制代码
<!-- 首先我们设置一个工具栏用于选择富文本的样式 -->
<template>
  
  <view class='toolbar' @tap="format">
    <!-- 加粗按钮,.bold是否为true -->
      <view :class="formats.bold ? 'active' : ''" class="iconfont icon-zitijiacu" data-name="bold"></view>
      <!-- 斜体按钮 -->
      <view :class="formats.italic ? 'active' : ''" class="iconfont icon-zitixieti" data-name="italic"></view>
      <!-- 下划线按钮 -->
     <view :class="formats.underline ? 'active' : ''" class="iconfont icon-zitixiahuaxian" data-name="underline">
      </view>
      <!-- 图片插入按钮 -->
      <view class="iconfont icon-addpicture" @tap="insertImage"></view>
  </view>
  <button @tap="upload">上传</button>
  <editor id="editor" class="container" placeholder="请输入...." show-img-size show-img-toolbar show-img-resize='true' @ready="onEditorReady" @input="updateContent">

</template>

这里我简单列了三个按钮,分别是加粗,斜体,下划线,我们可以通过点击按钮来改变富文本的样式。 通过自定义属性,我们可以知道用户点击了哪个按钮,从而改变富文本的样式。

ts 复制代码
// 初始化富文本编辑器
const editorCtx = ref(null)
const richText = ref('')

const onEditorReady = ()=>{
  uni.createSelectorQuery().select('#editor').context((res)=>{
    editorCtx.value = res.context
    if(richText.value && editorCtx.value!= null){
      // 初始化富文本的内容
      (editorCtx.value as any).setContents({
        html:richText.value,
      })
    }
  })
}

// 获取富文本的内容(方便我们提交到后端)
const updateContent = (e:any)=>{
  richText.value = e.detail.html
}

// 改变富文本的样式
const format = (e:any)=>{
  const {name,value} = e.target.dataset
  // 规定只有点击到能改变样式的按钮才能生效
  if(!name) return
  if(editorCtx.value != null){
    // 改变对应富文本的样式
    (editorCtx.value as any).format(name,value)
  }
}


// 插入图片
const insertImage =async ()=>{
  try{
    const res = await uni.chooseImage({
    count:1,
    soureType:['album','camera'],
  })
  const filePath = res.tempFilePaths[0]
  uni.showLoading({title:'上传中...'})

  // 上传图片到服务器
  const uploadRes = await uni.uploadFile({
    url:``,  // 上传图片的接口地址
    filePath:filePath,
    name:'images', // 后端设置的字段名
  })


  // 我这里假设后端返回的结构是[{url:'图片地址',name:'图片名称'}]
  const imageUrl = JSON.parse((uploadRes as any).data)[0].url

  // 将拿到的图片插入到富文本中
  if(editorCtx.value!= null){
    (editorCtx.value as any).insertImage({
      src:imageUrl,
      alt:'图片',
      success:()=>{
         console.log('图片插入成功')
      },
      fail:(err:any)=>{
        console.log(err) 
      }
    })
  }
  uni.hideLoading()
  }catch(err){
    uni.hideLoading()
    console.log(err) 
    uni.showToast({
      title:'上传失败',
      icon:'none',
    })
  }
}


const upload =async ()=>{
  
  try{
    if(richText === '') {
      uni.showToast({
        title:'请输入内容',
        icon:'none',
      }) 
      return
    }
    const results = await uni.request({
    url:'',
    method:'POST',
    data:{
      richText:richText.value, // 富文本的内容
    }
  })
  console.log(results)
  }catch(err){
    console.log(err)
  }

}

这里我只提供html的格式和ts代码,样式可以自行定义。

2.2 关于富文本的回显

刚才我们在一个文件中,借助官方提供的editor实现了富文本的编辑,可以实现图片和文字的混排 并且可以改变文字的样式。

但是在实际开发中,我们往往需要将富文本的内容回显到页面上,这里我们就需要用到rich-text组件了。

由于rich-text插件是官方内置的组件,我们并不需要额外的引入就能直接使用。

这里我简单介绍一下rich-text,官方文档的讲解有点难懂,这里我简单总结它的特点就是:

  • 在小程序中只读,所有生成的富文本是一个整体。
  • rich-text 组件内屏蔽所有节点的事件。所以如果内容中有链接、图片需要点击,则不能使用rich-text。
  • 可以使用Html String 或 node数组来作为rich-text的内容,node数组性能更好。(node节点的结构见下文)
  • 非 App 平台 img 标签仅支持网络图片。
  • attrs 属性不支持 id ,支持 class 。

下面是node数组的结构:

js 复制代码
[
  { type: 'text', text: '"' },
  { name: 'p', attrs: {}, children: [ [Object] ] },
  { name: 'p', attrs: {}, children: [ [Object], [Object] ] },
  { name: 'p', attrs: {}, children: [ [Object] ] },
  { name: 'p', attrs: {}, children: [ [Object], [Object] ] },
  { name: 'p', attrs: {}, children: [ [Object] ] },
  { name: 'p', attrs: {}, children: [ [Object], [Object] ] },
  { name: 'p', attrs: {}, children: [ [Object] ] },
  { name: 'p', attrs: {}, children: [ [Object], [Object] ] },
  { name: 'p', attrs: {}, children: [ [Object] ] },
  ... 275 more items
]

在小程序中使用rich-text只需要将后端回显回来的Html String 或者是 node节点数组作为rich-text中的node属性的值就可以

下面是showRichText.vue文件

html 复制代码
<rich-text :node="htmlString"></rich-text>
ts 复制代码
// 富文本的内容
const htmlString = ref('')


onLoad(()=>{
  uni.request({
    url:'',  // 获取富文本的接口地址
    method:'GET',
    success:(res:any)=>{
      htmlString.value = res.data.richText
    }
  })
})

通过这种方式,就能将富文本的内容回显到页面上了。

2.3 后端对富文本的处理

在前端,我们通过editor插件实现了富文本的编辑,上传与回显。

但是在后端,我们需要对富文本的内容进行处理,才能将其存储到数据库中。

editor提交到后端的富文本的内容是一个Html String ,由于官方的rich-text组件中直接传入nodes数组,性能会更好,所以我打算在后端将其处理成nodes数组存入数据库, 再这之后,前端通过接口请求到的数据就是nodes数组了。

下面是后端对富文本的处理:

js 复制代码
const htmlparser2 = require('htmlparser2');
const domhandler = require('domhandler')
const domutils = require('domutils')


// 将HTML字符串转换为rich-text需要的节点数组
async function htmlToNodes(htmlString) {
    const nodes = []
    const handler = new domhandler.DomHandler((error, dom) => {
        if (error) {
            console.error('Error parsing HTML:', error)
            return
        } 
        nodes.push(...domToRichText(dom)) // 将DOM节点转换为rich-text节点格式
    });
    const parser = new htmlparser2.Parser(handler); // 创建HTML解析器
    parser.write(htmlString); // 解析HTML字符串
    parser.end(); // 结束解析
    return nodes;
}


// 将DOM节点转换为rich-text节点格式
function domToRichText(domNodes) {
  return domNodes.map(node => {
    if (node.type === 'text') { // 文本节点
      return { type: 'text', text: node.data }
    } else if (node.type === 'tag'){
        return {
            name:node.name,
            attrs:Object.entries(node.attribs).reduce((acc,[key,value])=>{
                if(key === 'class'){
                    acc.class = value;
                }else if(key === 'style'){
                    acc.style = value;
                }else {
                    acc[key] = value;
                }
                return acc;
            },{}),
            children:node.children ? domToRichText(node.children) : []
        };
    }

    return {
        type:'text',
        text:''
    }
  })
}

module.exports = {
 htmlToNodes 
}

在处理完成之后,在用户上传富文本的时候,我们只需要将富文本的内容传入到htmlToNodes函数中,就可以将其转换为nodes数组了,并将其存入数据库中。

js 复制代码
const {htmlToNodes} = require('./utils')
const db = require('./db') // 数据库操作模块
const express = require('express')

const router = express.Router()

router.post('/upload',async (req,res)=>{
  try{
    const {richText} = req.body; // 富文本的内容
    const nodes = JSON.stringify(await htmlToNodes(richText)) // 将富文本的内容转换为nodes数组
    const sql = `insert into content set(richText) values(?)` // 插入数据的sql语句
    db.query(sql,[nodes],(err,results)=>{
     if(err){
        console.log(err)
        res.status(500).json({message:'服务器错误'})
     }
     res.json({message:'上传成功'})
    })
  }
 
})

3.使用uv-ui插件中的uv-parse实现富文本的回显

想要使用这个插件,建议在插件市场中使用HBuilderX进行安装,原因就是我之前在vscode中尝试使用npm安装无法使用,查看该插件的文档, 的解决方案也没有解决,后面迫不得已下载了HBuilderX来进行安装 但后面发现,直接去Gitte上引入uni-moudle到项目中也能解决。

编辑依然使用的是官方的editor,回显使用的是uv-parse

但是与rich-text不同的是,uv-parse要求传入html string,而不是nodes数组,所以后端不需要额外再处理了。 关于uv-parse的使用,详情见https://www.uvui.cn/components/parse.html

下面是showRichText.vue文件

html 复制代码
<template>
  <view>
    <uv-parse :content="htmlString"></uv-parse>
  </view>

</template>

4.关于两者的抉择

既然两者都能实现富文本的回显,那么我们应该选择哪一个呢?

如果你只要求富文本只读,而不做任何要求,那么使用官方提供的rich-text即可, 但是如果你要求你富文本内部的事件不被屏蔽,推荐使用uv-parse,而且uv-parse默认提供图片预览功能,而且uv-parse的功能更加丰富。

5.总结

这篇文章借鉴了网上的一些方案,并且上述实现是我从自己的项目中简化出来的,可能会有一些问题。 如果有更好的实现方式,欢迎留言交流。

相关推荐
鸿蒙场景化示例代码技术工程师4 分钟前
基于AssetStoreKit实现免密登录鸿蒙示例代码
前端
在掘金5 分钟前
【kk-utils】Excel工具——excel-js
前端·excel
Danny_FD7 分钟前
Canvas的应用与实践
前端·javascript
_请输入用户名9 分钟前
husky 切换 simlple-git-hook 失效解决方法
前端
前端九哥10 分钟前
🚀Vue 3 hooks 每次使用都是新建一个实例?一文彻底搞懂!🎉
前端·vue.js
盏灯10 分钟前
尤雨溪搞响应式为什么要从 Object.defineProperty 换成 Proxy❓
前端·vue.js
爱上大树的小猪10 分钟前
【前端样式】使用CSS Grid打造完美响应式卡片布局:auto-fill与minmax深度指南
前端·css·面试
代码小学僧10 分钟前
🤗 赛博佛祖 Cloudflare 初体验托管自定义域名与无限邮箱注册
前端·serverless·云计算
晴殇i11 分钟前
一行代码解决深拷贝问题,JavaScript新特性解析
前端
天天扭码21 分钟前
零基础入门 | 超详细讲解 | 小白也能看懂的爬虫程序——爬取微博热搜榜
前端·爬虫·cursor