Vue3点击父组件按钮调用子组件upload函数的问题排查
问题分析与解决方案
当前代码存在的问题
- 子组件Ref未正确声明:父组件模板中给
MediaImport设置了ref="MediaImport",但setup函数里没有定义对应的ref变量,无法获取子组件实例调用其upload方法。 - 自动上传与手动触发冲突:
MediaImport组件设置了:auto="true",文件选择后会自动上传,不符合“点击父组件按钮才触发上传”的需求。 - 上传完成事件触发时机错误:子组件
upload方法中,XHR请求发送后立即$emit('has-uploaded', true),但XHR是异步请求,此时上传并未真正完成,会导致父组件误判上传状态。 - 跳转逻辑时机错误:父组件
goToRecognitionView函数中同步判断hasUploaded.value,但上传是异步操作,无法正确捕获上传完成的状态。
正确实现方式
1. 父组件(VideoRecognition)修改
<script lang="ts"> import Button from 'primevue/button'; import MediaImport from '@/components/MediaImport'; import { ref } from 'vue'; import { useRouter } from 'vue-router'; import WebSocketService from '@/utils/WebSocketService'; export default { name: "VideoRecognition", emits: ['upload-media'], setup(_, { emit }) { const hasFiles = ref<Boolean>(false); const mediaObjectUrl = ref<String>(""); // 声明子组件Ref,用于调用子组件方法 const mediaImportRef = ref<InstanceType<typeof MediaImport> | null>(null); const handleHasFiles = (value: boolean) => { hasFiles.value = value; }; const handleSendMediaObjectUrl = (path: string) => { mediaObjectUrl.value = path; }; const router = useRouter(); const ws = new WebSocketService(); // 上传成功后的跳转逻辑 const handleUploadSuccess = () => { ws.send("video_recognition_media_object_url", mediaObjectUrl.value); ws.close(); router.push('/recognition'); }; const goToRecognitionView = () => { emit('upload-media', true); // 调用子组件的upload方法触发上传 if (mediaImportRef.value && hasFiles.value) { mediaImportRef.value.upload(); } else { console.log('请先选择文件'); } }; return { hasFiles, handleHasFiles, handleSendMediaObjectUrl, goToRecognitionView, mediaImportRef, handleUploadSuccess }; }, components: { Button, MediaImport, }, }; </script> <template> <div class="container"> <div class="flex flex-col gap-4"> <div class="flex flex-col justify-center w-full"> <div class="w-full flex-auto flex flex-col justify-center items-center border-2 border-solid border-surface-200 dark:border-surface-700 rounded-md bg-[#1e2d4b] font-medium p-4"> <p class="pb-4">Select video to recognize</p> <!-- 绑定Ref,关闭自动上传,监听上传成功事件 --> <MediaImport ref="mediaImportRef" name="media[]" url="/api/upload" mediaType="video" accept="video/*" @has-files="handleHasFiles" @media-object-url="handleSendMediaObjectUrl" @upload="handleUploadSuccess" :maxFileSize="512000000" :auto="false" chooseLabel="Browse" /> </div> <div class="flex pt-4 justify-end"> <Button label="Next" icon="pi pi-arrow-right" iconPos="right" @click="goToRecognitionView" /> </div> </div> </div> </div> </template>
2. 子组件(MediaImport)修改
调整upload方法,确保上传成功后才触发完成事件:
upload() { if (this.customUpload) { if (this.fileLimit) { this.uploadedFileCount += this.files.length; } this.$emit('uploader', { files: this.files }); this.clear(); // 自定义上传成功后触发完成事件 this.$emit('upload', { files: this.files }); } else { let xhr = new XMLHttpRequest(); let formData = new FormData(); this.$emit('before-upload', { xhr: xhr, formData: formData }); for (let file of this.files) { const minFileSizeToCreatePart = 80 * 1024 * 1024 // 80 MB const fileName = this.generateUniqueFileName(file.name); if (file.size >= minFileSizeToCreatePart) { let start = 0; let partSize = 60 * 1024 * 1024; // 60 MB while (start < file.size) { let chunk = file.slice(start, start + partSize); formData.append(`${this.name}[]`, chunk, `${fileName}.part${Math.ceil((start + 1) / partSize)}`); start += partSize; } } else { formData.append(this.name, file, fileName); } } xhr.upload.addEventListener('progress', (event) => { if (event.lengthComputable) { this.progress = Math.round((event.loaded * 100) / event.total); } this.$emit('progress', { originalEvent: event, progress: this.progress }); }); xhr.onreadystatechange = () => { if (xhr.readyState === 4) { this.progress = 0; if (xhr.status >= 200 && xhr.status < 300) { if (this.fileLimit) { this.uploadedFileCount += this.files.length; } this.$emit('upload', { xhr: xhr, files: this.files }); } else { this.$emit('error', { xhr: xhr, files: this.files }); } this.uploadedFiles.push(...this.files); this.clear(); } }; xhr.open('POST', this.url, true); this.$emit('before-send', { xhr: xhr, formData: formData }); xhr.withCredentials = this.withCredentials; xhr.send(formData); } },
内容的提问来源于stack exchange,提问作者Tebyy
相关产品推荐
相关产品推荐

