Taro+Vue3实现微信小程序富文本编辑器组件开发指南

发布于:2025-07-08 ⋅ 阅读:(16) ⋅ 点赞:(0)

实现思路与核心逻辑

1. 组件设计思路

我们基于微信小程序原生editor组件进行封装,主要实现以下功能:

  • 工具栏配置:通过图标按钮提供格式控制

  • 双向数据绑定:支持内容获取与设置

  • 图片上传:集成图片选择与上传功能

  • 格式控制:封装常用文本格式方法

2. 关键技术点

  1. 编辑器实例管理:通过createSelectorQuery获取编辑器上下文

  2. 内容同步机制:使用getContentssetContents实现内容双向绑定

  3. 图片处理流程:整合chooseImageuploadFileAPI实现图片上传

  4. 格式控制方法:统一封装format方法处理各种文本格式

  5. 事件处理:正确处理小程序特有的事件命名规则(首字母大写)

    完整实现代码

    核心代码

    <!-- RichTextEditor.vue -->
    <template>
      <view :class="styles.editor_box">
        <!-- 工具栏 -->
        <view :class="styles.tooltar" v-if="showToolbar">
          <view v-for="(item, index) in ToolBarList" @tap="item.fnc()" :key="index">
            <image :src="item.icon" :class="styles.img" />
          </view>
        </view>
        
        <!-- 编辑器主体 -->
        <editor
          id="editor"
          :placeholder="placeholder"
          @ready="editorReady"
          @Input="handleEditorChange"
          :style="editorStyle"
        />
      </view>
    </template>
    
    <script setup lang="ts">
    import Taro from '@tarojs/taro';
    import { ref, withDefaults } from 'vue';
    import styles from './index.module.scss';
    
    // 类型定义
    type FormatType = 'bold' | 'italic' | 'header' | 'align' | 'list';
    type FormatValue = 'h1' | 'h2' | 'h3' | 'left' | 'right' | 'ordered' | 'bullet';
    
    interface EditorProps {
      showToolbar?: boolean;
      defaultContent?: string;
      placeholder?: string;
    }
    
    // 组件属性
    const props = withDefaults(defineProps<EditorProps>(), {
      showToolbar: true,
      placeholder: '请输入内容',
      defaultContent: ''
    });
    
    const emits = defineEmits(['editorChange']);
    
    // 编辑器实例与状态
    const editorCtx = ref<any>(null);
    const isEditorReady = ref(false);
    const editorStyle = ref('color: black; padding: 12rpx');
    
    // 工具栏配置
    const ToolBarList = [
      { fnc: insertImage, icon: '实际图标链接', name: '插入图片' },
      { fnc: () => formatText('italic'), icon: '实际图标链接', name: '斜体' },
      { fnc: () => formatText('bold'), icon: '实际图标链接', name: '加粗' },
      { fnc: () => formatText('header', 'h1'), icon: '实际图标链接', name: '标题1' },
      { fnc: () => formatText('header', 'h2'), icon: '实际图标链接', name: '标题2' },
      { fnc: () => formatText('header', 'h3'), icon: '实际图标链接', name: '标题3' },
      { fnc: () => formatText('align', 'left'), icon: '实际图标链接', name: '左对齐' },
      { fnc: () => formatText('align', 'right'), icon: '实际图标链接', name: '右对齐' },
      { fnc: () => formatText('list', 'ordered'), icon: '实际图标链接', name: '有序列表' },
      { fnc: () => formatText('list', 'bullet'), icon: '实际图标链接', name: '无序列表' },
      { fnc: undo, icon: '实际图标链接', name: '撤销' }
    ];
    
    // 请求头配置
    const header = {
      'Content-Type': 'multipart/form-data',
      WEAPP_TOKEN_HEADER: xxxxxx,
    };
    
    // 初始化编辑器
    const editorReady = () => {
      Taro.createSelectorQuery()
        .select('#editor')
        .context((res) => {
          editorCtx.value = res.context;
          isEditorReady.value = true;
          if (props.defaultContent) {
            setEditorContent(props.defaultContent);
          }
        })
        .exec();
    };
    
    // 设置编辑器内容
    const setEditorContent = async (html: string) => {
      if (!editorCtx.value) return false;
      
      try {
        await editorCtx.value.setContents({ html });
        handleEditorChange();
        return true;
      } catch (err) {
        console.error('内容设置失败:', err);
        return false;
      }
    };
    
    // 获取编辑器内容
    const getEditorContent = () => {
      return new Promise((resolve) => {
        if (!editorCtx.value) return resolve(null);
        
        editorCtx.value.getContents({
          success: (res) => resolve({ html: res.html, text: res.text }),
          fail: (err) => {
            console.error('内容获取失败:', err);
            resolve(null);
          }
        });
      });
    };
    
    // 内容变化回调
    const handleEditorChange = async () => {
      const content = await getEditorContent();
      if (content) emits('editorChange', content);
    };
    
    // 文本格式控制
    const formatText = (type: FormatType, value?: FormatValue) => {
      if (editorCtx.value) editorCtx.value.format(type, value);
    };
    
    // 撤销操作
    const undo = () => {
      if (editorCtx.value) editorCtx.value.undo();
    };
    
    // 插入图片
    const insertImage = () => {
      Taro.chooseImage({
        count: 1,
        sizeType: ['compressed'],
        sourceType: ['album', 'camera'],
        success: async (res) => {
          Taro.showLoading({ title: '上传中...' });
          const filePath = res.tempFilePaths[0];
          
          try {
            const uploadRes = await Taro.uploadFile({
              url: `实际后台服务器地址`,
              filePath,
              header,
              name: 'file'
            });
            
            const data = JSON.parse(uploadRes.data);
            if (data?.data?.error) {
              throw new Error(data.data.error);
            }
            
            if (editorCtx.value) {
              await editorCtx.value.insertImage({
                src: data.data,
                width: '100%'
              });
              Taro.showToast({ title: '插入成功', icon: 'success' });
            }
          } catch (error) {
            Taro.showToast({ title: '上传失败', icon: 'error' });
            console.error('图片上传失败:', error);
          } finally {
            Taro.hideLoading();
          }
        }
      });
    };
    </script>
    

    index.module.scss 样式文件
     

    .editor_box {
      width: 100%;
      height: auto;
      box-sizing: border-box;
      background: #F7F7F7;
       border-radius: 12rpx;
    }
    
    .tooltar{
      display: flex;
      align-items: center;
      justify-content: space-between;
      box-sizing: border-box;
      padding: 10rpx 12rpx;
      border-bottom: 1rpx solid #EBEBEB;
      view{
        .img{
            width: 24rpx;
            height: 24rpx;
        }
      }
    }
    

    2. 方法说明

    方法名 说明 参数
    setEditorContent 设置编辑器内容 html: string
    getEditorContent 获取编辑器内容
    formatText 格式化文本 type: FormatType, value?: FormatValue
    insertImage 插入图片
    undo 撤销操作

    3. 注意事项

  • 图片上传需要配置正确的服务器地址和认证信息

  • 编辑器样式在小程序中只能使用内联样式

  • 内容变化事件会频繁触发,建议在父组件中添加防抖处理

  • 需要提前准备好工具栏所需的图标资源


网站公告

今日签到

点亮在社区的每一天
去签到