/** @Title: layui.upload 文件上传 @Author: 贤心 @License:MIT */ layui.define('layer' , function(exports){ "use strict"; var $ = layui.$ ,layer = layui.layer ,hint = layui.hint() ,device = layui.device() //外部接口 ,upload = { config: {} //全局配置项 //设置全局项 ,set: function(options){ var that = this; that.config = $.extend({}, that.config, options); return that; } //事件监听 ,on: function(events, callback){ return layui.onevent.call(this, MOD_NAME, events, callback); } } //操作当前实例 ,thisUpload = function(){ var that = this; return { upload: function(files){ that.upload.call(that, files); } ,config: that.config } } //字符常量 ,MOD_NAME = 'upload', ELEM = '.layui-upload', THIS = 'layui-this', SHOW = 'layui-show', HIDE = 'layui-hide', DISABLED = 'layui-disabled' ,ELEM_FILE = 'layui-upload-file', ELEM_FORM = 'layui-upload-form', ELEM_IFRAME = 'layui-upload-iframe', ELEM_CHOOSE = 'layui-upload-choose', ELEM_DRAG = 'layui-upload-drag' //构造器 ,Class = function(options){ var that = this; that.config = $.extend({}, that.config, upload.config, options); that.render(); }; //默认配置 Class.prototype.config = { accept: 'images' //允许上传的文件类型:images/file/video/audio ,exts: '' //允许上传的文件后缀名 ,auto: true //是否选完文件后自动上传 ,bindAction: '' //手动上传触发的元素 ,url: '' //上传地址 ,field: 'file' //文件字段名 ,method: 'post' //请求上传的http类型 ,data: {} //请求上传的额外参数 ,drag: true //是否允许拖拽上传 ,size: 0 //文件限制大小,默认不限制 ,multiple: false //是否允许多文件上传,不支持ie8-9 }; //初始渲染 Class.prototype.render = function(options){ var that = this ,options = that.config; options.elem = $(options.elem); options.bindAction = $(options.bindAction); that.file(); that.events(); }; //追加文件域 Class.prototype.file = function(){ var that = this ,options = that.config ,elemFile = that.elemFile = $([ '' ].join('')) ,next = options.elem.next(); if(next.hasClass(ELEM_FILE) || next.hasClass(ELEM_FORM)){ next.remove(); } //包裹ie8/9容器 if(device.ie && device.ie < 10){ options.elem.wrap('
'); } that.isFile() ? ( that.elemFile = options.elem ,options.field = options.elem[0].name ) : options.elem.after(elemFile); //初始化ie8/9的Form域 if(device.ie && device.ie < 10){ that.initIE(); } }; //ie8-9初始化 Class.prototype.initIE = function(){ var that = this ,options = that.config ,iframe = $('') ,elemForm = $([''].join('')); //插入iframe $('#'+ ELEM_IFRAME)[0] || $('body').append(iframe); //包裹文件域 if(!options.elem.next().hasClass(ELEM_IFRAME)){ that.elemFile.wrap(elemForm); //追加额外的参数 options.elem.next('.'+ ELEM_IFRAME).append(function(){ var arr = []; layui.each(options.data, function(key, value){ arr.push('') }); return arr.join(''); }()); } }; //异常提示 Class.prototype.msg = function(content){ return layer.msg(content, { icon: 2 ,shift: 6 }); }; //判断绑定元素是否为文件域本身 Class.prototype.isFile = function(){ var elem = this.config.elem[0]; if(!elem) return; return elem.tagName.toLocaleLowerCase() === 'input' && elem.type === 'file' } //预读图片信息 Class.prototype.preview = function(callback){ var that = this; if(window.FileReader){ layui.each(that.chooseFiles, function(index, file){ var reader = new FileReader(); reader.readAsDataURL(file); reader.onload = function(){ callback && callback(index, file, this.result); } }); } }; //执行上传 Class.prototype.upload = function(files, type){ var that = this ,options = that.config ,elemFile = that.elemFile[0] //高级浏览器处理方式,支持跨域 ,ajaxSend = function(){ layui.each(files || that.files || that.chooseFiles || elemFile.files, function(index, file){ var formData = new FormData(); formData.append(options.field, file); //追加额外的参数 layui.each(options.data, function(key, value){ formData.append(key, value); }); $.ajax({ url: options.url ,type: options.method ,data: formData ,contentType: false ,processData: false ,dataType: 'json' ,success: function(res){ done(index, res); } ,error: function(){ that.msg('请求上传接口出现异常'); error(index); } }); }); } //低版本IE处理方式,不支持跨域 ,iframeSend = function(){ var iframe = $('#'+ ELEM_IFRAME); that.elemFile.parent().submit(); //获取响应信息 clearInterval(Class.timer); Class.timer = setInterval(function() { var res, iframeBody = iframe.contents().find('body'); try { res = iframeBody.text(); } catch(e) { that.msg('获取上传后的响应信息出现异常'); clearInterval(Class.timer); error(); } if(res){ clearInterval(Class.timer); iframeBody.html(''); done(0, res); } }, 30); } //统一回调 ,done = function(index, res){ that.elemFile.next('.'+ ELEM_CHOOSE).remove(); elemFile.value = ''; if(typeof res !== 'object'){ try { res = JSON.parse(res); } catch(e){ res = {}; return that.msg('请对上传接口返回有效JSON'); } } typeof options.done === 'function' && options.done(res, index || 0, function(files){ that.upload(files); }); } //统一网络异常回调 ,error = function(index){ if(options.auto){ elemFile.value = ''; } typeof options.error === 'function' && options.error(index || 0, function(files){ that.upload(files); }); } ,exts = options.exts ,check ,value = function(){ var arr = []; layui.each(files || that.chooseFiles, function(i, item){ arr.push(item.name); }); return arr; }() //回调返回的参数 ,args = { preview: function(callback){ that.preview(callback); } ,upload: function(index, file){ var thisFile = {}; thisFile[index] = file; that.upload(thisFile); } ,pushFile: function(){ that.files = that.files || {}; layui.each(that.chooseFiles, function(index, item){ that.files[index] = item; }); return that.files; } } //提交上传 ,send = function(){ if(type === 'choose'){ return options.choose && options.choose(args); } //上传前的回调 options.before && options.before(args); //IE兼容处理 if(device.ie){ return device.ie > 9 ? ajaxSend() : iframeSend(); } ajaxSend(); } //校验文件格式 value = value.length === 0 ? ((elemFile.value.match(/[^\/\\]+\..+/g)||[]) || '') : value; switch(options.accept){ case 'file': //一般文件 if(exts && !RegExp('\\w\\.('+ exts +')$', 'i').test(escape(value))){ that.msg('选择的文件中包含不支持的格式'); return elemFile.value = ''; } break; case 'video': //视频文件 if(!RegExp('\\w\\.('+ (exts || 'avi|mp4|wma|rmvb|rm|flash|3gp|flv') +')$', 'i').test(escape(value))){ that.msg('选择的视频中包含不支持的格式'); return elemFile.value = ''; } break; case 'audio': //音频文件 if(!RegExp('\\w\\.('+ (exts || 'mp3|wav|mid') +')$', 'i').test(escape(value))){ that.msg('选择的音频中包含不支持的格式'); return elemFile.value = ''; } break; default: //图片文件 layui.each(value, function(i, item){ if(!RegExp('\\w\\.('+ (exts || 'jpg|png|gif|bmp|jpeg$') +')', 'i').test(escape(item))){ check = true; } }); if(check){ that.msg('选择的图片中包含不支持的格式'); return elemFile.value = ''; } break; } //检验文件大小 if(options.size > 0 && !(device.ie && device.ie < 10)){ var limitSize; layui.each(that.chooseFiles, function(index, file){ if(file.size > 1024*options.size){ var size = options.size/1024; size = size >= 1 ? (Math.floor(size) + (size%1 > 0 ? size.toFixed(1) : 0)) + 'MB' : options.size + 'KB' elemFile.value = ''; limitSize = size; } }); if(limitSize) return that.msg('文件不能超过'+ limitSize); } send(); }; //事件处理 Class.prototype.events = function(){ var that = this ,options = that.config //设置当前选择的文件队列 ,setChooseFile = function(files){ that.chooseFiles = {}; layui.each(files, function(i, item){ var time = new Date().getTime(); that.chooseFiles[time + '-' + i] = item; }); } //设置选择的文本 ,setChooseText = function(files, filename){ var elemFile = that.elemFile ,value = files.length > 1 ? files.length + '个文件' : ((files[0] || {}).name || (elemFile[0].value.match(/[^\/\\]+\..+/g)||[]) || ''); if(elemFile.next().hasClass(ELEM_CHOOSE)){ elemFile.next().remove(); } that.upload(null, 'choose'); if(that.isFile() || options.choose) return; elemFile.after(''+ value +''); }; //点击上传容器 options.elem.off('upload.start').on('upload.start', function(){ var othis = $(this), data = othis.attr('lay-data'); if(data){ try{ data = new Function('return '+ data)(); that.config = $.extend({}, options, data); } catch(e){ hint.error('Upload element property lay-data configuration item has a syntax error: ' + data) } } that.config.item = othis; that.elemFile[0].click(); }); //拖拽上传 if(!(device.ie && device.ie < 10)){ options.elem.off('upload.over').on('upload.over', function(){ var othis = $(this) othis.attr('lay-over', ''); }) .off('upload.leave').on('upload.leave', function(){ var othis = $(this) othis.removeAttr('lay-over'); }) .off('upload.drop').on('upload.drop', function(e, param){ var othis = $(this), files = param.originalEvent.dataTransfer.files || []; othis.removeAttr('lay-over'); setChooseFile(files); if(options.auto){ that.upload(files); } else { setChooseText(files); } }); } //文件选择 that.elemFile.off('upload.change').on('upload.change', function(){ var files = this.files || []; setChooseFile(files); options.auto ? that.upload() : setChooseText(files); //是否自动触发上传 }); //手动触发上传 options.bindAction.off('upload.action').on('upload.action', function(){ that.upload(); }); //防止事件重复绑定 if(options.elem.data('haveEvents')) return; that.elemFile.on('change', function(){ $(this).trigger('upload.change'); }); options.elem.on('click', function(){ if(that.isFile()) return; $(this).trigger('upload.start'); }); if(options.drag){ options.elem.on('dragover', function(e){ e.preventDefault(); $(this).trigger('upload.over'); }).on('dragleave', function(e){ $(this).trigger('upload.leave'); }).on('drop', function(e){ e.preventDefault(); $(this).trigger('upload.drop', e); }); } options.bindAction.on('click', function(){ $(this).trigger('upload.action'); }); options.elem.data('haveEvents', true); }; //核心入口 upload.render = function(options){ var inst = new Class(options); return thisUpload.call(inst); }; exports(MOD_NAME, upload); });