vue2.x使用ckeditor4(上传图片使用element-ui)

1.到官网下载ckeditor(full-.zip)
https://ckeditor.com/ckeditor-4/download/

2.解压后的文件放在Vue项目的static目录下


cke1.png

3.新建一个ckeditor组件,组件代码在下面

自定义按钮
在plugins文件下新建一个addpic文件夹,里面放一张图片和plugin.js文件


cke2.png

在plugin.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(function () {
  CKEDITOR.plugins.add( 'addpic', {
    init: function( editor ) {
      editor.addCommand( 'insertTimestamp', {
        exec: function(editor) {
          document.querySelector("#FuploadImg input").click();/*这里自定义的上传按钮,使用element-ui上传*/
          // editor.insertHtml( 'The current date and time is: <em>' + now.toString() + '</em>' );
        }
      });
      editor.ui.addButton( 'addpic', {
        label: '添加图片',
        command: 'insertTimestamp',
        toolbar: 'insert',
        icon:'plugins/addpic/icons/addpic.png'
      });
    }
  });
})();

在config.js里修改,就完成了


cke3.png

config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
CKEDITOR.editorConfig = function( config ) {
  // Define changes to default configuration here. For example:
  // config.language = 'fr';
  // config.uiColor = '#AADC6E';
  // config.readOnly = false
  config.toolbar = 'Full';
  config.height = 500
  // config.extraPlugins = 'addpic,pasteUploadImage';
  config.allowedContent = true;
  //config.fullPage = true;
  config.extraPlugins = 'addpic';
  config.pasteFilter = null;/*保留粘贴格式*/
  config.toolbar_Full = [
    { name: 'document', items: [ 'Source' ] },
    { name: 'clipboard', items: [ 'Cut', 'Copy', 'Paste', 'PasteText', '-', 'Undo', 'Redo' ] },
    { name: 'basicstyles', items: [ 'Bold', 'Italic', 'Underline', 'Strike', '-', 'CopyFormatting', 'RemoveFormat' ] },
    { name: 'paragraph', items: [ 'NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'BidiLtr', 'BidiRtl' ] },
    { name: 'insert', items: [ 'addpic','Table',] },
    '/',
    { name: 'styles', items: [ 'Styles', 'Format', 'Font', 'FontSize' ] },
    { name: 'colors', items: [ 'TextColor', 'BGColor' ] },
  ]
};

组件代码

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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
<template>
  <div>
    <el-upload
      style="display: none"
      class="fwb-avatar-uploader"
      id="FuploadImg"
      action=""
      name="img"
      :auto-upload="false"
      :show-file-list="false"
      :before-upload="fbeforeUpload"
      :on-change="fhandleAvatarChange"
      :on-success="fuploadSuccess"
    ></el-upload>
    <div style="position: relative;">
      <em class="num-loading" v-show="Imgloading"></em>
      <textarea id="Seditor" name="Seditor"></textarea>
    </div>
    <div class="rlPopup" v-if="imgPovp">
      <div class="rl-inner">
        <div class="g-c-33 g-fz-16 pop-txt">
          <p>图片不能超过30张</p>
        </div>
        <div class="pop-btn">
          <a href="javascript:;" class="pop-a edit-btn g-c-33" @click="imgPovp = false">返回</a>
          <a href="javascript:;" class="pop-a cancel-btn" @click="imgPovp = false">我知道了</a>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { uploadImg,uploadOOS} from '@/api/single'
import _get from 'lodash.get'
export default {
  name: 'ckeditor4',
  props: [
    'value'
  ],
  data: function() {
    return {
      ckeditor: null,
      imgPovp:false,
      Imgloading:false,
      totalImg:30,
      imgNum:0,
      uploadImgList:[],
      isUpload: false,
    }
  },
  mounted: function() {
    const self = this
    // 渲染编辑器
    self.ckeditor = window.CKEDITOR.replace("Seditor")
    CKEDITOR.instances["Seditor"].on("instanceReady", function () {
      this.document.on("paste", function(){ /*粘贴事件*/
        self.onImgQuery /*粘贴的内容有图片时自动上传到阿里云*/
      });
    });
    self.ckeditor.setReadOnly(false)//取消只读 加了这个会报错,不加在ie下使用工具栏会报没有权限,所有就还是加上了
    // 监听内容变更事件
    self.ckeditor.on('change', function() {
      self.$emit('input', self.ckeditor.getData())
    })
  },
  watch: {
    // 监听prop的变化,更新ckeditor中的值
    value: function() {
      let val = CKEDITOR.instances.Seditor.getData() /*获取数据*/
      let self = this;
      if (self.value != val && val == "" && self.value != "") {
        var hrValue = self.value.replace(/<hr />s*/g,'');/*赋值的内容显示的时候会有hr标签,这里去掉*/
        self.ckeditor.setData(hrValue) /*设置数据*/
        setTimeout(function(){
          let imgList = document.getElementsByTagName('iframe')[0].contentWindow.document.getElementsByTagName("img");
          self.imgNum = imgList.length;//将编辑模式图片数量统计进去
        },200)
        /*为了兼容ie,进入页面时先刷新一下页面 ie会报没有权限,刷新一下就好了,
        但是体验不好,为了兼容ie,没办法,没找到其他好的解决方案*/
        if (!self.getCookie('isRefresh')){
          self.setCookie('isRefresh','1',10);
          location.reload();
        } else {
          self.clearCookie('isRefresh');
        }
        /*如果不需要兼容ie10及以下,可以用以下方式获取数据,不然刚进入页面时有时候取不到数据*/
        /*let editor = CKEDITOR.instances["Seditor"]; //你的编辑器的"name"属性的值
        if (editor) {
          editor.destroy(true);//销毁编辑器
        }
        CKEDITOR.replace("Seditor"); //替换编辑器,editorID为ckeditor的"id"属性的值
        CKEDITOR.instances["Seditor"].on("instanceReady", function () {
          this.document.on("paste", function(){ //等一会
            setTimeout(self.onImgQuery, 500);
          });
        });
        document.getElementById("Seditor").innerHTML = this.value*/
      }
    }
  },
  methods:{
    async fbeforeUpload(file) {
      // console.log(file);
    },
    fuploadSuccess(res, file) {
      // console.log("成功" + res);
    },
    setCookie(name, value, seconds) {
      seconds = seconds || 0;   //seconds有值就直接赋值,没有为0
      var expires = "";
      if (seconds != 0 ) {      //设置cookie生存时间
        var date = new Date();
        date.setTime(date.getTime()+(seconds*1000));
        expires = "; expires="+date.toGMTString();
      }
      document.cookie = name+"="+escape(value)+expires+"; path=/";   //转码并赋值
    },
    clearCookie(name) {
      this.setCookie(name, "", -1);
    },
    getCookie(name) {
      var nameEQ = name + '='
      var ca = document.cookie.split(';') // 把cookie分割成组
      for (var i = 0; i < ca.length; i++) {
        var c = ca[i] // 取得字符串
        while (c.charAt(0) == ' ') { // 判断一下字符串有没有前导空格
          c = c.substring(1, c.length) // 有的话,从第二位开始取
        }
        if (c.indexOf(nameEQ) == 0) { // 如果含有我们要的name
          return unescape(c.substring(nameEQ.length, c.length)) // 解码并截取我们要值
        }
      }
      return false
    },
    onImgQuery(){
      /*获取所有的img*/
      // let doc = e.editor.document;
      let imgList = document.getElementsByTagName('iframe')[0].contentWindow.document.getElementsByTagName("img")
      if(this.imgNum > imgList.length) this.imgNum = imgList.length //针对用户删除图片
      if(this.isUpload) return
      let that = this
      let isErr = false
      if(imgList.length > 0){
        for(var i = 0; i < imgList.length; i++){
          if(imgList[i].src&&(imgList[i].src.indexOf("taoic") == -1 )){
            that.imgNum += 1
            if(that.imgNum > that.totalImg){
              if (this.isIE() || this.isIE11()) {
                imgList[i].removeNode(true);
              } else {
                imgList[i].remove()
              }
              that.imgNum -= 1
              isErr = true
            } else {
              that.uploadImgList.push(imgList[i].src)
            }
          }
        }
        if (that.uploadImgList.length >0){
          that.UploadOOSImg()
        }
        if (isErr) that.imgPovp = true;
      }
    },
    UploadOOSImg(){
      let that = this
      that.isUpload = true
      uploadOOS(that.uploadImgList.join("-")).then(res => {
        if(res.code == 1){
          let imgList = document.getElementsByTagName('iframe')[0].contentWindow.document.getElementsByTagName("img")
          let uploadImgList = that.uploadImgList
          for(var i = 0; i < imgList.length; i++){
            for(const j in uploadImgList){
              if(imgList[i].src ==  uploadImgList[j]){ //对应上
                if (res.Data[j] == "imageBig"){
                  if (that.isIE() || that.isIE11()) {
                    imgList[i].removeNode(true);
                  } else {
                    imgList[i].remove()
                  }
                  that.imgNum -= 1
                  // console.log(imgList)
                } else {
                  imgList[i].src = res.Data[j]
                }
              }
            }
          }
        }
        that.isUpload = false
        that.uploadImgList = [];
      }).catch(err => {
        that.isUpload = false
        that.uploadImgList = [];
      })
    },
    /*上传阿里云*/
    fhandleAvatarChange(file) {
      let that = this
      if (!file) {
        return
      }
      if(this.imgNum >= this.totalImg){
        that.imgPovp = true;
        return false;
      }
      // this.imgNum +=1
      let testmsg = file.name.substring(file.name.lastIndexOf(".") + 1).toLowerCase();
      const isJPG  = 'image/jpeg';
      const isLt1M = file.raw.size / 1024 / 1024 < 4;
      if(file.raw.type) {
        if(file.raw.type !== isJPG){
          this.$message.error('上传图片只能是 JPG 格式!');
          return false;
        }
      }else {
        if(testmsg !== 'jpg' && testmsg !== 'jpeg'){
          this.$message.error('上传图片只能是 JPG 格式!');
          return false;
        }
      }
      if(!isLt1M){
        this.$message.error('上传图片不能超过4M!');
        return false;
      }
      that.Imgloading = true
      uploadImg().then(resp => {
        if(resp.code == 1){
          let time = Date.parse(new Date())
          const form = new FormData()
          form.append('policy', _get(resp, 'data.policy'))
          form.append('OSSAccessKeyId', _get(resp, 'data.accessid'))
          form.append('signature', _get(resp, 'data.signature'))
          form.append('key', `${_get(resp, 'data.dir')}${_get(resp, 'data.key')}${'_'}${time}${'.jpg'}`)
          form.append('success_action_status', 200)
          form.append('file', file.raw)
          that.$http.post(process.env.OSS_URL, form).then(res => {
            if(res.status == 200){
              that.imgNum += 1
              that.Imgloading = false
              that.updateImg = (process.env.OSS_URL + '/' + `${_get(resp, 'data.dir')}${_get(resp, 'data.key')}${'_'}${time}${'.jpg'}`)
              /*插入到富文本*/
              var imgPath = CKEDITOR.dom.element.createFromHtml("<img src='" + that.updateImg + "'/>");
              window.CKEDITOR.instances.Seditor.insertElement(imgPath)
            }else {
              that.$message.error('网络错误,请稍后再试!');
            }
          }).catch(err => {
            console.log(err);
          })
        }else {
          that.$message.error('网络错误,请稍后再试!');
        }
      }).catch(err => {
        console.log(err);
      })
      // }
    },
    isIE() {
      if (!!window.ActiveXobject || "ActiveXObject" in window) {
        return true;
      } else {
        return false;
      }
    },
    isIE11(){
      if((/Trident/7./).test(navigator.userAgent)) {
        return true;
      } else {
        return false;
      }
    }
  },
  // 销毁组件前,销毁编辑器
  beforeDestroy: function() {
    // self.ckeditor.destroy()
  }
}
</script>
<style scoped>
.num-loading{
  height: 24px;
  width: 24px;
  background: #fff;
  border-radius: 50%;
  border: 1px solid #1D9AF2;
  border-top: 2px solid #fff;
  animation: spinner1 600ms linear infinite;
  -moz-animation: spinner1 600ms linear infinite;
  -webkit-animation: spinner1 600ms linear infinite;
  display: inline-block;
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 1000;
}
@keyframes spinner1 {
  to {
    transform: rotate(360deg);
    -moz-transform: rotate(360deg);
    -webkit-transform: rotate(360deg);
  }
}
.rlPopup{
  position: fixed;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0,0,0,0.5);
  z-index: 999;
}
.rl-inner{
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%,-50%);
  -webkit-transform: translate(-50%,-50%);
  width: 320px;
  height: 154px;
  background-color: #ffffff;
  border-radius: 10px;
  overflow: hidden;
}
.pop-txt{
  height: 114px;
  padding: 32px 24px 0 24px;
  border-bottom: 1px solid #f9f9f9;
}
.pop-btn{
  height: 40px;
}
.pop-a{
  width: 160px;
  height: 40px;
  display: block;
  text-align: center;
  line-height: 40px;
  float: left;
}
.cancel-btn{
  background-color: #409efe;
  color: #fff;
  /*border-radius: 0 0 10px 0;*/
}
.cancel-btn:hover{
  background-color: #3996f6;
}
</style>

在页面使用

获取富文本数据

1
CKEDITOR.instances.富文本id.getData()

设置富文本数据

1
CKEDITOR.instances.富文本id.setData()

提交数据时过滤富文本的某些标签

1
2
3
4
5
6
7
let scriptTag =  CKEDITOR.instances.Seditor.getData().replace(/<script(([sS])*?)</script>/g, ''); /*过滤script标签*/
filterTag = filterTag .replace(/<style type="text/css"(([sS])*?)</style>/g, '')/*过滤style标签*/
filterTag = filterTag .replace(/<style(([sS])*?)</style>/g, '')/*过滤style标签*/
filterTag = filterTag .replace(/<iframe(([sS])*?)</iframe>/g, '')/*过滤iframe标签*/
filterTag = filterTag .replace(/s*on[a-z]+s*=s*("[^"]+"|'[^']+'|[^s]+)s*/ig, '')/*过滤事件标签*/
filterTag = filterTag .replace(/href=/g, '')/*过滤a链接的href属性*/
this.msg = filterTag