编辑页面未选文件报错求助:TypeError问题排查
问题修复:未选择文件时编辑主题抛出迭代错误
错误原因
当未选择文件时,formData.value.images 为 undefined,执行 Array.from(formData.value.images) 时会触发 TypeError: undefined is not iterable——因为undefined不具备可迭代的Symbol.iterator属性。同时若Firestore文档中没有imgUrls字段,formData.value.imgUrls也会是undefined,后续concat操作同样会出错。
修复步骤
- 添加文件存在性判断:在
onSubmit函数中先检查是否有新上传的图片,没有则跳过上传逻辑 - 初始化默认值:给
formData的imgUrls设置默认空数组,避免undefined - 处理Firestore返回数据:拉取已有主题数据时,确保
imgUrls始终是数组类型 - 安全调用数组方法:图片上传函数中,确保操作
imgUrls前它已被正确初始化
修复后的完整代码
Script部分
<script setup> import { getAuth, onAuthStateChanged } from 'firebase/auth' import { getStorage, uploadBytesResumable, ref as firebaseRef, getDownloadURL } from 'firebase/storage' import { doc, updateDoc, getDoc, serverTimestamp } from 'firebase/firestore' import { db } from '../../firebase.config.js' import { v4 as uuidv4 } from 'uuid' import { ref, navigateTo, useRoute } from 'vue' const themeEditListing = ref([]) // 初始化formData,给imgUrls默认空数组 const formData = ref({ name: '', imgUrls: [] }) const route = useRoute() const themeId = route.params.id const newImgProgress = ref(0) const auth = getAuth() onAuthStateChanged(auth, (user) => { if (user) { formData.value.user = user.uid } else { navigateTo('/') } }) const fetchEditListing = async () => { const docRef = doc(db, 'themes', themeId) const docSnap = await getDoc(docRef) if (docSnap.exists) { themeEditListing.value = docSnap.data() // 确保imgUrls是数组,避免undefined formData.value = { ...themeEditListing.value, imgUrls: themeEditListing.value.imgUrls || [] } } } fetchEditListing() const onSubmit = async () => { let newImgUrls = [] // 仅当存在新图片时执行上传逻辑 if (formData.value.images) { try { newImgUrls = await Promise.all( Array.from(formData.value.images).map((image) => storeImage(image)) ) } catch (err) { console.log(err) return // 上传出错时终止提交 } } // 安全合并新旧图片链接,确保imgUrls始终是数组 const imgUrls = [...formData.value.imgUrls, ...newImgUrls] const formDataCopy = { ...formData.value, imgUrls, timestamp: serverTimestamp() } delete formDataCopy.images // 更新Firestore文档 const docRef = doc(db, 'themes', themeId) await updateDoc(docRef, formDataCopy) // 清除已上传的临时文件引用 delete formData.value.images } const onFileChange = (e) => { formData.value.images = e.target.files } const storeImage = async (image) => { return new Promise((resolve, reject) => { // 确保imgUrls存在再执行push if (!formData.value.imgUrls) formData.value.imgUrls = [] formData.value.imgUrls.push('') newImgProgress.value = 1 const storage = getStorage() const fileName = `${auth.currentUser.uid}-${image.name}-${uuidv4()}` const storageRef = firebaseRef(storage, 'images/' + fileName) const uploadTask = uploadBytesResumable(storageRef, image) uploadTask.on( 'state_changed', (snapshot) => { newImgProgress.value = (snapshot.bytesTransferred / snapshot.totalBytes) * 100 console.log('Upload is ' + newImgProgress.value + '% done') switch (snapshot.state) { case 'paused': console.log('O Upload está parado') break case 'running': console.log('Subindo a Imagem') break default: break } }, (error) => { // 出错时移除之前push的占位符 formData.value.imgUrls.pop() newImgProgress.value = 0 reject(error) }, () => { getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => { formData.value.imgUrls.pop() newImgProgress.value = 0 resolve(downloadURL) }) } ) }) } </script>
Template部分
<template> <div> <form @submit.prevent="onSubmit" class="w-full border shadow-md p-4 rounded-md"> <div> <input type="text" v-model="formData.name" placeholder="Nome do Tema" class="input w-full border-x-indigo-300" /> </div> <div> <input type="file" id="images" @change="onFileChange" accept=".jpg, .png, .jpeg, .webp" multiple class="hidden" /> <label for="images" class="cursor-pointer">选择图片</label> <!-- 可选:显示上传进度 --> <div v-if="newImgProgress > 0"> 上传进度: {{ newImgProgress.toFixed(0) }}% </div> </div> <button type="submit" class="mt-4">编辑主题</button> </form> </div> </template>
额外说明
- 修复了未选择文件时的迭代错误,同时处理了
imgUrls未初始化的边界情况 - 添加了上传出错时的回滚逻辑,避免在
imgUrls中留下空占位符 - 修复了模板中未闭合的
label标签,添加了可见的文件选择入口
内容的提问来源于stack exchange,提问作者Ricardo de Paula
相关产品推荐
相关产品推荐

