【第一个Vue上手小项目Day6】导出Excel表格(解决分页矛盾+导出指定内容)

一、初级版

【目标】导出页面上存在的表格

  1. 安装依赖(-S === --save、 -D === --save-dev)

    cnpm install -S file-saver xlsx
    cnpm install -D script-loader

  2. 静态数据导出测试

    <template>
      <div>
        <!-- 导出按钮 -->
        <div class="toexcel">
          <el-button @click="exportExcel" type="primary" class="button" style="width:70px;position:absolute;top:0;right:30px">导出</el-button>
        </div>
        <el-table class="table" :data="tableData" border style="width: 100%">
          <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="地址">
          </el-table-column>
        </el-table>
      </div>
    </template>
    
    <script>
      import FileSaver from "file-saver";
      import XLSX from "xlsx";//需要导出的位置才引入,不必要配置在main.js中
      export default {
        data() {
          return {
            tableData: [{
              date: '2016-05-02',
              name: '王小虎',
              address: '上海市普陀区金沙江路 1518 弄'
            }, {
              date: '2016-05-04',
              name: '王小虎',
              address: '上海市普陀区金沙江路 1517 弄'
            }, {
              date: '2016-05-01',
              name: '王小虎',
              address: '上海市普陀区金沙江路 1519 弄'
            }, {
              date: '2016-05-03',
              name: '王小虎',
              address: '上海市普陀区金沙江路 1516 弄'
            }]
          };
        },
        methods: {
          // 导出表格所用
          exportExcel() {
            // 设置当前日期
            let time = new Date();
            let year = time.getFullYear();
            let month = time.getMonth() + 1;
            let day = time.getDate();
            let name = year + "" + month + "" + day;
            // console.log(name)
            /* generate workbook object from table */
            //  .table要导出的是哪一个表格
            var wb = XLSX.utils.table_to_book(document.querySelector(".table"));//没有明确具体表名,因为一个vue钩子只有一张表
            /* get binary string as output */
            var wbout = XLSX.write(wb, {
              bookType: "xlsx",
              bookSST: true,
              type: "array"
            });
            try {
              //  name+'.xlsx'表示导出的excel表格名字
              FileSaver.saveAs(
                new Blob([wbout], {
                  type: "application/octet-stream"
                }),
                name + ".xlsx"
              );
            } catch (e) {
              if (typeof console !== "undefined") console.log(e, wbout);
            }
            return wbout;
          }
        }
      };
    </script>
    <style scoped>
      /* 导出按钮 */
      .toexcel {
        cursor: pointer;
        cursor: hand;
        width: 70px;
        height: 34px;
      }
    </style>
    
    

    测试结果:(简单表格读取打印ok~)
    在这里插入图片描述

二、中级篇(导出指定内容)

2.1 需求

【原因】上面这样的打印是页面上的内容title和内容全部打印出来,
如果是翻页加自定义动态表格,还会打印出btn名称???? ?
这就不对了,需要特别处理!
在这里插入图片描述
【分析】打印时不应该提取页面显示内容打印,应该对请求返回的list进行打印,即打印ajax返回的内容
【扩展需求】打印时,增加只打印勾选行和列进行打印功能。

2.2 重写文件导出方法

封装表格打印方法,实现可重写标题和内容;

  1. 创建src/vendor,并创建两个文件Blob.js(File格式流转换)和 Export2Excel.js(表格重写方法声明)
    在这里插入图片描述

    Blob.js快速链接Github-Blob.js
    Export2Excel.js: 快速链接

  2. 实现原理:

    <template>
        <el-button @click="handleDownload" type="primary" class="button">导出2</el-button>
    </template>
    
    <script>
     // import FileSaver from "file-saver";
     // import XLSX from "xlsx";//因为重写了,可省略
      export default {
        methods: {
          // 下载
          handleDownload() {//数据导出方法
            require.ensure([], () => {
              const {
                export_json_to_excel
              } = require('@/vendor/Export2Excel');//Export2Excel的地址
              const tHeader = ['序号', '文章标题', '作者', '阅读数', '发布时间'];//表头
              const filterVal = ['id', 'title', 'author', 'views', 'display_time'];//list里面的对应数据
              const list = [//将导出数据赋值
                {
                  id: 1,
                  title: 2,
                  author: 3,
                  pageviews: 4,
                  display_time: 5
                },
                {
                  id: 6,
                  title: 7,
                  author: 8,
                  pageviews: 9,
                  display_time: 10
                },
                {
                  id: 11,
                  title: 12,
                  author: 13,
                  pageviews: 14,
                  display_time: 15
                },
              ];
              const data = this.formatJson(filterVal, list);//参数过滤方法
              export_json_to_excel(tHeader, data, '列表excel');//表名,方法封装在在Export2Excel中
            })
          },
          // 参数过滤
          formatJson(filterVal, jsonData) {
            return jsonData.map(v => filterVal.map(j => v[j]))
          },
        }
      };
    </script>
    
    <style scoped>
    </style>
    
    

    测试结果:
    在这里插入图片描述

2.3 后端结合

先获取数据,再下载内容:

<template>
  <div>
    <!-- 导出按钮 -->
    <el-button @click="exportExcel" type="primary" class="button">导出2</el-button>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        userList: [],
      }
    },
    methods: {
      exportExcel(){
        this.getUserList();//先获取后端全部内容
        this.handleDownload();//再导出excel
      },
      //获取数据
      getUserList() {
        this.$ajax.get('/user/aop/list').then((res) => { //axios发送get请求
          if (res.data) {
            console.log(res.data)
            this.userList = res.data.data;
          } else {
            this.$message({
              type: 'error',
              message: '查询失败',
              showClose: true
            })
          }
        })
      },
      // 下载
      handleDownload() {//数据导出方法
        require.ensure([], () => {
          const {
            export_json_to_excel
          } = require('@/vendor/Export2Excel');//Export2Excel的地址,与配置路径相对应
          const tHeader = ['序号', '姓名', '年龄', '地址', '密码'];//表头
          const filterVal = ['id', 'name', 'age', 'address', 'password'];//list里面的对应数据
          const list = this.userList; //将导出数据赋值
          const data = this.formatJson(filterVal, list);//参数过滤方法
          export_json_to_excel(tHeader, data, '列表excel');//表名,方法封装在在Export2Excel中
        })
      },
      // 参数过滤
      formatJson(filterVal, jsonData) {
        return jsonData.map(v => filterVal.map(j => v[j]))
      },
    }
  };
</script>
<style scoped>
</style>

测试结果:
在这里插入图片描述
这里是跳过了分页,用以上得到全部list和自定义header的方法,直接下载数据库全部内容。

三、高级篇(解决分页问题)

【思路】

  1. 得到多选框内容,在multipleSelection存放将要导出的内容;
  2. 全部导出为userList内容(get请求获取内容),部分导出为multipleSelection的手动勾选内容;
  3. 把内容传给Excel导出的body中,导出 ?。

【用法】

  1. data中设置集合存储,userListmultipleSelection

  2. 批量导出Btn加上:@selection-change="handleSelectionChange"监听;

    【前提:】
    在前面文章中,我用到了element对数据进行翻页和多选设置,不是本篇重点,这里稍稍略过。
    参考:【第一个Vue上手小项目Day4】Element-table表格

    <!-- 导出按钮 -->
    <el-button @click="exportToExcel" @selection-change="handleSelectionChange" type="primary" class="button" size="mini">批量导出</el-button>
    <el-button @click="exportAllToExcel" type="warning" class="button" size="mini">全部导出</el-button>
    <script>
    ....
        data() {
          return {
            userList: [],
            multipleSelection: [], //标记选中的集合
          }
        }
    ..method中..
      handleSelectionChange(val) { //选中的内容,监听状态@selection-change="handleSelectionChange"
        this.multipleSelection = val;
      },
     // ======================部分下载======================
      exportToExcel() {
        if (this.multipleSelection.length) { //根据选中的某条数据进行导出
          require.ensure([], () => {
            const {
              export_json_to_excel
            } = require('@/vendor/Export2Excel'); //Export2Excel的地址,与配置路径相对应
            const tHeader = ['序号', '姓名', '年龄', '地址', '密码']; //表头
            const filterVal = ['id', 'name', 'age', 'address', 'password']; //userList里面的对应的全部数据
            const list = this.multipleSelection; //将导出数据赋值
            const data = this.formatJson(filterVal, list); //参数过滤方法
            export_json_to_excel(tHeader, data, '列表excel'); //表名,方法封装在在Export2Excel中
          })
        }else {
          this.$message({
            message: '请选择至少一项内容',
            type: 'warning',
            showClose: true
          })
        }
      },
       // ===================全部下载==================
      exportAllToExcel() { //数据导出方法
        require.ensure([], () => {
          const {
            export_json_to_excel
          } = require('@/vendor/Export2Excel'); //Export2Excel的地址,与配置路径相对应
          const tHeader = ['序号', '姓名', '年龄', '地址', '密码']; //表头
          const filterVal = ['id', 'name', 'age', 'address', 'password']; //userList里面的对应的全部数据
          const list = this.userList; //将导出数据赋值
          const data = this.formatJson(filterVal, list); //参数过滤方法
          export_json_to_excel(tHeader, data, '列表excel'); //表名,方法封装在在Export2Excel中
        })
      },
      // 参数过滤
      formatJson(filterVal, jsonData) {
        return jsonData.map(v => filterVal.map(j => v[j]))
      },
      //导出完毕
      </script>
    

导出测试:(勾选成功!)
在这里插入图片描述
【遇见问题】:翻页之后会刷新之前的勾选状态!
【解决方法】:element table 保持勾选解决方案

  1. el-table加上:row-key="getRowKeys"
  2. 在第一个勾选状态框加上:reserve-selection="true" (这是element专门提供翻页,且保持勾选状态)
    在这里插入图片描述
  3. 在method中加上:记录方法
      getRowKeys(row){//翻页也保存勾选状态
            return row.id;
          },
    

测试结果:大功告成!(翻页、选中内容、导出到表格!)
在这里插入图片描述
多页选中导出效果 ?
在这里插入图片描述

四、源码

4.1 项目源码

前端:cungudafa–VueDay6
后端:cungudafa–SpringbootDay8

4.2 本节全部源码

userList.vue

<template>
  <div>
    <el-row type="flex" class="row-bg" justify="space-around">
      <el-col :span="6">
        <el-button @click="toggleSelection([userList[1], userList[2]])" size="mini">切换第二、第三行的选中状态</el-button>
        <el-button @click="toggleSelection()" size="mini">取消选择</el-button>
      </el-col>
      <el-col :span="6">
      </el-col>
      <el-col :span="6">
        <el-button @click="clearFilter" type="success" size="mini"><i class="el-icon-refresh-right"></i>刷新</el-button>
        <!-- 导出按钮 -->
        <el-button @click="exportToExcel" @selection-change="handleSelectionChange" type="primary" class="button" size="mini"><i class="el-icon-download"></i>批量导出</el-button>
        <el-button @click="exportAllToExcel" type="warning" class="button" size="mini"><i class="el-icon-download"></i>全部导出</el-button>
      </el-col>
    </el-row>
    <el-table class="table" ref="filterTable" v-loading="loading" :data="userList.slice((currentPage-1)*pagesize,currentPage*pagesize).filter(data => !search || data.name.toLowerCase().includes(search.toLowerCase()))"
      :default-sort="{prop: 'id', order: 'ascending'}" border tooltip-effect="dark" style="width: 100%"
      @selection-change="handleSelectionChange" :row-key="getRowKeys">
      <el-table-column type="selection" reserve-selection="true" width="55">
      </el-table-column>
      <el-table-column prop="id" label="id" sortable width="180">
      </el-table-column>
      <el-table-column prop="name" label="姓名" width="180">
      </el-table-column>
      <el-table-column prop="age" label="年龄" width="180" :formatter="formatter">
      </el-table-column>
      <el-table-column prop="address" label="地址" width="180" :filters="[{ text: '重庆', value: '重庆' }, { text: '北京', value: '北京' },{ text: '上海', value: '上海' }]"
        :filter-method="filterTag" filter-placement="bottom-end">
        <template slot-scope="scope">
          <el-tag :type="scope.row.address === '重庆' ? 'primary' : 'success'" disable-transitions>{{scope.row.address}}</el-tag>
        </template>
      </el-table-column>
      <el-table-column align="right">
        <template slot="header" slot-scope="scope">
          <el-input v-model="search" size="mini" placeholder="输入姓名关键字搜索" />
        </template>
        <template slot-scope="scope">
          <el-button size="mini" @click="handleEdit(scope.$index, scope.row)">Edit</el-button>
          <el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)">Delete</el-button>
        </template>
      </el-table-column>
    </el-table>
    <div class="block">
      <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="currentPage"
        :page-sizes="[10, 20, 30, 40]" :page-size="pagesize" layout="total, sizes, prev, pager, next, jumper" :total="userList.length"
        background>
      </el-pagination>
    </div>
  </div>
</template>

<script>
  import FileSaver from 'file-saver'
  import XLSX from 'xlsx'
  export default {
    data() {
      return {
        userList: [],
        loading: false, //加载效果
        multipleSelection: [], //标记选中的集合
        search: '', //关键词查找
        currentPage: 1, //最初显示默认要跳转的一页
        pagesize: 10
      }
    },
    created() {
      this.getUserList();
    },
    methods: {
      //多行全选和反选
      toggleSelection(rows) {
        if (rows) {
          rows.forEach(row => {
            this.$refs.filterTable.toggleRowSelection(row);
          });
        } else {
          this.$refs.filterTable.clearSelection(); //反选
        }
      },
      handleSelectionChange(val) { //选中的内容,监听状态@selection-change="handleSelectionChange"
        this.multipleSelection = val;
      },
      getRowKeys(row){//翻页也保存勾选状态
        return row.id;
      },
      //地址排序
      formatter(row, column) {
        return row.age;//排序
      },
      //标签查找和清除标签
      resetDateFilter() {
        this.$refs.filterTable.clearFilter('address');
      },
      clearFilter() {
        this.$refs.filterTable.clearFilter();
      },
      filterTag(value, row) {
        return row.address === value;
      },
      filterHandler(value, row, column) {
        const property = column['property'];
        return row[property] === value;
      },
      //编辑
      handleEdit(index, row) {
        console.log(index, row);
      },
      //删除
      handleDelete(index, row) {
        console.log(index, row);
      },
      //分页
      handleSizeChange(size) {
        console.log(`每页 ${size} 条`);
        this.pagesize = size;
      },
      handleCurrentChange(currentPage) {
        console.log(`当前页: ${currentPage}`);
        this.currentPage = currentPage;
      },
      getUserList() {
        this.loading = true;
        this.$ajax.get('/user/aop/list').then((res) => { //axios发送get请求
          if (res.data) {
            this.loading = false;
            //console.log(res.data)
            this.userList = res.data.data;
          } else {
            this.loading = false;
            this.$message({
              type: 'error',
              message: '查询失败',
              showClose: true
            })
          }
        })
      },
      // 部分下载
      exportToExcel() {
        if (this.multipleSelection.length) { //根据选中的某条数据进行导出
          require.ensure([], () => {
            const {
              export_json_to_excel
            } = require('@/vendor/Export2Excel'); //Export2Excel的地址,与配置路径相对应
            const tHeader = ['序号', '姓名', '年龄', '地址', '密码']; //表头
            const filterVal = ['id', 'name', 'age', 'address', 'password']; //userList里面的对应的全部数据
            const list = this.multipleSelection; //将导出数据赋值
            const data = this.formatJson(filterVal, list); //参数过滤方法
            export_json_to_excel(tHeader, data, '列表excel'); //表名,方法封装在在Export2Excel中
          })
        }else {
          this.$message({
            message: '请选择至少一项内容',
            type: 'warning',
            showClose: true
          })
        }
      },

      // 全部下载
      exportAllToExcel() { //数据导出方法
        require.ensure([], () => {
          const {
            export_json_to_excel
          } = require('@/vendor/Export2Excel'); //Export2Excel的地址,与配置路径相对应
          const tHeader = ['序号', '姓名', '年龄', '地址', '密码']; //表头
          const filterVal = ['id', 'name', 'age', 'address', 'password']; //userList里面的对应的全部数据
          const list = this.userList; //将导出数据赋值
          const data = this.formatJson(filterVal, list); //参数过滤方法
          export_json_to_excel(tHeader, data, '列表excel'); //表名,方法封装在在Export2Excel中
        })
      },
      // 参数过滤
      formatJson(filterVal, jsonData) {
        return jsonData.map(v => filterVal.map(j => v[j]))
      },
      //导出完毕

    }
  }
</script>
<style scoped>
  .el-row {
    margin-bottom: 20px;
    &:last-child {
      margin-bottom: 0;
    }
  }
  .el-col {
    border-radius: 4px;
  }
  .row-bg {
    padding: 10px 0;
    background-color: #f9fafc;
  }
</style>

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值