1. 程式人生 > >使用element的upload元件實現一個完整的檔案上傳功能(下)

使用element的upload元件實現一個完整的檔案上傳功能(下)

  

  本篇文章是《使用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元件,對多檔案的上傳功能還是不太友好,兩個至今還沒有探究明白的問題,在文中也以紅色字型標出。

  這兩個問題雖然看著不影響什麼,但心裡總是有些不踏實。