normalArea.vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. <template>
  2. <div>
  3. <article :id="id" :style="fullScreen ? { ...fullPositionStyle, ...styles } : styles" class="article" :class="getClass">
  4. <!-- <textEditor :id="id" :value="inputValue" /> -->
  5. <span v-show="fullScreen" class="changeSizeBtn" @click="changeSize">
  6. <svg-icon
  7. icon-class="icon-sx"
  8. class="icon"
  9. />
  10. </span>
  11. <span v-show="!fullScreen" class="changeSizeBtn" @click="changeSize">
  12. <svg-icon
  13. icon-class="icon-qp"
  14. class="icon"
  15. />
  16. </span>
  17. <editor :id="'tinymce_'+id" ref="editor" v-model="inputValue" :init="init" @input="changeText" />
  18. </article>
  19. </div>
  20. </template>
  21. <script>
  22. import { keepLastIndex, uploadImg } from '@/utils/util'
  23. // import request from '@/utils/request'
  24. import tinymce from 'tinymce/tinymce'
  25. import Editor from '@tinymce/tinymce-vue'
  26. /* S 插件 */
  27. import 'tinymce/plugins/lists'
  28. import 'tinymce/plugins/fullscreen'
  29. import 'tinymce/plugins/textcolor'
  30. import 'tinymce/plugins/contextmenu'
  31. import 'tinymce/plugins/wordcount'
  32. /* E 插件 */
  33. import 'tinymce/themes/silver/theme'
  34. import 'tinymce/icons/default/icons'
  35. // import { uploadImage } from '@/api/common.js'
  36. // import textEditor from './editor'
  37. export default {
  38. components: {
  39. Editor
  40. // textEditor
  41. },
  42. props: {
  43. id: {
  44. type: String,
  45. default: '',
  46. required: true
  47. },
  48. value: {
  49. type: String,
  50. default: '',
  51. required: false
  52. },
  53. defaultPlaceholder: {
  54. type: String,
  55. default: '',
  56. required: false
  57. },
  58. height: {
  59. type: Number,
  60. default: 200,
  61. required: false
  62. },
  63. fullPositionStyle: {
  64. type: Object,
  65. default: () => {
  66. return { }
  67. },
  68. required: false
  69. },
  70. styles: {
  71. type: Object,
  72. default: () => {
  73. return { }
  74. },
  75. required: false
  76. },
  77. bottomMargin: {
  78. type: Boolean,
  79. default: false,
  80. required: false
  81. }
  82. },
  83. data() {
  84. return {
  85. fullScreen: false,
  86. inputValue: '',
  87. edit: false,
  88. init: {
  89. auto_focus: false,
  90. language_url: '/tinymce/langs/zh_CN.js',
  91. language: 'zh_CN',
  92. skin_url: '/tinymce/skins/ui/oxide', // 编辑器需要一个skin才能正常工作,所以要设置一个skin_url指向之前复制出来的skin文件
  93. height: this.height,
  94. browser_spellcheck: true, // 拼写检查
  95. branding: false, // 去水印
  96. elementpath: false, // 禁用编辑器底部的状态栏
  97. statusbar: false, // 隐藏编辑器底部的状态栏
  98. paste_data_images: true, // 允许粘贴图像
  99. menubar: false, // 隐藏最上方menu
  100. fontsize_formats: '14px 16px 18px 20px 24px 26px 28px 30px 32px 36px', // 字体大小
  101. file_picker_types: 'image',
  102. placeholder: this.defaultPlaceholder,
  103. images_upload_credentials: true,
  104. plugins: 'fullscreen lists table textcolor wordcount contextmenu', // 引入插件
  105. toolbar: 'fullscreen bold italic underline strikethrough | fontsizeselect | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent table | undo redo | removeformat formatselect',
  106. table_toolbar: 'tableprops | tableinsertrowbefore tableinsertrowafter tabledeleterow | tableinsertcolbefore tableinsertcolafter tabledeletecol | tablemergecells tablesplitcells'
  107. }
  108. }
  109. },
  110. computed: {
  111. getClass() {
  112. let className = ''
  113. if (this.fullScreen) {
  114. className += 'fullScreen'
  115. }
  116. if (this.bottomMargin) {
  117. className += ' bottomMargin'
  118. }
  119. return className
  120. }
  121. },
  122. watch: {
  123. value: {
  124. handler(newV, oldV) {
  125. this.inputValue = newV
  126. },
  127. immediate: true
  128. },
  129. inputValue: {
  130. handler(newV, oldV) {
  131. this.resetImgSrc(newV)
  132. },
  133. immediate: true
  134. }
  135. },
  136. mounted() {
  137. tinymce.init({ selector: `#tinymce_${this.id}` })
  138. },
  139. methods: {
  140. async resetImgSrc(str) {
  141. if (!str) return
  142. let newStr = str
  143. const imgReg = /<img.*?(?:>|\/>)/gi
  144. const srcReg = /src=[\'\"]?([^\'\"]*)[\'\"]?/i
  145. const imgArr = newStr.match(imgReg)
  146. imgArr && imgArr.map(async t => {
  147. const src = t.match(srcReg)
  148. if (src[1] && src[1].includes('data:image')) {
  149. const newImgUrl = await uploadImg(src[1])
  150. newStr = newStr.replace(src[1], newImgUrl)
  151. this.inputValue = newStr
  152. // 光标最后
  153. this.$nextTick(() => {
  154. const ifra = document.getElementById(`tinymce_${this.id}_ifr`)
  155. keepLastIndex(ifra.contentWindow.document.getElementById(`tinymce`), ifra.contentWindow)
  156. })
  157. }
  158. })
  159. },
  160. changeText(e) { // 富文本内容改变
  161. this.inputValue = e
  162. this.$emit('update:value', this.inputValue)
  163. this.$emit('change', this.inputValue)
  164. },
  165. changeSize() {
  166. this.fullScreen = !this.fullScreen
  167. }
  168. }
  169. }
  170. </script>
  171. <style scoped lang="less">
  172. .article {
  173. position: relative;
  174. .changeSizeBtn {
  175. position: absolute;
  176. top: 3px;
  177. right: 0px;
  178. z-index: 1000;
  179. height: 34px;
  180. width: 24px;
  181. text-align: center;
  182. line-height: 34px;
  183. border-radius: 3px;
  184. .icon {
  185. color: #409eff;
  186. font-weight: 700;
  187. font-size: 18px;
  188. }
  189. &:hover {
  190. background: #c8cbcf;
  191. border: 0;
  192. box-shadow: none;
  193. }
  194. }
  195. }
  196. .fullScreen {
  197. position: fixed;
  198. top: 0px;
  199. bottom: 0;
  200. left: 225px;
  201. right: 0;
  202. z-index: 1000;
  203. height: 100vh!important;
  204. /deep/.tox-tinymce {
  205. height: 100vh!important;
  206. }
  207. &.bottomMargin {
  208. /deep/.tox-tinymce {
  209. height: calc(100vh - 40px)!important;
  210. }
  211. }
  212. }
  213. .editor {
  214. border: 1px solid #666;
  215. }
  216. </style>