SpringBoot+Vue+ElementUI实现导入和导出

1.导出数据

1.1.后端需要导入的maven依赖

我用的是原生的poi,没有使用easyExcel和hutool,因为是作为学习使用,了解原生代码

 <!-- excel工具 -->
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml</artifactId>
                <version>4.1.2</version>
            </dependency>

            <!--            poi导出excel-->
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi</artifactId>
                <version>4.1.2</version>
            </dependency>

1.2编写controller

  • 因为是在浏览器下载,所以是通过response.getOutputStream响应回去的
  • 我这里没有使用poi的工具类,实际项目开发中会使用工具类简化代码开发的,不用害怕

最主要的是,有时候我们有些业务需要导出表格:

  • 比如我需要导出所有的数据,这个时候直接导出,都不需要通过SQL过滤
  • 但是如果用户先通过搜索再导出数据呢?所以需要一个表单来控制,当有用户搜索后,点击导出按钮,我们可以将前端搜索框中的表单数据一并通过导出按钮事件传给后端
/**
     * 导出操作日志excel导出操作日志excel
     * @param pageQuerySysLogOperationModel
     * @param response
     * @throws IOException
     */
    @ApiOperation("导出操作日志excel")
    @PostMapping("/export")
    public void export(@RequestBody @Valid PageQuerySysLogOperationModel pageQuerySysLogOperationModel, HttpServletResponse response) throws IOException {
        List<SysLogOperation> list = sysLogOperationService.selectSysLogOperationList(pageQuerySysLogOperationModel);
        // 新建一个工作表
        XSSFWorkbook workbook = new XSSFWorkbook();
        // 新建一个表单
        XSSFSheet sheet = workbook.createSheet("操作日志");
        // 第一行,也是标题行
        XSSFRow rowTitle = sheet.createRow(0);
        rowTitle.createCell(0).setCellValue("操作序号");
        rowTitle.createCell(1).setCellValue("操作描述");
        rowTitle.createCell(2).setCellValue("请求地址");
        rowTitle.createCell(3).setCellValue("请求方式");
        rowTitle.createCell(4).setCellValue("请求参数");
        rowTitle.createCell(5).setCellValue("请求时长");
        rowTitle.createCell(6).setCellValue("操作地址");
        rowTitle.createCell(7).setCellValue("操作人员");
        rowTitle.createCell(8).setCellValue("状态");
        rowTitle.createCell(9).setCellValue("操作时间");
        // 数据从第一行开始
        int rowNum = 1;
        for (SysLogOperation sysLogOperation : list) {
            Row row = sheet.createRow(rowNum);
            row.createCell(0).setCellValue(sysLogOperation.getId());
            row.createCell(1).setCellValue(sysLogOperation.getOperation());
            row.createCell(2).setCellValue(sysLogOperation.getRequestUri());
            row.createCell(3).setCellValue(sysLogOperation.getRequestMethod());
            row.createCell(4).setCellValue(sysLogOperation.getRequestParams());
            row.createCell(5).setCellValue(sysLogOperation.getRequestTime());
            row.createCell(6).setCellValue(sysLogOperation.getIp());
            row.createCell(7).setCellValue(sysLogOperation.getUsername());
            row.createCell(8).setCellValue(sysLogOperation.getStatus() == 0 ? "成功":"失败");
            row.createCell(9).setCellValue(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date.from(sysLogOperation.getCreateTime().atZone(ZoneId.systemDefault()).toInstant())));
            rowNum++;
        }
        String fileName = "操作日志_"+ String.valueOf(System.currentTimeMillis()) +".xlsx";
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Access-Control-Expose-Headers","Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename="+ URLEncoder.encode(fileName,"UTF-8"));
        response.setHeader("Content-Type", "application/vnd.ms-excel");
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setCharacterEncoding("UTF-8");
        OutputStream outputStream = response.getOutputStream();
        workbook.write(outputStream);
        outputStream.flush();
        outputStream.close();
        workbook.close();
    }

浏览器下载最主要的一部分代码

String fileName = "操作日志_"+ String.valueOf(System.currentTimeMillis()) +".xlsx";
response.setCharacterEncoding("UTF-8");
response.setHeader("Access-Control-Expose-Headers","Content-Disposition");
response.setHeader("Content-Disposition", "attachment;filename="+ URLEncoder.encode(fileName,"UTF-8"));
response.setHeader("Content-Type", "application/vnd.ms-excel");
response.setContentType("application/vnd.ms-excel;charset=utf-8");
response.setCharacterEncoding("UTF-8");
OutputStream outputStream = response.getOutputStream();
workbook.write(outputStream);
outputStream.flush();

1.3 前端代码

因为前端代码太多了,只整些核心关键代码

<el-button type="primary" @click="handleExport"
        >导出<i class="el-icon-top"></i
      ></el-button>

<script>
export default {
	methods:{
		handleExport() {
		   this.request
		     .post("/manager/sys/operation/export", this.queryForm, {
		       responseType: "blob",
		     })
		     .then((res) => {
		       if (!res.data) {
		         return;
		       }
		       const blob = new Blob([res.data], {
		         type: "application/vnd.ms-excel",
		       }); // 构造一个blob对象来处理数据,并设置文件类型
		       const href = URL.createObjectURL(blob); //创建新的URL表示指定的blob对象
		       const a = document.createElement("a"); //创建a标签
		       a.style.display = "none";
		       a.href = href; // 指定下载链接
		       let fileName = res.headers["content-disposition"];
		       fileName = fileName.split("=")[1];
		       a.download = decodeURIComponent(fileName); //指定下载文件名
		       a.click(); //触发下载
		       URL.revokeObjectURL(a.href); //释放URL对象
		     });
		 },
	}
}
</script>

2. 导入数据

2.1 编写后端代码

难点在于excel中数据的格式,在转换中可能会报错

public R<String> importData(@RequestParam("file") MultipartFile multipartFile) throws IOException, ParseException {
        // 获取io输入流
        InputStream inputStream = multipartFile.getInputStream();
        // 将输入流读取到workbook中
        Workbook workbook = new XSSFWorkbook(inputStream);
        for (Sheet sheet : workbook) {
            int lastRowNum = sheet.getLastRowNum();
            List<SysLogOperation> sysLogOperations = new ArrayList<>();
            for (int i = 1; i <= lastRowNum; i++) {
                SysLogOperation sysLogOperation = new SysLogOperation();
                Row row = sheet.getRow(i);
                String operation = row.getCell(1).getStringCellValue();
                sysLogOperation.setOperation(operation);
                String requestURI = row.getCell(2).getStringCellValue();
                sysLogOperation.setRequestUri(requestURI);
                String requestMethod = row.getCell(3).getStringCellValue();
                sysLogOperation.setRequestMethod(requestMethod);
                String requestParams = row.getCell(4).getStringCellValue();
                sysLogOperation.setRequestParams(requestParams);
                double requestTime = row.getCell(5).getNumericCellValue();
                sysLogOperation.setRequestTime((int) requestTime);
                String ip = row.getCell(6).getStringCellValue();
                sysLogOperation.setIp(ip);
                String username = row.getCell(7).getStringCellValue();
                sysLogOperation.setUsername(username);
                String status = row.getCell(8).toString();
                sysLogOperation.setStatus(status.equals("成功")?(short)0:(short)1);
                String createTime = row.getCell(9).getStringCellValue();
                sysLogOperation.setCreateTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(createTime).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
                sysLogOperations.add(sysLogOperation);
            }
            Assert.isTrue(sysLogOperationService.saveBatch(sysLogOperations),"添加失败");
        }
        return R.ok();
    }

2.2 前端代码

<el-upload
        class="upload-demo"
        action="http://localhost:8080/manager/sys/operation/import"
        :on-success="handleUploadSuccess"
        accep=".xlsx"
        :show-file-list="false"
        :before-upload="beforeUpload"
        style="display: inline-block; margin-right: 10px"
      >
        <el-button type="primary">导入<i class="el-icon-bottom"></i></el-button>
      </el-upload>

<script>
export default {
	methods:{
		beforeUpload(file) {
	      const isLimit = file.size / 1024 / 1024 < 10;
	
	      if (file.type.indexOf("application/vnd") === -1) {
	        this.$message.error("上传的文件格式不对吧,亲!");
	        return false;
	      }
	
	      if (!isLimit) {
	        this.$message.error("上传的文件不能大于10M!");
	        return false;
	      }
	    },
	    handleUploadSuccess(res, file, fileList) {
	      let formData = new FormData();
	      formData.append("file", file);
	      this.$message.success(res.msg);
	    },
	  },
	}
}

</script>

这里有一些配置需要结合ElementUI官方文档

  • accept 接受上传的文件类型(thumbnail-mode 模式下此参数无效)
  • on-success 文件上传成功时的钩子,这里一定要将文件传输过去,不然后端接收不到数据
  • before-upload 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。可以进行文件大小的判断,和文件格式是否正确的判断
  • show-file-list 是否显示已上传文件列表
  • action 必选参数,上传的地址

3.遇到的一些细节问题

3.1 时间日期格式的转换

因为我在项目中的日期类型是LocalDateTime类型所以他的值是这样的:2022-09-13T17:09:07
每次中间会有一个‘T’,所以我们可以使用@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”)把他的格式转换一下
然后就是时间格式的问题可以使用SimpleDateFormat(“这里写格式”)

3.2 LocalDateTime和Date的相互转换

  • LocalDateTime、Date 在数据库中对应的类型 datetime
  • Date 和 SimpleDateFormat 都是线程不安全的
  • LocalDateTime 和 DateTimeFormatter 都是线程安全的

LocalDateTime 转为Date类型

public static Date localDateTimeToDate(LocalDateTime localDateTime) {
	ZoneId zoneId = ZoneId.systemDefault();
	ZonedDateTime zdt = localDateTime.atZone(zoneId);
	Date date = Date.from(zdt.toInstant());
	return date;
}

Date类型转换为LocalDateTime


public static LocalDateTime dateToLocalDateTime(Date date) {
	Instant instant = date.toInstant();
	ZoneId zoneId = ZoneId.systemDefault();
	LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime();
	return localDateTime;
}

3.3 response响应回去的中文数据乱码

在进行文件通过浏览器下载的时候,后端传给前端一个文件名,文件名是中文的,会出现乱码,是如何解决的呢?
后端代码

String fileName = "操作日志_"+ String.valueOf(System.currentTimeMillis()) +".xlsx";
response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(fileName,"UTF-8"));     

前端代码:

handleExport() {
      this.request
        .post("/manager/sys/operation/export", this.queryForm, {
          responseType: "blob",
        })
        .then((res) => {
          if (!res.data) {
            return;
          }
          const blob = new Blob([res.data], {
            type: "application/vnd.ms-excel",
          }); // 构造一个blob对象来处理数据,并设置文件类型
          const href = URL.createObjectURL(blob); //创建新的URL表示指定的blob对象
          const a = document.createElement("a"); //创建a标签
          a.style.display = "none";
          a.href = href; // 指定下载链接
          let fileName = res.headers["content-disposition"];
          fileName = fileName.split("=")[1];
          a.download = decodeURIComponent(fileName); //指定下载文件名
          a.click(); //触发下载
          URL.revokeObjectURL(a.href); //释放URL对象
        });
    },

后端通过: URLEncoder.encode(fileName,“UTF-8”);
前端通过: decodeURIComponent(fileName);

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
摘 要 在如今这个人才需求量大的时代,各方企业为了永葆企业的活力与生机,在不断开 拓进取的同时,又广泛纳用人才,为企业的长久发展奠定了基础。于是,各个企业与部 门机构,都不可避免地会接触到人力资源管理的问题。 Hrm 是一款人力资源管理系统,其主要功能模块有员工个人信息修改、请假、员工 的薪资管理、考勤管理、社保管理。其中考勤管理实现了员工考勤状态的修改与员工考 勤月报表的导出,以及通过员工考勤记录的导入实现员工考勤状态的判断。社保管理, 主要实现了员工社保的计算以及明细的修改。薪资管理,实现了员工工资的调整,以及 员工月工资报表的导出。 本项目采用了前后端分离的技术,前端是基于 Vue+ElementUI+Axios 开发的,后端 则是基于 Spring Boot+MyBatis Plus+ Jwt+Mysql。本项目实现了权限菜单管理,通过员 工的权限动态渲染菜单,并动态生成路由。通过 Jwt token 来判断当前登录的员工以及 员工的登录状态。 关键词:人力资源管理系统,Spring Boot,Vue,权限管 人力资源管理是企业运营中必不可少的一环,它关系到企业的前途与发展。尤其对 于中小微企业来说,对企业的发展有着举足轻重的作用。随着近年来,政府对创业项目 的大力扶持,我国创业型企业蓬勃发展。据统计,2019 年,我国创业企业数量已达 1810 万余家,占全国企业数的 97%,截止 2020 年,我国创业企业数量达到了 2030 万,同比 增长 10%。虽然我国创业企业的基数在不断增大,但是能够长久存活的企业却少之又少。 在创业初期,随着企业初具规模,大多数创业者开始将主要精力集中在市场调研和 开发产品上,而忽略了团队的内部管理。据调查,中国企业的平均寿命是 7.02 年,但 70%的企业存活不超过 5 年,究其原因有很多,其中最重要的一点就是,人力资源管理 未能有效推动企业向前发展
实现文件上传需要完成以下几个步骤: 1. 在前端页面添加上传文件的表单,并绑定上传事件,将文件上传到后端。 2. 在后端接收前端上传的文件,并保存到服务器上。 下面是一个简单的示例,演示如何使用 Spring Boot + Vue.js + ElementUI 实现文件上传的功能。 前端代码: ```html <template> <div> <el-upload class="upload-demo" ref="upload" :action="uploadUrl" :on-success="handleSuccess" :before-upload="beforeUpload" :file-list="fileList"> <el-button size="small" type="primary">点击上传</el-button> <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div> </el-upload> </div> </template> <script> export default { data() { return { uploadUrl: "/upload", fileList: [] }; }, methods: { // 上传前的钩子函数 beforeUpload(file) { const isJPG = file.type === "image/jpeg" || file.type === "image/png"; const isLt500K = file.size / 1024 < 500; if (!isJPG) { this.$message.error("上传头像图片只能是 JPG/PNG 格式!"); } if (!isLt500K) { this.$message.error("上传头像图片大小不能超过 500KB!"); } return isJPG && isLt500K; }, // 上传成功的回调函数 handleSuccess(response, file, fileList) { this.fileList = fileList; this.$emit("upload-success", response); } } }; </script> ``` 在上面的代码中,我们使用了 ElementUI 的 Upload 组件,将上传文件的表单渲染到页面上。同时,我们还绑定了 beforeUpload 和 handleSuccess 两个事件,用于上传前的校验和上传成功后的回调。 注意:在使用 beforeUpload 钩子函数时,需要返回一个布尔值,表示是否允许上传。如果返回 false,上传将会被终止。 后端代码: ```java @RestController public class FileUploadController { @PostMapping("/upload") public String uploadFile(@RequestParam("file") MultipartFile file) throws Exception { if (file.isEmpty()) { return "上传文件为空!"; } // 获取文件名 String fileName = file.getOriginalFilename(); // 获取文件的后缀名 String suffixName = fileName.substring(fileName.lastIndexOf(".")); // 设置文件存储路径 String filePath = "/tmp/"; // 重新生成文件名 fileName = UUID.randomUUID() + suffixName; // 创建文件对象 File dest = new File(filePath + fileName); // 检测是否存在目录 if (!dest.getParentFile().exists()) { dest.getParentFile().mkdirs(); } // 保存文件 file.transferTo(dest); return "文件上传成功!"; } } ``` 在上面的代码中,我们使用了 Spring Boot 的 @PostMapping 注解,将上传文件的接口地址映射到 /upload 路径上。同时,我们还使用了 MultipartFile 类型的参数来接收前端上传的文件。 在接收到文件后,我们首先判断文件是否为空。如果不为空,我们通过 MultipartFile 类型的方法获取文件名和后缀名,并将文件存储到指定的路径下。最后,我们返回一个字符串,表示上传成功。 需要注意的是,在保存文件时,我们使用了 transferTo 方法。该方法会将上传文件保存到指定的路径下,并自动关闭文件流。同时,我们还判断了目录是否存在,如果不存在,就创建一个新的目录。 综上所述,通过以上的代码,我们可以实现 Spring Boot + Vue.js + ElementUI 的文件上传功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

毕竟尹稳健

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值