微信小程序--富文本编辑器

2,103 阅读4分钟

需求

富文本编辑器

使用vue3+Setup

做一个微信上的富文本编辑器,包含简单的文本样式设置以及图片上传等功能。

查阅资料发现uni-app的组件中自带editor。官方文档链接

但是官方文档没有太多细节,建议参考:#微信小程序实战之实现富文本编辑器 讲的非常仔细

效果图展示

图片.png

直接上代码

这里只展示富文本有关代码

<template>
  <!-- 工具栏:包含格式化按钮 -->
  <view class="toolbar" @tap="format">
    <!-- 加粗按钮,.bold是否为true -->
    <view :class="formats.bold ? 'ql-active' : ''" class="iconfont icon-zitijiacu" data-name="bold"></view>
    <!-- 斜体按钮 -->
    <view :class="formats.italic ? 'ql-active' : ''" class="iconfont icon-zitixieti" data-name="italic"></view>
    <!-- 下划线按钮 -->
    <view :class="formats.underline ? 'ql-active' : ''" class="iconfont icon-zitixiahuaxian" data-name="underline">
    </view>

    <!-- 文本对齐按钮:左对齐,center,justify排除百度小程序兼容性 -->
    <!-- 左对齐按钮,判断是否选中 -->
    <view :class="formats.align === 'left' ? 'ql-active' : ''" class="iconfont icon-zuoduiqi" data-name="align"
      data-value="left"></view>
    <!-- 中心对齐按钮 -->
    <view :class="formats.align === 'center' ? 'ql-active' : ''" class="iconfont icon-juzhongduiqi" data-name="align"
      data-value="center"></view>
    <!-- 右对齐按钮 -->
    <view :class="formats.align === 'right' ? 'ql-active' : ''" class="iconfont icon-youduiqi" data-name="align"
      data-value="right"></view>
    <!-- 两端对齐按钮 -->
    <view :class="formats.align === 'justify' ? 'ql-active' : ''" class="iconfont icon-zuoyouduiqi" data-name="align"
      data-value="justify"></view>

    <!-- 撤销和重做按钮,分别调用undo和redo函数 -->
    <view class="iconfont icon-undo" @tap="undo"></view>
    <view class="iconfont icon-redo" @tap="redo"></view>

    <!-- 缩进和增加缩进,分别设置data-value="-1"和"+1" -->
    <view class="iconfont icon-outdent" data-name="indent" data-value="-1"></view>
    <view class="iconfont icon-indent" data-name="indent" data-value="+1"></view>
    <!-- 标题按钮,设置为标题1格式 -->
    <view :class="formats.header === 1 ? 'ql-active' : ''" class="iconfont icon-format-header-1" data-name="header"
      :data-value="1"></view>
    <!-- 图片插入按钮,调用insertImage函数 -->
    <view class="iconfont icon-charutupian" @tap="insertImage"></view>
    <!-- 清除内容按钮,调用clear函数 -->
    <view class="iconfont icon-shanchu" @tap="clear"></view>
  </view>
  <!-- 富文本编辑器 -->
  <editor id="editor" class="ql-container" placeholder="{{placeholder}}" showImgSize showImgToolbar showImgResize
    bindstatuschange="onStatusChange" read-only="{{readOnly}}" @ready="onEditorReady" @input="updateContent">
  </editor>
</template>

script部分

注意1:uni的Editor不可以使用v-model进行双向绑定,但是官方给的办法是使用@input,当内容发生改变的时候就会记录。

详情参考官方文档

<script lang="ts" setup>
  import { ref } from 'vue'
  const editorCtx = ref(null) // 编辑器上下文
  const formats = ref({}) // 格式化状态存储
  const placeholder = ref('请输入信息...') 
  const form = ref({
    xiangQing: '' // 编辑器内容存储字段   我这里只显示xiangQing
  })
</script>

下面是初始化部分,这里留个数据回显的问题放在最后。

<script lang="ts" setup>
// 编辑器初始化
const onEditorReady = () => {
  // 选择编辑器组件并获取上下文
  uni.createSelectorQuery().select('#editor').context((res) => {
    editorCtx.value = res.context
    if (form.value.xiangQing) {
      editorCtx.value.setContents({ html: form.value.xiangQing }) // 如果有值,则回显给富文本编辑区
    }
  }).exec()
}

// 设置富文本格式化功能(如加粗、斜体等)
const format = (e) => {
  const { name, value } = e.target.dataset
  if (!name) return
  editorCtx.value.format(name, value) // 根据name和value应用格式
}

// 更新格式化状态
const onStatusChange = (e) => {
  const formats = e.detail // 获取格式化状态
  formats.value = formats // 将状态更新到formats变量
}

// 撤销操作
const undo = () => {
  editorCtx.value.undo()
}

// 重做操作
const redo = () => {
  editorCtx.value.redo()
}

// 清空编辑器内容
const clear = () => {
  uni.showModal({
    title: '清空编辑器',
    content: '确定清空编辑器全部内容?',
    success: (res) => {
      if (res.confirm) {
        editorCtx.value.clear({
          success: () => console.log('clear success'),
        })
      }
    },
  })
}
// 获取编辑器内容
const updateContent = (e) => {
  const content = e.detail // 获取编辑器内容
  form.value.xiangQing = content.html // 更新到 form.xiangQing
}
</script>

下面是图片部分,这里我的图片不需要存储到云端,只需要转换成base64和编辑区内容一起传给后端就可以了。

// 插入图片
const insertImage = () => {
  uni.chooseImage({
    count: 1,
    success: (res) => {
      const filePath = res.tempFilePaths[0]
      uni.getFileSystemManager().readFile({
        filePath,
        encoding: 'base64',
        success: (result) => {
          const base64Data = 'data:image/png;base64,' + result.data
          editorCtx.value.insertImage({
            src: base64Data,
            alt: '图像',
            success: () => console.log('insert image success with base64'),
          })
        },
        fail: (err) => {
          console.error('Failed to convert image to base64:', err)
        },
      })
    },
  })
}

样式部分

<script lang="scss" scoped>
  .toolbar {
      display: flex;
      flex-wrap: wrap; // 允许换行
      justify-content: space-around; // 平均分配空间
      align-items: center; // 垂直居中
      background-color: #f9f9f9; // 背景颜色
      padding: 10px; // 内边距
      border-radius: 10px; // 圆角
      box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); // 阴影效果
      margin-top: 20rpx; // 顶部边距
      .iconfont {
        text-align: center;
        flex: 0 0 10%; // 设置每个图标占据 10% 宽度
        margin: 5px; // 添加间距
        transition: color 0.3s, transform 0.3s; // 添加过渡效果
        cursor: pointer; // 手型光标
      }
      .ql-active {
        color: #497749; // 激活状态的颜色
        font-weight: bold; // 激活时加粗
      }
    }
    .ql-container {
      box-sizing: border-box;
      padding: 12px 15px;
      width: 100%;
      min-height: 30vh;
      height: 750rpx;
      background: #fff;
      margin-top: 20rpx;
      font-size: 16px;
      line-height: 1.5;
      border: 1rpx solid #f2f2f2;
      border-radius: 15rpx;
    }
</script>

最后

前面留的小问题 修改的时候会发生

我的的Id是需要从主页面链接过来,当我拿到Id后再去请求form的所有数据并赋值。 在这个时候如果赋值过来的xiangQing的数据太大比如有base64图片,就会加载不出来,但是很玄学有的时候可以 我的做法是设置个await,先让数据赋值过来 再去执行onEditorReady就没问题了。

<script lang="scss" scoped>
  onLoad(async (options) => {
  id.value = options.id
  name.value = options.name
  form.value.stationId = options.id
  if (options.analysisId) {
    await open(options.analysisId)
    uni.setNavigationBarTitle({
      title: '修改分析报告',
    })
    onEditorReady()
  }
})
</script>