Vue: 造轮子-04:Dialog组件
需求分析
- 点击后弹出
- 有遮罩层 overlay
- 有 close 按钮
- 有标题
- 有内容
- 有 yes / no 按钮
- 期望效果
<Dialog
visible
@yes="fn1" @no="fn2"
></Dialog>
支持visible属性
- 注意不要用show表示是否可见
- 创建dialog.vue和dialogDemo.VUE
- dialog.vue
overlay:笼罩层。wrapper:主体
<template>
<div class="dialog-overlay"></div>
<div class="dialog-wrapper">
<header>标题</header>
<main>
<p>1</p>
<p>2</p>
</main>
<footer>
<Button>ok</Button>
<Button>cancel</Button>
</footer>
</div>
</template>
dialog组件接受props:visible变量,用v-if判断是否显示。
让dialog可以点击关闭
- 注意不能通过修改 props.visible控制。不能直接改props
- 在dialog里添加close事件,用户点击触发。
dialog.VUE
const close=()=>{
context.emit('update:visible',false)
}
dialogDemo.vue
<Dialog :visible="x" @update:visible = "x = $event"></Dialog>
// :visible= 和 @update:visible=可以合并为 v-model:visible=
<Dialog v-model:visible="x" ></Dialog>
- 如果想点击dialog黑色遮罩层时也关闭,只需要在遮罩层上也加上@click="close"即可。 但是这样体验不好(用户有时可能不小心就点到了黑色部分,就关闭了),所以可以新加一个props:closeOnClickOverlay
dialog-template
<div class="gulu-dialog-overlay" @click="closeOnClickOverlay"></div>
dialog-script
props:{
visible:{
type:Boolean,
default: false
},
closeOnClickOverlay:{ // 是否点击遮罩层关闭,默认是
type:Boolean,
default:true
},
},
setup(props,context){
const close=()=>{
context.emit('update:visible',false)
}
const closeOnClickOverlay=()=>{
if(props.closeOnClickOverlay ){ // 如果开启了这个功能,才调用close,否则就什么都不做。
close()
}
}
return {close,closeOnClickOverlay}
}
- 点击cancel和ok
- cancel触发cancel事件(f2),ok触发ok事件(f1)。
- dialogDemo给dialog传f1和f2事件,dialog接受props,类型是Function
- ok
const ok=()=>{
if(props.ok && props.ok()!==false){ //如果ok存在,且ok执行后的结果不是false.新写法: props.ok ?.()!==false
close()
}
}
- cancel
const cancel=()=>{
context.emit('cancel')
close()
}
- 点击关闭代码
- dialogDemo
<Dialog v-model:visible="x" :ok="f1" :cancel="f2"></Dialog>
export default {
name: "DialogDemo",
components:{Dialog,Button},
setup(){
const x =ref(false)
const toggle = ()=>{
console.log(x)
x.value =!x.value
}
const f1=()=>{
return true
}
const f2=()=>{
}
return {x,toggle,f1,f2}
}
}
- dialog.vue
<template>
<template v-if="visible">
<div class="gulu-dialog-overlay" @click="closeOnClickOverlay"></div>
<div class="gulu-dialog-wrapper">
<div class="gulu-dialog">
<header>标题
<span @click="close" class="gulu-dialog-close"></span>
</header>
<main>
<p>1</p>
<p>2</p>
</main>
<footer>
<Button @click="ok">ok</Button>
<Button @click="cancel">cancel</Button>
</footer>
</div>
</div>
</template>
</template>
<script lang="ts">
import Button from './button.vue'
export default {
name: "dialog",
components:{Button},
props:{
visible:{
type:Boolean,
default: false
},
closeOnClickOverlay:{
type:Boolean,
default:true
},
ok:{
type:Function
},
cancel:{
type:Function
}
},
setup(props,context){
const close=()=>{
context.emit('update:visible',false)
}
const closeOnClickOverlay=()=>{
if(props.closeOnClickOverlay ){
close()
}
}
const cancel=()=>{
context.emit('cancel')
close()
}
const ok=()=>{
if(props.ok && props.ok()!==false){ //新写法: props.ok ?.()!==false
close()
}
}
return {close,closeOnClickOverlay,cancel,ok}
}
}
</script>
支持title和content
- 使用插槽
- dialogDemo
<Dialog v-model:visible="x" :ok="f1" :cancel="f2">
<template v-slot:content>
<div>hi</div>
</template>
<template v-slot:title>
<strong>加醋的title</strong>
</template>
</Dialog>
- dialog
<template>
<template v-if="visible">
<div class="gulu-dialog-overlay" @click="closeOnClickOverlay"></div>
<div class="gulu-dialog-wrapper">
<div class="gulu-dialog">
<header>
<slot name="title"></slot>
<span @click="close" class="gulu-dialog-close"></span>
</header>
<main>
<slot name="content"></slot>
</main>
<footer>
<Button @click="ok">ok</Button>
<Button @click="cancel">cancel</Button>
</footer>
</div>
</div>
</template>
</template>
- 语法:
// 外面
<template v-slot:content></template>
// 里面
<main>
<slot name="content"></slot>
</main>
把dialog移动到body下面
- 为什么要放到body下面: 防止dialog被遮挡。
- 注意堆叠上下文
- 使用新组建:Teleport
- 自带组件,不用引入
- 作用: 把里面的东西放到body下面。
- dialog.vue
<template>
<template v-if="visible">
<Teleport to="body">
<div class="gulu-dialog-overlay" @click="closeOnClickOverlay"></div>
<div class="gulu-dialog-wrapper">
<div class="gulu-dialog">
<header>
<slot name="title"></slot>
<span @click="close" class="gulu-dialog-close"></span>
</header>
<main>
<slot name="content"></slot>
</main>
<footer>
<Button @click="ok">ok</Button>
<Button @click="cancel">cancel</Button>
</footer>
</div>
</div>
</Teleport>
</template>
</template>
一句话打开 Dialog
- 我不想声明 visible 变量,然后改变它的值
- 技术点:动态挂载组件
- 希望ooutput
<Button @click="showDialog">showDialog</Button>
const showDialog=()=>{
openDialog({
title:'标题',
content:'HELLO',
ok(){
console.log('ok')
},
cancel(){
console.log('cancel')
}
})
}
- 新建openDialog.ts
import Dialog from './dialog.vue'
import {createApp,h} from 'vue'
export const openDialog = (options)=>{
const {title, content,ok,cancel} = options
const div = document.createElement('div')
document.body.appendChild(div) //创建一个div,并且放到body里面。
const close = ()=>{
app.unmount(div)
div.remove()
}
const app = createApp({
render(){
return h(Dialog,{visible:true, // 在渲染dialog的时候,传visible:true
'onUpdate:visible':(newVisible)=>{ // 关闭,visible
if(newVisible ===false){
close()
}
},
ok,cancel},{
title:title,
content:content
})
}
})
app.mount(div) // 把dialog放到div里面
}
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!