之前一篇博客介绍了使用POI来完成报表的导出,默认都是导出所有数据列,但是我们有时候可能需要导出某些指定的列,搭配我前端使用的Vue和ElementUI,然后便做出来个可以自定义导出列的功能
首先我们来看一下我们要导出的数据,先看一下所有的列
// el
ifLoading: false,
exportShow: false,
userDataList: [],
rangeTime: '',
searchData: {
total: 0,
pageNum: 1,
pageSize: 5,
userAccount: '',
},
columnTitle: [],
下面可以看到prop,label这些属性就是我们一会儿需要获取到的数据
<el-table :data="userDataList" border style="width: 100%;" class="el-table01" v-loading="ifLoading" ref="userDataListRef">
<el-table-column prop="index" :label="$t('serialNo')" align="center"></el-table-column>
<el-table-column prop="userAccount" :label="$t('account')" align="center"></el-table-column>
<el-table-column prop="userName" :label="$t('userName')" align="center"></el-table-column>
<el-table-column prop="userSex" :label="$t('userSex')" align="center">
<template slot-scope="scope">
<span v-if="scope.row.userSex == 0">{
{
$t('womanText')}}</span>
<span v-if="scope.row.userSex == 1">{
{
$t('manText')}}</span>
</template>
</el-table-column>
<el-table-column prop="userIslogin" :label="$t('isLogin')" align="center">
<template slot-scope="scope">
<span v-if="scope.row.userIslogin == 0">{
{
$t('offline')}}</span>
<span v-if="scope.row.userIslogin == 1" style="color: #357ae8;font-weight: bold;">{
{
$t('online')}}</span>
</template>
</el-table-column>
<el-table-column prop="userBirthday" :label="$t('birthday')" align="center"></el-table-column>
<el-table-column prop="userCreateTime" :label="$t('createTime')" align="center"></el-table-column>
</el-table>
在这里我使用了el-popover,弹出层组件都想试一下嘛,其实这个的确没有其他的好用
<div class="btnBox">
<el-button size="medium" :loading="ifLoading" type="primary" @click="queryMainData()">{
{
$t('search') }}</el-button>
<el-popover
placement="bottom"
width="320"
:offset="-100"
v-model="exportShow">
<h2>{
{
$t('pleaseSelectEx') }}</h2>
<div class="excel-title-cls">
<el-row>
<el-col v-for="ct in columnTitle" :key="ct.prop" :span="10">
<span v-if="ct.prop != 'index'">
<el-checkbox class="btn-check" v-model="ct.checked" border>{
{
ct.label}}</el-checkbox>
</span>
<span v-else>
<el-checkbox class="btn-check" v-model="ct.checked" disabled border>{
{
ct.label}}</el-checkbox>
</span>
</el-col>
</el-row>
</div>
<div style="text-align: right; margin: 0">
<el-button type="primary" @click="exportData('check')">{
{
$t('exportSome') }}</el-button>
<el-button class="btn-all" @click="exportData('all')">{
{
$t('exportAll') }}</el-button>
<el-button type="error" plain @click="exportShow = false">{
{
$t('close') }}</el-button>
</div>
<el-button slot="reference" size="medium" type="success" @click="exportOpen">{
{
$t('export') }}</el-button>
</el-popover>
</div>
获取所有列方法
我们在打开弹出层时获取所有的列,并循环遍历到弹出层中
exportOpen(){
this.columnTitle = []
this.$refs.userDataListRef.$children.forEach(obj => {
if (obj.label != undefined){
// 每个列我们自己设置3个属性,label:显示的文本,prop:英文编码,checked:选中状态
let columnChild = {
'label':obj.label,'checked':false,'prop':obj.prop}
// 存到columnTitle数组中
this.columnTitle.push(columnChild)
}
})
},
导出部分和导出全部的方法
根据参数 all 或者 check来判断全导出还是部分导出
exportData(type) {
// 日期搜索框
this.searchData.startDatetime = ''
this.searchData.endDatetime = ''
if (this.rangeTime && this.rangeTime.length) {
this.searchData.startDatetime = this.rangeTime[0]
this.searchData.endDatetime = this.rangeTime[1]
}
let s = this.searchData
// 遍历所有列数组,将序号去除,如果选中将数据加入新的数组
let columnTitleNew = []
this.columnTitle.forEach((item)=>{
if (type === 'all'){
if (item.prop != 'index'){
columnTitleNew.push(item)
}
} else if (type === 'check'){
if (item.checked){
columnTitleNew.push(item)
}
}
})
if (columnTitleNew.length == 0){
this.$message({
type: 'error',message: '请选择要导出的列'})
return
} else {
// 传递新的数组给后端,调用后端导出接口,这里需要对格式进行转换防止URL无法识别部分编码
this.exportShow = false
var url = 'http://localhost:8089/api/reportExport/userList'
var href = url + '?userAccount=' + s.userAccount + '&startDatetime=' + s.startDatetime + '&endDatetime=' + s.endDatetime + '&columnTitle=' + encodeURI(JSON.stringify(columnTitleNew))
window.open(href)
}
},
我们需要传递分页,模糊搜索,导出的列这些参数
@GetMapping("/reportExport/userList")
@ApiOperation(value="用户信息导出" , notes ="用户信息自定义列导出" , response = Result.class)
@ApiImplicitParams({
@ApiImplicitParam(name = "userAccount", value = "用户账号", required = true, dataType = "string", paramType = "query"),
@ApiImplicitParam(name = "startDatetime", value = "开始时间yyyy-MM-dd格式", dataType = "string", paramType = "query"),
@ApiImplicitParam(name = "endDatetime", value = "结束时间yyyy-MM-dd格式", dataType = "string", paramType = "query"),
@ApiImplicitParam(name = "pageNum", value = "数据页数量", required = false, dataType = "Integer", paramType = "query"),
@ApiImplicitParam(name = "pageSize", value = "数据页编号,从1开始", required = false, dataType = "Integer", paramType = "query"),
})
public Object prodTest(HttpServletResponse response,
@RequestParam(required = false) String userAccount,
@RequestParam(required = false) String startDatetime,
@RequestParam(required = false) String endDatetime,
@RequestParam(required = false) String columnTitle) throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
String fileName = DateUtils.format(new Date(),"yyyyMMddHHmmss") +"-USER.xls";
String headStr = "attachment; filename=" + fileName;
response.setContentType("APPLICATION/OCTET-STREAM");
response.setHeader("Content-Disposition", headStr);
reportService.exportUserList(response.getOutputStream(), userAccount, startDatetime, endDatetime, columnTitle);
return null;
}
来看一下Impl中的具体实现
@Override
public void exportUserList(OutputStream out, String userAccount, String startDatetime, String endDatetime, String columnTitle) throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
List<SysUser> list = userMapper.queryUserList(userAccount, startDatetime, endDatetime);
List<Object[]> dataList = new ArrayList<Object[]>();
// 把需要展示的列json数据转为List
List<Map<String, Object>> cols = JsonUtils.jsonStringToList1(columnTitle);
// 设置Excel列标题
String[] rowsName = new String[cols.size()+1];
rowsName[0] = "序号";
for (int i = 0; i < cols.size(); i++) {
rowsName[i+1] = cols.get(i).get("label").toString();
}
// 设置Excel行数据
for (SysUser p : list) {
Object[] objs = new Object[cols.size()+1];
objs[0] = "";
for (int j = 0; j < cols.size(); j++) {
String prop = cols.get(j).get("prop").toString();
if (prop.equals("userCreateTime")) {
objs[j + 1] = new DateTime(invokeGetMethod(SysUser.class, prop, p)).toString(DateUtils.DATE_PATTERN[3]);
} else if (prop.equals("userBirthday")){
objs[j + 1] = new DateTime(invokeGetMethod(SysUser.class, prop, p)).toString(DateUtils.DATE_PATTERN[5]);
} else if (prop.equals("userSex")) {
String userSex = invokeGetMethod(SysUser.class, prop, p).toString();
if (userSex.equals("0")) {
objs[j + 1] = "女";
} else if (userSex.equals("1")) {
objs[j + 1] = "男";
} else {
objs[j + 1] = "未知";
}
} else if (prop.equals("userIslogin")) {
String userLogin = invokeGetMethod(SysUser.class, prop, p).toString();
if (userLogin.equals("1")) {
objs[j + 1] = "在线";
} else {
objs[j + 1] = "离线";
}
} else {
objs[j + 1] = invokeGetMethod(SysUser.class, prop, p);
}
}
dataList.add(objs);
}
String title = "用户信息报表";
ExportExcel ex = new ExportExcel(title, rowsName, dataList);
try {
ex.export(out);
} catch (Exception e) {
e.printStackTrace();
}
out.flush();
out.close();
}
以上我们需要调用自定义的反射辅助类,根据实体类属性来调用实体类get方法
package cn.liu783.vueappjava.util;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectUtils {
/**
* 拼接属性,调用目标Class的get方法
*
* @param c 属性所属实体类
* @param filedName 需要调用的属性名
* @param obj 实体类实例
* @return 返回get方法结果
*/
public static Object invokeGetMethod(Class<?> c, String filedName, Object obj) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
String methodName = "get" + filedName.substring(0, 1).toUpperCase() + filedName.substring(1);
Class<?>[] nullClasses = null;
Method method = c.getMethod(methodName, nullClasses);
Object[] nullObjects = null;
return method.invoke(obj, nullObjects);
}
}
导出的工具类大家可以我的这篇文章
Vue+SpringBoot使用POI导出EXCEL