<组件封装:Vue + elementUi 通过excel文件实现 “ 批量导入 ” 表单数据,生成对应新增信息 >

标题图片


👉 前言

在 Vue + elementUi 开发中,当某些新增表单数据过多时,为了提高新增数据的效率,往往会需要实现批量新增的功能。为此,我们就需要封装一个批量新增弹窗的功能组件,因实际场景需要,本次封装的批量导入组件,涵盖附件上传功能,组件仅以抛砖引玉! 需要按照实际应用场景进行调整!

👉 一、封装组件对应API及绑定事件

> Attributes

参数说明类型可选值默认值
url批量导入模板文件上传地址String-必填
title批量导入标题String-批量导入
uploadTableFileUrl模板导入列表数据中附件上传地址String-必填
restrictFileTypes模板文件可上传附件类型限制Array‘xlsx’, ‘xls’…文件类型[ ]
fileName模板文件流入参名称String-files
fileData模板上传参数DataObject-null
downFileName模板下载按钮文本String-下载
columnArray附件列表表头配置(需与后端沟通)Array-[ ]
tableTypeOpinion列表类型切换配置参数Array-[{label: ‘类型一’,value: ‘类型一’}…]
tableApi获取列表数据的Axios方法名称,需要按照应用场景封装的Axios请求方式修改组件内部代码String-必填
fileDeleteApi模板数据列表中的Axios方法名称,需要按照应用场景封装的Axios请求方式修改组件内部代码String-必填
uploadFileIdName列表附件上传入参时对应的ID名称String-id
rowFileIdName列表数据的ID名称,与uploadFileIdName 配合使用,uploadFileIdName 入的参数为rowFileIdName对应的值String-id
outSideDownFn父组件中对应的下载模板的方法名String-必填
outSideShowName外部引用子组件的显隐判断参数名String-showFileUp
showTableType控制是否显示列表类型切换功能Boolean-false
tableTypeName列表类型切换时,请求列表入参名String-tableType
importDataTips导入提示String-

> Event

方法名说明参数
tableTypeChange列表类型切换组件发生变更时触发change值
upLoadSuccess导入模板文件成功时触发文件上传接口返回res
getRow列表操作方法对应点击触发方法操作事件类型,与columnArray 配置中定义的配置相关
submit点击保存时触发请求参数

👉 二、实现案例

> HTML父组件模板

<!-- 批量导入 -->
<fileUploads
  ref="fileUploads"
  v-if="showFileUploads"
  outSideShowName="showFileUploads"
  title="批量导入核实数据"
  :downFileName="'已选中 ' + (multipleSelection.length || 0) + ' 条数据,点击下载模板'"
  fileName="excelFile"
  url="/disposal/modelOrder/batchCheck/importExcel"
  :fileData="{}"
  fileDeleteApi="knowledgeExcelImportDeleteFile"
  importDataTips="提示: 导入批量核实模板明细选项条数最多不能超过1000条"
  :columnArray="columnArray_file"
  :exportMark="'knowledge' + $route.query.itemIndex"
  uploadTableFileUrl="/disposal/modelOrder/batchCheck/uploadFile"
  tableApi="disposalModelOrderBatchCheckImportList"
  :restrictFileTypes="['xlsx', 'xls']"
  uploadFileIdName="id"
  outSideDownFn="fileModelDownFn"
  :showTableType="true"
  :tableTypeOpinion="[
    {
      label: '非缺陷',
      value: 1
    },
    {
      label: '缺陷',
      value: 2
    }
  ]"
  tableTypeName="tableType"
  @tableTypeChange="getColumnArray_batch"
  @upLoadSuccess="upLoadSuccess($event)"
  @getRow="getRow"
  @submit="fileUploadsSubmit"
/>

// 数据
columnArray: [
	{
	  prop: "problemCode",
	  label: "问题编号",
	  width: "150",
	},
	{
	  prop: "problemFindTime",
	  label: "问题发现时间",
	  width: "165",
	},
	{
	  prop: "problemDescription",
	  label: "问题具体描述",
	  width: "160",
	},
	{
	  prop: "problemExistOrgName",
	  label: "问题整改情况",
	  width: "140",
	},
	{
	  type: "files",
	  prop: "files",
	  label: "附件名称",
	  width: "140",
	  showOverflowTooltip: false,
	},
	{
	  type: "uploadFile",
	  prop: "files",
	  label: "上传附件",
	  width: "140",
	},
	{
	  type: "operate",
	  linkName: ["删除"],
	  label: "操作",
	  width: "100",
	},
],
// 方法
<script>
// 获取表格表头
async getColumnArray_batch(type, fn = () => {}) {
 const res = await this.getDictionaryHeader({
   code: 'batch-' + type,
   ssywy: this.paramsInfo.data.ywy
 })
 
 if (Array.isArray(res) && res.length !== 0) {
   this.columnArray_file = [
     ...res,
     // {
     //   type: "uploadFile",
     //   prop: "files",
     //   label: "上传附件",
     //   width: "140",
     // },
     {
       type: "operate",
       linkName: ["删除"],
       label: "操作",
       width: "100",
     },
   ]
   fn()
 } else {
   this.columnArray_file = []
   this.$message.warning('批量核实需上传列表表头获取失败,请重试!')
 }
 return false;
},
// 批量核实部分代码
// 批量导入 - 导入文件成功
upLoadSuccess(res) {
  this.$refs["fileUploads"].initTable({
    versionId: res.data,
    ssywy: this.paramsInfo.data.ywy
  });
},
// 批量导入表格操作
getRow(row, type) {
  if (type == "删除") {
    this.disposalModelOrderBatchCheckImportListDelete({
      knowledgeId: row.id,
    }).then(() => {
      // window.console.log(res)
      this.$message.success("删除成功!");
      this.$refs["fileUploads"].initTable();
    });
  } else if (type == "下载") {
    // this.downloadFile(row)
  }
},
// 批量导入提交
fileUploadsSubmit(params) {
  let _params = new URLSearchParams();
  for (let key in params) {
    _params.append(key, params[key]);
  }
  this.disposalModelOrderBatchCheckSubmit(_params)
    .then(() => {
      // window.console.log(res)
      this.$message.success("批量导入成功!");
      this.$refs["fileUploads"].closeDialog();
      this.getDocumentQuery();
    })
    .catch((err) => {
      this.$message.warning(err.message || "请求出错,请检查后重试!");
    });
},
</script>

> 子组件模板

<template>
  <div
    :class="
      $root.themeHomeChange === '1'
        ? 'fileUpload fileUpload_light'
        : 'fileUpload'
    "
  >
    <bda-dialog
      :visible="dialogVisible"
      :modal="false"
      :close-on-click-modal="false"
      @cancel="closeDialog"
      @close="closeDialog"
      :title="title"
      :modal-append-to-body="false"
    >
      <div class="dialogBox">
        <div class="header">
          <el-row
            style="font-size: 1.125rem; line-height: 30px; padding: 15px 15px 0;"
          >
            <el-col :span="12" class="upload-box">
              <span class="title">导入数据:</span>
              <el-upload
                ref="uploaddemo"
                class="upload-demo"
                :limit="1"
                :data="fileData"
                :name="fileName"
                :action="fileUrl"
                :multiple="false"
                :on-change="handleChange"
                :on-exceed="handleExceed"
                :before-upload="beforeAvatarUpload"
                :on-success="handleAvatarSuccess"
                :show-file-list="false"
                :file-list="fileList"
              >
                <el-button size="mini" style="margin-left:10px">上传</el-button>
              </el-upload>
              <span class="text" v-if="fileList.length">
                已上传:
                <span v-for="(item, index) in fileList" :key="index">
                  {{ item.name }}
                  <el-button
                    type="text"
                    @click="handleClick('remove', item, index)"
                    >删除</el-button
                  >
                </span>
              </span>
            </el-col>
            <el-col :span="12">
              <span class="title">模板下载:</span>
              <el-button
                size="mini"
                :loading="exportLoading"
                @click="handleClick('down')"
              >{{ downFileName }}</el-button>
            </el-col>
          </el-row>
          <el-row
            style="font-size: 1.125rem; line-height: 30px; padding: 15px 15px 0;"
          >
            <span class="title">导入结果:</span>
            <strong v-if="importData !== 0">导入成功,导入数据{{ importData }}条</strong>
            <strong v-else>暂无导入文件,请先导入文件!</strong>
            <p v-if="!!importDataTips" style="margin: 8px 0 0 0; color: #fc6271; font-weight: bold;">{{  importDataTips }}</p>
          </el-row>
        </div>
        <div class="line"></div>
        <div class="tableHeader_1">
          <span class="name">需上传附件列表</span>
          <el-radio-group
            v-model="params[tableTypeName]"
            v-if="showTableType"
            @change="tableTypeChange"
          >
            <el-radio-button
              v-for="(item, index) in tableTypeOpinion"
              :key="index"
              :label="item.value"
            >{{ item.label }}</el-radio-button>
          </el-radio-group>
        </div>
        <el-table
          v-loading="tableLoading"
          element-loading-text="拼命加载中"
          element-loading-spinner="el-icon-loading"
          element-loading-background="rgba(0, 0, 0, 0.8)"
          :data="tableData"
          row-key="knowledgeId"
          ref="table"
          tooltip-effect="light"
          height="300"
          popper-append-to-body
        >
          <el-table-column
            type="index"
            label="序号"
            width="70"
            align="center"
          ></el-table-column>
          <el-table-column
            v-for="(item, index) in columnArray"
            :show-overflow-tooltip="
              item.showOverflowTooltip == undefined
                ? true
                : item.showOverflowTooltip
            "
            :key="index"
            :prop="item.prop"
            :label="item.label"
            :min-width="item.width"
            align="center"
          >
            <template slot-scope="scope">
              <span v-if="item.type === 'operate'">
                <el-button
                  type="text"
                  v-for="item2 in item.linkName"
                  :key="item2"
                  @click="rowClick(scope.row, item2)"
                  >{{ item2 || "" }}</el-button
                >
              </span>
              <template v-else-if="item.type === 'files'">
                <el-popover
                  popper-class="popoverClass_light"
                  v-if="scope.row[item.prop].length !== 0"
                  placement="top-start"
                  width="200"
                  trigger="hover"
                >
                  <div
                    class="table_file"
                    v-for="(item_1, index_1) in scope.row[item.prop]"
                    :key="index_1"
                    @click="rowClick(item_1, '下载')"
                  >
                    {{ "附件 [" + (index_1 + 1) + "]: " + item_1.fileName }}
                  </div>
                  <p slot="reference" class="fileTitle">
                    {{ "附件 [1]: " + scope.row[item.prop][0].fileName }}
                  </p>
                </el-popover>
                <p v-else>暂无数据</p>
              </template>
              <template v-else-if="item.type === 'uploadFile'">
                <el-button @click="showUploadFileDialog(scope.row, scope.row[item.prop])" >{{ scope.row[item.prop].length !== 0 ? '修改已上传文件' : '选择文件' }}</el-button>
              </template>
              <span v-else>{{ scope.row[item.prop] }}</span>
            </template>
          </el-table-column>
        </el-table>
        <el-pagination
          :disabled="params.total == 0"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="params.current"
          :page-sizes="[10, 50, 100, 200]"
          :page-size="params.size"
          background
          layout="prev, pager, next, sizes, jumper, total"
          :total="params.total"
        ></el-pagination>
        <el-dialog
          title="附件操作"
          :modal="false"
          :visible.sync="innerVisible"
          custom-class="uploadFiles"
        >
          <el-input
            v-show="false"
            v-model="tableFileParams.fileTable.length">
          </el-input>
          <p style="font-size: 12.5px; line-height: 25px; margin-bottom: 10px;">
            (单个文件大小不能超过50M,仅支持 xls、xlsx、doc、docx、pdf 格式)
          </p>
          <el-upload
            class="upload-demo"
            name="files"
            ref="uploaddemo_table"
            :action="tableFileParams.fileUrl"
            :data="tableFileParams.data"
            :on-change="handleChange_table"
            :before-upload="beforeAvatarUpload_table"
            :on-success="handleAvatarSuccess_table"
            :file-list="tableFileParams.fileList"
          >
            <el-button
              size="small"
              plain
            >上传附件</el-button>
          </el-upload>
          <el-table
            :data="tableFileParams.fileTable"
            style="width: 100%; margin-top: 10px;"
            height="calc(170px)"
            v-loading="tableFileParams.fileTableLoading"
            tooltip-effect="light"
            element-loading-text="拼命加载中"
            element-loading-spinner="el-icon-loading"
            element-loading-background="rgba(0, 0, 0, 0.8)"
          >
            <el-table-column
              type="index"
              label="序号"
              width="60"
              align="center"
            ></el-table-column>
            <el-table-column
              prop="fileName"
              label="附件名称"
              min-width="120"
              align="center"
              show-overflow-tooltip>
            </el-table-column>
            <el-table-column
              prop="fileType"
              label="类型"
              width="120"
              align="center"
              show-overflow-tooltip>
            </el-table-column>
            <el-table-column
              label="操作"
              width="100"
              align="center"
            >
              <template slot-scope="scope">
                <el-button
                  type="text"
                  style="margin-left: 0;"
                  @click="deleteFile(scope.row)"
                > 删除 </el-button>
              </template>
            </el-table-column>
          </el-table>
          <span slot="footer" class="dialog-footer">
            <el-button @click="tableFileSubmit" type="primary">保 存</el-button>
            <bda-button bdatype="defualt" @click="closeFileDialog">关闭</bda-button>
          </span>
        </el-dialog>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="submit()" type="primary">保 存</el-button>
        <bda-button bdatype="defualt" @click="closeDialog">关闭</bda-button>
      </span>
    </bda-dialog>
  </div>
</template>

<script>
import Interface from "@/base/mixin/enterprise-manage-center";
import Interface_1 from "@/base/mixin/risk-control-center";
import Interface_2 from "@/base/mixin/disposal-center.js";
import { API_PATH } from "@/config";
export default {
  name: "historyDoc",
  mixins: [Interface, Interface_1, Interface_2],
  props: {
    // 导入模板附件Url
    url: {
      type: String,
      required:true,
      default: () => {
        return "";
      },
    },
    // 列表附件上传API名称
    uploadTableFileUrl: {
      type: String,
      required:true,
      default: () => {
        return "";
      },
    },
    // 可上传附件类型
    restrictFileTypes: {
      type: Array,
      default: () => {
        return [];
      },
    },
    // 附件上传的附件参数名
    fileName: {
      type: String,
      default: () => {
        return "files";
      },
    },
    // 附件上传参数
    fileData: {
      type: Object,
      default: () => {
        return null;
      },
    },
    // 标题
    title: {
      type: String,
      default: () => {
        return "批量导入";
      },
    },
    // 下载模板提示文字
    downFileName: {
      type: String,
      default: () => {
        return "下载";
      },
    },
    // 动态表头配置项
    columnArray: {
      type: Array,
      required:true,
      default: () => {
        return [];
      },
    },
    // 列表类型切换配置项
    tableTypeOpinion: {
      type: Array,
      required:false,
      default: () => {
        return [
          {
            label: '类型一',
            value: '类型一'
          },
          {
            label: '类型二',
            value: '类型二'
          }
        ];
      },
    },
    // 导出模板备注,用于区分多种模板时区分模板类型
    exportMark: {
      type: String,
      default: () => {
        return '';
      },
    },
    // 获取列表数据API名称
    tableApi: {
      type: String,
      required:true,
      default: () => {
        return '';
      },
    },
    // 附件删除API名称
    fileDeleteApi: {
      type: String,
      required:true,
      default: () => {
        return '';
      },
    },
    // 对应列表项的id
    uploadFileIdName: {
      type: String,
      default: () => {
        return 'id';
      },
    },
    // 列表项对应的附件ID名称,用于绑定附件操作等
    rowFileIdName: {
      type: String,
      default: () => {
        return 'id';
      },
    },
    // 外部模板下载方法(名称)
    outSideDownFn: {
      type: String,
      required:true,
      default: () => {
        return null;
      },
    },
    // 外部入参
    inParams: {
      type: Object,
      default: () => {
        return {};
      },
    },
    // 是否显示列表类型切换
    showTableType: {
      type: Boolean,
      default: () => {
        return false;
      },
    },
    // 导入提示
    importDataTips: {
      type: String,
      default: () => {
        return null;
      },
    },
    // 列表类型切换入参名称
    tableTypeName: {
      type: String,
      default: () => {
        return 'tableType';
      },
    },
    // 外部控制显隐的字段,用于重新触发组件加载
    outSideShowName: {
      type: String,
      default: () => {
        return 'showFileUp';
      },
    },
  },
  data() {
    return {
      exportLoading: false,
      tableLoading: false,
      importData: 0,
      dialogVisible: false,
      innerVisible: false,
      fileUrl: null,
      fileList: [],
      tableData: [],
      // tableType: null,
      in_Params: {},
      params: {
        current: 1,
        size: 10,
        total: 0,
      },
      tableFileParams: {
        fileTable: [],
        fileTableLoading: false,
        fileList: [],
        data: {},
        fileUrl: ''
      }
    };
  },
  created() {
    this.fileUrl = API_PATH + this.url;
    this.tableFileParams.fileUrl = API_PATH + this.uploadTableFileUrl;
    if(this.showTableType) {
      window.console.log(1231)
      this.params[this.tableTypeName] = this.tableTypeOpinion.length !== 0 ? this.tableTypeOpinion[0].value : null
    }
  },
  mounted() {},
  methods: {
    /**
     * @description:关闭弹框回调
     */
    closeDialog() {
      this.dialogVisible = false;
      this.$parent[this.outSideShowName] = false;
      this.initData()
    },
    // 初始化数据
    initData() {
      this.in_Params = {};
      this.tableData = []
      this.fileList = []
      this.importData = 0
      this.params = {
        current: 1,
        size: 10,
        total: 0,
      }
    },
    // 切换表格类型
    tableTypeChange() {
      this.$emit('tableTypeChange', this.params[this.tableTypeName])
      if(this.fileList.length !== 0) {
        this.initTable()
      }
    },
    // 展示附件操作弹窗 并 进行参数传递
    showUploadFileDialog(row, fileList) {
      this.tableFileParams.data = {
        ...this.in_Params,
        [this.uploadFileIdName]: row[this.rowFileIdName]
      }
      this.tableFileParams.fileTable = fileList.map(item => {
        return {
          ...item,
          fileType: item.fileType || (item.filePath && item.filePath.substring(item.filePath.lastIndexOf(".")+1))
        }
      })
      this.innerVisible = true
    },
    rowClick(row, op) {
      if (op === "删除") {
        this.$confirm("此操作将删除该数据, 是否继续?", "提示", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        }).then(() => {
          window.console.log(row, op)
          this.$emit("getRow", row, op);
        })
      } else {
        this.$emit("getRow", row, op);
      }
    },
    //文件上传成功钩子
    handleAvatarSuccess(res) {
      if (res.success) {
        this.$message.success("上传成功");
        this.$emit("upLoadSuccess", res);
      } else {
        this.$message.warning(
          "上传失败, 失败原因:" +
            ((res.message || res.code) ? (res.message || res.code) : "未知")
        );
        this.$refs.uploaddemo.clearFiles();
      }
    },
    //文件上传之前钩子
    beforeAvatarUpload(file) {
      window.console.log(file.name.split(".")[file.name.split(".").length - 1]);
      // 限制文件格式类型
      // const isDocx = file.name.split('.')[1] === 'docx'
      const flag = this.restrictFileTypes.some((item) => {
        return file.name.split(".")[file.name.split(".").length - 1] === item;
      });

      if (!flag && this.restrictFileTypes.length !== 0) {
        this.$message({
          message: "请上传指定类型的文件!",
          type: "error",
        });
        // this.$forceUpdate();
        return false;
      }
      // 限制文件上传大小
      const isLt10M = file.size / 1024 / 1024 < 128;
      if (!isLt10M) {
        this.$message.error("上传文件大小不能超过 128MB!");
        // this.$forceUpdate();
        return false;
      }
    },
    handleChange(file, fileList) {
      this.fileList = fileList.slice(-1);
    },
    // 提交表单
    submit() {
      if(this.fileList.length !== 0) {
        this.$emit('submit', this.in_Params)
      } else {
        this.$message.warning('请先导入对应模板的数据文件!')
      }
    },
    tableFileSubmit() {
      let prop = this.columnArray.find(item => item.type == 'uploadFile').prop
      this.tableData.forEach(item => {
        if(item[this.rowFileIdName] == this.tableFileParams.data[this.uploadFileIdName]) {
          // window.console.log(item.id, this.tableFileParams.data[this.uploadFileIdName])
          item[prop] = JSON.parse(JSON.stringify(this.tableFileParams.fileTable))
        }
      })
      this.$nextTick(() => {
        this.closeFileDialog()
      });
    },
    handleClick(type, item, index) {
      // window.console.log(type);
      if (type === "remove") {
        this.$confirm("此操作将清空批量导入的文件及列表数据, 是否继续?", "提示", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        }).then(() => {
          this.fileList.splice(index, 1);
          this.initData()
        })
      } else if(type === "down") {
        if(this.outSideDownFn) {
          this.$parent[this.outSideDownFn]()
        } else {
          window.location.href = `${API_PATH}/dictionary/template?exportMark=${this.exportMark}`;
        }
      }
    },
    // 删除附件内容
    deleteFile(row) {
      this.$confirm("此操作将删除该附件, 是否继续?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
        this[this.fileDeleteApi]({ fileId: row.fileId }).then(() => {
          this.tableFileParams.fileTable.splice(this.tableFileParams.fileTable.indexOf(row), 1)
          this.$message.success('删除成功!')
          // 删除后同步到列表中
          this.$nextTick(() => {
            let prop = this.columnArray.find(item => item.type == 'uploadFile').prop
            this.tableData.forEach(item => {
              if(item[this.rowFileIdName] == this.tableFileParams.data[this.uploadFileIdName]) {
                item[prop] = JSON.parse(JSON.stringify(this.tableFileParams.fileTable))
              }
            })
          });
        }).finally(() => {})
      })
    },
    //文件上传成功钩子
    handleAvatarSuccess_table(res) {
      window.console.log(res)
      if (res.success){
        this.$message.success('上传成功');
        this.getFileList(res.data)
        this.tableFileParams.fileList = [];
        setTimeout(() => {
          this.$refs.uploaddemo_table.clearFiles();
        }, 1000);
      } else {
        this.$message.warning(res.code === '400' || res.code === '500' ? res.message : '上传失败,请重试!');
        this.$refs.uploaddemo_table.clearFiles();
      }
    },
    // 附件表格
    getFileList(files) {
      this.tableFileParams.fileTableLoading = true;
      this.tableFileParams.fileTable.push(...files);
      setTimeout(() => {
        this.tableFileParams.fileTableLoading = false;
      }, 1000)
    },
    //文件上传之前钩子
    beforeAvatarUpload_table() {
     return true
    },
    handleChange_table(file, fileList) {
      this.tableFileParams.fileList = fileList.slice(-1);
    },
    handleSizeChange(val) {
      // window.console.log(`每页 ${val} 条`);
      this.params.size = val;
      this.initTable();
    },
    handleCurrentChange(val) {
      // window.console.log(`当前页: ${val}`);
      this.params.current = val;
      this.initTable();
    },
    handleExceed() {
      this.$message.warning(`选择上传文件超出限制!`);
    },
    // 附件上传弹窗关闭
    closeFileDialog() {
      this.innerVisible = false
      this.tableFileParams.data = {}
      this.tableFileParams.fileTable = []
    },
    initTable(inParams = {}) {
      this.in_Params = {...inParams, ...this.in_Params}
      if(!this.tableApi) {
        this.$message.warning(`请先配置对应的列表请求API!`);
        return
      }
      this.tableLoading = true
      this[this.tableApi]({
        ...this.params,
        ...this.in_Params
      }).then(res => {
        this.tableData = res.rows
        this.params.total = res.records
        this.importData = this.importData === 0 ? res.records : this.importData
      }).finally(() => {
        this.tableLoading = false
      })
    },
  },
};
</script>

<style lang="scss">
.table_file {
  line-height: 20px;
  cursor: pointer;
  color: #333;
  transition: all 0.3s;
  &:hover {
    color: #66b9ff;
  }
}
.fileTitle {
  display: block;
  width: 100%!important;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
// popover 弹窗样式 浅色
.popoverClass_light {
  min-width: 176px;
  max-width: calc(30vw + 24px);
  // max-height: calc(25vh + 24px);
  .el-popover__title {
    font-family: MicrosoftYaHei-Bold;
    font-size: 12.5px;
    color: #666;
    font-weight: 700;
  }
  .tableFile {
    display: flex;
    justify-content: space-between;
    padding-left: 5px;
    transition: all 0.3s;
    .name {
      width: 240px;
      // max-width: 800px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      color: #666;
      &:hover {
        color: #66b9ff;
      }
    }
    .btn {
      display: flex;
      justify-content: space-around;
      align-items: center;
      width: 60px;
      font-size: 16px;
      font-weight: bold;
      color: #b8c9e6;
    }
  }
}
</style>

<style scoped lang="scss">
.fileUpload {
  /deep/ {
    // 修改分页组件样式
    .el-pagination {
      margin-top: 20px;
      text-align: center;
      color: #fff;
      .btn-next,
      .btn-prev,
      .el-pager li,
      .el-select .el-input .el-input__inner,
      .el-pagination__editor.el-input .el-input__inner {
        background: rgba(67, 137, 249, 0.06);
        color: #333333;
        border: 0px solid #2a5cb0;
        border-radius: 4px;
      }
      .el-pager li:not(.disabled).active {
        background-color: #4389f9;
      }
      .el-pagination__jump,
      .el-pagination__total {
        color: #545454;
        margin-left: 10px;
      }
    }
    .line {
      height: 1px;
      margin: 15px 15px 0;
      // background: #ccc;
      border-bottom: 1px solid #d9d9d9;
    }
    .el-dialog__wrapper {
      // background-color: rgba(0,0,0,0.4)
    }
    .uploadFiles {
      width: 650px!important;
      height: auto!important;
      margin-top: calc(50vh - 150px)!important;
      .el-dialog__header {
        border-bottom: 1px solid #4389f9 !important;
        height: 48px !important;
      
        .el-dialog__headerbtn {
          font-family: MicrosoftYaHei-Bold;
          font-size: 16px !important;
          color: #ffffff !important;
          line-height: 30px;
          font-weight: 700;
        }
      }
      .el-dialog__body {
        padding: 15px!important;
        height: 250px;
        overflow-y: auto;
      }
    }
    /*修改添加模型弹窗 */
    .el-dialog {
      width: 994px;
      height: 540px;
      background: #1f2c4d !important;
      border: 1px solid #4389f9;
      border-radius: 4px;
      .el-dialog__header {
        border-bottom: 1px solid #4389f9 !important;
        height: 48px !important;

        .bda-dialog-header .bda-dialog-header-title {
          font-family: MicrosoftYaHei-Bold;
          font-size: 16px !important;
          color: #ffffff !important;
          line-height: 48px;
          font-weight: 700;
        }
      }

      .bda-dialog-body-main {
        height: 438px !important;

        .el-form {
          .el-row:nth-of-type(3) {
            height: 80px;
            .el-textarea__inner {
              height: 60px;
              background: #041338;
              border: 1px solid #4389f9;
              color: #4298f3;
              &::placeholder {
                font-family: MicrosoftYaHei;
                font-size: 14px;
                color: #4298f3;
                font-weight: 400;
              }
            }
          }

          .el-row:nth-of-type(4) {
            height: 110px;
            .el-textarea__inner {
              height: 90px;
              background: #041338;
              border: 1px solid #4389f9;
              color: #4298f3;
              &::placeholder {
                font-family: MicrosoftYaHei;
                font-size: 14px;
                color: #4298f3;
                font-weight: 400;
              }
            }
          }
        }
      }

      .el-dialog__footer {
        border-top: 1px solid #4389f9 !important;
        height: 52px !important;

        .dialog-footer {
          display: block;
          height: 100%;
          padding: 11px 20px;
          box-sizing: border-box;
        }
      }
    }
    /*修改表格单元格高度*/
    .bda-pagination-table {
      .bda-pagination-table-page {
        border: 0 !important;
        height: 50px !important;
      }
      .bda-pagination-table-main {
        height: calc(100% - 40px);
      }
    } /*解决调整单元格高度后,fixed错位*/
    .el-table__fixed-body-wrapper {
      top: 37px !important;
    }

    /*解决表格滚动高度固定导致的异常*/
    .el-table__body-wrapper.is-scrolling-left {
      height: calc(100% - 48px) !important;
    }
    .el-table__body-wrapper.is-scrolling-right {
      height: calc(100% - 48px) !important;
    }
    .el-table__body-wrapper.is-scrolling-middle {
      height: calc(100% - 48px) !important;
    }
    .el-table__fixed-body-wrapper {
      height: calc(100% - 34px) !important;
    }

    /*解决调整列宽度时,出现列内容未完整显示的异常*/
    .el-table .cell.el-tooltip {
      width: 100% !important;
    }

    .el-table__header-wrapper {
      background-color: #fff !important;
    }

    .el-table__body-wrapper {
      background-color: #fff !important;
    }

    .el-table__header th {
      // background-color: #22519c !important;
      background: #e9f1fe !important;
      height: 48px;
      border-color: #bbb;

      .cell {
        color: #666 !important;
        text-align: center;
      }
    }
    .bda-table-main {
      background-color: #ffffff;
    }

    .bda-main {
      /*height: calc(100% - 52px);*/
      border: 1px solid #bbb;
      .el-table__row {
        &:hover > td {
          background-color: #ebfcff !important;
        }
        > td {
          height: 48px;
          border-color: #bbb;

          .cell {
            color: #333;
            text-align: center;
            .el-button {
              color: #66b9ff;
            }
          }
        }
        > td:nth-last-child(3) {
          .cell {
            /*color: red;*/
            text-align: center;
          }
        }
      }
      .el-table__row:nth-of-type(odd) {
        background-color: #fff;
      }
      .el-table__row:nth-of-type(even) {
        background-color: #fff;
      }
      .bda-table {
        .bda-table-scroll_x {
          height: 8px !important;
        }
        .bda-table-scroll_x_view {
          height: 8px !important;
          background-color: #b0b1b2 !important;
        }
      }
    }
  }
  .title {
    text-align: left!important;
  }
  .upload-box {
    display: flex;
    .text {
      margin-left: 10px;
    }
  }
  h2 {
    font-size: 16px;
    color: #333333;
    font-weight: 700;
  }
  .dialogBox {
    padding-bottom: 15px;
    .tableHeader_1 {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 10px 0;
      .name {
        font-size: 14px;
        font-weight: bold;
        color: #333;
      }
      /deep/ {
        .el-radio-group {
          /* // border: 1px solid #4389F9 ; */
          margin: 0 -1px;
          border-radius: 5px;
          .el-radio-button {
            margin-bottom: 5px;
            .el-radio-button__inner {
              border: 1px solid #4389F9;
              margin-right: -1px;
            }
            &:first-child {
              .el-radio-button__inner {
                border-top-left-radius: 5px;
                border-bottom-left-radius: 5px;
              }
            }
            &:last-child {
              .el-radio-button__inner {
                border-top-right-radius: 5px;
                border-bottom-right-radius: 5px;
              }
            }
          }
          .el-radio-button:not(.is-active) {
            .el-radio-button__inner {
              color: #333;
              border-color: #4389f9;
              background-color: rgba($color: #fff, $alpha: 1);
            }
          }
          .el-radio-button.is-active {
            .el-radio-button__inner {
              border-color: #4389f9;
              background-color: rgba($color: #4389f9, $alpha: 1);
              color: #fff;
            }
          }
        }
      }
    }
  }
}
.fileUpload_light {
  /deep/ {
    .el-dialog {
      background: #ffffff !important;
      border: 1px solid #c2ddff;
      border-radius: 4px;

      .el-dialog__header {
        border-bottom: 1px solid #cccccc !important;
        height: 48px !important;

        .bda-dialog-header .bda-dialog-header-title {
          font-family: MicrosoftYaHei-Bold;
          font-size: 16px !important;
          color: #333333 !important;
          line-height: 48px;
          font-weight: 700;
        }
      }
      .el-dialog__footer {
        border-top: 1px solid #cccccc !important;
        height: 52px !important;

        .dialog-footer {
          display: block;
          height: 100%;
          padding: 11px 20px;
          box-sizing: border-box;
        }
      }
    }
  }
}
</style>

案例较为粗浅,仅供参考!

👉 功能补充

> 列表附件多选上传报错

在开发过程中,测试提及的一个需求:列表附件上传要求能够多选,但是单独新增对应的elementUi 上传组件的 multiple 属性,并不能实现多选。组件报了个 上传状态错位的问题!如下图所示:

在这里插入图片描述

原因:查询资料得知,这是由于多个文件上传,我们又在文件上传成功后,对存储file的fileList文件进行了变动(删除),导致组件内部逻辑无法查询到上传的对应文件的file值, 从而诱使后续的取值为null报错!

解决方案如下,增加判断逻辑,延长定时器定时清理时间!change方法避免截取掉多选上传的附件内容!

//文件上传成功钩子
handleAvatarSuccess_table(res) {
  window.console.log(res)
  if (res.success){
    this.$message.success('上传成功');
    this.getFileList(res.data)
    if(this.isMultiple){
      setTimeout(() => {
        this.tableFileParams.fileList = [];
        this.$refs.uploaddemo_table.clearFiles();
      }, 2500);
    } else {
      this.tableFileParams.fileList = [];
      setTimeout(() => {
        this.$refs.uploaddemo_table.clearFiles();
      }, 1000);
    }
  } else {
    this.$message.warning(res.code === '400' || res.code === '500' ? res.message : '上传失败,请重试!');
    this.$refs.uploaddemo_table.clearFiles();
  }
},
// 附件状态变更
handleChange_table(file, fileList) {
  window.console.log(file,fileList)
  if(!this.isMultiple){
    this.tableFileParams.fileList = fileList.slice(-1);
  } else {
    this.tableFileParams.fileList = fileList;
  }
},

👉 三、效果演示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


往期内容 💨

🔥 < 每日小技巧: 基于Vue状态的过渡动画 - Transition 和 TransitionGroup>

🔥 < 每日份知识快餐:axios是什么?如何在Vue中 封装 axios ? >

🔥 < 在Vue中 el-popover + el-tiptap 实现 富文本框输入,表格点击展示 (富文本HTML标签渲染) >

🔥 < 知识拓展:CSS 中常用的计量单位有哪些? >

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术宅小温

你小小的鼓励,是我搬砖的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值