优化 upload 渲染入口逻辑,以解决因重复渲染导致的若干问题

优化 upload 实例的 `reload` 方法,可更好地进行完整重载
修复 upload 当 `unified: true` 时的报错问题
pull/1386/head
贤心 2023-09-25 19:36:54 +08:00
parent debfe48968
commit e0dcabb9fb
1 changed files with 93 additions and 63 deletions

View File

@ -3,16 +3,22 @@
* 上传组件 * 上传组件
*/ */
layui.define(['lay','layer'], function(exports){ layui.define(['lay', 'layer'], function(exports){
"use strict"; "use strict";
var $ = layui.$; var $ = layui.$;
var lay = layui.lay;
var layer = layui.layer; var layer = layui.layer;
var device = layui.device(); var device = layui.device();
// 模块名
var MOD_NAME = 'upload';
var MOD_INDEX = 'layui_'+ MOD_NAME +'_index'; // 模块索引名
// 外部接口 // 外部接口
var upload = { var upload = {
config: {}, // 全局配置项 config: {}, // 全局配置项
index: layui[MOD_NAME] ? (layui[MOD_NAME].index + 10000) : 0, // 索引
// 设置全局项 // 设置全局项
set: function(options){ set: function(options){
var that = this; var that = this;
@ -26,8 +32,13 @@ layui.define(['lay','layer'], function(exports){
}; };
// 操作当前实例 // 操作当前实例
var thisUpload = function(){ var thisModule = function(){
var that = this; var that = this;
var options = that.config;
var id = options.id;
thisModule.that[id] = that; // 记录当前实例对象
return { return {
upload: function(files){ upload: function(files){
that.upload.call(that, files); that.upload.call(that, files);
@ -40,7 +51,6 @@ layui.define(['lay','layer'], function(exports){
}; };
// 字符常量 // 字符常量
var MOD_NAME = 'upload';
var ELEM = 'layui-upload'; var ELEM = 'layui-upload';
var THIS = 'layui-this'; var THIS = 'layui-this';
var SHOW = 'layui-show'; var SHOW = 'layui-show';
@ -57,6 +67,7 @@ layui.define(['lay','layer'], function(exports){
// 构造器 // 构造器
var Class = function(options){ var Class = function(options){
var that = this; var that = this;
that.index = ++upload.index;
that.config = $.extend({}, that.config, upload.config, options); that.config = $.extend({}, that.config, upload.config, options);
that.render(); that.render();
}; };
@ -86,15 +97,51 @@ layui.define(['lay','layer'], function(exports){
"limit-size": null // 限制 size 属性的提示 --- function "limit-size": null // 限制 size 属性的提示 --- function
} }
}; };
// 重载实例
Class.prototype.reload = function(options){
var that = this;
that.config = $.extend({}, that.config, options);
that.render(true);
};
// 初始渲染 // 初始渲染
Class.prototype.render = function(){ Class.prototype.render = function(rerender){
var that = this; var that = this;
var options = that.config; var options = that.config;
// 若 elem 非唯一
var elem = $(options.elem);
if (elem.length > 1) {
layui.each(elem, function() {
upload.render($.extend({}, options, {
elem: this
}));
});
return that;
}
// 合并 lay-options 属性上的配置信息
$.extend(options, lay.options(elem[0], {
attr: elem.attr('lay-data') ? 'lay-data' : null // 兼容旧版的 lay-data 属性
}));
// 若重复执行 render则视为 reload 处理
if (!rerender && elem[0] && elem.data(MOD_INDEX)) {
var newThat = thisModule.getThis(elem.data(MOD_INDEX));
if(!newThat) return;
return newThat.reload(options);
}
options.elem = $(options.elem); options.elem = $(options.elem);
options.bindAction = $(options.bindAction); options.bindAction = $(options.bindAction);
// 初始化 id 属性 - 优先取 options > 元素 id > 自增索引
options.id = 'id' in options ? options.id : (
elem.attr('id') || that.index
);
that.file(); that.file();
that.events(); that.events();
}; };
@ -219,11 +266,14 @@ layui.define(['lay','layer'], function(exports){
var request = function(sets){ var request = function(sets){
var formData = new FormData(); var formData = new FormData();
// 删除正在上传中的文件队列 // 恢复文件状态
var removeUploaded = function(index, file) { var resetFileState = function(file) {
if (file[UPLOADING]) { if (sets.unified) {
delete items[index]; layui.each(items, function(index, file){
return true; delete file[UPLOADING];
});
} else {
delete file[UPLOADING];
} }
}; };
@ -234,24 +284,22 @@ layui.define(['lay','layer'], function(exports){
}); });
/* /*
*添加 file 到表单域 * 添加 file 到表单域
*/ */
// 是否统一上传 // 是否统一上传
if (sets.unified) { if (sets.unified) {
layui.each(items, function(index, file){ layui.each(items, function(index, file){
if (removeUploaded(index, file)) return; if (file[UPLOADING]) return;
file[UPLOADING] = true; file[UPLOADING] = true; // 上传中的标记
formData.append(options.field, file); formData.append(options.field, file);
}); });
} else { // 逐一上传 } else { // 逐一上传
if (removeUploaded(sets.index, sets.file)) return; if (sets.file[UPLOADING]) return;
formData.append(options.field, sets.file); formData.append(options.field, sets.file);
sets.file[UPLOADING] = true; // 上传中的标记 sets.file[UPLOADING] = true; // 上传中的标记
} }
// ajax 参数 // ajax 参数
var opts = { var opts = {
url: options.url, url: options.url,
@ -264,7 +312,8 @@ layui.define(['lay','layer'], function(exports){
success: function(res){ // 成功回调 success: function(res){ // 成功回调
options.unified ? (successful += that.fileLength) : successful++; options.unified ? (successful += that.fileLength) : successful++;
done(sets.index, res); done(sets.index, res);
allDone(); allDone(sets.index);
resetFileState(sets.file);
}, },
error: function(e){ // 异常回调 error: function(e){ // 异常回调
options.unified ? (failed += that.fileLength) : failed++; options.unified ? (failed += that.fileLength) : failed++;
@ -273,7 +322,8 @@ layui.define(['lay','layer'], function(exports){
'status: '+ (e.status || '') +' - '+ (e.statusText || 'error') 'status: '+ (e.status || '') +' - '+ (e.statusText || 'error')
].join('<br>')); ].join('<br>'));
error(sets.index); error(sets.index);
allDone(); allDone(sets.index);
resetFileState(sets.file);
} }
}; };
@ -357,7 +407,7 @@ layui.define(['lay','layer'], function(exports){
}; };
// 统一网络异常回调 // 统一网络异常回调
var error = function(index){ var error = function(index){
if(options.auto){ if(options.auto){
elemFile.value = ''; elemFile.value = '';
} }
@ -405,7 +455,7 @@ layui.define(['lay','layer'], function(exports){
}; };
// 提交上传 // 提交上传
var send = function(){ var send = function(){
// 上传前的回调 - 如果回调函数明确返回 false则停止上传 // 上传前的回调 - 如果回调函数明确返回 false则停止上传
if(options.before && (options.before(args) === false)) return; if(options.before && (options.before(args) === false)) return;
@ -430,7 +480,8 @@ layui.define(['lay','layer'], function(exports){
? ((elemFile.value.match(/[^\/\\]+\..+/g)||[]) || '') ? ((elemFile.value.match(/[^\/\\]+\..+/g)||[]) || '')
: value; : value;
if(value.length === 0) return; // 若文件域值为空
if (value.length === 0) return;
// 根据文件类型校验 // 根据文件类型校验
switch(options.accept){ switch(options.accept){
@ -517,24 +568,6 @@ layui.define(['lay','layer'], function(exports){
send(); send();
}; };
// 重置方法
Class.prototype.reload = function(opts){
opts = opts || {};
delete opts.elem;
delete opts.bindAction;
var that = this;
var options = that.config = $.extend({}, that.config, upload.config, opts);
var next = options.elem.next();
// 更新文件域相关属性
next.attr({
name: options.name,
accept: options.acceptMime,
multiple: options.multiple
});
};
//事件处理 //事件处理
Class.prototype.events = function(){ Class.prototype.events = function(){
var that = this; var that = this;
@ -565,23 +598,10 @@ layui.define(['lay','layer'], function(exports){
elemFile.after('<span class="layui-inline '+ ELEM_CHOOSE +'">'+ value +'</span>'); elemFile.after('<span class="layui-inline '+ ELEM_CHOOSE +'">'+ value +'</span>');
}; };
// 合并 lay-options/lay-data 属性配置项
var extendAttrs = function(){
var othis = $(this);
var data = othis.attr('lay-data') || othis.attr('lay-options'); // 优先兼容旧版本
if(data){
that.config = $.extend({}, options, lay.options(this, {
attr: othis.attr('lay-data') ? 'lay-data' : null
}));
}
};
// 点击上传容器 // 点击上传容器
options.elem.off('upload.start').on('upload.start', function(){ options.elem.off('upload.start').on('upload.start', function(){
var othis = $(this); var othis = $(this);
extendAttrs.call(this);
that.config.item = othis; that.config.item = othis;
that.elemFile[0].click(); that.elemFile[0].click();
}); });
@ -601,7 +621,6 @@ layui.define(['lay','layer'], function(exports){
var files = param.originalEvent.dataTransfer.files || []; var files = param.originalEvent.dataTransfer.files || [];
othis.removeAttr('lay-over'); othis.removeAttr('lay-over');
extendAttrs.call(this);
setChooseFile(files); setChooseFile(files);
options.auto ? that.upload() : setChooseText(files); // 是否自动触发上传 options.auto ? that.upload() : setChooseText(files); // 是否自动触发上传
@ -609,12 +628,11 @@ layui.define(['lay','layer'], function(exports){
} }
// 文件选择 // 文件选择
that.elemFile.off('upload.change').on('upload.change', function(){ that.elemFile.on('change', function(){
var files = this.files || []; var files = this.files || [];
if(files.length === 0) return; if(files.length === 0) return;
extendAttrs.call(this);
setChooseFile(files); setChooseFile(files);
options.auto ? that.upload() : setChooseText(files); // 是否自动触发上传 options.auto ? that.upload() : setChooseText(files); // 是否自动触发上传
@ -624,19 +642,19 @@ layui.define(['lay','layer'], function(exports){
options.bindAction.off('upload.action').on('upload.action', function(){ options.bindAction.off('upload.action').on('upload.action', function(){
that.upload(); that.upload();
}); });
// 防止事件重复绑定 // 防止事件重复绑定
if(options.elem.data('haveEvents')) return; if(options.elem.data(MOD_INDEX)) return;
that.elemFile.on('change', function(){
$(this).trigger('upload.change'); // 目标元素 click 事件
});
options.elem.on('click', function(){ options.elem.on('click', function(){
if(that.isFile()) return; if(that.isFile()) return;
$(this).trigger('upload.start'); $(this).trigger('upload.start');
}); });
// 目标元素 drop 事件
if(options.drag){ if(options.drag){
options.elem.on('dragover', function(e){ options.elem.on('dragover', function(e){
e.preventDefault(); e.preventDefault();
@ -649,17 +667,29 @@ layui.define(['lay','layer'], function(exports){
}); });
} }
// 手动上传时触发上传的元素 click 事件
options.bindAction.on('click', function(){ options.bindAction.on('click', function(){
$(this).trigger('upload.action'); $(this).trigger('upload.action');
}); });
options.elem.data('haveEvents', true); // 绑定元素索引
options.elem.data(MOD_INDEX, options.id);
};
// 记录所有实例
thisModule.that = {}; // 记录所有实例对象
// 获取当前实例对象
thisModule.getThis = function(id){
var that = thisModule.that[id];
if(!that) hint.error(id ? (MOD_NAME +' instance with ID \''+ id +'\' not found') : 'ID argument required');
return that;
}; };
// 核心入口 // 核心入口
upload.render = function(options){ upload.render = function(options){
var inst = new Class(options); var inst = new Class(options);
return thisUpload.call(inst); return thisModule.call(inst);
}; };
exports(MOD_NAME, upload); exports(MOD_NAME, upload);