vue中富文本编辑器wangeditor@4.x的v-model封装

以下代码实现只配置部分功能,更多功能扩展请查阅wangeditor官网 (opens new window)

# 一、下载依赖

注意wangeditor全部是小写

npm install wangeditor --save
1

# 二、实现过程中遇到的坑

1、当一个页面中使用到多个富文本组件时,会导致组件之间的id或者ref重复,导致渲染错误。

解决方案:在组件内部使用随机字符串绑定组件的ref或者id


2、在父组件中改变v-model绑定的值,富文本内容不改变。

解决方案:使用watch进行监听value变化,具体看代码实现。


3、每次调用editor.txt.html()方法,富文本中的光标就会出现在文末,导致在编辑富文本中间的内容时,出现鬼畜现象。

解决方案:不要在editor.config.onchange()中调用editor.txt.html()方法,而使用组件内部的一个content变量进行储存。然后watch content触发input事件,改变父组件的邦定值

# 三、完整代码实现

<template>
  <div>
    <div :ref="toolId" class="toolbar"></div>
    <div :ref="editorId" class="text"> <!--可使用 min-height 实现编辑区域自动增加高度-->
    </div>
  </div>
</template>

<script>
import wangEditor from 'wangeditor'

export default {
  name: 'rich-text',
  props: {
    value: String
  },
  data() {
    return {
      content: '',
      editor: {},
      toolId: '',
      editorId: ''
    }
  },
  computed: {

  },
  watch: {
    value(val) {
      if (val) {
        if (val !== this.content) {
          this.setContent(val);
        }
      } else {
        this.setContent('');
      }
    },

    content(val) {
      this.$emit('input', val);
    }
  },
  mounted() {
    this.toolId = this.randomString(12)
    this.editorId = this.randomString(12)
    this.content = this.value
    this.$nextTick(() => {
      this.editor = new wangEditor(this.$refs[this.toolId], this.$refs[this.editorId])
      this.editor.config.onchange = (html) => {
        // 监控变化,同步更新到 textarea
        this.content = html
      }
      //配置图片上传服务器接口
      this.editor.config.uploadImgServer = '/file/upload/customizeDirUpload'
      // 文件名
      this.editor.config.uploadFileName = 'file'
      // 配置上传图片请求头部
      // this.editor.config.uploadImgHeaders = {}
      // 上传图片钩子函数
      this.editor.config.uploadImgHooks = {
        before: function(xhr, editor, files) {
          // 图片上传之前触发
          // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象,files 是选择的图片文件

          // 如果返回的结果是 {prevent: true, msg: 'xxxx'} 则表示用户放弃上传
          // return {
          //     prevent: true,
          //     msg: '放弃上传'
          // }
        },
        success: function(xhr, editor, result) {
          // 图片上传并返回结果,图片插入成功之后触发
          // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象,result 是服务器端返回的结果
          // alert('成功')
        },
        fail: function(xhr, editor, result) {
          // 图片上传并返回结果,但图片插入错误时触发
          // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象,result 是服务器端返回的结果
        },
        error: function(xhr, editor) {
          // 图片上传出错时触发
          // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象
        },
        timeout: function(xhr, editor) {
          // 图片上传超时时触发
          // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象
        },

        // 如果服务器端返回的不是 {errno:0, data: [...]} 这种格式,可使用该配置
        // (但是,服务器端返回的必须是一个 JSON 格式字符串!!!否则会报错)
        customInsert: function(insertImg, result, editor) {
          // 图片上传并返回结果,自定义插入图片的事件(而不是编辑器自动插入图片!!!)
          // insertImg 是插入图片的函数,editor 是编辑器对象,result 是服务器端返回的结果

          // 举例:假如上传图片成功后,服务器端返回的是 {url:'....'} 这种格式,即可这样插入图片:
          var url = result.data
          insertImg(url)

          // result 必须是一个 JSON 格式字符串!!!否则报错
        }
      }
      this.editor.create()
      this.editor.txt.html(this.value)
    })
  },
  methods: {
    // 生成随机字符串id
    randomString(len) {
      len = len || 32
      let $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678' /** **默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
      let maxPos = $chars.length
      let pwd = ''
      for (let i = 0; i < len; i++) {
        pwd += $chars.charAt(Math.floor(Math.random() * maxPos))
      }
      return 'a' + pwd
    },
    setContent(val) {
      this.editor.txt.html(val)
    }
  }
}
</script>

<style scoped>
  .toolbar {
    border: 1px solid #ccc;
  }
  .text {
    border: 1px solid #ccc;
    min-height: 200px;
  }
</style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134

# 四、使用

import导入组件后,和使用输入框一样使用即可:

<rich-text v-model="html"></rich-text>
1

# 五、实现效果