| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589 | <template>  <view    v-if="showPopup"    class="uni-popup"    :class="[popupstyle, isDesktop ? 'fixforpc-z-index' : '']"    :style="[{ zIndex: zIndex }]"    @touchmove.stop.prevent="clear"  >    <view @touchstart="touchstart">      <uni-transition        key="1"        v-if="maskShow"        name="mask"        mode-class="fade"        :styles="maskClass"        :duration="duration"        :show="showTrans"        @click="onTap"      />      <uni-transition        key="2"        :mode-class="ani"        name="content"        :styles="{ ...transClass, ...borderRadius }"        :duration="duration"        :show="showTrans"        @click="onTap"      >        <view          v-if="showPopup"          class="uni-popup__wrapper"          :style="[{ backgroundColor: bg }, borderRadius]"          :class="[popupstyle]"          @click="clear"        >          <uni-icons            v-if="showClose"            class="close-icon"            color="#F6F6F6"            type="closeempty"            size="32"            @click="close"          ></uni-icons>          <slot />        </view>      </uni-transition>    </view>    <!-- #ifdef H5 -->    <keypress v-if="maskShow" @esc="onTap" />    <!-- #endif -->  </view>  <!-- #ifdef MP -->  <view v-else style="display: none">    <slot></slot>  </view>  <!-- #endif --></template><script>  // #ifdef H5  import keypress from './keypress.js';  // #endif  /**   * PopUp 弹出层   * @description 弹出层组件,为了解决遮罩弹层的问题   * @tutorial https://ext.dcloud.net.cn/plugin?id=329   * @property {String} type = [top|center|bottom|left|right|message|dialog|share] 弹出方式   * 	@value top 顶部弹出   * 	@value center 中间弹出   * 	@value bottom 底部弹出   * 	@value left		左侧弹出   * 	@value right  右侧弹出   * 	@value message 消息提示   * 	@value dialog 对话框   * 	@value share 底部分享示例   * @property {Boolean} animation = [true|false] 是否开启动画   * @property {Boolean} maskClick = [true|false] 蒙版点击是否关闭弹窗(废弃)   * @property {Boolean} isMaskClick = [true|false] 蒙版点击是否关闭弹窗   * @property {String}  backgroundColor 主窗口背景色   * @property {String}  maskBackgroundColor 蒙版颜色   * @property {Boolean} safeArea		   是否适配底部安全区   * @event {Function} change 打开关闭弹窗触发,e={show: false}   * @event {Function} maskClick 点击遮罩触发   */  import sheep from '@/sheep';  export default {    name: 'SuPopup',    components: {      // #ifdef H5      keypress,      // #endif    },    emits: ['change', 'maskClick', 'close'],    props: {      // 开启状态      show: {        type: Boolean,        default: false,      },      // 顶部,底部时有效      space: {        type: Number,        default: 0,      },      // 默认圆角      round: {        type: [String, Number],        default: 0,      },      // 是否显示关闭      showClose: {        type: Boolean,        default: false,      },      // 开启动画      animation: {        type: Boolean,        default: true,      },      // 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层      // message: 消息提示 ; dialog : 对话框      type: {        type: String,        default: 'bottom',      },      // maskClick      isMaskClick: {        type: Boolean,        default: null,      },      // TODO 2 个版本后废弃属性 ,使用 isMaskClick      maskClick: {        type: Boolean,        default: null,      },      // 可设置none      backgroundColor: {        type: String,        default: '#ffffff',      },      backgroundImage: {        type: String,        default: '',      },      safeArea: {        type: Boolean,        default: true,      },      maskBackgroundColor: {        type: String,        default: 'rgba(0, 0, 0, 0.4)',      },      zIndex: {        type: [String, Number],        default: 10075,      },    },    watch: {      show: {        handler: function (newValue, oldValue) {          if (typeof oldValue === 'undefined' && !newValue) {            return;          }          if (newValue) {            this.open();          } else {            this.close();          }        },        immediate: true,      },      /**       * 监听type类型       */      type: {        handler: function (type) {          if (!this.config[type]) return;          this[this.config[type]](true);        },        immediate: true,      },      isDesktop: {        handler: function (newVal) {          if (!this.config[newVal]) return;          this[this.config[this.type]](true);        },        immediate: true,      },      /**       * 监听遮罩是否可点击       * @param {Object} val       */      maskClick: {        handler: function (val) {          this.mkclick = val;        },        immediate: true,      },      isMaskClick: {        handler: function (val) {          this.mkclick = val;        },        immediate: true,      },      // H5 下禁止底部滚动      showPopup(show) {        // #ifdef H5        // fix by mehaotian 处理 h5 滚动穿透的问题        document.getElementsByTagName('body')[0].style.overflow = show ? 'hidden' : 'visible';        // #endif      },    },    data() {      return {        sheep,        duration: 300,        ani: [],        showPopup: false,        showTrans: false,        popupWidth: 0,        popupHeight: 0,        config: {          top: 'top',          bottom: 'bottom',          center: 'center',          left: 'left',          right: 'right',          message: 'top',          dialog: 'center',          share: 'bottom',        },        maskClass: {          position: 'fixed',          bottom: 0,          top: 0,          left: 0,          right: 0,          backgroundColor: 'rgba(0, 0, 0, 0.4)',        },        transClass: {          position: 'fixed',          left: 0,          right: 0,        },        maskShow: true,        mkclick: true,        popupstyle: this.isDesktop ? 'fixforpc-top' : 'top',      };    },    computed: {      isDesktop() {        return this.popupWidth >= 500 && this.popupHeight >= 500;      },      bg() {        if (this.backgroundColor === '' || this.backgroundColor === 'none') {          return 'transparent';        }        return this.backgroundColor;      },      borderRadius() {        if (this.round) {          if (this.type === 'bottom') {            return {              'border-top-left-radius': parseFloat(this.round) + 'px',              'border-top-right-radius': parseFloat(this.round) + 'px',            };          }          if (this.type === 'center') {            return {              'border-top-left-radius': parseFloat(this.round) + 'px',              'border-top-right-radius': parseFloat(this.round) + 'px',              'border-bottom-left-radius': parseFloat(this.round) + 'px',              'border-bottom-right-radius': parseFloat(this.round) + 'px',            };          }          if (this.type === 'top') {            return {              'border-bottom-left-radius': parseFloat(this.round) + 'px',              'border-bottom-right-radius': parseFloat(this.round) + 'px',            };          }        }      },    },    mounted() {      const fixSize = () => {        const { windowWidth, windowHeight, windowTop, safeArea, screenHeight, safeAreaInsets } =          sheep.$platform.device;        this.popupWidth = windowWidth;        this.popupHeight = windowHeight + (windowTop || 0);        // TODO fix by mehaotian 是否适配底部安全区 ,目前微信ios 、和 app ios 计算有差异,需要框架修复        if (safeArea && this.safeArea) {          // #ifdef MP-WEIXIN          this.safeAreaInsets = screenHeight - safeArea.bottom;          // #endif          // #ifndef MP-WEIXIN          this.safeAreaInsets = safeAreaInsets.bottom;          // #endif        } else {          this.safeAreaInsets = 0;        }      };      fixSize();      // #ifdef H5      // window.addEventListener('resize', fixSize)      // this.$once('hook:beforeDestroy', () => {      // 	window.removeEventListener('resize', fixSize)      // })      // #endif    },    // #ifndef VUE3    // TODO vue2    destroyed() {      this.setH5Visible();    },    // #endif    // #ifdef VUE3    // TODO vue3    unmounted() {      this.setH5Visible();    },    // #endif    created() {      // this.mkclick =  this.isMaskClick || this.maskClick      if (this.isMaskClick === null && this.maskClick === null) {        this.mkclick = true;      } else {        this.mkclick = this.isMaskClick !== null ? this.isMaskClick : this.maskClick;      }      if (this.animation) {        this.duration = 300;      } else {        this.duration = 0;      }      // TODO 处理 message 组件生命周期异常的问题      this.messageChild = null;      // TODO 解决头条冒泡的问题      this.clearPropagation = false;      this.maskClass.backgroundColor = this.maskBackgroundColor;    },    methods: {      setH5Visible() {        // #ifdef H5        // fix by mehaotian 处理 h5 滚动穿透的问题        document.getElementsByTagName('body')[0].style.overflow = 'visible';        // #endif      },      /**       * 公用方法,不显示遮罩层       */      closeMask() {        this.maskShow = false;      },      /**       * 公用方法,遮罩层禁止点击       */      disableMask() {        this.mkclick = false;      },      // TODO nvue 取消冒泡      clear(e) {        // #ifndef APP-NVUE        e.stopPropagation();        // #endif        this.clearPropagation = true;      },      open(direction) {        // fix by mehaotian 处理快速打开关闭的情况        if (this.showPopup) {          clearTimeout(this.timer);          this.showPopup = false;        }        let innerType = ['top', 'center', 'bottom', 'left', 'right', 'message', 'dialog', 'share'];        if (!(direction && innerType.indexOf(direction) !== -1)) {          direction = this.type;        }        if (!this.config[direction]) {          console.error('缺少类型:', direction);          return;        }        this[this.config[direction]]();        this.$emit('change', {          show: true,          type: direction,        });      },      close(type) {        this.showTrans = false;        this.$emit('change', {          show: false,          type: this.type,        });        this.$emit('close');        clearTimeout(this.timer);        // // 自定义关闭事件        // this.customOpen && this.customClose()        this.timer = setTimeout(() => {          this.showPopup = false;        }, 300);      },      // TODO 处理冒泡事件,头条的冒泡事件有问题 ,先这样兼容      touchstart() {        this.clearPropagation = false;      },      onTap() {        if (this.clearPropagation) {          // fix by mehaotian 兼容 nvue          this.clearPropagation = false;          return;        }        this.$emit('maskClick');        if (!this.mkclick) return;        this.close();      },      /**       * 顶部弹出样式处理       */      top(type) {        this.popupstyle = this.isDesktop ? 'fixforpc-top' : 'top';        this.ani = ['slide-top'];        this.transClass = {          position: 'fixed',          left: 0,          right: 0,          top: this.space + 'px',          backgroundColor: this.bg,        };        // TODO 兼容 type 属性 ,后续会废弃        if (type) return;        this.showPopup = true;        this.showTrans = true;        this.$nextTick(() => {          if (this.messageChild && this.type === 'message') {            this.messageChild.timerClose();          }        });      },      /**       * 底部弹出样式处理       */      bottom(type) {        this.popupstyle = 'bottom';        this.ani = ['slide-bottom'];        this.transClass = {          position: 'fixed',          left: 0,          right: 0,          bottom: 0,          paddingBottom: this.safeAreaInsets + this.space + 'px',          backgroundColor: this.bg,        };        // TODO 兼容 type 属性 ,后续会废弃        if (type) return;        this.showPopup = true;        this.showTrans = true;      },      /**       * 中间弹出样式处理       */      center(type) {        this.popupstyle = 'center';        this.ani = ['zoom-out', 'fade'];        this.transClass = {          position: 'fixed',          /* #ifndef APP-NVUE */          display: 'flex',          flexDirection: 'column',          /* #endif */          bottom: 0,          left: 0,          right: 0,          top: 0,          justifyContent: 'center',          alignItems: 'center',        };        // TODO 兼容 type 属性 ,后续会废弃        if (type) return;        this.showPopup = true;        this.showTrans = true;      },      left(type) {        this.popupstyle = 'left';        this.ani = ['slide-left'];        this.transClass = {          position: 'fixed',          left: 0,          bottom: 0,          top: 0,          backgroundColor: this.bg,          /* #ifndef APP-NVUE */          display: 'flex',          flexDirection: 'column',          /* #endif */        };        // TODO 兼容 type 属性 ,后续会废弃        if (type) return;        this.showPopup = true;        this.showTrans = true;      },      right(type) {        this.popupstyle = 'right';        this.ani = ['slide-right'];        this.transClass = {          position: 'fixed',          bottom: 0,          right: 0,          top: 0,          backgroundColor: this.bg,          /* #ifndef APP-NVUE */          display: 'flex',          flexDirection: 'column',          /* #endif */        };        // TODO 兼容 type 属性 ,后续会废弃        if (type) return;        this.showPopup = true;        this.showTrans = true;      },    },  };</script><style lang="scss">  // 关闭icon  .close-icon {    position: absolute;    left: 50%;    transform: translateX(-50%);    bottom: -80rpx;    z-index: 100;  }  .uni-popup {    position: fixed;    /* #ifndef APP-NVUE */    z-index: 99;    /* #endif */    &.top,    &.left,    &.right {      /* #ifdef H5 */      top: var(--window-top);      /* #endif */      /* #ifndef H5 */      top: 0;      /* #endif */    }    .uni-popup__wrapper {      /* #ifndef APP-NVUE */      display: block;      /* #endif */      position: relative;      background: v-bind(backgroundImage) no-repeat;      background-size: 100% 100%;      /* iphonex 等安全区设置,底部安全区适配 */      /* #ifndef APP-NVUE */      // padding-bottom: constant(safe-area-inset-bottom);      // padding-bottom: env(safe-area-inset-bottom);      /* #endif */      &.left,      &.right {        /* #ifdef H5 */        padding-top: var(--window-top);        /* #endif */        /* #ifndef H5 */        padding-top: 0;        /* #endif */        flex: 1;      }    }  }  .fixforpc-z-index {    /* #ifndef APP-NVUE */    z-index: 999;    /* #endif */  }  .fixforpc-top {    top: 0;  }</style>
 |