Vue 3现有表单如何将文本输入框替换为标签输入组件并提交到Firestore
实现方案
1. 先封装通用标签输入组件
在项目src/components目录下新建TagInput.vue文件,代码如下:
<template> <div class="tag-input-wrapper"> <div class="tag-list"> <span v-for="(tag, index) in localTags" :key="index" class="tag-item"> {{ tag }} <button type="button" @click="removeTag(index)" class="tag-remove">×</button> </span> </div> <input type="text" v-model="inputValue" @keydown.enter.prevent="addTag" @keydown.comma.prevent="addTag" placeholder="输入后按回车或逗号添加标签" class="tag-input" /> </div> </template> <script> import { ref, watch } from 'vue' export default { props: { modelValue: { type: Array, default: () => [] }, maxTags: { type: Number, default: 3 // 适配你要求的3个标签限制 } }, emits: ['update:modelValue'], setup(props, { emit }) { const inputValue = ref('') const localTags = ref([...props.modelValue]) // 同步父组件绑定值 watch(() => props.modelValue, (newVal) => { localTags.value = [...newVal] }, { deep: true }) const addTag = () => { const trimmedVal = inputValue.value.trim() if (!trimmedVal || localTags.value.includes(trimmedVal) || localTags.value.length >= props.maxTags) return localTags.value.push(trimmedVal) emit('update:modelValue', [...localTags.value]) inputValue.value = '' } const removeTag = (index) => { localTags.value.splice(index, 1) emit('update:modelValue', [...localTags.value]) } return { inputValue, localTags, addTag, removeTag } } } </script> <style scoped> .tag-input-wrapper { border: 1px solid #ddd; padding: 8px; border-radius: 4px; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; } .tag-list { display: flex; gap: 6px; flex-wrap: wrap; } .tag-item { background: #e0f2fe; padding: 4px 8px; border-radius: 4px; display: flex; align-items: center; gap: 4px; } .tag-remove { background: none; border: none; cursor: pointer; color: #0ea5e9; font-size: 16px; padding: 0 2px; } .tag-input { border: none; outline: none; flex: 1; min-width: 120px; } </style>
2. 修改现有表单页面代码
2.1 替换template中对应输入框
找到原来的优点、缺点输入框部分,替换为标签组件:
<!-- 优点部分改造后 --> <div> <h3>优点</h3> <div for="petPros">这个宠物的3个优点是什么?</div> <TagInput id="petPros" v-model="petPros" :max-tags="3" /> <label for="petPros">最多添加3个标签</label> </div> <hr> <!-- 缺点部分改造后 --> <div> <h3>缺点</h3> <div for="petCons">这个宠物的3个缺点是什么?</div> <TagInput id="petCons" v-model="petCons" :max-tags="3" /> <label for="petCons">最多添加3个标签</label> </div>
2.2 修改script部分逻辑
首先引入组件,然后把原来字符串类型的petPros、petCons改成数组类型:
<script> import { ref } from 'vue' import useStorage from '@/composables/useStorage' import useCollection from '@/composables/useCollection' // 引入标签组件 import TagInput from '@/components/TagInput.vue' export default { // 注册组件 components: { TagInput }, setup() { const { filePath, url, uploadImage } = useStorage() const { error, addDoc } = useCollection('reviews') const { user } = getUser() const router = useRouter() const pet_type = ref('') // 改为数组类型默认值 const petPros = ref([]) const petCons = ref([]) const reviewPrivacy = ref('') const file = ref(null) const fileError = ref(null) const isPending = ref(false) const handleSubmit = async () => { // 可加标签非空校验 if (petPros.value.length === 0 || petCons.value.length === 0) { alert('请填写至少1个优点和1个缺点') return } if (file.value) { isPending.value = true await uploadImage(file.value) const res = await addDoc({ pet_type: pet_type.value, // Firestore原生支持数组类型,直接存即可 petPros: petPros.value, petCons: petCons.value, // 如果需要兼容原来的字符串存储格式,改成如下代码即可 // petPros: petPros.value.join(','), // petCons: petCons.value.join(','), reviewPrivacy: reviewPrivacy.value, userId: user.value.uid, userName: user.value.displayName, createdAt: timestamp() }) isPending.value = false if (!error.value) { router.push({ name: 'ReviewDetails', params: { id: res.id }}) } } } // 允许的文件类型 const types = ['image/png', 'image/jpeg'] const handleChange = (e) => { let selected = e.target.files[0] console.log(selected) if (selected && types.includes(selected.type)) { file.value = selected fileError.value = null } else { file.value = null fileError.value = '请选择图片文件(png、jpg或jpeg格式)' } } return { pet_type, petPros, petCons, reviewPrivacy, handleSubmit, fileError, handleChange, isPending } } } </script>
说明
- Firestore原生支持数组类型字段,直接存储标签数组即可,不需要额外转换,后续查询过滤也更灵活
- 组件默认限制最多3个标签,符合你的业务需求,可以通过
max-tags属性调整限制数量 - 你可以根据需要自行调整组件的样式,匹配现有表单的UI风格
内容的提问来源于stack exchange,提问作者Mark Gavagan
相关产品推荐
相关产品推荐

