editor.vue 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <template>
  2. <div>
  3. <article :id="id" :style="styles">
  4. <a id="edit-btn" href="javascript:;" @click="() => styleHandle('bold')">加粗</a>
  5. <a id="edit-btn" href="javascript:;" @click="() => styleHandle('italic')">italic</a>
  6. <a id="edit-btn" href="javascript:;" @click="() => styleHandle('enableInlineTableEditing')">表格</a>
  7. <a id="edit-btn" href="javascript:;" @click="() => styleHandle('backColor', '#666')">背景颜色</a>
  8. <a id="edit-btn" href="javascript:;" @click="() => insertTable()">插入表格</a>
  9. <div :id="'editor_'+id" contenteditable="true" style="display:inline-block;border:1px solid #eee; width:200px; height:200px" class="editor" v-html="value">
  10. <p>12321</p>
  11. </div>
  12. <!-- <div :id="'tinymce_'+id" class="editor" contenteditable="true" style="display:inline-block;border:1px solid #eee; width:300px; height:200px">
  13. <p>12321</p>
  14. </div> -->
  15. <div id="menu">
  16. <div class="menu" @click.stop="getCursorPosition">功能1</div>
  17. <div class="menu">功能2</div>
  18. <div class="menu">功能3</div>
  19. <div class="menu">功能4</div>
  20. <div class="menu">功能5</div>
  21. </div>
  22. </article>
  23. </div>
  24. </template>
  25. <script>
  26. export default {
  27. props: {
  28. id: {
  29. type: String,
  30. default: '',
  31. required: true
  32. },
  33. value: {
  34. type: String,
  35. default: '',
  36. required: false
  37. },
  38. height: {
  39. type: Number,
  40. default: 200,
  41. required: false
  42. },
  43. styles: {
  44. type: Object,
  45. default: () => {
  46. return { }
  47. },
  48. required: false
  49. }
  50. },
  51. data() {
  52. return {
  53. inputValue: '',
  54. edit: false,
  55. init: {
  56. auto_focus: true,
  57. language_url: '/tinymce/langs/zh_CN.js',
  58. language: 'zh_CN',
  59. skin_url: '/tinymce/skins/ui/oxide', // 编辑器需要一个skin才能正常工作,所以要设置一个skin_url指向之前复制出来的skin文件
  60. height: this.height,
  61. browser_spellcheck: true, // 拼写检查
  62. branding: false, // 去水印
  63. elementpath: false, // 禁用编辑器底部的状态栏
  64. statusbar: false, // 隐藏编辑器底部的状态栏
  65. paste_data_images: true, // 允许粘贴图像
  66. menubar: false, // 隐藏最上方menu
  67. fontsize_formats: '14px 16px 18px 20px 24px 26px 28px 30px 32px 36px', // 字体大小
  68. file_picker_types: 'image',
  69. images_upload_credentials: true,
  70. plugins: 'lists table textcolor wordcount contextmenu', // 引入插件
  71. toolbar: 'bold italic underline strikethrough | fontsizeselect | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent table | undo redo | removeformat formatselect',
  72. table_toolbar: 'tableprops | tableinsertrowbefore tableinsertrowafter tabledeleterow | tableinsertcolbefore tableinsertcolafter tabledeletecol | tablemergecells tablesplitcells'
  73. }
  74. }
  75. },
  76. watch: {
  77. value: {
  78. handler(newV, oldV) {
  79. this.inputValue = newV
  80. },
  81. immediate: true
  82. }
  83. },
  84. mounted() {
  85. const editor = document.getElementById(`editor_${this.id}`)
  86. editor.oncontextmenu = function(e) {
  87. // 取消默认的浏览器自带右键 很重要!!
  88. e.preventDefault()
  89. // 获取我们自定义的右键菜单
  90. var menu = document.querySelector('#menu')
  91. // 根据事件对象中鼠标点击的位置,进行定位
  92. menu.style.left = e.clientX + 'px'
  93. menu.style.top = e.clientY + 'px'
  94. // 改变自定义菜单的宽,让它显示出来
  95. menu.style.width = '125px'
  96. }
  97. editor.onclick = function(e) {
  98. console.log('第几列', e.target.cellIndex + 1, '第几行', e.target.parentNode.rowIndex + 1)
  99. const ForeColor = document.queryCommandValue('ForeColor') // 字体颜色
  100. const FontSize = document.queryCommandValue('FontSize') // 字体大小
  101. const bold = document.queryCommandValue('bold') // 是否加粗
  102. const italic = document.queryCommandValue('italic')
  103. const BackgroundColor = document.queryCommandValue('BackColor')
  104. console.log(ForeColor, FontSize, bold, italic, BackgroundColor, 111)
  105. console.log('这是table', e.path[3])
  106. const tableBox = e.path[3]
  107. console.log(tableBox)
  108. // 必须保存一下不然下面会变
  109. // const idx = e.target.cellIndex
  110. // 在一行添加一行
  111. // tableBox.insertRow(e.target.parentNode.rowIndex)
  112. // 插入一列
  113. console.log(tableBox.rows)
  114. for (let i = 0; i < tableBox.rows.length; i++) {
  115. console.log(i, e.target.cellIndex)
  116. // 在之后插入一列
  117. // tableBox.rows[i].insertCell(e.target.cellIndex+1)
  118. // 在之前一列
  119. // 在之前一列插入
  120. // tableBox.rows[i].insertCell(idx)
  121. console.log(tableBox.rows[i])
  122. }
  123. // tableBox.rows[e.target.parentNode.rowIndex].insertCell()
  124. // 用户触发click事件就可以关闭了,因为绑定在window上,按事件冒泡处理,不会影响菜单的功能
  125. document.querySelector('#menu').style.width = 0
  126. }
  127. },
  128. methods: {
  129. changeText(e) { // 富文本内容改变
  130. this.inputValue = e
  131. this.$emit('update:value', this.inputValue)
  132. this.$emit('change', this.inputValue)
  133. },
  134. styleHandle(aCommandName, val) {
  135. // const editor = document.getElementById('cc')
  136. document.execCommand(aCommandName, false, val)
  137. },
  138. insertTable() {
  139. const HTML = `<table border='1' border-collapse: collapse; style='border-color: #666;width:90%'>
  140. <tr>
  141. <td><span>123</span></td>
  142. <td>1</td>
  143. <td>3</td>
  144. </tr>
  145. <tr>
  146. <td><span>1233</span></td>
  147. <td>4</td>
  148. <td>5</td>
  149. </tr>
  150. <tr>
  151. <td><span>1235</span></td>
  152. <td>6</td>
  153. <td>7</td>
  154. </tr>
  155. </table>`
  156. document.execCommand('insertHTML', false, HTML)
  157. },
  158. getCursorPosition(event) {
  159. const editEl = event.target
  160. // return console.log(editEl);
  161. if (editEl.selectionStart || editEl.selectionStart === 0) {
  162. // 非IE浏览器
  163. this.cursorPosition = editEl.selectionStart
  164. } else {
  165. // IE
  166. const range = document.selection.createRange()
  167. range.moveStart('character', -editEl.value.length)
  168. this.cursorPosition = range.text.length
  169. }
  170. console.log(this.cursorPosition)
  171. }
  172. }
  173. }
  174. </script>
  175. <style scoped lang="scss">
  176. .editor {
  177. border: 1px solid #666;
  178. }
  179. #menu{
  180. width: 0; /*设置为0 隐藏自定义菜单*/
  181. height: 125px;
  182. overflow: hidden; /*隐藏溢出的元素*/
  183. box-shadow: 0 1px 1px #888,1px 0 1px #ccc;
  184. position: absolute; /*自定义菜单相对与body元素进行定位*/
  185. }
  186. .menu{
  187. width: 130px;
  188. height: 25px;
  189. line-height: 25px;
  190. padding: 0 10px;
  191. cursor: pointer;
  192. }
  193. </style>