Vue 3中如何通过点击父组件中的按钮来调用子组件的函数?

问题描述 投票:0回答:1
我有一个名为“MediaImport”的组件,它的“方法”中有一个“上传”功能,我需要在单击“MediaUpload”组件所在的父组件中的相应按钮时以及在其之后执行它执行,导航到另一条路线。有谁知道我在这里做错了什么以及它应该如何正确显示?

页面

视频识别代码:

<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'; //import { ws } from '@/main'; export default { name: "VideoRecognition", emits: ['upload-media'], setup(_, { emit }) { const hasFiles = ref<Boolean>(false); const hasUploaded = ref<Boolean>(false); const mediaObjectUrl = ref<String>(""); const handleHasFiles = (value: boolean) => { hasFiles.value = value; }; const handleHasUploaded = (value: boolean) => { hasUploaded.value = value; }; const handleSendMediaObjectUrl = (path: string) => { mediaObjectUrl.value = path; }; const ws = new WebSocketService(); const router = useRouter(); const goToRecognitionView = () => { emit('upload-media', true); if (hasFiles.value && hasUploaded.value) { console.log(hasUploaded.value) //ws.send("video_recognition_media_object_url", mediaObjectUrl.value); //ws.close(); //router.push('/recognition'); } else { console.log('Focus') } }; return { hasFiles, handleHasFiles, handleHasUploaded, handleSendMediaObjectUrl, goToRecognitionView }; }, components: { // eslint-disable-next-line vue/no-reserved-component-names 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> <MediaImport ref="MediaImport" name="media[]" url="/api/upload" mediaType="video" accept="video/*" @has-files="handleHasFiles" @media-object-url="handleSendMediaObjectUrl" @has-uploaded="handleHasUploaded" :maxFileSize="512000000" :auto="true" 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>
组件

MediaImport代码:

<script lang="ts"> import Message from 'primevue/message'; import BaseMediaImport from './BaseMediaImport.vue'; import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; import { Comment, ref } from 'vue'; import { randomNumber } from '@/utils/randomNumber'; export interface MediaFile extends File { name: string; type: string; size: number; objectURL?: string; path?: string; } export default { name: 'MediaImport', extends: BaseMediaImport, emits: ['select', 'before-upload', 'progress', 'error', 'clear', 'remove', 'has-files', 'media-object-url', 'upload', 'uploader', 'has-uploaded', 'before-send'], setup() { const mediaUpload = ref<Boolean>(false); const handleMediaUpload = (state: boolean) => { mediaUpload.value = state; } return { handleMediaUpload }; }, data() { return { files: [] as MediaFile[], uploadedFiles: [] as MediaFile[], messages: [] as String[], focused: false, progress: null, acceptedTypes: '', showDragOverIcon: false, uploadedFileCount: 0 }; }, methods: { upload() { if (this.customUpload) { if (this.fileLimit) { this.uploadedFileCount += this.files.length; } this.$emit('uploader', { files: this.files }); this.clear(); } 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) { // @ts-ignore this.progress = Math.round((event.loaded * 100) / event.total); } this.$emit('progress', { originalEvent: event, progress: this.progress }); }); xhr.onreadystatechange = () => { if (xhr.readyState === 4) { // @ts-ignore 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); } this.$emit('has-uploaded', true); }, choose() { this.$refs.fileInput.click(); }, clear() { this.files = []; this.messages = []; this.$emit('clear'); this.$emit('has-files', false); }, onFocus() { this.focused = true; }, onBlur() { this.focused = false; }, getFileExtension(file: MediaFile) { return '.' + file.name.split('.').pop(); }, isImage(file: MediaFile) { return /^image\//.test(file.type); }, isVideo(file: MediaFile) { return /^video\//.test(file.type); }, generateUniqueFileName(file: any) { let prefix = this.isImage(file) ? 'image' : 'video'; let fileUniqueId = randomNumber(18); let fileExtension = file.split('.').pop(); return `${prefix}-${fileUniqueId}.${fileExtension}`; }, getMediaTypeSupported() { const mediaType = this.mediaType.toLowerCase(); if (mediaType === 'image') { return this.acceptedTypes = this.accept ? this.accept.split(',').filter(type => !type.includes('video')).join(', ') : 'image/jpeg, image/jpg, image/png'; } else if (mediaType === 'video') { return this.acceptedTypes = this.accept ? this.accept.split(',').filter(type => !type.includes('image')).join(', ') : 'video/mp4, video/webm, video/avi'; } else { return this.acceptedTypes = (this.accept || 'image/jpeg, image/jpg, image/png, video/mp4, video/webm, video/avi'); } }, getAllSupportedMediaTypes() { const imageTypes = ['jpeg', 'jpg', 'png', 'gif', 'svg']; const videoTypes = ['mp4', 'webm', 'avi', 'mov', 'mkv']; const supportedTypes = this.acceptedTypes.includes('image/*') ? imageTypes : this.acceptedTypes.includes('video/*') ? videoTypes : [...imageTypes, ...videoTypes]; return supportedTypes.map(type => type.toUpperCase()); } }, computed: { hasFiles() { return Boolean(this.files && this.files.length > 0); }, hasUploadedFiles() { return Boolean(this.uploadedFiles && this.uploadedFiles.length > 0); }, chooseDisabled() { return this.disabled || Boolean(this.fileLimit && this.fileLimit <= this.files.length); }, uploadDisabled() { return this.disabled || !this.hasFiles || Boolean(this.fileLimit && this.fileLimit < this.files.length); }, cancelDisabled() { return this.disabled || !this.hasFiles; }, hasContentInsideTemplateOrEmpty() { if (this.$slots.default && this.$slots.default().length > 0) { return this.$slots.default().every(slot => { return slot.type === 'template' || slot.type === Comment; }); } else { return true; } }, supportedFileTypes() { const specialCases: Record<string, string> = { 'svg+xml': 'svg', 'x-matroska': 'mkv', 'quicktime': 'mov', }; return [...new Set(this.acceptedTypes.split(',').map(type => { const fileType = type.trim().split('/')[1]; return fileType.includes('*') ? this.getAllSupportedMediaTypes().join(', ') : (specialCases[fileType] || fileType).toUpperCase(); }))].join(', '); } }, components: { ErrorMessage: Message, FontAwesomeIcon }, } </script> <template>...</template>
    
javascript typescript vue.js vuejs3
1个回答
0
投票
你可以通过两种方式做到这一点,任何方法都有效

    单击父组件中的按钮,您可以将新道具传递给子组件。在该道具上编写一个监视并触发您想要从子组件中的监视执行的方法。
  1. 使用defineExpose()并使用父组件中的ref访问它。
© www.soinside.com 2019 - 2024. All rights reserved.