Files
vue-formkit-example/src/__fk-inputs__/components/file-upload/file-upload.vue
严浩 7277e8e392
All checks were successful
/ test (push) Successful in 20s
/ surge (push) Successful in 23s
feat: 更新文件上传组件以支持空文件列表处理
2024-12-31 17:04:23 +08:00

218 lines
6.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import type { FormKitFrameworkContext } from '@formkit/core';
import FileUpload, { type FileUploadUploaderEvent } from 'primevue/fileupload';
import { computed, onMounted, useTemplateRef } from 'vue';
import FileUploadItem from './file-upload-item.vue';
import type { CustomRequest, FileExt, FileUploadInst, PropFilesToValue, PropValueToFiles } from './types';
const props = defineProps<{
context: FormKitFrameworkContext & {
fileLimit?: number;
maxFileSize?: number;
customRequest?: CustomRequest;
valueToFiles?: PropValueToFiles;
filesToValue?: PropFilesToValue;
autoUpload?: boolean;
};
}>();
const formkitContext = props.context;
const customRequest = props.context.customRequest;
const fileUploadRef = useTemplateRef<FileUploadInst>('fileUploadRef');
const cmpt_disabled = computed(() => {
if (formkitContext.disabled === true) {
return true;
}
if (fileUploadRef.value) {
// 有上传失败的文件
if (fileUploadRef.value.uploadedFiles.some((f) => f.status === 'failed')) {
return true;
}
// 已上传文件数量超过限制(这里是大于*等于*
if (fileUploadRef.value.uploadedFileCount >= (formkitContext.fileLimit ?? Infinity)) {
return true;
}
// 已上传和待上传文件数量超过限制(这里是*大于*
const uploaded_and_pending_count = fileUploadRef.value.uploadedFileCount + fileUploadRef.value.files.length;
if (uploaded_and_pending_count > (formkitContext.fileLimit ?? Infinity)) {
return true;
}
}
return false;
});
const cmpt_showUploadButton = computed(() => {
if (fileUploadRef.value?.files?.length) {
return true;
}
return false;
});
const changeModelValue = () => {
const uploadedFiles = fileUploadRef.value!.uploadedFiles.filter((f) => f.status === 'uploaded');
if (!formkitContext.filesToValue) {
console.warn('[FileUpload] filesToValue is not defined');
return;
}
if (uploadedFiles.length === 0) {
formkitContext.node.input(null);
} else {
formkitContext.node.input(formkitContext.filesToValue(uploadedFiles));
}
};
const changeUploadedFiles = () => {
if (!formkitContext.valueToFiles) {
console.warn('[FileUpload] valueToFiles is not defined');
return;
}
try {
const files = formkitContext.valueToFiles(formkitContext._value);
if (files === null) return;
fileUploadRef.value!.uploadedFiles = files.map((f) => ({
name: f.name,
url: f.url,
status: f.status || 'uploaded',
progress: f.progress || 100,
}));
fileUploadRef.value!.uploadedFileCount = files.length;
} catch (error) {
console.warn('[FileUpload] valueToFiles error:', error, 'value:', formkitContext._value);
}
};
onMounted(() => {
changeUploadedFiles();
});
const onUploader = (event: FileUploadUploaderEvent) => {
if (!customRequest) {
console.warn('[FileUpload] customRequest is not defined');
return;
}
const files = event.files as FileExt[];
for (const file of files) {
fileUploadRef.value!.uploadedFiles.push({
rawFile: file,
name: file.name,
url: '',
status: 'uploading',
progress: 0,
});
const fileItem = fileUploadRef.value!.uploadedFiles[fileUploadRef.value!.uploadedFiles.length - 1];
customRequest({
file,
onProgress: (percent) => {
fileItem.progress = percent;
},
})
.then((result) => {
fileItem.status = 'uploaded';
fileItem.url = result.url;
changeModelValue();
})
.catch(() => {
fileItem.status = 'failed';
});
}
};
// fileUploadRef.value?.uploadedFiles
</script>
<template>
<FileUpload
ref="fileUploadRef"
:auto="formkitContext.autoUpload"
:disabled="cmpt_disabled"
:showUploadButton="cmpt_showUploadButton"
:showCancelButton="false"
:customUpload="true"
mode="advanced"
:multiple="true"
accept="image/*"
:maxFileSize="formkitContext.maxFileSize"
invalidFileSizeMessage="文件 {0} 大小超过限制 {1}"
:fileLimit="formkitContext.fileLimit"
invalidFileLimitMessage="最多只能上传 {0} 个文件,请移除多余文件后点击上传"
@uploader="onUploader"
:chooseButtonProps="{ size: 'small' }"
:uploadButtonProps="{ size: 'small', severity: 'secondary' }"
:cancelButtonProps="{ size: 'small', severity: 'secondary' }"
@remove="changeModelValue()"
@removeUploadedFile="changeModelValue()"
>
<template #empty>
<Message
size="small"
severity="secondary"
>未上传附件</Message
>
</template>
<template #content="{ messages, removeFileCallback, removeUploadedFileCallback }">
<Message
size="small"
v-for="msg of messages"
closable
@close="fileUploadRef!.messages = []"
:key="msg"
severity="error"
>{{ msg }}</Message
>
<!-- 已上传列表上传中上传成功上传失败 -->
<template
v-for="(file, index) of fileUploadRef?.uploadedFiles"
:key="file.name + file.url"
>
<FileUploadItem
:disabled="formkitContext.disabled === true"
:url="file.url || file.rawFile?.objectURL || ''"
:filename="file.name || file.url || '未知文件'"
@remove="
() => {
fileUploadRef!.uploadedFileCount--;
removeUploadedFileCallback(index);
}
"
:status="file.status"
:progress="file.progress"
/>
</template>
<!-- 待上传列表 -->
<template
v-for="(file, index) of fileUploadRef?.files"
:key="file.name + file.type + file.size"
>
<FileUploadItem
:disabled="formkitContext.disabled === true"
:url="file.objectURL"
:filename="file.name"
@remove="removeFileCallback(index)"
status="pending"
/>
</template>
</template>
</FileUpload>
</template>
<style>
.p-floatlabel:has(.p-fileupload) label {
top: var(--p-floatlabel-over-active-top);
transform: translateY(0);
font-size: var(--p-floatlabel-active-font-size);
font-weight: var(--p-floatlabel-label-active-font-weight);
}
.p-fileupload-content .p-progressbar {
--p-fileupload-progressbar-height: 1rem;
}
</style>