vue-editify 0.2.13 → 0.2.15

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.
Files changed (190) hide show
  1. package/examples/App.vue +49 -67
  2. package/lib/components/button/button.vue.d.ts +62 -60
  3. package/lib/components/button/index.d.ts +4 -0
  4. package/lib/components/button/props.d.ts +12 -1
  5. package/lib/components/checkbox/checkbox.vue.d.ts +9 -9
  6. package/lib/components/checkbox/index.d.ts +4 -0
  7. package/lib/components/checkbox/props.d.ts +2 -2
  8. package/lib/components/colors/colors.vue.d.ts +4 -4
  9. package/lib/components/colors/index.d.ts +4 -0
  10. package/lib/components/colors/props.d.ts +2 -2
  11. package/lib/components/icon/index.d.ts +4 -0
  12. package/lib/components/insertAttachment/index.d.ts +4 -0
  13. package/lib/{plugins/attachment → components}/insertAttachment/insertAttachment.vue.d.ts +3 -3
  14. package/lib/{plugins/attachment → components}/insertAttachment/props.d.ts +1 -1
  15. package/lib/components/insertImage/index.d.ts +4 -0
  16. package/lib/components/insertImage/insertImage.vue.d.ts +3 -3
  17. package/lib/components/insertImage/props.d.ts +1 -1
  18. package/lib/components/insertLink/index.d.ts +4 -0
  19. package/lib/components/insertLink/insertLink.vue.d.ts +6 -6
  20. package/lib/components/insertLink/props.d.ts +3 -3
  21. package/lib/components/insertMathformula/index.d.ts +4 -0
  22. package/lib/{plugins/mathformula → components}/insertMathformula/insertMathformula.vue.d.ts +3 -3
  23. package/lib/{plugins/mathformula → components}/insertMathformula/props.d.ts +2 -2
  24. package/lib/components/insertTable/index.d.ts +4 -0
  25. package/lib/components/insertTable/insertTable.vue.d.ts +3 -3
  26. package/lib/components/insertTable/props.d.ts +2 -2
  27. package/lib/components/insertVideo/index.d.ts +4 -0
  28. package/lib/components/insertVideo/insertVideo.vue.d.ts +3 -3
  29. package/lib/components/insertVideo/props.d.ts +1 -1
  30. package/lib/components/layer/index.d.ts +4 -0
  31. package/lib/components/layer/layer.vue.d.ts +10 -8
  32. package/lib/components/tooltip/index.d.ts +4 -0
  33. package/lib/components/tooltip/tooltip.vue.d.ts +6 -4
  34. package/lib/components/triangle/index.d.ts +4 -0
  35. package/lib/components/triangle/triangle.vue.d.ts +1 -1
  36. package/lib/components/updateLink/index.d.ts +4 -0
  37. package/lib/components/updateLink/props.d.ts +17 -0
  38. package/lib/components/updateLink/updateLink.vue.d.ts +38 -0
  39. package/lib/core/function.d.ts +113 -36
  40. package/lib/core/rule.d.ts +20 -0
  41. package/lib/core/tool.d.ts +36 -34
  42. package/lib/editify/editify.vue.d.ts +86 -53
  43. package/lib/editify/menu/index.d.ts +4 -0
  44. package/lib/{components → editify}/menu/menu.vue.d.ts +3 -4
  45. package/lib/{components → editify}/menu/props.d.ts +1 -1
  46. package/lib/editify/props.d.ts +3 -7
  47. package/lib/editify/toolbar/index.d.ts +4 -0
  48. package/lib/{components → editify}/toolbar/props.d.ts +2 -2
  49. package/lib/{components → editify}/toolbar/toolbar.vue.d.ts +53 -53
  50. package/lib/editify.es.js +8879 -7668
  51. package/lib/editify.umd.js +2 -2
  52. package/lib/feature/align.d.ts +32 -0
  53. package/lib/feature/attachment.d.ts +18 -0
  54. package/lib/feature/backColor.d.ts +32 -0
  55. package/lib/feature/bold.d.ts +32 -0
  56. package/lib/feature/code.d.ts +32 -0
  57. package/lib/feature/codeBlock.d.ts +32 -0
  58. package/lib/feature/fontFamily.d.ts +32 -0
  59. package/lib/feature/fontSize.d.ts +32 -0
  60. package/lib/feature/foreColor.d.ts +32 -0
  61. package/lib/feature/formatClear.d.ts +32 -0
  62. package/lib/feature/fullScreen.d.ts +18 -0
  63. package/lib/feature/heading.d.ts +32 -0
  64. package/lib/feature/image.d.ts +32 -0
  65. package/lib/feature/indent.d.ts +18 -0
  66. package/lib/feature/infoBlock.d.ts +18 -0
  67. package/lib/feature/italic.d.ts +32 -0
  68. package/lib/feature/lineHeight.d.ts +32 -0
  69. package/lib/feature/link.d.ts +26 -0
  70. package/lib/feature/mathformula.d.ts +22 -0
  71. package/lib/feature/orderList.d.ts +32 -0
  72. package/lib/feature/panel.d.ts +18 -0
  73. package/lib/feature/quote.d.ts +18 -0
  74. package/lib/feature/redo.d.ts +18 -0
  75. package/lib/feature/separator.d.ts +18 -0
  76. package/lib/feature/sourceView.d.ts +18 -0
  77. package/lib/feature/strikethrough.d.ts +32 -0
  78. package/lib/feature/sub.d.ts +32 -0
  79. package/lib/feature/super.d.ts +32 -0
  80. package/lib/feature/table.d.ts +32 -0
  81. package/lib/feature/task.d.ts +32 -0
  82. package/lib/feature/underline.d.ts +32 -0
  83. package/lib/feature/undo.d.ts +18 -0
  84. package/lib/feature/unorderList.d.ts +32 -0
  85. package/lib/feature/video.d.ts +38 -0
  86. package/lib/index.d.ts +104 -69
  87. package/package.json +5 -5
  88. package/src/components/button/button.vue +21 -24
  89. package/src/components/button/index.ts +5 -0
  90. package/src/components/button/props.ts +14 -1
  91. package/src/components/checkbox/checkbox.vue +1 -1
  92. package/src/components/checkbox/index.ts +5 -0
  93. package/src/components/checkbox/props.ts +1 -1
  94. package/src/components/colors/colors.vue +3 -3
  95. package/src/components/colors/index.ts +5 -0
  96. package/src/components/colors/props.ts +2 -2
  97. package/src/components/icon/index.ts +5 -0
  98. package/src/components/insertAttachment/index.ts +5 -0
  99. package/src/{plugins/attachment → components}/insertAttachment/insertAttachment.vue +4 -2
  100. package/src/{plugins/attachment → components}/insertAttachment/props.ts +1 -1
  101. package/src/components/insertImage/index.ts +5 -0
  102. package/src/components/insertImage/insertImage.vue +5 -5
  103. package/src/components/insertImage/props.ts +1 -1
  104. package/src/components/insertLink/index.ts +5 -0
  105. package/src/components/insertLink/insertLink.vue +10 -10
  106. package/src/components/insertLink/props.ts +3 -3
  107. package/src/components/insertMathformula/index.ts +5 -0
  108. package/src/{plugins/mathformula → components}/insertMathformula/props.ts +2 -2
  109. package/src/components/insertTable/index.ts +5 -0
  110. package/src/components/insertTable/props.ts +2 -2
  111. package/src/components/insertVideo/index.ts +5 -0
  112. package/src/components/insertVideo/insertVideo.vue +2 -2
  113. package/src/components/insertVideo/props.ts +1 -1
  114. package/src/components/layer/index.ts +5 -0
  115. package/src/components/layer/layer.vue +42 -4
  116. package/src/components/tooltip/index.ts +5 -0
  117. package/src/components/tooltip/tooltip.vue +1 -1
  118. package/src/components/triangle/index.ts +5 -0
  119. package/src/components/triangle/triangle.vue +1 -1
  120. package/src/components/updateLink/index.ts +5 -0
  121. package/src/components/updateLink/props.ts +21 -0
  122. package/src/components/{toolbar/toolbar.less → updateLink/updateLink.less} +4 -20
  123. package/src/components/updateLink/updateLink.vue +74 -0
  124. package/src/core/function.ts +289 -97
  125. package/src/core/rule.ts +96 -7
  126. package/src/core/tool.ts +234 -78
  127. package/src/editify/editify.less +2 -0
  128. package/src/editify/editify.vue +182 -185
  129. package/src/editify/menu/index.ts +5 -0
  130. package/src/editify/menu/menu.vue +215 -0
  131. package/src/{components → editify}/menu/props.ts +1 -1
  132. package/src/editify/props.ts +7 -11
  133. package/src/editify/toolbar/index.ts +5 -0
  134. package/src/{components → editify}/toolbar/props.ts +1 -1
  135. package/src/editify/toolbar/toolbar.less +10 -0
  136. package/src/editify/toolbar/toolbar.vue +103 -0
  137. package/src/feature/align.ts +128 -0
  138. package/src/feature/attachment.ts +109 -0
  139. package/src/feature/backColor.ts +171 -0
  140. package/src/feature/bold.ts +136 -0
  141. package/src/feature/code.ts +136 -0
  142. package/src/feature/codeBlock.ts +204 -0
  143. package/src/feature/fontFamily.ts +140 -0
  144. package/src/feature/fontSize.ts +142 -0
  145. package/src/feature/foreColor.ts +173 -0
  146. package/src/feature/formatClear.ts +118 -0
  147. package/src/feature/fullScreen.ts +57 -0
  148. package/src/feature/heading.ts +154 -0
  149. package/src/feature/image.ts +225 -0
  150. package/src/feature/indent.ts +73 -0
  151. package/src/feature/infoBlock.ts +94 -0
  152. package/src/feature/italic.ts +136 -0
  153. package/src/feature/lineHeight.ts +165 -0
  154. package/src/feature/link.ts +149 -0
  155. package/src/feature/mathformula.ts +147 -0
  156. package/src/feature/orderList.ts +116 -0
  157. package/src/feature/panel.ts +108 -0
  158. package/src/feature/quote.ts +61 -0
  159. package/src/feature/redo.ts +56 -0
  160. package/src/feature/separator.ts +62 -0
  161. package/src/feature/sourceView.ts +59 -0
  162. package/src/feature/strikethrough.ts +136 -0
  163. package/src/feature/sub.ts +136 -0
  164. package/src/feature/super.ts +136 -0
  165. package/src/feature/table.ts +994 -0
  166. package/src/feature/task.ts +116 -0
  167. package/src/feature/underline.ts +136 -0
  168. package/src/feature/undo.ts +56 -0
  169. package/src/feature/unorderList.ts +116 -0
  170. package/src/feature/video.ts +339 -0
  171. package/src/hljs/index.ts +1 -1
  172. package/src/index.ts +69 -21
  173. package/src/locale/en_US.ts +3 -3
  174. package/src/locale/zh_CN.ts +3 -3
  175. package/lib/plugins/attachment/index.d.ts +0 -37
  176. package/lib/plugins/infoBlock/index.d.ts +0 -55
  177. package/lib/plugins/mathformula/index.d.ts +0 -49
  178. package/lib/plugins/panel/index.d.ts +0 -48
  179. package/src/components/menu/menu.vue +0 -1655
  180. package/src/components/toolbar/toolbar.vue +0 -1677
  181. package/src/plugins/attachment/index.ts +0 -237
  182. package/src/plugins/infoBlock/index.ts +0 -238
  183. package/src/plugins/mathformula/index.ts +0 -295
  184. package/src/plugins/panel/index.ts +0 -228
  185. package/tsconfig.json +0 -31
  186. package/tsconfig.node.json +0 -11
  187. /package/src/{plugins/attachment → components}/insertAttachment/insertAttachment.less +0 -0
  188. /package/src/{plugins/mathformula → components}/insertMathformula/insertMathformula.less +0 -0
  189. /package/src/{plugins/mathformula → components}/insertMathformula/insertMathformula.vue +0 -0
  190. /package/src/{components → editify}/menu/menu.less +0 -0
@@ -1,18 +1,18 @@
1
1
  <template>
2
- <div class="editify" :class="{ 'editify-fullscreen': isFullScreen, 'editify-autoheight': !isFullScreen && autoheight }" :style="{ zIndex: zIndex, paddingTop: isFullScreen ? '' : (offset || '') + 'px' }" ref="elRef">
2
+ <div class="editify" :class="{ 'editify-fullscreen': isFullScreen, 'editify-autoheight': !isFullScreen && isAutoHeight }" :style="{ zIndex: zIndex, paddingTop: isFullScreen ? '' : (offset || '') + 'px' }" ref="elRef">
3
3
  <!-- 菜单区域 -->
4
- <Menu ref="menuRef" v-if="menuConfig.use" :config="menuConfig" :color="color" :z-index="zIndex + 1"></Menu>
4
+ <Menu ref="menuRef" v-if="menuConfig.use && editor" :config="menuConfig" :color="color" :z-index="zIndex + 1"></Menu>
5
5
  <!-- 编辑层,与编辑区域宽高相同必须适配 -->
6
6
  <div ref="bodyRef" class="editify-body" :class="{ 'editify-border': showBorder, 'editify-menu_inner': menuConfig.use && menuConfig.mode == 'inner' }" :data-editify-uid="instance.uid">
7
7
  <!-- 编辑器 -->
8
- <div ref="contentRef" class="editify-content" :class="{ 'editify-placeholder': showPlaceholder, 'editify-disabled': disabled }" @click="handleEditorClick" @compositionstart="isInputChinese = true" @compositionend="isInputChinese = false" :data-editify-placeholder="placeholder"></div>
8
+ <div ref="contentRef" class="editify-content" :class="{ 'editify-placeholder': showPlaceholder, 'editify-disabled': isDisabled }" @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-sourceview" />
11
11
  <!-- 工具条 -->
12
12
  <Toolbar ref="toolbarRef" v-model="toolbarOptions.show" :node="toolbarOptions.node!" :type="toolbarOptions.type" :scroll-node="contentRef!" :config="toolbarConfig" :color="color" :z-index="zIndex + 10"></Toolbar>
13
13
  </div>
14
14
  <!-- 编辑器尾部 -->
15
- <div v-if="showWordLength" class="editify-footer" :class="{ 'editify-fullscreen': isFullScreen && !isSourceView }" ref="footerRef" :style="{ zIndex: zIndex + 1 }">
15
+ <div v-if="showWordLength" class="editify-footer" :class="{ 'editify-fullscreen': isFullScreen && !isSourceView }" :style="{ zIndex: zIndex + 1 }">
16
16
  <!-- 字数统计 -->
17
17
  <div class="editify-footer-words">{{ $editTrans('totalWordCount') }}{{ textValue.length }}</div>
18
18
  </div>
@@ -22,13 +22,14 @@
22
22
  import { computed, getCurrentInstance, nextTick, onBeforeUnmount, onMounted, provide, ref, watch } from 'vue'
23
23
  import { AlexEditor, AlexElement, AlexElementRangeType, AlexElementsRangeType } from 'alex-editor'
24
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, PluginResultType, clickIsOut } from '@/core/tool'
26
- import { parseList, orderdListHandle, commonElementHandle, tableThTdHandle, tableFormatHandle, tableRangeMergedHandle, preHandle, specialInblockHandle } from '@/core/rule'
27
- import { isTask, elementToParagraph, getMatchElementByRange, hasTableInRange, hasLinkInRange, hasPreInRange, hasImageInRange, hasVideoInRange } from '@/core/function'
28
- import Toolbar from '@/components/toolbar/toolbar.vue'
29
- import Menu from '@/components/menu/menu.vue'
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'
30
28
  import { trans } from '@/locale'
31
29
  import { LanguagesItemType } from '@/hljs'
30
+ import { extraKeepTagsForMathformula } from '@/feature/mathformula'
31
+ import { Toolbar } from './toolbar'
32
+ import { Menu } from './menu'
32
33
  import { EditifyProps, EditifyResizeParamsType, EditifyToolbarOptionsType } from './props'
33
34
 
34
35
  //定义组件名称
@@ -45,6 +46,32 @@ const emits = defineEmits(['update:modelValue', 'focus', 'blur', 'change', 'keyd
45
46
  //设置国际化方法
46
47
  const $editTrans = trans(props.locale || 'zh_CN')
47
48
 
49
+ //菜单栏组件实例
50
+ const menuRef = ref<InstanceType<typeof Menu> | null>(null)
51
+ //工具栏组件实例
52
+ const toolbarRef = ref<InstanceType<typeof Toolbar> | null>(null)
53
+
54
+ //自身dom
55
+ const elRef = ref<HTMLElement | null>(null)
56
+ //编辑器主体dom
57
+ const bodyRef = ref<HTMLElement | null>(null)
58
+ //编辑器内容区域dom
59
+ const contentRef = ref<HTMLElement | null>(null)
60
+
61
+ //编辑器对象
62
+ const editor = ref<AlexEditor | null>(null)
63
+ //是否代码视图
64
+ const isSourceView = ref<boolean>(false)
65
+ //是否全屏
66
+ const isFullScreen = ref<boolean>(false)
67
+ //代表真实光标变化,如果为null表示光标不在编辑器内,否则就是大于0的数字
68
+ const rangeKey = ref<number | null>(0)
69
+ //光标选区范围内的元素数组
70
+ const dataRangeCaches = ref<AlexElementsRangeType>({
71
+ flatList: [],
72
+ list: []
73
+ })
74
+
48
75
  //是否编辑器内部修改值
49
76
  const isModelChange = ref<boolean>(false)
50
77
  //是否正在输入中文
@@ -66,27 +93,6 @@ const toolbarOptions = ref<EditifyToolbarOptionsType>({
66
93
  type: 'text'
67
94
  })
68
95
 
69
- const menuRef = ref<InstanceType<typeof Menu> | null>(null)
70
- const bodyRef = ref<HTMLElement | null>(null)
71
- const contentRef = ref<HTMLElement | null>(null)
72
- const toolbarRef = ref<InstanceType<typeof Toolbar> | null>(null)
73
- const footerRef = ref<HTMLElement | null>(null)
74
- const elRef = ref<HTMLElement | null>(null)
75
-
76
- //编辑器对象
77
- const editor = ref<AlexEditor | null>(null)
78
- //是否代码视图
79
- const isSourceView = ref<boolean>(false)
80
- //是否全屏
81
- const isFullScreen = ref<boolean>(false)
82
- //菜单栏是否可以使用标识
83
- const canUseMenu = ref<boolean>(false)
84
- //光标选取范围内的元素数组
85
- const dataRangeCaches = ref<AlexElementsRangeType>({
86
- flatList: [],
87
- list: []
88
- })
89
-
90
96
  //编辑器的值
91
97
  const value = computed<string>({
92
98
  set(val) {
@@ -98,7 +104,7 @@ const value = computed<string>({
98
104
  })
99
105
  //编辑器的纯文本值
100
106
  const textValue = computed<string>(() => {
101
- return (<HTMLElement>DapElement.string2dom(`<div>${value.value}</div>`)).innerText
107
+ return (DapElement.string2dom(`<div>${value.value}</div>`) as HTMLElement).innerText
102
108
  })
103
109
  //是否显示占位符
104
110
  const showPlaceholder = computed<boolean>(() => {
@@ -119,25 +125,23 @@ const showBorder = computed<boolean>(() => {
119
125
  })
120
126
  //最终生效的工具栏配置
121
127
  const toolbarConfig = computed<ToolbarConfigType>(() => {
122
- return <ToolbarConfigType>mergeObject(getToolbarConfig($editTrans, props.locale), props.toolbar || {})
123
- })
124
- //插件配置读取
125
- const pluginResultList = computed<PluginResultType[]>(() => {
126
- const pluginResultList: PluginResultType[] = []
127
- props.plugins.forEach(plugin => {
128
- let pluginResult = plugin(instance, $editTrans)
129
- pluginResultList.push(pluginResult)
130
- })
131
- return pluginResultList
128
+ return mergeObject(getToolbarConfig($editTrans, props.locale), props.toolbar || {}) as ToolbarConfigType
132
129
  })
133
130
  //最终生效的菜单栏配置
134
131
  const menuConfig = computed<MenuConfigType>(() => {
135
- return <MenuConfigType>mergeObject(getMenuConfig($editTrans, props.locale), props.menu || {})
132
+ return mergeObject(getMenuConfig($editTrans, props.locale), props.menu || {}) as MenuConfigType
136
133
  })
134
+ //编辑器菜单栏区域高度
135
+ const menuHeight = computed<number | null>(() => {
136
+ return menuRef.value ? menuRef.value.height : null
137
+ })
138
+
137
139
  //是否深色模式
138
140
  const isDark = computed<boolean>(() => props.dark)
139
- //编辑器菜单栏区域高度
140
- const menuHeight = computed<number | null>(() => (menuRef.value ? menuRef.value.height : null))
141
+ //是否禁用编辑器
142
+ const isDisabled = computed<boolean>(() => props.disabled)
143
+ //是否自适应高度
144
+ const isAutoHeight = computed<boolean>(() => props.autoheight)
141
145
 
142
146
  //编辑器内部修改值的方法
143
147
  const internalModify = (val: string) => {
@@ -152,54 +156,21 @@ const hideToolbar = () => {
152
156
  toolbarOptions.value.show = false
153
157
  toolbarOptions.value.node = null
154
158
  }
155
- //监听滚动隐藏工具条
156
- const handleScroll = () => {
157
- const setScroll = (el: HTMLElement) => {
158
- DapEvent.on(el, `scroll.editify_${instance.uid}`, () => {
159
- if (toolbarConfig.value.use && toolbarOptions.value.show) {
160
- hideToolbar()
161
- }
162
- })
163
- if (el.parentNode) {
164
- setScroll(<HTMLElement>el.parentNode)
165
- }
166
- }
167
- setScroll(contentRef.value!)
168
- }
169
- //移除上述滚动事件的监听
170
- const removeScrollHandle = () => {
171
- const removeScroll = (el: HTMLElement) => {
172
- DapEvent.off(el, `scroll.editify_${instance.uid}`)
173
- if (el.parentNode) {
174
- removeScroll(<HTMLElement>el.parentNode)
175
- }
176
- }
177
- removeScroll(contentRef.value!)
178
- }
179
159
  //工具条显示判断
180
160
  const handleToolbar = () => {
181
- if (props.disabled || isSourceView.value) {
161
+ if (isDisabled.value || isSourceView.value) {
182
162
  return
183
163
  }
184
164
  hideToolbar()
185
165
  nextTick(() => {
186
166
  const table = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'table' })
187
- const pre = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'pre' })
167
+ const codeBlock = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'pre' })
188
168
  const link = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'a' })
189
169
  const image = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'img' })
190
170
  const video = getMatchElementByRange(editor.value!, dataRangeCaches.value, { parsedom: 'video' })
191
- //显示链接工具条
192
- if (link) {
193
- toolbarOptions.value.type = 'link'
194
- toolbarOptions.value.node = `[data-editify-uid="${instance.uid}"] [data-editify-element="${link.key}"]`
195
- if (toolbarOptions.value.show) {
196
- toolbarRef.value!.layerRef!.setPosition()
197
- } else {
198
- toolbarOptions.value.show = true
199
- }
200
- }
171
+
201
172
  //显示图片工具条
202
- else if (image) {
173
+ if (image) {
203
174
  toolbarOptions.value.type = 'image'
204
175
  toolbarOptions.value.node = `[data-editify-uid="${instance.uid}"] [data-editify-element="${image.key}"]`
205
176
  if (toolbarOptions.value.show) {
@@ -218,6 +189,16 @@ const handleToolbar = () => {
218
189
  toolbarOptions.value.show = true
219
190
  }
220
191
  }
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
+ }
221
202
  //显示表格工具条
222
203
  else if (table) {
223
204
  toolbarOptions.value.type = 'table'
@@ -229,9 +210,9 @@ const handleToolbar = () => {
229
210
  }
230
211
  }
231
212
  //显示代码块工具条
232
- else if (pre) {
213
+ else if (codeBlock) {
233
214
  toolbarOptions.value.type = 'codeBlock'
234
- toolbarOptions.value.node = `[data-editify-uid="${instance.uid}"] [data-editify-element="${pre.key}"]`
215
+ toolbarOptions.value.node = `[data-editify-uid="${instance.uid}"] [data-editify-element="${codeBlock.key}"]`
235
216
  if (toolbarOptions.value.show) {
236
217
  toolbarRef.value!.layerRef!.setPosition()
237
218
  } else {
@@ -240,9 +221,7 @@ const handleToolbar = () => {
240
221
  }
241
222
  //显示文本工具条
242
223
  else {
243
- const result = dataRangeCaches.value.flatList.filter((item: AlexElementRangeType) => {
244
- return item.element.isText()
245
- })
224
+ const result = dataRangeCaches.value.flatList.filter((item: AlexElementRangeType) => item.element.isText())
246
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)) {
247
226
  toolbarOptions.value.type = 'text'
248
227
  if (toolbarOptions.value.show) {
@@ -256,29 +235,10 @@ const handleToolbar = () => {
256
235
  }
257
236
  //初始创建编辑器
258
237
  const createEditor = () => {
259
- //注册插件:自定义规则校验函数
260
- let pluginRules: ((el: AlexElement) => void)[] = []
261
- pluginResultList.value.forEach(pluginResult => {
262
- if (pluginResult.renderRule) {
263
- pluginRules.push(pluginResult.renderRule)
264
- }
265
- })
266
- //注册插件:将插件定义的额外保留的标签数组与配置合并
267
- let extraKeepTags: string[] = [...props.extraKeepTags]
268
- pluginResultList.value.forEach(pluginResult => {
269
- if (pluginResult.extraKeepTags && Array.isArray(pluginResult.extraKeepTags)) {
270
- pluginResult.extraKeepTags.forEach(tag => {
271
- //如果不包含则加入数组
272
- if (!extraKeepTags.includes(tag)) {
273
- extraKeepTags.push(tag)
274
- }
275
- })
276
- }
277
- })
278
238
  //创建编辑器
279
239
  editor.value = new AlexEditor(contentRef.value!, {
280
240
  value: value.value,
281
- disabled: props.disabled,
241
+ disabled: isDisabled.value,
282
242
  renderRules: [
283
243
  el => {
284
244
  parseList(editor.value!, el)
@@ -299,14 +259,23 @@ const createEditor = () => {
299
259
  tableRangeMergedHandle(editor.value!, el)
300
260
  },
301
261
  el => {
302
- preHandle(editor.value!, el, !!(toolbarConfig.value?.use && toolbarConfig.value?.codeBlock?.languages?.show), <(string | LanguagesItemType)[]>toolbarConfig.value?.codeBlock?.languages?.options)
262
+ preHandle(editor.value!, el, !!(toolbarConfig.value?.use && toolbarConfig.value?.codeBlock?.languages?.show), toolbarConfig.value?.codeBlock?.languages?.options as (string | LanguagesItemType)[])
303
263
  },
304
264
  el => {
305
265
  specialInblockHandle(editor.value!, el)
306
266
  },
307
- ...pluginRules,
267
+ el => {
268
+ attachmentHandle(editor.value!, el, $editTrans)
269
+ },
270
+ el => {
271
+ mathformulaHandle(editor.value!, el)
272
+ },
273
+ el => {
274
+ infoBlockHandle(editor.value!, el, props.color)
275
+ },
308
276
  ...props.renderRules
309
277
  ],
278
+ extraKeepTags: [...extraKeepTagsForMathformula, ...props.extraKeepTags],
310
279
  allowCopy: props.allowCopy,
311
280
  allowPaste: props.allowPaste,
312
281
  allowCut: props.allowCut,
@@ -317,8 +286,7 @@ const createEditor = () => {
317
286
  customFilePaste: props.customFilePaste,
318
287
  customHtmlPaste: handleCustomHtmlPaste,
319
288
  customMerge: handleCustomMerge,
320
- customParseNode: handleCustomParseNode,
321
- extraKeepTags: extraKeepTags
289
+ customParseNode: handleCustomParseNode
322
290
  })
323
291
  //编辑器渲染后会有一个渲染过程,会改变内容,因此重新获取内容的值来设置value
324
292
  internalModify(editor.value.value)
@@ -337,7 +305,7 @@ const createEditor = () => {
337
305
  editor.value.formatElementStack()
338
306
  editor.value.domRender()
339
307
  //自动获取焦点
340
- if (props.autofocus && !isSourceView.value && !props.disabled) {
308
+ if (props.autofocus && !isSourceView.value && !isDisabled.value) {
341
309
  collapseToEnd()
342
310
  }
343
311
  }
@@ -349,7 +317,7 @@ const setVideoHeight = () => {
349
317
  }
350
318
  //鼠标在页面按下:处理表格拖拽改变列宽、拖拽改变图片视频宽度和菜单栏是否使用判断
351
319
  const documentMouseDown = (e: Event) => {
352
- if (props.disabled) {
320
+ if (isDisabled.value) {
353
321
  return
354
322
  }
355
323
  const elm = e.target as HTMLElement
@@ -386,14 +354,14 @@ const documentMouseDown = (e: Event) => {
386
354
  }
387
355
  }
388
356
  }
389
- //如果点击了除编辑器外的地方,菜单栏不可使用
357
+ //如果点击了除编辑器外的地方,菜单栏不可使用,此时将range置为null
390
358
  if (clickIsOut(elRef.value!, elm) && !isSourceView.value) {
391
- canUseMenu.value = false
359
+ rangeKey.value = null
392
360
  }
393
361
  }
394
362
  //鼠标在页面移动:处理表格拖拽改变列宽、拖拽改变图片视频宽度
395
363
  const documentMouseMove = (e: Event) => {
396
- if (props.disabled) {
364
+ if (isDisabled.value) {
397
365
  return
398
366
  }
399
367
  const event = e as MouseEvent
@@ -448,7 +416,7 @@ const documentMouseMove = (e: Event) => {
448
416
  }
449
417
  //鼠标在页面松开:处理表格拖拽改变列宽、拖拽改变图片视频宽度
450
418
  const documentMouseUp = () => {
451
- if (props.disabled) {
419
+ if (isDisabled.value) {
452
420
  return
453
421
  }
454
422
  if (!resizeParams.value.element) {
@@ -497,7 +465,7 @@ const documentMouseUp = () => {
497
465
  }
498
466
  //鼠标点击页面:处理任务列表复选框勾选
499
467
  const documentClick = (e: Event) => {
500
- if (props.disabled) {
468
+ if (isDisabled.value) {
501
469
  return
502
470
  }
503
471
  const elm = e.target as HTMLElement
@@ -508,7 +476,7 @@ const documentClick = (e: Event) => {
508
476
  if (key) {
509
477
  const element = editor.value!.getElementByKey(key)!
510
478
  //如果是任务列表元素
511
- if (isTask(element)) {
479
+ if (elementIsTask(element)) {
512
480
  const rect = DapElement.getElementBounding(elm)
513
481
  //在复选框范围内
514
482
  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)) {
@@ -586,7 +554,7 @@ const handleCustomHtmlPaste = async (elements: AlexElement[]) => {
586
554
  marks['target'] = el.marks!['target']
587
555
  }
588
556
  //有序和无序列表属性保留
589
- if (el.parsedom == 'div' && el.marks!['data-editify-list']) {
557
+ if (elementIsList(el, true) || elementIsList(el, false)) {
590
558
  marks['data-editify-list'] = el.marks!['data-editify-list']
591
559
  //有序列表保留序列号标记
592
560
  if (el.marks!['data-editify-value']) {
@@ -598,7 +566,7 @@ const handleCustomHtmlPaste = async (elements: AlexElement[]) => {
598
566
  marks['data-editify-code'] = el.marks!['data-editify-code']
599
567
  }
600
568
  //任务列表属性保留
601
- if (el.parsedom == 'div' && el.marks!['data-editify-task']) {
569
+ if (elementIsTask(el)) {
602
570
  marks['data-editify-task'] = el.marks!['data-editify-task']
603
571
  }
604
572
  //表格列宽属性保留
@@ -617,6 +585,25 @@ const handleCustomHtmlPaste = async (elements: AlexElement[]) => {
617
585
  if (['td', 'th'].includes(el.parsedom!) && el.marks!['data-editify-merged']) {
618
586
  marks['data-editify-merged'] = el.marks!['data-editify-merged']
619
587
  }
588
+ //附件属性保留
589
+ if (elementIsAttachment(el)) {
590
+ marks['data-editify-attachment'] = el.marks!['data-editify-attachment']
591
+ if (el.marks!['data-editify-attachment-name']) {
592
+ marks['data-editify-attachment-name'] = el.marks!['data-editify-attachment-name']
593
+ }
594
+ }
595
+ //数学公式内的属性全部保留
596
+ if (!!getMathformulaByElement(el)) {
597
+ marks = mergeObject(marks, cloneData(el.marks!))!
598
+ }
599
+ //面板属性保留
600
+ if (elementIsPanel(el)) {
601
+ marks['data-editify-panel'] = el.marks!['data-editify-panel']
602
+ }
603
+ //信息块属性保留
604
+ if (elementIsInfoBlock(el)) {
605
+ marks['data-editify-info'] = el.marks!['data-editify-info']
606
+ }
620
607
  }
621
608
  //处理需要保留的样式
622
609
  if (el.hasStyles()) {
@@ -636,18 +623,11 @@ const handleCustomHtmlPaste = async (elements: AlexElement[]) => {
636
623
  if ((el.isBlock() || el.isInblock()) && el.styles!['line-height']) {
637
624
  styles['line-height'] = el.styles!['line-height']
638
625
  }
639
- }
640
- //注册插件:自定义属性和样式的保留
641
- pluginResultList.value.forEach(pluginResult => {
642
- if (typeof pluginResult.pasteKeepMarks == 'function') {
643
- const keepMarks = pluginResult.pasteKeepMarks(el)
644
- marks = mergeObject(marks, keepMarks)!
645
- }
646
- if (typeof pluginResult.pasteKeepStyles == 'function') {
647
- const keepStyles = pluginResult.pasteKeepStyles(el)
648
- styles = mergeObject(styles, keepStyles)!
626
+ //数学公式内的样式全部保留
627
+ if (!!getMathformulaByElement(el)) {
628
+ styles = mergeObject(styles, cloneData(el.styles!))!
649
629
  }
650
- })
630
+ }
651
631
  //对外的自定义属性和样式保留
652
632
  if (typeof props.pasteKeepMarks == 'function') {
653
633
  marks = mergeObject(marks, props.pasteKeepMarks(el))!
@@ -690,24 +670,33 @@ const handleCustomMerge = (ele: AlexElement, preEle: AlexElement) => {
690
670
  }
691
671
  //针对node转为元素进行额外的处理
692
672
  const handleCustomParseNode = (ele: AlexElement) => {
693
- //注册插件:自定义元素转换处理
694
- pluginResultList.value.forEach(pluginResult => {
695
- if (pluginResult.customParseNode) {
696
- ele = pluginResult.customParseNode(ele)
697
- }
698
- })
673
+ //附件元素设为自闭合元素
674
+ if (elementIsAttachment(ele)) {
675
+ ele.type = 'closed'
676
+ }
677
+ //数学公式元素处理
678
+ if (elementIsMathformula(ele)) {
679
+ AlexElement.flatElements(ele.children!).forEach(item => {
680
+ //锁定元素防止合并
681
+ item.locked = true
682
+ //没有子元素的非文本元素设为自闭合元素
683
+ if (!item.isText() && !item.hasChildren()) {
684
+ item.type = 'closed'
685
+ }
686
+ })
687
+ }
699
688
  if (typeof props.customParseNode == 'function') {
700
689
  ele = props.customParseNode(ele)
701
690
  }
702
691
  return ele
703
692
  }
704
- //编辑区域键盘按下:设置缩进快捷键
693
+ //编辑区域键盘按下
705
694
  const handleEditorKeydown = (val: string, e: Event) => {
706
- if (props.disabled) {
695
+ if (isDisabled.value) {
707
696
  return
708
697
  }
709
698
  //单独按下tab键
710
- if ((<KeyboardEvent>e).key.toLocaleLowerCase() == 'tab' && !(<KeyboardEvent>e).metaKey && !(<KeyboardEvent>e).shiftKey && !(<KeyboardEvent>e).ctrlKey && !(<KeyboardEvent>e).altKey && props.tab) {
699
+ 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) {
711
700
  e.preventDefault()
712
701
  editor.value!.insertText(' ')
713
702
  editor.value!.formatElementStack()
@@ -719,7 +708,7 @@ const handleEditorKeydown = (val: string, e: Event) => {
719
708
  }
720
709
  //编辑区域键盘松开
721
710
  const handleEditorKeyup = (val: string, e: Event) => {
722
- if (props.disabled) {
711
+ if (isDisabled.value) {
723
712
  return
724
713
  }
725
714
  //自定义键盘松开操作
@@ -727,7 +716,7 @@ const handleEditorKeyup = (val: string, e: Event) => {
727
716
  }
728
717
  //点击编辑器:处理图片和视频的光标聚集
729
718
  const handleEditorClick = (e: Event) => {
730
- if (props.disabled || isSourceView.value) {
719
+ if (isDisabled.value || isSourceView.value) {
731
720
  return
732
721
  }
733
722
  const elm = e.target as HTMLElement
@@ -747,7 +736,7 @@ const handleEditorClick = (e: Event) => {
747
736
  }
748
737
  //编辑器的值更新
749
738
  const handleEditorChange = (newVal: string, oldVal: string) => {
750
- if (props.disabled) {
739
+ if (isDisabled.value) {
751
740
  return
752
741
  }
753
742
  //内部修改
@@ -757,7 +746,7 @@ const handleEditorChange = (newVal: string, oldVal: string) => {
757
746
  }
758
747
  //编辑器失去焦点
759
748
  const handleEditorBlur = (val: string) => {
760
- if (props.disabled) {
749
+ if (isDisabled.value) {
761
750
  return
762
751
  }
763
752
  if (props.border && props.color && !isFullScreen.value) {
@@ -775,7 +764,7 @@ const handleEditorBlur = (val: string) => {
775
764
  }
776
765
  //编辑器获取焦点
777
766
  const handleEditorFocus = (val: string) => {
778
- if (props.disabled) {
767
+ if (isDisabled.value) {
779
768
  return
780
769
  }
781
770
  if (props.border && props.color && !isFullScreen.value) {
@@ -803,11 +792,7 @@ const handleEditorFocus = (val: string) => {
803
792
  bodyRef.value!.style.boxShadow = `0 0 8px rgba(${rgb[0]},${rgb[1]},${rgb[2]},0.5)`
804
793
  }
805
794
  }
806
- //获取焦点时可以使用菜单栏
807
- setTimeout(() => {
808
- canUseMenu.value = true
809
- emits('focus', val)
810
- }, 0)
795
+ emits('focus', val)
811
796
  }
812
797
  //编辑器换行
813
798
  const handleInsertParagraph = (element: AlexElement, previousElement: AlexElement) => {
@@ -823,7 +808,7 @@ const handleInsertParagraph = (element: AlexElement, previousElement: AlexElemen
823
808
  }
824
809
  }
825
810
  //如果当前换行元素是任务列表则改为不勾选状态
826
- if (isTask(element)) {
811
+ if (elementIsTask(element)) {
827
812
  element.marks!['data-editify-task'] = 'uncheck'
828
813
  }
829
814
  }
@@ -831,11 +816,15 @@ const handleInsertParagraph = (element: AlexElement, previousElement: AlexElemen
831
816
  }
832
817
  //编辑器焦点更新
833
818
  const handleRangeUpdate = () => {
834
- if (props.disabled) {
819
+ if (isDisabled.value) {
835
820
  return
836
821
  }
837
- //如果没有range禁用菜单栏
838
- canUseMenu.value = !!editor.value!.range
822
+ //更新rangeKey
823
+ if (rangeKey.value) {
824
+ rangeKey.value++
825
+ } else {
826
+ rangeKey.value = 1
827
+ }
839
828
 
840
829
  //没有range直接返回
841
830
  if (!editor.value!.range) {
@@ -851,16 +840,9 @@ const handleRangeUpdate = () => {
851
840
  }
852
841
  //延时200ms进行判断
853
842
  rangeUpdateTimer.value = setTimeout(() => {
854
- //如果使用工具条或者菜单栏
855
- if (toolbarConfig.value.use || menuConfig.value.use) {
856
- //如果使用工具条
857
- if (toolbarConfig.value.use) {
858
- handleToolbar()
859
- }
860
- //如果使用菜单栏
861
- if (menuConfig.value.use) {
862
- menuRef.value!.handleRangeUpdate()
863
- }
843
+ //如果使用工具条
844
+ if (toolbarConfig.value.use) {
845
+ handleToolbar()
864
846
  }
865
847
  }, 200)
866
848
  //触发rangeupdate事件
@@ -884,30 +866,47 @@ const handleDeleteComplete = () => {
884
866
  const handleAfterRender = () => {
885
867
  //设定视频高度
886
868
  setVideoHeight()
887
- //注册插件:自定义dom渲染后处理
888
- pluginResultList.value.forEach(pluginResult => {
889
- if (pluginResult.updateView) {
890
- pluginResult.updateView()
869
+ //附件元素下载事件设置
870
+ AlexElement.flatElements(editor.value!.stack).forEach(el => {
871
+ if (elementIsAttachment(el)) {
872
+ DapEvent.off(el.elm as HTMLElement, 'click')
873
+ //单击下载
874
+ DapEvent.on(el.elm as HTMLElement, 'click', async () => {
875
+ //获取文件地址
876
+ const url = el.marks!['data-editify-attachment']
877
+ //使用fetch读取文件地址
878
+ const res = await fetch(url, {
879
+ method: 'GET'
880
+ })
881
+ //获取blob数据
882
+ const blob = await res.blob()
883
+ //创建a标签进行下载
884
+ const a = document.createElement('a')
885
+ a.setAttribute('target', '_blank')
886
+ a.setAttribute('href', URL.createObjectURL(blob))
887
+ a.setAttribute('download', el.marks!['data-editify-attachment-name'])
888
+ a.click()
889
+ })
891
890
  }
892
891
  })
893
892
  emits('updateview')
894
893
  }
895
894
  //api:光标设置到文档底部
896
895
  const collapseToEnd = () => {
897
- if (props.disabled) {
896
+ if (isDisabled.value) {
898
897
  return
899
898
  }
900
899
  editor.value!.collapseToEnd()
901
900
  editor.value!.rangeRender()
902
901
  DapElement.setScrollTop({
903
902
  el: contentRef.value!,
904
- number: 1000000,
903
+ number: DapElement.getScrollHeight(contentRef.value!),
905
904
  time: 0
906
905
  })
907
906
  }
908
907
  //api:光标设置到文档头部
909
908
  const collapseToStart = () => {
910
- if (props.disabled) {
909
+ if (isDisabled.value) {
911
910
  return
912
911
  }
913
912
  editor.value!.collapseToStart()
@@ -922,7 +921,7 @@ const collapseToStart = () => {
922
921
  }
923
922
  //api:撤销
924
923
  const undo = () => {
925
- if (props.disabled) {
924
+ if (isDisabled.value) {
926
925
  return
927
926
  }
928
927
  const historyRecord = editor.value!.history.get(-1)
@@ -937,7 +936,7 @@ const undo = () => {
937
936
  }
938
937
  //api:重做
939
938
  const redo = () => {
940
- if (props.disabled) {
939
+ if (isDisabled.value) {
941
940
  return
942
941
  }
943
942
  const historyRecord = editor.value!.history.get(1)
@@ -981,9 +980,9 @@ watch(
981
980
  }
982
981
  }
983
982
  )
984
- //监听disabled
983
+ //监听isDisabled
985
984
  watch(
986
- () => props.disabled,
985
+ () => isDisabled.value,
987
986
  newVal => {
988
987
  if (newVal) {
989
988
  editor.value!.setDisabled()
@@ -1010,8 +1009,6 @@ watch(
1010
1009
  onMounted(() => {
1011
1010
  //创建编辑器
1012
1011
  createEditor()
1013
- //监听滚动隐藏工具条
1014
- handleScroll()
1015
1012
  //鼠标按下监听
1016
1013
  DapEvent.on(document.documentElement, `mousedown.editify_${instance.uid}`, documentMouseDown)
1017
1014
  //鼠标移动监听
@@ -1025,8 +1022,6 @@ onMounted(() => {
1025
1022
  })
1026
1023
 
1027
1024
  onBeforeUnmount(() => {
1028
- //卸载绑定在滚动元素上的事件
1029
- removeScrollHandle()
1030
1025
  //卸载绑定在document.documentElement上的事件
1031
1026
  DapEvent.off(document.documentElement, `mousedown.editify_${instance.uid} mousemove.editify_${instance.uid} mouseup.editify_${instance.uid} click.editify_${instance.uid}`)
1032
1027
  //卸载绑定在window上的事件
@@ -1035,22 +1030,24 @@ onBeforeUnmount(() => {
1035
1030
  editor.value!.destroy()
1036
1031
  })
1037
1032
 
1038
- provide('$editTrans', $editTrans)
1039
- provide('editify', instance)
1033
+ provide('editor', editor)
1040
1034
  provide('isSourceView', isSourceView)
1041
1035
  provide('isFullScreen', isFullScreen)
1042
- provide('canUseMenu', canUseMenu)
1043
- provide('editor', editor)
1036
+ provide('rangeKey', rangeKey)
1044
1037
  provide('dataRangeCaches', dataRangeCaches)
1038
+ provide('undo', undo)
1039
+ provide('redo', redo)
1040
+ provide('$editTrans', $editTrans)
1045
1041
  provide('showBorder', showBorder)
1046
- provide('pluginResultList', pluginResultList)
1047
1042
  provide('isDark', isDark)
1043
+ provide('isDisabled', isDisabled)
1044
+ provide('isAutoHeight', isAutoHeight)
1048
1045
 
1049
1046
  defineExpose({
1050
1047
  editor,
1051
1048
  isSourceView,
1052
1049
  isFullScreen,
1053
- canUseMenu,
1050
+ rangeKey,
1054
1051
  dataRangeCaches,
1055
1052
  textValue,
1056
1053
  menuHeight,