normalArea.vue 6.0 KB

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