用到的技术点:
1.springDatajpa
2.EasyExcel
3.数据库
4.vue
前端实现:
1.创建按钮(点击此按钮弹出填写导出条件的弹出框)
<el-button type="primary" round @click="dialogVisible = true"><svg-icon icon-class="jurassic" /> 导出</el-button>
//================================
<el-dialog
title="导出条件"
:visible.sync="dialogVisible"
width="30%"
>
<el-form ref="exportConditionForm" :model="exportCondition" :rules="exportConditionRules" label-width="100px" class="demo-ruleForm">
<el-form-item label="登录账号" prop="account">
<el-input v-model="exportCondition.account" />
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="exportCondition.name" />
</el-form-item>
<el-form-item label="登录IP地址" prop="ip">
<el-input v-model="exportCondition.ip" />
</el-form-item>
<el-form-item label="登录时间" prop="time">
<el-date-picker
v-model="exportCondition.time"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']"
/>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="exportButtonEvent">导出</el-button>
</span>
</el-dialog>
2.为导出按钮添加导出事件(这里是下载的关键)
exportButtonEvent() {
const shuju = this.exportCondition
axios({
method: 'post',
//这里是我的后端接口地址改成你的,这里会涉及到跨域的
//问题1.使用注解形式解决2.重写addCorsMappings方法3.前
//端处理
//其他地方就不用改了
url: window.wgParameter.VUE_APP_FILE_USER_API + '/loginformation/query/exportuserinfor',
data: shuju,
responseType: 'arraybuffer'
}).then(res => {
const name = res.headers['content-disposition']
if (res.size === 0) { // 如果返回是空的
this.$message({
message: '报错了',
type: 'warning'
})
} else { // 返回不为空
if (!!window.ActiveXObject || 'ActiveXObject' in window) {
window.navigator.msSaveOrOpenBlob(res, '计算结果.xlsx')
} else {
const url = window.URL.createObjectURL(
new Blob([res.data], {
type: 'application/octet-stream'
})
)
const link = document.createElement('a')
link.href = url
link.setAttribute(
'download', decodeURI(name.split("attachment;filename*=utf-8''")[1], 'utf-8')
)
link.click()
}
}
}).catch(error => {
this.$message({
message: '报错了:' + error,
type: 'warning'
})
})
}
后端实现:
1.建表,用EasyExcel的注解(@ExcelProperty)标记表中属性(easyExcel的用法见其官网)
用来对应后台数据的实体类(在其中规定了excel的格式标题位置)
package com.zgy.handle.userService.model.loginformation;
import com.alibaba.excel.annotation.ExcelProperty;
import com.zgy.handle.userService.model.dto.BaseDTO;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Embeddable;
@Embeddable
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LogInformationDto extends BaseDTO {
@ExcelProperty(value = "账号", index = 0)
@ApiModelProperty("账号")
String account;
@ExcelProperty(value = "姓名", index = 1)
@ApiModelProperty("姓名")
String name;
@ApiModelProperty("ip地址")
@ExcelProperty(value = "ip地址", index = 2)
String ip;
@ApiModelProperty("登录时间/操作时间")
@ExcelProperty(value = "登录时间/操作时间", index = 3)
String time;
/**
* 类型
* 1--登录
* 2--操作
*/
@ApiModelProperty("类型")
@ExcelProperty(value = "类型", index = 4)
Integer type;
@ApiModelProperty("模块名")
@ExcelProperty(value = "模块名", index = 5)
String modulename;
}
封装前台参数的实体类
package com.zgy.handle.userService.model.loginformation;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ExportLoginformation {
@ApiModelProperty("账号")
String account;
@ApiModelProperty("姓名")
String name;
@ApiModelProperty("ip地址")
String ip;
@ApiModelProperty("开始时间-结束时间")
String[] time;
}
2.写业务处理层代码(用动态sql查数据,之后用Easyexcel将数据以excel的格式写出到response响应体中供前端下载用)
@Override
public void exportUserInfor(HttpServletResponse response, ExportLoginformation exportLoginformation) {
//动态查询
Specification<LogInformation> sf = (Specification<LogInformation>) (root, query, cb) -> {
//用于添加所有查询条件
List<Predicate> p = new ArrayList<>();
if (!exportLoginformation.getIp().isBlank()) {
Predicate p1 = cb.equal(root.get("ip").as(String.class), exportLoginformation.getIp());
p.add(p1);
}
if (!exportLoginformation.getName().isBlank()) {
Predicate p2 = cb.equal(root.get("name").as(String.class), exportLoginformation.getName());
p.add(p2);
}
if (!exportLoginformation.getAccount().isBlank()) {
Predicate p3 = cb.equal(root.get("account").as(String.class),exportLoginformation.getAccount() );
p.add(p3);
}
if(exportLoginformation.getTime().length>0){
String[] time = exportLoginformation.getTime();
Date startDate = null;
Date endDate = null;
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
try {
if (StringUtils.isNotBlank(time[0])) {
startDate = dateFormat.parse(time[0]);
}
if (StringUtils.isNotBlank(time[1])) {
endDate = dateFormat.parse(time[1]);
}
} catch (ParseException e) {
e.printStackTrace();
return null;
}
Predicate p4 = null;
if (startDate != null && endDate != null) {
p4 = cb.between(root.get("time").as(Date.class), startDate, endDate);
} else if (startDate != null) {
p4 = cb.greaterThanOrEqualTo(root.get("date").as(Date.class), startDate);
} else {
p4 = cb.lessThanOrEqualTo(root.get("date").as(Date.class), endDate);
}
p.add(p4);
}
Predicate[] pre = new Predicate[p.size()];
Predicate and = cb.and(p.toArray(pre)); //查询条件 and
//Predicate or = cb.or(p.toArray(pre)); //查询条件 or
query.where(and); //添加查询条件
//设置排序
List<Order> orders = new ArrayList<>();
orders.add(cb.desc(root.get("time").as(Date.class))); //倒序
// orders.add(cb.asc(root.get("username"))); //正序
return query.orderBy(orders).getRestriction();
};
List<LogInformation> all = logInformationQueryReposition.findAll(sf);
List<LogInformationDto> logInformationDtos = logInformationMapper.toLogInformationDtoList(all);
String fileName =URLEncoder.encode("用户登录信息-" + System.currentTimeMillis() + ".xlsx", StandardCharsets.UTF_8).replaceAll("\\+", "%20");
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想使用03 则 传入excelType参数即可
// 设置响应头
response.setHeader("Access-Control-Expose-Headers", "Content-disposition");
response.setHeader("Content-disposition","attachment;filename*=utf-8''"+fileName);
try {
EasyExcel.write(response.getOutputStream(), LogInformationDto.class).sheet("用户登录信息").doWrite(logInformationDtos);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
上面的业务逻辑:用户根据ip,账号,用户名,要查询的一个时间范围(开始时间,结束时间)去动态的进行数据查询,并通过数据库中的时间字段排序fileName(换成你的文件名) EasyExcel.write(response.getOutputStream(), LogInformationDto.class).sheet("用户登录信息").doWrite(logInformationDtos);
其中LogInformationDto.class
是你的数据封装类行用户登录信息
是excel中sheet页的名字logInformationDtos
你从数据库中查出的数据
后端解决跨域的问题
package com.zgy.handle.userService.confg.cros;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CORSConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
// `/loginformation/query/exportuserinfor`这是你的接口
//地址没有`http://ip:端口`的部分
registry.addMapping("/loginformation/query/exportuserinfor")
.allowedMethods("*")
.allowedOrigins("*")
.allowedHeaders("*");
}
}
前端传来的时间格式的转换这块儿可能会报错建议使用catgpt(BIto插件)解决问题
将你的错误告诉他就好了,我的代码就是问过他后解决的错误
.as(类型)这个在每个属性的断言中都要加否则会报与期望的类型不匹配的错误