vue-editify 0.2.17 → 0.2.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. package/examples/App.vue +4 -12
  2. package/lib/components/colors/colors.vue.d.ts +9 -0
  3. package/lib/components/colors/props.d.ts +4 -0
  4. package/lib/components/tooltip/tooltip.vue.d.ts +1 -1
  5. package/lib/core/function.d.ts +112 -64
  6. package/lib/core/rule.d.ts +23 -17
  7. package/lib/core/shortcut.d.ts +36 -0
  8. package/lib/core/tool.d.ts +12 -16
  9. package/lib/editify/editify.vue.d.ts +162 -15
  10. package/lib/editify/props.d.ts +1 -5
  11. package/lib/editify/toolbar/props.d.ts +1 -1
  12. package/lib/editify/toolbar/toolbar.vue.d.ts +3 -3
  13. package/lib/editify.es.js +13660 -12954
  14. package/lib/editify.umd.js +2 -2
  15. package/lib/feature/align.d.ts +0 -14
  16. package/lib/feature/heading.d.ts +0 -14
  17. package/lib/feature/lineHeight.d.ts +0 -14
  18. package/lib/feature/orderList.d.ts +1 -3
  19. package/lib/feature/task.d.ts +0 -14
  20. package/lib/feature/unorderList.d.ts +1 -3
  21. package/lib/index.d.ts +164 -17
  22. package/package.json +2 -2
  23. package/src/components/button/button.vue +3 -3
  24. package/src/components/checkbox/checkbox.vue +1 -1
  25. package/src/components/colors/colors.vue +4 -4
  26. package/src/components/colors/props.ts +6 -1
  27. package/src/components/insertAttachment/insertAttachment.vue +1 -1
  28. package/src/components/insertImage/insertImage.vue +1 -1
  29. package/src/components/insertLink/insertLink.vue +1 -1
  30. package/src/components/insertVideo/insertVideo.vue +1 -1
  31. package/src/components/layer/layer.vue +9 -3
  32. package/src/components/tooltip/tooltip.vue +1 -1
  33. package/src/components/updateLink/updateLink.vue +1 -1
  34. package/src/core/function.ts +873 -491
  35. package/src/core/rule.ts +86 -368
  36. package/src/core/shortcut.ts +386 -0
  37. package/src/core/tool.ts +111 -159
  38. package/src/css/var.less +0 -10
  39. package/src/editify/editify.less +85 -39
  40. package/src/editify/editify.vue +204 -88
  41. package/src/editify/menu/menu.vue +2 -3
  42. package/src/editify/props.ts +1 -6
  43. package/src/editify/toolbar/props.ts +2 -2
  44. package/src/editify/toolbar/toolbar.vue +12 -12
  45. package/src/feature/align.ts +2 -62
  46. package/src/feature/attachment.ts +14 -27
  47. package/src/feature/backColor.ts +2 -1
  48. package/src/feature/bold.ts +1 -1
  49. package/src/feature/code.ts +1 -1
  50. package/src/feature/codeBlock.ts +3 -3
  51. package/src/feature/fontFamily.ts +1 -1
  52. package/src/feature/fontSize.ts +1 -1
  53. package/src/feature/foreColor.ts +2 -1
  54. package/src/feature/formatClear.ts +1 -1
  55. package/src/feature/fullScreen.ts +1 -1
  56. package/src/feature/heading.ts +5 -76
  57. package/src/feature/image.ts +1 -1
  58. package/src/feature/indent.ts +1 -1
  59. package/src/feature/infoBlock.ts +6 -37
  60. package/src/feature/italic.ts +1 -1
  61. package/src/feature/lineHeight.ts +2 -78
  62. package/src/feature/link.ts +1 -1
  63. package/src/feature/mathformula.ts +4 -51
  64. package/src/feature/orderList.ts +168 -37
  65. package/src/feature/quote.ts +3 -3
  66. package/src/feature/redo.ts +1 -1
  67. package/src/feature/separator.ts +1 -1
  68. package/src/feature/sourceView.ts +1 -1
  69. package/src/feature/strikethrough.ts +1 -1
  70. package/src/feature/sub.ts +1 -1
  71. package/src/feature/super.ts +1 -1
  72. package/src/feature/table.ts +3 -3
  73. package/src/feature/task.ts +4 -58
  74. package/src/feature/underline.ts +1 -1
  75. package/src/feature/undo.ts +1 -1
  76. package/src/feature/unorderList.ts +108 -37
  77. package/src/feature/video.ts +1 -1
  78. package/src/icon/iconfont.css +39 -3
  79. package/src/icon/iconfont.ttf +0 -0
  80. package/src/icon/iconfont.woff +0 -0
  81. package/src/index.ts +13 -11
  82. package/src/locale/en_US.ts +109 -110
  83. package/src/locale/zh_CN.ts +11 -12
  84. package/lib/feature/panel.d.ts +0 -18
  85. package/src/feature/panel.ts +0 -107
@@ -136,23 +136,92 @@
136
136
  :deep(h6) {
137
137
  font-size: 16px;
138
138
  }
139
+
140
+ //不是有序列表元素则重置后面的有序列表序列
141
+ :deep(:not(div[data-editify-list='ol'])) + div[data-editify-list='ol'] {
142
+ counter-reset: item 0;
143
+ }
144
+
139
145
  //有序列表样式
140
146
  :deep(div[data-editify-list='ol']) {
141
147
  margin-bottom: 15px;
142
148
 
149
+ //第一个元素重置序列
150
+ &:first-of-type {
151
+ counter-reset: item 0;
152
+ }
153
+
143
154
  &::before {
144
- content: attr(data-editify-value) '.';
155
+ counter-increment: item;
156
+ content: counter(item) '.';
145
157
  margin-right: 10px;
146
158
  }
159
+
160
+ &[data-editify-list-style='decimal'] {
161
+ &::before {
162
+ content: counter(item, decimal) '.';
163
+ }
164
+ }
165
+ &[data-editify-list-style='decimal-leading-zero'] {
166
+ &::before {
167
+ content: counter(item, decimal-leading-zero) '.';
168
+ }
169
+ }
170
+ &[data-editify-list-style='lower-roman'] {
171
+ &::before {
172
+ content: counter(item, lower-roman) '.';
173
+ }
174
+ }
175
+ &[data-editify-list-style='upper-roman'] {
176
+ &::before {
177
+ content: counter(item, upper-roman) '.';
178
+ }
179
+ }
180
+ &[data-editify-list-style='lower-alpha'] {
181
+ &::before {
182
+ content: counter(item, lower-alpha) '.';
183
+ }
184
+ }
185
+ &[data-editify-list-style='upper-alpha'] {
186
+ &::before {
187
+ content: counter(item, upper-alpha) '.';
188
+ }
189
+ }
190
+ &[data-editify-list-style='lower-greek'] {
191
+ &::before {
192
+ content: counter(item, lower-greek) '.';
193
+ }
194
+ }
195
+ &[data-editify-list-style='cjk-ideographic'] {
196
+ &::before {
197
+ content: counter(item, cjk-ideographic) '.';
198
+ }
199
+ }
147
200
  }
148
201
  //无序列表样式
149
202
  :deep(div[data-editify-list='ul']) {
150
203
  margin-bottom: 15px;
151
204
 
152
205
  &::before {
153
- content: '\2022';
206
+ content: counter(item, disc);
154
207
  margin-right: 10px;
155
208
  }
209
+
210
+ &[data-editify-list-style='disc'] {
211
+ &::before {
212
+ content: counter(item, disc);
213
+ }
214
+ }
215
+ &[data-editify-list-style='circle'] {
216
+ &::before {
217
+ content: counter(item, circle);
218
+ }
219
+ }
220
+ &[data-editify-list-style='square'] {
221
+ &::before {
222
+ content: counter(item, square);
223
+ }
224
+ }
156
225
  }
157
226
  //代码样式
158
227
  :deep([data-editify-code]) {
@@ -309,15 +378,13 @@
309
378
  padding-left: 26px;
310
379
  font-size: var(--editify-font-size);
311
380
  color: var(--editify-font-color-dark);
312
- transition: all 300ms;
313
381
 
314
382
  &::before {
315
383
  display: block;
316
- width: 16px;
317
- height: 16px;
384
+ width: 14px;
385
+ height: 14px;
318
386
  border-radius: 2px;
319
387
  border: 1px solid var(--editify-font-color-light);
320
- transition: all 300ms;
321
388
  box-sizing: border-box;
322
389
  user-select: none;
323
390
  content: '';
@@ -332,29 +399,32 @@
332
399
  &::after {
333
400
  display: inline-block;
334
401
  width: 10px;
335
- height: 6px;
402
+ height: 5px;
336
403
  position: absolute;
337
404
  content: '';
338
- left: 3px;
405
+ left: 2px;
339
406
  top: 50%;
340
- margin-top: -2px;
341
- border: 1px solid var(--editify-font-color-light);
407
+ margin-top: -1px;
408
+ border: 2px solid transparent;
342
409
  border-top: none;
343
410
  border-right: none;
344
411
  transform: translateY(-50%) rotate(-45deg);
345
412
  transform-origin: center;
346
- box-sizing: border-box;
347
413
  z-index: 2;
348
414
  cursor: pointer;
349
- opacity: 0;
350
- transition: all 300ms;
351
415
  }
352
416
 
353
417
  &[data-editify-task='checked'] {
354
418
  text-decoration: line-through;
355
419
  color: var(--editify-font-color-light);
420
+
421
+ &::before {
422
+ background-color: var(--editify-font-color-disabled);
423
+ border-color: var(--editify-font-color-disabled);
424
+ }
425
+
356
426
  &::after {
357
- opacity: 1;
427
+ border-color: #fff;
358
428
  }
359
429
  }
360
430
  }
@@ -407,30 +477,6 @@
407
477
  background: var(--editify-background-darker);
408
478
  }
409
479
  }
410
- //面板样式
411
- :deep(div[data-editify-panel]) {
412
- display: block;
413
- position: relative;
414
- width: 100%;
415
- background-color: var(--editify-background-dark);
416
- color: var(--editify-font-color);
417
- border-radius: 4px;
418
- margin-bottom: 15px;
419
- padding: 10px 10px 1px 10px;
420
- border: 1px solid var(--editify-border-color);
421
-
422
- & > div {
423
- margin: 0 0 10px 0;
424
- font-size: var(--editify-font-size);
425
-
426
- &:first-child {
427
- margin: 0 0 15px 0;
428
- font-size: 18px;
429
- border-bottom: 1px solid var(--editify-border-color);
430
- padding-bottom: 8px;
431
- }
432
- }
433
- }
434
480
  //信息块样式
435
481
  :deep(div[data-editify-info]) {
436
482
  display: block;
@@ -452,7 +498,7 @@
452
498
  display: flex;
453
499
  justify-content: center;
454
500
  align-items: center;
455
- content: '\e610';
501
+ content: '\e600';
456
502
  font-family: 'editify-icon' !important;
457
503
  font-size: 1.25em;
458
504
  padding: 0 20px;
@@ -21,10 +21,10 @@
21
21
  <script setup lang="ts">
22
22
  import { computed, getCurrentInstance, nextTick, onBeforeUnmount, onMounted, provide, ref, watch } from 'vue'
23
23
  import { AlexEditor, AlexElement, AlexElementRangeType, AlexElementsRangeType } from 'alex-editor'
24
- import { element as DapElement, event as DapEvent, data as DapData, number as DapNumber, color as DapColor } from 'dap-util'
25
- import { mergeObject, getToolbarConfig, getMenuConfig, MenuConfigType, ObjectType, ToolbarConfigType, clickIsOut, cloneData } from '@/core/tool'
26
- import { parseList, orderdListHandle, commonElementHandle, tableThTdHandle, tableFormatHandle, tableRangeMergedHandle, preHandle, specialInblockHandle, attachmentHandle, mathformulaHandle, infoBlockHandle } from '@/core/rule'
27
- import { elementToParagraph, getMatchElementByRange, hasTableInRange, hasLinkInRange, hasPreInRange, hasImageInRange, hasVideoInRange, elementIsTask, elementIsAttachment, elementIsList, elementIsMathformula, getMathformulaByElement, elementIsPanel, elementIsInfoBlock } from '@/core/function'
24
+ import { element as DapElement, event as DapEvent, data as DapData, number as DapNumber, color as DapColor, common as DapCommon } from 'dap-util'
25
+ import { mergeObject, getToolbarConfig, getMenuConfig, MenuConfigType, ObjectType, ToolbarConfigType, clickIsOut, ShortcutType, MenuExtendType } from '@/core/tool'
26
+ import { listHandle, imageHandle, videoHandle, separatorHandle, linkHandle, codeHandle, tableHandle, preHandle, attachmentHandle, mathformulaHandle, infoBlockHandle, specialInblockHandle } from '@/core/rule'
27
+ import { elementToParagraph, getMatchElementByRange, elementIsTask, elementIsAttachment, elementIsList, elementIsMathformula, getMathformulaByElement, elementIsInfoBlock, getMatchElementByElement, hasTableInRange, hasPreInRange } from '@/core/function'
28
28
  import { trans } from '@/locale'
29
29
  import { LanguagesItemType } from '@/hljs'
30
30
  import { extraKeepTagsForMathformula } from '@/feature/mathformula'
@@ -125,7 +125,7 @@ const showBorder = computed<boolean>(() => {
125
125
  })
126
126
  //最终生效的工具栏配置
127
127
  const toolbarConfig = computed<ToolbarConfigType>(() => {
128
- return mergeObject(getToolbarConfig($editTrans, props.locale), props.toolbar || {}) as ToolbarConfigType
128
+ return mergeObject(getToolbarConfig($editTrans), props.toolbar || {}) as ToolbarConfigType
129
129
  })
130
130
  //最终生效的菜单栏配置
131
131
  const menuConfig = computed<MenuConfigType>(() => {
@@ -163,9 +163,7 @@ const handleToolbar = () => {
163
163
  }
164
164
  hideToolbar()
165
165
  nextTick(() => {
166
- const table = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'table' })
167
166
  const codeBlock = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'pre' })
168
- const link = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'a' })
169
167
  const image = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'img' })
170
168
  const video = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'video' })
171
169
 
@@ -189,26 +187,6 @@ const handleToolbar = () => {
189
187
  toolbarOptions.value.show = true
190
188
  }
191
189
  }
192
- //显示链接工具条
193
- else if (link) {
194
- toolbarOptions.value.type = 'link'
195
- toolbarOptions.value.node = `[data-editify-uid="${instance.uid}"] [data-editify-element="${link.key}"]`
196
- if (toolbarOptions.value.show) {
197
- toolbarRef.value!.layerRef!.setPosition()
198
- } else {
199
- toolbarOptions.value.show = true
200
- }
201
- }
202
- //显示表格工具条
203
- else if (table) {
204
- toolbarOptions.value.type = 'table'
205
- toolbarOptions.value.node = `[data-editify-uid="${instance.uid}"] [data-editify-element="${table.key}"]`
206
- if (toolbarOptions.value.show) {
207
- toolbarRef.value!.layerRef!.setPosition()
208
- } else {
209
- toolbarOptions.value.show = true
210
- }
211
- }
212
190
  //显示代码块工具条
213
191
  else if (codeBlock) {
214
192
  toolbarOptions.value.type = 'codeBlock'
@@ -219,10 +197,11 @@ const handleToolbar = () => {
219
197
  toolbarOptions.value.show = true
220
198
  }
221
199
  }
222
- //显示文本工具条
200
+ //以下是选区时显示文本工具条,非选区时显示其他工具条的情况
223
201
  else {
224
202
  const result = dataRangeCaches.value.flatList.filter((item: AlexElementRangeType) => item.element.isText())
225
- if (result.length && !hasTableInRange(editor.value!, dataRangeCaches.value) && !hasPreInRange(editor.value!, dataRangeCaches.value) && !hasLinkInRange(editor.value!, dataRangeCaches.value) && !hasImageInRange(editor.value!, dataRangeCaches.value) && !hasVideoInRange(editor.value!, dataRangeCaches.value)) {
203
+ //显示文本工具条
204
+ if (result.length) {
226
205
  toolbarOptions.value.type = 'text'
227
206
  if (toolbarOptions.value.show) {
228
207
  toolbarRef.value!.layerRef!.setPosition()
@@ -230,6 +209,63 @@ const handleToolbar = () => {
230
209
  toolbarOptions.value.show = true
231
210
  }
232
211
  }
212
+ //显示其他工具条
213
+ else {
214
+ const table = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'table' })
215
+ const link = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'a' })
216
+ const orderList = getMatchElementByRange(editor.value!, dataRangeCaches.value, {
217
+ parsedom: 'div',
218
+ marks: {
219
+ 'data-editify-list': 'ol'
220
+ }
221
+ })
222
+ const unorderList = getMatchElementByRange(editor.value!, dataRangeCaches.value, {
223
+ parsedom: 'div',
224
+ marks: {
225
+ 'data-editify-list': 'ul'
226
+ }
227
+ })
228
+ //显示链接工具条
229
+ if (link) {
230
+ toolbarOptions.value.type = 'link'
231
+ toolbarOptions.value.node = `[data-editify-uid="${instance.uid}"] [data-editify-element="${link.key}"]`
232
+ if (toolbarOptions.value.show) {
233
+ toolbarRef.value!.layerRef!.setPosition()
234
+ } else {
235
+ toolbarOptions.value.show = true
236
+ }
237
+ }
238
+ //显示表格工具条
239
+ else if (table) {
240
+ toolbarOptions.value.type = 'table'
241
+ toolbarOptions.value.node = `[data-editify-uid="${instance.uid}"] [data-editify-element="${table.key}"]`
242
+ if (toolbarOptions.value.show) {
243
+ toolbarRef.value!.layerRef!.setPosition()
244
+ } else {
245
+ toolbarOptions.value.show = true
246
+ }
247
+ }
248
+ //显示有序列表工具条
249
+ else if (orderList) {
250
+ toolbarOptions.value.type = 'orderList'
251
+ toolbarOptions.value.node = `[data-editify-uid="${instance.uid}"] [data-editify-element="${orderList.key}"]`
252
+ if (toolbarOptions.value.show) {
253
+ toolbarRef.value!.layerRef!.setPosition()
254
+ } else {
255
+ toolbarOptions.value.show = true
256
+ }
257
+ }
258
+ //显示无序列表工具条
259
+ else if (unorderList) {
260
+ toolbarOptions.value.type = 'unorderList'
261
+ toolbarOptions.value.node = `[data-editify-uid="${instance.uid}"] [data-editify-element="${unorderList.key}"]`
262
+ if (toolbarOptions.value.show) {
263
+ toolbarRef.value!.layerRef!.setPosition()
264
+ } else {
265
+ toolbarOptions.value.show = true
266
+ }
267
+ }
268
+ }
233
269
  }
234
270
  })
235
271
  }
@@ -241,28 +277,28 @@ const createEditor = () => {
241
277
  disabled: isDisabled.value,
242
278
  renderRules: [
243
279
  el => {
244
- parseList(editor.value!, el)
280
+ listHandle(editor.value!, el)
245
281
  },
246
282
  el => {
247
- orderdListHandle(editor.value!, el)
283
+ imageHandle(editor.value!, el)
248
284
  },
249
285
  el => {
250
- commonElementHandle(editor.value!, el)
286
+ videoHandle(editor.value!, el)
251
287
  },
252
288
  el => {
253
- tableThTdHandle(editor.value!, el)
289
+ separatorHandle(editor.value!, el)
254
290
  },
255
291
  el => {
256
- tableFormatHandle(editor.value!, el)
292
+ linkHandle(editor.value!, el)
257
293
  },
258
294
  el => {
259
- tableRangeMergedHandle(editor.value!, el)
295
+ codeHandle(editor.value!, el)
260
296
  },
261
297
  el => {
262
- preHandle(editor.value!, el, !!(toolbarConfig.value?.use && toolbarConfig.value?.codeBlock?.languages?.show), toolbarConfig.value?.codeBlock?.languages?.options as (string | LanguagesItemType)[])
298
+ tableHandle(editor.value!, el)
263
299
  },
264
300
  el => {
265
- specialInblockHandle(editor.value!, el)
301
+ preHandle(editor.value!, el, !!(toolbarConfig.value?.use && toolbarConfig.value?.codeBlock?.languages?.show), toolbarConfig.value?.codeBlock?.languages?.options as (string | LanguagesItemType)[])
266
302
  },
267
303
  el => {
268
304
  attachmentHandle(editor.value!, el, $editTrans)
@@ -273,6 +309,9 @@ const createEditor = () => {
273
309
  el => {
274
310
  infoBlockHandle(editor.value!, el, props.color)
275
311
  },
312
+ el => {
313
+ specialInblockHandle(editor.value!, el)
314
+ },
276
315
  ...props.renderRules
277
316
  ],
278
317
  extraKeepTags: [...extraKeepTagsForMathformula, ...props.extraKeepTags],
@@ -473,7 +512,7 @@ const documentClick = (e: Event) => {
473
512
  if (key) {
474
513
  const element = editor.value!.getElementByKey(key)!
475
514
  //如果是任务列表元素
476
- if (elementIsTask(element)) {
515
+ if (element && elementIsTask(element)) {
477
516
  const rect = DapElement.getElementBounding(elm)
478
517
  //在复选框范围内
479
518
  if (event.pageX >= Math.abs(rect.left) && event.pageX <= Math.abs(rect.left + 16) && event.pageY >= Math.abs(rect.top + elm.offsetHeight / 2 - 8) && event.pageY <= Math.abs(rect.top + elm.offsetHeight / 2 + 8)) {
@@ -499,7 +538,8 @@ const documentClick = (e: Event) => {
499
538
  }
500
539
  //重新定义编辑器粘贴html
501
540
  const handleCustomHtmlPaste = async (elements: AlexElement[]) => {
502
- AlexElement.flatElements(elements).forEach(el => {
541
+ const flatElements = AlexElement.flatElements(elements)
542
+ flatElements.forEach(el => {
503
543
  if (!el.isText()) {
504
544
  let marks: ObjectType = {}
505
545
  let styles: ObjectType = {}
@@ -552,10 +592,7 @@ const handleCustomHtmlPaste = async (elements: AlexElement[]) => {
552
592
  //有序和无序列表属性保留
553
593
  if (elementIsList(el, true) || elementIsList(el, false)) {
554
594
  marks['data-editify-list'] = el.marks!['data-editify-list']
555
- //有序列表保留序列号标记
556
- if (el.marks!['data-editify-value']) {
557
- marks['data-editify-value'] = el.marks!['data-editify-value']
558
- }
595
+ marks['data-editify-list-style'] = el.marks!['data-editify-list-style']
559
596
  }
560
597
  //行内代码属性保留
561
598
  if (el.parsedom == AlexElement.TEXT_NODE && el.marks!['data-editify-code']) {
@@ -590,11 +627,7 @@ const handleCustomHtmlPaste = async (elements: AlexElement[]) => {
590
627
  }
591
628
  //数学公式内的属性全部保留
592
629
  if (!!getMathformulaByElement(el)) {
593
- marks = mergeObject(marks, cloneData(el.marks!))!
594
- }
595
- //面板属性保留
596
- if (elementIsPanel(el)) {
597
- marks['data-editify-panel'] = el.marks!['data-editify-panel']
630
+ marks = mergeObject(marks, DapCommon.clone(el.marks!))!
598
631
  }
599
632
  //信息块属性保留
600
633
  if (elementIsInfoBlock(el)) {
@@ -621,7 +654,7 @@ const handleCustomHtmlPaste = async (elements: AlexElement[]) => {
621
654
  }
622
655
  //数学公式内的样式全部保留
623
656
  if (!!getMathformulaByElement(el)) {
624
- styles = mergeObject(styles, cloneData(el.styles!))!
657
+ styles = mergeObject(styles, DapCommon.clone(el.styles!))!
625
658
  }
626
659
  }
627
660
  //对外的自定义属性和样式保留
@@ -635,7 +668,24 @@ const handleCustomHtmlPaste = async (elements: AlexElement[]) => {
635
668
  el.marks = marks
636
669
  el.styles = styles
637
670
  }
671
+ //对于不在table下的这些表格元素直接置空
672
+ if (el.parsedom && ['tbody', 'tr', 'th', 'td', 'thead', 'tfooter', 'colgroup', 'col'].includes(el.parsedom)) {
673
+ const flag = !!getMatchElementByElement(el, {
674
+ parsedom: 'table'
675
+ })
676
+ if (!flag) {
677
+ el.toEmpty()
678
+ if (el.parent) {
679
+ const index = el.parent.children!.findIndex(item => item.isEqual(el))
680
+ el.parent.children!.splice(index, 1)
681
+ }
682
+ }
683
+ }
638
684
  })
685
+ elements = elements.filter(el => !el.isEmpty())
686
+ if (!elements.length) {
687
+ return
688
+ }
639
689
  //如果使用了自定义粘贴html的功能
640
690
  if (typeof props.customHtmlPaste == 'function') {
641
691
  await props.customHtmlPaste(elements)
@@ -687,12 +737,94 @@ const handleCustomParseNode = (ele: AlexElement) => {
687
737
  return ele
688
738
  }
689
739
  //编辑区域键盘按下
690
- const handleEditorKeydown = (val: string, e: Event) => {
740
+ const handleEditorKeydown = (val: string, e: KeyboardEvent) => {
691
741
  if (isDisabled.value) {
692
742
  return
693
743
  }
694
- //单独按下tab键
695
- if ((e as KeyboardEvent).key.toLocaleLowerCase() == 'tab' && !(e as KeyboardEvent).metaKey && !(e as KeyboardEvent).shiftKey && !(e as KeyboardEvent).ctrlKey && !(e as KeyboardEvent).altKey && props.tab) {
744
+ //遍历菜单栏配置,设置快捷键
745
+ for (let key in menuConfig.value) {
746
+ //如果是拓展菜单
747
+ if (key == 'extends') {
748
+ //获取拓展菜单列表
749
+ const extendMenus = (menuConfig.value as any)[key] as MenuExtendType
750
+ //遍历拓展菜单列表
751
+ Object.keys(extendMenus).forEach(extendKey => {
752
+ //获取拓展菜单配置
753
+ const item = extendMenus[extendKey]
754
+ //获取该菜单按钮的快捷键配置
755
+ const shortcut = item.shortcut as ShortcutType | undefined
756
+ //如果快捷键配置存在并且定义了define方法
757
+ if (shortcut && typeof shortcut.define == 'function') {
758
+ //获取define方法执行结果
759
+ const res = shortcut.define(e) as boolean | { [code: string]: boolean }
760
+ //如果执行结果是true,则表示该快捷键按下
761
+ if (res === true) {
762
+ //阻止默认行为
763
+ e.preventDefault()
764
+ //没有被禁用则执行对应的操作
765
+ if (!item.disabled) {
766
+ shortcut.operation?.(editor.value!, dataRangeCaches.value, isSourceView, isFullScreen)
767
+ }
768
+ }
769
+ //如果是对象,则表示有多个快捷键操作
770
+ else if (res && DapCommon.isObject(res)) {
771
+ //遍历
772
+ Object.keys(res).forEach(code => {
773
+ //如果是true,则执行对应的操作
774
+ if (!!res[code]) {
775
+ //阻止默认行为
776
+ e.preventDefault()
777
+ //没有被禁用则执行对应的操作
778
+ if (!item.disabled) {
779
+ shortcut.operation?.(editor.value!, dataRangeCaches.value, isSourceView, isFullScreen, code)
780
+ }
781
+ }
782
+ })
783
+ }
784
+ }
785
+ })
786
+ }
787
+ //如果是内置菜单
788
+ else if (!['use', 'tooltip', 'mode', 'style', 'sequence'].includes(key)) {
789
+ const item = (menuConfig.value as any)[key]
790
+ //这里多做一步判断:判断是否菜单并且是否有快捷键配置
791
+ if (DapCommon.isObject(item) && item.show === true && DapCommon.isObject(item.shortcut)) {
792
+ //获取该菜单按钮的快捷键配置
793
+ const shortcut = item.shortcut as ShortcutType
794
+ //如果定义了define方法
795
+ if (typeof shortcut.define == 'function') {
796
+ //获取define方法执行结果
797
+ const res = shortcut.define(e) as boolean | { [code: string]: boolean }
798
+ //如果执行结果是true,则表示该快捷键按下
799
+ if (res === true) {
800
+ //阻止默认行为
801
+ e.preventDefault()
802
+ //没有被禁用则执行对应的操作
803
+ if (!item.disabled) {
804
+ shortcut.operation?.(editor.value!, dataRangeCaches.value, isSourceView, isFullScreen)
805
+ }
806
+ }
807
+ //如果是对象,则表示有多个快捷键操作
808
+ else if (res && DapCommon.isObject(res)) {
809
+ //遍历
810
+ Object.keys(res).forEach(code => {
811
+ //如果是true,则执行对应的操作
812
+ if (!!res[code]) {
813
+ //阻止默认行为
814
+ e.preventDefault()
815
+ //没有被禁用则执行对应的操作
816
+ if (!item.disabled) {
817
+ shortcut.operation?.(editor.value!, dataRangeCaches.value, isSourceView, isFullScreen, code)
818
+ }
819
+ }
820
+ })
821
+ }
822
+ }
823
+ }
824
+ }
825
+ }
826
+ //代码块和表格中单独按下tab键,插入4个空格
827
+ if (e.key.toLocaleLowerCase() == 'tab' && !e.metaKey && !e.shiftKey && !e.ctrlKey && !e.altKey && (hasTableInRange(editor.value!, dataRangeCaches.value) || hasPreInRange(editor.value!, dataRangeCaches.value))) {
696
828
  e.preventDefault()
697
829
  editor.value!.insertText(' ')
698
830
  editor.value!.domRender()
@@ -862,27 +994,25 @@ const handleAfterRender = () => {
862
994
  //设定视频高度
863
995
  setVideoHeight()
864
996
  //附件元素下载事件设置
865
- AlexElement.flatElements(editor.value!.stack).forEach(el => {
866
- if (elementIsAttachment(el)) {
867
- DapEvent.off(el.elm as HTMLElement, 'click')
868
- //单击下载
869
- DapEvent.on(el.elm as HTMLElement, 'click', async () => {
870
- //获取文件地址
871
- const url = el.marks!['data-editify-attachment']
872
- //使用fetch读取文件地址
873
- const res = await fetch(url, {
874
- method: 'GET'
875
- })
876
- //获取blob数据
877
- const blob = await res.blob()
878
- //创建a标签进行下载
879
- const a = document.createElement('a')
880
- a.setAttribute('target', '_blank')
881
- a.setAttribute('href', URL.createObjectURL(blob))
882
- a.setAttribute('download', el.marks!['data-editify-attachment-name'])
883
- a.click()
997
+ contentRef.value!.querySelectorAll('span[data-editify-attachment]').forEach(dom => {
998
+ DapEvent.off(dom as HTMLElement, 'click')
999
+ //单击下载
1000
+ DapEvent.on(dom as HTMLElement, 'click', async () => {
1001
+ //获取文件地址
1002
+ const url = dom.getAttribute('data-editify-attachment')!
1003
+ //使用fetch读取文件地址
1004
+ const res = await fetch(url, {
1005
+ method: 'GET'
884
1006
  })
885
- }
1007
+ //获取blob数据
1008
+ const blob = await res.blob()
1009
+ //创建a标签进行下载
1010
+ const a = document.createElement('a')
1011
+ a.setAttribute('target', '_blank')
1012
+ a.setAttribute('href', URL.createObjectURL(blob))
1013
+ a.setAttribute('download', dom.getAttribute('data-editify-attachment-name')!)
1014
+ a.click()
1015
+ })
886
1016
  })
887
1017
  emits('updateview')
888
1018
  }
@@ -919,28 +1049,14 @@ const undo = () => {
919
1049
  if (isDisabled.value) {
920
1050
  return
921
1051
  }
922
- const historyRecord = editor.value!.history.get(-1)
923
- if (historyRecord) {
924
- editor.value!.history.current = historyRecord.current
925
- editor.value!.stack = historyRecord.stack
926
- editor.value!.range = historyRecord.range
927
- editor.value!.domRender(true)
928
- editor.value!.rangeRender()
929
- }
1052
+ editor.value!.undo()
930
1053
  }
931
1054
  //api:重做
932
1055
  const redo = () => {
933
1056
  if (isDisabled.value) {
934
1057
  return
935
1058
  }
936
- const historyRecord = editor.value!.history.get(1)
937
- if (historyRecord) {
938
- editor.value!.history.current = historyRecord.current
939
- editor.value!.stack = historyRecord.stack
940
- editor.value!.range = historyRecord.range
941
- editor.value!.domRender(true)
942
- editor.value!.rangeRender()
943
- }
1059
+ editor.value!.redo()
944
1060
  }
945
1061
 
946
1062
  //监听编辑的值变更
@@ -46,7 +46,6 @@ import { SourceViewMenuButton } from '@/feature/sourceView'
46
46
  import { FullScreenMenuButton } from '@/feature/fullScreen'
47
47
  import { AttachmentMenuButton } from '@/feature/attachment'
48
48
  import { MathformulaMenuButton } from '@/feature/mathformula'
49
- import { PanelMenuButton } from '@/feature/panel'
50
49
  import { InfoBlockMenuButton } from '@/feature/infoBlock'
51
50
 
52
51
  defineOptions({
@@ -84,7 +83,7 @@ const menuNames = computed<string[]>(() => {
84
83
  })
85
84
  })
86
85
  //内置菜单组件的数组
87
- const defaultMenus = shallowRef([UndoMenuButton, RedoMenuButton, HeadingMenuButton, IndentMenuButton, QuoteMenuButton, SeparatorMenuButton, AlignMenuButton, OrderListMenuButton, UnorderListMenuButton, TaskMenuButton, BoldMenuButton, UnderlineMenuButton, ItalicMenuButton, StrikethroughMenuButton, CodeMenuButton, SuperMenuButton, SubMenuButton, FormatClearMenuButton, FontSizeMenuButton, FontFamilyMenuButton, LineHeightMenuButton, ForeColorMenuButton, BackColorMenuButton, LinkMenuButton, ImageMenuButton, VideoMenuButton, TableMenuButton, CodeBlockMenuButton, SourceViewMenuButton, FullScreenMenuButton, AttachmentMenuButton, MathformulaMenuButton, PanelMenuButton, InfoBlockMenuButton])
86
+ const defaultMenus = shallowRef([UndoMenuButton, RedoMenuButton, HeadingMenuButton, IndentMenuButton, QuoteMenuButton, SeparatorMenuButton, AlignMenuButton, OrderListMenuButton, UnorderListMenuButton, TaskMenuButton, BoldMenuButton, UnderlineMenuButton, ItalicMenuButton, StrikethroughMenuButton, CodeMenuButton, SuperMenuButton, SubMenuButton, FormatClearMenuButton, FontSizeMenuButton, FontFamilyMenuButton, LineHeightMenuButton, ForeColorMenuButton, BackColorMenuButton, LinkMenuButton, ImageMenuButton, VideoMenuButton, TableMenuButton, CodeBlockMenuButton, SourceViewMenuButton, FullScreenMenuButton, AttachmentMenuButton, MathformulaMenuButton, InfoBlockMenuButton])
88
87
  //根据菜单名称获取对应的内置菜单组件
89
88
  const currentDefaultMenu = computed(() => {
90
89
  return (name: string) => defaultMenus.value.find(item => item.name == `_${name}`)
@@ -128,7 +127,7 @@ const ExtendMenuButton = defineComponent(
128
127
  zIndex: props.zIndex + 1,
129
128
  ref: btnRef,
130
129
  type: configuration.type || 'default',
131
- title: configuration.title || '',
130
+ title: `${configuration.title || ''}${configuration.shortcut?.title ? `【${configuration.shortcut?.title}】` : ''}`,
132
131
  leftBorder: configuration.leftBorder || false,
133
132
  rightBorder: configuration.rightBorder || false,
134
133
  hideScroll: configuration.hideScroll || false,
@@ -12,7 +12,7 @@ export type EditifyResizeParamsType = {
12
12
  export type EditifyToolbarOptionsType = {
13
13
  show: boolean
14
14
  node: string | null
15
- type: 'text' | 'link' | 'image' | 'video' | 'table' | 'codeBlock'
15
+ type: 'text' | 'link' | 'image' | 'video' | 'table' | 'codeBlock' | 'orderList' | 'unorderList'
16
16
  }
17
17
 
18
18
  export const EditifyProps = {
@@ -156,11 +156,6 @@ export const EditifyProps = {
156
156
  type: Boolean,
157
157
  default: false
158
158
  },
159
- //是否使用tab快捷键
160
- tab: {
161
- type: Boolean,
162
- default: true
163
- },
164
159
  //是否使用深色模式
165
160
  dark: {
166
161
  type: Boolean,
@@ -19,10 +19,10 @@ export const ToolbarProps = {
19
19
  },
20
20
  //类型
21
21
  type: {
22
- type: String as PropType<'text' | 'table' | 'link' | 'codeBlock' | 'image' | 'video'>,
22
+ type: String as PropType<'text' | 'table' | 'link' | 'codeBlock' | 'image' | 'video' | 'orderList' | 'unorderList'>,
23
23
  default: 'text',
24
24
  validator(value: any) {
25
- return ['text', 'table', 'link', 'codeBlock', 'image', 'video'].includes(value)
25
+ return ['text', 'table', 'link', 'codeBlock', 'image', 'video', 'orderList', 'unorderList'].includes(value)
26
26
  }
27
27
  },
28
28
  //工具条配置