创建组件

  1. <template>
  2. <div class="pika-dialog-overlay"></div>
  3. <div class="pika-dialog-wrapper">
  4. <div class="pika-dialog">
  5. <header>标题 <span class="pika-dialog-close"></span></header>
  6. <main>
  7. <p>第一行字</p>
  8. <p>第二行字</p>
  9. </main>
  10. <footer>
  11. <Button level="main">OK</Button>
  12. <Button>Cancel</Button>
  13. </footer>
  14. </div>
  15. </div>
  16. </template>

CSS实现右上角关闭按钮

  1. &-close {
  2. position: relative;
  3. display: inline-block;
  4. width: 16px;
  5. height: 16px;
  6. cursor: pointer;
  7. &::before,
  8. &::after {
  9. content: '';
  10. position: absolute;
  11. height: 1px;
  12. background: black;
  13. width: 100%;
  14. left: 50%;
  15. }
  16. &::before {
  17. transform: translate(-50%, -50%) rotate(-45deg);
  18. }
  19. &::after {
  20. transform: translate(-50%, -50%) rotate(45deg);
  21. }
  22. }

支持visible

props接受visible来支持显示或隐藏, template可作为占位标签

  1. <template>
  2. <template v-if="visible">
  3. // ...
  4. </template>
  5. </template>

实现点击关闭

关闭按钮,遮罩层,ok和cancel按钮,四个位置都可点击关闭

  1. <template>
  2. <h1>示例1</h1>
  3. <Button @click="toggle">Toggle</Button>
  4. <Dialog v-model:visible="x" :ok="f1" :cancel="f2" />
  5. </template>
  1. <template>
  2. <template v-if="visible">
  3. <div class="pika-dialog-overlay" @click="closeOverlay"></div>
  4. <div class="pika-dialog-wrapper">
  5. <div class="pika-dialog">
  6. <header>标题 <span @click="close" class="pika-dialog-close"></span></header>
  7. <main>
  8. <p>第一行字</p>
  9. <p>第二行字</p>
  10. </main>
  11. <footer>
  12. <Button level="main" @click="ok">OK</Button>
  13. <Button @click="cancel">Cancel</Button>
  14. </footer>
  15. </div>
  16. </div>
  17. </template>
  18. </template>
  19. <script lang="ts">
  20. import Button from './Button.vue'
  21. export default {
  22. props: {
  23. visible: {
  24. type: Boolean,
  25. default: false
  26. },
  27. closeOnClickOverlay: {
  28. type: Boolean,
  29. default: true
  30. },
  31. ok: {
  32. type: Function
  33. },
  34. cancel: {
  35. type: Function
  36. }
  37. },
  38. components: {
  39. Button
  40. },
  41. setup(props, context){
  42. const close = () => {
  43. context.emit('update:visible', false)
  44. }
  45. const closeOverlay = () => {
  46. if(props.closeOnClickOverlay) {
  47. close()
  48. }
  49. }
  50. const ok = () => {
  51. if(props.ok?.() !== false) {
  52. close()
  53. }
  54. }
  55. const cancel = () => {
  56. if(props.cancel?.() !== false) {
  57. close()
  58. }
  59. }
  60. return {
  61. close,
  62. closeOverlay,
  63. ok,
  64. cancel
  65. }
  66. }
  67. }
  68. </script>

支持自定义title和content

使用具名插槽

  1. <template>
  2. <h1>示例1</h1>
  3. <Button @click="toggle">Toggle</Button>
  4. <Dialog v-model:visible="x" :ok="f1" :cancel="f2" >
  5. <template v-slot:title>
  6. <strong>加粗的标题</strong>
  7. </template>
  8. <template v-slot:content>
  9. <strong>第一行文字</strong>
  10. <div>第二行文字</div>
  11. </template>
  12. </Dialog>
  13. </template>
  1. <template>
  2. <template v-if="visible">
  3. <div class="pika-dialog-overlay" @click="closeOverlay"></div>
  4. <div class="pika-dialog-wrapper">
  5. <div class="pika-dialog">
  6. <header>
  7. <slot name="title"/>
  8. <span @click="close" class="pika-dialog-close"></span>
  9. </header>
  10. <main>
  11. <slot name="content" />
  12. </main>
  13. <footer>
  14. <Button level="main" @click="ok">OK</Button>
  15. <Button @click="cancel">Cancel</Button>
  16. </footer>
  17. </div>
  18. </div>
  19. </template>
  20. </template>

使用teleport

为了防止dialog所在环境的z-index低于其他环境,导致被遮挡,使用teleport标签将dialog传送到body标签下

  1. <template>
  2. <template v-if="visible">
  3. <teleport to="body">
  4. // ...
  5. </teleport>
  6. </template>
  7. </template>

实现一句话打开Dialog

新建openDialog.ts
h函数三个参数分别是:type, props, children

  1. import { createApp, render, h } from "vue";
  2. import Dialog from "./Dialog.vue";
  3. export const openDialog = (options) => {
  4. const div = document.createElement("div");
  5. document.body.appendChild(div);
  6. const { title, content } = options;
  7. const close = () => {
  8. app.unmount();
  9. };
  10. const app = createApp({
  11. render() {
  12. return h(
  13. Dialog,
  14. {
  15. visible: true,
  16. "onUpdate:visible": (newVisible) => {
  17. if (newVisible === false) {
  18. close();
  19. div.remove();
  20. }
  21. },
  22. },
  23. {
  24. title,
  25. content,
  26. }
  27. );
  28. },
  29. });
  30. app.mount(div);
  31. };

调用

  1. const show = () => {
  2. openDialog({title: h('strong', {}, '标题'), content: '你好'})
  3. }

注意下面两端代码并不等价,
第二段props.cancel?.() !== false如果cancel不存在,则等价于 undefined !== false

  1. if(props.cancel && props.cancel() !== false) {
  2. console.log(props.cancel)
  3. console.log('cancel')
  4. close()
  5. }
  1. if(props.cancel?.() !== false) {
  2. console.log(props.cancel)
  3. console.log('cancel')
  4. close()
  5. }