vue-editify 0.2.14 → 0.2.15

Sign up to get free protection for your applications and to get access to all the features.
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 +18 -27
  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 +8797 -7560
  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 +36 -43
  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,