本文主要通過原生的js封裝附件上傳upload.js。可成功內嵌釘釘,ios和安卓端可正常使用,支持單個、多個附件上傳。
一、業務需求
封裝原生JS附件上傳,動態創建附件列表,可對附件列表進行刪除和新增功能。
二、業務邏輯
2.1 app.js 全局封裝ajax請求,附件上傳將文件流傳遞給後端,便於接收。
注:如果傳遞base64位字符串,字節長度解析可能導致服務器的端口過載或者文件太大拋異常。
2.2 原生js封裝upload.js,適用於只讀和非只讀的兩種情況下,後者可上傳附件和編輯附件,前者只能查看和下載操作,對於新上傳的附件,暫不支持預覽等下載操作。單個附件控制不大於50M。
對於新上傳的附件,暫不支持預覽等下載操作說明:由於原生window.open直接打開base64位,會有默認的防釣魚操作,實現上有安全性考慮。可通過上傳服務器再返回,實現該附件下載或預覽的統一操作,但會佔用服務器資源,此處暫不支持。
2.3 代碼的引入和調用
儘量可能做到js調用只用一行代碼實現,增加便捷和可行性。
2.4 全局的基礎樣式美化
現在點擊位置寫好按鈕的樣式,圖片效果最佳。通過將input type = file
設置絕對定位,寬高100%,上左定位爲0,透明度爲0,層級z-index
在點擊位置之上即可。建議層級比圖片高一級即可。
三、實例代碼
3.1 app.js
ajaxFile: function(option) { // 通過formData的形式將附件的文件流請求發給後端
ajaxCount++;
var _ajaxCount = ajaxCount;
if (!option.error) {
option.error = ajaxError;
}
return $.ajax({
type: option.type || "POST",
url: app.getItem(app.localKey.url) + option.url, // 自行配置URL
timeout: option.timeout || 5 * 60 * 1000,
dataType: "json",
data: option.data,
processData: false, // ajax不處理髮送的數據
contentType : false,
success: function(r) { // 返回值根據實際情況再做調整
if (option.mustexe && option.validateUserInfo) {
option.success && option.success(r);
} else if (_ajaxCount == ajaxCount && option.validateUserInfo) {
option.success && option.success(r);
}
},
error: option.error
});
},
attach: function(fileId, url) { // 附件下載
var isDD = app.getItem(app.localKey.isDD) || app.otherLogin; // 用戶信息,下載校驗
var _url = app.serverUrl + "file.sp?act=fileDownload&loginUser=" + app.getItem(app.localKey.loginUser) +
"&encAttachId=" + fileId + "&isDD=" + isDD;
if (url) {
_url = url;
}
window.open(_url);
},
3.2 upload.js
以下方法調用了mui.js做彈窗提示組件,如需引入jq可以自行修改。
(function(doc, $, app) {
window.upload = {
isRead: false, // 默認false,附件是否只讀,否可以上傳附件,否可以刪除附件
uploadFiles: [], // 新附件
oldFiles: [], // 舊附件
onReady: function(isRead, obj) { // 附件列表初始化isRead 是否只讀 obj爲舊附件的對象
upload.isRead = isRead;
if(obj&& obj.length>0){ // 是否有數據傳遞
upload.oldFiles = obj; // 新舊數組的操作
upload._isShowFileTitle();
for (var i = 0, e; i < obj.length; i++) {
e = obj[i];
upload._innerHTML(e.name, e.id);
}
}
var uploadBtn = $("#uploadBtn")[0]; // 附件上傳按鈕
if (upload.isRead) {
if (uploadBtn) {
uploadBtn.innerHTML = "";
}
} else {
if (uploadBtn) {
var newtd =
'<td width="100px" align="left" valign="middle"><div class="grid"><label class="title">附件</label></div></td><td width="*" align="left" valign="middle"><input class="btn-upload" type="file" accept="*" name="upload" id="uploadfile" multiple /><a class="mui-btn mui-icon mui-icon-upload" style="margin: 10px;"></a></td><td width="40px"><div class="grid"><a class="mui-navigate-right"></a></div></td>'
uploadBtn.innerHTML = newtd;
}
var input = $("#uploadfile")[0];
input.onchange = function() {
readFile(this);
}
var readFile = function(obj) {
for (var i = 0; i < obj.files.length; i++) {
var path = obj.files[i],
fileName = path.name;
if (path.size < 1024 * 1024 * 50) {
upload.uploadFiles.push(path);
upload._innerHTML(path.name);
} else {
$.toast('單個附件大小不能超過50M');
}
}
input.value = "";
upload._isShowFileTitle();
}
}
},
_innerHTML(fileName, id) { // 附件列表動態輸出
var ul = ul = $('#selectList')[0];
var newli = doc.createElement('li');
var img = upload._getIcon(fileName);
newli.setAttribute("class", "mui-table-view-cell text-overflow");
newli.setAttribute("style", "height:44px;");
if (upload.isRead) { // 只讀沒有刪除按鈕
if (id) {
newli.innerHTML =
'<div class="mui-pull-left"><img src="../../images/download/' + img +
'" style="height:32px;padding-bottom:10px;"></img></div>' +
'<div class="mui-media-body"><div onclick="app.attach(\'' + id + '\');" class="mui-ellipsis">' +
fileName + '</div></div>';
} else {
newli.innerHTML =
'<div class="mui-pull-left"><img src="../../images/download/' + img +
'" style="height:32px;padding-bottom:10px;"></img></div>' +
'<div class="mui-media-body"><div class="mui-ellipsis">' +
fileName + '</div></div>';
}
} else {
if (id) {
newli.innerHTML =
'<div class="mui-pull-left"><img src="../../images/download/' + img +
'" style="height:32px;padding-bottom:10px;"></img></div><div class="mui-pull-right" onclick="upload._delElement(this,\'' +
fileName + '\')"><img src="../../images/task/delete.png" style="height:24px;"></img></div>' +
'<div class="mui-media-body"><div onclick="app.attach(\'' + id + '\');" class="mui-ellipsis">' +
fileName + '</div></div>';
} else {
newli.innerHTML =
'<div class="mui-pull-left"><img src="../../images/download/' + img +
'" style="height:32px;padding-bottom:10px;"></img></div><div class="mui-pull-right" onclick="upload._delElement(this,\'' +
fileName + '\')"><img src="../../images/task/delete.png" style="height:24px;"></img></div>' +
'<div class="mui-media-body"><div class="mui-ellipsis">' +
fileName + '</div></div>';
}
}
newli.setAttribute('fileName', fileName);
ul.appendChild(newli);
},
_getIcon(name) { // 附件圖標 如果沒有一一匹配的圖表,請默認一個圖片
var type = name.substr(name.lastIndexOf('.') + 1).toLowerCase(); //判斷文件類型
var img = "";
if ('png' != type && 'doc' != type && 'jpg' != type && 'pdf' != type && 'rar' != type && 'txt' != type && 'wav' !=
type && 'xls' != type && 'zip' != type) {
img = 'file-file.png';
} else {
img = 'file-' + type + '.png';
}
return img;
},
_delElement(obj, filename) { // 刪除附件
$.confirm('是否要刪除該附件?', '*****', ['確定', '取消'], function(e) {
if (e.index == 0) {
var ul = obj.parentNode.parentNode;
ul.removeChild(obj.parentNode); // 刪除節點
for (var i = 0; i < upload.uploadFiles.length; i++) {
var e = upload.uploadFiles[i];
if (e.name == filename) {
upload.uploadFiles.splice(i, 1);
upload._isShowFileTitle(); // 判斷是否顯示附件列表
$.toast('刪除成功');
return;
}
}
if (upload.oldFiles) {
for (var i = 0; i < upload.oldFiles.length; i++) {
var e = upload.oldFiles[i];
if (e.name == filename) {
upload.oldFiles.splice(i, 1);
upload._isShowFileTitle(); // 判斷是否顯示附件列表
// taskFeedbkEdit.updataFile(); // 獲取就舊數組傳值
$.toast('刪除成功');
return;
}
}
}
}
});
},
_isShowFileTitle() { // 添加頁是否顯示附加標題 && 編輯頁判斷數組是否爲空
var appendix = $("#appendix")[0];
if (appendix) {
if (upload.uploadFiles.length > 0 || upload.oldFiles.length > 0) {
appendix.style.display = "block";
} else {
appendix.style.display = "none";
}
} else {
var empty = doc.getElementById("empty");
if (upload.oldFiles.length == 0 && upload.uploadFiles.length == 0 && upload.isRead) {
var ul = doc.getElementById("selectList");
ul.innerHTML = "";
var m = doc.createElement("li");
m.id = "empty";
m.setAttribute("class", "mui-table-view");
m.setAttribute("style", "background-color: #FFF;padding: 10px;margin-top: 0;");
m.innerHTML = "暫無附件。";
ul.appendChild(m);
}
}
},
_getFormData(paramObj) { // 帶附件的ajax請求 將參數轉爲formData的形式
var formData = new FormData();
if (paramObj) {
for (var key in paramObj) {
if (typeof paramObj[key] == "object" && paramObj[key].length) {
var data = paramObj[key];
data.map(e => {
formData.append(key, e);
})
} else {
formData.append(key, paramObj[key])
}
}
}
// 用戶標識 默認上傳
var loginUser = app.getItem(app.localKey.loginUser),
isDD = app.getItem(app.localKey.isDD) || app.otherLogin;
var user = JSON.parse(localStorage.getItem("userInfo"));
if (loginUser) {
formData.append("loginUser", loginUser);
}
if (isDD) {
formData.append("isDD", isDD);
}
return formData;
},
}
})(document, mui, app)
3.3 引入和調用
3.3.1 index.html 關鍵代碼
<tr id="uploadBtn"></tr>
<div class="split_bar" id="appendix" style="display: none;">附件列表</div>
<ul id="selectList" class="mui-table-view"></ul>
<script src="../../js/mui.min.js"></script>
<script src="../../js/app.js"></script>
<script src="../../js/upload.js"></script>
<script src="../../js/index.js"></script>
3.3.2 index.js 文件代碼
① upload.js調用,附件初始化
upload.onReady(); //附件初始化,附件刪除
upload.onReady(false, obj); // 附件初始化,不可編輯,舊附件顯示
upload.onReady(true, rfat); // 附件初始化,可編輯,舊附件顯示
② ajax請求調用
var data = {};
for(var i = 0; i<upload.uploadFiles.length;i++){
var filePath = upload.uploadFiles[i];
console.log(filePath)
data["filedata" + i] = filePath;
}
var FormData = upload._getFormData(data);
app.ajaxFile({
url: "saveTask", // URL自行修改
data: FormData,
success: function(r) {
if (r == '1') {
$.toast("保存成功"); // mui.js組件
setTimeout(function() {
app.go();
}, 500);
} else {
mui.toast(r); // mui.js組件
}
app.closeWaiting(); // 全局app.js組件
}
});
3.4 參考樣式
app.css
.btn-upload{
opacity: 0;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 10;
}