MyPopup.vue 9.4 KB
<template>
  <div class="my-popup-view" :class="`theme-${theme}`" v-if="open">
    <div v-if="overlay" class="popup-mask-view" @click="closeOnClickOverlay ? _close() : ''"></div>
    <div class="popup-content-view" :class="`position-${position} ${transition ? `position-${position}-enter-to` : `position-${position}-leave-to`}`" :style="contentStyle">
      <div
        v-if="popupTitle !== '' || showCloseIcon"
        class="popup-title"
        :style="[popupTitleTextStyle, theme !== 'light' && theme !== 'dark' ? { backgroundColor: contentBgColor } : {}]"
      >
        <div v-if="showCloseIcon" class="close-btn van-icon van-icon-cross" @click="_close"></div>
        <span>{{ popupTitle }}</span>
      </div>
      <div class="popup-slot">
        <slot></slot>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'MyPopup',
  props: {
    theme: {
      type: String,
      default: 'light'
    },
    // 是否显示
    value: {
      type: Boolean,
      required: true
    },
    // 遮罩
    overlay: {
      type: Boolean,
      default: () => {
        return true
      }
    },
    closeOnClickOverlay: {
      type: Boolean,
      default: () => {
        return true
      }
    },
    popupTitle: {
      type: String,
      default: ''
    },
    popupTitleTextStyle: {
      type: Object,
      default: () => {
        return {}
      }
    },
    contentBgColor: {
      type: String,
      default: '#fff'
    },
    showCloseIcon: {
      type: Boolean,
      default: () => {
        return false
      }
    },

    // 弹出位置
    // position.options: top bottom right left
    position: {
      type: String,
      default: 'center'
    },
    // 圆角
    round: {
      type: Boolean,
      default: () => {
        return false
      }
    },
    width: {
      type: [String, Number],
      default: '80'
    },
    height: {
      type: [String, Number],
      default: '300'
    },
    bottom: {
      type: String,
      default: null
    }
  },
  watch: {
    isOpen(newVal) {
      if (typeof newVal === 'boolean') {
        this.open = newVal
      } else {
        newVal.then(res => (this.open = res))
      }
    }
  },
  computed: {
    isOpen: {
      get() {
        if (this.value) {
          setTimeout(() => {
            this.transition = true
          }, 100)
          return this.value
        } else {
          this.transition = false
          return new Promise(resolve => {
            setTimeout(() => {
              resolve(false)
            }, 300)
          })
        }
      },
      set(show) {
        this.$emit('input', show)
      }
    },
    contentStyle() {
      let bc = {}
      if (this.theme !== 'light' && this.theme !== 'dark') {
        bc = {
          backgroundColor: this.contentBgColor
        }
      }
      if (this.position === 'center') {
        return Object.assign(bc, {
          left: '50%',
          top: '50%',
          transform: 'translate(-50%, -50%)'
        })
      } else if (this.position === 'top') {
        return Object.assign(bc, {
          left: 0,
          top: '-100%',
          maxHeight: '100%',
          width: '100%',
          height:
            typeof this.height === 'string' && this.height.indexOf('%') !== -1
              ? this.height
              : typeof this.height === 'string' && this.height.indexOf('%') === -1 && Number(this.height) < 100
              ? this.height + '%'
              : typeof this.height === 'string' && this.height.indexOf('%') === -1 && Number(this.height) > 100
              ? this.height + 'px'
              : typeof this.height === 'number'
              ? this.height + 'px'
              : '300px',
          'border-bottom-left-radius': this.round ? '12px' : '0',
          'border-bottom-right-radius': this.round ? '12px' : '0'
        })
      } else if (this.position === 'bottom') {
        return Object.assign(bc, {
          left: 0,
          bottom: '-100%',
          maxHeight: '100%',
          width: '100%',
          height:
            typeof this.height === 'string' && this.height.indexOf('%') !== -1
              ? this.height
              : typeof this.height === 'string' && this.height.indexOf('%') === -1 && Number(this.height) < 100
              ? this.height + '%'
              : typeof this.height === 'string' && this.height.indexOf('%') === -1 && Number(this.height) > 100
              ? this.height + 'px'
              : typeof this.height === 'number'
              ? this.height + 'px'
              : '300px',
          'border-top-left-radius': this.round ? '12px' : '0',
          'border-top-right-radius': this.round ? '12px' : '0'
        })
      } else if (this.position === 'left') {
        return Object.assign(bc, {
          left: '-100%',
          top: this.bottom ? 'unset' : '0',
          bottom: this.bottom ? this.bottom : 'unset',
          maxWidth: '100%',
          width:
            typeof this.width === 'string' && this.width.indexOf('%') !== -1
              ? this.width
              : typeof this.width === 'string' && this.width.indexOf('%') === -1 && Number(this.width) < 100
              ? this.width + '%'
              : typeof this.width === 'string' && this.width.indexOf('%') === -1 && Number(this.width) > 100
              ? this.width + 'px'
              : typeof this.width === 'number'
              ? this.width + 'px'
              : '240px',
          height:
            typeof this.height === 'string' && this.height.indexOf('%') !== -1
              ? this.height
              : typeof this.height === 'string' && this.height.indexOf('%') === -1 && Number(this.height) < 100
              ? this.height + '%'
              : typeof this.height === 'string' && this.height.indexOf('%') === -1 && Number(this.height) > 100
              ? this.height + 'px'
              : typeof this.height === 'number'
              ? this.height + 'px'
              : '100%',
          'border-top-right-radius': this.round ? '12px' : '0',
          'border-bottom-right-radius': this.round ? '12px' : '0'
        })
      } else if (this.position === 'right') {
        return Object.assign(bc, {
          right: '-100%',
          top: this.bottom ? 'unset' : '0',
          bottom: this.bottom ? this.bottom : 'unset',
          maxWidth: '100%',
          width:
            typeof this.width === 'string' && this.width.indexOf('%') !== -1
              ? this.width
              : typeof this.width === 'string' && this.width.indexOf('%') === -1 && Number(this.width) < 100
              ? this.width + '%'
              : typeof this.width === 'string' && this.width.indexOf('%') === -1 && Number(this.width) > 100
              ? this.width + 'px'
              : typeof this.width === 'number'
              ? this.width + 'px'
              : '240px',
          height:
            typeof this.height === 'string' && this.height.indexOf('%') !== -1
              ? this.height
              : typeof this.height === 'string' && this.height.indexOf('%') === -1 && Number(this.height) < 100
              ? this.height + '%'
              : typeof this.height === 'string' && this.height.indexOf('%') === -1 && Number(this.height) > 100
              ? this.height + 'px'
              : typeof this.height === 'number'
              ? this.height + 'px'
              : '100%',
          'border-top-left-radius': this.round ? '12px' : '0',
          'border-bottom-left-radius': this.round ? '12px' : '0'
        })
      }
    }
  },
  data() {
    return {
      open: false,
      transition: false
    }
  },
  mounted() {
    this.isOpen.then(value => {
      this.open = value
    })
  },
  methods: {
    _close() {
      this.isOpen = false
    }
  }
}
</script>

<style lang="scss" scoped>
.my-popup-view {
  position: fixed;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  z-index: 999999;
  pointer-events: auto;

  .popup-mask-view {
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 10;
    background-color: #000;
    opacity: 0.5;
  }
  .popup-content-view {
    position: absolute;
    z-index: 11;
    overflow-y: auto;
    transition: all 0.3s;
    &.position-top-leave-to {
      top: -100% !important;
    }
    &.position-top-enter-to {
      top: 0 !important;
    }
    &.position-bottom-leave-to {
      bottom: -100% !important;
    }
    &.position-bottom-enter-to {
      bottom: 0 !important;
    }
    &.position-left-leave-to {
      left: -100% !important;
    }
    &.position-left-enter-to {
      left: 0 !important;
    }
    &.position-right-leave-to {
      right: -100% !important;
    }
    &.position-right-enter-to {
      right: 0 !important;
    }

    .popup-title {
      padding: 0 10px;
      font-size: 16px;
      font-weight: bold;
      text-align: center;
      height: 44px;
      line-height: 44px;
      position: relative;
    }
    .close-btn {
      width: 36px;
      height: 36px;
      line-height: 36px;
      position: absolute;
      top: 4px;
      right: 5px;
      text-align: center;
      font-size: 16px;
      color: #888;
    }
    .popup-slot {
      height: calc(100% - 44px);
    }
  }
  &.theme-light {
    .popup-content-view {
      background-color: #fff;
      .popup-title {
        background-color: #fff;
        border-bottom: 1px solid #eee;
        color: #333;
      }
    }
  }
  &.theme-dark {
    .popup-content-view {
      background-color: #1e1e20;
      .popup-title {
        background-color: #252528;
        border-bottom: 1px solid #414346;
        color: #797c82;
      }
    }
  }
}
</style>