index.vue
3.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
<template>
<Upload
v-bind="$attrs"
:on-before-upload="onBeforeUpload"
:custom-request="onUpload"
:file-list="[]"
:show-file-list="false"
style="width: 100%"
>
<template #upload-button>
<Input :model-value="modelValue" :readonly="true" :placeholder="placeholder">
<template #suffix>
<Progress v-if="loading" :percent="percent" size="mini" />
<IconUpload v-else />
</template>
</Input>
</template>
</Upload>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { IconUpload } from '@arco-design/web-vue/es/icon';
import useLoading from '@/hooks/loading';
import useOss from '@/hooks/oss';
import { Input, Message, Progress, Upload, UploadRequest, useFormItem } from '@arco-design/web-vue';
import { startsWith } from 'lodash';
type FileType = { name: string; url: string; size: number; type: string; width?: number; height?: number; duration?: number };
const props = defineProps({
modelValue: { type: String, default: '' },
prefix: { type: String, default: 'file' },
limit: { type: Number, default: 0 },
placeholder: { type: String, default: '请选择' },
});
const emits = defineEmits<{
(e: 'update:modelValue', value: string): void;
(e: 'success', value: FileType): void;
(e: 'choose-file', value: File): void;
}>();
const { eventHandlers } = useFormItem();
const { loading, setLoading } = useLoading(false);
const { upload } = useOss();
const percent = ref<number>(0);
const onBeforeUpload = (file: File & { width?: number; height?: number; duration?: number }) => {
if (props.limit !== 0 && file.size > props.limit * 1024 * 1024) {
Message.warning(`${file.name} 文件超过${props.limit}MB,无法上传`);
return Promise.resolve(false);
}
if (startsWith(file.type, 'image/')) {
const imgObj = new Image();
imgObj.src = URL.createObjectURL(file);
imgObj.onload = () => {
file.width = imgObj.width;
file.height = imgObj.height;
};
}
if (startsWith(file.type, 'audio/') || startsWith(file.type, 'video/')) {
const audioElement = new Audio(URL.createObjectURL(file));
audioElement.addEventListener('loadedmetadata', () => {
file.duration = audioElement.duration * 1000;
});
}
return Promise.resolve(file);
};
const onProgress = (p: number) => {
percent.value = p;
};
const onUpload = (option: any): UploadRequest => {
const { fileItem } = option;
if (fileItem.file) {
setLoading(true);
// eslint-disable-next-line vue/custom-event-name-casing
emits('choose-file', fileItem.file as File);
upload(fileItem.file, props.prefix, onProgress)
.then((res) => {
emits('update:modelValue', res?.url || '');
eventHandlers.value?.onChange?.();
fileItem.percent = 100;
fileItem.url = res?.url || '';
fileItem.status = 'done';
emits('success', {
name: fileItem.name,
url: fileItem.url,
size: fileItem.file.size,
type: fileItem.file.type,
width: fileItem.file.width || 0,
height: fileItem.file.height || 0,
duration: fileItem.file.duration || 0,
});
})
.finally(() => {
setLoading(false);
});
}
return {};
};
</script>
<style lang="less" scoped>
::v-deep(.arco-input-append) {
padding: 0;
}
</style>