一、初级版
【目标】导出页面上存在的表格
-
安装依赖(-S === --save、 -D === --save-dev)
cnpm install -S file-saver xlsx
cnpm install -D script-loader -
静态数据导出测试
<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 重写文件导出方法
封装表格打印方法,实现可重写标题和内容;
-
创建src/vendor,并创建两个文件
Blob.js
(File格式流转换)和Export2Excel.js
(表格重写方法声明)
Blob.js
:快速链接Github-Blob.js
Export2Excel.js
: 快速链接 -
实现原理:
<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的方法,直接下载数据库全部内容。
三、高级篇(解决分页问题)
【思路】
- 得到多选框内容,在
multipleSelection
存放将要导出的内容; - 全部导出为
userList
内容(get请求获取内容),部分导出为multipleSelection
的手动勾选内容; - 把内容传给Excel导出的body中,导出 ?。
【用法】
-
data中设置集合存储,
userList
、multipleSelection
; -
在批量导出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 保持勾选解决方案
- 在
el-table
加上:row-key="getRowKeys"
- 在第一个勾选状态框加上:
reserve-selection="true"
(这是element专门提供翻页,且保持勾选状态)
- 在method中加上:记录方法
getRowKeys(row){//翻页也保存勾选状态 return row.id; },
测试结果:大功告成!(翻页、选中内容、导出到表格!)
多页选中导出效果 ?
四、源码
4.1 项目源码
前端:cungudafa–VueDay6
后端:cungudafa–SpringbootDay8
4.2 本节全部源码
<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>