THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 
 
 

235 Zeilen
6.1 KiB

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