Vue3父组件向模态框传值并编辑更新数据库的实现方案
Vue3 实现编辑文章并通过Modal更新数据的方案
1. 核心逻辑梳理
- 用
PostsComponent负责文章列表展示、触发编辑请求、获取单条文章数据、控制Modal显示/隐藏、处理更新提交 - 通用
ModalComponent通过props接收显示状态和待编辑数据,通过插槽传递数据和操作方法给父组件,同时通过emit触发关闭、更新事件
2. 实现ModalComponent.vue(带插槽的通用弹窗)
<template> <div v-if="show" class="modal-overlay"> <div class="modal-content"> <h3>编辑文章</h3> <!-- 作用域插槽:传递编辑用数据和操作方法 --> <slot :post="editablePost" @submit="handleSubmit" @close="handleClose" ></slot> <button @click="handleClose" class="cancel-btn">取消</button> </div> </div> </template> <script setup> import { ref } from 'vue' const props = defineProps({ show: { type: Boolean, default: false }, post: { type: Object, required: true } }) const emit = defineEmits(['close', 'update']) // 深拷贝原数据,避免编辑时直接修改列表中的原始数据 const editablePost = ref(JSON.parse(JSON.stringify(props.post))) // 触发更新事件,传递编辑后的数据 const handleSubmit = () => { emit('update', editablePost.value) handleClose() } // 触发关闭弹窗事件 const handleClose = () => { emit('close') } </script> <style scoped> .modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; } .modal-content { background: #fff; padding: 2rem; border-radius: 8px; width: 90%; max-width: 500px; } .cancel-btn { margin-top: 1rem; margin-left: 0.5rem; } </style>
3. 实现PostsComponent.vue(文章列表与编辑逻辑)
<template> <div class="posts-container"> <!-- 文章列表 --> <div v-for="post in posts" :key="post.id" class="post-item"> <h4>{{ post.title }}</h4> <p>{{ post.content.slice(0, 100) }}...</p> <button @click="editPost(post.id)" class="edit-btn">编辑</button> </div> <!-- 编辑弹窗 --> <ModalComponent :show="isModalOpen" :post="selectedPost" @close="isModalOpen = false" @update="handleUpdatePost" > <!-- 插槽内容:自定义编辑表单 --> <template #default="{ post, submit }"> <form @submit.prevent="submit" class="edit-form"> <div class="form-group"> <label>标题:</label> <input v-model="post.title" type="text" required> </div> <div class="form-group"> <label>内容:</label> <textarea v-model="post.content" rows="5" required></textarea> </div> <button type="submit" class="save-btn">保存修改</button> </form> </template> </ModalComponent> </div> </template> <script setup> import { ref } from 'vue' import axios from 'axios' import ModalComponent from './ModalComponent.vue' const posts = ref([]) const isModalOpen = ref(false) const selectedPost = ref({}) // 初始化获取文章列表 const fetchPosts = async () => { const res = await axios.get('/api/posts') posts.value = res.data } // 点击编辑按钮:获取单条文章数据并打开弹窗 const editPost = async (id) => { const res = await axios.get(`/api/posts/${id}`) selectedPost.value = res.data isModalOpen.value = true } // 提交更新请求 const handleUpdatePost = async (updatedPost) => { try { await axios.put(`/api/posts/${updatedPost.id}`, updatedPost) // 更新成功后刷新列表 await fetchPosts() } catch (err) { console.error('更新失败:', err) // 可在此添加用户错误提示(如Toast组件) } } // 页面加载时获取列表 fetchPosts() </script> <style scoped> .posts-container { max-width: 800px; margin: 2rem auto; padding: 0 1rem; } .post-item { border-bottom: 1px solid #eee; padding: 1.5rem 0; } .edit-btn { background: #409eff; color: #fff; border: none; padding: 0.4rem 0.8rem; border-radius: 4px; cursor: pointer; } .edit-form { margin: 1rem 0; } .form-group { margin-bottom: 1rem; } .form-group label { display: block; margin-bottom: 0.5rem; } .form-group input, .form-group textarea { width: 100%; padding: 0.5rem; border: 1px solid #ddd; border-radius: 4px; } .save-btn { background: #67c23a; color: #fff; border: none; padding: 0.5rem 1rem; border-radius: 4px; cursor: pointer; } </style>
4. 关键细节说明
- 数据隔离:Modal中对传入的
post做深拷贝,避免编辑过程中实时修改列表原始数据,只有点击保存后才提交更新 - 插槽灵活性:通过作用域插槽传递数据和方法,让父组件可以完全自定义表单结构,同时复用Modal的弹窗逻辑
- 复杂数据处理:如果待编辑数据包含
Date、函数等复杂类型,建议使用lodash.cloneDeep替代JSON深拷贝,避免数据丢失 - 错误处理:更新请求添加
try/catch,可根据业务需求添加用户友好的错误提示
内容的提问来源于stack exchange,提问作者cookie
相关产品推荐
相关产品推荐

