(function ($) { /** * 多文件上传控件,用于上传文件的控件,支持多文件,格式限制,图片预览,文件大小限制等功能。 * * @example * var file = new FR.MultiFileEditor({ * renderEl : 'body' * }); * * @class FR.MultiFileEditor * @extends FR.BaseEditor * * @cfg {JSON} options 属性配置 * @cfg {Number} [options.width=120] 宽度 * @cfg {Number} [options.height=20] 高度 * @cfg {Boolean} [options.render=true] 是否渲染 * @cfg {Boolean} [options.showViewImage=true] 编辑完显示预览图片 */ FR.MultiFileEditor = FR.extend(FR.BaseEditor, /**@class FR.MultiFileEditor*/{ /** * @returns {*} * @private */ _defaultConfig: function () { return $.extend(FR.MultiFileEditor.superclass._defaultConfig.apply(), { width: 120, // 预览面板的宽度 height: 20, // 预览面板的高度 render: true, // 编辑完显示预览图片 showViewImage: true }); }, /** * @private */ _init: function () { FR.$defaultImport('/com/fr/web/core/js/noswfupload.js', 'js'); FR.$defaultImport('/com/fr/web/core/css/widget/noswfupload-icons.css', 'css'); FR.MultiFileEditor.superclass._init.apply(this, arguments); var o = this.options; var self = this; this.$uploadForm = $('<form enctype="multipart/form-data"></form>') .css('position', 'relative').appendTo(this.element); this.divWrap = $("<div></div>").css({ 'width': this.options.width + 'px', 'height': this.options.height + 'px', 'position': 'relative', 'overflow': 'hidden' }).appendTo(this.$uploadForm); this.$fileupload = $('<input type="file" name="file"/>') .addClass('fr-fileupload').appendTo(this.divWrap); // be sure input accept multiple files if (o.maxlength !== 1) { this.$fileupload[0].setAttribute("multiple", "multiple"); } if (o.disabled) { this.$fileupload[0].setAttribute("disabled", "disabled"); } // 添加预览面板 if (o.render === true) { var min = Math.min(this.options.height, this.options.width); var pImageSize = min > 32 ? 32 : min - 2; var pCountSize = min > 32 ? 20 : min * 0.7; this.$previewImage = $("<img>").attr("src", FR.servletURL + "?op=resource&resource=/com/fr/web/images/file/upload.gif") .appendTo(this.divWrap).addClass("fr-fileupload-image") .css({ left: this.options.width > 32 ? (this.options.width - 32) / 2 : 0, top: this.options.height > 32 ? (this.options.height - 32) / 2 : 0, width: pImageSize, height: pImageSize }); if (o.tooltipText) { var toolSpan = $("<span/>").addClass("fr-fileupload-tooltipspan"); this.tooltipDiv = $("<div/>").css({ left: pImageSize + 7 + 'px', height: pImageSize + 'px' }).append(toolSpan.text(o.tooltipText)).addClass("fr-fileupload-tooltip").appendTo(this.$uploadForm); } this.$preview = this.$previewImage; if (self.options.maxlength !== 1) { this.$previewCount = $('<div class="fr-fileupload-preview-count"/>').text(0) .appendTo(this.divWrap).css({ left: this.options.width > 32 ? (this.options.width - 32) / 2 + 22 : this.$previewImage.width() - 10, top: this.options.height > 32 ? (this.options.height - 32) / 2 + 8 : 0, width: pCountSize, height: pCountSize, 'border-radius': pCountSize, 'font-size': pCountSize * 0.6 }); } } else { this.$preview = this.element; } if (this.options.write) { $('.fr-fileupload', self.element).css({ 'right': 0, 'font-size': self.element.height() < 100 ? '100px' : self.element.height() + 'px', 'width': this.options.width, 'height': this.options.height }); } // create the noswfupload.wrap Object // wrap.maxSize 文件大小限制 // wrap.maxlength 文件个数限制 this.wrap = noswfupload.wrap((this.$fileupload)[0], o.maxSize, o.maxlength, this.options.width, this.options.height); var wrap = this.wrap; // fileType could contain whatever text but filter checks *.{extension} // if present // handlers wrap.onerror = function () { noswfupload.text(this.dom.info, "WARNING: Unable to upload " + this.file.fileName); }; wrap.onloadstart = function (rpe, xhr) { this.show(0, 0); noswfupload.text(this.dom.info, "Preparing for upload ... "); }; wrap.onprogress = function (rpe, xhr) { // percent for each bar this.show((this.sent + rpe.loaded) * 100 / this.total, rpe.loaded * 100 / rpe.total); noswfupload.text(this.dom.info, FR.i18nText("FR-Basic-File_Uping_File") + ": " + this.file.fileName); // fileSize is -1 only if browser does not support file info access // this if splits recent browsers from others if (this.file.fileSize !== -1) { // simulation property indicates when the progress event is fake if (rpe.simulation) { noswfupload.text(this.dom.info, FR.i18nText("FR-Basic-File_Uping_File") + ": " + this.file.fileName, FR.i18nText("FR-Basic-File_All_File_Uploaded") + noswfupload.size(this.sent + rpe.loaded)); } else { noswfupload.text(this.dom.info, FR.i18nText("FR-Basic-File_Uping_File") + ": " + this.file.fileName, FR.i18nText("FR-Basic-File_Uploaded_File") + ": " + noswfupload.size(rpe.loaded), FR.i18nText("FR-Basic-File_All_File_Uploaded") + noswfupload.size(this.sent + rpe.loaded)); } } else { // if fileSIze is -1 browser is using an iframe because it does // not support // files sent via Ajax (XMLHttpRequest) // We can still show some information noswfupload.text(this.dom.info, "Uploading: " + this.file.fileName, "Sent: " + (this.sent / 100) + " out of " + (this.total / 100)); } }; // generated if there is something wrong during upload wrap.onerror = function () { // just inform the user something was wrong noswfupload.text(this.dom.info, "WARNING: Unable to upload " + this.file.fileName); }; // generated when every file has been sent (one or more, it does not // matter) wrap.onload = function (rpe, xhr) { var self_ = this; // just show everything is fine ... noswfupload.text(this.dom.info, "Upload complete"); // ... and after a second reset the component setTimeout(function () { self_.clean(); // remove files from list self_.hide(); // hide progress bars and enable input file noswfupload.text(self_.dom.info, ""); self.fireEvent(FR.Events.AFTEREDIT); // enable again the submit button/element }, 1000); self.showViewList(); var attach =self.wrap.attach_array[0]; self.fireEvent(FR.Events.CALLBACK, attach); // fileListWrap.parent().css("overflow", "auto"); }; wrap.url = this.options.url ? this.options.url : FR.servletURL + '?op=fr_attach&cmd=ah_upload'; //传到不同的服务器上, 会导致httpsession相互覆盖, 处理起来更麻烦了 wrap.url += ((window.FS && window.FS.serverID) ? '&serverID='+ window.FS.serverID : ""); wrap.fileType = o.accept; //文件类型限制 wrap.attach_array = []; wrap.attach_names = []; wrap.attachNum = 0; this._dealValueWithEvents(o.value); }, /** * @param {JSON} attach 文件对象 * @private */ _dealValueWithEvents: function (attach) { if (FR.equals(attach, this.wrap.attach_array) || (this.wrap.attach_array.length === 1 && FR.equals( this.wrap.attach_array[0], attach))) { return; } if (($.isArray(attach) && attach.length > 0 && attach[0].attach_id) || (attach && attach.attach_id)) { var attach_array = $.isArray(attach) ? attach : [attach]; this.wrap.attach_array = []; for (var i = 0; i < attach_array.length; i++) { // 编辑前setValue4Write(),不能让oldvalue跟getValue是同一个对象 this.wrap.attach_array.push(attach_array[i]); var obj = {}; this.wrap.files.push(obj); } this.showViewList(); this.wrap.files.length = 0; } // 按delete或者backspace时候 if (attach == '') { this.reset(); } }, /** * 生成上传文件列表 * @param {JSON} attach 上传的文件信息对象 * @param {Number} i 位置索引 * @returns {jQuery} 返回生成的列表DOM */ createListItem: function (attach, i) { var self = this; var download = $("<li></li>"); var cancelButton; download .append(cancelButton = ($("<a class='fr-fileupload-progressCancel' href='#'> </a>")) .click(function (e) { self.removeAttach(attach, download); e.stopEvent(); }).hide()) .append($("<span class='fr-fileupload-fileNameItem'>" + self.wrap.attach_names[i] + " " + FR.__fileSizeFormat__(attach.fileSize) + "</span>") .bind("click", function () { var attach_id = attach.attach_id; window.open(FR.servletURL + "?op=fr_attach&cmd=ah_download&id=" + attach_id); }) .mouseover(function () { $(this).css({color: 'blue' }) }) .mouseout(function () { $(this).css({color: '' }) })).addClass("fr-fileupload-listitem").mouseover( function () { cancelButton.show(); $(this).css({'background-color': '#9FC5F8'}) }).mouseout(function () { cancelButton.hide(); $(this).css({'background-color': ''}) }); self.wrap.attachNum++; if (self.wrap.maxlength !== 1) { self.$previewCount.text(self.wrap.attachNum); } return download; }, /** * 显示文件列表 */ showViewList: function () { var self = this; var fileListDiv; var fileListWrap; if (!self.options.hideFileList) { var fileListUl = $("ul", self.element); if (fileListUl.length === 0) { var contentDiv = $("<div class='fr-fileupload-popview'/>").appendTo(self.element).css({ marginTop: 18 - self.options.height / 2, marginLeft : self.options.width / 2 + 18 }); fileListDiv = $("<div class='fr-fileupload-popview-list'/>").appendTo(contentDiv); fileListWrap = $("<ul/>").addClass("fr-fileupload-list").appendTo(fileListDiv); } else { fileListDiv = fileListUl.parent(); fileListWrap = fileListUl; } } var wrap = self.wrap; this.imageURL = null; if (self.options.maxlength === 1) { var o = this.options; var attach = wrap.attach_array[0]; this.removeBackground(); if (attach.attach_type == 'image') { self.$tempPreview = $("<td></td>") .appendTo(this.divWrap).css({ left: self.element.outerWidth(true) - self.element.width(), width: o.width, height: o.height, top: 0, position: 'absolute' }); FR.lastTarget = self.$tempPreview; var $target = $(self.$tempPreview); $target.css("background", ""); var im_url = FR.servletURL + '?op=fr_attach&cmd=ah_image&id=' + attach.attach_id; $target.css('background', 'url(' + im_url + ") 0 0 no-repeat transparent"); this.imageURL = im_url; $target.css("cursor", "default").unbind("click", download) } wrap.attach_names[0] = attach.filename; if (fileListWrap) { fileListWrap.children().remove(); fileListWrap.append(this.createListItem(attach, 0)); } } // 多文件时要处理重名的问题 if (self.options.maxlength !== 1) { for (var i = wrap.attach_array.length - 1, m = 0; m < wrap.files.length; i--, m++) { wrap.attach_names[i] = wrap.attach_array[i].filename; var dup = 0; for (var j = 0; j < wrap.attach_array.length - wrap.files.length; j++) { if (wrap.attach_names[i] === wrap.attach_array[j].filename) { dup++; } } if (dup > 0) { wrap.attach_names[i] = FR.lengthenFileName(wrap.attach_array[i].filename, "(" + dup + ")"); } fileListWrap && fileListWrap.append(this.createListItem(wrap.attach_array[i], i)); } } // ie杂项不支持max-height属性 if (FR.Browser.isIE() && fileListDiv) { if (fileListDiv.height() > 160) { fileListDiv.height(160); if (fileListWrap.width() + 18 > fileListDiv.width()) { fileListDiv.width(fileListWrap.width() + 18); } } } function download(e) { window.open(FR.servletURL + "?op=fr_attach&cmd=ah_download&id=" + e.data); e.stopPropagation(); } }, /** * 移除已上传的文件 * @param {JSON} attach 上传的文件对象 * @param {jQuery} download 文件显示所在的DOM对象 */ removeAttach: function (attach, download) { var attach_id = attach.attach_id; var self = this; var wrap = this.wrap; FR.ajax({ url: FR.servletURL + "?op=fr_attach&cmd=ah_release", data: { id: attach_id }, complete: function (res, status) { if (status == 'success') { if (download) { download.remove(); } for (var n = 0; n < wrap.attach_array.length; n++) { if (wrap.attach_array[n].attach_id == attach_id) { if (self.wrap.maxlength !== 1) { wrap.attachNum--; self.$previewCount.text(self.wrap.attachNum); if (wrap.attachNum === 0) { $(".fr-fileupload-popview", self.element).remove(); } } wrap.attach_array.remove(wrap.attach_array[n]); break; } } if (self.wrap.maxlength === 1) { $(".fr-fileupload-popview", self.element).remove(); self.removeBackground(); } self.fireEvent(FR.Events.CALLBACK); } } }) }, /** * 重置背景图片 * 格子的高度可能发生了改变 要调整 * @param u */ resetBackground: function(u) { var url = u || this.imageURL; this.removeBackground(); if (this.options.tdCell) { this.options.tdCell.css('background', 'url(' + url + "&height=" + this.options.tdCell.height() + ") 0 0 no-repeat transparent"); } }, /** * 特指移除单文件上传中添加图片后的背景 */ removeBackground: function () { // 这个background是指单文件添加了图片的情况 // 有两处需要移除 一个原始报表结构的td中 一个是动态生成的x-editor中 // 还有可能用在表单啊 if (this.options.tdCell) { this.options.tdCell.css('background-image', ''); } var previousAtt = this.divWrap.children().eq(2); previousAtt.css('background-image', ''); previousAtt.remove(); }, startEditing: function () { if (this.options.tdCell) { $('.attach-download-div,.fr-fileupload-download-all', $(this.options.tdCell)).hide(); } this.$preview.show(); // richer:文件的enable需要单独处理 if (this.options.disabled) { this.$preview.unbind("click"); } // IE杂项模式 x-editor的高度总是比文件控件高一些 导致浅蓝色边框超出 // 没搞懂为什么变大 索性去掉这个控件编辑的浅蓝框子 if (FR.Browser.isIE() && !$.support.boxModel && this.options.write) { $(".x-editor").addClass('none-border'); } }, stopEditing: function () { if (this.options.tdCell) { var cur = $('.attach-download-div,.fr-fileupload-download-all', $(this.options.tdCell)); cur.show(); } if (FR.Browser.isIE() && !$.support.boxModel && this.options.write) { $(".x-editor").removeClass('none-border'); } if (this.imageURL != null) { var self = this; setTimeout(function() { self.resetBackground(); }, 200); } }, getValue: function () { if (!this.wrap.attach_array) { //return []; // shoc: 返回空数组有个问题,提交入库的时候空文件控件值到后台变成"[]"始终会入库一条记录, // 后台拦截了null、""等情况,在那里判断拦截这个不太合适,还是改这里了 return ""; } if (this.options.maxlength === 1 && this.wrap.attach_array[0] && this.wrap.attach_array[0].attach_type == "image") { return this.wrap.attach_array[0]; } return this.wrap.attach_array.length === 0 ? "" : this.wrap.attach_array; }, setEnable: function (enable) { FR.MultiFileEditor.superclass.setEnable.apply(this, arguments); if (this.$fileupload && this.$fileupload[0]) { if (enable) { this.$fileupload[0].removeAttribute("disabled"); } else { this.$fileupload[0].setAttribute("disabled", "disabled"); } } }, /** * 是否显示预览图片 * @returns {Boolean} 返回是否显示预览图片 */ isShowViewImage: function () { return this.options.showViewImage && this.options.maxlength === 1; }, reset: function () { this.wrap.attach_array = []; this.wrap.attach_names = []; this.wrap.attachNum = 0; if (this.options.tdCell) { $('.attach-download-div', $(this.options.tdCell)).remove(); } $('.fr-fileupload-popview', this.element).remove(); this.removeBackground(); } }); $.shortcut("multifile", FR.MultiFileEditor); })(jQuery);