v-for动态渲染组件中element上传组件file-list变量问题

环境

vue 2.5.2 、 element-ui 2.13.0


遇到的问题

v-for动态渲染组件中element上传组件file-list变量问题_第1张图片


相关代码

html

<table v-for="(industryForm,index) in simulationTableArr" :key="index" cellspacing="0" cellpadding="0" class="simulationTable">
  <tr>
    <td :rowspan="industryForm.industryArr.length+1" style="width: 200px">{{industryForm.title}}td>
    <td style="height:0;border:0;width: 400px">td>
    <td :rowspan="industryForm.industryArr.length+1" class="table-upload">
      <fileUpload :fileUrl.sync="formData[industryForm.formFileName]">
        <i class="industry-upload-icon">i>上传
      fileUpload>
    td>
  tr>
  <tr v-for="(item,index2) in industryForm.industryArr" :key="'item'+index2">
    <td>
      <i class="industry-icon">i><span>{{item.name}}span>    
      <div @click="downTemp(item.downUrl)" class="industry-down">
        <i class="industry-down-icon">i><span>下载模板span>
      div>
    td>
  tr>
table>

js

showSimulation(value) {
  const temp = [STRUCTURE_SIMULATION, ELECTROMAGNETIC_SIMULATION, HOT_SIMULATION, FLUID_SIMULATION];
  let arr = [];
  value.forEach(e => {
    arr.push(temp[e]);
  });
  this.simulationTableArr = arr;
},

组件内部

<template>
  <el-upload class="file-upload-display"
             :action="fileUploadUrl"
             :headers="headers"
             :before-upload="beforeUpload"
             :on-remove="handleRemove"
             :on-success="handleSuccess"
             :on-change="handleChange"
             :file-list="initialFile">
    <slot></slot>
  </el-upload>
</template>

<script>
  import {createProjectUploadUrl} from "@/server/config"

  export default {
    name: "fileUpload",
    data() {
      return {
        headers: {
          "Authorization": "JWT " + this.$store.getters.token
        },
        fileUploadUrl: createProjectUploadUrl,
        initialFile: [],
      }
    },
    props: {
      fileUrl: {
        type: String,
        default: ''
      },
      limitSize: {
        type: Number,
        default: 50
      }
    },
    methods: {
      beforeUpload(file) {
        const isOverSize = file.size / 1024 / 1024 > this.limitSize;
        if (isOverSize) {
          this.error(`上传文件不能超过${this.limitSize}MB!`);
          return false
        }
      },
      handleRemove(file, fileList) {
        this.$emit('update:fileUrl', '');
      },
      handleSuccess(file, fileList) {
        console.log(file);
        this.$emit('update:fileUrl', file.data);
      },
      handleChange(file, fileList) {
        this.initialFile = fileList.slice(-1);
      }
    },
    watch:{
      'fileUrl':{
        handler:function (val,oldVal) {
          if (val) {
            this.initialFile = [
              {
                url: val,
                name: val.slice(val.lastIndexOf('/') + 1, val.lastIndexOf('_')) + val.slice(val.lastIndexOf('.'))
              }
            ]
          } else {
            this.initialFile = []
          }
        },
        immediate:true
      }
    }
  }
</script>

分析

  • 直接原因:element内部的file-list在vue的复用策略中,没有发生改变。内部initialFile已经改变了,file-list的值依旧没有改变。
  • 深层次原因:v-for的复用策略,在删除第一行的时候,vue在实际上的操作为删除第二行的内容,然后将第二行的内容赋值给第一行,达到复用目的。即使已经添加key值,但由于vue关于对象监听的bug,(原本解决这个bug是可以通过vue.$set),file-list没有监听到initFile已经改变了。在这里是element内部封装fileList的赋值监听,不能去修改(团队也不允许TAT。
    v-for动态渲染组件中element上传组件file-list变量问题_第2张图片

解决方案

思路就是将v-for的复用策略干掉。经过一系列尝试,最终方案。先将数据清空,等dom挂载上去的时候,再赋值数据渲染。东西都没了,看你怎么复用2333。

showSimulation(value) {
  const temp = [STRUCTURE_SIMULATION, ELECTROMAGNETIC_SIMULATION, HOT_SIMULATION, FLUID_SIMULATION];
  let arr = [];
  value.forEach(e => {
    arr.push(temp[e]);
  });
  this.simulationTableArr = [];
  this.$nextTick(() => {
    this.simulationTableArr = arr;
  });
},

后续补充

在充分了解v-for关于diff的算法后,我知道我错了。:key值不能轻易的用index来使用,v-for只要涉及到列表的变动,特别是删除和更换顺序等,绝对不能用index。因此改成如下即可解决,不需要那么大费周折
html

<table v-for="(industryForm,index) in simulationTableArr" :key="industryForm.id" cellspacing="0" cellpadding="0" class="simulationTable">
  <tr>
    <td :rowspan="industryForm.industryArr.length+1" style="width: 200px">{{industryForm.title}}td>
    <td style="height:0;border:0;width: 400px">td>
    <td :rowspan="industryForm.industryArr.length+1" class="table-upload">
      <fileUpload :fileUrl.sync="formData[industryForm.formFileName]">
        <i class="industry-upload-icon">i>上传
      fileUpload>
    td>
  tr>
  <tr v-for="(item,index2) in industryForm.industryArr" :key="'item'+index2">
    <td>
      <i class="industry-icon">i><span>{{item.name}}span>    
      <div @click="downTemp(item.downUrl)" class="industry-down">
        <i class="industry-down-icon">i><span>下载模板span>
      div>
    td>
  tr>
table>

你可能感兴趣的:(前端)