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 | } |
正文
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 | |
15 | |
16 | |
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 |
|
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.使用效果