Files
vue-ts-example/src/components/primevue/upload-demo.vue
严浩 2bc762a89b
Some checks failed
/ surge (push) Successful in 2m34s
/ build-and-deploy-to-vercel (push) Successful in 2m46s
/ lint-build-and-check (push) Has been cancelled
/ playwright (push) Has been cancelled
feat: 更新 ESLint 配置
2025-04-02 11:27:09 +08:00

174 lines
5.6 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 { FileUploadState, FileUploadUploaderEvent } from 'primevue/fileupload';
import TimesIcon from '@primevue/icons/times';
import axios from 'axios';
interface FileExt extends File {
objectURL: string;
}
interface FileUploadInst extends FileUploadState {
chooseDisabled?: boolean;
files: FileExt[];
uploadedFiles: {
name: string;
progress: number;
rawFile: FileExt;
status: 'failed' | 'uploaded' | 'uploading';
url: string;
}[];
}
// https://primevue.org/toast/#headless
const fileUploadRef = useTemplateRef<FileUploadInst>('fileUploadRef');
const onUploader = (event: FileUploadUploaderEvent) => {
console.debug(`event.files :>> `, event.files);
const files = event.files as FileExt[];
for (const file of files) {
fileUploadRef.value!.uploadedFiles.push({
name: file.name,
progress: 0,
rawFile: file,
status: 'uploading',
url: '',
});
const formData = new FormData();
formData.append('file', file);
axios
.post('/fake-api/fake/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
onUploadProgress: (e) => {
const progress = Math.round((e.loaded * 100) / (e.total || 1));
const item = fileUploadRef.value!.uploadedFiles.find((f) => f.rawFile === file);
if (item) {
item.progress = progress;
}
},
})
.then((res) => {
console.debug(`res :>> `, res);
const item = fileUploadRef.value!.uploadedFiles.find((f) => f.rawFile === file);
if (item) {
item.status = 'uploaded';
item.url = res.data.url;
}
})
.catch((error) => {
console.error(`err :>> `, error);
const item = fileUploadRef.value!.uploadedFiles.find((f) => f.rawFile === file);
if (item) {
item.status = 'failed';
}
});
}
};
// https://github.com/primefaces/primevue/issues/4722#issuecomment-2241581616
// const fileObjectURL = (file: File) => window.URL.createObjectURL(file);
// const TemplateFoo = createReusableTemplate<{ msg: string }>();
const uploadedFiles = computed(() => fileUploadRef.value?.uploadedFiles || []);
const files = computed(() => fileUploadRef.value?.files || []);
const showUploadButton = computed(() => {
if (fileUploadRef.value?.files?.length) {
return true;
}
return false;
});
const fileLimit = 2;
const disabled = computed(() => {
if (fileUploadRef.value?.uploadedFiles?.some((f) => f.status === 'failed')) {
return true;
}
if ((fileUploadRef.value?.uploadedFileCount || 0) >= fileLimit) {
return true;
}
return false;
});
</script>
<template>
<FileUpload
ref="fileUploadRef"
:auto="true"
:disabled="disabled"
:showUploadButton="showUploadButton"
:showCancelButton="false"
customUpload
mode="advanced"
:multiple="true"
accept="image/*"
:maxFileSize="1000000 * 1"
invalidFileSizeMessage="文件 {0} 大小超过限制 {1}"
:fileLimit="fileLimit"
invalidFileLimitMessage="最多只能上传 {0} 个文件,请移除多余文件后点击上传"
@uploader="onUploader"
:chooseButtonProps="{ size: 'small' }"
:uploadButtonProps="{ size: 'small', severity: 'secondary' }"
:cancelButtonProps="{ size: 'small', severity: 'secondary' }"
>
<template #empty>
<Message size="small" severity="info">请上传图片</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 files" :key="file.name + file.type + file.size">
<div class="border p-2 rounded-md flex flex-wrap items-center gap-2">
<img alt="" :src="file.objectURL" class="w-10 h-10 shrink-0" />
<div class="break-all break-anywhere">{{ file.name }}</div>
<Badge value="待上传" severity="warn" />
<Button text :rounded="true" severity="danger" class="ml-auto" @click="removeFileCallback(index)">
<TimesIcon aria-hidden="true" />
</Button>
</div>
</template>
<!-- 已上传列表上传中上传成功上传失败 -->
<template v-for="(file, index) of uploadedFiles" :key="file.name + file.url">
<div class="border p-2 rounded-md flex flex-wrap items-center gap-2">
<img alt="" :src="file.url || file.rawFile.objectURL" class="w-10 h-10 shrink-0" />
<div class="break-all break-anywhere">{{ file.name }}</div>
<Badge
:value="file.status === 'uploading' ? '上传中' : file.status === 'uploaded' ? '已上传' : '上传失败'"
:severity="file.status === 'uploading' ? 'warn' : file.status === 'uploaded' ? 'success' : 'danger'"
/>
<Button
text
:rounded="true"
severity="danger"
class="ml-auto"
@click="
fileUploadRef!.uploadedFileCount--;
removeUploadedFileCallback(index);
"
>
<TimesIcon aria-hidden="true" />
</Button>
<ProgressBar v-if="file.status === 'uploading'" :value="file.progress" />
</div>
</template>
<div>files.length: {{ files.length }}</div>
<div>uploadedFiles.length: {{ uploadedFiles.length }}</div>
<div>uploadedFileCount: {{ fileUploadRef?.uploadedFileCount }}</div>
</template>
</FileUpload>
</template>
<style></style>