ソースを参照

chore: dialog

richard1015 4 年 前
コミット
4e2314a294

+ 1 - 1
src/config.json

@@ -293,7 +293,7 @@
           "cName": "对话框",
           "desc": "模态对话框,在浮层中显示,引导用户进行相关操作,支持图片对话框。",
           "sort": 8,
-          "show": false,
+          "show": true,
           "author": "dsj"
         },
         {

ファイルの差分が大きいため隠しています
+ 29 - 242
src/packages/dialog/demo.vue


+ 31 - 121
src/packages/dialog/doc.md

@@ -1,157 +1,67 @@
 # Dialog 对话框
 
-模态对话框,在浮层中显示,引导用户进行相关操作,支持图片对话框。
 
-## 基本用法
+### 介绍
 
-```javascript
-import Dialog from './index';  
-// 全局注册  
-const app = createApp();  
-app.use(Dialog);  
-```
-
-```javascript
-Dialog({
-  title: "确定删除此订单?",
-  content: "删除后将从你的记录里消失,无法找回"
-});
-```
-## 直接关闭当前dialog
-```javascript
-Dialog.closed()  //可以直接关闭当前dialog
-```
-
-## ID
-
-同一个页面中,id相同的Dialog的DOM只会同时存在一个,不指定id时,id的默认值为**nut-dialog-default-id**。
-
-```javascript
-Dialog({
-  id:'my-dialog',
-  title: "确定删除此订单?",
-  content: "删除后将从你的记录里消失,无法找回"
-});
-```
-> 如果希望同时弹出多个Dialog,请给不同的Dialog设置不同的id。
+模态对话框,在浮层中显示,引导用户进行相关操作,常用于消息提示、消息确认,或在当前页面内完成特定的交互操作。
 
-## 事件
-```javascript
-Dialog({
-        title: "自定义Dialog标题",
-        content: "小屏或移动端浏览效果最佳",
-        closeBtn:true,  //显式右上角关闭按钮
-        onOkBtn(event) {  //确定按钮点击事件
-          alert("okBtn");
-          this.close(); //关闭对话框
-        },
-        onCancelBtn(event) {  //取消按钮点击事件,默认行为关闭对话框
-          alert("cancelBtn");
-          //return false;  //阻止默认“关闭对话框”的行为
-        },
-        onCloseBtn(event) { //右上角关闭按钮点击事件
-          alert("closeBtn");
-          //return false;  //阻止默认“关闭对话框”的行为
-        },
-        closeCallback(target) {
-          alert("will close");  //对话框关闭回调函数,无论通过何种方式关闭都会触发
-        }
-});
-        
-```
-## 关闭dialog不销毁实例
-```javascript
- Dialog({
-        animation: false, //禁用弹出动效
-        title: "注册说明",
-        canDestroy:false,
-        content:
-          "原账号为您本人所有,建议直接登录或找回密码。原账号内的订单资产可能丢失,可联系京东客服找回。"
-      });
-        
-```
-## 页面滚动锁定
-
-**lockBgScroll** 值设为 **true** 时,可在弹窗出现时锁定页面滚动,且不影响窗体内部滚动。
+弹出框组件支持函数调用和组件调用两种方式。
 
+### 安装
+    
 ```javascript
-Dialog({
-        title: "背景滚动锁定",
-        lockBgScroll:true,
-        content:"弹窗弹出后,页面滚动锁止。在窗体和遮罩层上滑动时,页面不再跟随滚动。"
-});
-```
+import { createApp } from 'vue';
+import { Dialog } from '@nutui/nutui';
 
-## 图片弹窗
-
-**type** 值为 **image** 时为图片弹窗,需要配置一张图片,可带链接(非必须)。默认展示关闭按钮。点击图片触发 **onClickImageLink** 事件,返回**false**可阻止默认的跳转链接行为。
-
-```javascript
-Dialog({
-  type:"image", //设置弹窗类型为”图片弹窗“
-  link:"http://m.jd.com", //点击图片跳转的Url
-  imgSrc:"https://m.360buyimg.com/mobilecms/s750x750_jfs/t1/4875/23/1968/285655/5b9549eeE4997a18c/070eaf5bddf26be8.jpg", //图片Url
-  onClickImageLink:function(){ //图片点击事件,默认行为是跳转Url
-    console.log(this); //this指向该Dialog实例
-    return false;  //返回false可阻止默认的链接跳转行为
-  }
-});
+const app = createApp();
+app.use(Dialog);
 ```
-
 ## 标签式写法
 
 如果Dialog内容有复杂交互,可使用Dialog的标签式用法。注意标签使用的时候,属性不建议使用驼峰,推荐使用如下写法
 
 ```html
-<nut-dialog title="标签形式调用" :visible="dialogShow" @ok-btn-click="dialogShow=false" @cancel-btn-click="dialogShow=false" @close="dialogShow=false">
-    <a href="javascript:;" @click="dialogShow=false" :noCancelBtn="true">点我可以直接关闭对话框</a>
-</nut-dialog>
+<nut-dialog :title="title" :close-on-click-overlay="false" :content="content" v-model:visible="visible"></nut-dialog>
 ```
 
-```javascript
+``` javascript
+import { ref } from 'vue';
+
 export default {
-  data() {
-    return {
-      dialogShow: false
-    };
-  }
-}
+  setup() {
+    const visible = ref(true);
+    const title = '标签式使用';
+    const content = '内容';
+  
+    return { visible,title,content };
+  },
+};
 ```
 
-## prop
+## Props
 
 | 字段 | 说明 | 类型 | 默认值
 |----- | ----- | ----- | ----- 
-| id | 标识符,相同者共享一个实例 | String/Number | nut-dialog-default-id
-| canDestroy | 是否关闭弹窗时销毁实例 | Boolean | true
 | title | 标题 | String | -
 | content | 内容,支持HTML | String | -
-| type | 弹窗类型,值为**image**时为图片弹窗 | String | -
-| closeOnClickModal | 点击蒙层是否关闭对话框 | Boolean | true
+| close-on-click-overlay | 点击蒙层是否关闭对话框 | Boolean | true
 | noFooter | 是否隐藏底部按钮栏 | Boolean | false
 | noOkBtn | 是否隐藏确定按钮 | Boolean | false
 | noCancelBtn | 是否隐藏取消按钮 | Boolean | false
-| cancelBtnTxt | 取消按钮文案 | String | ”取 消“
-| okBtnTxt | 确定按钮文案 | String | ”确 定“
+| cancelText | 取消按钮文案 | String | ”取消“
+| okText | 确定按钮文案 | String | ”确 定“
 | okBtnDisabled | 禁用确定按钮 | Boolean | false
 | cancelAutoClose | 取消按钮是否默认关闭弹窗 | Boolean | true
 | textAlign | 文字对齐方向,可选值同css的text-align | String | "center"
-| maskBgStyle | 遮罩层样式(颜色、透明度) | String | -
-| customClass | 增加一个自定义class | String | -
-| link | 点击图片跳转的Url,仅对图片类型弹窗有效 | String | -
-| imgSrc | 图片Url,仅对图片类型弹窗有效 | String | -
-| animation | 是否开启默认动效 | Boolean | true
 | closeOnPopstate | 是否在页面回退时自动关闭 | Boolean | false
-| lockBgScroll | 锁定遮罩层滚动,不影响弹窗内部滚动(实验性质)会给body添加posotion:fix属性,注意 | Boolean | false
+| lock-scroll | 背景是否锁定 | Boolean | false
 
 
-## 事件
+## Events
 
 | 字段 | 说明 | 类型 | 默认值
 |----- | ----- | ----- | ----- 
-| onOkBtn | 确定按钮回调 | Function | -
-| onCancelBtn | 取消按钮回调 | Function | -
-| onCloseBtn | 关闭按钮回调 | Function | -
-| closeCallback | 关闭回调,任何情况关闭弹窗都会触发 | Function | -
-| onClickImageLink | 图片链接点击回调,仅对图片类型弹窗有效 | Function | -
-| closed | 关闭dialog | Function | -
+| ok | 确定按钮回调 | Function | -
+| cancel | 取消按钮回调 | Function | -
+| open | 关闭按钮回调 | Function | -
+| closed | 关闭回调,任何情况关闭弹窗都会触发 | Function | -

+ 40 - 183
src/packages/dialog/index.scss

@@ -1,190 +1,47 @@
-@import '../../styles/variables.scss';
-@import '../../styles/mixins/make-animation';
-@import '../../styles/mixins/text-ellipsis.scss';
-@import '../../styles/animation/fade';
-@import '../../styles/animation/ease';
-$mask-bg: rgba(0, 0, 0, 0.5) !default;
-$font-size-base: 14px !default;
-body.dialog-open {
-  position: fixed;
-}
-
-.nut-dialog-wrapper {
-  position: relative;
-  z-index: $zindex-mask;
-}
-
-.nut-dialog-box {
-  position: fixed;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  color: $text-color;
-}
-
-.nut-dialog-mask,
-.nut-dialog-box {
-  left: 0;
-  top: 0;
-  right: 0;
-  bottom: 0;
-}
-
-.nut-dialog-mask {
-  position: fixed;
-  background: $mask-bg;
-}
-
 .nut-dialog {
-  position: relative;
-  width: 86%;
-  max-height: 70vh;
-  background: #fff;
-  border-radius: 12px;
-  overflow: hidden;
   display: flex;
   flex-direction: column;
-}
-
-.nut-dialog-title {
-  display: block;
-  line-height: 1.5;
-  color: #262626;
-  font-size: 16px;
-  text-align: center;
-  flex-shrink: 0;
-  @include text-ellipsis;
-  padding-bottom: 11px;
-  // &:only-child {
-  //     padding-bottom: 0;
-  // }
-}
-
-.nut-dialog-close {
-  position: absolute;
-  right: 0;
-  top: 0;
-  width: 36px;
-  height: 46px;
-  font-size: 20px;
-  text-align: center;
-  text-decoration: none;
-  background: url('//img13.360buyimg.com/imagetools/jfs/t1/144349/40/19537/4004/5fe1ca9bE2daa4196/7afe4a2ac681804a.png')
-    no-repeat center;
-  background-size: 10px 10px;
-  img {
-    height: 10px;
-  }
-}
-
-.nut-dialog-image-wrapper {
-  position: relative;
-  .nut-dialog {
-    width: auto;
-    max-width: 80%;
-    max-height: 75%;
-    background: transparent;
-    border-radius: none;
-    display: inline-block;
-    overflow: visible;
-  }
-  .nut-dialog-close {
-    position: absolute;
-    left: 50%;
-    top: auto;
-    bottom: -48px;
-    width: 24px;
-    height: 24px;
-    margin-left: -12px;
-    background: url(./close.svg) no-repeat center;
-    background-size: 100%;
-  }
-}
-
-.nut-dialog-link {
-  display: inline-block;
-}
-
-.nut-dialog-image {
-  max-width: 100%;
-  max-height: 100%;
-  vertical-align: bottom;
-}
-
-.nut-dialog-body {
-  box-sizing: border-box;
-  padding: 30px 20px 20px;
-  display: flex;
-  flex-direction: column;
-  flex: 0 1 auto;
-  overflow: auto;
-}
-
-.nut-dialog-content {
-  flex: 1;
-  justify-content: center;
-  overflow: auto;
-  font-size: $font-size-base;
-  word-break: break-all;
-  padding-bottom: 10px;
-  -webkit-overflow-scrolling: touch;
-}
-
-.nut-dialog-footer {
-  height: 50px;
-  width: 100%;
-  line-height: 50px;
-  display: flex;
-  flex-shrink: 0;
-  overflow: hidden;
-  flex-direction: row;
-  justify-content: center;
-  padding: 0 30px;
-}
-
-.nut-dialog-btn {
-  display: block;
-  max-width: 104px;
-  height: 30px;
-  border-radius: 17px;
-  position: relative;
-  flex: 1;
-  font-size: $font-size-base;
-  border: none;
-  background: transparent;
-  appearance: none;
-  outline: none;
-  user-select: none;
-  margin: 0 10px;
-  &.nut-dialog-ok {
-    width: 128px;
-    color: #fff;
-    background: linear-gradient(
-      135deg,
-      #fa2c19 0%,
-      #fa3f19 45%,
-      #fa5919 83%,
-      #fa6419 100%
-    );
-  }
-
-  &.nut-dialog-cancel {
-    color: #fa2c19;
-    border: 1px solid #fa2c19;
+  align-items: center;
+  width: $dialog-width;
+  min-height: 156px;
+  padding: 28px 24px 16px 24px;
+
+  &__header {
+    display: block;
+    text-align: center;
+    height: 20px;
+    font-size: 16px;
+    color: rgba(38, 38, 38, 1);
+    @include oneline-ellipsis();
   }
-  &.disabled {
-    cursor: not-allowed;
-    opacity: 0.68;
+  &__content {
+    width: 100%;
+    overflow: auto;
+    flex: 1;
+    margin: 20px 0;
+    max-height: 268px;
+    line-height: 16px;
+    font-size: 12px;
+    color: $text-color;
+    word-wrap: break-word;
+    word-break: break-all;
+    white-space: pre-wrap;
   }
-  &:only-child {
-    max-width: 128px;
-    color: #fff;
-    background: linear-gradient(
-      135deg,
-      #fa2c19 0%,
-      #fa3f19 45%,
-      #fa5919 83%,
-      #fa6419 100%
-    );
+  &__footer {
+    display: flex;
+    align-items: center;
+    width: 100%;
+    justify-content: space-around;
+
+    .nut-button {
+      flex: 1;
+    }
+
+    &-cancel {
+      margin-right: 20px;
+    }
+    &-ok {
+      max-width: 128px;
+    }
   }
 }

+ 55 - 41
src/packages/dialog/index.ts

@@ -1,47 +1,61 @@
-import dialog from './index.vue';
-import { defineComponent, createVNode, render, toRef, watch } from 'vue';
-
-const confirmConstructor = defineComponent(dialog);
+import dialogInstance from './index.vue';
+import { render, createVNode, ref } from 'vue';
+export const show = ref(false);
+export class DialogOptions {
+  title: string = '';
+  content: string = '';
+  cancelText: string = '取消';
+  okText: string = '确定';
+  textAlign: string = 'center';
+  teleport: String | Element = 'body';
+
+  // function
+  private onUpdate: Function = (value: boolean) => {
+    show.value = value;
+  };
+  onOk: Function = () => {};
+  onCancel: Function = () => {};
+  onClose: Function = () => {};
+  onClosed: Function = () => {};
+
+  noFooter: boolean = false;
+  noOkBtn: boolean = false;
+  noCancelBtn: boolean = false;
+  okBtnDisabled: boolean = false;
+  closeOnPopstate: boolean = false;
+  lockScroll: boolean = false;
+}
+
+class Dialog {
+  options: DialogOptions = new DialogOptions();
+
+  constructor(_options: DialogOptions) {
+    Object.assign(this.options, _options);
+    show.value = true;
+    const instance: any = createVNode(dialogInstance, this.options as any);
+    render(instance, document.body);
+  }
 
-let instance: any;
-const Dialog = (options: any) => {
-  options = options ? options : {};
+  close = () => {
+    // if (instance) {
+    //   instance.component.ctx.close();
+    // }
+  };
 
-  options.id = options.id || 'nut-dialog-default-id';
-  options.visible = true;
-  if (options.type === 'image' && typeof options.closeBtn === 'undefined') {
-    options.closeBtn = true;
-  }
+  setDefaultOptions = (options: DialogOptions) => {
+    // Object.assign(this.currentOptions, options);
+  };
 
-  // 生成组件实例
-  instance = createVNode(confirmConstructor, options);
-
-  // 渲染挂载组件
-  const container = document.createElement('div');
-  render(instance, container);
-  const dialogDom = document.querySelector('#' + options.id);
-  if (options.id && dialogDom && dialogDom.parentNode) {
-    dialogDom.parentNode.replaceChild(instance.el, dialogDom);
-  } else {
-    document.body.appendChild(instance.el);
-  }
+  resetDefaultOptions = () => {
+    // Dialog.currentOptions = { ...Dialog.defaultOptions };
+  };
+}
 
-  // 初始化组件参数
-  const props = instance.component.props;
-  Object.keys(options).forEach(key => {
-    props[key] = options[key];
-  });
+const _Dialog = function(options: DialogOptions) {
+  return new Dialog(options);
 };
-Dialog.close = function() {
-  if (instance) {
-    instance.component.ctx.close();
-  }
-};
-
-Dialog.install = function(app: any) {
-  app.use(dialog);
-  app.config.globalProperties.$dialog = Dialog;
+_Dialog.install = (app: any) => {
+  app.use(dialogInstance);
+  app.config.globalProperties.$dialog = _Dialog;
 };
-
-Dialog.Component = dialog;
-export default Dialog;
+export default _Dialog;

+ 114 - 252
src/packages/dialog/index.vue

@@ -1,120 +1,73 @@
 <template>
-  <view :class="classes" @click="handleClick">
-    <div
-      v-if="destroy"
-      :class="[
-        'nut-dialog-wrapper',
-        customClass,
-        { 'nut-dialog-image-wrapper': type === 'image' }
-      ]"
-      :id="id"
-    >
-      <transition :name="animation ? 'nutFade' : ''">
-        <div
-          :class="'nut-dialog-mask'"
-          :style="{ background: maskBgStyle }"
-          @click="modalClick"
-          v-show="curVisible"
-        >
-        </div>
-      </transition>
-      <transition :name="animation ? 'nutEase' : ''">
-        <div class="nut-dialog-box" v-show="curVisible" @click="modalClick">
-          <div class="nut-dialog" @click.stop>
-            <a
-              href="javascript:;"
-              v-if="closeBtn"
-              @click="closeBtnClick"
-              class="nut-dialog-close"
-            ></a>
-            <template v-if="type === 'image'">
-              <a
-                href="javascript:;"
-                @click="imageLinkClick"
-                class="nut-dialog-link"
-              >
-                <img :src="imgSrc" class="nut-dialog-image" alt />
-              </a>
-            </template>
-            <template v-else>
-              <div class="nut-dialog-body">
-                <span
-                  class="nut-dialog-title"
-                  v-html="title"
-                  v-if="title"
-                ></span>
-                <div
-                  class="nut-dialog-content"
-                  v-if="isShowContent"
-                  :style="{ textAlign }"
-                >
-                  <slot></slot>
-                </div>
-                <div
-                  class="nut-dialog-content"
-                  v-html="content"
-                  v-else-if="content"
-                  :style="{ textAlign }"
-                ></div>
-              </div>
-              <div class="nut-dialog-footer" v-if="!noFooter">
-                <button
-                  class="nut-dialog-btn nut-dialog-cancel"
-                  v-if="!noCancelBtn"
-                  @click="cancelBtnClick(cancelAutoClose)"
-                  >{{ cancelBtnTxt }}</button
-                >
-                <button
-                  class="nut-dialog-btn nut-dialog-ok"
-                  v-if="!noOkBtn"
-                  :class="{ disabled: okBtnDisabled }"
-                  :disabled="okBtnDisabled"
-                  @click="okBtnClick"
-                  >{{ okBtnTxt }}</button
-                >
-              </div>
-            </template>
-          </div>
-        </div>
-      </transition>
-    </div>
-  </view>
+  <nut-popup
+    name="pop"
+    :teleport="teleport"
+    v-model:visible="showPopup"
+    :close-on-click-overlay="closeOnClickOverlay"
+    :lock-scroll="lockScroll"
+    round
+    @click-overlay="closed"
+    @click-close-icon="closed"
+  >
+    <view :class="classes">
+      <view v-if="title" class="nut-dialog__header">
+        <slot v-if="$slots.header" name="header"></slot>
+        <template v-else>{{ title }}</template>
+      </view>
+
+      <view class="nut-dialog__content" :style="{ textAlign }">
+        <slot v-if="$slots.default" name="default"></slot>
+        <template v-else>{{ content }}</template>
+      </view>
+
+      <view class="nut-dialog__footer" v-if="!noFooter">
+        <slot v-if="$slots.footer" name="footer"></slot>
+        <template v-else>
+          <nut-button
+            size="small"
+            plain
+            type="primary"
+            class="nut-dialog__footer-cancel"
+            v-if="!noCancelBtn"
+            @click="onCancel"
+          >
+            {{ cancelText }}
+          </nut-button>
+          <nut-button
+            v-if="!noOkBtn"
+            size="small"
+            type="primary"
+            class="nut-dialog__footer-ok"
+            :class="{ disabled: okBtnDisabled }"
+            :disabled="okBtnDisabled"
+            @click="onOk"
+          >
+            {{ okText }}
+          </nut-button>
+        </template>
+      </view>
+    </view>
+  </nut-popup>
 </template>
 <script lang="ts">
-import { ref, onMounted, watch, watchEffect, computed } from 'vue';
+import { onMounted, computed, watch, onUnmounted, ref, toRefs } from 'vue';
 import { createComponent } from '@/utils/create';
 const { componentName, create } = createComponent('dialog');
-
-const lockMaskScroll = (bodyCls => {
-  let scrollTop = 0;
-  return {
-    afterOpen: function() {
-      scrollTop =
-        (document.scrollingElement && document.scrollingElement.scrollTop) ||
-        document.body.scrollTop;
-      document.body.classList.add(bodyCls);
-      document.body.style.top = -scrollTop + 'px';
-    },
-    beforeClose: function() {
-      if (document.body.classList.contains(bodyCls)) {
-        document.body.classList.remove(bodyCls);
-        if (document.scrollingElement) {
-          document.scrollingElement.scrollTop = scrollTop;
-        }
-      }
-    }
-  };
-})('dialog-open');
+import { Button, Popup } from '@/nutui';
+import { show } from './index';
 export default create({
+  inheritAttrs: false,
+  children: [Popup, Button],
+  components: {
+    'nut-popup': Popup,
+    'nut-button': Button
+  },
   props: {
+    ...Popup.popupProps,
     visible: {
       type: Boolean,
       default: false
     },
-    id: {
-      type: String,
-      default: ''
-    },
     title: {
       type: String,
       default: ''
@@ -123,35 +76,6 @@ export default create({
       type: String,
       default: ''
     },
-    type: {
-      type: String,
-      default: ''
-    },
-    link: {
-      type: String,
-      default: ''
-    },
-    imgSrc: {
-      type: String,
-      default: ''
-    },
-    animation: {
-      type: Boolean,
-      default: true
-    },
-    lockBgScroll: {
-      type: Boolean,
-      default: false
-    },
-
-    closeBtn: {
-      type: Boolean,
-      default: false
-    },
-    closeOnClickModal: {
-      type: Boolean,
-      default: true
-    },
     noFooter: {
       type: Boolean,
       default: false
@@ -164,11 +88,11 @@ export default create({
       type: Boolean,
       default: false
     },
-    cancelBtnTxt: {
+    cancelText: {
       type: String,
       default: '取消'
     },
-    okBtnTxt: {
+    okText: {
       type: String,
       default: '确定'
     },
@@ -184,136 +108,57 @@ export default create({
       type: String,
       default: 'center'
     },
-    onOkBtn: {
-      type: Function,
-      default: null
-    },
-    onCloseBtn: {
+    onOk: {
       type: Function,
       default: null
     },
-    onCancelBtn: {
+    onCancel: {
       type: Function,
       default: null
     },
-    closeCallback: {
+    onClose: {
       type: Function,
       default: null
     },
-    onClickImageLink: {
+    onClosed: {
       type: Function,
       default: null
     },
-    maskBgStyle: {
-      type: String,
-      default: ''
-    },
-    canDestroy: {
-      type: Boolean,
-      default: true
-    },
-    customClass: {
-      type: String,
-      default: ''
-    },
     closeOnPopstate: {
       type: Boolean,
       default: false
     }
   },
-  // emits: ['click'],
+  emits: [
+    'update',
+    'update:visible',
+    'ok',
+    'cancel',
+    'open',
+    'opened',
+    'close',
+    'closed'
+  ],
+  setup(props, { emit }) {
+    const showPopup = ref(false);
+    showPopup.value = show.value;
 
-  setup(props, { emit, slots }) {
-    const curVisible = ref(false);
-    let destroy = ref(true);
-    onMounted(() => {
-      curVisible.value = props.visible;
-    });
-    const isShowContent = computed(() => {
-      return slots.default;
-    });
-
-    const todestroy = () => {
-      if (!props.canDestroy) {
-        destroy = ref(false);
-      }
-    };
-    const close = (target?: string) => {
-      emit('close', target);
-      emit('close-callback', target);
-      todestroy();
-      if (
-        typeof props.closeCallback === 'function' &&
-        props.closeCallback(target) === false
-      ) {
-        return;
-      }
-      curVisible.value = false;
-    };
-    const modalClick = () => {
-      if (!props.closeOnClickModal) {
-        return;
-      }
-      close('modal');
-    };
-    const okBtnClick = () => {
-      emit('ok-btn-click');
-      if (typeof props.onOkBtn === 'function') {
-        props.onOkBtn.call(props);
-      }
-    };
-    const cancelBtnClick = (autoClose: boolean) => {
-      emit('cancel-btn-click');
-      if (!autoClose) {
-        return;
-      }
-      if (typeof props.onCancelBtn === 'function') {
-        if (props.onCancelBtn.call(props) === false) {
-          return;
-        }
-      }
-      close('cancelBtn');
-    };
-    const closeBtnClick = () => {
-      if (typeof props.onCloseBtn === 'function') {
-        if (props.onCloseBtn.call(props) === false) {
-          return;
-        }
-      }
-      close('closeBtn');
-    };
-    //图片类型弹窗中的链接点击事件,默认跳转
-    const imageLinkClick = () => {
-      if (
-        props.onClickImageLink &&
-        props.onClickImageLink.call(props) === false
-      ) {
-        return;
-      }
-      if (props.link) {
-        location.href = props.link;
-      }
-    };
-    const handleClick = (event: Event) => {
-      emit('click', event);
-    };
     onMounted(() => {
       if (props.closeOnPopstate) {
         window.addEventListener('popstate', function() {
-          close();
+          closed();
         });
       }
     });
-    watchEffect(() => {
-      if (props.lockBgScroll) {
-        //锁定or解锁页面滚动
-        lockMaskScroll[curVisible.value ? 'afterOpen' : 'beforeClose']();
-      }
+
+    watch(show, value => {
+      showPopup.value = value;
     });
+
     watch(
       () => props.visible,
-      val => {
-        curVisible.value = val;
+      value => {
+        showPopup.value = value;
       }
     );
 
@@ -322,19 +167,36 @@ export default create({
         [componentName]: true
       };
     });
+
+    const update = (val: boolean) => {
+      emit('update', val);
+      emit('update:visible', val);
+    };
+
+    const closed = () => {
+      update(false);
+      emit('closed');
+    };
+
+    const onCancel = () => {
+      emit('cancel');
+      if (props.cancelAutoClose) {
+        closed();
+      }
+    };
+
+    const onOk = () => {
+      closed();
+      emit('ok');
+    };
+
     return {
-      handleClick,
-      curVisible,
-      destroy,
-      modalClick,
-      close,
-      todestroy,
-      okBtnClick,
-      cancelBtnClick,
-      closeBtnClick,
-      imageLinkClick,
-      isShowContent,
-      classes
+      closed,
+      classes,
+      onCancel,
+      onOk,
+      show,
+      showPopup
     };
   }
 });

+ 5 - 0
src/packages/popup/index.vue

@@ -7,6 +7,7 @@
       :class="overlayClass"
       :style="overlayStyle"
       :z-index="zIndex"
+      :lock-scroll="lockScroll"
       :duration="duration"
       @click="onClickOverlay"
     />
@@ -53,6 +54,7 @@ import { useLockScroll } from './use-lock-scroll';
 import { overlayProps } from './../overlay/index.vue';
 import overlay from '@/packages/overlay/index.vue';
 import { createComponent } from '@/utils/create';
+import { OverLay } from '@/nutui';
 const { componentName, create } = createComponent('popup');
 
 let _zIndex = 2000;
@@ -112,6 +114,9 @@ export const popupProps = {
 };
 export default create({
   children: [overlay],
+  components: {
+    'nut-overlay': OverLay
+  },
   props: {
     ...popupProps
   },

+ 7 - 14
src/styles/variables.scss

@@ -22,6 +22,10 @@ $padding-xs: 12px;
 $font-family: PingFang SC, Microsoft YaHei, Helvetica, Hiragino Sans GB, SimSun,
   sans-serif !default;
 
+// ---- Animation ----
+$animation-duration: 0.25s !default;
+$animation-timing-fun: cubic-bezier(0.55, 0.085, 0.68, 0.53) !default;
+
 // Font
 $font-size-0: 10px;
 $font-size-1: 12px;
@@ -170,20 +174,6 @@ $overlay-bg-color: rgba(0, 0, 0, 0.7);
 //popup
 $popup-close-icon-margin: 16px;
 $popup-border-radius: 20px;
-// ---- Animation ----
-$animation-duration: 0.25s !default;
-$transition-duration: 0.2s !default;
-$transition-duration-fast: 0.2s !default;
-$transition-duration-slow: 0.4s !default;
-$animation-timing-fun: cubic-bezier(0.55, 0.085, 0.68, 0.53) !default;
-$ease-in-out: cubic-bezier(0.445, 0.05, 0.55, 0.95);
-$ease-out: cubic-bezier(0.895, 0.03, 0.685, 0.22);
-
-// ---- z-index ----
-$zindex-mask: 9998 !default;
-$zindex-actionsheet: 10001 !default;
-$zindex-dialog: 10000 !default;
-$zindex-picker: 10050 !default;
 
 // Notify
 $notify-text-color: $white;
@@ -246,6 +236,9 @@ $address-region-tab-line: linear-gradient(
   $primary-color-end 100%
 );
 
+// dialog
+$dialog-width: 296px;
+
 view-block {
   display: block;
 }