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

Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

235 linhas
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>