通用封装弹窗 / 自定义组件模板,可套用
js
// 子组件
<template>
<el-dialog v-model="innerFlag" title="弹窗">
// 业务内容
<template #footer>
<span class="">
<el-button @click="state.peopleDialogShow = false">取消</el-button>
<el-button type="primary" @click="confirm">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { computed } from 'vue'
// 1. 定义接收的开关参数
const props = defineProps(['show'])
// 2. 定义更新事件 + 业务事件
const emit = defineEmits(['update:show', 'confirmSelect'])
// 封装双向绑定
const innerFlag = computed({
get() { return props.show },
set(val) { emit('update:show', val) }
})
// 业务操作,抛出数据
const confirm = () => {
emit('confirmSelect', '选中的数据')
}
</script>
// 父组件
<CustomDialog
v-model:show="isDialogShow"
@confirmSelect="getData" />
<script setup>
const isDialogShow = ref(false)
const getData = (res) => {
console.log('子组件传回数据', res)
isDialogShow.value = false
}
</script>
业务示例:
html
<template>
<el-dialog v-model="innerVisible" title="组件Library" v-if="innerVisible" width="90%" style="margin-top: 4%">
<div class="comBox">
<div class="xxBox" @click="selectComp('msg')">
<XxCom></XxCom>
</div>
<div class="rwBox" @click="selectComp('task')">
<RwCom></RwCom>
</div>
<div class="chartBox" @click="selectComp('chart')">
<BarChart ref="barChartsChild"></BarChart>
</div>
</div>
</el-dialog>
</template>
<script setup>
import { ref, onMounted, reactive, computed, } from 'vue'
import XxCom from '@/components/text/xx.vue'
import RwCom from '@/components/text/rw.vue'
import BarChart from '@/components/chart/barChart.vue'
const emit = defineEmits(['select', 'update:comDialogShow'])
const props = defineProps(['comDialogShow'])
const innerVisible = computed({
get(){ return props.comDialogShow },//父组件传进来的弹窗显示状态
set(val){ emit('update:comDialogShow', val) } //通知父组件更新变量
})
// 自定义业务事件 把用户选中的组件标识 传给父页面
const selectComp = (key)=>{
emit('select', key)
}
onMounted(()=>{})
</script>
<style lang="scss" scoped>
//dialog 相关样式
.comBox{
display: flex;
flex-wrap: wrap;
gap: 12px; /* 模块间距,按需调整 */
width: 100%;
height: 100%;
overflow: auto;
.xxBox, .rwBox, .chartBox, .zbBox{
width: 24.4%;
height: 36%;
background: #f5f7fa;
border-radius: 8px;
padding: 12px;
overflow-y: auto; /* 内容超出时滚动 */
}
}
</style>
主页面引入
html
<template>
<ComponentLibDialog v-model:comDialogShow="state.dialogVisible" @select="selectCompHandle" />
</template>
<script setup>
const state = reactive({
dialogVisible: false,
})
// 自定义事件;弹窗选中组件,赋值给对应模块
const selectCompHandle = (compKey) => {
// 给当前操作模块赋值
moduleData[activeModuleId].key = compKey
// 关闭弹窗
state.dialogVisible = false
}
</script>
详解 v-model:visible 和 @select 分别是什么、为什么这么写
Vue3 组件自定义双向绑定规范:
v-model:xxx="变量"
等价于
:xxx="变量" @update:xxx="新值 => 变量 = 新值"
- v-model:comDialogShow="dialogVisible" ------ Vue3 多参数 v-model 语法
规则:绑定参数名 xxx,更新事件必须固定叫 update:xxx,这是 Vue 语法约定,不能随便改事件名。
js
// 弹窗组件里定义了:
const emit = defineEmits(['select', 'update:comDialogShow'])
const props = defineProps(['comDialogShow'])
const innerVisible = computed({
get(){ return props.comDialogShow },//父组件传进来的弹窗显示状态
set(val){ emit('update:comDialogShow', val) } //通知父组件更新变量
})
// 父组件对应写法,两种写法完全等价
//写法1. 简写(推荐)
<ComponentLibDialog v-model:comDialogShow="dialogVisible" />
//写法2. 完整展开写法,更好理解底层
<ComponentLibDialog
:comDialogShow="dialogVisible"
@update:comDialogShow="val => dialogVisible = val" />
//能不能换名字?完全可以,举例子
//比如不想用 visible,改用 open:
//1.子组件改 props、emit 名称:
const props = defineProps(['open'])
const emit = defineEmits(['update:open'])
const innerOpen = computed({
get() { return props.open },
set(val) { emit('update:open', val) }
})
//2.父组件同步改成:
<ComponentLibDialog v-model:open="dialogVisible" />
- @select="selectCompHandle" ------ 自定义业务事件
子组件触发: emit('select', 参数):第一个参数是事件名,后面是要传给父页面的数据
js
// 弹窗需要把 用户选中的组件标识(msg/task/chart)传给父页面,所以自定义事件 select
// 选中组件时抛出key
const selectComp = (key)=>{
emit('select', key)
}
父组件接收: @select="selectCompHandle"
js
const selectCompHandle = (compKey) => {
if (!activeModuleId) return
// 给当前操作模块赋值组件
moduleData[activeModuleId].key = compKey
// 关闭弹窗
state.comDialogShow = false
}
事件名可以随便自定义,没有强制约束
比如你想叫 chooseComponent、pick 都没问题:
子组件:emit('chooseComponent', compKey)
父组件:@chooseComponent="handleSelectComp"
举个对比示例,换一套名字演示。功能完全不变,只是参数和事件名换了。
js
//子组件
const props = defineProps(['open'])
const emit = defineEmits(['update:open', 'pickComponent'])
const innerOpen = computed({
get() { return props.open },
set(val) { emit('update:open', val) }
})
const handleSelect = (key) => {
emit('pickComponent', key)
innerOpen.value = false
}
//父组件
<ComponentSelectDialog
v-model:open="dialogVisible"
@pickComponent="handleSelectComp" />
总结:两类事件的区别
| 类型 | 语法规则 | 作用 | 命名约束 |
|---|---|---|---|
| v-model:xxx 双向绑定 | 事件固定 update:xxx | 同步开关类状态(弹窗显隐、开关、输入框值) | 更新事件名不能自定义,必须 update:xxx |
| 自定义业务事件 @xxx | 事件名完全自定义 | 传递业务数据(选中项、提交、删除、点击) | 随便起名,见名知意即可 |
使用场景:
什么时候用 v-model:xxx,什么时候用自定义事件?
1.控制开关、输入值、显隐这类双向同步状态 → 用 v-model:xxx
2.点击选择、提交、删除、获取业务数据这类单向回调传参 → 自定义事件 @xxx