vue-editify 0.1.21 → 0.1.23

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. package/examples/App.vue +8 -69
  2. package/examples/test.html +32 -0
  3. package/lib/components/button/button.vue.d.ts +3 -3
  4. package/lib/components/button/props.d.ts +1 -1
  5. package/lib/components/checkbox/checkbox.vue.d.ts +3 -3
  6. package/lib/components/checkbox/props.d.ts +1 -1
  7. package/lib/components/colors/colors.vue.d.ts +3 -3
  8. package/lib/components/colors/props.d.ts +1 -1
  9. package/lib/components/insertImage/insertImage.vue.d.ts +6 -6
  10. package/lib/components/insertImage/props.d.ts +2 -2
  11. package/lib/components/insertLink/insertLink.vue.d.ts +3 -3
  12. package/lib/components/insertLink/props.d.ts +2 -2
  13. package/lib/components/insertTable/insertTable.vue.d.ts +3 -3
  14. package/lib/components/insertTable/props.d.ts +2 -2
  15. package/lib/components/insertVideo/insertVideo.vue.d.ts +6 -6
  16. package/lib/components/insertVideo/props.d.ts +2 -2
  17. package/lib/components/menu/menu.vue.d.ts +3 -3
  18. package/lib/components/menu/props.d.ts +1 -1
  19. package/lib/components/toolbar/props.d.ts +1 -1
  20. package/lib/components/toolbar/toolbar.vue.d.ts +3 -3
  21. package/lib/core/rule.d.ts +1 -1
  22. package/lib/core/tool.d.ts +19 -9
  23. package/lib/editify/editify.vue.d.ts +12 -3
  24. package/lib/editify/props.d.ts +6 -2
  25. package/lib/editify.es.js +577 -188
  26. package/lib/editify.umd.js +1 -1
  27. package/lib/index.d.ts +13 -15
  28. package/lib/plugins/attachment/index.d.ts +17 -0
  29. package/lib/plugins/attachment/insertAttachment/insertAttachment.vue.d.ts +83 -0
  30. package/lib/plugins/attachment/insertAttachment/props.d.ts +38 -0
  31. package/lib/style.css +1 -1
  32. package/package.json +1 -1
  33. package/src/components/button/button.vue +4 -1
  34. package/src/components/button/props.ts +1 -1
  35. package/src/components/checkbox/props.ts +1 -1
  36. package/src/components/colors/colors.vue +1 -1
  37. package/src/components/colors/props.ts +1 -1
  38. package/src/components/insertImage/insertImage.vue +12 -10
  39. package/src/components/insertImage/props.ts +2 -2
  40. package/src/components/insertLink/insertLink.vue +1 -1
  41. package/src/components/insertLink/props.ts +2 -2
  42. package/src/components/insertTable/props.ts +2 -2
  43. package/src/components/insertVideo/insertVideo.vue +12 -10
  44. package/src/components/insertVideo/props.ts +2 -2
  45. package/src/components/menu/menu.vue +28 -13
  46. package/src/components/menu/props.ts +1 -1
  47. package/src/components/toolbar/props.ts +1 -1
  48. package/src/components/toolbar/toolbar.vue +3 -4
  49. package/src/core/function.ts +5 -5
  50. package/src/core/rule.ts +1 -1
  51. package/src/core/tool.ts +24 -11
  52. package/src/editify/editify.less +27 -0
  53. package/src/editify/editify.vue +50 -6
  54. package/src/editify/props.ts +9 -2
  55. package/src/icon/iconfont.css +4 -0
  56. package/src/icon/iconfont.ttf +0 -0
  57. package/src/icon/iconfont.woff +0 -0
  58. package/src/index.ts +23 -22
  59. package/src/locale/en_US.ts +9 -1
  60. package/src/locale/zh_CN.ts +9 -1
  61. package/src/plugins/attachment/images/attachment.png +0 -0
  62. package/src/plugins/attachment/index.ts +121 -0
  63. package/src/plugins/attachment/insertAttachment/insertAttachment.less +135 -0
  64. package/src/plugins/attachment/insertAttachment/insertAttachment.vue +189 -0
  65. package/src/plugins/attachment/insertAttachment/props.ts +48 -0
package/src/index.ts CHANGED
@@ -1,32 +1,33 @@
1
- //引入AlexElement
2
- import { AlexElement } from 'alex-editor'
1
+ import { App, FunctionPlugin } from 'vue'
2
+ //引入字体图标样式
3
+ import './icon/iconfont.css'
3
4
  //引入组件
4
5
  import Editify from './editify/editify.vue'
5
- //引入图标样式
6
- import './icon/iconfont.css'
7
6
 
8
- import { App } from 'vue'
9
- import { ButtonTypeType, ButtonOptionsItemType, ButtonSelectConfigType, ButtonDisplayConfigType } from './components/button/props'
10
- import { MenuButtonType, MenuSelectButtonType, MenuDisplayButtonType, MenuImageButtonType, MenuVideoButtonType, MenuTableButtonType, MenuCustomButtonType, CodeBlockToolbarType, TextToolbarType, ToolbarConfigType, MenuSequenceType, MenuModeType, MenuConfigType } from './core/tool'
11
- import { InsertImageUploadErrorType } from './components/insertImage/props'
12
- import { InsertVideoUploadErrorType } from './components/insertVideo/props'
7
+ //导出类型
8
+ export type { ButtonTypeType, ButtonOptionsItemType, ButtonSelectConfigType, ButtonDisplayConfigType } from './components/button/props'
9
+ export type { InsertImageUploadErrorType } from './components/insertImage/props'
10
+ export type { InsertVideoUploadErrorType } from './components/insertVideo/props'
11
+ export type { MenuButtonType, MenuSelectButtonType, MenuDisplayButtonType, MenuImageButtonType, MenuVideoButtonType, MenuTableButtonType, MenuCustomButtonType, CodeBlockToolbarType, TextToolbarType, ToolbarConfigType, MenuSequenceType, MenuModeType, MenuExtendType, MenuConfigType, PluginType, PluginResultType } from './core/tool'
12
+ //插件相关类型
13
+ export type { AttachmentOptionsType } from './plugins/attachment'
14
+ export type { InsertAttachmentUploadErrorType } from './plugins/attachment/insertAttachment/props'
15
+
16
+ //导出编辑器操作方法
17
+ export { getParsedomElementByElement, getCurrentParsedomElement, elementIsInList, elementIsInTask, isList, isTask, hasPreInRange, isRangeInPre, hasQuoteInRange, isRangeInQuote, hasListInRange, isRangeInList, hasTaskInRange, isRangeInTask, hasLinkInRange, hasTableInRange, hasImageInRange, hasVideoInRange, queryTextStyle, queryTextMark, getRangeText, setIndentIncrease, setIndentDecrease, setQuote, setAlign, setList, setTask, setTextStyle, setTextMark, removeTextStyle, removeTextMark, setLineHeight, insertLink, insertImage, insertVideo, insertTable, insertCodeBlock } from './core/function'
13
18
 
14
- //版本号
15
- const version = '0.1.21'
16
19
  //安装函数
17
- const install = (app: App) => {
20
+ const install: FunctionPlugin = (app: App) => {
18
21
  app.component(Editify.name!, Editify)
19
22
  }
20
- //全局导出的对象
21
- const stdin_default = {
22
- install,
23
- version
24
- }
23
+ //版本号
24
+ const version = '0.1.23'
25
25
 
26
- //导出一些编辑器操作方法
27
- export * from './core/function'
26
+ //导出AlexElement元素
27
+ export { AlexElement } from 'alex-editor'
28
28
 
29
- //导出类型
30
- export type { ButtonTypeType, ButtonOptionsItemType, ButtonSelectConfigType, ButtonDisplayConfigType, MenuButtonType, MenuSelectButtonType, MenuDisplayButtonType, MenuImageButtonType, MenuVideoButtonType, MenuTableButtonType, MenuCustomButtonType, CodeBlockToolbarType, TextToolbarType, ToolbarConfigType, MenuSequenceType, MenuModeType, MenuConfigType, InsertImageUploadErrorType, InsertVideoUploadErrorType }
29
+ //导出组件和安装函数
30
+ export { install as default, install, Editify, version }
31
31
 
32
- export { stdin_default as default, install, version, Editify, AlexElement }
32
+ //导出插件
33
+ export { attachment } from './plugins/attachment'
@@ -84,5 +84,13 @@ export const en_US: ObjectType = {
84
84
  alignJustify: 'Align justify',
85
85
  defaultLineHeight: 'Default',
86
86
  auto: 'auto',
87
- fullScreen: 'Full screen'
87
+ fullScreen: 'Full screen',
88
+
89
+ //插件语言配置
90
+ insertAttachment: 'Insert attachment',
91
+ uploadAttachment: 'Upload',
92
+ remoteAttachment: 'Remote',
93
+ attachmentUrlPlaceholder: 'Please enter the attachment address',
94
+ downloadAttachment: 'Click to download attachment',
95
+ attachmentDownloadName: 'attachment'
88
96
  }
@@ -84,5 +84,13 @@ export const zh_CN: ObjectType = {
84
84
  alignJustify: '两端对齐',
85
85
  defaultLineHeight: '默认行高',
86
86
  auto: '自适应',
87
- fullScreen: '全屏'
87
+ fullScreen: '全屏',
88
+
89
+ //插件语言配置
90
+ insertAttachment: '插入附件',
91
+ uploadAttachment: '上传附件',
92
+ remoteAttachment: '远程地址',
93
+ attachmentUrlPlaceholder: '请输入远程地址',
94
+ downloadAttachment: '点击下载附件',
95
+ attachmentDownloadName: '附件'
88
96
  }
@@ -0,0 +1,121 @@
1
+ import { ComponentInternalInstance, h } from 'vue'
2
+ import { AlexEditor, AlexElement } from 'alex-editor'
3
+ import { PluginType } from '../../core/tool'
4
+ import Layer from '../../components/layer/layer.vue'
5
+ import Button from '../../components/button/button.vue'
6
+ import Icon from '../../components/icon/icon.vue'
7
+ import InsertAttachment from './insertAttachment/insertAttachment.vue'
8
+ import { InsertAttachmentUploadErrorType } from './insertAttachment/props'
9
+ import { event as DapEvent, common as DapCommon } from 'dap-util'
10
+
11
+ export type AttachmentOptionsType = {
12
+ //排序
13
+ sequence?: number
14
+ //工具提示内容
15
+ title?: string
16
+ //按钮是否显示左侧边框
17
+ leftBorder?: boolean
18
+ //按钮是否显示右侧边框
19
+ rightBorder?: boolean
20
+ //定义可选择的文件类型,默认不限制类型,设定此参数后选择文件时会自动过滤非符合的文件类型
21
+ accept?: 'rar' | 'zip' | 'txt' | 'image' | 'video' | 'audio' | 'html' | 'doc' | 'xml' | 'js' | 'json' | 'ppt' | 'pdf' | null
22
+ //支持的类型数组
23
+ allowedFileType?: string[]
24
+ //是否多选
25
+ multiple?: boolean
26
+ //选择的单个文件最大值,单位kb,不设置将不限制
27
+ maxSize?: number
28
+ //选择的单个文件最小值,单位kb,不设置将不限制
29
+ minSize?: number
30
+ //是否自定义上传附件
31
+ customUpload?: (files: File[]) => string[] | Promise<string[]>
32
+ //处理上传附件异常
33
+ handleError?: (error: InsertAttachmentUploadErrorType, file: File) => void
34
+ }
35
+
36
+ export const attachment = (options?: AttachmentOptionsType) => {
37
+ if (!DapCommon.isObject(options)) {
38
+ options = {}
39
+ }
40
+ const plugin: PluginType = (editifyInstance: ComponentInternalInstance, color: string | null, editTrans: (key: string) => any) => {
41
+ return {
42
+ //附件菜单项配置
43
+ menu: {
44
+ sequence: {
45
+ attachment: options!.sequence || 100
46
+ },
47
+ extends: {
48
+ attachment: {
49
+ type: 'select',
50
+ title: options!.title || editTrans('insertAttachment'),
51
+ leftBorder: options!.leftBorder,
52
+ rightBorder: options!.rightBorder,
53
+ default: () => h(Icon, { value: 'attachment' }),
54
+ layer: (_name: string, btnInstance: InstanceType<typeof Button>) =>
55
+ h(InsertAttachment, {
56
+ color: color,
57
+ accept: options!.accept,
58
+ allowedFileType: options!.allowedFileType || [],
59
+ multiple: !!options!.multiple,
60
+ maxSize: options!.maxSize,
61
+ minSize: options!.minSize,
62
+ customUpload: options!.customUpload,
63
+ handleError: options!.handleError,
64
+ onChange: () => {
65
+ ;(<InstanceType<typeof Layer>>btnInstance.$refs.layerRef).setPosition()
66
+ },
67
+ onInsert: (url: string) => {
68
+ //创建元素
69
+ const attachmentElement = new AlexElement('closed', 'span', { 'data-attachment': url, contenteditable: 'false' }, null, null)
70
+ const editor = <AlexEditor>editifyInstance.exposed!.editor.value
71
+ //插入编辑器
72
+ editor.insertElement(attachmentElement)
73
+ //移动光标到新插入的元素
74
+ editor.range!.anchor.moveToEnd(attachmentElement)
75
+ editor.range!.focus.moveToEnd(attachmentElement)
76
+ editor.formatElementStack()
77
+ editor.domRender()
78
+ editor.rangeRender()
79
+ btnInstance.show = false
80
+ }
81
+ })
82
+ }
83
+ }
84
+ },
85
+ //找到附件元素点击下载
86
+ updateView: () => {
87
+ const editor = <AlexEditor>editifyInstance.exposed!.editor.value
88
+ AlexElement.flatElements(editor.stack).forEach(el => {
89
+ if (el.parsedom == 'span' && el.hasMarks() && el.marks!['data-attachment']) {
90
+ DapEvent.off(<HTMLElement>el.elm, 'click')
91
+ DapEvent.on(<HTMLElement>el.elm, 'click', () => {
92
+ const url = el.marks!['data-attachment']
93
+ const a = document.createElement('a')
94
+ a.setAttribute('href', url)
95
+ a.setAttribute('download', editTrans('attachmentDownloadName'))
96
+ a.click()
97
+ })
98
+ }
99
+ })
100
+ },
101
+ //span含有data-attachment的元素设为自闭合元素
102
+ customParseNode: (el: AlexElement) => {
103
+ if (el.hasMarks() && el.marks!['data-attachment'] && el.parsedom == 'span') {
104
+ el.type = 'closed'
105
+ }
106
+ return el
107
+ },
108
+ //span元素粘贴保留data-attachment
109
+ pasteKeepMarks: {
110
+ 'data-attachment': ['span']
111
+ },
112
+ //设置元素的title属性标记
113
+ renderRule: (el: AlexElement) => {
114
+ if (el.type == 'closed' && el.hasMarks() && el.marks!['data-attachment']) {
115
+ el.marks!['title'] = editTrans('downloadAttachment')
116
+ }
117
+ }
118
+ }
119
+ }
120
+ return plugin
121
+ }
@@ -0,0 +1,135 @@
1
+ .editify-attachment {
2
+ display: block;
3
+ width: 280px;
4
+ padding: 10px 14px;
5
+
6
+ .editify-attachment-header {
7
+ display: flex;
8
+ justify-content: flex-start;
9
+ align-items: center;
10
+ width: 100%;
11
+ margin-bottom: 20px;
12
+ position: relative;
13
+ padding-bottom: 6px;
14
+
15
+ .editify-attachment-header-slider {
16
+ position: absolute;
17
+ width: 50px;
18
+ height: 2px;
19
+ border-radius: 2px;
20
+ left: 0;
21
+ bottom: 0;
22
+ transition: left 200ms;
23
+
24
+ &.editify-upload {
25
+ left: 5px;
26
+ }
27
+
28
+ &.editify-remote {
29
+ left: 85px;
30
+ }
31
+ }
32
+
33
+ .editify-attachment-header-item {
34
+ display: block;
35
+ text-align: center;
36
+ font-size: @font-size;
37
+ color: @font-color;
38
+ opacity: 0.8;
39
+ transition: all 200ms;
40
+ width: 60px;
41
+ overflow: hidden;
42
+ white-space: nowrap;
43
+ text-overflow: ellipsis;
44
+
45
+ &:hover {
46
+ opacity: 1;
47
+ cursor: pointer;
48
+ }
49
+
50
+ &:first-child {
51
+ margin-right: 20px;
52
+ }
53
+
54
+ &.editify-active {
55
+ opacity: 1;
56
+ color: @font-color-dark;
57
+ }
58
+ }
59
+ }
60
+
61
+ .editify-attachment-remote {
62
+ display: block;
63
+ width: 100%;
64
+
65
+ input {
66
+ appearance: none;
67
+ -webkit-appearance: none;
68
+ -moz-appearance: none;
69
+ display: block;
70
+ width: 100%;
71
+ margin: 0 0 10px 0;
72
+ padding: 4px 2px;
73
+ border: none;
74
+ font-size: @font-size;
75
+ color: @font-color;
76
+ border-bottom: 1px solid @border-color;
77
+ line-height: 1.5;
78
+ transition: border-color 500ms;
79
+ background-color: transparent;
80
+ outline: none;
81
+ box-sizing: border-box;
82
+
83
+ &::-webkit-input-placeholder,
84
+ &::placeholder {
85
+ color: @font-color-disabled;
86
+ font-family: inherit;
87
+ font-size: inherit;
88
+ vertical-align: middle;
89
+ }
90
+ }
91
+
92
+ .editify-attachment-remote-footer {
93
+ display: flex;
94
+ justify-content: flex-end;
95
+ align-items: center;
96
+ width: 100%;
97
+ font-size: @font-size;
98
+ opacity: 0.8;
99
+ transition: all 200ms;
100
+
101
+ &:hover {
102
+ cursor: pointer;
103
+ opacity: 1;
104
+ }
105
+ }
106
+ }
107
+
108
+ .editify-attachment-upload {
109
+ display: flex;
110
+ justify-content: center;
111
+ align-items: center;
112
+ width: 100%;
113
+ padding: 15px 0;
114
+ font-size: 36px;
115
+ opacity: 0.8;
116
+ transition: all 200ms;
117
+ position: relative;
118
+
119
+ &:hover {
120
+ cursor: pointer;
121
+ opacity: 1;
122
+ }
123
+
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;
133
+ }
134
+ }
135
+ }
@@ -0,0 +1,189 @@
1
+ <template>
2
+ <div class="editify-attachment">
3
+ <div class="editify-attachment-header">
4
+ <div @click="current = 'upload'" class="editify-attachment-header-item" :class="{ 'editify-active': current == 'upload' }" :style="activeStyle('upload')">{{ $editTrans('uploadAttachment') }}</div>
5
+ <div @click="current = 'remote'" class="editify-attachment-header-item" :class="{ 'editify-active': current == 'remote' }" :style="activeStyle('remote')">{{ $editTrans('remoteAttachment') }}</div>
6
+ <div class="editify-attachment-header-slider" :class="'editify-' + current" :style="{ backgroundColor: color || '' }"></div>
7
+ </div>
8
+ <!-- 网络图片 -->
9
+ <div class="editify-attachment-remote" v-if="current == 'remote'">
10
+ <input v-model.trim="remoteUrl" :placeholder="$editTrans('attachmentUrlPlaceholder')" @blur="handleInputBlur" @focus="handleInputFocus" />
11
+ <div class="editify-attachment-remote-footer" :style="{ color: color || '' }">
12
+ <span @click="insertRemoteAttachment">{{ $editTrans('insert') }}</span>
13
+ </div>
14
+ </div>
15
+ <!-- 上传图片 -->
16
+ <div class="editify-attachment-upload" v-else>
17
+ <Icon value="upload"></Icon>
18
+ <input :multiple="multiple" :accept="acceptValue" @change="selectFile" type="file" />
19
+ </div>
20
+ </div>
21
+ </template>
22
+ <script setup lang="ts">
23
+ import { file as DapFile } from 'dap-util'
24
+ import Icon from '../../../components/icon/icon.vue'
25
+ import { InsertAttachmentProps } from './props'
26
+ import { computed, inject, ref, watch } from 'vue'
27
+ import { ObjectType } from '../../../core/tool'
28
+
29
+ defineOptions({
30
+ name: 'InsertAttachment'
31
+ })
32
+ const props = defineProps(InsertAttachmentProps)
33
+ const emits = defineEmits(['change', 'insert'])
34
+
35
+ const $editTrans = inject<(key: string) => any>('$editTrans')!
36
+
37
+ //当前展示的面板,取值remote和upload
38
+ const current = ref<'remote' | 'upload'>('upload')
39
+ //远程图片链接
40
+ const remoteUrl = ref<string>('')
41
+
42
+ const activeStyle = computed<(name: 'remote' | 'upload') => ObjectType>(() => {
43
+ return (name: 'remote' | 'upload') => {
44
+ if (current.value == name) {
45
+ return {
46
+ color: props.color
47
+ }
48
+ }
49
+ return {}
50
+ }
51
+ })
52
+ const acceptValue = computed<string | undefined>(() => {
53
+ if (props.accept === 'rar') {
54
+ return 'application/x-rar-compressed'
55
+ }
56
+ if (props.accept === 'zip') {
57
+ return 'application/x-zip-compressed'
58
+ }
59
+ if (props.accept === 'txt') {
60
+ return 'text/plain'
61
+ }
62
+ if (props.accept === 'image') {
63
+ return 'image/*'
64
+ }
65
+ if (props.accept === 'video') {
66
+ return 'video/*'
67
+ }
68
+ if (props.accept === 'audio') {
69
+ return 'aduio/*'
70
+ }
71
+ if (props.accept === 'html') {
72
+ return 'text/html'
73
+ }
74
+ if (props.accept === 'doc') {
75
+ return 'application/msword'
76
+ }
77
+ if (props.accept === 'xml') {
78
+ return 'text/xml'
79
+ }
80
+ if (props.accept === 'js') {
81
+ return 'text/javascript'
82
+ }
83
+ if (props.accept === 'json') {
84
+ return 'application/json'
85
+ }
86
+ if (props.accept === 'ppt') {
87
+ return 'application/vnd.ms-powerpoint'
88
+ }
89
+ if (props.accept === 'pdf') {
90
+ return 'application/pdf'
91
+ }
92
+ })
93
+
94
+ //获取文件后缀
95
+ const getSuffix = (file: File) => {
96
+ const index = file.name.lastIndexOf('.')
97
+ if (index <= 0) {
98
+ return ''
99
+ }
100
+ return file.name.substring(index + 1)
101
+ }
102
+ //输入框获取焦点
103
+ const handleInputFocus = (e: Event) => {
104
+ if (props.color) {
105
+ ;(<HTMLInputElement>e.currentTarget).style.borderColor = props.color
106
+ }
107
+ }
108
+ //输入框失去焦点
109
+ const handleInputBlur = (e: Event) => {
110
+ ;(<HTMLInputElement>e.currentTarget).style.borderColor = ''
111
+ }
112
+ //插入网络文件
113
+ const insertRemoteAttachment = () => {
114
+ emits('insert', remoteUrl.value)
115
+ }
116
+ //选择文件
117
+ const selectFile = async (e: Event) => {
118
+ const inputEle = <HTMLInputElement>e.currentTarget
119
+ const files = inputEle.files
120
+ if (!files || !files.length) {
121
+ return
122
+ }
123
+ let filterFiles = []
124
+ for (let i = 0; i < files.length; i++) {
125
+ const file = files[i]
126
+ const suffix = getSuffix(file)
127
+ const isMatch =
128
+ props.allowedFileType && Array.isArray(props.allowedFileType) && props.allowedFileType.length
129
+ ? props.allowedFileType.some(item => {
130
+ return item.toLocaleLowerCase() == suffix.toLocaleLowerCase()
131
+ })
132
+ : true
133
+ //后缀不符合
134
+ if (!isMatch) {
135
+ //如果自定义了异常处理
136
+ if (typeof props.handleError == 'function') {
137
+ props.handleError('suffixError', file)
138
+ }
139
+ continue
140
+ }
141
+ //超过最大值
142
+ if (props.maxSize && file.size / 1024 > props.maxSize) {
143
+ //如果自定义了异常处理
144
+ if (typeof props.handleError == 'function') {
145
+ props.handleError('maxSizeError', file)
146
+ }
147
+ continue
148
+ }
149
+ //没达到最小值
150
+ if (props.minSize && file.size / 1024 < props.minSize) {
151
+ //如果自定义了异常处理
152
+ if (typeof props.handleError == 'function') {
153
+ props.handleError('minSizeError', file)
154
+ }
155
+ continue
156
+ }
157
+ filterFiles.push(file)
158
+ }
159
+ //有文件可上传
160
+ if (filterFiles.length) {
161
+ let attachments = []
162
+ //自定义上传方法
163
+ if (typeof props.customUpload == 'function') {
164
+ attachments = (await props.customUpload(filterFiles)) || []
165
+ }
166
+ //默认上传方法
167
+ else {
168
+ for (let i = 0; i < filterFiles.length; i++) {
169
+ const url = await DapFile.dataFileToBase64(filterFiles[i])
170
+ attachments.push(url)
171
+ }
172
+ }
173
+ attachments.forEach(url => {
174
+ emits('insert', url)
175
+ })
176
+ }
177
+ //清空文件选择框
178
+ inputEle.value = ''
179
+ }
180
+
181
+ //监听current变更触发change事件
182
+ watch(
183
+ () => current.value,
184
+ () => {
185
+ emits('change')
186
+ }
187
+ )
188
+ </script>
189
+ <style scoped src="./insertAttachment.less"></style>
@@ -0,0 +1,48 @@
1
+ import { ExtractPublicPropTypes, PropType } from 'vue'
2
+
3
+ export type InsertAttachmentUploadErrorType = 'suffixError' | 'maxSizeError' | 'minSizeError'
4
+
5
+ export const InsertAttachmentProps = {
6
+ //主题色
7
+ color: {
8
+ type: String as PropType<string | null>,
9
+ default: ''
10
+ },
11
+ //可选择的文件类型
12
+ accept: {
13
+ type: String as PropType<'rar' | 'zip' | 'txt' | 'image' | 'video' | 'audio' | 'html' | 'doc' | 'xml' | 'js' | 'json' | 'ppt' | 'pdf' | null>,
14
+ default: null
15
+ },
16
+ //支持的类型数组
17
+ allowedFileType: {
18
+ type: Array as PropType<string[]>,
19
+ default: null
20
+ },
21
+ //是否支持多选
22
+ multiple: {
23
+ type: Boolean,
24
+ default: false
25
+ },
26
+ //单个文件最大值
27
+ maxSize: {
28
+ type: Number,
29
+ default: null
30
+ },
31
+ //单个文件最小值
32
+ minSize: {
33
+ type: Number,
34
+ default: null
35
+ },
36
+ //是否自定义上传附件
37
+ customUpload: {
38
+ type: Function as PropType<(files: File[]) => string[] | Promise<string[]>>,
39
+ default: null
40
+ },
41
+ //处理上传附件异常
42
+ handleError: {
43
+ type: Function as PropType<(error: InsertAttachmentUploadErrorType, file: File) => void>,
44
+ default: null
45
+ }
46
+ }
47
+
48
+ export type InsertAttachmentPropsType = ExtractPublicPropTypes<typeof InsertAttachmentProps>