vue-editify 0.0.51 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/Editify.vue CHANGED
@@ -1,11 +1,11 @@
1
1
  <template>
2
- <div class="editify" :class="{ fullheight: height === true && !isFullScreen, fullscreen: isFullScreen }">
2
+ <div class="editify" :class="{ fullscreen: isFullScreen }">
3
3
  <!-- 菜单区域 -->
4
- <Menu v-if="menuConfig.use" :config="menuConfig" :disabled="disabled || !canUseMenu" :color="color" ref="menu"></Menu>
4
+ <Menu v-if="menuConfig.use" :config="menuConfig" :color="color" ref="menu"></Menu>
5
5
  <!-- 编辑层,与编辑区域宽高相同必须适配 -->
6
6
  <div ref="body" class="editify-body" :class="{ border: showBorder, menu_inner: menuConfig.use && menuConfig.mode == 'inner' }" :data-editify-uid="uid">
7
7
  <!-- 编辑器 -->
8
- <div ref="content" class="editify-content" :class="{ placeholder: showPlaceholder, disabled: disabled }" :style="contentStyle" @keydown="handleEditorKeydown" @click="handleEditorClick" @compositionstart="isInputChinese = true" @compositionend="isInputChinese = false" :data-editify-placeholder="placeholder"></div>
8
+ <div ref="content" class="editify-content" :class="{ placeholder: showPlaceholder, disabled: disabled }" @keydown="handleEditorKeydown" @click="handleEditorClick" @compositionstart="isInputChinese = true" @compositionend="isInputChinese = false" :data-editify-placeholder="placeholder"></div>
9
9
  <!-- 代码视图 -->
10
10
  <textarea v-if="isSourceView" :value="value" readonly class="editify-source" />
11
11
  <!-- 工具条 -->
@@ -21,16 +21,18 @@
21
21
  <script>
22
22
  import { getCurrentInstance } from 'vue'
23
23
  import { AlexEditor, AlexElement } from 'alex-editor'
24
- import Dap from 'dap-util'
25
- import { pasteKeepData, editorProps, parseList, mediaHandle, tableHandle, preHandle, blockToParagraph, blockToList, blockIsList, getButtonOptionsConfig, getToolbarConfig, getMenuConfig, mergeObject, blockIsTask, blockToTask } from './core'
26
- import Toolbar from './components/bussiness/Toolbar'
24
+ import { element as DapElement, event as DapEvent, data as DapData, number as DapNumber, color as DapColor } from 'dap-util'
25
+ import { pasteKeepData, editorProps, mergeObject, getToolbarConfig, getMenuConfig } from './core/tool'
26
+ import { parseList, orderdListHandle, mediaHandle, tableHandle, preHandle, specialInblockHandle } from './core/rule'
27
+ import { isTask, elementToParagraph, getCurrentParsedomElement, hasTableInRange, hasLinkInRange, hasPreInRange, hasImageInRange, hasVideoInRange, setIndentIncrease, setIndentDecrease, insertImage, insertVideo } from './core/function'
27
28
  import Tooltip from './components/base/Tooltip'
28
- import Menu from './components/bussiness/Menu'
29
+ import Toolbar from './components/Toolbar'
30
+ import Menu from './components/Menu'
29
31
 
30
32
  export default {
31
33
  name: 'editify',
32
34
  props: { ...editorProps },
33
- emits: ['update:modelValue', 'focus', 'blur', 'change', 'keydown', 'insertparagraph', 'rangeupdate', 'copy', 'cut', 'paste-text', 'paste-html', 'paste-image', 'paste-video', 'before-render', 'after-render'],
35
+ emits: ['update:modelValue', 'focus', 'blur', 'change', 'keydown', 'insertparagraph', 'rangeupdate', 'updateview'],
34
36
  setup() {
35
37
  const instance = getCurrentInstance()
36
38
  return {
@@ -39,14 +41,8 @@ export default {
39
41
  },
40
42
  data() {
41
43
  return {
42
- //编辑器对象
43
- editor: null,
44
44
  //是否编辑器内部修改值
45
45
  isModelChange: false,
46
- //是否代码视图
47
- isSourceView: false,
48
- //是否全屏
49
- isFullScreen: false,
50
46
  //是否正在输入中文
51
47
  isInputChinese: false,
52
48
  //表格列宽拖拽记录数据
@@ -63,12 +59,22 @@ export default {
63
59
  //类型
64
60
  type: 'text'
65
61
  },
66
- //rangeUpdate更新延时器
67
- updateTimer: null,
62
+
63
+ /** 以下是可对外使用的属性 */
64
+
65
+ //编辑器对象
66
+ editor: null,
67
+ //是否代码视图
68
+ isSourceView: false,
69
+ //是否全屏
70
+ isFullScreen: false,
68
71
  //菜单栏是否可以使用标识
69
72
  canUseMenu: false,
70
- //手动设定的编辑器编辑区域高度
71
- contentHeight: 0
73
+ //光标选取范围内的元素数组
74
+ dataRangeCaches: {
75
+ flatList: [],
76
+ list: []
77
+ }
72
78
  }
73
79
  },
74
80
  computed: {
@@ -83,7 +89,7 @@ export default {
83
89
  },
84
90
  //编辑器的纯文本值
85
91
  textValue() {
86
- return Dap.element.string2dom(`<div>${this.value}</div>`).innerText
92
+ return DapElement.string2dom(`<div>${this.value}</div>`).innerText
87
93
  },
88
94
  //是否显示占位符
89
95
  showPlaceholder() {
@@ -103,15 +109,6 @@ export default {
103
109
  }
104
110
  return this.border
105
111
  },
106
- //编辑器样式设置
107
- contentStyle() {
108
- if (this.height === true || this.isFullScreen) {
109
- return {
110
- height: this.contentHeight + 'px'
111
- }
112
- }
113
- return this.autoheight ? { minHeight: this.height } : { height: this.height }
114
- },
115
112
  //最终生效的工具栏配置
116
113
  toolbarConfig() {
117
114
  return mergeObject(getToolbarConfig(this.$editTrans, this.$editLocale), this.toolbar || {})
@@ -152,19 +149,12 @@ export default {
152
149
  }
153
150
  }
154
151
  },
155
- //全屏切换
156
- isFullScreen() {
157
- this.$nextTick(() => {
158
- this.setContentHeight()
159
- })
160
- },
161
- //监听height为true
162
- height: {
163
- immediate: true,
164
- handler: function () {
165
- this.$nextTick(() => {
166
- this.setContentHeight()
167
- })
152
+ //监听disabled
153
+ disabled(newValue) {
154
+ if (newValue) {
155
+ this.editor.setDisabled()
156
+ } else {
157
+ this.editor.setEnabled()
168
158
  }
169
159
  }
170
160
  },
@@ -174,71 +164,17 @@ export default {
174
164
  //监听滚动隐藏工具条
175
165
  this.handleScroll()
176
166
  //鼠标按下监听
177
- Dap.event.on(document.documentElement, `mousedown.editify_${this.uid}`, this.documentMouseDown)
167
+ DapEvent.on(document.documentElement, `mousedown.editify_${this.uid}`, this.documentMouseDown)
178
168
  //鼠标移动监听
179
- Dap.event.on(document.documentElement, `mousemove.editify_${this.uid}`, this.documentMouseMove)
169
+ DapEvent.on(document.documentElement, `mousemove.editify_${this.uid}`, this.documentMouseMove)
180
170
  //鼠标松开监听
181
- Dap.event.on(document.documentElement, `mouseup.editify_${this.uid}`, this.documentMouseUp)
171
+ DapEvent.on(document.documentElement, `mouseup.editify_${this.uid}`, this.documentMouseUp)
182
172
  //鼠标点击箭头
183
- Dap.event.on(document.documentElement, `click.editify_${this.uid}`, this.documentClick)
173
+ DapEvent.on(document.documentElement, `click.editify_${this.uid}`, this.documentClick)
184
174
  //监听窗口改变
185
- Dap.event.on(window, `resize.editify_${this.uid}`, () => {
186
- this.setVideoHeight()
187
- this.setContentHeight()
188
- })
175
+ DapEvent.on(window, `resize.editify_${this.uid}`, this.setVideoHeight)
189
176
  },
190
177
  methods: {
191
- //初始创建编辑器
192
- createEditor() {
193
- //创建编辑器
194
- this.editor = new AlexEditor(this.$refs.content, {
195
- value: this.value,
196
- disabled: this.disabled,
197
- renderRules: [
198
- parseList,
199
- mediaHandle,
200
- tableHandle,
201
- el => {
202
- preHandle.apply(this.editor, [el, this.toolbarConfig?.use && this.toolbarConfig?.codeBlock?.languages?.show, this.toolbarConfig?.codeBlock?.languages.options])
203
- },
204
- ...this.renderRules
205
- ],
206
- allowCopy: this.allowCopy,
207
- allowPaste: this.allowPaste,
208
- allowCut: this.allowCut,
209
- allowPasteHtml: this.allowPasteHtml,
210
- allowPasteHtml: this.allowPasteHtml,
211
- customImagePaste: this.customImagePaste,
212
- customVideoPaste: this.customVideoPaste,
213
- customMerge: this.handleCustomMerge,
214
- customParseNode: this.handleCustomParseNode
215
- })
216
- //编辑器渲染后会有一个渲染过程,会改变内容,因此重新获取内容的值来设置value
217
- this.internalModify(this.editor.value)
218
- //设置监听事件
219
- this.editor.on('change', this.handleEditorChange)
220
- this.editor.on('focus', this.handleEditorFocus)
221
- this.editor.on('blur', this.handleEditorBlur)
222
- this.editor.on('insertParagraph', this.handleInsertParagraph)
223
- this.editor.on('rangeUpdate', this.handleRangeUpdate)
224
- this.editor.on('copy', this.handleCopy)
225
- this.editor.on('cut', this.handleCut)
226
- this.editor.on('pasteText', this.handlePasteText)
227
- this.editor.on('pasteHtml', this.handlePasteHtml)
228
- this.editor.on('pasteImage', this.handlePasteImage)
229
- this.editor.on('pasteVideo', this.handlePasteVideo)
230
- this.editor.on('deleteInStart', this.handleDeleteInStart)
231
- this.editor.on('deleteComplete', this.handleDeleteComplete)
232
- this.editor.on('beforeRender', this.handleBeforeRender)
233
- this.editor.on('afterRender', this.handleAfterRender)
234
- //格式化和dom渲染
235
- this.editor.formatElementStack()
236
- this.editor.domRender()
237
- //自动获取焦点
238
- if (this.autofocus && !this.isSourceView && !this.disabled) {
239
- this.collapseToEnd()
240
- }
241
- },
242
178
  //编辑器内部修改值的方法
243
179
  internalModify(val) {
244
180
  this.isModelChange = true
@@ -247,10 +183,15 @@ export default {
247
183
  this.isModelChange = false
248
184
  })
249
185
  },
186
+ //隐藏工具条
187
+ hideToolbar() {
188
+ this.toolbarOptions.show = false
189
+ this.toolbarOptions.node = null
190
+ },
250
191
  //监听滚动隐藏工具条
251
192
  handleScroll() {
252
193
  const setScroll = el => {
253
- Dap.event.on(el, `scroll.editify_${this.uid}`, () => {
194
+ DapEvent.on(el, `scroll.editify_${this.uid}`, () => {
254
195
  if (this.toolbarConfig.use && this.toolbarOptions.show) {
255
196
  this.hideToolbar()
256
197
  }
@@ -264,7 +205,7 @@ export default {
264
205
  //移除上述滚动事件的监听
265
206
  removeScrollHandle() {
266
207
  const removeScroll = el => {
267
- Dap.event.off(el, `scroll.editify_${this.uid}`)
208
+ DapEvent.off(el, `scroll.editify_${this.uid}`)
268
209
  if (el.parentNode) {
269
210
  removeScroll(el.parentNode)
270
211
  }
@@ -272,17 +213,17 @@ export default {
272
213
  removeScroll(this.$refs.content)
273
214
  },
274
215
  //工具条显示判断
275
- handleToolbar(useCache = false) {
216
+ handleToolbar() {
276
217
  if (this.disabled || this.isSourceView) {
277
218
  return
278
219
  }
279
220
  this.hideToolbar()
280
221
  this.$nextTick(() => {
281
- const table = this.getCurrentParsedomElement('table', useCache)
282
- const pre = this.getCurrentParsedomElement('pre', true)
283
- const link = this.getCurrentParsedomElement('a', true)
284
- const image = this.getCurrentParsedomElement('img', true)
285
- const video = this.getCurrentParsedomElement('video', true)
222
+ const table = getCurrentParsedomElement(this, 'table')
223
+ const pre = getCurrentParsedomElement(this, 'pre')
224
+ const link = getCurrentParsedomElement(this, 'a')
225
+ const image = getCurrentParsedomElement(this, 'img')
226
+ const video = getCurrentParsedomElement(this, 'video')
286
227
  if (link) {
287
228
  this.toolbarOptions.type = 'link'
288
229
  this.toolbarOptions.node = `[data-editify-uid="${this.uid}"] [data-editify-element="${link.key}"]`
@@ -324,10 +265,10 @@ export default {
324
265
  this.toolbarOptions.show = true
325
266
  }
326
267
  } else {
327
- const result = this.editor.getElementsByRange(true).flatIncludes.filter(item => {
268
+ const result = this.dataRangeCaches.flatList.filter(item => {
328
269
  return item.element.isText()
329
270
  })
330
- if (result.length && !this.hasTable(true) && !this.hasPreStyle(true) && !this.hasLink(true) && !this.hasImage(true) && !this.hasVideo(true)) {
271
+ if (result.length && !hasTableInRange(this) && !hasPreInRange(this) && !hasLinkInRange(this) && !hasImageInRange(this) && !hasVideoInRange(this)) {
331
272
  this.toolbarOptions.type = 'text'
332
273
  if (this.toolbarOptions.show) {
333
274
  this.$refs.toolbar.$refs.layer.setPosition()
@@ -338,41 +279,67 @@ export default {
338
279
  }
339
280
  })
340
281
  },
341
- //重新定义编辑器合并元素的逻辑
342
- handleCustomMerge(ele, preEle) {
343
- const uneditable = preEle.getUneditableElement()
344
- if (uneditable) {
345
- uneditable.toEmpty()
346
- } else {
347
- preEle.children.push(...ele.children)
348
- preEle.children.forEach(item => {
349
- item.parent = preEle
350
- })
351
- ele.children = null
352
- }
282
+ //设定编辑器内的视频高度
283
+ setVideoHeight() {
284
+ this.$refs.content.querySelectorAll('video').forEach(video => {
285
+ video.style.height = video.offsetWidth / this.videoRatio + 'px'
286
+ })
353
287
  },
354
- //针对node转为元素进行额外的处理
355
- handleCustomParseNode(ele) {
356
- if (ele.parsedom == 'code') {
357
- ele.parsedom = 'span'
358
- const marks = {
359
- 'data-editify-code': true
360
- }
361
- if (ele.hasMarks()) {
362
- Object.assign(ele.marks, marks)
363
- } else {
364
- ele.marks = marks
365
- }
366
- }
367
- if (typeof this.customParseNode == 'function') {
368
- ele = this.customParseNode.apply(this, [ele])
288
+ //初始创建编辑器
289
+ createEditor() {
290
+ //创建编辑器
291
+ this.editor = new AlexEditor(this.$refs.content, {
292
+ value: this.value,
293
+ disabled: this.disabled,
294
+ renderRules: [
295
+ el => {
296
+ parseList(this.editor, el)
297
+ },
298
+ el => {
299
+ orderdListHandle(this.editor, el)
300
+ },
301
+ el => {
302
+ mediaHandle(this.editor, el)
303
+ },
304
+ el => {
305
+ tableHandle(this.editor, el)
306
+ },
307
+ el => {
308
+ preHandle(this.editor, el, this.toolbarConfig?.use && this.toolbarConfig?.codeBlock?.languages?.show, this.toolbarConfig?.codeBlock?.languages.options)
309
+ },
310
+ el => {
311
+ specialInblockHandle(this.editor, el)
312
+ }
313
+ ],
314
+ allowCopy: this.allowCopy,
315
+ allowPaste: this.allowPaste,
316
+ allowCut: this.allowCut,
317
+ allowPasteHtml: this.allowPasteHtml,
318
+ allowPasteHtml: this.allowPasteHtml,
319
+ customImagePaste: this.handleCustomImagePaste,
320
+ customVideoPaste: this.handleCustomVideoPaste,
321
+ customMerge: this.handleCustomMerge,
322
+ customParseNode: this.handleCustomParseNode
323
+ })
324
+ //编辑器渲染后会有一个渲染过程,会改变内容,因此重新获取内容的值来设置value
325
+ this.internalModify(this.editor.value)
326
+ //设置监听事件
327
+ this.editor.on('change', this.handleEditorChange)
328
+ this.editor.on('focus', this.handleEditorFocus)
329
+ this.editor.on('blur', this.handleEditorBlur)
330
+ this.editor.on('insertParagraph', this.handleInsertParagraph)
331
+ this.editor.on('rangeUpdate', this.handleRangeUpdate)
332
+ this.editor.on('pasteHtml', this.handlePasteHtml)
333
+ this.editor.on('deleteInStart', this.handleDeleteInStart)
334
+ this.editor.on('deleteComplete', this.handleDeleteComplete)
335
+ this.editor.on('afterRender', this.handleAfterRender)
336
+ //格式化和dom渲染
337
+ this.editor.formatElementStack()
338
+ this.editor.domRender()
339
+ //自动获取焦点
340
+ if (this.autofocus && !this.isSourceView && !this.disabled) {
341
+ this.collapseToEnd()
369
342
  }
370
- return ele
371
- },
372
- //隐藏工具条
373
- hideToolbar() {
374
- this.toolbarOptions.show = false
375
- this.toolbarOptions.node = null
376
343
  },
377
344
  //鼠标在页面按下:处理表格拖拽改变列宽和菜单栏是否使用判断
378
345
  documentMouseDown(e) {
@@ -380,9 +347,9 @@ export default {
380
347
  return
381
348
  }
382
349
  //鼠标在编辑器内按下
383
- if (Dap.element.isContains(this.$refs.content, e.target)) {
350
+ if (DapElement.isContains(this.$refs.content, e.target)) {
384
351
  const elm = e.target
385
- const key = Dap.data.get(elm, 'data-alex-editor-key')
352
+ const key = DapData.get(elm, 'data-alex-editor-key')
386
353
  if (key) {
387
354
  const element = this.editor.getElementByKey(key)
388
355
  if (element && element.parsedom == 'td') {
@@ -391,7 +358,7 @@ export default {
391
358
  if (element.parent.children[length - 1].isEqual(element)) {
392
359
  return
393
360
  }
394
- const rect = Dap.element.getElementBounding(elm)
361
+ const rect = DapElement.getElementBounding(elm)
395
362
  //在可拖拽范围内
396
363
  if (e.pageX >= Math.abs(rect.left + elm.offsetWidth - 5) && e.pageX <= Math.abs(rect.left + elm.offsetWidth + 5)) {
397
364
  this.tableColumnResizeParams.element = element
@@ -401,7 +368,7 @@ export default {
401
368
  }
402
369
  }
403
370
  //如果点击了除编辑器外的地方,菜单栏不可使用
404
- if (!Dap.element.isContains(this.$el, e.target) && !this.isSourceView) {
371
+ if (!DapElement.isContains(this.$el, e.target) && !this.isSourceView) {
405
372
  this.canUseMenu = false
406
373
  }
407
374
  },
@@ -413,7 +380,7 @@ export default {
413
380
  if (!this.tableColumnResizeParams.element) {
414
381
  return
415
382
  }
416
- const table = this.getCurrentParsedomElement('table')
383
+ const table = getCurrentParsedomElement(this, 'table')
417
384
  if (!table) {
418
385
  return
419
386
  }
@@ -429,14 +396,14 @@ export default {
429
396
  this.tableColumnResizeParams.start = e.pageX
430
397
  },
431
398
  //鼠标在页面松开:处理表格拖拽改变列宽
432
- documentMouseUp(e) {
399
+ documentMouseUp() {
433
400
  if (this.disabled) {
434
401
  return
435
402
  }
436
403
  if (!this.tableColumnResizeParams.element) {
437
404
  return
438
405
  }
439
- const table = this.getCurrentParsedomElement('table')
406
+ const table = getCurrentParsedomElement(this, 'table')
440
407
  if (!table) {
441
408
  return
442
409
  }
@@ -461,17 +428,17 @@ export default {
461
428
  if (this.disabled) {
462
429
  return
463
430
  }
464
- //鼠标在编辑器内按下
465
- if (Dap.element.isContains(this.$refs.content, e.target)) {
431
+ //鼠标在编辑器内点击
432
+ if (DapElement.isContains(this.$refs.content, e.target)) {
466
433
  const elm = e.target
467
- const key = Dap.data.get(elm, 'data-alex-editor-key')
434
+ const key = DapData.get(elm, 'data-alex-editor-key')
468
435
  if (key) {
469
436
  const element = this.editor.getElementByKey(key)
470
437
  //如果是任务列表元素
471
- if (blockIsTask(element)) {
472
- const rect = Dap.element.getElementBounding(elm)
438
+ if (isTask(element)) {
439
+ const rect = DapElement.getElementBounding(elm)
473
440
  //在复选框范围内
474
- if (e.pageX >= Math.abs(rect.left) && e.pageX <= Math.abs(rect.left + 16) && e.pageY >= Math.abs(rect.top + elm.offsetHeight / 2 - 8) && e.pageY <= Math.abs(rect.top + elm.offsetHeight / 2 + 8)) {
441
+ if (e.pageX >= Math.abs(rect.left) && e.pageX <= Math.abs(rect.left + 16) && e.pageY >= Math.abs(rect.top + 2) && e.pageY <= Math.abs(rect.top + 18)) {
475
442
  //取消勾选
476
443
  if (element.marks['data-editify-task'] == 'checked') {
477
444
  element.marks['data-editify-task'] = 'uncheck'
@@ -493,6 +460,99 @@ export default {
493
460
  }
494
461
  }
495
462
  },
463
+ //自定义图片粘贴
464
+ async handleCustomImagePaste(url) {
465
+ const newUrl = await this.customImagePaste.apply(this, [url])
466
+ if (newUrl) {
467
+ insertImage(this, newUrl)
468
+ }
469
+ },
470
+ //自定义视频粘贴
471
+ async handleCustomVideoPaste(url) {
472
+ const newUrl = await this.customVideoPaste.apply(this, [url])
473
+ if (newUrl) {
474
+ insertVideo(this, newUrl)
475
+ }
476
+ },
477
+ //重新定义编辑器合并元素的逻辑
478
+ handleCustomMerge(ele, preEle) {
479
+ const uneditable = preEle.getUneditableElement()
480
+ if (uneditable) {
481
+ uneditable.toEmpty()
482
+ } else {
483
+ preEle.children.push(...ele.children)
484
+ preEle.children.forEach(item => {
485
+ item.parent = preEle
486
+ })
487
+ ele.children = null
488
+ }
489
+ },
490
+ //针对node转为元素进行额外的处理
491
+ handleCustomParseNode(ele) {
492
+ if (ele.parsedom == 'code') {
493
+ ele.parsedom = 'span'
494
+ const marks = {
495
+ 'data-editify-code': true
496
+ }
497
+ if (ele.hasMarks()) {
498
+ Object.assign(ele.marks, marks)
499
+ } else {
500
+ ele.marks = marks
501
+ }
502
+ }
503
+ if (typeof this.customParseNoe == 'function') {
504
+ ele = this.customParseNoe.apply(this, [ele])
505
+ }
506
+ return ele
507
+ },
508
+ //编辑区域键盘按下:设置缩进快捷键
509
+ handleEditorKeydown(e) {
510
+ if (this.disabled) {
511
+ return
512
+ }
513
+ //增加缩进
514
+ if (e.keyCode == 9 && !e.metaKey && !e.shiftKey && !e.ctrlKey && !e.altKey) {
515
+ e.preventDefault()
516
+ if (!hasTableInRange(this)) {
517
+ setIndentIncrease(this)
518
+ this.editor.formatElementStack()
519
+ this.editor.domRender()
520
+ this.editor.rangeRender()
521
+ }
522
+ }
523
+ //减少缩进
524
+ else if (e.keyCode == 9 && !e.metaKey && e.shiftKey && !e.ctrlKey && !e.altKey) {
525
+ e.preventDefault()
526
+ if (!hasTableInRange(this)) {
527
+ setIndentDecrease(this)
528
+ this.editor.formatElementStack()
529
+ this.editor.domRender()
530
+ this.editor.rangeRender()
531
+ }
532
+ }
533
+ //自定义键盘按下操作
534
+ this.$emit('keydown', e)
535
+ },
536
+ //点击编辑器:处理图片和视频的光标聚集
537
+ handleEditorClick(e) {
538
+ if (this.disabled || this.isSourceView) {
539
+ return
540
+ }
541
+ const node = e.target
542
+ //点击的是图片或者视频
543
+ if (node.nodeName.toLocaleLowerCase() == 'img' || node.nodeName.toLocaleLowerCase() == 'video') {
544
+ const key = Number(node.getAttribute('data-editify-element'))
545
+ if (DapNumber.isNumber(key)) {
546
+ const element = this.editor.getElementByKey(key)
547
+ if (!this.editor.range) {
548
+ this.editor.initRange()
549
+ }
550
+ this.editor.range.anchor.moveToStart(element)
551
+ this.editor.range.focus.moveToEnd(element)
552
+ this.editor.rangeRender()
553
+ }
554
+ }
555
+ },
496
556
  //编辑器的值更新
497
557
  handleEditorChange(newVal, oldVal) {
498
558
  if (this.disabled) {
@@ -530,7 +590,7 @@ export default {
530
590
  //编辑区域边框颜色
531
591
  this.$refs.body.style.borderColor = this.color
532
592
  //转换颜色值
533
- const rgb = Dap.color.hex2rgb(this.color)
593
+ const rgb = DapColor.hex2rgb(this.color)
534
594
  //菜单栏模式为inner
535
595
  if (this.menuConfig.use && this.menuConfig.mode == 'inner') {
536
596
  //编辑区域除顶部边框的阴影
@@ -554,56 +614,15 @@ export default {
554
614
  //获取焦点时可以使用菜单栏
555
615
  setTimeout(() => {
556
616
  this.canUseMenu = true
617
+ this.$emit('focus', val)
557
618
  }, 0)
558
- this.$emit('focus', val)
559
- },
560
- //编辑区域键盘按下
561
- handleEditorKeydown(e) {
562
- if (this.disabled) {
563
- return
564
- }
565
- //增加缩进
566
- if (e.keyCode == 9 && !e.metaKey && !e.shiftKey && !e.ctrlKey && !e.altKey) {
567
- e.preventDefault()
568
- this.setIndentIncrease()
569
- }
570
- //减少缩进
571
- else if (e.keyCode == 9 && !e.metaKey && e.shiftKey && !e.ctrlKey && !e.altKey) {
572
- e.preventDefault()
573
- this.setIndentDecrease()
574
- }
575
- //自定义键盘按下操作
576
- this.$emit('keydown', e)
577
- },
578
- //点击编辑器
579
- handleEditorClick(e) {
580
- if (this.disabled || this.isSourceView) {
581
- return
582
- }
583
- const node = e.target
584
- //点击的是图片或者视频
585
- if (node.nodeName.toLocaleLowerCase() == 'img' || node.nodeName.toLocaleLowerCase() == 'video') {
586
- const key = node.getAttribute('data-editify-element')
587
- if (key) {
588
- const element = this.editor.getElementByKey(key)
589
- if (!this.editor.range) {
590
- this.editor.initRange()
591
- }
592
- this.editor.range.anchor.moveToStart(element)
593
- this.editor.range.focus.moveToEnd(element)
594
- this.editor.rangeRender()
595
- }
596
- }
597
619
  },
598
620
  //编辑器换行
599
621
  handleInsertParagraph(element, previousElement) {
600
622
  //前一个块元素如果是只包含换行符的元素,并且当前块元素也是包含换行符的元素,则当前块元素转为段落
601
623
  if (previousElement.isOnlyHasBreak() && element.isOnlyHasBreak()) {
602
- if (!previousElement.isBlock()) {
603
- previousElement.convertToBlock()
604
- }
605
624
  if (previousElement.parsedom != AlexElement.BLOCK_NODE) {
606
- blockToParagraph(previousElement)
625
+ elementToParagraph(previousElement)
607
626
  this.editor.range.anchor.moveToStart(previousElement)
608
627
  this.editor.range.focus.moveToStart(previousElement)
609
628
  element.toEmpty()
@@ -616,41 +635,31 @@ export default {
616
635
  if (this.disabled) {
617
636
  return
618
637
  }
638
+
639
+ //如果没有range禁用菜单栏
619
640
  this.canUseMenu = !!this.editor.range
641
+
642
+ //没有range直接返回
620
643
  if (!this.editor.range) {
621
644
  return
622
645
  }
623
- if (this.updateTimer) {
624
- clearTimeout(this.updateTimer)
625
- }
626
- this.updateTimer = setTimeout(() => {
627
- //如果使用工具条或者菜单栏
628
- if (this.toolbarConfig.use || this.menuConfig.use) {
629
- //先获取选区内的元素设置内部缓存
630
- this.editor.getElementsByRange()
631
- //如果使用工具条
632
- if (this.toolbarConfig.use) {
633
- this.handleToolbar(true)
634
- }
635
- //如果使用菜单栏
636
- if (this.menuConfig.use) {
637
- this.$refs.menu.handleRangeUpdate(true)
638
- }
646
+
647
+ //获取光标选取范围内的元素数据,并且进行缓存
648
+ this.dataRangeCaches = this.editor.getElementsByRange()
649
+
650
+ //如果使用工具条或者菜单栏
651
+ if (this.toolbarConfig.use || this.menuConfig.use) {
652
+ //如果使用工具条
653
+ if (this.toolbarConfig.use) {
654
+ this.handleToolbar()
639
655
  }
640
- }, 200)
641
- this.$emit('rangeupdate', this.value)
642
- },
643
- //编辑器复制
644
- handleCopy(text, html) {
645
- this.$emit('copy', text, html)
646
- },
647
- //编辑器剪切
648
- handleCut(text, html) {
649
- this.$emit('cut', text, html)
650
- },
651
- //编辑器粘贴纯文本
652
- handlePasteText(data) {
653
- this.$emit('paste-text', data)
656
+ //如果使用菜单栏
657
+ if (this.menuConfig.use) {
658
+ this.$refs.menu.handleRangeUpdate()
659
+ }
660
+ }
661
+
662
+ this.$emit('rangeupdate')
654
663
  },
655
664
  //编辑器粘贴html
656
665
  handlePasteHtml(elements) {
@@ -677,20 +686,11 @@ export default {
677
686
  el.styles = styles
678
687
  }
679
688
  })
680
- this.$emit('paste-html', elements)
681
689
  },
682
- //编辑器粘贴图片
683
- handlePasteImage(url) {
684
- this.$emit('paste-image', url)
685
- },
686
- //编辑器粘贴视频
687
- handlePasteVideo(url) {
688
- this.$emit('paste-video', url)
689
- },
690
- //编辑器部分删除情景
690
+ //编辑器部分删除情景(在编辑器起始处)
691
691
  handleDeleteInStart(element) {
692
692
  if (element.isBlock()) {
693
- blockToParagraph(element)
693
+ elementToParagraph(element)
694
694
  }
695
695
  },
696
696
  //编辑器删除完成后事件
@@ -700,39 +700,12 @@ export default {
700
700
  uneditable.toEmpty()
701
701
  }
702
702
  },
703
- //编辑器dom渲染之前
704
- handleBeforeRender() {
705
- this.$emit('before-render')
706
- },
707
703
  //编辑器dom渲染
708
704
  handleAfterRender() {
709
705
  //设定视频高度
710
706
  this.setVideoHeight()
711
- //触发事件
712
- this.$emit('after-render')
713
- },
714
- //设定视频高度
715
- setVideoHeight() {
716
- this.$refs.content.querySelectorAll('video').forEach(video => {
717
- video.style.height = video.offsetWidth / this.videoRatio + 'px'
718
- })
719
- },
720
- //设置编辑器主体高度
721
- setContentHeight() {
722
- if (this.height === true || this.isFullScreen) {
723
- let height = this.$el.offsetHeight
724
- if (this.menuConfig.use) {
725
- height -= this.$refs.menu.$el.offsetHeight
726
- }
727
- if (this.showWordLength) {
728
- height -= this.$refs.footer.offsetHeight
729
- }
730
- if (this.$refs.menu.menuMode == 'default') {
731
- height -= 10
732
- }
733
- //这里减去2px是因为body设了padding:1px
734
- this.contentHeight = height - 2
735
- }
707
+
708
+ this.$emit('updateview')
736
709
  },
737
710
 
738
711
  //api:光标设置到文档底部
@@ -742,7 +715,7 @@ export default {
742
715
  }
743
716
  this.editor.collapseToEnd()
744
717
  this.editor.rangeRender()
745
- Dap.element.setScrollTop({
718
+ DapElement.setScrollTop({
746
719
  el: this.$refs.content,
747
720
  number: 1000000,
748
721
  time: 0
@@ -756,364 +729,13 @@ export default {
756
729
  this.editor.collapseToStart()
757
730
  this.editor.rangeRender()
758
731
  this.$nextTick(() => {
759
- Dap.element.setScrollTop({
732
+ DapElement.setScrollTop({
760
733
  el: this.$refs.content,
761
734
  number: 0,
762
735
  time: 0
763
736
  })
764
737
  })
765
738
  },
766
- //api:获取某个元素是否在某个标签元素下,如果是返回该标签元素,否则返回null
767
- getParsedomElementByElement(element, parsedom) {
768
- if (element.isBlock()) {
769
- return element.parsedom == parsedom ? element : null
770
- }
771
- if (!element.isText() && element.parsedom == parsedom) {
772
- return element
773
- }
774
- return this.getParsedomElementByElement(element.parent, parsedom)
775
- },
776
- //api:获取光标是否在指定标签元素下,如果是返回该标签元素,否则返回null
777
- getCurrentParsedomElement(parsedom, useCache = false) {
778
- if (this.disabled) {
779
- return null
780
- }
781
- if (!this.editor.range) {
782
- return null
783
- }
784
- if (this.editor.range.anchor.element.isEqual(this.editor.range.focus.element)) {
785
- return this.getParsedomElementByElement(this.editor.range.anchor.element, parsedom)
786
- }
787
- const arr = this.editor.getElementsByRange(useCache).includes.map(item => {
788
- return this.getParsedomElementByElement(item.element, parsedom)
789
- })
790
- let hasNull = arr.some(el => {
791
- return el == null
792
- })
793
- //如果存在null,则表示有的选区元素不在指定标签下,返回null
794
- if (hasNull) {
795
- return null
796
- }
797
- //如果只有一个元素,则返回该元素
798
- if (arr.length == 1) {
799
- return arr[0]
800
- }
801
- //默认数组中的元素都相等
802
- let flag = true
803
- for (let i = 1; i < arr.length; i++) {
804
- if (!arr[i].isEqual(arr[0])) {
805
- flag = false
806
- break
807
- }
808
- }
809
- //如果相等,则返回该元素
810
- if (flag) {
811
- return arr[0]
812
- }
813
- return null
814
- },
815
- //api:删除光标所在的指定标签元素
816
- deleteByParsedom(parsedom, isRender = true, useCache = false) {
817
- if (this.disabled) {
818
- return
819
- }
820
- if (!this.editor.range) {
821
- return
822
- }
823
- const element = this.getCurrentParsedomElement(parsedom, useCache)
824
- if (element) {
825
- element.toEmpty()
826
- if (isRender) {
827
- this.editor.formatElementStack()
828
- this.editor.domRender()
829
- this.editor.rangeRender()
830
- }
831
- }
832
- },
833
- //api:当光标在链接上时可以移除链接
834
- removeLink(isRender = true, useCache = false) {
835
- if (this.disabled) {
836
- return
837
- }
838
- if (!this.editor.range) {
839
- return
840
- }
841
- const link = this.getCurrentParsedomElement('a', useCache)
842
- if (link) {
843
- link.parsedom = AlexElement.TEXT_NODE
844
- delete link.marks.target
845
- delete link.marks.href
846
- if (isRender) {
847
- this.editor.formatElementStack()
848
- this.editor.domRender()
849
- this.editor.rangeRender()
850
- }
851
- }
852
- },
853
- //api:设置标题
854
- setHeading(parsedom, isRender = true, useCache = false) {
855
- if (this.disabled) {
856
- return
857
- }
858
- if (!this.editor.range) {
859
- return
860
- }
861
- const values = getButtonOptionsConfig(this.$editTrans, this.$editLocale).heading.map(item => {
862
- return item.value
863
- })
864
- if (!values.includes(parsedom)) {
865
- throw new Error('The parameter supports only h1-h6 and p')
866
- }
867
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
868
- const block = this.editor.range.anchor.element.getBlock()
869
- //先转为段落
870
- blockToParagraph(block)
871
- //设置标题
872
- block.parsedom = parsedom
873
- } else {
874
- const result = this.editor.getElementsByRange(useCache).includes
875
- result.forEach(el => {
876
- if (el.element.isBlock()) {
877
- blockToParagraph(el.element)
878
- el.element.parsedom = parsedom
879
- } else {
880
- const block = el.element.getBlock()
881
- blockToParagraph(block)
882
- block.parsedom = parsedom
883
- }
884
- })
885
- }
886
- if (isRender) {
887
- this.editor.formatElementStack()
888
- this.editor.domRender()
889
- this.editor.rangeRender()
890
- }
891
- },
892
- //api:插入有序列表 ordered为true表示有序列表
893
- setList(ordered, isRender = true, useCache = false) {
894
- if (this.disabled) {
895
- return
896
- }
897
- if (!this.editor.range) {
898
- return
899
- }
900
- //起点和终点在一起
901
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
902
- const block = this.editor.range.anchor.element.getBlock()
903
- const isList = blockIsList(block, ordered)
904
- if (isList) {
905
- blockToParagraph(block)
906
- } else {
907
- blockToList(block, ordered)
908
- }
909
- }
910
- //起点和终点不在一起
911
- else {
912
- let blocks = []
913
- const result = this.editor.getElementsByRange(useCache).includes
914
- result.forEach(item => {
915
- const block = item.element.getBlock()
916
- const exist = blocks.some(el => block.isEqual(el))
917
- if (!exist) {
918
- blocks.push(block)
919
- }
920
- })
921
- blocks.forEach(block => {
922
- const isList = blockIsList(block, ordered)
923
- if (isList) {
924
- blockToParagraph(block)
925
- } else {
926
- blockToList(block, ordered)
927
- }
928
- })
929
- }
930
- if (isRender) {
931
- this.editor.formatElementStack()
932
- this.editor.domRender()
933
- this.editor.rangeRender()
934
- }
935
- },
936
- //api:插入任务列表
937
- setTask(isRender = true, useCache = false) {
938
- if (this.disabled) {
939
- return
940
- }
941
- if (!this.editor.range) {
942
- return
943
- }
944
- //起点和终点在一起
945
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
946
- const block = this.editor.range.anchor.element.getBlock()
947
- const isTask = blockIsTask(block)
948
- if (isTask) {
949
- blockToParagraph(block)
950
- } else {
951
- blockToTask(block)
952
- }
953
- }
954
- //起点和终点不在一起
955
- else {
956
- let blocks = []
957
- const result = this.editor.getElementsByRange(useCache).includes
958
- result.forEach(item => {
959
- const block = item.element.getBlock()
960
- const exist = blocks.some(el => block.isEqual(el))
961
- if (!exist) {
962
- blocks.push(block)
963
- }
964
- })
965
- blocks.forEach(block => {
966
- const isTask = blockIsTask(block)
967
- if (isTask) {
968
- blockToParagraph(block)
969
- } else {
970
- blockToTask(block)
971
- }
972
- })
973
- }
974
- if (isRender) {
975
- this.editor.formatElementStack()
976
- this.editor.domRender()
977
- this.editor.rangeRender()
978
- }
979
- },
980
- //api:设置样式
981
- setTextStyle(name, value, isRender = true, useCache = false) {
982
- if (this.disabled) {
983
- return
984
- }
985
- if (!this.editor.range) {
986
- return
987
- }
988
- const active = this.queryTextStyle(name, value, useCache)
989
- if (active) {
990
- this.editor.removeTextStyle([name], true)
991
- } else {
992
- let styles = {}
993
- styles[name] = value
994
- this.editor.setTextStyle(styles, true)
995
- }
996
- if (isRender) {
997
- this.editor.formatElementStack()
998
- this.editor.domRender()
999
- this.editor.rangeRender()
1000
- }
1001
- },
1002
- //api:查询是否具有某个样式
1003
- queryTextStyle(name, value, useCache = false) {
1004
- return this.editor.queryTextStyle(name, value, useCache)
1005
- },
1006
- //api:设置标记
1007
- setTextMark(name, value, isRender = true, useCache = false) {
1008
- if (this.disabled) {
1009
- return
1010
- }
1011
- if (!this.editor.range) {
1012
- return
1013
- }
1014
- const active = this.queryTextMark(name, value, useCache)
1015
- if (active) {
1016
- this.editor.removeTextMark([name], true)
1017
- } else {
1018
- let marks = {}
1019
- marks[name] = value
1020
- this.editor.setTextMark(marks, true)
1021
- }
1022
- if (isRender) {
1023
- this.editor.formatElementStack()
1024
- this.editor.domRender()
1025
- this.editor.rangeRender()
1026
- }
1027
- },
1028
- //api:查询是否具有某个标记
1029
- queryTextMark(name, value, useCache = false) {
1030
- return this.editor.queryTextMark(name, value, useCache)
1031
- },
1032
- //api:清除文本样式和标记
1033
- formatText(isRender = true, useCache = false) {
1034
- if (this.disabled) {
1035
- return
1036
- }
1037
- if (!this.editor.range) {
1038
- return
1039
- }
1040
- this.editor.removeTextStyle(null, useCache)
1041
- //这里不能使用缓存,因为removeTextStyle会更新光标位置
1042
- this.editor.removeTextMark(null)
1043
- if (isRender) {
1044
- this.editor.formatElementStack()
1045
- this.editor.domRender()
1046
- this.editor.rangeRender()
1047
- }
1048
- },
1049
- //api:设置对齐方式,参数取值justify/left/right/center
1050
- setAlign(value, isRender = true, useCache = false) {
1051
- if (this.disabled) {
1052
- return
1053
- }
1054
- if (!this.editor.range) {
1055
- return
1056
- }
1057
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
1058
- const block = this.editor.range.anchor.element.getBlock()
1059
- const inblock = this.editor.range.anchor.element.getInblock()
1060
- if (inblock) {
1061
- if (inblock.hasStyles()) {
1062
- inblock.styles['text-align'] = value
1063
- } else {
1064
- inblock.styles = {
1065
- 'text-align': value
1066
- }
1067
- }
1068
- } else {
1069
- if (block.hasStyles()) {
1070
- block.styles['text-align'] = value
1071
- } else {
1072
- block.styles = {
1073
- 'text-align': value
1074
- }
1075
- }
1076
- }
1077
- } else {
1078
- const result = this.editor.getElementsByRange(useCache).includes
1079
- result.forEach(el => {
1080
- if (el.element.isBlock() || el.element.isInblock()) {
1081
- if (el.element.hasStyles()) {
1082
- el.element.styles['text-align'] = value
1083
- } else {
1084
- el.element.styles = {
1085
- 'text-align': value
1086
- }
1087
- }
1088
- } else {
1089
- const block = el.element.getBlock()
1090
- const inblock = el.element.getInblock()
1091
- if (inblock) {
1092
- if (inblock.hasStyles()) {
1093
- inblock.styles['text-align'] = value
1094
- } else {
1095
- inblock.styles = {
1096
- 'text-align': value
1097
- }
1098
- }
1099
- } else {
1100
- if (block.hasStyles()) {
1101
- block.styles['text-align'] = value
1102
- } else {
1103
- block.styles = {
1104
- 'text-align': value
1105
- }
1106
- }
1107
- }
1108
- }
1109
- })
1110
- }
1111
- if (isRender) {
1112
- this.editor.formatElementStack()
1113
- this.editor.domRender()
1114
- this.editor.rangeRender()
1115
- }
1116
- },
1117
739
  //api:撤销
1118
740
  undo() {
1119
741
  if (this.disabled) {
@@ -1143,621 +765,15 @@ export default {
1143
765
  this.editor.domRender(true)
1144
766
  this.editor.rangeRender()
1145
767
  }
1146
- },
1147
- //api:插入引用
1148
- setQuote(isRender = true, useCache = false) {
1149
- if (this.disabled) {
1150
- return
1151
- }
1152
- if (!this.editor.range) {
1153
- return
1154
- }
1155
- //起点和终点在一起
1156
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
1157
- const block = this.editor.range.anchor.element.getBlock()
1158
- const oldParsedom = block.parsedom
1159
- blockToParagraph(block)
1160
- if (oldParsedom != 'blockquote') {
1161
- block.parsedom = 'blockquote'
1162
- }
1163
- }
1164
- //起点和终点不在一起
1165
- else {
1166
- let blocks = []
1167
- const result = this.editor.getElementsByRange(useCache).includes
1168
- result.forEach(item => {
1169
- const block = item.element.getBlock()
1170
- const exist = blocks.some(el => block.isEqual(el))
1171
- if (!exist) {
1172
- blocks.push(block)
1173
- }
1174
- })
1175
- blocks.forEach(block => {
1176
- const oldParsedom = block.parsedom
1177
- blockToParagraph(block)
1178
- if (oldParsedom != 'blockquote') {
1179
- block.parsedom = 'blockquote'
1180
- }
1181
- })
1182
- }
1183
- if (isRender) {
1184
- this.editor.formatElementStack()
1185
- this.editor.domRender()
1186
- this.editor.rangeRender()
1187
- }
1188
- },
1189
- //api:设置行高
1190
- setLineHeight(value, isRender = true, useCache = false) {
1191
- if (this.disabled) {
1192
- return
1193
- }
1194
- if (!this.editor.range) {
1195
- return
1196
- }
1197
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
1198
- const block = this.editor.range.anchor.element.getBlock()
1199
- const inblock = this.editor.range.anchor.element.getInblock()
1200
- if (inblock) {
1201
- if (inblock.hasStyles()) {
1202
- inblock.styles['line-height'] = value
1203
- } else {
1204
- inblock.styles = {
1205
- 'line-height': value
1206
- }
1207
- }
1208
- } else {
1209
- if (block.hasStyles()) {
1210
- block.styles['line-height'] = value
1211
- } else {
1212
- block.styles = {
1213
- 'line-height': value
1214
- }
1215
- }
1216
- }
1217
- } else {
1218
- const result = this.editor.getElementsByRange(useCache).includes
1219
- result.forEach(el => {
1220
- if (el.element.isBlock() || el.element.isInblock()) {
1221
- if (el.element.hasStyles()) {
1222
- el.element.styles['line-height'] = value
1223
- } else {
1224
- el.element.styles = {
1225
- 'line-height': value
1226
- }
1227
- }
1228
- } else {
1229
- const block = el.element.getBlock()
1230
- const inblock = el.element.getInblock()
1231
- if (inblock) {
1232
- if (inblock.hasStyles()) {
1233
- inblock.styles['line-height'] = value
1234
- } else {
1235
- inblock.styles = {
1236
- 'line-height': value
1237
- }
1238
- }
1239
- } else {
1240
- if (block.hasStyles()) {
1241
- block.styles['line-height'] = value
1242
- } else {
1243
- block.styles = {
1244
- 'line-height': value
1245
- }
1246
- }
1247
- }
1248
- }
1249
- })
1250
- }
1251
- if (isRender) {
1252
- this.editor.formatElementStack()
1253
- this.editor.domRender()
1254
- this.editor.rangeRender()
1255
- }
1256
- },
1257
- //api:增加缩进
1258
- setIndentIncrease(isRender = true, useCache = false) {
1259
- if (this.disabled) {
1260
- return
1261
- }
1262
- if (!this.editor.range) {
1263
- return
1264
- }
1265
- const fn = element => {
1266
- if (element.hasStyles()) {
1267
- if (element.styles.hasOwnProperty('text-indent')) {
1268
- let val = element.styles['text-indent']
1269
- if (val.endsWith('em')) {
1270
- val = parseFloat(val)
1271
- } else {
1272
- val = 0
1273
- }
1274
- element.styles['text-indent'] = `${val + 2}em`
1275
- } else {
1276
- element.styles['text-indent'] = '2em'
1277
- }
1278
- } else {
1279
- element.styles = {
1280
- 'text-indent': '2em'
1281
- }
1282
- }
1283
- }
1284
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
1285
- const block = this.editor.range.anchor.element.getBlock()
1286
- const inblock = this.editor.range.anchor.element.getInblock()
1287
- if (inblock && inblock.behavior == 'block' && !inblock.isPreStyle()) {
1288
- fn(inblock)
1289
- } else if (!block.isPreStyle()) {
1290
- fn(block)
1291
- }
1292
- } else {
1293
- const result = this.editor.getElementsByRange(useCache).includes
1294
- result.forEach(item => {
1295
- const block = item.element.getBlock()
1296
- const inblock = item.element.getInblock()
1297
- if (inblock && inblock.behavior == 'block' && !inblock.isPreStyle()) {
1298
- fn(inblock)
1299
- } else if (!block.isPreStyle()) {
1300
- fn(block)
1301
- }
1302
- })
1303
- }
1304
- if (isRender) {
1305
- this.editor.formatElementStack()
1306
- this.editor.domRender()
1307
- this.editor.rangeRender()
1308
- }
1309
- },
1310
- //api:减少缩进
1311
- setIndentDecrease(isRender = true, useCache = false) {
1312
- if (this.disabled) {
1313
- return
1314
- }
1315
- if (!this.editor.range) {
1316
- return
1317
- }
1318
- const fn = element => {
1319
- if (element.hasStyles() && element.styles.hasOwnProperty('text-indent')) {
1320
- let val = element.styles['text-indent']
1321
- if (val.endsWith('em')) {
1322
- val = parseFloat(val)
1323
- } else {
1324
- val = 0
1325
- }
1326
- element.styles['text-indent'] = `${val - 2 >= 0 ? val - 2 : 0}em`
1327
- }
1328
- }
1329
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
1330
- const block = this.editor.range.anchor.element.getBlock()
1331
- const inblock = this.editor.range.anchor.element.getInblock()
1332
- if (inblock && inblock.behavior == 'block' && !inblock.isPreStyle()) {
1333
- fn(inblock)
1334
- } else if (!block.isPreStyle()) {
1335
- fn(block)
1336
- }
1337
- } else {
1338
- const result = this.editor.getElementsByRange(useCache).includes
1339
- result.forEach(item => {
1340
- const block = item.element.getBlock()
1341
- const inblock = item.element.getInblock()
1342
- if (inblock && inblock.behavior == 'block' && !inblock.isPreStyle()) {
1343
- fn(inblock)
1344
- } else if (!block.isPreStyle()) {
1345
- fn(block)
1346
- }
1347
- })
1348
- }
1349
- if (isRender) {
1350
- this.editor.formatElementStack()
1351
- this.editor.domRender()
1352
- this.editor.rangeRender()
1353
- }
1354
- },
1355
- //api:插入图片
1356
- insertImage(url, isRender = true, useCache = false) {
1357
- if (this.disabled) {
1358
- return
1359
- }
1360
- if (!this.editor.range) {
1361
- return
1362
- }
1363
- if (!url || typeof url != 'string') {
1364
- throw new Error('An image address must be given')
1365
- }
1366
- const image = new AlexElement(
1367
- 'closed',
1368
- 'img',
1369
- {
1370
- src: url
1371
- },
1372
- null,
1373
- null
1374
- )
1375
- this.editor.insertElement(image, true, useCache)
1376
- if (isRender) {
1377
- this.editor.formatElementStack()
1378
- this.editor.domRender()
1379
- this.editor.rangeRender()
1380
- }
1381
- },
1382
- //api:插入视频
1383
- insertVideo(url, isRender = true, useCache = false) {
1384
- if (this.disabled) {
1385
- return
1386
- }
1387
- if (!this.editor.range) {
1388
- return
1389
- }
1390
- if (!url || typeof url != 'string') {
1391
- throw new Error('A video address must be given')
1392
- }
1393
- const video = new AlexElement(
1394
- 'closed',
1395
- 'video',
1396
- {
1397
- src: url
1398
- },
1399
- null,
1400
- null
1401
- )
1402
- this.editor.insertElement(video, true, useCache)
1403
- const leftSpace = AlexElement.getSpaceElement()
1404
- const rightSpace = AlexElement.getSpaceElement()
1405
- this.editor.addElementAfter(rightSpace, video)
1406
- this.editor.addElementBefore(leftSpace, video)
1407
- this.editor.range.anchor.moveToEnd(rightSpace)
1408
- this.editor.range.focus.moveToEnd(rightSpace)
1409
- if (isRender) {
1410
- this.editor.formatElementStack()
1411
- this.editor.domRender()
1412
- this.editor.rangeRender()
1413
- }
1414
- },
1415
- //api:选区是否含有代码块样式
1416
- hasPreStyle(useCache = false) {
1417
- if (!this.editor.range) {
1418
- return false
1419
- }
1420
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
1421
- return this.editor.range.anchor.element.isPreStyle()
1422
- }
1423
- const result = this.editor.getElementsByRange(useCache).includes
1424
- return result.some(item => {
1425
- return item.element.isPreStyle()
1426
- })
1427
- },
1428
- //api:选区是否含有引用
1429
- hasQuote(useCache = false) {
1430
- if (!this.editor.range) {
1431
- return false
1432
- }
1433
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
1434
- const block = this.editor.range.anchor.element.getBlock()
1435
- return block.parsedom == 'blockquote'
1436
- }
1437
- const result = this.editor.getElementsByRange(useCache).includes
1438
- return result.some(item => {
1439
- if (item.element.isBlock()) {
1440
- return item.element.parsedom == 'blockquote'
1441
- } else {
1442
- const block = item.element.getBlock()
1443
- return block.parsedom == 'blockquote'
1444
- }
1445
- })
1446
- },
1447
- //api:选区是否含有列表
1448
- hasList(ordered = false, useCache = false) {
1449
- if (!this.editor.range) {
1450
- return false
1451
- }
1452
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
1453
- const block = this.editor.range.anchor.element.getBlock()
1454
- return blockIsList(block, ordered)
1455
- }
1456
- const result = this.editor.getElementsByRange(useCache).includes
1457
- return result.some(item => {
1458
- if (item.element.isBlock()) {
1459
- return blockIsList(item.element, ordered)
1460
- } else {
1461
- const block = item.element.getBlock()
1462
- return blockIsList(block, ordered)
1463
- }
1464
- })
1465
- },
1466
- //api:选区是否含有链接
1467
- hasLink(useCache = false) {
1468
- if (!this.editor.range) {
1469
- return false
1470
- }
1471
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
1472
- return !!this.getParsedomElementByElement(this.editor.range.anchor.element, 'a')
1473
- }
1474
- const result = this.editor.getElementsByRange(useCache).flatIncludes.filter(item => {
1475
- return item.element.isText()
1476
- })
1477
- return result.some(item => {
1478
- return !!this.getParsedomElementByElement(item.element, 'a')
1479
- })
1480
- },
1481
- //api:选区是否含有表格
1482
- hasTable(useCache = false) {
1483
- if (!this.editor.range) {
1484
- return false
1485
- }
1486
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
1487
- const block = this.editor.range.anchor.element.getBlock()
1488
- return block.parsedom == 'table'
1489
- }
1490
- const result = this.editor.getElementsByRange(useCache).includes
1491
- return result.some(item => {
1492
- if (item.element.isBlock()) {
1493
- return item.element.parsedom == 'table'
1494
- } else {
1495
- const block = item.element.getBlock()
1496
- return block.parsedom == 'table'
1497
- }
1498
- })
1499
- },
1500
- //api:选区是否含有任务列表
1501
- hasTask(useCache = false) {
1502
- if (!this.editor.range) {
1503
- return false
1504
- }
1505
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
1506
- const block = this.editor.range.anchor.element.getBlock()
1507
- return blockIsTask(block)
1508
- }
1509
- const result = this.editor.getElementsByRange(useCache).includes
1510
- return result.some(item => {
1511
- if (item.element.isBlock()) {
1512
- return blockIsTask(item.element)
1513
- } else {
1514
- const block = item.element.getBlock()
1515
- return blockIsTask(block)
1516
- }
1517
- })
1518
- },
1519
- //api:选区是否含有图片
1520
- hasImage(useCache = false) {
1521
- if (!this.editor.range) {
1522
- return false
1523
- }
1524
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
1525
- return this.editor.range.anchor.element.isClosed() && this.editor.range.anchor.element.parsedom == 'img'
1526
- }
1527
- const result = this.editor.getElementsByRange(useCache).flatIncludes
1528
- return result.some(item => {
1529
- return item.element.isClosed() && item.element.parsedom == 'img'
1530
- })
1531
- },
1532
- //api:选区是否含有视频
1533
- hasVideo(useCache = false) {
1534
- if (!this.editor.range) {
1535
- return false
1536
- }
1537
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
1538
- return this.editor.range.anchor.element.isClosed() && this.editor.range.anchor.element.parsedom == 'video'
1539
- }
1540
- const result = this.editor.getElementsByRange(useCache).flatIncludes
1541
- return result.some(item => {
1542
- return item.element.isClosed() && item.element.parsedom == 'video'
1543
- })
1544
- },
1545
- //api:选区是否全部在引用内
1546
- inQuote(useCache = false) {
1547
- if (!this.editor.range) {
1548
- return false
1549
- }
1550
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
1551
- const block = this.editor.range.anchor.element.getBlock()
1552
- return block.parsedom == 'blockquote'
1553
- }
1554
- const result = this.editor.getElementsByRange(useCache).includes
1555
- return result.every(item => {
1556
- if (item.element.isBlock()) {
1557
- return item.element.parsedom == 'blockquote'
1558
- } else {
1559
- const block = item.element.getBlock()
1560
- return block.parsedom == 'blockquote'
1561
- }
1562
- })
1563
- },
1564
- //api:选区是否全部在列表内
1565
- inList(ordered = false, useCache = false) {
1566
- if (!this.editor.range) {
1567
- return false
1568
- }
1569
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
1570
- const block = this.editor.range.anchor.element.getBlock()
1571
- return blockIsList(block, ordered)
1572
- }
1573
- const result = this.editor.getElementsByRange(useCache).includes
1574
- return result.every(item => {
1575
- if (item.element.isBlock()) {
1576
- return blockIsList(item.element, ordered)
1577
- } else {
1578
- const block = item.element.getBlock()
1579
- return blockIsList(block, ordered)
1580
- }
1581
- })
1582
- },
1583
- //api:选区是否全部在任务列表里
1584
- inTask(useCache = false) {
1585
- if (!this.editor.range) {
1586
- return false
1587
- }
1588
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
1589
- const block = this.editor.range.anchor.element.getBlock()
1590
- return blockIsTask(block)
1591
- }
1592
- const result = this.editor.getElementsByRange(useCache).includes
1593
- return result.every(item => {
1594
- if (item.element.isBlock()) {
1595
- return blockIsTask(item.element)
1596
- } else {
1597
- const block = item.element.getBlock()
1598
- return blockIsTask(block)
1599
- }
1600
- })
1601
- },
1602
- //api:创建一个空的表格
1603
- insertTable(rowLength, colLength, isRender = true, useCache = false) {
1604
- if (this.disabled) {
1605
- return
1606
- }
1607
- if (!this.editor.range) {
1608
- return
1609
- }
1610
- const table = new AlexElement('block', 'table', null, null, null)
1611
- const tbody = new AlexElement('inblock', 'tbody', null, null, null)
1612
- this.editor.addElementTo(tbody, table)
1613
- for (let i = 0; i < rowLength; i++) {
1614
- const row = new AlexElement('inblock', 'tr', null, null, null)
1615
- for (let j = 0; j < colLength; j++) {
1616
- const column = new AlexElement('inblock', 'td', null, null, null)
1617
- const breakEl = new AlexElement('closed', 'br', null, null, null)
1618
- this.editor.addElementTo(breakEl, column)
1619
- this.editor.addElementTo(column, row)
1620
- }
1621
- this.editor.addElementTo(row, tbody)
1622
- }
1623
- this.editor.insertElement(table, true, useCache)
1624
- //在表格后创建一个段落
1625
- const paragraph = new AlexElement('block', AlexElement.BLOCK_NODE, null, null, null)
1626
- const breakEl = new AlexElement('closed', 'br', null, null, null)
1627
- this.editor.addElementTo(breakEl, paragraph)
1628
- this.editor.addElementAfter(paragraph, table)
1629
- this.editor.range.anchor.moveToStart(tbody)
1630
- this.editor.range.focus.moveToStart(tbody)
1631
- if (isRender) {
1632
- this.editor.formatElementStack()
1633
- this.editor.domRender()
1634
- this.editor.rangeRender()
1635
- }
1636
- },
1637
- //api:插入代码块
1638
- insertCodeBlock(isRender = true, useCache = false) {
1639
- if (this.disabled) {
1640
- return
1641
- }
1642
- if (!this.editor.range) {
1643
- return
1644
- }
1645
- const pre = this.getCurrentParsedomElement('pre', useCache)
1646
- if (pre) {
1647
- let content = ''
1648
- AlexElement.flatElements(pre.children)
1649
- .filter(item => {
1650
- return item.isText()
1651
- })
1652
- .forEach(item => {
1653
- content += item.textContent
1654
- })
1655
- const splits = content.split('\n')
1656
- splits.forEach(item => {
1657
- const paragraph = new AlexElement('block', AlexElement.BLOCK_NODE, null, null, null)
1658
- const text = new AlexElement('text', null, null, null, item)
1659
- this.editor.addElementTo(text, paragraph)
1660
- this.editor.addElementBefore(paragraph, pre)
1661
- })
1662
- pre.toEmpty()
1663
- } else {
1664
- //起点和终点在一起
1665
- if (this.editor.range.anchor.isEqual(this.editor.range.focus)) {
1666
- const block = this.editor.range.anchor.element.getBlock()
1667
- blockToParagraph(block)
1668
- block.parsedom = 'pre'
1669
- const paragraph = new AlexElement('block', AlexElement.BLOCK_NODE, null, null, null)
1670
- const breakEl = new AlexElement('closed', 'br', null, null, null)
1671
- this.editor.addElementTo(breakEl, paragraph)
1672
- this.editor.addElementAfter(paragraph, block)
1673
- }
1674
- //起点和终点不在一起
1675
- else {
1676
- let result = this.editor.getElementsByRange(true).includes
1677
- this.editor.range.anchor.moveToStart(result[0].element.getBlock())
1678
- this.editor.range.focus.moveToEnd(result[result.length - 1].element.getBlock())
1679
- const res = this.editor.getElementsByRange(true).flatIncludes.filter(el => el.element.isText())
1680
- const obj = {}
1681
- res.forEach(el => {
1682
- if (obj[el.element.getBlock().key]) {
1683
- obj[el.element.getBlock().key].push(el.element.clone())
1684
- } else {
1685
- obj[el.element.getBlock().key] = [el.element.clone()]
1686
- }
1687
- })
1688
- const pre = new AlexElement('block', 'pre', null, null, null)
1689
- Object.keys(obj).forEach((key, index) => {
1690
- if (index > 0) {
1691
- const text = new AlexElement('text', null, null, null, '\n')
1692
- if (pre.hasChildren()) {
1693
- this.editor.addElementTo(text, pre, pre.children.length)
1694
- } else {
1695
- this.editor.addElementTo(text, pre)
1696
- }
1697
- }
1698
- obj[key].forEach(el => {
1699
- if (pre.hasChildren()) {
1700
- this.editor.addElementTo(el, pre, pre.children.length)
1701
- } else {
1702
- this.editor.addElementTo(el, pre)
1703
- }
1704
- })
1705
- })
1706
- this.editor.delete()
1707
- this.editor.insertElement(pre)
1708
- const paragraph = new AlexElement('block', AlexElement.BLOCK_NODE, null, null, null)
1709
- const breakEl = new AlexElement('closed', 'br', null, null, null)
1710
- this.editor.addElementTo(breakEl, paragraph)
1711
- this.editor.addElementAfter(paragraph, pre)
1712
- }
1713
- }
1714
- if (isRender) {
1715
- this.editor.formatElementStack()
1716
- this.editor.domRender()
1717
- this.editor.rangeRender()
1718
- }
1719
- },
1720
- //api:插入文本
1721
- insertText(text, isRender = true, useCache = false) {
1722
- if (this.disabled) {
1723
- return
1724
- }
1725
- if (!this.editor.range) {
1726
- return
1727
- }
1728
- this.editor.insertText(text, useCache)
1729
- if (isRender) {
1730
- this.editor.formatElementStack()
1731
- this.editor.domRender()
1732
- this.editor.rangeRender()
1733
- }
1734
- },
1735
- //api:插入html
1736
- insertHtml(html, isRender = true, useCache = false) {
1737
- if (this.disabled) {
1738
- return
1739
- }
1740
- if (!this.editor.range) {
1741
- return
1742
- }
1743
- const elements = this.editor.parseHtml(html)
1744
- for (let i = 0; i < elements.length; i++) {
1745
- this.editor.insertElement(elements[i], false, i == 0 ? useCache : false)
1746
- }
1747
- if (isRender) {
1748
- this.editor.formatElementStack()
1749
- this.editor.domRender()
1750
- this.editor.rangeRender()
1751
- }
1752
768
  }
1753
769
  },
1754
770
  beforeUnmount() {
1755
771
  //卸载绑定在滚动元素上的事件
1756
772
  this.removeScrollHandle()
1757
773
  //卸载绑定在document.documentElement上的事件
1758
- Dap.event.off(document.documentElement, `mousedown.editify_${this.uid} mousemove.editify_${this.uid} mouseup.editify_${this.uid} click.editify_${this.uid}`)
774
+ DapEvent.off(document.documentElement, `mousedown.editify_${this.uid} mousemove.editify_${this.uid} mouseup.editify_${this.uid} click.editify_${this.uid}`)
1759
775
  //卸载绑定在window上的事件
1760
- Dap.event.off(window, `resize.editify_${this.uid}`)
776
+ DapEvent.off(window, `resize.editify_${this.uid}`)
1761
777
  //销毁编辑器
1762
778
  this.editor.destroy()
1763
779
  }
@@ -1765,8 +781,11 @@ export default {
1765
781
  </script>
1766
782
  <style lang="less" scoped>
1767
783
  .editify {
1768
- display: block;
784
+ display: flex;
785
+ justify-content: flex-start;
786
+ flex-direction: column;
1769
787
  width: 100%;
788
+ height: 100%;
1770
789
  position: relative;
1771
790
  box-sizing: border-box;
1772
791
  -webkit-tap-highlight-color: transparent;
@@ -1781,18 +800,14 @@ export default {
1781
800
  -webkit-tap-highlight-color: transparent;
1782
801
  outline: none;
1783
802
  }
1784
-
1785
- &.fullheight {
1786
- height: 100%;
1787
- }
1788
-
1789
803
  &.fullscreen {
1790
804
  position: fixed;
1791
805
  z-index: 1000;
1792
806
  left: 0;
1793
807
  top: 0;
1794
- width: 100vw;
1795
- height: 100vh;
808
+ width: 100vw !important;
809
+ height: 100vh !important;
810
+ background: @background;
1796
811
 
1797
812
  .editify-body {
1798
813
  border-radius: 0;
@@ -1803,6 +818,8 @@ export default {
1803
818
  .editify-body {
1804
819
  display: block;
1805
820
  width: 100%;
821
+ height: 0;
822
+ flex: 1;
1806
823
  position: relative;
1807
824
  background-color: @background;
1808
825
  padding: 1px;
@@ -1834,6 +851,7 @@ export default {
1834
851
  overflow-x: hidden;
1835
852
  overflow-y: auto;
1836
853
  width: 100%;
854
+ height: 100%;
1837
855
  border-radius: inherit;
1838
856
  padding: 6px 10px;
1839
857
  line-height: 1.5;
@@ -2061,8 +1079,7 @@ export default {
2061
1079
  content: '';
2062
1080
  position: absolute;
2063
1081
  left: 0;
2064
- top: 50%;
2065
- transform: translateY(-50%);
1082
+ top: 2px;
2066
1083
  z-index: 1;
2067
1084
  cursor: pointer;
2068
1085
  }