手动封装一个弹窗组件

398 阅读1分钟

需求

最近项目有需求说要用户在弹窗内做一些事情,考虑使用第三方的Vodal,但是发现这个库好久没有更新了,于是乎就自己封装了一个。

分析

  • 弹窗结构主要包括:头部、内容、遮罩;
  • 实现的功能:设定宽高、设定弹框标题、设定是否显示遮罩等

预期效果

下载.gif

代码部分

  • components文件目录下新建一个MyDialog目录,存放一个index.vue组件

image.png

  • 逻辑代码
<template>
  <div v-if="ifShow" v-show="mainShow" :style="{'z-index': zIndex}" :class="{'window': window}" class="my-dialog-main">
    <div v-show="mask && !window" class="my-dialog-mask" />
    <transition name="el-zoom-in-center">
      <div v-show="show" :style="{'height': height, 'width': width}" class="my-dialog">
        <div class="my-dialog-button">
          <div class="close">
            <i class="el-icon-close" @click="close" />
          </div>
        </div>
        <div class="my-dialog-head">
          <div>
            <i class="el-icon-info" />
            {{ title }}
          </div>
        </div>
        <div class="line" />
        <div :class="{'my-dialog-body-padding': padding}" class="my-dialog-body">
          <slot />
        </div>
      </div>
    </transition>
  </div>
</template>
<script>
export default {
  name: 'DialogIndex',
  props: {
    show: { // 是否显示
      type: Boolean,
      default: false
    },
    title: { // 标题
      type: String,
      default: '我的弹框组件'
    },
    height: { // window为false时,控制弹框高度
      type: [String, Number],
      default: '500px'
    },
    width: { // window为false时,控制弹框宽度
      type: [String, Number],
      default: '500px'
    },
    window: { // window为true时,开启窗口模式
      type: Boolean,
      default: false
    },
    padding: { // 是否启用 dialog-body 内置padding: 15px 20px;
      type: Boolean,
      default: false
    },
    mask: { //  window为false时,控制是否显示遮罩,true:显示,false:不显示
      type: Boolean,
      default: true
    }
  },
  data() {
    return {
      ifShow: false,
      mainShow: false,
      zIndex: 100,
      dialogWidth: store.state.sliderCollapse
    }
  },
  watch: {
    show(val) {
      // 同一页面调用多个弹窗层级问题
      if (val) {
        const dialogs = document.querySelectorAll('.my-dialog-main')
        let maxZindex = 100
        dialogs.forEach((item) => {
          const zIndex = item.style.zIndex ? Number(item.style.zIndex) : 0
          if (item.style.display !== 'none' && zIndex >= maxZindex) {
            maxZindex = zIndex + 1
          }
        })
        this.zIndex = maxZindex
        this.ifShow = val
        this.mainShow = val
      } else {
        setTimeout(() => {
          this.mainShow = val
        }, 200)
      }
    }
  },
  mounted() {
    this.$nextTick(() => {
      setTimeout(() => {
        const body = document.querySelector('#app')
        const child = body ? body.children : ''
        let append = ''
        if (child) {
          for (let i = 0; i < child.length; i++) {
            if (child[i].style.display !== 'none') {
              append = child[i]
            }
          }
        }
        if (append) {
          if (append.append) {
            append.append(this.$el)
          }
        }
      }, 300)
    })
  },
  methods: {
    close() {
      this.$emit('close')
    }
  }
}

</script>
<style lang="scss" scoped>
.my-dialog-main {
  position: fixed;
  top: 80px;
  left: 242px;
  width: calc(100% - 280px);
  height: calc(100% - 100px);
  z-index: 100;
  .my-dialog-mask {
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 100;
    background: rgba(0,0,0,.3);
  }
  .my-dialog {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    margin: auto;
    z-index: 101;
    background: #fff;
    border-radius: 3px;
    .my-dialog-button {
      top: 12px;
      right: 12px;
      position: absolute;
      .close {
        font-size: 20px;
        font-weight: 600;
        color: #999;
        cursor: pointer;
        &:hover {
          color: #333;
        }
        > i {
          font-weight: 600;
        }
      }
    }
    .line {
      width: 100%;
      height: 2px;
      background: #99a6ff;
      opacity: 1;
    }
    .my-dialog-head {
      height: 30px;
      font-size: 16px;
      font-weight: 600;
      text-align: left;
      line-height: 30px;
      padding: 12px 0 12px 12px;
    }
    .my-dialog-body {
      height: calc(100% - 42px);
      overflow: auto;
    }
    .my-dialog-body-padding {
      padding: 15px 20px;
    }
  }
}
.window {
  position: absolute;
  width: 100%;
  height: 100%;
  -webkit-box-shadow: 1px 1px 8px 6px #eaeaea;
  box-shadow: 1px 1px 8px 6px #eaeaea;
  z-index: 99;
  .my-dialog {
    height: 100% !important;
    width: 100% !important;
  }
}
</style>

使用

image.png

写到最后

高度、宽度、支持百分比和px且支持窗口模式和全局模式,如有不足还请大佬指正。