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/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
- menu?: MenuConfigType
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
- //粘贴html时保留的数据
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
- //根据行元素获取colgroup的col数量
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
  //是否使用菜单栏
@@ -348,7 +348,7 @@
348
348
  color: @font-color-link;
349
349
  transition: all 200ms;
350
350
  position: relative;
351
- padding: 0 2px;
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
  }
@@ -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 }" @keydown="handleEditorKeydown" @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': 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
- let menu: MenuConfigType = {}
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
- //获取经过hljs处理的html元素
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.26'
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;')
@@ -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
- downloadAttachment: 'Click to download attachment',
95
- attachmentDownloadName: 'attachment'
95
+ attachmentDownloadTitle: 'Click to download attachment',
96
+ attachmentDefaultName: 'attachment'
96
97
  }
@@ -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]
@@ -90,7 +90,8 @@ export const zh_CN: ObjectType = {
90
90
  insertAttachment: '插入附件',
91
91
  uploadAttachment: '上传附件',
92
92
  remoteAttachment: '远程地址',
93
- attachmentUrlPlaceholder: '请输入远程地址',
94
- downloadAttachment: '点击下载附件',
95
- attachmentDownloadName: '附件'
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?: 'rar' | 'zip' | 'txt' | 'image' | 'video' | 'audio' | 'html' | 'doc' | 'xml' | 'js' | 'json' | 'ppt' | 'pdf' | null
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
- attachment: options!.sequence || 100
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
- extends: {
49
- attachment: {
50
- type: 'select',
51
- title: options!.title || editTrans('insertAttachment'),
52
- leftBorder: options!.leftBorder,
53
- rightBorder: options!.rightBorder,
54
- disabled: editifyInstance.exposed!.editor.value ? hasPreInRange(editifyInstance.exposed!.editor.value, editifyInstance.exposed!.dataRangeCaches.value) : false,
55
- default: () => h(Icon, { value: 'attachment' }),
56
- layer: (_name: string, btnInstance: InstanceType<typeof Button>) =>
57
- h(InsertAttachment, {
58
- color: color,
59
- accept: options!.accept,
60
- allowedFileType: options!.allowedFileType || [],
61
- multiple: !!options!.multiple,
62
- maxSize: options!.maxSize,
63
- minSize: options!.minSize,
64
- customUpload: options!.customUpload,
65
- handleError: options!.handleError,
66
- onChange: () => {
67
- ;(<InstanceType<typeof Layer>>btnInstance.$refs.layerRef).setPosition()
68
- },
69
- onInsert: (url: string) => {
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('attachmentDownloadName'),
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
- DapEvent.on(<HTMLElement>el.elm, 'click', () => {
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('href', url)
111
- a.setAttribute('download', editTrans('attachmentDownloadName'))
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('downloadAttachment')
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: 20px;
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: center;
111
- align-items: center;
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
- &:hover {
120
- cursor: pointer;
121
- opacity: 1;
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
- input {
125
- opacity: 0;
126
- position: absolute;
127
- left: 0;
128
- top: 0;
129
- width: 100%;
130
- height: 100%;
131
- z-index: 1;
132
- cursor: pointer;
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
  }