123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- <template>
- <div class="file-upload">
- <el-upload
- ref="uploadRef"
- :action="uploadUrl"
- :headers="headers"
- :data="uploadDataDict"
- :show-file-list="false"
- :auto-upload="autoUpload"
- :http-request="customRequest"
- :disabled="disabled"
- :before-upload="handleBeforeUpload"
- :accept="accept"
- :multiple="multiple"
- :on-exceed="handleExceededSize"
- @change="handleFileChange"
- @error="handleError"
- @success="handleSuccess"
- >
- <template #trigger>
- <el-button type="primary" :disabled="loading">{{ btnText }}</el-button>
- <!-- <el-button
- v-if="!autoUpload"
- type="primary"
- :loading="loading"
- :disabled="!canUpload"
- @click.stop="startUpload"
- >开始上传</el-button
- > -->
- </template>
- </el-upload>
- </div>
- </template>
- <script setup lang="ts">
- import { ref } from 'vue';
- import { fileMD5 } from '@/utils/md5';
- import { ElMessage } from 'element-plus';
- import type {
- UploadProps,
- UploadRequestOptions,
- UploadFile,
- UploadFiles,
- UploadRawFile,
- UploadProgressEvent,
- } from 'element-plus';
- import axios, { AxiosHeaders } from 'axios';
- defineOptions({
- name: 'UploadButton',
- });
- const props = withDefaults(
- defineProps<{
- uploadUrl: string;
- uploadData?: Record<string, any>;
- maxSize?: number;
- uploadFileAlias?: string;
- autoUpload?: boolean;
- disabled?: boolean;
- btnText?: string;
- accept?: string;
- multiple?: boolean;
- }>(),
- {
- uploadUrl: '',
- uploadData: () => {
- return {};
- },
- maxSize: 20 * 1024 * 1024,
- uploadFileAlias: 'file',
- autoUpload: true,
- disabled: false,
- btnText: '选择',
- accept: '.xls,.xlsx',
- multiple: false,
- }
- );
- const emit = defineEmits([
- 'uploading',
- 'uploadError',
- 'uploadSuccess',
- 'validError',
- ]);
- const uploadRef = ref();
- const attachmentName = ref('');
- const canUpload = ref(false);
- const uploadDataDict = ref({});
- const headers = ref({ md5: '' });
- const result = ref({ success: true, message: '' });
- const loading = ref(false);
- function startUpload() {
- loading.value = true;
- uploadRef.value?.submit();
- }
- function handleFileChange(uploadFile: UploadFile, uploadFiles: UploadFiles) {
- if (props.autoUpload || !uploadFiles.length) return;
- // Element Plus's status: ready, uploading, success, fail
- // Arco's status: init, uploading, success, error
- // We'll assume 'ready' is similar to 'init' for this logic, though direct mapping might not be perfect.
- canUpload.value = uploadFiles[0]?.status === 'ready';
- }
- async function handleBeforeUpload(rawFile: UploadRawFile) {
- uploadDataDict.value = {
- ...props.uploadData,
- filename: rawFile.name,
- };
- if (rawFile.size > props.maxSize) {
- // Element Plus uses on-exceed for this, but we can also check here
- // For consistency with original logic, we call handleExceededSize and reject
- // However, on-exceed will also be triggered if :limit is set for file count, not size directly.
- // Here, we manually trigger the message and prevent upload.
- const content = `文件大小不能超过${Math.floor(
- props.maxSize / 1024 / 1024
- )}MB`;
- ElMessage.error(content);
- result.value = {
- success: false,
- message: content,
- };
- emit('validError', result.value);
- return Promise.reject(new Error(content));
- }
- const md5 = await fileMD5(rawFile);
- headers.value.md5 = md5;
- if (props.autoUpload) loading.value = true;
- return true;
- }
- function customRequest(options: UploadRequestOptions) {
- // Changed return type to any
- const { onProgress, file, data } = options;
- const formData = new FormData();
- const paramData: Record<string, any> = data || {};
- Object.entries(paramData).forEach(([k, v]) => {
- formData.append(k, v);
- });
- formData.append(props.uploadFileAlias, file as File);
- emit('uploading');
- return axios.post(options.action, formData, {
- headers: options.headers as AxiosHeaders,
- onUploadProgress: ({ loaded, total }) => {
- onProgress({
- percent: total ? Math.floor((100 * loaded) / total) : 0,
- } as UploadProgressEvent);
- },
- });
- }
- function handleError(
- error: Error,
- uploadFile: UploadFile,
- uploadFiles: UploadFiles
- ) {
- canUpload.value = false;
- loading.value = false;
- result.value = {
- success: false,
- message: error.message || '上传失败',
- };
- ElMessage.error(result.value.message);
- emit('uploadError', result.value);
- }
- function handleSuccess(
- response: any,
- uploadFile: UploadFile,
- uploadFiles: UploadFiles
- ) {
- canUpload.value = false;
- loading.value = false;
- result.value = {
- success: true,
- message: '上传成功!',
- };
- ElMessage.success('上传成功!');
- emit('uploadSuccess', {
- ...result.value,
- filename: uploadFile.name,
- response,
- });
- }
- function handleExceededSize(files: File[]) {
- // This function is now primarily called by el-upload's on-exceed prop
- // The before-upload also has a check, this is a fallback or for multiple files if `limit` (count) is exceeded.
- const file = files[0]; // Assuming single file upload context for this message
- const content = `文件 ${file.name} 大小超过限制 (${Math.floor(
- props.maxSize / 1024 / 1024
- )}MB)`;
- result.value = {
- success: false,
- message: content,
- };
- loading.value = false;
- ElMessage.error(content);
- emit('validError', result.value);
- }
- </script>
- <style lang="less">
- .file-upload {
- display: flex;
- justify-content: space-between;
- align-items: center;
- width: 100%;
- .arco-upload-hide {
- display: inline-block;
- }
- .el-input__wrapper {
- flex-grow: 2;
- margin-right: 10px;
- }
- .arco-upload {
- flex-grow: 0;
- flex-shrink: 0;
- }
- }
- </style>
|