ソースを参照

fix:修改popup bug,完善其功能

yangkaixuan 5 年 前
コミット
c2da4888d1

+ 14 - 3
src/packages/popup/demo.vue

@@ -8,7 +8,7 @@
       @click.native="showBasic = true"
     >
     </nut-cell>
-    <nut-popup :style="{ padding: '30px 50px' }" v-model="showBasic" >正文</nut-popup>
+    <nut-popup :style="{ padding: '30px 50px' }"   v-model="showBasic" >正文</nut-popup>
 
     <h2 class="title">弹出位置</h2>
 
@@ -116,6 +116,16 @@
       position="bottom"
       :style="{ height: '20%' }"
     ></nut-popup>
+
+    <h2 class="title">指定挂载节点</h2>
+    <nut-cell
+      isLink
+      title="指定挂载节点"
+      :showIcon="true"
+      @click.native="getContainer = true"
+    >
+    </nut-cell>
+    <nut-popup :style="{ padding: '30px 50px' }"  get-container="body"  v-model="getContainer" >body</nut-popup>
   </div>
 </template>
 <script>
@@ -131,10 +141,11 @@ export default {
       showIcon: false,
       showRound: false,
       showIconPosition: false,
-      showCloseIcon: false
+      showCloseIcon: false,
+      getContainer:false
     };
   },
-  methods: {
+  methods: {     
     show() {
       this.isShow = true;
     }

+ 18 - 1
src/packages/popup/doc.md

@@ -64,4 +64,21 @@ export default {
 | closeable  | 是否显示关闭图标                        | Boolean  | false     |
 | close-icon | 关闭图标名称                  | string  | cross     |
 | close-icon-position | 关闭图标位置,可选值为top-left bottom-left bottom-right | string  | top-right  |
-
+| overlay-class | 自定义遮罩层类名 | string  |   |
+| overlay-style | 自定义遮罩层样式 | object  |   |
+| lock-scroll | 是否锁定背景滚动 | boolean  |  true |
+| close-on-click-overlay	 | 是否在点击遮罩层后关闭 | boolean  |  true |
+| get-container | 指定挂载节点 | string   |   |
+
+
+## Event
+
+| 事件名       | 说明                                     |  
+| ---------- | ---------------------------------------- |  
+| click    |	点击弹出层时触发	                     | 
+| open    |		打开弹出层时触发                     | 
+| close    |	关闭弹出层时触发	                     | 
+| opened    |	打开弹出层且动画结束后触发	                     | 
+| closed    |	关闭弹出层且动画结束后触发	                     | 
+| click-overlay    |	点击遮罩层时触发	                     | 
+ 

+ 0 - 14
src/packages/popup/mask.vue

@@ -1,14 +0,0 @@
-<template>
-  <transition name="popup-fade">
-    <div v-show="show" class="bg nut-mask" ></div>
-  </transition>
-</template>
-<script>
-export default {
-  name: "nut-popup-mask",
-  props: {
-    show: { type: Boolean, default: true }
-  },    
-};
-</script>
- 

+ 15 - 0
src/packages/popup/overlay.vue

@@ -0,0 +1,15 @@
+<template>
+  <transition name="popup-fade">
+    <div v-show="show" class="bg nut-mask" :class="className"></div>
+  </transition>
+</template>
+<script>
+export default {
+  name: "nut-popup-mask",
+  props: {
+    show: { type: Boolean, default: true },
+    className: { type: String, default: "" },
+    customStyle: { type: String, default: "" },    	
+  }
+};
+</script>

+ 4 - 2
src/packages/popup/popup.scss

@@ -71,7 +71,7 @@ $popup-close-icon-margin: 16px;
     background-color: #fff;
     transition: transform 0.3s;
     -webkit-overflow-scrolling: touch;
-    z-index: 2028;
+    z-index: 100;
   }
   @keyframes nut-fade-in {
     from {
@@ -93,7 +93,9 @@ $popup-close-icon-margin: 16px;
     }
   }
  
-  
+  .nut-overflow-hidden{
+    overflow: hidden !important;
+  }
  
 
 .nutui-popup{

+ 103 - 23
src/packages/popup/popup.vue

@@ -1,20 +1,32 @@
 <template>
-  <transition :name="transitionName">
+  <transition
+    :name="transitionName"
+    @after-enter="$emit('opened')"
+    @after-leave="$emit('closed')"
+  >
     <div
       ref="popupBox"
       v-show="value"
       class="popup-box"
       :class="[`popup-${position}`, { round }]"
+      @click="$emit('click', this)"
     >
-        <slot></slot>
-        <nut-icon v-if='closeable' @click.native='$emit("input", false)' :type="closeIcon" size="12px"  class="nutui-popup__close-icon" :class="'nutui-popup__close-icon--'+closeIconPosition">
-        </nut-icon>
+      <slot></slot>
+      <nut-icon
+        v-if="closeable"
+        @click.native="$emit('input', false)"
+        :type="closeIcon"
+        size="12px"
+        class="nutui-popup__close-icon"
+        :class="'nutui-popup__close-icon--' + closeIconPosition"
+      >
+      </nut-icon>
     </div>
   </transition>
 </template>
 <script>
 import Vue from "vue";
-import Mask from "./mask.vue";
+import overlay from "./overlay.vue";
 
 export default {
   name: "nut-popup",
@@ -30,20 +42,38 @@ export default {
       type: Boolean,
       default: true
     },
-    closeable:{
-        type: Boolean,
-        default: false 
+    closeable: {
+      type: Boolean,
+      default: false
     },
     closeIconPosition: {
       type: String,
-      default: 'top-right'
+      default: "top-right"
+    },
+    closeIcon: {
+      type: String,
+      default: "cross"
+    },
+    lockScroll:{
+        type:Boolean,
+        default:true
+    },
+    closeOnClickOverlay:{
+        type:Boolean,
+        default:true
+    },
+    overlayClass: {
+        type:String,
+        default:""
     },
-    closeIcon:{
-        type: String,
-        default: 'cross'
+    overlayStyle: {
+        type:String,
+        default:""
     },
+    getContainer:String,
     round: Boolean
   },
+  beforeCreate() {},
   created() {
     if (this.transition) {
       this.transitionName = this.transition;
@@ -56,6 +86,12 @@ export default {
   mounted() {
     if (this.duration) {
       this.$refs.popupBox.style.transitionDuration = this.duration + "s";
+    }    
+    if (this.getContainer) {
+        this.portal();
+    }
+    if (this.value) {
+      this.open();
     }
   },
   watch: {
@@ -69,16 +105,16 @@ export default {
       } else {
         this.transitionName = `popup-slide-${this.position}`;
       }
-    }
+    },
+    getContainer: 'portal'
   },
   data() {
     return {
       transitionName: "popup-fade",
-      maskInstant: null
+      overlayInstant: null
     };
   },
-
-  methods: {
+  methods: {    
     mount(Component, data) {
       const instance = new Vue({
         el: document.createElement("div"),
@@ -90,26 +126,70 @@ export default {
           });
         }
       });
-      document.body.appendChild(instance.$el);
+      const el = this.$refs.popupBox;
+      if (el && el.parentNode) {
+        el.parentNode.insertBefore(instance.$el, el);
+      } else {
+        document.body.appendChild(instance.$el);
+      }
       return instance;
     },
 
     open() {
-      if (!this.maskInstant) {
-        this.maskInstant = this.mount(Mask, {
+      if (!this.overlayInstant) {
+        this.overlayInstant = this.mount(overlay, {
+          className: this.overlayClass,
+          customStyle: this.overlayStyle,
           nativeOn: {
             click: () => {
-              this.$emit("input", false);
+              this.$emit("click-overlay", this);
+              if(this.closeOnClickOverlay){
+                  this.$emit("input", false);
+              }              
             }
           }
         });
       } else {
-        this.maskInstant.show = true;
+        this.overlayInstant.show = true;
       }
+   
+     if (this.lockScroll && !this.locked) {
+             
+        document.body.classList.add('nut-overflow-hidden');
+        this.locked = true;
+     }
+     
+      this.$emit("open", this);
     },
     close() {
-      this.maskInstant.show = false;
-    }
+      this.overlayInstant.show = false;
+      if (this.lockScroll && this.locked) {                
+        document.body.classList.remove('nut-overflow-hidden');  
+        this.locked = false;      
+      }
+      this.$emit("close", this);
+    },
+    getElement(selector){   
+      if (typeof selector === "string") {
+        return document.querySelector(selector);
+      }
+      return selector();
+    },
+    portal() {
+        const { getContainer } = this;
+        const el = this.$el;
+
+        let container;
+        if (getContainer) {
+          container = this.getElement(getContainer);
+        } else if (this.$parent) {
+          container = this.$parent.$el;
+        }
+
+        if (container && container !== el.parentNode) {
+          container.appendChild(el);
+        }
+      }
   }
 };
 </script>