场景
使用若依前后端分离版实现Excel的导入和导出。
前端:Vue+ElementUI
后端:SpringBoot+POI+Mysql
注:
博客:
https://blog.****.net/badao_liumang_qizhi
关注公众号
霸道的程序猿
获取编程相关电子书、教程推送与免费下载。
实现
Excel导入
点击导入按钮时的效果
选中Excel后
首先是前端页面,添加导入的dialog
<el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body> <el-upload ref="upload" :limit="1" accept=".xlsx, .xls" :headers="upload.headers" :action="upload.url" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="false" :data="{updateSupport:upload.updateSupport}" drag > <i class="el-icon-upload"></i> <div class="el-upload__text"> 将文件拖到此处,或 <em>点击上传</em> </div> <div class="el-upload__tip" slot="tip"> <el-checkbox v-model="upload.updateSupport" />是否更新已经存在的下井次数设置数据 <el-link type="info" style="font-size:12px" @click="downloadTemplate(\'xjszTemplate.xlsx\')" >下载模板</el-link> </div> <div class="el-upload__tip" style="color:red" slot="tip" >提示:仅允许导入“xls”或“xlsx”格式文件!是否全勤中:1代表全勤,0代表固定次数,不得有空值!!</div> </el-upload> <div slot="footer" class="dialog-footer"> <el-button type="primary" @click="submitFileForm">确 定</el-button> <el-button @click="upload.open = false">取 消</el-button> </div> </el-dialog>
通过:visible.sync="upload.open"控制默认隐藏,其中upload是声明的用于存储上传相关的参数的model
需要声明它
export default { name: "Xjcssz", data() { return { // 导入参数 upload: { // 是否显示弹出层 open: false, // 弹出层标题 title: "", // 是否禁用上传 isUploading: false, // 是否更新已经存在的数据 updateSupport: 0, // 设置上传的请求头部 headers: { Authorization: "Bearer " + getToken() }, // 上传的地址 url: process.env.VUE_APP_BASE_API + "/kqgl/xjcssz/importData", },
这里的getToken()是从auth中引入
import { getToken } from "@/utils/auth";
是要获取登录的token
export function getToken() { return Cookies.get(TokenKey) }
文件上传组件使用的是e-upload组件,设置其一些属性
limit限制只能选择一个文件
accept限制能选择的文件类型
headers设置请求头携带token
action设置上传请求的url
disabled设置正在上传时禁用
on-progress设置正在上传时的处理事件
on-success设置上传成功后的事件
auto-upload设置自动提交为false,用来实现手动提交时才提交
data设置上传时携带的数据
drag表示支持可拖拽
设置on-progress正在上传时将其禁用
// 文件上传中处理 handleFileUploadProgress(event, file, fileList) { this.upload.isUploading = true; },
设置on-success上传成功后关闭上传窗口并设置上传可用,然后清除选择的文件并提示导入结果然后刷新数据。
// 文件上传成功处理 handleFileSuccess(response, file, fileList) { this.upload.open = false; this.upload.isUploading = false; this.$refs.upload.clearFiles(); this.$alert(response.msg, "导入结果", { dangerouslyUseHTMLString: true }); this.getList(); },
这里是携带了参数 是否更新已经存在的数据,将其与勾选框进行双向数据绑定
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的下井次数设置数据
并且作为参数在提交时进行传递
:data="{updateSupport:upload.updateSupport}"
注意传递参数时的格式。
然后点击确定按钮时触发事件
<el-button type="primary" @click="submitFileForm">确 定</el-button>
在事件处理中,通过设置的ref属性将表单提交
submitFileForm() { this.$refs.upload.submit(); },
此时表单就会提交到指定的url的后台接口。
来到后台接口
@RequestMapping("/importData") @ResponseBody @ApiOperation("导入下井次数设置数据") public AjaxResult importData(@RequestParam MultipartFile file, @RequestParam boolean updateSupport) throws Exception { ExcelUtil<KqXjcssz> util = new ExcelUtil<KqXjcssz>(KqXjcssz.class); List<KqXjcssz> xjcsszList = util.importExcel(file.getInputStream()); //循环插入数据 for (KqXjcssz xjcssz:xjcsszList) { if(xjcssz.getGh()==null) { return AjaxResult.error("存在为空的工号数据"); } xjcssz.setSzrq(new Date()); xjcssz.setSzr(SecurityUtils.getUsername()); //根据工号查询是否已经存在 Integer count = kqXjcsszService.isExistByGh(xjcssz.getGh()); if(count>0) { //如果设置了更新 if(updateSupport) { kqXjcsszService.updateKqXjcssz(xjcssz); }else { //选择了不更新 啥也不干 } } else { //之前不存在直接插入 kqXjcsszService.insertKqXjcssz(xjcssz); } } return AjaxResult.success("导入成功"); }
这里的后台接口使用@RequestMapping接收,并且使用@ResponseBody注解响应json数据。
接受请求参数时,文件必须是@RequestParam MultipartFile file,且名称为file,如果不进行更改指定的话。
然后第二个参数要与传递时的参数名一致。
然后调用若依自带的工具类
ExcelUtil<KqXjcssz> util = new ExcelUtil<KqXjcssz>(KqXjcssz.class); List<KqXjcssz> xjcsszList = util.importExcel(file.getInputStream());
以及实体类上的注解
/** 工号 */ @Excel(name = "工号") private String gh;
等就能实现解析Excel的数据并获取成对象的list。
这里的导入时的模板建议用下面的导出的EXCEL作为导入模板用。
然后上传时点击下载模板时调用公共下载接口。
Excel导出
页面上添加导出按钮
<el-button type="warning" icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="[\'kqgl:bcgl:export\']" >导出</el-button>
导出按钮对应的处理方法
/** 导出按钮操作 */ handleExport() { const queryParams = this.queryParams; this.$confirm("是否确认导出所有数据项?", "警告", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", }) .then(function () { return exportBcgl(queryParams); }) .then((response) => { this.download(response.msg); }) .catch(function () {}); },
会弹窗提示,点击确定后执行exportBcgl方法,此方法是从外部js中引入
import { exportBcgl, } from "@/api/kqgl/bcgl";
在js方法中
export function exportBcgl(query) { return request({ url: \'/kqgl/bcgl/export\', method: \'get\', params: query }) }
在此方法中发送get请求给SpringBoot后台接口。
其中request是来自request.js,封装的axios发送请求的对象。
在对应的SpringBoot后台接口
@GetMapping("/export") public AjaxResult export(KqBcgl kqBcgl) { List<KqBcgl> list = kqBcglService.getBcListByNameToExport(kqBcgl); ExcelUtil<KqBcgl> util = new ExcelUtil<KqBcgl>(KqBcgl.class); return util.exportExcel(list, "bcgl"); }
直接调用若依自带的Excel工具类就可以实现导出。
其中KqBcgl是对应的业务的实体类,可以使用代码生成工具去生成。
在实体类中通过添加注解的方式就能实现将此属性导出,如果不加此注解则不导出
/** 编号 */ @Excel(name = "编号") private String bcbh;
而且注解里面的name属性就是导出时那列的标题。
关于这个注解还有好多个属性,具体可以参考其源码
public @interface Excel { /** * 导出到Excel中的名字. */ public String name() default ""; /** * 日期格式, 如: yyyy-MM-dd */ public String dateFormat() default ""; /** * 读取内容转表达式(如:0=男,1=女,2=未知) */ public String readConverterExp() default ""; /** * 导出类型(0数字 1字符串) */ public ColumnType cellType() default ColumnType.STRING; /** * 导出时在excel中每个列的高度 单位为字符 */ public double height() default 14; /** * 导出时在excel中每个列的宽 单位为字符 */ public double width() default 16; /** * 文字后缀,如% 90 变成90% */ public String suffix() default ""; /** * 当值为空时,字段的默认值 */ public String defaultValue() default ""; /** * 提示信息 */ public String prompt() default ""; /** * 设置只能选择不能输入的列内容. */ public String[] combo() default {}; /** * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. */ public boolean isExport() default true; /** * 另一个类中的属性名称,支持多级获取,以小数点隔开 */ public String targetAttr() default ""; /** * 字段类型(0:导出导入;1:仅导出;2:仅导入) */ Type type() default Type.ALL; public enum Type { ALL(0), EXPORT(1), IMPORT(2); private final int value; Type(int value) { this.value = value; } public int value() { return this.value; } } public enum ColumnType { NUMERIC(0), STRING(1); private final int value; ColumnType(int value) { this.value = value; } public int value() { return this.value; } } }
还有一种情况是,在导出前的查询数据的方法,如果调用的是和查询接口一样的方法。
某些属性比如某某状态等需要用到字典表的列。在查询接口可能就是直接查询出来,返回值
直接就是1或者2等这些字典的值。然后返回给前端,前端再进行格式化显示。
但是在导出时必须要显示对应的字典表的label,所以需要修改查询数据的方法getBcListByNameToExport
将要查询的表与字典表相关联,查询出其label值作为对应的属性,如果有多个需要关联字典表的属性,则关联两次,下面是示例代码
<select id="getBcListByNameToExport" parameterType="KqBcgl" resultMap="KqBcglResult"> SELECT b.id, b.bcbh, b.bcmc, s.dict_label AS bclx, sfkt, b.xss, b.jgs, b.sfyb, kqts, b.mzxx, b.bz, s1.dict_label AS jxbclx FROM kq_bcgl b LEFT JOIN sys_dict_data s ON b.bclx = s.dict_value AND s.dict_type = "kq_kqgl_bcgl_bclx" LEFT JOIN sys_dict_data s1 ON b.jxbclx = s1.dict_value AND s1.dict_type = "kq_kqgl_bcgl_jxbclx" <where> <if test="bcmc != null and bcmc != \'\'"> and bcmc LIKE "%"#{bcmc}"%"</if> </where> </select>
那么点击导出按钮就能实现导出了