vue-editify 0.1.26 → 0.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/core/function.d.ts +265 -0
- package/lib/core/rule.d.ts +32 -0
- package/lib/core/tool.d.ts +50 -1
- package/lib/editify/editify.vue.d.ts +2 -0
- package/lib/editify.es.js +241 -151
- package/lib/editify.umd.js +1 -1
- package/lib/hljs/index.d.ts +9 -0
- package/lib/index.d.ts +2 -2
- package/lib/locale/index.d.ts +5 -0
- package/lib/plugins/attachment/index.d.ts +20 -1
- package/lib/plugins/attachment/insertAttachment/insertAttachment.vue.d.ts +3 -3
- package/lib/plugins/attachment/insertAttachment/props.d.ts +1 -1
- package/lib/style.css +1 -1
- package/package.json +2 -2
- package/src/components/insertImage/insertImage.less +1 -0
- package/src/components/insertVideo/insertVideo.less +1 -0
- package/src/components/menu/menu.vue +45 -9
- package/src/core/function.ts +265 -42
- package/src/core/rule.ts +40 -7
- package/src/core/tool.ts +51 -9
- package/src/editify/editify.less +2 -1
- package/src/editify/editify.vue +20 -17
- package/src/hljs/index.ts +9 -2
- package/src/index.ts +6 -4
- package/src/locale/en_US.ts +3 -2
- package/src/locale/index.ts +5 -2
- package/src/locale/zh_CN.ts +4 -3
- package/src/plugins/attachment/index.ts +94 -36
- package/src/plugins/attachment/insertAttachment/insertAttachment.less +48 -19
- package/src/plugins/attachment/insertAttachment/insertAttachment.vue +26 -56
- package/src/plugins/attachment/insertAttachment/props.ts +1 -1
- package/src/plugins/attachment/images/attachment.png +0 -0
package/src/core/tool.ts
CHANGED
@@ -198,8 +198,15 @@ export type MenuConfigType = {
|
|
198
198
|
extends?: MenuExtendType
|
199
199
|
}
|
200
200
|
|
201
|
+
export type PluginMenuConfigType = {
|
202
|
+
extraDisabled?: ((name: string) => boolean) | null
|
203
|
+
sequence: number
|
204
|
+
extend: MenuCustomButtonType
|
205
|
+
}
|
206
|
+
|
201
207
|
export type PluginResultType = {
|
202
|
-
|
208
|
+
name: string
|
209
|
+
menu?: PluginMenuConfigType
|
203
210
|
updateView?: () => void
|
204
211
|
customParseNode?: (element: AlexElement) => AlexElement
|
205
212
|
renderRule?: (el: AlexElement) => void
|
@@ -209,7 +216,9 @@ export type PluginResultType = {
|
|
209
216
|
|
210
217
|
export type PluginType = (editifyInstance: ComponentInternalInstance, color: string | null, editTrans: (key: string) => any) => PluginResultType
|
211
218
|
|
212
|
-
|
219
|
+
/**
|
220
|
+
* 粘贴html时保留的数据
|
221
|
+
*/
|
213
222
|
export const pasteKeepData: ObjectType = {
|
214
223
|
//粘贴html时元素保留的样式(全部元素)
|
215
224
|
marks: {
|
@@ -237,7 +246,12 @@ export const pasteKeepData: ObjectType = {
|
|
237
246
|
}
|
238
247
|
}
|
239
248
|
|
240
|
-
|
249
|
+
/**
|
250
|
+
* 对象平替值方法
|
251
|
+
* @param o1
|
252
|
+
* @param o2
|
253
|
+
* @returns
|
254
|
+
*/
|
241
255
|
export const mergeObject = function (o1: ObjectType, o2: ObjectType) {
|
242
256
|
if (!DapCommon.isObject(o1) && DapCommon.isObject(o2)) {
|
243
257
|
return null
|
@@ -255,7 +269,13 @@ export const mergeObject = function (o1: ObjectType, o2: ObjectType) {
|
|
255
269
|
return o1
|
256
270
|
}
|
257
271
|
|
258
|
-
|
272
|
+
/**
|
273
|
+
* 判断对象是否含有某个属性或者属性值是否一致
|
274
|
+
* @param obj
|
275
|
+
* @param name
|
276
|
+
* @param value
|
277
|
+
* @returns
|
278
|
+
*/
|
259
279
|
export const queryHasValue = function (obj: ObjectType, name: string, value?: string | number) {
|
260
280
|
//如果value不存在则判断是否拥有属性name
|
261
281
|
if (value == null || value == undefined) {
|
@@ -296,7 +316,11 @@ export const queryHasValue = function (obj: ObjectType, name: string, value?: st
|
|
296
316
|
return ownValue == value
|
297
317
|
}
|
298
318
|
|
299
|
-
|
319
|
+
/**
|
320
|
+
* 深拷贝函数
|
321
|
+
* @param data
|
322
|
+
* @returns
|
323
|
+
*/
|
300
324
|
export const cloneData = function (data: any) {
|
301
325
|
if (DapCommon.isObject(data) || Array.isArray(data)) {
|
302
326
|
return JSON.parse(JSON.stringify(data))
|
@@ -304,7 +328,11 @@ export const cloneData = function (data: any) {
|
|
304
328
|
return data
|
305
329
|
}
|
306
330
|
|
307
|
-
|
331
|
+
/**
|
332
|
+
* 根据行元素获取colgroup的col数量
|
333
|
+
* @param row
|
334
|
+
* @returns
|
335
|
+
*/
|
308
336
|
export const getColNumbers = function (row: AlexElement) {
|
309
337
|
const children = row.children || []
|
310
338
|
let num = 0
|
@@ -321,7 +349,11 @@ export const getColNumbers = function (row: AlexElement) {
|
|
321
349
|
return num
|
322
350
|
}
|
323
351
|
|
324
|
-
|
352
|
+
/**
|
353
|
+
* 获取菜单按钮列表数据配置
|
354
|
+
* @param editTrans
|
355
|
+
* @returns
|
356
|
+
*/
|
325
357
|
export const getButtonOptionsConfig = function (editTrans: (key: string) => any): ButtonOptionsConfigType {
|
326
358
|
return {
|
327
359
|
//标题配置
|
@@ -517,7 +549,12 @@ export const getButtonOptionsConfig = function (editTrans: (key: string) => any)
|
|
517
549
|
}
|
518
550
|
}
|
519
551
|
|
520
|
-
|
552
|
+
/**
|
553
|
+
* 工具条全量配置
|
554
|
+
* @param editTrans
|
555
|
+
* @param editLocale
|
556
|
+
* @returns
|
557
|
+
*/
|
521
558
|
export const getToolbarConfig = function (editTrans: (key: string) => any, editLocale: LocaleType): ToolbarConfigType {
|
522
559
|
return {
|
523
560
|
//是否使用工具条
|
@@ -762,7 +799,12 @@ export const getToolbarConfig = function (editTrans: (key: string) => any, editL
|
|
762
799
|
}
|
763
800
|
}
|
764
801
|
|
765
|
-
|
802
|
+
/**
|
803
|
+
* 菜单栏全量配置
|
804
|
+
* @param editTrans
|
805
|
+
* @param editLocale
|
806
|
+
* @returns
|
807
|
+
*/
|
766
808
|
export const getMenuConfig = function (editTrans: (key: string) => any, editLocale: LocaleType): MenuConfigType {
|
767
809
|
return {
|
768
810
|
//是否使用菜单栏
|
package/src/editify/editify.less
CHANGED
@@ -348,7 +348,7 @@
|
|
348
348
|
color: @font-color-link;
|
349
349
|
transition: all 200ms;
|
350
350
|
position: relative;
|
351
|
-
padding: 0
|
351
|
+
padding: 0 10px;
|
352
352
|
font-size: 14px;
|
353
353
|
vertical-align: baseline;
|
354
354
|
|
@@ -378,6 +378,7 @@
|
|
378
378
|
&.editify-placeholder::before {
|
379
379
|
cursor: auto;
|
380
380
|
}
|
381
|
+
|
381
382
|
:deep(a) {
|
382
383
|
cursor: pointer;
|
383
384
|
}
|
package/src/editify/editify.vue
CHANGED
@@ -5,7 +5,7 @@
|
|
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 }" @
|
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>
|
9
9
|
<!-- 代码视图 -->
|
10
10
|
<textarea v-if="isSourceView" :value="value" readonly class="editify-sourceview" />
|
11
11
|
<!-- 工具条 -->
|
@@ -41,12 +41,10 @@ const instance = getCurrentInstance()!
|
|
41
41
|
//属性
|
42
42
|
const props = defineProps(EditifyProps)
|
43
43
|
//事件
|
44
|
-
const emits = defineEmits(['update:modelValue', 'focus', 'blur', 'change', 'keydown', 'insertparagraph', 'rangeupdate', 'updateview'])
|
44
|
+
const emits = defineEmits(['update:modelValue', 'focus', 'blur', 'change', 'keydown', 'keyup', 'insertparagraph', 'rangeupdate', 'updateview'])
|
45
45
|
|
46
46
|
//设置国际化方法
|
47
47
|
const $editTrans = trans(props.locale || 'zh_CN')
|
48
|
-
//对子孙后代组件提供国际化方法
|
49
|
-
provide('$editTrans', $editTrans)
|
50
48
|
|
51
49
|
//是否编辑器内部修改值
|
52
50
|
const isModelChange = ref<boolean>(false)
|
@@ -135,15 +133,7 @@ const pluginResultList = computed<PluginResultType[]>(() => {
|
|
135
133
|
})
|
136
134
|
//最终生效的菜单栏配置
|
137
135
|
const menuConfig = computed<MenuConfigType>(() => {
|
138
|
-
|
139
|
-
//注册插件:自定义菜单栏
|
140
|
-
pluginResultList.value.forEach(pluginResult => {
|
141
|
-
menu = <MenuConfigType>mergeObject(menu, pluginResult.menu || {})
|
142
|
-
})
|
143
|
-
//加入自定义menu配置
|
144
|
-
menu = <MenuConfigType>mergeObject(menu, props.menu || {})
|
145
|
-
//返回最终配置
|
146
|
-
return <MenuConfigType>mergeObject(getMenuConfig($editTrans, props.locale), menu)
|
136
|
+
return <MenuConfigType>mergeObject(getMenuConfig($editTrans, props.locale), props.menu || {})
|
147
137
|
})
|
148
138
|
|
149
139
|
//编辑器内部修改值的方法
|
@@ -303,6 +293,8 @@ const createEditor = () => {
|
|
303
293
|
editor.value.on('change', handleEditorChange)
|
304
294
|
editor.value.on('focus', handleEditorFocus)
|
305
295
|
editor.value.on('blur', handleEditorBlur)
|
296
|
+
editor.value.on('keydown', handleEditorKeydown)
|
297
|
+
editor.value.on('keyup', handleEditorKeyup)
|
306
298
|
editor.value.on('insertParagraph', handleInsertParagraph)
|
307
299
|
editor.value.on('rangeUpdate', handleRangeUpdate)
|
308
300
|
editor.value.on('deleteInStart', handleDeleteInStart)
|
@@ -522,7 +514,7 @@ const handleCustomParseNode = (ele: AlexElement) => {
|
|
522
514
|
return ele
|
523
515
|
}
|
524
516
|
//编辑区域键盘按下:设置缩进快捷键
|
525
|
-
const handleEditorKeydown = (e: Event) => {
|
517
|
+
const handleEditorKeydown = (val: string, e: Event) => {
|
526
518
|
if (props.disabled) {
|
527
519
|
return
|
528
520
|
}
|
@@ -535,7 +527,15 @@ const handleEditorKeydown = (e: Event) => {
|
|
535
527
|
editor.value!.rangeRender()
|
536
528
|
}
|
537
529
|
//自定义键盘按下操作
|
538
|
-
emits('keydown', e)
|
530
|
+
emits('keydown', val, e)
|
531
|
+
}
|
532
|
+
//编辑区域键盘松开
|
533
|
+
const handleEditorKeyup = (val: string, e: Event) => {
|
534
|
+
if (props.disabled) {
|
535
|
+
return
|
536
|
+
}
|
537
|
+
//自定义键盘松开操作
|
538
|
+
emits('keyup', val, e)
|
539
539
|
}
|
540
540
|
//点击编辑器:处理图片和视频的光标聚集
|
541
541
|
const handleEditorClick = (e: Event) => {
|
@@ -625,8 +625,8 @@ const handleEditorFocus = (val: string) => {
|
|
625
625
|
const handleInsertParagraph = (element: AlexElement, previousElement: AlexElement) => {
|
626
626
|
//两个元素不一致,则表示不在代码块样式内
|
627
627
|
if (!element.isEqual(previousElement)) {
|
628
|
-
|
629
|
-
if (previousElement.isOnlyHasBreak() && element.isOnlyHasBreak()) {
|
628
|
+
//前一个根级块元素如果是只包含换行符的元素,并且当前根级块元素也是包含换行符的元素,则当前根级块元素转为段落
|
629
|
+
if (previousElement.isBlock() && element.isBlock() && previousElement.isOnlyHasBreak() && element.isOnlyHasBreak()) {
|
630
630
|
if (previousElement.parsedom != AlexElement.BLOCK_NODE) {
|
631
631
|
elementToParagraph(previousElement)
|
632
632
|
editor.value!.range!.anchor.moveToStart(previousElement)
|
@@ -674,6 +674,7 @@ const handleRangeUpdate = () => {
|
|
674
674
|
}
|
675
675
|
}
|
676
676
|
}, 200)
|
677
|
+
//触发rangeupdate事件
|
677
678
|
emits('rangeupdate')
|
678
679
|
}
|
679
680
|
//编辑器部分删除情景(在编辑器起始处)
|
@@ -830,6 +831,7 @@ onBeforeUnmount(() => {
|
|
830
831
|
editor.value!.destroy()
|
831
832
|
})
|
832
833
|
|
834
|
+
provide('$editTrans', $editTrans)
|
833
835
|
provide('editify', instance)
|
834
836
|
provide('isSourceView', isSourceView)
|
835
837
|
provide('isFullScreen', isFullScreen)
|
@@ -837,6 +839,7 @@ provide('canUseMenu', canUseMenu)
|
|
837
839
|
provide('editor', editor)
|
838
840
|
provide('dataRangeCaches', dataRangeCaches)
|
839
841
|
provide('showBorder', showBorder)
|
842
|
+
provide('pluginResultList', pluginResultList)
|
840
843
|
|
841
844
|
defineExpose({
|
842
845
|
editor,
|
package/src/hljs/index.ts
CHANGED
@@ -74,7 +74,12 @@ export type LanguagesItemType = {
|
|
74
74
|
value?: string
|
75
75
|
}
|
76
76
|
|
77
|
-
|
77
|
+
/**
|
78
|
+
* 获取经过hljs处理的html元素
|
79
|
+
* @param code
|
80
|
+
* @param language
|
81
|
+
* @returns
|
82
|
+
*/
|
78
83
|
export const getHljsHtml = function (code: string, language: string) {
|
79
84
|
if (language) {
|
80
85
|
return hljs.highlight(code, {
|
@@ -84,7 +89,9 @@ export const getHljsHtml = function (code: string, language: string) {
|
|
84
89
|
}
|
85
90
|
return hljs.highlightAuto(code).value
|
86
91
|
}
|
87
|
-
|
92
|
+
/**
|
93
|
+
* 可选择语言列表
|
94
|
+
*/
|
88
95
|
export const languages: LanguagesItemType[] = [
|
89
96
|
{
|
90
97
|
label: 'Plain Text',
|
package/src/index.ts
CHANGED
@@ -9,7 +9,7 @@ export type { ButtonTypeType, ButtonOptionsItemType, ButtonSelectConfigType, But
|
|
9
9
|
export type { InsertImageUploadErrorType } from './components/insertImage/props'
|
10
10
|
export type { InsertVideoUploadErrorType } from './components/insertVideo/props'
|
11
11
|
export type { MenuButtonType, MenuSelectButtonType, MenuDisplayButtonType, MenuImageButtonType, MenuVideoButtonType, MenuTableButtonType, MenuCustomButtonType, CodeBlockToolbarType, TextToolbarType, ToolbarConfigType, MenuSequenceType, MenuModeType, MenuExtendType, MenuConfigType, PluginType, PluginResultType } from './core/tool'
|
12
|
-
|
12
|
+
//导出插件相关类型
|
13
13
|
export type { AttachmentOptionsType } from './plugins/attachment'
|
14
14
|
export type { InsertAttachmentUploadErrorType } from './plugins/attachment/insertAttachment/props'
|
15
15
|
|
@@ -21,13 +21,15 @@ const install: FunctionPlugin = (app: App) => {
|
|
21
21
|
app.component(Editify.name!, Editify)
|
22
22
|
}
|
23
23
|
//版本号
|
24
|
-
const version = '0.1.
|
24
|
+
const version = '0.1.29'
|
25
25
|
|
26
26
|
//导出AlexElement元素
|
27
27
|
export { AlexElement } from 'alex-editor'
|
28
28
|
|
29
|
+
//导出插件
|
30
|
+
export { attachment } from './plugins/attachment'
|
31
|
+
|
29
32
|
//导出组件和安装函数
|
30
33
|
export { install as default, install, Editify, version }
|
31
34
|
|
32
|
-
|
33
|
-
export { attachment } from './plugins/attachment'
|
35
|
+
console.log(`%c vue-editify %c v${version} `, 'padding: 2px 1px; border-radius: 3px 0 0 3px; color: #fff; background: #606060; font-weight: bold;', 'padding: 2px 1px; border-radius: 0 3px 3px 0; color: #fff; background: #42c02e; font-weight: bold;')
|
package/src/locale/en_US.ts
CHANGED
@@ -90,7 +90,8 @@ export const en_US: ObjectType = {
|
|
90
90
|
insertAttachment: 'Insert attachment',
|
91
91
|
uploadAttachment: 'Upload',
|
92
92
|
remoteAttachment: 'Remote',
|
93
|
+
attachmentNamePlaceholder: 'Please enter the attachment name',
|
93
94
|
attachmentUrlPlaceholder: 'Please enter the attachment address',
|
94
|
-
|
95
|
-
|
95
|
+
attachmentDownloadTitle: 'Click to download attachment',
|
96
|
+
attachmentDefaultName: 'attachment'
|
96
97
|
}
|
package/src/locale/index.ts
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
import { en_US } from './en_US'
|
2
2
|
import { zh_CN } from './zh_CN'
|
3
3
|
|
4
|
-
//语言类型
|
5
4
|
export type LocaleType = 'zh_CN' | 'en_US'
|
6
5
|
|
7
|
-
|
6
|
+
/**
|
7
|
+
* 翻译方法
|
8
|
+
* @param locale
|
9
|
+
* @returns
|
10
|
+
*/
|
8
11
|
export const trans = (locale: LocaleType) => {
|
9
12
|
return (key: string) => {
|
10
13
|
return { zh_CN, en_US }[locale][key]
|
package/src/locale/zh_CN.ts
CHANGED
@@ -90,7 +90,8 @@ export const zh_CN: ObjectType = {
|
|
90
90
|
insertAttachment: '插入附件',
|
91
91
|
uploadAttachment: '上传附件',
|
92
92
|
remoteAttachment: '远程地址',
|
93
|
-
|
94
|
-
|
95
|
-
|
93
|
+
attachmentNamePlaceholder: '请输入附件名称',
|
94
|
+
attachmentUrlPlaceholder: '请输入附件地址',
|
95
|
+
attachmentDownloadTitle: '点击下载附件',
|
96
|
+
attachmentDefaultName: '附件'
|
96
97
|
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { ComponentInternalInstance, h } from 'vue'
|
2
|
-
import { AlexEditor, AlexElement } from 'alex-editor'
|
2
|
+
import { AlexEditor, AlexElement, AlexElementsRangeType } from 'alex-editor'
|
3
3
|
import { ObjectType, PluginType } from '../../core/tool'
|
4
4
|
import Layer from '../../components/layer/layer.vue'
|
5
5
|
import Button from '../../components/button/button.vue'
|
@@ -7,7 +7,7 @@ import Icon from '../../components/icon/icon.vue'
|
|
7
7
|
import InsertAttachment from './insertAttachment/insertAttachment.vue'
|
8
8
|
import { InsertAttachmentUploadErrorType } from './insertAttachment/props'
|
9
9
|
import { event as DapEvent, common as DapCommon } from 'dap-util'
|
10
|
-
import { hasPreInRange } from '../../core/function'
|
10
|
+
import { hasLinkInRange, hasPreInRange, hasQuoteInRange } from '../../core/function'
|
11
11
|
|
12
12
|
export type AttachmentOptionsType = {
|
13
13
|
//排序
|
@@ -19,7 +19,7 @@ export type AttachmentOptionsType = {
|
|
19
19
|
//按钮是否显示右侧边框
|
20
20
|
rightBorder?: boolean
|
21
21
|
//定义可选择的文件类型,默认不限制类型,设定此参数后选择文件时会自动过滤非符合的文件类型
|
22
|
-
accept?:
|
22
|
+
accept?: string
|
23
23
|
//支持的类型数组
|
24
24
|
allowedFileType?: string[]
|
25
25
|
//是否多选
|
@@ -34,42 +34,90 @@ export type AttachmentOptionsType = {
|
|
34
34
|
handleError?: (error: InsertAttachmentUploadErrorType, file: File) => void
|
35
35
|
}
|
36
36
|
|
37
|
+
/**
|
38
|
+
* 元素是否附件
|
39
|
+
* @param element
|
40
|
+
* @returns
|
41
|
+
*/
|
42
|
+
export const isAttachment = (element: AlexElement) => {
|
43
|
+
if (element.isEmpty()) {
|
44
|
+
return false
|
45
|
+
}
|
46
|
+
return element.parsedom == 'span' && element.type == 'closed' && element.hasMarks() && element.marks!['data-attachment']
|
47
|
+
}
|
48
|
+
|
49
|
+
/**
|
50
|
+
* 选区是否含有附件
|
51
|
+
* @param editor
|
52
|
+
* @param dataRangeCaches
|
53
|
+
* @returns
|
54
|
+
*/
|
55
|
+
export const hasAttachmentInRange = (editor: AlexEditor, dataRangeCaches: AlexElementsRangeType) => {
|
56
|
+
if (!editor.range) {
|
57
|
+
return false
|
58
|
+
}
|
59
|
+
if (editor.range.anchor.isEqual(editor.range.focus)) {
|
60
|
+
return isAttachment(editor.range.anchor.element)
|
61
|
+
}
|
62
|
+
return dataRangeCaches.flatList.some(item => {
|
63
|
+
return isAttachment(item.element)
|
64
|
+
})
|
65
|
+
}
|
66
|
+
|
67
|
+
/**
|
68
|
+
* 附件插件
|
69
|
+
* @param options
|
70
|
+
* @returns
|
71
|
+
*/
|
37
72
|
export const attachment = (options?: AttachmentOptionsType) => {
|
38
73
|
if (!DapCommon.isObject(options)) {
|
39
74
|
options = {}
|
40
75
|
}
|
41
76
|
const plugin: PluginType = (editifyInstance: ComponentInternalInstance, color: string | null, editTrans: (key: string) => any) => {
|
77
|
+
let isDisabled = false
|
78
|
+
//如果光标范围内有链接、代码块和引用则禁用
|
79
|
+
if (editifyInstance.exposed!.editor.value) {
|
80
|
+
isDisabled = hasPreInRange(editifyInstance.exposed!.editor.value, editifyInstance.exposed!.dataRangeCaches.value) || hasLinkInRange(editifyInstance.exposed!.editor.value, editifyInstance.exposed!.dataRangeCaches.value) || hasQuoteInRange(editifyInstance.exposed!.editor.value, editifyInstance.exposed!.dataRangeCaches.value)
|
81
|
+
}
|
42
82
|
return {
|
83
|
+
name: 'attachment',
|
43
84
|
//附件菜单项配置
|
44
85
|
menu: {
|
45
|
-
sequence:
|
46
|
-
|
86
|
+
sequence: options!.sequence || 100,
|
87
|
+
extraDisabled: (name: string) => {
|
88
|
+
//如果光标选区内有附件则禁用链接菜单和引用菜单
|
89
|
+
if (name == 'link' || name == 'quote') {
|
90
|
+
return hasAttachmentInRange(editifyInstance.exposed!.editor.value, editifyInstance.exposed!.dataRangeCaches.value)
|
91
|
+
}
|
92
|
+
return false
|
47
93
|
},
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
94
|
+
extend: {
|
95
|
+
type: 'select',
|
96
|
+
title: options!.title || editTrans('insertAttachment'),
|
97
|
+
leftBorder: options!.leftBorder,
|
98
|
+
rightBorder: options!.rightBorder,
|
99
|
+
hideScroll: true,
|
100
|
+
disabled: isDisabled,
|
101
|
+
default: () => h(Icon, { value: 'attachment' }),
|
102
|
+
layer: (_name: string, btnInstance: InstanceType<typeof Button>) =>
|
103
|
+
h(InsertAttachment, {
|
104
|
+
color: color,
|
105
|
+
accept: options!.accept,
|
106
|
+
allowedFileType: options!.allowedFileType || [],
|
107
|
+
multiple: !!options!.multiple,
|
108
|
+
maxSize: options!.maxSize,
|
109
|
+
minSize: options!.minSize,
|
110
|
+
customUpload: options!.customUpload,
|
111
|
+
handleError: options!.handleError,
|
112
|
+
onChange: () => {
|
113
|
+
;(<InstanceType<typeof Layer>>btnInstance.$refs.layerRef).setPosition()
|
114
|
+
},
|
115
|
+
onInsert: (name: string, url: string) => {
|
116
|
+
//如果地址存在
|
117
|
+
if (url) {
|
70
118
|
const marks: ObjectType = {
|
71
119
|
'data-attachment': url,
|
72
|
-
'data-attachment-name': editTrans('
|
120
|
+
'data-attachment-name': name || editTrans('attachmentDefaultName'),
|
73
121
|
contenteditable: 'false'
|
74
122
|
}
|
75
123
|
//创建元素
|
@@ -91,11 +139,11 @@ export const attachment = (options?: AttachmentOptionsType) => {
|
|
91
139
|
editor.formatElementStack()
|
92
140
|
editor.domRender()
|
93
141
|
editor.rangeRender()
|
94
|
-
//关闭浮层
|
95
|
-
btnInstance.show = false
|
96
142
|
}
|
97
|
-
|
98
|
-
|
143
|
+
//关闭浮层
|
144
|
+
btnInstance.show = false
|
145
|
+
}
|
146
|
+
})
|
99
147
|
}
|
100
148
|
},
|
101
149
|
//找到附件元素点击下载
|
@@ -104,11 +152,21 @@ export const attachment = (options?: AttachmentOptionsType) => {
|
|
104
152
|
AlexElement.flatElements(editor.stack).forEach(el => {
|
105
153
|
if (el.parsedom == 'span' && el.hasMarks() && el.marks!['data-attachment']) {
|
106
154
|
DapEvent.off(<HTMLElement>el.elm, 'click')
|
107
|
-
|
155
|
+
//单击下载
|
156
|
+
DapEvent.on(<HTMLElement>el.elm, 'click', async () => {
|
157
|
+
//获取文件地址
|
108
158
|
const url = el.marks!['data-attachment']
|
159
|
+
//使用fetch读取文件地址
|
160
|
+
const res = await fetch(url, {
|
161
|
+
method: 'GET'
|
162
|
+
})
|
163
|
+
//获取blob数据
|
164
|
+
const blob = await res.blob()
|
165
|
+
//创建a标签进行下载
|
109
166
|
const a = document.createElement('a')
|
110
|
-
a.setAttribute('
|
111
|
-
a.setAttribute('
|
167
|
+
a.setAttribute('target', '_blank')
|
168
|
+
a.setAttribute('href', URL.createObjectURL(blob))
|
169
|
+
a.setAttribute('download', el.marks!['data-attachment-name'])
|
112
170
|
a.click()
|
113
171
|
})
|
114
172
|
}
|
@@ -130,7 +188,7 @@ export const attachment = (options?: AttachmentOptionsType) => {
|
|
130
188
|
renderRule: (el: AlexElement) => {
|
131
189
|
if (el.type == 'closed' && el.hasMarks() && el.marks!['data-attachment']) {
|
132
190
|
//设置title
|
133
|
-
el.marks!['title'] = editTrans('
|
191
|
+
el.marks!['title'] = editTrans('attachmentDownloadTitle')
|
134
192
|
//获取editor对象
|
135
193
|
const editor = <AlexEditor>editifyInstance.exposed!.editor.value
|
136
194
|
//前一个元素
|
@@ -8,9 +8,10 @@
|
|
8
8
|
justify-content: flex-start;
|
9
9
|
align-items: center;
|
10
10
|
width: 100%;
|
11
|
-
margin-bottom:
|
11
|
+
margin-bottom: 10px;
|
12
12
|
position: relative;
|
13
13
|
padding-bottom: 6px;
|
14
|
+
user-select: none;
|
14
15
|
|
15
16
|
.editify-attachment-header-slider {
|
16
17
|
position: absolute;
|
@@ -107,29 +108,57 @@
|
|
107
108
|
|
108
109
|
.editify-attachment-upload {
|
109
110
|
display: flex;
|
110
|
-
justify-content:
|
111
|
-
align-items:
|
111
|
+
justify-content: space-between;
|
112
|
+
align-items: flex-end;
|
112
113
|
width: 100%;
|
113
114
|
padding: 15px 0;
|
114
|
-
font-size: 36px;
|
115
|
-
opacity: 0.8;
|
116
|
-
transition: all 200ms;
|
117
|
-
position: relative;
|
118
115
|
|
119
|
-
|
120
|
-
|
121
|
-
|
116
|
+
input[type='text'] {
|
117
|
+
appearance: none;
|
118
|
+
-webkit-appearance: none;
|
119
|
+
-moz-appearance: none;
|
120
|
+
display: block;
|
121
|
+
width: 100%;
|
122
|
+
margin: 0;
|
123
|
+
padding: 4px 2px;
|
124
|
+
border: none;
|
125
|
+
font-size: @font-size;
|
126
|
+
color: @font-color;
|
127
|
+
border-bottom: 1px solid @border-color;
|
128
|
+
line-height: 1.5;
|
129
|
+
transition: border-color 500ms;
|
130
|
+
background-color: transparent;
|
131
|
+
outline: none;
|
132
|
+
box-sizing: border-box;
|
133
|
+
|
134
|
+
&::-webkit-input-placeholder,
|
135
|
+
&::placeholder {
|
136
|
+
color: @font-color-disabled;
|
137
|
+
font-family: inherit;
|
138
|
+
font-size: inherit;
|
139
|
+
vertical-align: middle;
|
140
|
+
}
|
122
141
|
}
|
123
142
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
143
|
+
.editify-attachment-btn {
|
144
|
+
position: relative;
|
145
|
+
font-size: 20px;
|
146
|
+
line-height: 1;
|
147
|
+
opacity: 0.8;
|
148
|
+
transition: all 200ms;
|
149
|
+
color: @font-color;
|
150
|
+
border-radius: 2px;
|
151
|
+
padding: 2px;
|
152
|
+
margin-left: 15px;
|
153
|
+
|
154
|
+
&:hover {
|
155
|
+
cursor: pointer;
|
156
|
+
opacity: 1;
|
157
|
+
}
|
158
|
+
|
159
|
+
input[type='file'] {
|
160
|
+
display: none;
|
161
|
+
}
|
133
162
|
}
|
134
163
|
}
|
135
164
|
}
|