jazz
2022-03-03 adf10a882ee565d6a5c37ba07a9e8ec2289ccf34
提交 | 用户 | age
4dde14 1 <template>
L 2   <div :class="{fullscreen:fullscreen}" class="tinymce-container" :style="{width:containerWidth}">
3     <textarea :id="tinymceId" class="tinymce-textarea" />
4     <div class="editor-custom-btn-container">
5       <editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK" />
6     </div>
7   </div>
8 </template>
9
10 <script>
11 /**
12  * docs:
13  * https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html#tinymce
14  */
15 import editorImage from './components/EditorImage'
16 import plugins from './plugins'
17 import toolbar from './toolbar'
18 import load from './dynamicLoadScript'
19
20 // why use this cdn, detail see https://github.com/PanJiaChen/tinymce-all-in-one
21 const tinymceCDN = 'https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.3/tinymce.min.js'
22
23 export default {
24   name: 'Tinymce',
25   components: { editorImage },
26   props: {
27     id: {
28       type: String,
29       default: function() {
30         return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
31       }
32     },
33     value: {
34       type: String,
35       default: ''
36     },
37     toolbar: {
38       type: Array,
39       required: false,
40       default() {
41         return []
42       }
43     },
44     menubar: {
45       type: String,
46       default: 'file edit insert view format table'
47     },
48     height: {
49       type: [Number, String],
50       required: false,
51       default: 360
52     },
53     width: {
54       type: [Number, String],
55       required: false,
56       default: 'auto'
57     }
58   },
59   data() {
60     return {
61       hasChange: false,
62       hasInit: false,
63       tinymceId: this.id,
64       fullscreen: false,
65       languageTypeList: {
66         'en': 'en',
67         'zh': 'zh_CN',
68         'es': 'es_MX',
69         'ja': 'ja'
70       }
71     }
72   },
73   computed: {
74     containerWidth() {
75       const width = this.width
76       if (/^[\d]+(\.[\d]+)?$/.test(width)) { // matches `100`, `'100'`
77         return `${width}px`
78       }
79       return width
80     }
81   },
82   watch: {
83     value(val) {
84       if (!this.hasChange && this.hasInit) {
85         this.$nextTick(() =>
86           window.tinymce.get(this.tinymceId).setContent(val || ''))
87       }
88     }
89   },
90   mounted() {
91     this.init()
92   },
93   activated() {
94     if (window.tinymce) {
95       this.initTinymce()
96     }
97   },
98   deactivated() {
99     this.destroyTinymce()
100   },
101   destroyed() {
102     this.destroyTinymce()
103   },
104   methods: {
105     init() {
106       // dynamic load tinymce from cdn
107       load(tinymceCDN, (err) => {
108         if (err) {
109           this.$message.error(err.message)
110           return
111         }
112         this.initTinymce()
113       })
114     },
115     initTinymce() {
116       const _this = this
117       window.tinymce.init({
118         selector: `#${this.tinymceId}`,
119         language: this.languageTypeList['en'],
120         height: this.height,
121         body_class: 'panel-body ',
122         object_resizing: false,
123         toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
124         menubar: this.menubar,
125         plugins: plugins,
126         end_container_on_empty_block: true,
127         powerpaste_word_import: 'clean',
128         code_dialog_height: 450,
129         code_dialog_width: 1000,
130         advlist_bullet_styles: 'square',
131         advlist_number_styles: 'default',
132         imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'],
133         default_link_target: '_blank',
134         link_title: false,
135         nonbreaking_force_tab: true, // inserting nonbreaking space &nbsp; need Nonbreaking Space Plugin
136         init_instance_callback: editor => {
137           if (_this.value) {
138             editor.setContent(_this.value)
139           }
140           _this.hasInit = true
141           editor.on('NodeChange Change KeyUp SetContent', () => {
142             this.hasChange = true
143             this.$emit('input', editor.getContent())
144           })
145         },
146         setup(editor) {
147           editor.on('FullscreenStateChanged', (e) => {
148             _this.fullscreen = e.state
149           })
150         },
151         // it will try to keep these URLs intact
152         // https://www.tiny.cloud/docs-3x/reference/configuration/Configuration3x@convert_urls/
153         // https://stackoverflow.com/questions/5196205/disable-tinymce-absolute-to-relative-url-conversions
154         convert_urls: false
155         // 整合七牛上传
156         // images_dataimg_filter(img) {
157         //   setTimeout(() => {
158         //     const $image = $(img);
159         //     $image.removeAttr('width');
160         //     $image.removeAttr('height');
161         //     if ($image[0].height && $image[0].width) {
162         //       $image.attr('data-wscntype', 'image');
163         //       $image.attr('data-wscnh', $image[0].height);
164         //       $image.attr('data-wscnw', $image[0].width);
165         //       $image.addClass('wscnph');
166         //     }
167         //   }, 0);
168         //   return img
169         // },
170         // images_upload_handler(blobInfo, success, failure, progress) {
171         //   progress(0);
172         //   const token = _this.$store.getters.token;
173         //   getToken(token).then(response => {
174         //     const url = response.data.qiniu_url;
175         //     const formData = new FormData();
176         //     formData.append('token', response.data.qiniu_token);
177         //     formData.append('key', response.data.qiniu_key);
178         //     formData.append('file', blobInfo.blob(), url);
179         //     upload(formData).then(() => {
180         //       success(url);
181         //       progress(100);
182         //     })
183         //   }).catch(err => {
184         //     failure('出现未知问题,刷新页面,或者联系程序员')
185         //     console.log(err);
186         //   });
187         // },
188       })
189     },
190     destroyTinymce() {
191       const tinymce = window.tinymce.get(this.tinymceId)
192       if (this.fullscreen) {
193         tinymce.execCommand('mceFullScreen')
194       }
195
196       if (tinymce) {
197         tinymce.destroy()
198       }
199     },
200     setContent(value) {
201       window.tinymce.get(this.tinymceId).setContent(value)
202     },
203     getContent() {
204       window.tinymce.get(this.tinymceId).getContent()
205     },
206     imageSuccessCBK(arr) {
207       arr.forEach(v => window.tinymce.get(this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`))
208     }
209   }
210 }
211 </script>
212
213 <style lang="scss" scoped>
214 .tinymce-container {
215   position: relative;
216   line-height: normal;
217 }
218
219 .tinymce-container {
220   ::v-deep {
221     .mce-fullscreen {
222       z-index: 10000;
223     }
224   }
225 }
226
227 .tinymce-textarea {
228   visibility: hidden;
229   z-index: -1;
230 }
231
232 .editor-custom-btn-container {
233   position: absolute;
234   right: 4px;
235   top: 4px;
236   /*z-index: 2005;*/
237 }
238
239 .fullscreen .editor-custom-btn-container {
240   z-index: 10000;
241   position: fixed;
242 }
243
244 .editor-upload-btn {
245   display: inline-block;
246 }
247 </style>