创建组件
<template><div class="pika-dialog-overlay"></div><div class="pika-dialog-wrapper"><div class="pika-dialog"><header>标题 <span class="pika-dialog-close"></span></header><main><p>第一行字</p><p>第二行字</p></main><footer><Button level="main">OK</Button><Button>Cancel</Button></footer></div></div></template>
CSS实现右上角关闭按钮
&-close {position: relative;display: inline-block;width: 16px;height: 16px;cursor: pointer;&::before,&::after {content: '';position: absolute;height: 1px;background: black;width: 100%;left: 50%;}&::before {transform: translate(-50%, -50%) rotate(-45deg);}&::after {transform: translate(-50%, -50%) rotate(45deg);}}
支持visible
props接受visible来支持显示或隐藏, template可作为占位标签
<template><template v-if="visible">// ...</template></template>
实现点击关闭
关闭按钮,遮罩层,ok和cancel按钮,四个位置都可点击关闭
<template><h1>示例1</h1><Button @click="toggle">Toggle</Button><Dialog v-model:visible="x" :ok="f1" :cancel="f2" /></template>
<template><template v-if="visible"><div class="pika-dialog-overlay" @click="closeOverlay"></div><div class="pika-dialog-wrapper"><div class="pika-dialog"><header>标题 <span @click="close" class="pika-dialog-close"></span></header><main><p>第一行字</p><p>第二行字</p></main><footer><Button level="main" @click="ok">OK</Button><Button @click="cancel">Cancel</Button></footer></div></div></template></template><script lang="ts">import Button from './Button.vue'export default {props: {visible: {type: Boolean,default: false},closeOnClickOverlay: {type: Boolean,default: true},ok: {type: Function},cancel: {type: Function}},components: {Button},setup(props, context){const close = () => {context.emit('update:visible', false)}const closeOverlay = () => {if(props.closeOnClickOverlay) {close()}}const ok = () => {if(props.ok?.() !== false) {close()}}const cancel = () => {if(props.cancel?.() !== false) {close()}}return {close,closeOverlay,ok,cancel}}}</script>
支持自定义title和content
使用具名插槽
<template><h1>示例1</h1><Button @click="toggle">Toggle</Button><Dialog v-model:visible="x" :ok="f1" :cancel="f2" ><template v-slot:title><strong>加粗的标题</strong></template><template v-slot:content><strong>第一行文字</strong><div>第二行文字</div></template></Dialog></template>
<template><template v-if="visible"><div class="pika-dialog-overlay" @click="closeOverlay"></div><div class="pika-dialog-wrapper"><div class="pika-dialog"><header><slot name="title"/><span @click="close" class="pika-dialog-close"></span></header><main><slot name="content" /></main><footer><Button level="main" @click="ok">OK</Button><Button @click="cancel">Cancel</Button></footer></div></div></template></template>
使用teleport
为了防止dialog所在环境的z-index低于其他环境,导致被遮挡,使用teleport标签将dialog传送到body标签下
<template><template v-if="visible"><teleport to="body">// ...</teleport></template></template>
实现一句话打开Dialog
新建openDialog.ts
h函数三个参数分别是:type, props, children
import { createApp, render, h } from "vue";import Dialog from "./Dialog.vue";export const openDialog = (options) => {const div = document.createElement("div");document.body.appendChild(div);const { title, content } = options;const close = () => {app.unmount();};const app = createApp({render() {return h(Dialog,{visible: true,"onUpdate:visible": (newVisible) => {if (newVisible === false) {close();div.remove();}},},{title,content,});},});app.mount(div);};
调用
const show = () => {openDialog({title: h('strong', {}, '标题'), content: '你好'})}
注意下面两端代码并不等价,
第二段props.cancel?.() !== false如果cancel不存在,则等价于 undefined !== false
if(props.cancel && props.cancel() !== false) {console.log(props.cancel)console.log('cancel')close()}
if(props.cancel?.() !== false) {console.log(props.cancel)console.log('cancel')close()}
