Vue 实现 Excel 导入功能

前言

  • 在实际开发中导入功能是非常常见的,导入功能前端并不难,难的是后端字段的对应,主要处理在后端。

  • 我们只需要用饿了吗ui upload上传组件封装一层就可以了,主要起到上传文件作用。

  • 通过按钮跳转到页面,携带参数,下载excel模板。导入页面接受参数区分从哪一个页面过来。

  • 页面EXCEL导入成功就回到上一级页面,刷新数据。失败动画效果,把失败数据原因用表格展现出来。

代码实现

1.整体代码

<template>
  <div class="center">
    <!-- drag  是否启用拖拽上传 -->
    <!-- multiple 是否支持多选文件 -->
    <!-- action 必要属性,上传文件的地址,可以不给,但必须要有,不给就i调接口上传 -->
    <!-- :http-request="uploadFile"这个是就上传文件的方法,把上传的接口写在这个方法里 -->
    <!-- :limit="fileLimit"上传文件个数的限制 -->
    <!-- :on-remove="handleRemove"//上传之后,移除的事件 -->
    <!-- :file-list="fileList"//上传了那些文件的列表 -->
    <!-- :on-exceed="handleExceed"//超出上传文件个数的错误回调 -->
    <!-- :before-upload="beforeUpload"//文件通过接口上传之前,一般用来判断规则, -->
    <!-- :show-file-list="false"//是否用默认文件列表显示 -->
    <!-- :headers="headers"//上传文件的请求头 -->
    <div class="upload" :class="{ active: blureimport }">
      <div class="btn-box">
        <!-- 问号组件 主页文章有 我是全局在main.js挂载 全局组件直接用 -->
        <UsageTooltip data="bottom">
          <H1>Excel参考模板下载</H1>
          <p style="">
            <i
              class="el-icon-warning-outline"
              style="font-size: 15px; background-color: red; border-radius: 50%"
            ></i
            >请严格按照excel填写数据
          </p>
          <p>
            填写完数据可以将excel文件推入指定区域或点击区域选择需要导入的excel文件
          </p>
        </UsageTooltip>
        <el-button type="success" @click="downloadTemplate"
          >Excel模板下载</el-button
        >
        <!-- 后期要删掉 方便看效果 又接口失败开启事件和赋值 -->
        <el-button type="success" @click="qin">失败样式开关</el-button>
      </div>
      <!-- :file-list="fileList" 没用到 -->
      <el-upload
        drag
        action
        multiple
        :http-request="uploadFile"
        ref="upload"
        :limit="fileLimit"
        :on-remove="handleRemove"
        :on-exceed="handleExceed"
        :before-upload="beforeUpload"
        :show-file-list="true"
        :headers="headers"
      >
        <i class="el-icon-upload"></i>
        <p>点击上传,或将文件拖到此处</p>
      </el-upload>
    </div>
​
    <div class="table" :class="{ fade: blureimport }" v-if="blureimport">
      <el-table
        :data="tableData"
        border
        size="mini"
        stripe
        height="100%"
        style="width: 100%"
      >
        <el-table-column label="序号" width="70" align="center">
          <template slot-scope="scope">
            <!-- {
   
   { (pageInfo.pageNo - 1) * pageInfo.pageSize + scope.$index + 1 }}
            -- -->
            {
   
   { scope.$index }}
          </template>
        </el-table-column>
        <el-table-column
          prop="data"
          align="center"
          label="合同名称"
          width="200"
        >
        </el-table-column>
        <el-table-column
          prop="data"
          align="center"
          label="合同类型"
          width="150"
        >
        </el-table-column>
        <el-table-column
          prop="data"
          align="center"
          label="合同编号"
          width="200"
        >
        </el-table-column>
        <el-table-column prop="data" align="center" label="负责人" width="150">
        </el-table-column>
        <el-table-column
          prop="data"
          align="center"
          label="归属部门"
          width="150"
        >
        </el-table-column>
        <el-table-column
          prop="data"
          align="center"
          label="导入状态"
          width="180"
        >
        </el-table-column>
        <el-table-column
          prop="data"
          align="center"
          label="失败原因"
          width="280"
        >
        </el-table-column>
      </el-table>
    </div>
  </div>
</template>
​
<script>
// 导入上传接口
export default {
  name: "import",
  data() {
    return {
      // 附件数量限制
      fileLimit: 10,
      //上传后的文件列表
      fileList: [],
      //请求头
      headers: { "Content-Type": "multipart/form-data" },
      // 允许的文件类型
      fileType: ["xls", "xlsx"],
      // 运行上传文件大小,单位 M
      fileSize: 10,
      // 失败样式开关
      blureimport: false,
      // 表格数据
      tableData: [
        {
          data: "风过无痕",
        },
        {
          data: "风过无痕",
        },
        {
          data: "风过无痕",
        },
        {
          data: "风过无痕",
        },
        {
          data: "风过无痕",
        },
        {
          data: "风过无痕",
        },
        {
          data: "风过无痕",
        },
        {
          data: "风过无痕",
        },
        {
          data: "风过无痕",
        },
        {
          data: "风过无痕",
        },
        {
          data: "风过无痕",
        },
        {
          data: "风过无痕",
        },
        {
          data: "风过无痕",
        },
        {
          data: "风过无痕",
        },
        {
          data: "风过无痕",
        },
      ],
    };
  },
  methods: {
    //模板下载
    downloadTemplate() {
      let type = this.$route.query.type;
      console.log(type);
      // 创建a标签
      let a = document.createElement("a");
      // 根据不同的页面参数下载不同模板
      if (type == "personnel") {
        // 例子
        // 模板位置 最好不要出现中文
        a.href = "./static/a.xlsx";
        // 下载模板名称
        a.download = "人事档案数据.xlsx";
      } else if (type == "contract") {
        // 模板位置 最好不要出现中文
        a.href = "";
        // 下载模板名称
        a.download = "";
      }
      a.style.display = "none";
      document.body.appendChild(a);
      a.click();
      a.remove();
    },
    //上传文件之前
    beforeUpload(file) {
      if (file.type != "" || file.type != null || file.type != undefined) {
        //截取文件的后缀,判断文件类型
        const FileExt = file.name.replace(/.+\./, "").toLowerCase();
        //计算文件的大小
        const isLt5M = file.size / 1024 / 1024 < 50; //这里做文件大小限制
        // console.log(1);
        //如果大于50M
        if (!isLt5M) {
          this.$showMessage("上传文件大小不能超过 10MB!");
          return false;
        }
        //如果文件类型不在允许上传的范围内
        if (this.fileType.includes(FileExt)) {
          return true;
        } else {
          this.$message.error("上传文件格式不正确!");
          return false;
        }
      }
    },
    //超出文件个数的回调
    handleExceed() {
      this.$message({
        type: "warning",
        message: "超出最大上传文件数量的限制!",
      });
      return;
    },
    //上传文件的事件
    uploadFile(item) {},
    //上传了的文件给移除的事件,由于我没有用到默认的展示,所以没有用到
    handleRemove() {},
    //上传了的文件给移除的事件,由于我没有用到默认的展示,所以没有用到
    async handleRemove() {},
    async uploadFile(item) {
      // this.$showMessage("文件上传中........");
      //上传文件的需要formdata类型;所以要转
      // 创建formDatas类型
      let formDatas = new FormData();
      formDatas.append("uploadFile", item.file);
      console.log("文件是", item.file);
      // console.log("文件是 " + item.file.name);
      // 判断 根据不同页面参数调用不同的接口
      let type = this.$route.query.type;
      if (type == "personnel") {
        console.log("执行了");
        eqbilkimport(formDatas)
          .then((res) => {
            console.log("成功打印", res);
            // 导入成功退回导入之前页面 并且刷新
            this.$route.go(-1);
          })
          .catch((err) => {
            console.log("失败打印", err);
            // 把导入失败的数据赋值给表格数组
            // 开启失败之后的动画和淡入淡出效果
            this.blureimport = true;
          });
      } else if (type == "contract") {
        // 一样不同页面
      }
    },
    // 模拟导入失败的样式变化
    qin() {
      console.log("失败了");
      this.blureimport = true;
    },
  },
};
</script>
​
<style lang="scss" scoped>
.center {
  height: 827px;
  background-color: #fff;
  position: relative;
  // 正常屏幕下的上下间距
  margin-top: 35px;
  margin-bottom: 25px;
  // 适配谷歌火狐,没有书签栏的上下边距
  @media screen and (min-height: 950px) and (max-height: 990px) {
    margin-top: 50px;
    margin-bottom: 40px;
  }
  // 适配浏览器全屏模式下的上下边距
  @media screen and (min-height: 1070px) {
    margin-top: 100px;
    margin-bottom: 100px;
  }
  .upload {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 720px;
    display: flex;
    .btn-box {
      width: 360px;
      height: 180px;
      box-sizing: border-box;
      border: 1px dashed #d9d9d9;
      display: flex;
      align-items: center;
      justify-content: center;
      ::v-deep .el-tooltip {
        color: rgb(88, 87, 81) !important;
        font-size: 25px !important;
        margin-right: 10px;
      }
    }
  }
  .active {
    left: 35%;
    display: block;
    transition: left 2s, transform 3s;
  }
  .table {
    width: 900px;
    height: 600px;
    background-color: skyblue;
    position: fixed;
    right: 100px;
    top: 50%;
    transform: translateY(-50%);
  }
  .fade {
    animation-name: fade;
    animation-duration: 4s;
    /* Safari and Chrome */
    -webkit-animation-name: fade;
    -webkit-animation-duration: 4s;
    @-webkit-keyframes fade {
      from {
        opacity: 0.1;
      }
      to {
        opacity: 1;
      }
    }
    @keyframes fade {
      from {
        opacity: 0.1;
      }
      to {
        opacity: 1;
      }
    }
  }
}
</style>

2.按钮带参数跳转

 <el-button
      type="success"
      plain
      icon="el-icon-upload2"
      size="mini"
      style="float: right"
      @click="$router.push({ path: '/import', query: { type: 'contract' } })"
      >导入数据</el-button
    >

3.Excel 模板下载

把需要下载的EXCEL模板放到public文件夹/static/a.xlsx

3.全局提示组件 - 主页文章有(全局导入)

4.导入失败结构动画效果,数据失败原因淡入效果-动态绑定class类实现,代码有注释

复制细节-直接复制需要注意细节

1.我使用的vue-element-admin 路由是放在前端的

// 数据导入页面
  {
    path: '/import',
    component: Layout,
    children: [
      {
        name: 'import',
        path:'',
        component: () => import('@/views/import/index'),
        hidden: true,
        meta: {
          title: '导入设备'
        }
      }
    ]
  },

2.全局组件的问号提示 - 主页文章有,全局组件注册方式

3.Excel 模板下载注意放的位置和命名(不要出现中文名),代码中有注释

 // 模板位置 最好不要出现中文
        a.href = "./static/a.xlsx";
        // 下载模板名称
        a.download = "人事档案数据.xlsx";

4.效果图-不是动态的

 


经过这一趟流程下来相信你也对 Vue 实现 Excel 导入功能 有了初步的深刻印象,但在实际开发中我 们遇到的情况肯定是不一样的,所以我们要理解它的原理,万变不离其宗。加油,打工人!

什么不足的地方请大家指出谢谢 -- 風过无痕

猜你喜欢

转载自blog.csdn.net/weixin_53579656/article/details/129396361