使用element的upload組件實現一個完整的文件上傳功能(下)


  

 

作者:小土豆biubiubiu

博客園:www.cnblogs.com/HouJiao/

掘金:https://juejin.im/user/58c61b4361ff4b005d9e894d

簡書:https://www.jianshu.com/u/cb1c3884e6d5

微信公眾號:不知名寶藏程序媛

(關注"不知名寶藏程序媛"免費領取前端電子書籍。文章公眾號首發,關注公眾號第一時間獲取最新文章。)

碼字不易,點贊鼓勵喲~ 

  

  本篇文章是《使用element的upload組件實現一個完整的文件上傳功能(上)》的續篇。

  話不多說,接着上一篇直接開始

一.功能完善—保存表格中每一列的文件列表狀態

1.思路

  保存表格中每一列的文件列表狀態這個功能是什么意思呢,我們先看下前面示例的效果。

  

  在上面這個操作中,我們做了兩件事:

    1.給表格第一列的上傳了一個附件圖片

    2.點擊表格第二列、第三列、第四列的上傳按鈕,分別查看這三列的附件列表

   那么最后的結果發現后三列的附件列表展示的都是第一列的附件圖片,這個顯然不符合正常的邏輯。仔細去看看我們的代碼並且思考一下,也很快能知道這個問題出現的原因:我們給<el-upload>的file-list屬性綁定了attachList數據。attachList這個值初始是空數組,當我們點擊第一列的附件管理上傳一張圖片后,attachList數組就會增加一個元素。而所有上傳按鈕觸發打開的彈框組件是同一個(我們頁面中只有一個<el-upload>元素),而彈框組件綁定的文件列表數據attachList也是公用的,因此就會出現上面的情況。到這里也很容易能想到思路去解決這個問題:不同彈窗綁定的文件列表數據attachList分開保存。

  那這個辦法的言外之意就是需要在data中定義4個attachList,那定義四個數據,我們就得定義寫四個<el-dialog>分別去綁定這個四個數據。

  這個辦法到是能解決問題,但是假如我們的表格有100行數據呢,我們難道要定義100個attachList,在寫100個<el-dialog>嗎?這顯然就不現實了。

  然后我換了個思路:定義一個數組去保存不同的文件列表數據。這樣在每次點擊上傳按鈕時,將該列的文件列表數據賦值給另外一個數據currentAttachList,然后我們的<el-dialog>組件只需要綁定這個currentAttachList數據即可。這樣就省事多了。

  最后就是保存不同的文件列表數據的數組,這個要怎么定義呢。實際上也很簡單,我們可以將這個數據當做表格數據的一個屬性定義在tableData中。

currentAttachList: [],
tableData: [{
  date: '2016-05-02',
  name: '王小虎',
  address: '上海市普陀區金沙江路',
  attachList:[]
}, {
  date: '2016-05-04',
  name: '王小虎',
  address: '上海市普陀區金沙江路',
  attachList:[]
}, {
  date: '2016-05-01',
  name: '王小虎',
  address: '上海市普陀區金沙江路',
  attachList:[]
}, {
  date: '2016-05-03',
  name: '王小虎',
  address: '上海市普陀區金沙江路',
  attachList:[]
}]

  tableData中的attachList就是我們定義的文件列表數據

  數據定義好了之后,我們繼續下面的工作。

2.上傳按鈕的點擊事件修改

  根據前面我們寫的一大堆的思路,可以知曉當我們點擊【附件管理】按鈕時,需要做兩件事:

    1.獲取這一列表格數據中的附件列表,賦值給currentAttachList

    2.將控制彈框顯示的dialogVisible設置為true,讓彈框顯示  

  那我們之前寫的點擊【附件管理】按鈕的事件處理程序如下:

<el-button size='small' type="primary" @click="dialogVisible = true">
  上傳
  <i class="el-icon-upload el-icon--right"></i>
</el-button>          

  所以現在我們需要將點擊事件改為函數調用。

  在這之前呢,我們說了需要獲取上傳按鈕對應那一列的attachList數據,那我們如何知道當前點擊的上傳按鈕是屬於表格的第幾列呢?這個我們使用插槽就可以實現了。

<el-table-column
  prop="attach"
  label="附件管理"
  width="180">
  <template slot-scope="scope">
    <!-- 上傳按鈕綁定click事件 -->
    <el-button size='small' type="primary" @click="uploadBtnClick(scope.$index)">
      上傳
      <i class="el-icon-upload el-icon--right"></i>
    </el-button>          
  </template>
</el-table-column>

  uploadBtnClick函數實現:

uploadBtnClick (index){
  // 獲取上傳按鈕對應那一列表格數據中的附件列表,賦值給currentAttachList
  this.currentAttachList = this.tableData[index].attachList;
  // 將控制彈框顯示的dialogVisible設置為true,讓彈框顯示
  this.dialogVisible = true;
}

  這兩件事情完成后呢,記得將<el-dialog>的file-list綁定的數據改為currentAttachList。

  最后完整的App.vue組件代碼如下

<template>
  <div id="app">
    <!-- element-ui Table表格組件 -->
    <el-table
        class="my-table"
        :data="tableData"
        stripe
        style="width:725px;">
        <el-table-column
          prop="date"
          label="日期"
          width="180">
        </el-table-column>
        <el-table-column
          prop="name"
          label="姓名"
          width="180">
        </el-table-column>
        <el-table-column
          prop="address"
          label="地址"
          width="180">
        </el-table-column>
        <!-- 添加一列附件管理(文件上傳) -->
        <el-table-column
          prop="attach"
          label="附件管理"
          width="180">
          <template slot-scope="scope">
            <!-- 上傳按鈕綁定click事件 -->
            <el-button size='small' type="primary" @click="uploadBtnClick(scope.$index)">
              上傳
              <i class="el-icon-upload el-icon--right"></i>
            </el-button>          
          </template>
        </el-table-column>
      </el-table>
      <el-dialog
        title="附件管理"
        :visible.sync="dialogVisible"
        width="30%">
          <!-- 將<el-upload>代碼添加到<el-dialog>代碼塊中 -->
          <el-upload
            class="upload-demo"
            drag
            action="https://jsonplaceholder.typicode.com/posts/"
            :file-list="currentAttachList">
            <i class="el-icon-upload"></i>
            <div class="el-upload__text">將文件拖到此處,或<em>點擊上傳</em></div>
            <div class="el-upload__tip" slot="tip">只能上傳jpg/png文件,且不超過500kb</div>
          </el-upload>
        <span slot="footer" class="dialog-footer">
          <el-button @click="dialogVisible = false">取 消</el-button>
          <el-button type="primary" @click="dialogVisible = false">確 定</el-button>
        </span>
      </el-dialog>
  </div>
</template>

<script>
export default {
  name: 'App',
  data () {
    return {
      // 添加屬性,默認值為false,表示彈框不顯示
      dialogVisible: false,
      // 設置當前文件列表數據currentAttachList,每次用戶點擊上傳按鈕,該數據就會被賦值為當前按鈕那一列tableData中的attachList數據
      currentAttachList: [],
      tableData: [{
        date: '2016-05-02',
        name: '王小虎',
        address: '上海市普陀區金沙江路',
        attachList:[]
      }, {
        date: '2016-05-04',
        name: '王小虎',
        address: '上海市普陀區金沙江路',
        attachList:[]
      }, {
        date: '2016-05-01',
        name: '王小虎',
        address: '上海市普陀區金沙江路',
        attachList:[]
      }, {
        date: '2016-05-03',
        name: '王小虎',
        address: '上海市普陀區金沙江路',
        attachList:[]
      }]
    } 
  },
  methods: {
    uploadBtnClick (index){
      // 獲取上傳按鈕對應那一列表格數據中的附件列表,賦值給currentAttachList
      this.currentAttachList = this.tableData[index].attachList;
      // 將控制彈框顯示的dialogVisible設置為true,讓彈框顯示
      this.dialogVisible = true;
    }
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  margin: 50px 30px;
  text-align: center;
}
#app .el-dialog__header{
  background:#EBEEF5;
  border-bottom: 1px solid#EBEEF5;
}
#app .el-dialog{
  text-align: left;
}
#app .el-upload,#app .el-upload .el-upload-dragger{
  width: 100%;
}
</style>
<style scoped>
#app .my-table{
  display: inline-block;
  border: 1px solid #EBEEF5;
}
</style>

  在瀏覽器中看下效果:

  

  仔細的看幾遍這個效果,描述一下我們的操作和結果:

    1.在第四列上傳了一張圖片,完成之后關閉彈窗

    2.點擊第三列的上傳按鈕,獲取tableData中第三列的attachList賦值給currentAttachList,此時第三列的attachList為空,所以currentAttachList也是空,所以第三列的附件列表展示為空,正常。(可以看到第三列的彈框點開后文件列表是一個從無到有的動畫,是因為第四列上傳了一個圖片,currentAttachList包含一個元素,當點擊第三列的上傳按鈕時將currentAttachList賦值為空,而element-ui提供的控件是包含動畫的,所以就有了這個視覺上不太好的效果)

    然而,我們最后還有一個操作:在查看完第三列的文件列表后,在返回點擊第四列的附件管理按鈕,查看第一個操作上傳的文件列表。最后這個操作,我們驚奇的發現前面上傳在第四列的文件列表丟了。

  這個問題也比較好理解:上傳完成后,需要將上傳成功的文件信息保存到對應的那一列的attachList數組中。前面寫的代碼,我們只讀取了tableData中的attachList,在上傳成功以后卻沒有將文件的信息保存到attachList里面,那么每次重新點擊【附件管理】按鈕,從tableData獲取的attachList永遠是空,在賦值給currentAttachList,文件列表就什么也不會展示。現在我們可以接着修改代碼了,需要修改的內容如下:

1.<el-upload>添加on-success鈎子函數,當上傳成功將本次上傳的文件信息push到對應tableData.attachList

2.添加methods:uploadSuccess

關於這個uploadSuccess函數,它需要將上傳成功的文件信息保存到對應的tableData.attachList,那我們就需要知道當前是一列的按鈕觸發的彈框。這個問題就是之前在uploadBtnClick函數傳遞的參數index,所以我們需要將這個index保存到vue的數據屬性上,這樣在uploadSuccess函數中也能用上。

data () {
    return {
          //當前點擊打開彈框的按鈕在表格中是那一列
      currentIndex: 0,
    }
}

  uploadBtnclick方法需要新增加下面的代碼

uploadBtnClick (index){
  // 設置currentIndex
  this.currentIndex = index;
},

  uploadSuccess實現

 uploadSuccess(response, file, fileList){
   var currentIndex = this.currentIndex;
   this.tableData[currentIndex].attachList.push({
     'name':file.name
   });
 }

  最終完整的App.vue代碼如下

<template>
  <div id="app">
    <!-- element-ui Table表格組件 -->
    <el-table
        class="my-table"
        :data="tableData"
        stripe
        style="width:725px;">
        <el-table-column
          prop="date"
          label="日期"
          width="180">
        </el-table-column>
        <el-table-column
          prop="name"
          label="姓名"
          width="180">
        </el-table-column>
        <el-table-column
          prop="address"
          label="地址"
          width="180">
        </el-table-column>
        <!-- 添加一列附件管理(文件上傳) -->
        <el-table-column
          prop="attach"
          label="附件管理"
          width="180">
          <template slot-scope="scope">
            <!-- 上傳按鈕綁定click事件 -->
            <el-button size='small' type="primary" @click="uploadBtnClick(scope.$index)">
              上傳
              <i class="el-icon-upload el-icon--right"></i>
            </el-button>          
          </template>
        </el-table-column>
      </el-table>
      <el-dialog
        title="附件管理"
        :visible.sync="dialogVisible"
        width="30%">
          <!-- 將<el-upload>代碼添加到<el-dialog>代碼塊中 -->
          <el-upload
            class="upload-demo"
            drag
            action="https://jsonplaceholder.typicode.com/posts/"
            :file-list="currentAttachList"
            :on-success="uploadSuccess">
            <i class="el-icon-upload"></i>
            <div class="el-upload__text">將文件拖到此處,或<em>點擊上傳</em></div>
            <div class="el-upload__tip" slot="tip">只能上傳jpg/png文件,且不超過500kb</div>
          </el-upload>
        <span slot="footer" class="dialog-footer">
          <el-button @click="dialogVisible = false">取 消</el-button>
          <el-button type="primary" @click="dialogVisible = false">確 定</el-button>
        </span>
      </el-dialog>
  </div>
</template>

<script>
export default {
  name: 'App',
  data () {
    return {
      // 添加屬性,默認值為false,表示彈框不顯示
      dialogVisible: false,
     // 設置當前文件列表數據currentAttachList,每次用戶點擊上傳按鈕,該數據就會被賦值為當前按鈕那一列tableData中的attachList數據
      currentAttachList: [],
      //當前點擊打開彈框的按鈕是那一列
      currentIndex: 0,
      tableData: [{
        date: '2016-05-02',
        name: '王小虎',
        address: '上海市普陀區金沙江路',
        attachList:[]
      }, {
        date: '2016-05-04',
        name: '王小虎',
        address: '上海市普陀區金沙江路',
        attachList:[]
      }, {
        date: '2016-05-01',
        name: '王小虎',
        address: '上海市普陀區金沙江路',
        attachList:[]
      }, {
        date: '2016-05-03',
        name: '王小虎',
        address: '上海市普陀區金沙江路',
        attachList:[]
      }]
    } 
  },
  methods: {
    uploadBtnClick (index){
      // 獲取上傳按鈕對應那一列表格數據中的附件列表,賦值給currentAttachList
      this.currentAttachList = this.tableData[index].attachList;
      // 將控制彈框顯示的dialogVisible設置為true,讓彈框顯示
      this.dialogVisible = true;
      // 設置currentIndex
      this.currentIndex = index;
    },
    uploadSuccess(response, file, fileList){
      var currentIndex = this.currentIndex;
      this.tableData[currentIndex].attachList.push({
        'name':file.name
      });
    }
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  margin: 50px 30px;
  text-align: center;
}
#app .el-dialog__header{
  background:#EBEEF5;
  border-bottom: 1px solid#EBEEF5;
}
#app .el-dialog{
  text-align: left;
}
#app .el-upload,#app .el-upload .el-upload-dragger{
  width: 100%;
}
</style>
<style scoped>
#app .my-table{
  display: inline-block;
  border: 1px solid #EBEEF5;
}
</style>

  瀏覽器查看結果:

  

  可以發現,前面的問題已經被我們成功解決。

3.動畫刪除

現在呢,這個功能已經實現了。唯一不好的視覺效果就是文件列表的那個動畫,我們將這個動畫刪除。

<style>
#app .el-upload-list li{
  transition: none;
}

動畫刪除后的效果

   

  可以看到,現在的效果就正常了。

 

三.功能完善—刪除附件

  刪除文件這個功能,<el-upload>組件本身是支持的,只是在我們這種多文件上傳的情況中,還需要添加一下代碼。

  我們先看一下組件本身提供的這個刪除功能

  

  我們的操作順序和結果如下

    ①點擊第四列上傳按鈕

    ②成功上傳兩張圖片

    ③刪除第二張圖片

    ④關閉彈窗,查看第四列文件列表,文件列表顯示正常

    ⑤點擊第三列上傳按鈕,文件列表為空顯示正常

    ⑥在點擊第四列的上傳按鈕,發現前面刪除的那張圖片依然顯示在文件列表中。

  關於我們刪除第四列的一張圖片后,在第⑥步點擊查看發現圖片依然存在的這個問題很好解釋,我們可以回頭看一下uploadBtnclick函數的邏輯:

    每次點擊上傳按鈕,將對應的tableData.attachList賦值給currentAttachList。

    而我們刪除的時候,並沒有刪除對應的tableData.attachList中的數據,所以給currentAttachList的賦值操作導致文件列表展示的依然是之前的數據。

但是這里有一個疑惑的點就是第④個步驟:關閉彈窗,查看第四列文件列表,文件列表顯示正常。這個就比較奇怪了,刪除第二張圖片后,按照前面我們梳理的uploadBtnclick函數的邏輯,此時文件列表應該還是會包含刪除的那個文件。

關於這個問題,我們在uploadBtnClick函數中添加一些打印信息:

uploadBtnClick (index){
    console.log('uploadBtnClick');
    console.log("this.tableData[index].attachList");
    console.log(this.tableData[index].attachList);
    // 獲取上傳按鈕對應那一列表格數據中的附件列表,賦值給currentAttachList
    this.currentAttachList = this.tableData[index].attachList;
    console.log("this.currentAttachList");
    console.log(this.currentAttachList);
    // 將控制彈框顯示的dialogVisible設置為true,讓彈框顯示
    this.dialogVisible = true;
    // 設置currentIndex
    this.currentIndex = index;
},

  然后截圖看一下步驟④中的打印信息:

  

   可以看到,<el-upload>的file-list綁定的currentAttachList是包含兩個元素(其中包含第③步刪除的那個),但是文件列表卻只顯示了一個。

  enmmmmmm,這個地方比較費解。

  我們先處理第六個步驟中刪除出現的異常顯示。根據我們前面的梳理的邏輯,需要做兩個修改

  methods添加handleRemove函數處理刪除數據的功能

  <el-upload>添加on-remove鈎子函數調用handleRemove

1.<el-upload>添加on-remove鈎子函數調用handleRemove

 <el-upload
  class="upload-demo"
  drag
  action="https://jsonplaceholder.typicode.com/posts/"
  :on-remove="handleRemove"
  :file-list="currentAttachList"
  :on-success="uploadSuccess">
  <i class="el-icon-upload"></i>
    <div class="el-upload__text">將文件拖到此處,或<em>點擊上傳</em></div>
  <div class="el-upload__tip" slot="tip">只能上傳jpg/png文件,且不超過500kb</div>
</el-upload>

2.methods添加handleRemove函數處理刪除數據的功能

  關於handleRemove函數要實現的功能,前面我們已經講過:當刪除一張圖片后,刪除對應的tableData.attachList中的數據。

  關於這個功能有兩個辦法可以實現:

    ①遍歷tableData.attachList中的文件信息,將需要刪除的文件刪除。

    ②on-remove鈎子函數在調用時有兩個參數:file和fileList。

       file就是我們當前操作的文件,對於刪除操作,file就是當前刪除文件的信息;

         fileList是操作完成后<el-upload>控件的的所有文件列表。

  因此,可直接將fileList賦值給tableData.attachList。

  我們分別使用兩種辦法去實現。

  方法一:遍歷tableData.attachList中的文件信息,將需要刪除的文件刪除

handleRemove(file, fileList){
  var currentIndex = this.currentIndex;
  var attachList = this.tableData[currentIndex].attachList;
  var tempList = [];
  for(var i = 0; i<attachList.length; i++){
    if(file.name != attachList[i].name){
      tempList.push(attachList[i]);
    }
  }
  this.tableData[currentIndex].attachList = tempList;
}

  方法二:直接將on-remove鈎子函數的參數fileList賦值給tableData.attachList

handleRemove(file, fileList){
    var currentIndex = this.currentIndex;
    this.tableData[currentIndex].attachList = fileList;
}

  可以任意選擇一種實現,效果均相同

  

四.功能完善—驗證文件名是否重復

  element的多文件上傳控件對重復的文件名並沒有任何限制。

  

  這個也不符合我們實際的開發場景。因此我們需要完善這個功能。

  查看element文檔,我們可以看到一個before-upload鈎子函數

  

   因此我們可以給<el-upload>控件添加before-upload鈎子函數,在上傳文件之前去判斷文件是否重名,若有重名則阻止上傳。

1.給<el-upload>控件添加before-upload鈎子函數

 

 <!-- 將<el-upload>代碼添加到<el-dialog>代碼塊中 -->
<el-upload
   class="upload-demo"
   drag
   action="https://jsonplaceholder.typicode.com/posts/"
   :on-remove="handleRemove"
   :file-list="currentAttachList"
   :on-success="uploadSuccess"
   :before-upload="beforeUpload">
  <i class="el-icon-upload"></i>
  <div class="el-upload__text">將文件拖到此處,或<em>點擊上傳</em></div>
  <div class="el-upload__tip" slot="tip">只能上傳jpg/png文件,且不超過500kb</div>
</el-upload>

 

2.methods定義beforeUpload函數

beforeUpload(file){
  var currentIndex = this.currentIndex;
  //首先需要獲取當前已經上傳的文件列表
  var list = this.tableData[currentIndex].attachList;
  //循環文件列表判斷是否有重復的文件
  for(var i = 0;i<list.length;i++){
    if(list[i].name == file.name){
      this.$message.error(file.name + '文件名重復');
      //記得一定要返回false,否則控件繼續會執行上傳操作
      return false;
    }
  }
}

  現在看下效果:

  

  可以看到當文件名稱重復時,會有一個錯誤提示並且成功阻止了這個重復文件的上傳。

  然而,當我們在此查看文件列表時,發現之前存在的文件在列表中丟失了。

  這個原因是為啥呢?因為當bfeore-upload返回false之后,該組件會默認執行before-remove和on-remove這個兩個鈎子函數,我們在使用這個控件的時候只添加了on-remove這個鈎子函數,為了證實這個默認行為,我們把before-remove這個鈎子函數加上,並且在添加一些打印信息。

  添加before-remove鈎子函數

<el-upload
  class="upload-demo"
  drag
  action="https://jsonplaceholder.typicode.com/posts/"
  :on-remove="handleRemove"
  :before-remove="beforeRemove"
  :file-list="currentAttachList"
  :on-success="uploadSuccess"
  :before-upload="beforeUpload">
  <i class="el-icon-upload"></i>
  <div class="el-upload__text">將文件拖到此處,或<em>點擊上傳</em></div>
  <div class="el-upload__tip" slot="tip">只能上傳jpg/png文件,且不超過500kb</div>
</el-upload>

  methods添加beforeRemove和一些打印信息

beforeRemove(file, fileList){
  console.log('我是before-remove鈎子函數,我被調用了');
},
 handleRemove(file, fileList){
   console.log('我是on-remove鈎子函數,我被調用了');
   var currentIndex = this.currentIndex;
   var attachList = this.tableData[currentIndex].attachList;
   var tempList = [];
   for(var i = 0; i<attachList.length; i++){
     if(file.name != attachList[i].name){
       tempList.push(attachList[i]);
     }
   }
   this.tableData[currentIndex].attachList = tempList;
 },

 

  然后就剛剛上傳重復文件的那個操作我們看下打印信息

  

 

  可以看到我們前面的說法已經被證實了。

  同時,在深入一步思考一下,因為阻止上傳重復的文件名,導致on-remove鈎子函數被調用刪除了對應tableData.attachList中的數據,所以當我們在此點擊查看文件列表時【皇阿瑪問號.jpg】已經不存在了。

  那如何解決這個問題呢?

  首先我們先將before-remove這個鈎子函數完善一下:刪除前給用戶提示確認是否刪除

beforeRemove(file, fileList){
  return this.$confirm('此操作將永久刪除' + file.name +'文件, 是否繼續?');
},

  然后,解決這個問題的關鍵是:當before-upload返回false后,不執行before-remove和on-remove這兩個鈎子函數里面的邏輯。

  那我們看一下關於before-remove鈎子函數的文檔

  

  可以看到,該鈎子函數可以通過返回false停止刪除,即可以阻止on-remove函數的調用。

  所以我們將思路轉到before-remove函數,只要能在before-remove里面做出一些判斷,在上傳重復的文件后使函數返回false。

  那么現在需要做的就是在before-upload中得知上傳了重復文件后,設置isRepeat標志值為true,在before-remove判斷如果isRepeat這個標志值為true,就令該鈎子函數返回false阻止on-remove函數的調用。

data () {
  return {
    //是否包含重復的文件名稱,默認不包含值為false
    isRepeat: false 
  } 
}, 
methods: {
  beforeRemove(file, fileList){
    if(this.isRepeat == false){
      return this.$confirm('此操作將永久刪除' + file.name +'文件, 是否繼續?');
    }else{
      // 這個邏輯表示包含重復的文件,這按照文檔返回false可阻止文件繼續上傳
      return false;
    }
  },
  beforeUpload(file){
    var currentIndex = this.currentIndex;
    //首先需要獲取當前已經上傳的文件列表
    var list = this.tableData[currentIndex].attachList;
    //循環文件列表判斷是否有重復的文件
    for(var i = 0;i<list.length;i++){
      if(list[i].name == file.name){
        this.$message.error(file.name + '文件名重復');
        //添加邏輯:得知上傳了重復文件后,設置一個標志值為true,提供給beforeRemove函數使用
        this.isRepeat = true;
        //記得一定要返回false,否則控件繼續會執行上傳操作
        return false;
      }
    }
  }
}

  然后我們看下效果:

  

  這個結果看到之后有些吐血。

  雖然當有重復文件上傳時有了錯誤提示,但是這個重復發文件名卻展示在了文件列表中。查看了打印信息,發現並沒有調用on-remove鈎子函數。

  (這個文件上傳控件這么雞肋嗎?還是我用法有誤?)

  沒辦法,也不知道啥原因,我只能在想想辦法。

  在轉了轉腦子,於是想嘗試把before-remove中 else{ return false;}邏輯刪除,這樣當文件名稱重復后,會自動調用on-remove鈎子函數,我們把對isRepeat數據的判斷加在on-remove鈎子函數中去阻止刪除操作。

beforeRemove(file, fileList){
  if(this.isRepeat == false){
    return this.$confirm('此操作將永久刪除' + file.name +'文件, 是否繼續?');
  }
},
handleRemove(file, fileList){
  console.log('我是on-remove鈎子函數,我被調用了');
  if(this.isRepeat == false){
    var currentIndex = this.currentIndex;
    var attachList = this.tableData[currentIndex].attachList;
    var tempList = [];
    for(var i = 0; i<attachList.length; i++){
      if(file.name != attachList[i].name){
        tempList.push(attachList[i]);
      }
    }
    this.tableData[currentIndex].attachList = tempList;
  }
 },  

  再看下效果:

  

  現在看起來這個效果是正常了,重復的文件沒有上傳也沒有展示到文件列表中,在此點擊查看也顯示正常。

  但是呢,還有最后一個問題,保證是最后一個問題了:

    因為當文件重復后,isRepeat設置為了true,之后在沒有地方修改這個數據,那么一個重復圖片上傳后,我們操作刪除文件,此時isRepeat設置為true,按照邏輯before-remove中的刪除提示不會執行,on-remove中的刪除邏輯也不會執行。

  所以呢,我們還需要在上傳重復圖片后,將this.isRepeat還原為false,那么我們將代碼添加到on-remove函數中即可。

handleRemove(file, fileList){
  console.log('我是on-remove鈎子函數,我被調用了');
  if(this.isRepeat == false){
    var currentIndex = this.currentIndex;
    var attachList = this.tableData[currentIndex].attachList;
    var tempList = [];
    for(var i = 0; i<attachList.length; i++){
      if(file.name != attachList[i].name){
        tempList.push(attachList[i]);
      }
    }
    this.tableData[currentIndex].attachList = tempList;
  }else{
    this.isRepeat = false;
  }
 },  

  最后我們將打印信息刪除,貼上完整的代碼

src/App.vue

<template>
  <div id="app">
    <!-- element-ui Table表格組件 -->
    <el-table
        class="my-table"
        :data="tableData"
        stripe
        style="width:725px;">
        <el-table-column
          prop="date"
          label="日期"
          width="180">
        </el-table-column>
        <el-table-column
          prop="name"
          label="姓名"
          width="180">
        </el-table-column>
        <el-table-column
          prop="address"
          label="地址"
          width="180">
        </el-table-column>
        <!-- 添加一列附件管理(文件上傳) -->
        <el-table-column
          prop="attach"
          label="附件管理"
          width="180">
          <template slot-scope="scope">
            <!-- 上傳按鈕綁定click事件 -->
            <el-button size='small' type="primary" @click="uploadBtnClick(scope.$index)">
              上傳
              <i class="el-icon-upload el-icon--right"></i>
            </el-button>          
          </template>
        </el-table-column>
      </el-table>
      <el-dialog
        title="附件管理"
        :visible.sync="dialogVisible"
        width="30%">
          <!-- 將<el-upload>代碼添加到<el-dialog>代碼塊中 -->
          <el-upload
            class="upload-demo"
            drag
            action="https://jsonplaceholder.typicode.com/posts/"
            :on-remove="handleRemove"
            :before-remove="beforeRemove"
            :file-list="currentAttachList"
            :on-success="uploadSuccess"
            :before-upload="beforeUpload">
            <i class="el-icon-upload"></i>
            <div class="el-upload__text">將文件拖到此處,或<em>點擊上傳</em></div>
            <div class="el-upload__tip" slot="tip">只能上傳jpg/png文件,且不超過500kb</div>
          </el-upload>
        <span slot="footer" class="dialog-footer">
          <el-button @click="dialogVisible = false">取 消</el-button>
          <el-button type="primary" @click="dialogVisible = false">確 定</el-button>
        </span>
      </el-dialog>
  </div>
</template>

<script>
export default {
  name: 'App',
  data () {
    return {
      // 添加屬性,默認值為false,表示彈框不顯示
      dialogVisible: false,
     // 設置當前文件列表數據currentAttachList,每次用戶點擊上傳按鈕,該數據就會被賦值為當前按鈕那一列tableData中的attachList數據
      currentAttachList: [],
      //當前點擊打開彈框的按鈕在表格中是那一列
      currentIndex: 0,
      //是否包含重復的文件名稱,默認不包含值為false
      isRepeat: false,
      tableData: [{
        date: '2016-05-02',
        name: '王小虎',
        address: '上海市普陀區金沙江路',
        attachList:[]
      }, {
        date: '2016-05-04',
        name: '王小虎',
        address: '上海市普陀區金沙江路',
        attachList:[]
      }, {
        date: '2016-05-01',
        name: '王小虎',
        address: '上海市普陀區金沙江路',
        attachList:[]
      }, {
        date: '2016-05-03',
        name: '王小虎',
        address: '上海市普陀區金沙江路',
        attachList:[]
      }]
    } 
  },
  methods: {
    uploadBtnClick (index){
      // 獲取上傳按鈕對應那一列表格數據中的附件列表,賦值給currentAttachList
      this.currentAttachList = this.tableData[index].attachList;
      // 將控制彈框顯示的dialogVisible設置為true,讓彈框顯示
      this.dialogVisible = true;
      // 設置currentIndex
      this.currentIndex = index;
    },
    uploadSuccess(response, file, fileList){
      var currentIndex = this.currentIndex;
      this.tableData[currentIndex].attachList.push({
        'name':file.name
      });
    },
    beforeRemove(file, fileList){
      if(this.isRepeat == false){
        return this.$confirm('此操作將永久刪除' + file.name +'文件, 是否繼續?');
      }
    },
    handleRemove(file, fileList){
      if(this.isRepeat == false){
        var currentIndex = this.currentIndex;
        var attachList = this.tableData[currentIndex].attachList;
        var tempList = [];
        for(var i = 0; i<attachList.length; i++){
          if(file.name != attachList[i].name){
            tempList.push(attachList[i]);
          }
        }
        this.tableData[currentIndex].attachList = tempList;
      }else{
        this.isRepeat = false;
      }
    },
    beforeUpload(file){
      var currentIndex = this.currentIndex;
      //首先需要獲取當前已經上傳的文件列表
      var list = this.tableData[currentIndex].attachList;
      //循環文件列表判斷是否有重復的文件
      for(var i = 0;i<list.length;i++){
        if(list[i].name == file.name){
          this.$message.error(file.name + '文件名重復');
          //添加邏輯:得知上傳了重復文件后,設置一個標志值為true,提供給beforeRemove函數使用
          this.isRepeat = true;
          //記得一定要返回false,否則控件繼續會執行上傳操作
          return false;
        }
      }
    }
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  margin: 50px 30px;
  text-align: center;
}
#app .el-dialog__header{
  background:#EBEEF5;
  border-bottom: 1px solid#EBEEF5;
}
#app .el-dialog{
  text-align: left;
}
#app .el-upload,#app .el-upload .el-upload-dragger{
  width: 100%;
}
#app .el-upload-list li{
  transition: none;
}
</style>
<style scoped>
#app .my-table{
  display: inline-block;
  border: 1px solid #EBEEF5;
}
</style>

 

  最后在來操作一波

  

   

五.總結

  到此,《使用element的upload組件實現一個完整的文件上傳功能》完成,該功能是結合前段時間在實際項目開發中做的一個功能,在這里單獨拿出來總結。

  在整個實踐過程中,個人感覺element的upload組件,對多文件的上傳功能還是不太友好,兩個至今還沒有探究明白的問題,在文中也以紅色字體標出。

  這兩個問題雖然看着不影響什么,但心里總是有些不踏實。

 

 

作者:小土豆biubiubiu

博客園:www.cnblogs.com/HouJiao/

掘金:https://juejin.im/user/58c61b4361ff4b005d9e894d

簡書:https://www.jianshu.com/u/cb1c3884e6d5

微信公眾號:不知名寶藏程序媛

(關注"不知名寶藏程序媛"免費領取前端電子書籍。文章公眾號首發,關注公眾號第一時間獲取最新文章。)

碼字不易,點贊鼓勵喲~

 

 


  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM