vue 后台管理系统富文本组件(二)kindeditor

简介

富文本组件作为后台管理系统的最重要的基础组件之一,好多公司还是习惯使用 kindeditor。虽然他的界面比较老,但是相当稳定 bug 少。

主要依赖说明 (先安装,步骤略)

1
{
2
  "axios": "^0.18.0",
3
  "element-ui": "2.11.1",
4
  "vue": "^2.6.10",
5
  "kindeditor": "^4.1.10"
6
}

kindeditor 官方文档

正文

1.组件

文件目录

在这里插入图片描述

src/components/Kindeditor/index.vue

1
<template>
2
  <div class="margin-top-20">
3
    <textarea :id="id" name="content" v-model="outContent"></textarea>
4
    <input
5
      @change="selectedFile"
6
      style="visibility: hidden;height:0;"
7
      type="file"
8
      name
9
      id="inputFile"
10
    />
11
  </div>
12
</template>
13
<script>
14
  import "kindeditor/themes/default/default.css";
15
  import "kindeditor/kindeditor-all-min.js";
16
  import "kindeditor/lang/zh-CN.js";
17
18
  // 以下四个配置文件见下文
19
20
  import items from "./config/items";
21
  import htmlTags from "./config/htmlTags";
22
  import fontSizeTable from "./config/fontSizeTable";
23
  import otherConfig from "./config/otherConfig";
24
25
  export default {
26
    name: "kindeditor-component",
27
    props: {
28
      // 编辑器内容 url
29
      html: {
30
        type: String,
31
        default: ""
32
      },
33
      // 编辑器内容
34
      content: {
35
        type: String,
36
        default: ""
37
      },
38
      // 编辑器id
39
      id: {
40
        type: String,
41
        // required: true,
42
        default: "kindeditor-id"
43
      },
44
      // 宽
45
      width: {
46
        type: String,
47
        default: `100%`
48
      },
49
      // 高
50
      height: {
51
        type: String,
52
        default: "400"
53
      },
54
      // 最小宽
55
      minWidth: {
56
        type: Number,
57
        default: 650
58
      },
59
      // 最小高
60
      minHeight: {
61
        type: Number,
62
        default: 400
63
      },
64
      // toolbar 工具栏配置
65
      items: {
66
        type: Array,
67
        default: function() {
68
          return [...items];
69
        }
70
      },
71
      // 标签配置
72
      htmlTags: {
73
        type: Object,
74
        default: function() {
75
          return { ...htmlTags };
76
        }
77
      },
78
      //字号配置
79
      fontSizeTable: {
80
        type: Array,
81
        default: function() {
82
          return [...fontSizeTable];
83
        }
84
      },
85
      // 语言配置
86
      langType: {
87
        type: String,
88
        default: "zh-CN"
89
      },
90
      // 主题配置
91
      themeType: {
92
        type: String,
93
        default: "default"
94
      },
95
      // body 的样式
96
      bodyClass: {
97
        type: String,
98
        default: "ke-content"
99
      },
100
      // 其他配置项
101
      ...otherConfig
102
    },
103
    data() {
104
      return {
105
        editor: null,
106
        outContent: this.content
107
      };
108
    },
109
110
    watch: {
111
      content(val) {
112
        this.editor && val !== this.outContent && this.editor.html(val);
113
      },
114
      // 分发编辑器内容改变事件
115
      outContent(val) {
116
        this.$emit("update:content", val);
117
        this.$emit("on-content-change", val);
118
        this.$emit("input", val);
119
      },
120
      // 初始化编辑器内容
121
      html(val) {
122
        if (
123
          this.html &&
124
          (this.html.startsWith("https://") || this.html.startsWith("http://"))
125
        ) {
126
          this.loadUrl(val);
127
        } else {
128
          this.outContent = "";
129
          this.outContent ? this.editor.appendHtml(this.outContent) : "";
130
        }
131
      }
132
    },
133
    created() {
134
      if (
135
        this.html &&
136
        (this.html.startsWith("https://") || this.html.startsWith("http://"))
137
      ) {
138
        this.loadUrl(this.html);
139
      } else {
140
        this.outContent = "";
141
        setTimeout(() => {
142
          this.outContent ? this.editor.appendHtml(this.outContent) : "";
143
        }, 1000);
144
      }
145
    },
146
    mounted() {
147
      // 初始访问时创建
148
      this.initEditor();
149
      //  添加焦点
150
      // this.editor.focus();
151
      // 添加点击图片回调函数
152
      this.editor.clickToolbar("image", () => {
153
        // 禁用自带的图片弹窗
154
        this.editor.hideDialog();
155
        // 打开文件
156
        this.handleOpenFile();
157
      });
158
    },
159
    activated() {
160
      // keep-alive 进入时创建
161
      this.initEditor();
162
    },
163
    deactivated() {
164
      // keep-alive 离开时移除
165
      this.removeEditor();
166
    },
167
    beforeDestroy() {
168
      // 实例销毁之前移除
169
      this.removeEditor();
170
    },
171
    methods: {
172
      // 打开文件
173
      handleOpenFile() {
174
        let input = document.getElementById("inputFile");
175
        // 解决同一个文件不能监听的问题
176
        input.addEventListener(
177
          "click",
178
          function() {
179
            this.value = "";
180
          },
181
          false
182
        );
183
        // 点击input
184
        input.click();
185
      },
186
      // 选择好文件
187
    async  selectedFile($event) {
188
        const file = $event.target.files[0];
189
190
      // 把图片上传到后端服务器 拿到url  uploadImage 是自己后端上传图片的接口
191
      // 调用appendHtml方法把图片追加到富文本
192
193
      // const url= await  uploadImage (file)
194
      //   this.editor.appendHtml(
195
      //           `<img style="max-width:100%;" src="https://${data.Location}">`
196
      //         );
197
      },
198
      // 编辑器内容上传到cos,调用返回url
199
      async content2Url() {
200
     // 把html片段上传到后端服务器 拿到url  uploadHtml 是自己后端上传的接口
201
      //   try {
202
      //     const res = await uploadHtml(this.outContent)
203
      //     return res
204
      //   } catch (error) {
205
      //     this.$message({
206
      //       message: error.data.message,
207
      //       type: 'error'
208
      //     })
209
      //   }
210
      }
211
      // 加载html填充编辑器内容
212
      loadUrl(url) {
213
        if (url && url.length > 0) {
214
          axios.get(url)
215
            .then(response => {
216
              // 处理HTML显示
217
              this.outContent = response.data;
218
              this.editor.appendHtml(this.outContent);
219
              this.$emit("subLoadUrlToHtml", response.data);
220
            })
221
            .catch(() => {
222
              this.outContent = "服务器数据加载失败,请重试!";
223
              this.editor.appendHtml(this.outContent);
224
            });
225
        }
226
      },
227
      // 移除编辑器实例
228
      removeEditor() {
229
        window.KindEditor.remove(`#${this.id}`);
230
      },
231
      // 初始化编辑器
232
      initEditor() {
233
        this.removeEditor();
234
        this.editor = window.KindEditor.create("#" + this.id, {
235
          width: this.width,
236
          height: this.height,
237
          minWidth: this.minWidth,
238
          minHeight: this.minHeight,
239
          items: this.items,
240
          noDisableItems: this.noDisableItems,
241
          filterMode: this.filterMode,
242
          htmlTags: this.htmlTags,
243
          wellFormatMode: this.wellFormatMode,
244
          resizeType: this.resizeType,
245
          themeType: this.themeType,
246
          langType: this.langType,
247
          designMode: this.designMode,
248
          fullscreenMode: this.fullscreenMode,
249
          basePath: this.basePath,
250
          themesPath: this.themesPath,
251
          pluginsPath: this.pluginsPath,
252
          langPath: this.langPath,
253
          minChangeSize: this.minChangeSize,
254
          loadStyleMode: this.loadStyleMode,
255
          urlType: this.urlType,
256
          newlineTag: this.newlineTag,
257
          pasteType: this.pasteType,
258
          dialogAlignType: this.dialogAlignType,
259
          shadowMode: this.shadowMode,
260
          zIndex: this.zIndex,
261
          useContextmenu: this.useContextmenu,
262
          syncType: this.syncType,
263
          indentChar: this.indentChar,
264
          cssPath: this.cssPath,
265
          cssData: this.cssData,
266
          bodyClass: this.bodyClass,
267
          colorTable: this.colorTable,
268
          afterCreate: this.afterCreate,
269
          // 编辑器内容改变回调
270
          afterChange: () => {
271
            this.editor ? (this.outContent = this.editor.html()) : "";
272
          },
273
          afterTab: this.afterTab,
274
          afterFocus: this.afterFocus,
275
          afterBlur: this.afterBlur,
276
          afterUpload: this.afterUpload,
277
          uploadJson: this.uploadJson,
278
          fileManagerJson: this.fileManagerJson,
279
          allowPreviewEmoticons: this.allowPreviewEmoticons,
280
          allowImageUpload: this.allowImageUpload,
281
          allowFlashUpload: this.allowFlashUpload,
282
          allowMediaUpload: this.allowMediaUpload,
283
          allowFileUpload: this.allowFileUpload,
284
          allowFileManager: this.allowFileManager,
285
          fontSizeTable: this.fontSizeTable,
286
          imageTabIndex: this.imageTabIndex,
287
          formatUploadUrl: this.formatUploadUrl,
288
          fullscreenShortcut: this.fullscreenShortcut,
289
          extraFileUploadParams: this.extraFileUploadParams,
290
          filePostName: this.filePostName,
291
          fillDescAfterUploadImage: this.fillDescAfterUploadImage,
292
          afterSelectFile: this.afterSelectFile,
293
          pagebreakHtml: this.pagebreakHtml,
294
          allowImageRemote: this.allowImageRemote,
295
          autoHeightMode: this.autoHeightMode,
296
          fixToolBar: this.fixToolBar,
297
          tabIndex: this.tabIndex
298
        });
299
      }
300
    }
301
  };
302
</script>
303
304
<style></style>

src/components/Kindeditor/config/items.js

1
// toolbar配置
2
const items = [
3
  // "source",
4
  // "|",
5
  "undo",
6
  "redo",
7
  "|",
8
  "preview",
9
  // "print",
10
  // "template",
11
  // "code",
12
  "cut",
13
  "copy",
14
  "paste",
15
  "plainpaste",
16
  "wordpaste",
17
  "|",
18
  "justifyleft",
19
  "justifycenter",
20
  "justifyright",
21
  "justifyfull",
22
  "insertorderedlist",
23
  "insertunorderedlist",
24
  "indent",
25
  "outdent",
26
  "subscript",
27
  "superscript",
28
  "clearhtml",
29
  "quickformat",
30
  // "selectall",
31
  "|",
32
  "fullscreen",
33
  "/",
34
  "formatblock",
35
  // "fontname",
36
  "fontsize",
37
  "|",
38
  "forecolor",
39
  "hilitecolor",
40
  "bold",
41
  "italic",
42
  "underline",
43
  "strikethrough",
44
  "lineheight",
45
  "removeformat",
46
  "|",
47
  "image",
48
  // "multiimage",
49
  // "flash",
50
  // "media",
51
  // "insertfile",
52
  // "table",
53
  "hr",
54
  // "emoticons",
55
  // "baidumap",
56
  "pagebreak",
57
  "anchor",
58
  "link",
59
  "unlink",
60
  "|",
61
  "about"
62
];
63
64
export default items;

src/components/Kindeditor/config/htmlTags.js

1
const htmlTags = {
2
  font: ["color", "size", "face", ".background-color"],
3
  span: ["style"],
4
  div: ["class", "align", "style"],
5
  table: [
6
    "class",
7
    "border",
8
    "cellspacing",
9
    "cellpadding",
10
    "width",
11
    "height",
12
    "align",
13
    "style"
14
  ],
15
  "td,th": [
16
    "class",
17
    "align",
18
    "valign",
19
    "width",
20
    "height",
21
    "colspan",
22
    "rowspan",
23
    "bgcolor",
24
    "style"
25
  ],
26
  a: ["class", "href", "target", "name", "style"],
27
  embed: [
28
    "src",
29
    "width",
30
    "height",
31
    "type",
32
    "loop",
33
    "autostart",
34
    "quality",
35
    "style",
36
    "align",
37
    "allowscriptaccess",
38
    "/"
39
  ],
40
  img: [
41
    "src",
42
    "width",
43
    "height",
44
    "border",
45
    "alt",
46
    "title",
47
    "align",
48
    "style",
49
    "/"
50
  ],
51
  hr: ["class", "/"],
52
  br: ["/"],
53
  "p,ol,ul,li,blockquote,h1,h2,h3,h4,h5,h6": ["align", "style"],
54
  "tbody,tr,strong,b,sub,sup,em,i,u,strike": []
55
};
56
57
export default htmlTags;

src/components/Kindeditor/config/fontSizeTable.js

1
// 字体配置
2
const fontSizeTable = [
3
  "9px",
4
  "10px",
5
  "12px",
6
  "14px",
7
  "16px",
8
  "18px",
9
  "24px",
10
  "32px"
11
];
12
export default fontSizeTable;

src/components/Kindeditor/config/otherConfig.js

1
// 其他非主要配置项
2
const otherConfig = {
3
  noDisableItems: {
4
    type: Array,
5
    default: function() {
6
      return ["source", "fullscreen"];
7
    }
8
  },
9
  filterMode: {
10
    type: Boolean,
11
    default: true
12
  },
13
14
  wellFormatMode: {
15
    type: Boolean,
16
    default: true
17
  },
18
  resizeType: {
19
    type: Number,
20
    default: 2
21
  },
22
23
  designMode: {
24
    type: Boolean,
25
    default: true
26
  },
27
  fullscreenMode: {
28
    type: Boolean,
29
    default: false
30
  },
31
  basePath: {
32
    type: String
33
  },
34
  themesPath: {
35
    type: String
36
  },
37
  pluginsPath: {
38
    type: String,
39
    default: ""
40
  },
41
  langPath: {
42
    type: String
43
  },
44
  minChangeSize: {
45
    type: Number,
46
    default: 5
47
  },
48
  loadStyleMode: {
49
    type: Boolean,
50
    default: true
51
  },
52
  urlType: {
53
    type: String,
54
    default: ""
55
  },
56
  newlineTag: {
57
    type: String,
58
    default: "p"
59
  },
60
  pasteType: {
61
    type: Number,
62
    default: 2
63
  },
64
  dialogAlignType: {
65
    type: String,
66
    default: "page"
67
  },
68
  shadowMode: {
69
    type: Boolean,
70
    default: true
71
  },
72
  zIndex: {
73
    type: Number,
74
    default: 811213
75
  },
76
  useContextmenu: {
77
    type: Boolean,
78
    default: true
79
  },
80
  syncType: {
81
    type: String,
82
    default: "form"
83
  },
84
  indentChar: {
85
    type: String,
86
    default: "\t"
87
  },
88
  cssPath: {
89
    type: [String, Array]
90
  },
91
  cssData: {
92
    type: String
93
  },
94
95
  colorTable: {
96
    type: Array
97
  },
98
  afterCreate: {
99
    type: Function
100
  },
101
102
  afterTab: {
103
    type: Function
104
  },
105
  afterFocus: {
106
    type: Function
107
  },
108
  afterBlur: {
109
    type: Function
110
  },
111
  afterUpload: {
112
    type: Function
113
  },
114
  uploadJson: {
115
    type: String
116
  },
117
  fileManagerJson: {
118
    type: String
119
  },
120
  allowPreviewEmoticons: {
121
    type: Boolean,
122
    default: true
123
  },
124
  allowImageUpload: {
125
    type: Boolean,
126
    default: true
127
  },
128
  allowFlashUpload: {
129
    type: Boolean,
130
    default: true
131
  },
132
  allowMediaUpload: {
133
    type: Boolean,
134
    default: true
135
  },
136
  allowFileUpload: {
137
    type: Boolean,
138
    default: true
139
  },
140
  allowFileManager: {
141
    type: Boolean,
142
    default: false
143
  },
144
145
  imageTabIndex: {
146
    type: Number,
147
    default: 0
148
  },
149
  formatUploadUrl: {
150
    type: Boolean,
151
    default: true
152
  },
153
  fullscreenShortcut: {
154
    type: Boolean,
155
    default: false
156
  },
157
  extraFileUploadParams: {
158
    type: Object,
159
    default: function() {
160
      return {};
161
    }
162
  },
163
  filePostName: {
164
    type: String,
165
    default: "imgFile"
166
  },
167
  fillDescAfterUploadImage: {
168
    type: Boolean,
169
    default: false
170
  },
171
  afterSelectFile: {
172
    type: Function
173
  },
174
  pagebreakHtml: {
175
    type: String,
176
    default: '<hr style="page-break-after: always;" class="ke-pagebreak" />'
177
  },
178
  allowImageRemote: {
179
    type: Boolean,
180
    default: true
181
  },
182
  autoHeightMode: {
183
    type: Boolean,
184
    default: false
185
  },
186
  fixToolBar: {
187
    type: Boolean,
188
    default: false
189
  },
190
  tabIndex: {
191
    type: Number
192
  }
193
};
194
195
export default otherConfig;

2.使用

1
<template>
2
  <div>
3
    <Kind-editor
4
      ref="kindeditor"
5
      :html="html"
6
      @input="getContent"
7
    ></Kind-editor>
8
  </div>
9
</template>
10
11
<script>
12
  import KindEditor from "@/components/Kindeditor";
13
  export default {
14
    name: "GoodsForm",
15
    components: {
16
      KindEditor
17
    },
18
    data() {
19
      return {
20
        html:
21
          "https://ebusiness-1255313385.cosbj.myqcloud.com/image/20190823/center2019082304054532.html",
22
        content: ""
23
      };
24
    },
25
    methods: {
26
      // 获取编辑器内容
27
      getContent(content) {
28
        this.content = content;
29
      },
30
      // 编辑器内容转换成在线url
31
      async getcontent2Url() {
32
        try {
33
          const htmlUrl = await this.$refs.kindeditor.content2Url();
34
          return htmlUrl;
35
        } catch (error) {
36
          console.log(error);
37
        }
38
      }
39
    }
40
  };
41
</script>

3.使用效果

在这里插入图片描述