创建组件
<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()
}