Vue项目中富文本编辑器组件二次封装实践

2023-11-14

最近接到一个小需求,要求对原项目的富文本编辑器进行统一化。由于之前没有具体的规范约束,该PC端项目中在不同模块中分别引入了3种不一样的富文本编辑器,一来不便于维护,二来样式不统一,用户使用也可能存在疑惑,于是我需要考虑重新封装一个富文本编辑器组件,再将旧代码中引入的富文本编辑器组件统一替换为新的。以下记录整个任务实现的大致思路。


首先,分析代码结构,发现项目中不同的模块分别使用了两种不同的form组件,element-UI和iView。

f561611a875d495497b9b0a7a5bbb974.png

2a4d7ce727fd4d088c2592c809f21b1b.png

因此,需要对富文本编辑器组件最外层的form-item组件进行判断处理,这里使用了Vue内置的component动态组件,绑定is属性动态渲染对应的form-item组件。 

<template>
  <!--  根据页面使用form是elementUI还是iView,外层使用对应的formItem组件(兼容旧代码有的用iView有的用elementUI的情况)  -->
  <!--  初始化时先默认不渲染,等获取到dom节点后再渲染,解决控制台在created阶段报错-->
  <component :is="wrapForm" v-if="isMounted" :rulesList="rulesList" :label="label" :lableWidth="labelWidth"
             :required="required" :prop="prop">
    <div class="fullEdit">
      ...
    </div>
  </component>
</template>

而判断条件为父组件中的dom元素是否包含类名“el-form”,是则渲染el-form-item组件,否则渲染iview的form-item组件。

created() {
    this.$nextTick(()=>{
      this.isElForm = document.querySelector('.mainCont').querySelector('.el-form') !== null
      this.isMounted = true
      ...
    })
  },
computed: {
    ...
    //渲染哪种外层form-item组件,elementUI或iView的form-item
    wrapForm() {
      return this.isElForm ? 'elFormItem' : 'FormItem'
    },
  },

富文本编辑器使用了wangEditor,具体参数参考官方文档配置:编辑器 API | wangEditor

<template>
  <!--  根据页面使用form是elementUI还是iView,外层使用对应的formItem组件(兼容旧代码有的用iView有的用elementUI的情况)  -->
  <!--  初始化时先默认不渲染,等mounted后再渲染,解决控制台在created阶段报错-->
  <component :is="wrapForm" v-if="isMounted" :rulesList="rulesList" :label="label" :labelWidth="labelWidth"
             :required="required" :prop="prop || name">
    <div class="fullEdit">
      <div style="border: 1px solid #ccc;">
        <Toolbar style="border-bottom: 1px solid #ccc"
                 :editor="pcEditor"
                 :defaultConfig="pcToolbarConfig"
                 :mode="mode"
        />
        <Editor :ref="name"
                v-model="formData[name]"
                style="height: 400px; overflow-y: hidden;padding: 10px;"
                :defaultConfig="pcEditorConfig"
                :mode="mode"
                @submit="submit"
                @onChange="onEditorChange($event)"
                @onCreated="onCreatedPC"
                @onMaxLength="onMaxLength"
                @onBlur="onBlur"
        />
      </div>
      <div v-if="isShowError" class="error-tip">{{ errorText }}</div>
    </div>
  </component>
</template>
<script>
import editorMixin from '@/mixins/editorMixin'
import {Editor, Toolbar} from '@wangeditor/editor-for-vue'

export default {
  name: 'RichTextEditor',
  components: {
    Editor,
    Toolbar
  },
  mixins: [editorMixin],
  props: [
    //表单数据对象
    'formData',
    //富文本编辑器内容在表单数据对象中的名称
    'name',
    //用于校验的prop,不填则默认为与name相同
    'prop',
    //标题标签宽度
    'labelWidth',
    //是否必填,是则需要执行校验
    'required',
    //标题标签名
    'label',
    //指定文件大小
    'fileSize',
    //父组件form校验规则,需修改父组件中该对象内富文本的校验方法
    'rulesList',
    //最大可输入字数
    'length',
    //错误提示,默认为"请填写xxx"
    'errorMsg',
  ],
  data() {
    return {
      //是否显示错误提示
      isShowError: false,
      //错误提示内容
      errorText: '',
      //父组件传入的整个form数据的校验规则
      rules: {},
      //判断外层form是否使用了elementUI的form组件
      isElForm: false,
      //是否完成挂载(用于判断外层使用的form组件并渲染本组件)
      isMounted: false,
      //初始化状态,此时不校验富文本框是否为空,直至父组件提交表单后再触发
      isInitialized: true
    }
  },
  computed: {
    //最大文件大小,默认为10M
    maxSize() {
      return (this.fileSize || 10) * 1024 * 1024
    },
    //渲染哪种外层form-item组件,elementUI或iView的form-item
    wrapForm() {
      return this.isElForm ? 'elFormItem' : 'FormItem'
    },
  },
  created() {
    //nextTick中通过获取外层dom元素是否包含el-form类名判断本组件最外层渲染哪一种form-item组件
    this.$nextTick(() => {
      this.isElForm = document.querySelector('.mainCont').querySelector('.el-form') !== null
      this.isMounted = true
      //更新父组件rules
      this.getNewRules()
    })
  },
  methods: {
    //更新父组件的rules(覆盖富文本校验的方法)
    getNewRules() {
      this.rules = this.rulesList
      //如果父组件rules已设置富文本校验validator,则无需赋值
      if (this.rulesList && this.name in this.rulesList && typeof this.rulesList[this.name].validator === 'function') {
        this.isInitialized = false
        return
      }
      if (!this.rulesList) this.rules = {}
      this.rules[this.name] = {validator: this.judgeEditor, message: this.errorText}
      this.$emit('update:rulesList', this.rules)
    },
    onEditorChange(e) {
      if (this.required) {
        if (e.isEmpty() && !this.isInitialized) {
          this.isShowError = true
          this.errorText = this.errorMsg || '请填写' + this.label.replace(/[::]/g, '')
        } else {
          this.isShowError = false
        }
      }
      return this.isShowError
    },
    judgeEditor(rules, val, callback) {
      //触发校验后再将isInitialized设置为false
      this.isInitialized = false
      // 富文本校验
      const flag = this.onEditorChange(this.pcEditor)
      if (flag) {
        // callback(new Error())
      } else {
        callback()
      }
    },
    submit(){
      console.log('submit!')
    }
  }
}
</script>
<style lang="less" scoped>
.error-tip {
  color: #ed4014;
  font-size: 13px;
  padding-top: 6px;
}
.wrapper{
  border: 1px solid #dcdee2;
  z-index: 9;
}
.warning {
  border-color: #ed4014;
}
</style>

为了方便组件复用,减轻传递过多参数造成的心智负担,这里考虑将富文本内容的校验方法以缺省值的形式写在组件内部(judgeEditor方法),但由于需要配合父组件中form组件下的rules属性校验才能生效,就需要修改外部父组件中的值。

7e68e480753b46d1b2ae5c158682ab67.png96e06facb7594420ad92b19dda066c5d.png

 这里通过$emit(update:xxx) 和更新父组件绑定值的语法糖修饰符.sync的结合,实现对父组件绑定的校验规则对象rulesList的修改,使表单在submit前的校验时使用更新后的校验规则加入对富文本编辑器内容的校验。

methods: {
    //更新父组件的rules(覆盖富文本校验的方法)
    getNewRules() {
      this.rules = this.rulesList
      //如果父组件rules已设置富文本校验validator,则无需赋值
      if (this.rulesList && this.name in this.rulesList && typeof this.rulesList[this.name].validator === 'function') {
        this.isInitialized = false
        return
      }
      if (!this.rulesList) this.rules = {}
      this.rules[this.name] = {validator: this.judgeEditor, message: this.errorText}
      this.$emit('update:rulesList', this.rules)
    },
    onEditorChange(e) {
      if (this.required) {
        if (e.isEmpty() && !this.isInitialized) {
          this.isShowError = true
          this.errorText = this.errorMsg || '请填写' + this.label.replace(/[::]/g, '')
        } else {
          this.isShowError = false
        }
      }
      return this.isShowError
    },
    judgeEditor(rules, val, callback) {
      //触发校验后再将isInitialized设置为false
      this.isInitialized = false
      // 富文本校验
      const flag = this.onEditorChange(this.pcEditor)
      if (flag) {
        // callback(new Error())
      } else {
        callback()
      }
    }
  }
<RichTextEditor :rulesList.sync="ruleValidate" :formData="orgIntroduceData" name="content" label="简介内容:" :length="6000" :required="true" />

最终效果

57dcba5896794bd6ad5aef8368b98128.png

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Vue项目中富文本编辑器组件二次封装实践 的相关文章

随机推荐

  • iframe 跳转到其他页面

    也是第一次用这个标签 嵌套其他网站有的跳转了 本以为是自己的问题 百度才得知 是其他网站 有如下代码 if top location self location top location self location 会自动判断当前的loca
  • vue3中使用element-plus表格多选进行回显,分页勾选保存

    获取分页数据 第一页 td value id 10 id 30 id 70 id 80 id 90 根据接口获取会显得数据 const temp2 id 196 deviceId 1 isDelete false id 197 device
  • OpenHarmony仓库大整理 可以指定系统类型下载对应代码

    前言 以前下载OpenHarmony代码的时候 我们都是需要下载全量包 代码量非常大 现在已经有30多G了 而我们如果只是想开发轻量系统 例如润和的Hi3861智能家居开发套件的话 实际上使用不到那么多代码的 很多代码都是小型系统 标准系统
  • Java threadpool机制深入分析

    简介 在前面的一篇文章里我对java threadpool的几种基本应用方法做了个总结 Java的线程池针对不同应用的场景 主要有固定长度类型 可变长度类型以及定时执行等几种 针对这几种类型的创建 java中有一个专门的Executors类
  • new和delete工作机制

    C 中创建堆对象时 我们要使用new delete 那么它们具体做了什么 现在深入讨论 1 new表达式工作步骤 使用new表达式时发生的三个步骤 调用名为operator new的标准库函数来分配足够大的原始的未类型化的堆空间 以保存指定
  • Android 环境搭建,Helloworld以及常见错误处理,最新版哦

    1 下载所需软件1 1 JDK1 6 下载网址 http java sun com javase downloads index jsp1 2 Eclipse3 4 下载网址 http www eclipse org downloads 下
  • 游戏开发unity动画系列:用Animation创建简单动画

    参考 https zhuanlan zhihu com p 141569339
  • vue 中 mapbox 的使用 ,(同一页面进行多次切换操作)

    一 vue 中 引入 mapbox 同一页面进行多次切换操作 1 mapbox的 底图用天地图 1准备工作 安装 mapbox mapbox gl geocoder npm install save mapbox mapbox gl geo
  • mds的 labelIndex 静态预排序

    一般排序是数据 doc resultItem 取出来之后 按某个某个字段的值排序 也就是必须拿到doc resultItem之后才能排序 mds排序的特点是在取resultItem之前就排序 不是对resultItem排序 而是对docId
  • Perl 正则表达式

    正则表达式文中列表 将下一个字符标记为一个特殊字符 或一个原义字符 或一个后向引用 或一个八进制转义符 匹配输入字符串的开始位置 如果设置了 RegExp 对象的Multiline 属性 也匹配 n 或 r 之后的位置 匹配输入字符串的结束
  • 用Virtualbox搭建的ubuntu虚拟机开启与windows的共享文件夹

    一 VirtualBox共享文件夹设置 Virtualbox以共享文件夹的方式提供与真实系统的文件交互 将需要传输的文件放入建立的共享文件夹中将在两个系统中都可以访问 除了开启virtualbox设置中的共享文件夹 对于ubuntu系统还要
  • 网络基础学习:布线、计算机数制

    文章目录 一 双绞线 1 什么是双绞线 2 5类线 3 100Base T 补充 4 双绞线的连接规范 二 光纤概述 1 光纤的特点 2 光纤分类 补充 小结 三 计算机的数制 1 定义 2 数字系统的由来 3 二进制与十进制的转换 一 双
  • idea的使用-常用/快捷键/设置/插件/问题/配置备份-持续更新

    以前编译器都是eclipse最近换到了idea 还不是很习惯 记录下 目录 区别 intelij idea 和 eclipse 使用上的 idea常用快捷键记录 设置 IDEA 默认不折叠代码 IntelliJ IDEA设置自定义autho
  • STM32如何利用串口发送接收数据

    STM32如何利用串口发送接收数据 我现在计划利用STM32F103X的串口对迪文屏发送及接收数据 手中硬件 正点原子开发板 旗舰版 迪文屏 4 3寸 电脑 软件 MCU程序下载 FLYMCU 串口助手 XCOM 迪文屏配置 DGUS TO
  • DatePicker日期选择器图标替换

    实现目标 实现datepicker中的日历图标替换成类似于select选择器组件的箭头 且箭头旋转和时间面板的展开收起相耦合 遇到的问题 点击日历图标不能控制面板的开合动作 datepicker组件实例上通过代码手动聚焦 不支持手动失焦 无
  • linux操作系统有哪些

    微信设置水滴昵称 个性中带点萌 区别 1 Linux速度比较快 安全性比windows好 2 有很多软件只能在windows里运行 与Linux兼容的软件正在开发中 3 Linux适用在网络方面 4 Linux的操作比较复杂 windows
  • 利用python编程画出 圣诞树

    针对利用Python编程画出圣诞树并增加彩灯 雪花等装饰并实现动态效果的需求 以下是需求分析 1 界面需求 需要一个图形化界面 用于显示绘制出的圣诞树和装饰 并实现动态效果 该界面应该包括一个画布和控制按钮 用户可以在界面上看到圣诞树和装饰
  • 浏览器暂停ajax访问,前端js ajax请求阻止浏览器的默认行为

    在前端开发工作中 由于浏览器兼容性等问题 我们会经常用到 停止事件冒泡 和 阻止浏览器默认行为 1 阻止浏览器的默认行为 function stopDefault e 如果提供了事件对象 则这是一个非IE浏览器 if e e prevent
  • Java中的垃圾回收原理

    垃圾回收简介 用户程序 mutator 会修改还堆区中的对象集合 从存储管理器处获取空间 创建对象 还可一引入和消除对已有对象的引用 当mutator不能 达到 某些对象的时候 这些对象就成了垃圾 目的 找到不可达的对象 并将这些对象交给跟
  • Vue项目中富文本编辑器组件二次封装实践

    最近接到一个小需求 要求对原项目的富文本编辑器进行统一化 由于之前没有具体的规范约束 该PC端项目中在不同模块中分别引入了3种不一样的富文本编辑器 一来不便于维护 二来样式不统一 用户使用也可能存在疑惑 于是我需要考虑重新封装一个富文本编辑