vue-editify 0.1.26 → 0.1.29
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
}
|