提交 4e2314a2 编写于 作者: richard_1015's avatar richard_1015

chore: dialog

上级 aec2f726
...@@ -293,7 +293,7 @@ ...@@ -293,7 +293,7 @@
"cName": "对话框", "cName": "对话框",
"desc": "模态对话框,在浮层中显示,引导用户进行相关操作,支持图片对话框。", "desc": "模态对话框,在浮层中显示,引导用户进行相关操作,支持图片对话框。",
"sort": 8, "sort": 8,
"show": false, "show": true,
"author": "dsj" "author": "dsj"
}, },
{ {
......
<template> <template>
<div class="demo"> <div class="demo">
<h2>基本用法</h2> <!-- <nut-cell title="基础弹框" @click="baseClick"></nut-cell> -->
<div> <nut-cell title="标签弹框" @click="noTitleClick"></nut-cell>
<nut-cell
:is-link="true"
:show-icon="true"
@click="showDialog1"
title="自定义标题和内容"
>
</nut-cell>
<nut-cell
:is-link="true"
:show-icon="true"
@click="showDialog2"
title="只有标题"
>
</nut-cell>
<nut-cell
:is-link="true"
:show-icon="true"
@click="showDialog3"
title="只有内容"
>
</nut-cell>
<nut-cell
:is-link="true"
:show-icon="true"
@click="showDialog4"
title="移除按钮栏"
>
</nut-cell>
<nut-cell
:is-link="true"
:show-icon="true"
@click="showDialog5"
title="事件"
>
</nut-cell>
<nut-cell
:is-link="true"
:show-icon="true"
@click="showDialog6"
title="无弹出动效且关闭时不销毁dislog实例"
>
</nut-cell>
<nut-cell
:is-link="true"
:show-icon="true"
@click="showDialog7"
title="遮罩层透明"
>
</nut-cell>
</div>
<h2>图片弹窗</h2>
<p>
type值为“image”时为图片弹窗,需要配置一张图片,可带链接(非必须)。默认展示关闭按钮。点击图片触发onClickImageLink事件,返回false可阻止默认的跳转链接行为。
</p>
<div>
<nut-cell
:is-link="true"
:show-icon="true"
@click="showImageDialog"
title="图片弹窗"
>
</nut-cell>
</div>
<h2>背景滚动锁定</h2>
<p
>lockBgScroll值设为true时,可在弹窗出现时锁定页面滚动,且不影响窗体内部滚动。</p
>
<div>
<nut-cell
:is-link="true"
:show-icon="true"
@click="showDialog8"
title="背景滚动锁定"
>
</nut-cell>
<nut-cell
:is-link="true"
:show-icon="true"
@click="showDialog9"
title="窗体内部滚动不影响页面滚动"
>
</nut-cell>
</div>
<h2>高级用法</h2>
<p>如果Dialog内容有复杂交互,可使用Dialog的标签式用法。</p>
<div>
<nut-cell
:is-link="true"
:show-icon="true"
@click="dialogShow = true"
title="以标签形式调用Dialog"
>
</nut-cell>
</div>
<!-- 以标签形式调用Dialog -->
<nut-dialog <nut-dialog
title="标签形式调用" title="标签式使用"
:visible="dialogShow" :close-on-click-overlay="false"
:cancel-auto-close="false" :content="content"
@ok-btn-click="dialogShow = false" v-model:visible="visible"
@cancel-btn-click="dialogShow = false"
@close="dialogShow = false"
> >
<a href="javascript:;" @click="dialogShow = false" :noCancelBtn="true"
>点我可以直接关闭对话框</a
>
</nut-dialog> </nut-dialog>
<!-- <template v-slot:header>
</template> -->
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { reactive, toRefs, createApp } from 'vue'; import { ref, getCurrentInstance } from 'vue';
import { createComponent } from '@/utils/create'; import { createComponent } from '@/utils/create';
import Dialog from './index';
// 全局注册
const app = createApp({});
app.use(Dialog);
const { createDemo } = createComponent('dialog'); const { createDemo } = createComponent('dialog');
export default createDemo({ export default createDemo({
props: {}, props: {},
setup() { setup() {
const data = reactive({ const { proxy } = getCurrentInstance();
dialogShow: false, const content = ref(
isEditor: false, '模态对话框,在浮层中显示,引导用户进行相关操作,支持图片对话框。'
item: {} );
}); const visible = ref(false);
function showDialog1() { const baseClick = () => {
const options = { proxy.$dialog({
title: '确定删除此订单?', title: '基础弹框',
content: '删除后将从你的记录里消失,无法找回', content: '基础弹框内容'
closeOnPopstate: true
};
Dialog(options);
}
function showDialog2() {
const options = {
title: '确定要加入购物车吗?'
};
Dialog(options);
}
function showDialog3() {
const options = {
content: '点击返回将中断注册,确定返回?<br>删除后您可以在回收站还原。',
closeBtn: true,
noOkBtn: true,
cancelBtnTxt: '我知道了'
};
Dialog(options);
}
function showDialog4() {
const options = {
customClass: 'my-dialog',
title: '注册说明',
content:
'原账号为您本人所有,建议直接登录或找回密码。原账号内的订单资产可能丢失,可联系京东客服找回。',
closeBtn: true,
noFooter: true
};
Dialog(options);
}
function showDialog5() {
const options = {
okBtnTxt: '好 的',
title: '事件',
content: '点击按钮触发事件',
closeBtn: true,
onOkBtn(event) {
alert('okBtn');
Dialog.close(); //关闭对话框
},
onCancelBtn(event) {
alert('cancelBtn');
//return false; //阻止默认“关闭对话框”的行为
},
onCloseBtn(event) {
alert('closeBtn');
//return false; //阻止默认“关闭对话框”的行为
},
closeCallback(target) {
alert('will close');
}
};
Dialog(options);
}
function showDialog6() {
Dialog({
animation: false, //禁用弹出动效
title: '注册说明',
canDestroy: false,
content:
'原账号为您本人所有,建议直接登录或找回密码。原账号内的订单资产可能丢失,可联系京东客服找回。'
});
}
function showDialog7() {
Dialog({
maskBgStyle: 'rgba(0,0,0,0)', //设置遮罩层背景透明
title: '注册说明',
content:
'原账号为您本人所有,建议直接登录或找回密码。原账号内的订单资产可能丢失,可联系京东客服找回。'
});
}
function showDialog8() {
Dialog({
title: '背景滚动锁定',
lockBgScroll: true,
content:
'弹窗弹出后,页面滚动锁止。在窗体和遮罩层上滑动时,页面不再跟随滚动。',
noOkBtn: true,
cancelBtnTxt: '我知道了',
onCancelBtn() {
Dialog.close();
}
});
}
function showDialog9() {
Dialog({
title: '《桃花行》',
lockBgScroll: true,
content:
'桃花帘外东风软,<br>桃花帘内晨妆懒。<br>帘外桃花帘内人,<br>人与桃花隔不远。<br>东风有意揭帘栊,<br>花欲窥人帘不卷。<br>桃花帘外开仍旧,<br>帘中人比桃花瘦。<br>花解怜人花也愁,<br>隔帘消息风吹透。<br>风透帘栊花满庭,<br>庭前春色倍伤情。<br>闲苔院落帘空卷,<br>斜日栏干人自凭。<br>凭栏人向东风泣,<br>茜裙偷傍桃花立。<br>桃花桃叶乱纷纷,<br>花绽新红叶凝碧。<br>树树烟封一万株,<br>烘照楼台红模糊。<br>天机烧破鸳鸯锦,<br>春色欲酣珊瑚枕。<br>侍女金盆进水来,<br>香泉欲蘸胭脂冷。<br>胭脂鲜艳何相类,<br>花之颜色人之泪。<br>若将人泪比桃花,<br>泪自长流花自媚。<br>泪眼看花泪易乾,<br>泪乾春尽花憔悴。<br>憔悴花枝憔悴人,<br>花飞人倦易黄昏。<br>一声杜宇春归尽,<br>寂寞帘栊空月痕。',
noOkBtn: true,
cancelBtnTxt: '我知道了'
}); });
} };
function showImageDialog() { const noTitleClick = () => {
Dialog({ visible.value = true;
type: 'image', };
link: 'http://m.jd.com',
imgSrc:
'https://m.360buyimg.com/mobilecms/s750x750_jfs/t1/4875/23/1968/285655/5b9549eeE4997a18c/070eaf5bddf26be8.jpg!q80.dpg',
onClickImageLink() {
return false; //返回false可阻止默认的链接跳转行为
}
});
}
return { return {
...toRefs(data), content,
showDialog1, visible,
showDialog2, baseClick,
showDialog3, noTitleClick
showDialog4,
showDialog5,
showDialog6,
showDialog7,
showDialog8,
showDialog9,
showImageDialog
}; };
} }
}); });
......
# Dialog 对话框 # 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 ```javascript
Dialog({ import { createApp } from 'vue';
title: "背景滚动锁定", import { Dialog } from '@nutui/nutui';
lockBgScroll:true,
content:"弹窗弹出后,页面滚动锁止。在窗体和遮罩层上滑动时,页面不再跟随滚动。"
});
```
## 图片弹窗 const app = createApp();
app.use(Dialog);
**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可阻止默认的链接跳转行为
}
});
``` ```
## 标签式写法 ## 标签式写法
如果Dialog内容有复杂交互,可使用Dialog的标签式用法。注意标签使用的时候,属性不建议使用驼峰,推荐使用如下写法 如果Dialog内容有复杂交互,可使用Dialog的标签式用法。注意标签使用的时候,属性不建议使用驼峰,推荐使用如下写法
```html ```html
<nut-dialog title="标签形式调用" :visible="dialogShow" @ok-btn-click="dialogShow=false" @cancel-btn-click="dialogShow=false" @close="dialogShow=false"> <nut-dialog :title="title" :close-on-click-overlay="false" :content="content" v-model:visible="visible"></nut-dialog>
<a href="javascript:;" @click="dialogShow=false" :noCancelBtn="true">点我可以直接关闭对话框</a>
</nut-dialog>
``` ```
```javascript ``` javascript
import { ref } from 'vue';
export default { export default {
data() { setup() {
return { const visible = ref(true);
dialogShow: false const title = '标签式使用';
}; const content = '内容';
}
} return { visible,title,content };
},
};
``` ```
## prop ## Props
| 字段 | 说明 | 类型 | 默认值 | 字段 | 说明 | 类型 | 默认值
|----- | ----- | ----- | ----- |----- | ----- | ----- | -----
| id | 标识符,相同者共享一个实例 | String/Number | nut-dialog-default-id
| canDestroy | 是否关闭弹窗时销毁实例 | Boolean | true
| title | 标题 | String | - | title | 标题 | String | -
| content | 内容,支持HTML | String | - | content | 内容,支持HTML | String | -
| type | 弹窗类型,值为**image**时为图片弹窗 | String | - | close-on-click-overlay | 点击蒙层是否关闭对话框 | Boolean | true
| closeOnClickModal | 点击蒙层是否关闭对话框 | Boolean | true
| noFooter | 是否隐藏底部按钮栏 | Boolean | false | noFooter | 是否隐藏底部按钮栏 | Boolean | false
| noOkBtn | 是否隐藏确定按钮 | Boolean | false | noOkBtn | 是否隐藏确定按钮 | Boolean | false
| noCancelBtn | 是否隐藏取消按钮 | Boolean | false | noCancelBtn | 是否隐藏取消按钮 | Boolean | false
| cancelBtnTxt | 取消按钮文案 | String | ”取 消“ | cancelText | 取消按钮文案 | String | ”取消“
| okBtnTxt | 确定按钮文案 | String | ”确 定“ | okText | 确定按钮文案 | String | ”确 定“
| okBtnDisabled | 禁用确定按钮 | Boolean | false | okBtnDisabled | 禁用确定按钮 | Boolean | false
| cancelAutoClose | 取消按钮是否默认关闭弹窗 | Boolean | true | cancelAutoClose | 取消按钮是否默认关闭弹窗 | Boolean | true
| textAlign | 文字对齐方向,可选值同css的text-align | String | "center" | textAlign | 文字对齐方向,可选值同css的text-align | String | "center"
| maskBgStyle | 遮罩层样式(颜色、透明度) | String | -
| customClass | 增加一个自定义class | String | -
| link | 点击图片跳转的Url,仅对图片类型弹窗有效 | String | -
| imgSrc | 图片Url,仅对图片类型弹窗有效 | String | -
| animation | 是否开启默认动效 | Boolean | true
| closeOnPopstate | 是否在页面回退时自动关闭 | Boolean | false | closeOnPopstate | 是否在页面回退时自动关闭 | Boolean | false
| lockBgScroll | 锁定遮罩层滚动,不影响弹窗内部滚动(实验性质)会给body添加posotion:fix属性,注意 | Boolean | false | lock-scroll | 背景是否锁定 | Boolean | false
## 事件 ## Events
| 字段 | 说明 | 类型 | 默认值 | 字段 | 说明 | 类型 | 默认值
|----- | ----- | ----- | ----- |----- | ----- | ----- | -----
| onOkBtn | 确定按钮回调 | Function | - | ok | 确定按钮回调 | Function | -
| onCancelBtn | 取消按钮回调 | Function | - | cancel | 取消按钮回调 | Function | -
| onCloseBtn | 关闭按钮回调 | Function | - | open | 关闭按钮回调 | Function | -
| closeCallback | 关闭回调,任何情况关闭弹窗都会触发 | Function | - | closed | 关闭回调,任何情况关闭弹窗都会触发 | Function | -
| onClickImageLink | 图片链接点击回调,仅对图片类型弹窗有效 | Function | - \ No newline at end of file
| closed | 关闭dialog | Function | -
\ No newline at end of file
@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 { .nut-dialog {
position: relative;
width: 86%;
max-height: 70vh;
background: #fff;
border-radius: 12px;
overflow: hidden;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} align-items: center;
width: $dialog-width;
.nut-dialog-title { min-height: 156px;
display: block; padding: 28px 24px 16px 24px;
line-height: 1.5;
color: #262626; &__header {
font-size: 16px; display: block;
text-align: center; text-align: center;
flex-shrink: 0; height: 20px;
@include text-ellipsis; font-size: 16px;
padding-bottom: 11px; color: rgba(38, 38, 38, 1);
// &:only-child { @include oneline-ellipsis();
// 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;
} }
&.disabled { &__content {
cursor: not-allowed; width: 100%;
opacity: 0.68; 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 { &__footer {
max-width: 128px; display: flex;
color: #fff; align-items: center;
background: linear-gradient( width: 100%;
135deg, justify-content: space-around;
#fa2c19 0%,
#fa3f19 45%, .nut-button {
#fa5919 83%, flex: 1;
#fa6419 100% }
);
&-cancel {
margin-right: 20px;
}
&-ok {
max-width: 128px;
}
} }
} }
import dialog from './index.vue'; import dialogInstance from './index.vue';
import { defineComponent, createVNode, render, toRef, watch } from 'vue'; import { render, createVNode, ref } from 'vue';
export const show = ref(false);
const confirmConstructor = defineComponent(dialog); 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; close = () => {
const Dialog = (options: any) => { // if (instance) {
options = options ? options : {}; // instance.component.ctx.close();
// }
};
options.id = options.id || 'nut-dialog-default-id'; setDefaultOptions = (options: DialogOptions) => {
options.visible = true; // Object.assign(this.currentOptions, options);
if (options.type === 'image' && typeof options.closeBtn === 'undefined') { };
options.closeBtn = true;
}
// 生成组件实例 resetDefaultOptions = () => {
instance = createVNode(confirmConstructor, options); // Dialog.currentOptions = { ...Dialog.defaultOptions };
};
// 渲染挂载组件 }
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);
}
// 初始化组件参数 const _Dialog = function(options: DialogOptions) {
const props = instance.component.props; return new Dialog(options);
Object.keys(options).forEach(key => {
props[key] = options[key];
});
}; };
Dialog.close = function() { _Dialog.install = (app: any) => {
if (instance) { app.use(dialogInstance);
instance.component.ctx.close(); app.config.globalProperties.$dialog = _Dialog;
}
};
Dialog.install = function(app: any) {
app.use(dialog);
app.config.globalProperties.$dialog = Dialog;
}; };
export default _Dialog;
Dialog.Component = dialog;
export default Dialog;
<template> <template>
<view :class="classes" @click="handleClick"> <nut-popup
<div name="pop"
v-if="destroy" :teleport="teleport"
:class="[ v-model:visible="showPopup"
'nut-dialog-wrapper', :close-on-click-overlay="closeOnClickOverlay"
customClass, :lock-scroll="lockScroll"
{ 'nut-dialog-image-wrapper': type === 'image' } round
]" @click-overlay="closed"
:id="id" @click-close-icon="closed"
> >
<transition :name="animation ? 'nutFade' : ''"> <view :class="classes">
<div <view v-if="title" class="nut-dialog__header">
:class="'nut-dialog-mask'" <slot v-if="$slots.header" name="header"></slot>
:style="{ background: maskBgStyle }" <template v-else>{{ title }}</template>
@click="modalClick" </view>
v-show="curVisible"
> <view class="nut-dialog__content" :style="{ textAlign }">
</div> <slot v-if="$slots.default" name="default"></slot>
</transition> <template v-else>{{ content }}</template>
<transition :name="animation ? 'nutEase' : ''"> </view>
<div class="nut-dialog-box" v-show="curVisible" @click="modalClick">
<div class="nut-dialog" @click.stop> <view class="nut-dialog__footer" v-if="!noFooter">
<a <slot v-if="$slots.footer" name="footer"></slot>
href="javascript:;" <template v-else>
v-if="closeBtn" <nut-button
@click="closeBtnClick" size="small"
class="nut-dialog-close" plain
></a> type="primary"
<template v-if="type === 'image'"> class="nut-dialog__footer-cancel"
<a v-if="!noCancelBtn"
href="javascript:;" @click="onCancel"
@click="imageLinkClick" >
class="nut-dialog-link" {{ cancelText }}
> </nut-button>
<img :src="imgSrc" class="nut-dialog-image" alt /> <nut-button
</a> v-if="!noOkBtn"
</template> size="small"
<template v-else> type="primary"
<div class="nut-dialog-body"> class="nut-dialog__footer-ok"
<span :class="{ disabled: okBtnDisabled }"
class="nut-dialog-title" :disabled="okBtnDisabled"
v-html="title" @click="onOk"
v-if="title" >
></span> {{ okText }}
<div </nut-button>
class="nut-dialog-content" </template>
v-if="isShowContent" </view>
:style="{ textAlign }" </view>
> </nut-popup>
<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>
</template> </template>
<script lang="ts"> <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'; import { createComponent } from '@/utils/create';
const { componentName, create } = createComponent('dialog'); const { componentName, create } = createComponent('dialog');
import { Button, Popup } from '@/nutui';
const lockMaskScroll = (bodyCls => { import { show } from './index';
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');
export default create({ export default create({
inheritAttrs: false,
children: [Popup, Button],
components: {
'nut-popup': Popup,
'nut-button': Button
},
props: { props: {
...Popup.popupProps,
visible: { visible: {
type: Boolean, type: Boolean,
default: false default: false
}, },
id: {
type: String,
default: ''
},
title: { title: {
type: String, type: String,
default: '' default: ''
...@@ -123,35 +76,6 @@ export default create({ ...@@ -123,35 +76,6 @@ export default create({
type: String, type: String,
default: '' 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: { noFooter: {
type: Boolean, type: Boolean,
default: false default: false
...@@ -164,11 +88,11 @@ export default create({ ...@@ -164,11 +88,11 @@ export default create({
type: Boolean, type: Boolean,
default: false default: false
}, },
cancelBtnTxt: { cancelText: {
type: String, type: String,
default: '取消' default: '取消'
}, },
okBtnTxt: { okText: {
type: String, type: String,
default: '确定' default: '确定'
}, },
...@@ -184,136 +108,57 @@ export default create({ ...@@ -184,136 +108,57 @@ export default create({
type: String, type: String,
default: 'center' default: 'center'
}, },
onOkBtn: { onOk: {
type: Function,
default: null
},
onCloseBtn: {
type: Function, type: Function,
default: null default: null
}, },
onCancelBtn: { onCancel: {
type: Function, type: Function,
default: null default: null
}, },
closeCallback: { onClose: {
type: Function, type: Function,
default: null default: null
}, },
onClickImageLink: { onClosed: {
type: Function, type: Function,
default: null default: null
}, },
maskBgStyle: {
type: String,
default: ''
},
canDestroy: {
type: Boolean,
default: true
},
customClass: {
type: String,
default: ''
},
closeOnPopstate: { closeOnPopstate: {
type: Boolean, type: Boolean,
default: false 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(() => { onMounted(() => {
if (props.closeOnPopstate) { if (props.closeOnPopstate) {
window.addEventListener('popstate', function() { window.addEventListener('popstate', function() {
close(); closed();
}); });
} }
}); });
watchEffect(() => {
if (props.lockBgScroll) { watch(show, value => {
//锁定or解锁页面滚动 showPopup.value = value;
lockMaskScroll[curVisible.value ? 'afterOpen' : 'beforeClose']();
}
}); });
watch( watch(
() => props.visible, () => props.visible,
val => { value => {
curVisible.value = val; showPopup.value = value;
} }
); );
...@@ -322,19 +167,36 @@ export default create({ ...@@ -322,19 +167,36 @@ export default create({
[componentName]: true [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 { return {
handleClick, closed,
curVisible, classes,
destroy, onCancel,
modalClick, onOk,
close, show,
todestroy, showPopup
okBtnClick,
cancelBtnClick,
closeBtnClick,
imageLinkClick,
isShowContent,
classes
}; };
} }
}); });
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
:class="overlayClass" :class="overlayClass"
:style="overlayStyle" :style="overlayStyle"
:z-index="zIndex" :z-index="zIndex"
:lock-scroll="lockScroll"
:duration="duration" :duration="duration"
@click="onClickOverlay" @click="onClickOverlay"
/> />
...@@ -53,6 +54,7 @@ import { useLockScroll } from './use-lock-scroll'; ...@@ -53,6 +54,7 @@ import { useLockScroll } from './use-lock-scroll';
import { overlayProps } from './../overlay/index.vue'; import { overlayProps } from './../overlay/index.vue';
import overlay from '@/packages/overlay/index.vue'; import overlay from '@/packages/overlay/index.vue';
import { createComponent } from '@/utils/create'; import { createComponent } from '@/utils/create';
import { OverLay } from '@/nutui';
const { componentName, create } = createComponent('popup'); const { componentName, create } = createComponent('popup');
let _zIndex = 2000; let _zIndex = 2000;
...@@ -112,6 +114,9 @@ export const popupProps = { ...@@ -112,6 +114,9 @@ export const popupProps = {
}; };
export default create({ export default create({
children: [overlay], children: [overlay],
components: {
'nut-overlay': OverLay
},
props: { props: {
...popupProps ...popupProps
}, },
......
...@@ -22,6 +22,10 @@ $padding-xs: 12px; ...@@ -22,6 +22,10 @@ $padding-xs: 12px;
$font-family: PingFang SC, Microsoft YaHei, Helvetica, Hiragino Sans GB, SimSun, $font-family: PingFang SC, Microsoft YaHei, Helvetica, Hiragino Sans GB, SimSun,
sans-serif !default; 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
$font-size-0: 10px; $font-size-0: 10px;
$font-size-1: 12px; $font-size-1: 12px;
...@@ -170,20 +174,6 @@ $overlay-bg-color: rgba(0, 0, 0, 0.7); ...@@ -170,20 +174,6 @@ $overlay-bg-color: rgba(0, 0, 0, 0.7);
//popup //popup
$popup-close-icon-margin: 16px; $popup-close-icon-margin: 16px;
$popup-border-radius: 20px; $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
$notify-text-color: $white; $notify-text-color: $white;
...@@ -246,6 +236,9 @@ $address-region-tab-line: linear-gradient( ...@@ -246,6 +236,9 @@ $address-region-tab-line: linear-gradient(
$primary-color-end 100% $primary-color-end 100%
); );
// dialog
$dialog-width: 296px;
view-block { view-block {
display: block; display: block;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册