Vue3 父组件引用子组件并控制子组件显隐及数据传递

子组件 UserFormDialog.vue

html 复制代码
<!-- 用户新增和修改组件 -->
<template>
  <!-- 弹窗 -->
  <el-dialog v-model="visible" :title="title" width="60%" @close="handleClose">
    <el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="80px">      
      ......
    </el-form>

    <template #footer>
      <div class="dialog-footer">
        <el-button type="primary" :loading="loading" @click="handleSubmit">确 定</el-button>
        <el-button @click="handleClose">取 消</el-button>
      </div>
    </template>
  </el-dialog>
</template>

<script setup>
import {ref, reactive, watch, nextTick} from 'vue'
import { ElMessage } from 'element-plus'

// 1.使用 Vue 3.4+ 的 defineModel 定义了一个名为 visible 的双向绑定变量。父组件通过 v-model 传入该值,子组件修改它会自动同步到父组件,常用于控制弹窗显示/隐藏
const visible = defineModel({
  type: Boolean,
  default: false
})

// 定义属性
const props = defineProps({
  // 弹窗类型 add: 新增 edit: 修改
  title: {
    type: String,
    default: ''
  },
  // 表单数据
  initData: {
    type: Object,
    default: () => ({})
  }
})

// 2.定义事件
const emit = defineEmits(['success'])

const loading = ref(false)
const dataFormRef = ref()

// 本地表单数据(用于双向绑定和验证)
const formData = reactive({});

// 表单校验
const rules = {
  ......
};
// 监听外部传入的显示状态,当 visible 为 true 时填充表单数据
watch(visible, (val) => {
  if (val) {
    // 打开时填充表单数据
    nextTick(() => {
      Object.assign(formData, props.initData || {})
      dataFormRef.value.clearValidate()
    })
  }
})

// 关闭弹窗
const handleClose = () => {
  visible.value = false
}

// 提交表单
const handleSubmit = () => {
  dataFormRef.value.validate(async (valid) => {
    if (valid) {
      try {
        loading.value = true;
        const submitData = {...formData}
        // 新增或修改
        if (submitData.userId) {
          await UserAPI.update(submitData)
          ElMessage.success("修改成功");
        } else {
          await UserAPI.add(submitData)
          ElMessage.success("新增成功");
        }
        // 3.通知父组件刷新列表
        emit('success') 
        handleClose();
      } catch (error) {
        ElMessage.error(error?.message || '操作失败');
      } finally {
        loading.value = false;
      }
    }
  });
}
</script>

<style lang="scss" scoped>

</style>

父组件 index.vue

html 复制代码
<template>
  <div class="app-container-row">
    <el-button type="success" icon="plus" @click="handleAddOrUpdate(undefined)">新增</el-button>
    <!-- 1.子组件 
        v-model="dialog.visible"  对应子组件的 const visible = defineModel({type: Boolean,  default: false})
        @success="handleQuery"    对应子组件的 const emit = defineEmits(['success']); emit('success') 
	-->
    <UserFormDialog
        v-model="dialog.visible"
        :title="dialog.title"
        :init-data="initFormData"
        @success="handleQuery"
    />
  </div>
</template>

<script setup>
import {onMounted, reactive, ref, toRaw} from 'vue'
import UserFormDialog from "@/views/system/user/UserFormDialog.vue";

/**
 * 定义组件名称:在调试工具(如 Vue DevTools)中会显示为组件名称,便于识别和调试
 * 支持<keep-alive>缓存
 */
defineOptions({
  name: 'User'
})

//==============用户表单=================
// 2.初始化子组件标题和显隐
const dialog = reactive({
  title: "",
  visible: false,
});

const defaultFormData = {
  userId: undefined,
  loginName: '',
  nickName: '',
  gender: 1,
  deptId: undefined,
  phone: '',
  email: '',
  remark: ''
}

const initFormData = reactive({ ...defaultFormData })

// 新增或修改页面
function handleAddOrUpdate(rowData) {
  ......
  dialog.visible = true;
}
    
// 查询
function handleQuery() {
  
}
</script>

<style lang="scss" scoped>

</style>
相关推荐
卸任39 分钟前
Vibe coding工具使用心得
前端·ai编程
brien91 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance2 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M2 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写2 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李2 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易2 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马2 小时前
前沿视界
前端
kyriewen2 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易3 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员