1.說明
空間第一篇技術總結,給我的ElementUI圖片上傳。最近被安排使用ElementUI+VUE2來開發一個PC的應聘申請界面,在寫上傳多個圖片的效果時,遇到許多問題,在這里記錄一下。
2.問題總覽
2.1 通過自定義方法上傳圖片
2.2 JavaScript Array的對象屬性
2.3 this.$refs使用
2.4 上傳、刪除圖片數據集合數據問題
2.5 this[] 類似java的反射賦值
2.6 通過http-request自定義方法上傳圖片
2.7 獲取當前文件在數組中的下標
2.8 對象自定義方法
2.9 隱藏圖片上傳框
2.10 視圖更新$set和強制更新視圖this.$forceUpdate()頁面
3.最終實現代碼
<!--標簽實現-->
<div class="div-title-two" ><i class="i-btx" ></i>身份證正面照</div>
<div>
<el-upload
action=""
class="upload-idCard"
list-type="picture-card"
:headers="{'Content-Type':'application/json'}"
:http-request="function(val){ customPictureUpload('idCard',val,1) }"
:file-list="idCardImageList"
ref="imgRef"
:on-remove="function(file ,raw){ handleRemovePic('idCard',file ,raw) }">
<i class="el-icon-plus"></i>
</el-upload>
</div>
//自定義上傳方法
customPictureUpload (name , obj ,limit) {
//校驗圖片格式、size
let flag = this.beforePictureUpload(obj.file);
if(!flag) return;
//因為有多個模塊上傳框,所以這兒寫成通過傳入類型動態判斷類型
//動態拼接ClassName; 即 .upload-idCardImageList
let className = '.upload-'+name;
//動態拼接數組名稱 即:idCardImageList
let arrayName = name+"ImageList";
//動態拼接ref引用 : idCardRef
let refName = name+"Ref"
//獲取當前上傳文件數組里當前文件的下標序號
let index = this.$refs[refName].uploadFiles.indexOf(this.$refs[refName].getFile(obj.file))
//判斷圖片是否已到達上限limit
var imgs = this.$refs[refName].uploadFiles.length;
limit = limit -1 ;
//如果達到上限,則隱藏上傳的框框
if(imgs == limit){
document.querySelector(className+" .el-upload--picture-card").style.display = 'none'
}
readerFile(obj.file).then(fileResult => {
//調用上傳圖片方法
uploadImg({
data: {
imgInfo: fileResult.base64,
token: sessionStorage.getItem('hr_apply_token'),
phone: this.ruleForm.phone
}
}).then(res=>{
if (+res['responseCode'] === 0) {
//給當前文件設置返回的res_path值
this.$refs[refName].uploadFiles[index].res_path =res.path
//判斷當前文件前面是否有還在上傳的文件,若有則等待
while(index == this[arrayName].length){
this[arrayName]= this.$refs[refName].uploadFiles
}
} else if(+res['responseCode'] === 5555){
this.$message({
showClose: true,
message: res['responseMsg'],
type: 'error'
});
this.$router.replace('/login');
} else{
this.$message({
showClose: true,
message: res['responseMsg'],
type: 'error'
});
document.querySelector(className+' .el-upload--picture-card').style.display = ''
}
})
})
},
//刪除方法
handleRemovePic(name, file ,fileList) {
//拼接calssName
let className = '.upload-'+name;
//拼接數組名字
let arrayName = name+'ImageList';
//將刪除后的fileList賦給數組
this[arrayName] = fileList
document.querySelector(className+' .el-upload--picture-card').style.display = ''
},
4.經驗總結
4.1 通過自定義方法上傳圖片
ElementUI默認是通過action去調用圖片上傳,但是在實際使用中action在傳數據方面比較麻煩,所以這兒可以通過 :http-request來自定義一個方法來上傳圖片。
4.2 JavaScript Array的對象屬性
splice:splice在js中是用來對數組進行刪除、替換操作,具體實現:
刪除:
index代表要刪除的數組的下標, num代表刪除的數量
this.idCardImageList.splice(index,num)
替換:
item*可以有多個,當item的值為null時,即可置空index下標的值
this.idCardImageList.splice(index,num,item*)
every:用於遍歷數組中元素是否都符合條件,若存在不符合元素則直接返回,不繼續往下遍歷
let picFlag = this.idCardImageList.every( item => {
if(item.res_path){
return true;
}else{
return false;
}
});
some: 與every正好相反,遍歷數組中元素是否符合條件,若存在則直接返回,不繼續往下遍歷
let picFlag = this.idCardImageList.some( item => {
if(item.res_path){
return true;
}else{
return false;
}
});
forEach:遍歷數組中的所有數據,並且會遍歷遍歷完
this.idCardImageList.forEach(item => {
item.validate((valid) => {
if (!valid) {
flagForm = false;
};;
})
});
indexOf: 獲取當前元素數組中的下標
this.idCardImageList.forEach(item => let index = this.idCardImageList.indexOf(item))
filter():過濾,即返回符合條件的所有元素數組
this.idCardImageList.filter(item => item.age >18 );
push:向數組的最后面插入一/多個元素,並返回新數組的長度
this.idCardImageList.push({age:18,idcard:'www.baidu.com'});
[].forEach.call():是一種快速的方法訪問forEach,並將空數組的this換成想要遍歷的list
這個方法的使用,拿到querySelectorAll時需要遍歷數組,具體看示例:
//將所有className為must的元素display樣式設置為 block
[].forEach.call(document.querySelectorAll(".must"), item => item.style.display="block" );
4.3 this.$refs使用
首先說明下ref
ref:被用來給元素或子組件注冊引用信息。引用信息將會注冊在父組件的$refs對象上。如果在普通Dom元素上使用,引用指向的就是Dom元素,如果用在自組件上,就引用指向組件實例。
在業務處理中,我需要拿到ElementUI封裝的uploadFiles對象,所以通過this.$refs.imgRef.uploadFiles,就能拿到實例中uploadFiles對象的數據然后賦值給我的this.imgList 就拿到了上傳圖片的數組。
4.4 上傳、刪除圖片數據集合數據問題
在處理中會出現以下情況
情景一:
連續上傳兩張圖片,第一張大,第二張小
問題:
小圖上傳成功並且大圖消失,然后過了幾秒大圖出現
情景二:
上傳一張大圖,並且迅速刪除一個已上傳成功的圖
問題:
已上傳的圖和正在上傳的大圖會同時被刪掉,並且過了一會兒大圖又重新加載出來。
情景三:
上傳一張大圖,並且迅速刪除正在上傳的圖
問題:
正在上傳的大圖被刪掉,並且過了一會兒大圖又重新加載出來。
this.imgList.push(file) 引起的錯亂
以上三個錯誤問題都是由於對 imgList賦值引起的,以情景一為例。
首先一個前提,上傳圖片是異步的。
我的處理邏輯是:
1、調用自定義上傳圖片方法
2、后台返回上傳圖片路徑,進行this.imgList.push(file)
但是這樣處理有一個問題,如果按情景一走就會出現
3、小圖上傳完成push進imgList,接着刷新視圖大圖消失
4、大圖上傳完,大圖push進imgList,這時大圖才顯示
請求前就push?
出現問題后,猜想既然是因為異步問題引起,那么在請求之前就把數據push這種方法可行嗎,接着按此操作結果:push之后視圖樣式就直接顯示上傳完成,猜想不可行。
拿到父組件的fileList集合
既然這條路走不同,那么把push代碼注釋掉再嘗試,發現不會出現圖片錯亂的問題。於是猜有一個對象存了當前正在上傳和已上傳的圖片的數據,查看源碼發現有一個fileList對象,結合4.3 ref的使用,通過this.$ref.imgList.uploadFiles拿到完成圖片上傳后的FileList的文件和狀態,不需要每次上傳成功才push,可以直接把將當前的fileList集合賦給imgList。
push前判斷一下
賦值給項目后,發現情景一情況並沒有解決,於是再仔細分析發現:大圖的位置還是被小圖占了,因為不管如何處理肯定是小圖先返回,那么就給當前的fileList與我的imgList的長度做一個判斷,如果長度相等了代表前面沒有待上傳的圖片,那么就不會把他的坑給占了。
//判斷當前文件前面是否有還在上傳的文件,若有則等待
while(index == this[arrayName].length){
this[arrayName]= this.$refs[refName].uploadFiles
}
4.5 this[] 類似java的反射賦值
這個方法是類似於java的反射實現對元素賦值取值,例如:this.imgList = this["imgList"], 有了這個就可以動態的設置進來的方法參數了。
4.6 通過http-request自定義方法上傳圖片
在整個方法http-request的方法執行中,on-change、on-progress、on-success等方法都會無效掉。
4.7 獲取當前文件在數組中的下標
let index = this.imgList.indexOf(img);
4.8 方法處理中自定義方法
這個的處理是在array數組上增加刪除的方法
Array.prototype.add = (item =>{
let index = this.indexOf(item)
this.splice(index,1)
return this;
})
4.9 隱藏圖片上傳框
有時圖片上傳會有個數限制,在達到上限后,上傳框框就應該隱藏掉,目前用的是最笨的辦法
document.querySelector(className+'.el-upload--picture-card').style.display = 'none'
有時間得去找找有沒有vue的屬性可以直接隱藏的。
4.10 視圖更新$set和強制更新視圖this.$forceUpdate()頁面
this.$forceUpdate():強制更新視圖
當自己設置的元素因為層次太深而無法更新時,可以通過調用this.$forceUpdate()來實現強制視圖更新
$set:如果對象是響應式的,確保屬性被創建后也是響應式的,同時觸發視圖更新。
this.$set(target, key, value) 等同於Vue.set( target, key, value )
{Object | Array} target 對象/數組
{string | number} key 設置的元素
{any} value 設置的值