diff --git a/docs/layer/detail/demo.md b/docs/layer/detail/demo.md
index ceb39de0..43ba463c 100644
--- a/docs/layer/detail/demo.md
+++ b/docs/layer/detail/demo.md
@@ -88,6 +88,12 @@
+
+
+
+
主题风格
diff --git a/docs/layer/detail/options.md b/docs/layer/detail/options.md
index e5f585d5..a173a72b 100644
--- a/docs/layer/detail/options.md
+++ b/docs/layer/detail/options.md
@@ -397,6 +397,53 @@ layer.open({
+[btnAsync](#options.btnAsync) 2.9.12+
+
+ |
+
+
+
+
+异步按钮。开启之后,除 `layer.prompt` 的按钮外,按钮回调的返回值将支持 `boolean | Promise | JQueryDeferred` 类型,返回 `false` 或 `Promise.reject` 时阻止关闭。
+
+注意,此时 `yes` 和 `btn1`(两者等效) 回调的默认行为发生了变化,即由触发时不关闭弹层变为关闭弹层。
+
+
+
+```
+var sleep = function (time) {
+ return $.Deferred(function (defer) {
+ setTimeout(function () {
+ defer.resolve();
+ }, time)
+ })
+}
+// 下面以 confirm 层为例
+layer.confirm('一个询问框的示例?', {
+ btnAsync: true,
+ btn: ['确定', '关闭'] // 按钮
+ },
+ function (index, layero, that) {
+ var defer = $.Deferred();
+ // 注: that.loading() 仅 btnAsync 开启后支持,参数为 boolean 类型,表示打开或关闭按钮的加载效果。
+ that.loading(true);
+ sleep(1000).then(defer.resolve);
+ return defer.promise();
+ }
+);
+```
+
+ |
+boolean |
+
+
+`false`
+
+ |
+
+
+
+
[skin](#options.skin)
|
@@ -746,7 +793,7 @@ layer.open({
layer.open({
content: '',
/** @type {(layero: JQuery, index: number) => boolean | JQueryDeferred | Promise} */
- beforeEnd: function(layero, index){
+ beforeEnd: function(layero, index, that){
return $.Deferred(function(defer){
var el = layero.find('#id');
var val = el.val().trim();
@@ -914,4 +961,4 @@ layer.open({
-
\ No newline at end of file
+
diff --git a/docs/layer/examples/btnasync.md b/docs/layer/examples/btnasync.md
new file mode 100644
index 00000000..fa6affcc
--- /dev/null
+++ b/docs/layer/examples/btnasync.md
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/css/modules/layer.css b/src/css/modules/layer.css
index 7d6a09ee..4ebfdfd4 100644
--- a/src/css/modules/layer.css
+++ b/src/css/modules/layer.css
@@ -144,6 +144,8 @@ html #layuicss-layer{display: none; position: absolute; width: 1989px;}
.layui-layer-btn .layui-layer-btn0{border-color: transparent; background-color: #1E9FFF; color:#fff;}
.layui-layer-btn-l{text-align: left;}
.layui-layer-btn-c{text-align: center;}
+.layui-layer-btn-is-loading{opacity:0.5 !important; cursor:not-allowed !important; cursor:wait !important; overflow:hidden; white-space:nowrap; -webkit-user-select: none; -ms-user-select: none;user-select: none;}
+.layui-layer-btn-is-loading .layui-layer-btn-loading-icon{margin-right: 8px; font-size: 14px;}
/* 定制化 */
.layui-layer-dialog{min-width: 240px;}
diff --git a/src/modules/layer.js b/src/modules/layer.js
index 81332f91..f6c1fb8d 100644
--- a/src/modules/layer.js
+++ b/src/modules/layer.js
@@ -2,7 +2,7 @@
* layer
* 通用 Web 弹出层组件
*/
-
+//@ts-ignore
;!function(window, undefined){
"use strict";
@@ -859,6 +859,16 @@ Class.pt.move = function(){
return that;
};
+Class.pt.btnLoading = function(btnElem, isLoading){
+ if(isLoading){
+ var loadingTpl = '';
+ if(btnElem.find('.layui-layer-btn-loading-icon')[0]) return;
+ btnElem.addClass('layui-layer-btn-is-loading').attr({disabled: ''}).prepend(loadingTpl);
+ }else{
+ btnElem.removeClass('layui-layer-btn-is-loading').removeAttr('disabled').find('.layui-layer-btn-loading-icon').remove();
+ }
+}
+
Class.pt.callback = function(){
var that = this, layero = that.layero, config = that.config;
that.openLayer();
@@ -875,18 +885,42 @@ Class.pt.callback = function(){
// 按钮
layero.find('.'+ doms[6]).children('a').on('click', function(){
- var index = $(this).index();
- if(index === 0){
- if(config.yes){
- config.yes(that.index, layero, that);
- } else if(config['btn1']){
- config['btn1'](that.index, layero, that);
- } else {
+ var btnElem = $(this);
+ var index = btnElem.index();
+ if(btnElem.attr('disabled')) return;
+
+ // 若为异步按钮
+ if(config.btnAsync){
+ var btnCallback = index === 0 ? (config.yes || config['btn1']) : config['btn'+(index+1)];
+ that.loading = function(isLoading){
+ that.btnLoading(btnElem, isLoading);
+ }
+
+ if(btnCallback){
+ ready.promiseLikeResolve(btnCallback.call(config, that.index, layero, that))
+ .then(function(result){
+ if(result !== false){
+ layer.close(that.index)
+ }
+ }, function(reason){
+ reason !== undefined && window.console && window.console.error('layer error hint: ' + reason);
+ });
+ }else{
layer.close(that.index);
}
- } else {
- var close = config['btn'+(index+1)] && config['btn'+(index+1)](that.index, layero, that);
- close === false || layer.close(that.index);
+ } else { // 普通按钮
+ if(index === 0){
+ if(config.yes){
+ config.yes(that.index, layero, that);
+ } else if(config['btn1']){
+ config['btn1'](that.index, layero, that);
+ } else {
+ layer.close(that.index);
+ }
+ } else {
+ var close = config['btn'+(index+1)] && config['btn'+(index+1)](that.index, layero, that);
+ close === false || layer.close(that.index);
+ }
}
});
@@ -926,7 +960,7 @@ Class.pt.callback = function(){
});
config.end && (ready.end[that.index] = config.end);
- config.beforeEnd && (ready.beforeEnd[that.index] = config.beforeEnd);
+ config.beforeEnd && (ready.beforeEnd[that.index] = $.proxy(config.beforeEnd, config, layero, that.index, that));
};
// for ie6 恢复 select
@@ -1000,6 +1034,18 @@ ready.restScrollbar = function(index){
}
};
+// 类似 Promise.resolve
+ready.promiseLikeResolve = function(value){
+ var deferred = $.Deferred();
+
+ if(value && typeof value.then === 'function'){
+ value.then(deferred.resolve, deferred.reject);
+ }else{
+ deferred.resolve(value);
+ }
+ return deferred.promise();
+}
+
/** 内置成员 */
window.layer = layer;
@@ -1307,19 +1353,7 @@ layer.close = function(index, callback){
}
if(!hideOnClose && typeof ready.beforeEnd[index] === 'function'){
- // 类似 Promise.resolve
- var promiseLikeResolve = function(value){
- var deferred = $.Deferred();
-
- if(value && typeof value.then === 'function'){
- value.then(deferred.resolve, deferred.reject);
- }else{
- deferred.resolve(value);
- }
- return deferred.promise();
- }
-
- promiseLikeResolve(ready.beforeEnd[index](layero, index))
+ ready.promiseLikeResolve(ready.beforeEnd[index]())
.then(function(result){
if(result !== false){
delete ready.beforeEnd[index];