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介绍。
 
- 提示下载完但解压或打开不了?
 
- 找不到素材资源介绍文章里的示例图片?
 
- 模板不会安装或需要功能定制以及二次开发?
 
                    
    
发表评论
还没有评论,快来抢沙发吧!