1 Implementation effect

use

    <on-dialog title="Tip" v-model:visible="visible">
      <span>This is a paragraph</span>
      <template v-slot:footer>
        <on-button type="primary" class="btn">determine</on-button>
        <on-button class="btn" @click="visible = false">cancel</on-button>
      </template>
    </on-dialog>
Copy the code

2 Knowledge points involved

  • Inter-component Communication (SYNC)
  • Use of slots
  • v-show
  • To prevent a bubble

3 Function Implementation

This article will explain the function of dialog box components from simple to complex split, interspersed with knowledge points involved in the middle, the basic skeleton is as follows

<template>
  <div class="on-dialog_wrapper" v-show="visible" @click.self="handleClose">
    <div class="on-dialog" :style="{width:width,marginTop:top}">
      <div class="on-dialog_header">
        <slot name="title">
          <span class="on-dialog_title">{{title}}</span>
        </slot>
        <button class="on-dialog_headerbtn" @click="handleClose">
          <i class="on-icon-close"></i>
        </button>
      </div>
      <div class="on-dialog_body">
        <! -- Default slot -->
        <slot></slot>
      </div>
      <div class="on-dialog_footer" v-if="$slots.footer">
        <slot name="footer"></slot>
      </div>
    </div>
  </div>
</template>
Copy the code

3.1 Title of the dialog box

/ / template section
       <slot name="title">
         <span class="on-dialog_title">{{title}}</span>
       </slot>
/ / props part
      props: {
        title: {
          type: String.default: 'tip'}},// The parent component is basically used
     <on-dialog title="Warm reminder"></on-dialog>
 // The parent component customizes the header content
     <on-dialog v-model:visible="visible">
      <template #title>
        <h3>I am heading</h3>
      </template>
    </on-dialog>
Copy the code

In this section, the parent component can pass the title to the Dialog component via the title property or customize the title using slots. The important thing to note in this section is that the slot is written as above, using the contents of the slot if the parent component does not pass in, and overwriting the contents of the slot if the parent component passes in.

3.2 Contents and buttons in the dialog box

        <button class="on-dialog_headerbtn" @click="handleClose">
          <i class="on-icon-close"></i>
        </button>
      </div>
      <div class="on-dialog_body">
        <! -- Default slot -->
        <slot></slot>
      </div>
      <div class="on-dialog_footer" v-if="$slots.footer">
        <slot name="footer"></slot>
      </div>
Copy the code

This part is also implemented in the form of named slots, which are refined after the skeleton is complete

3.3 Displaying and Hiding dialog Boxes

All the code for this component is shown for illustrative purposes

<template>
  <div class="on-dialog_wrapper" v-show="visible" @click.self="handleClose">
    <div class="on-dialog" :style="{width:width,marginTop:top}">
      <div class="on-dialog_header">
        <slot name="title">
          <span class="on-dialog_title">{{title}}</span>
        </slot>
        <button class="on-dialog_headerbtn" @click="handleClose">
          <i class="on-icon-close"></i>
        </button>
      </div>
      <div class="on-dialog_body">
        <! -- Default slot -->
        <slot></slot>
      </div>
      <div class="on-dialog_footer" v-if="$slots.footer">
        <slot name="footer"></slot>
      </div>
    </div>
  </div>
</template>

<script lang='ts'>
import { defineComponent } from 'vue'
export default defineComponent({
  name: 'OnDialog'.props: {
    title: {
      type: String.default: 'tip'
    },
    width: {
      type: String.default: '30%'
    },
    top: {
      type: String.default: '15vh'
    },
    visible: {
      type: Boolean.default: false
    }
  },
  setup (props, { emit }) {
    const handleClose = () = > {
      emit('update:visible'.false)}return {
      handleClose
    }
  }
})
Copy the code

3.3.1 V-show =”visible” controls show and hide

  • Use v-show=”visible” primarily to control show and hide. Since dialog box components need to be shown and hidden frequently, v-show is used here rather than V-if because v-if removes elements from the DOM, causing browser backflow.

3.3.2 When clicking x and the element outside the dialog box, close the dialog box

  • Since you are clicking on a component, you need to add the click event where the component is clicked, changing visible to False when it is clicked. Note that, in normal writing, when clicked, the visible value is registered with this.$emit and the parent component calls the event.
  • Writing this way, however, makes it cumbersome for the consumer of the component to register an event on the component each time to receive data from the child component. So here we use sync, a syntactic sugar provided by VUE. Since sync is modified by Vue3.0, we will focus on the usage of sync in Vue3.0.
// Used by the parent component
    <on-dialog title="Tip" v-model:visible="visible">
      <span>This is a paragraph</span>
      <template v-slot:footer>
        <on-button type="primary" class="btn">determine</on-button>
        <on-button class="btn" @click="visible = false">cancel</on-button>
    </on-dialog>
Copy the code
  // Dialog box component
  <div class="on-dialog_wrapper" v-show="visible" @click.self="handleClose">
    <div class="on-dialog" :style="{width:width,marginTop:top}">
      <div class="on-dialog_header">
        <slot name="title">
          <span class="on-dialog_title">{{title}}</span>
        </slot>
        <button class="on-dialog_headerbtn" @click="handleClose">
          <i class="on-icon-close"></i>
        </button>
      </div>
     setup (props, { emit }) {
    const handleClose = () => {
      emit('update:visible', false)
    }
    return {
      handleClose
    }
  }
Copy the code

The handleClose event is triggered when the user clicks, and the event can be passed to the parent component via the Update: Visible event. The parent component can be vue3.0 Sync using v-Model: Visible =”visible”

4 Complete Code

<template>
  <div class="on-dialog_wrapper" v-show="visible" @click.self="handleClose">
    <div class="on-dialog" :style="{width:width,marginTop:top}">
      <div class="on-dialog_header">
        <slot name="title">
          <span class="on-dialog_title">{{title}}</span>
        </slot>
        <button class="on-dialog_headerbtn" @click="handleClose">
          <i class="on-icon-close"></i>
        </button>
      </div>
      <div class="on-dialog_body">
        <! -- Default slot -->
        <slot></slot>
      </div>
      <div class="on-dialog_footer" v-if="$slots.footer">
        <slot name="footer"></slot>
      </div>
    </div>
  </div>
</template>

<script lang='ts'>
import { defineComponent } from 'vue'
export default defineComponent({
  name: 'OnDialog'.props: {
    title: {
      type: String.default: 'tip'
    },
    width: {
      type: String.default: '30%'
    },
    top: {
      type: String.default: '15vh'
    },
    visible: {
      type: Boolean.default: false
    }
  },
  setup (props, { emit }) {
    const handleClose = () = > {
      emit('update:visible'.false)}return {
      handleClose
    }
  }
})

</script>
<style lang="scss" scoped>
.on-dialog_wrapper {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  overflow: auto;
  margin: 0;
  z-index: 2001;
  background-color: rgba(0.0.0.0.5);
  .on-dialog {
    position: relative;
    margin: 15vh auto 50px;
    background: #fff;
    border-radius: 2px;
    box-shadow: 0 1px 3px rgba(0.0.0.0.3);
    box-sizing: border-box;
    width: 30%;
    &_header {
      padding: 20px 20px 10px;
      .on-dialog_title {
        line-height: 24px;
        font-size: 18px;
        color: # 303133;
      }
      .on-dialog_headerbtn {
        position: absolute;
        top: 20px;
        right: 20px;
        padding: 0;
        background: transparent;
        border: none;
        outline: none;
        cursor: pointer;
        font-size: 16px;
        .one-icon-close {
          color: 909399;
        }
      }
    }
    &_body {
      padding: 30px 20px;
      color: # 606266;
      font-size: 14px;
      word-break: break-all;
    }
    &_footer {
      padding: 10px 20px 20px;
      text-align: right;
      box-sizing: border-box;
      ::v-deep .on-button:first-child {
        margin-right: 20px; }}}}.dialog-fade-enter-active {
  animation: fade 0.3 s;
}
.dialog-fade-leave-active {
  animation: fade 0.3 s reverse;
}
@keyframes fade {
  0% {
    opacity: 0;
    transform: translateY(-20px);
  }
  100% {
    opacity: 1;
    transform: translateY(0); }}</style>

Copy the code