SpringBoot 项目实战:ECharts 数据可视化 + POI Excel 报表导出完整版教程

张开发
2026/5/26 20:46:35 15 分钟阅读
SpringBoot 项目实战:ECharts 数据可视化 + POI Excel 报表导出完整版教程
本文基于 SpringBoot 后端项目完整实现ECharts 图形报表展示会员折线图、套餐饼图、运营数据统计、POI 报表导出功能全程代码可直接复用适合后端数据可视化场景学习。一、ECharts 基础入门1.1 ECharts 简介EChartsEnterprise Charts是百度开源的商业级数据可视化库基于 JavaScript 实现兼容 PC / 移动端与主流浏览器底层依赖 ZRender 矢量图形库支持折线图、饼图、柱状图、地图等丰富图表类型。官网https://echarts.apache.org/下载npm install echarts 或下载 dist 包引入 echarts.js1.2 5 分钟快速上手 ECharts引入 js 文件htmlscript srcecharts.js/script准备 DOM 容器必须设置宽高div idmain stylewidth: 600px;height:400px;/div初始化并渲染图表javascriptvar myChart echarts.init(document.getElementById(main)); var option { title: { text: ECharts 入门示例 }, tooltip: {}, legend: { data:[销量] }, xAxis: { data: [衬衫,羊毛衫,雪纺衫,裤子,高跟鞋,袜子] }, yAxis: {}, series: [{ name: 销量, type: bar, data: [5, 20, 36, 10, 10, 20] }] }; myChart.setOption(option);二、会员数量折线图实现2.1 需求说明展示过去 12 个月每月会员总量直观反映会员增长趋势。2.2 前端页面实现ReportMember.vue准备图表容器发送 Ajax 请求获取后台数据基于 ECharts 渲染折线图数据格式要求json{ data:{ months:[2019.01,2019.02,2019.03,2019.04,...], memberCount:[3,4,8,10,...] }, flag:true, message:获取会员统计数据成功 }2.3 后台代码实现2.3.1 ControllerReportControllerjava/** * 统计报表 Controller * 用于提供各类统计报表数据包括会员统计、套餐预约统计等 * author hg */ RestController RequestMapping(/report) public class ReportController { Autowired private ReportService reportService; /** * 查询近 12 个月的会员数量统计报表 * 统计过去 12 个月每个月的会员注册数量用于折线图展示 * * return Result 包含统计数据的统一响应对象 * 成功时返回 * - memberCount: ListInteger 12 个月的会员数量列表 * - monthList: ListString 12 个月的月份列表格式yyyy-MM */ RequestMapping(/getMemberReport) public Result getMemberReport(){ try { // 调用 Service 层获取会员统计数据 MapString,Object map reportService.getMemberReport(); return new Result(true, MessageConstant.GET_MEMBER_NUMBER_REPORT_SUCCESS, map); }catch (Exception e){ e.printStackTrace(); return new Result(false, MessageConstant.GET_MEMBER_NUMBER_REPORT_FAIL); } } }2.3.2 Service 接口javapublic interface ReportService { MapString, Object getMemberReport(); }2.3.3 Service 实现类java/** * 统计报表 Service 实现类 * 提供会员统计、套餐预约统计等业务逻辑实现 * author hg */ Service public class ReportServiceImpl implements ReportService { Autowired private ReportMapper reportMapper; /** * 查询近 12 个月的会员数量统计 * 从当前月份往前推算 12 个月统计每个月的会员注册数量 * * return MapString, Object 包含会员统计数据 * - memberCount: ListInteger 12 个月的会员数量列表 * - monthList: ListString 12 个月的月份列表格式yyyy-MM */ Override public MapString, Object getMemberReport() { // 日期格式化器格式为 yyyy-MM如2026-04 SimpleDateFormat dateFormat new SimpleDateFormat(yyyy-MM); // 获取当前日期的日历实例 Calendar calendar Calendar.getInstance(); // 往前推 12 个月作为统计的起始月份 calendar.add(Calendar.MONTH, -12); // 存储 12 个月的月份列表 ListString list new ArrayList(); // 存储 12 个月的会员数量列表 ListInteger memberCountList new ArrayList(); // 循环 12 次统计最近 12 个月的数据 for (int i 0; i 12; i) { // 月份加 1向后推算一个月 calendar.add(Calendar.MONTH, 1); // 格式化为 yyyy-MM 字符串如2026-04 String formatDate dateFormat.format(calendar.getTime()); // 根据月份查询该月的会员注册数量 Integer memberCount reportMapper.findMemberCountByMonth(formatDate); // 将月份添加到月份列表 list.add(formatDate); // 将会员数量添加到数量列表 memberCountList.add(memberCount); } // 封装返回结果 MapString, Object map new HashMap(); map.put(memberCount, memberCountList); // 会员数量列表 map.put(monthList, list); // 月份列表 return map; } }2.3.4 Dao 接口javapublic interface ReportMapper { Integer findMemberCountByMonth(String formatDate); }2.3.5 Mapper 映射文件xml?xml version1.0 encodingUTF-8 ? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.hg.mapper.ReportMapper select idfindMemberCountByMonth parameterTypestring resultTypeint select count(id) from t_member where regTime LIKE concat(#{regTime}, %) /select /mapper三、套餐预约占比饼形图实现3.1 需求说明以饼图展示各体检套餐的预约占比便于分析热门套餐。3.2 前端页面实现ReportSetmeal.vue定义饼图容器Ajax 请求数据并 setOption 渲染数据格式要求json{ data:{ setmealNames:[套餐1,套餐2,套餐3], setmealCount:[{name:套餐1,value:10},{name:套餐2,value:30}] }, flag:true, message:获取套餐统计数据成功 }前端核心代码javascriptaxios.get(/report/getSetmealReport.do).then((res){ myChart1.setOption({ title : { text: 套餐预约占比, x:center }, tooltip : { trigger: item, formatter: {a} br{b} : {c} ({d}%) }, legend: { orient: vertical, left: left, data: res.data.data.setmealNames }, series : [{ name: 套餐预约占比, type: pie, radius : 55%, center: [50%, 60%], data:res.data.data.setmealCount }] }); });3.3 后台代码实现3.3.1 ControllerjavaRequestMapping(/getSetMealReport) public Result getSetMealReport(){ try { // 调用 Service 层获取套餐预约统计数据 ListMapString,Object map reportService.getSetMealReport(); return new Result(true, MessageConstant.GET_SETMEAL_COUNT_REPORT_SUCCESS, map); }catch (Exception e){ e.printStackTrace(); return new Result(false, MessageConstant.GET_SETMEAL_COUNT_REPORT_FAIL); } }3.3.2 Service 接口javaListMapString, Object getSetMealReport();3.3.3 Service 实现类java/** * 查询套餐预约人数占比统计 * 统计各个体检套餐的预约人数用于饼图展示 * * return ListMapString, Object 套餐预约统计数据列表 * 每个 Map 包含 * - name: String 套餐名称 * - value: Integer 该套餐的预约数量 */ Override public ListMapString, Object getSetMealReport() { // 直接调用 Mapper 层查询套餐预约统计数据 // SQL 语句会关联 t_setmeal 和 t_order 表按套餐名称分组统计预约数量 return reportMapper.getSetMealReport(); }3.3.4 Dao 接口javaListMapString, Object getSetMealReport();3.3.5 Mapper 映射文件xmlselect idgetSetMealReport resultTypemap SELECT t1.name, COUNT(t2.id) value FROM t_setmeal t1,t_order t2 WHERE t1.id t2.setmeal_id GROUP BY t1.name /select四、运营数据统计页面实现4.1 需求说明以表格形式展示会员数据、预约到诊数据、热门套餐支持导出 Excel。4.2 前端页面ReportBusiness.vue定义数据模型绑定页面created 钩子请求数据提供导出按钮数据模型javascriptdata:{ reportData:{ reportDate:null, todayNewMember :0, totalMember :0, thisWeekNewMember :0, thisMonthNewMember :0, todayOrderNumber :0, todayVisitsNumber :0, thisWeekOrderNumber :0, thisWeekVisitsNumber :0, thisMonthOrderNumber :0, thisMonthVisitsNumber :0, hotSetmeal :[] } }请求数据javascriptcreated(){ axios.get(/report/getBusinessReportData.do).then((res){ if(res.data.flag){ this.reportData res.data.data; }else{ this.$message.error(res.data.message); } }) }导出按钮htmlel-button clickexportExcel导出Excel/el-buttonjavascriptexportExcel(){ window.location.href /report/exportBusinessReport; }4.3 后台代码实现4.3.1 ControllerjavaRequestMapping(/getBusinessReportData) public Result getBusinessReportData(){ try { MapString,Object map reportService.getBusinessReportData(); return new Result(true,MessageConstant.GET_BUSINESS_REPORT_SUCCESS,map); } catch (Exception e) { e.printStackTrace(); return new Result(false,MessageConstant.GET_BUSINESS_REPORT_FAIL); } }4.3.2 Service 接口javapublic interface ReportService { MapString, Object getBusinessReportData() throws Exception; }4.3.3 Service 实现类javaService(interfaceClass ReportService.class) Transactional public class ReportServiceImpl implements ReportService { Autowired MemberDao memberDao; Autowired OrderDao orderDao; Override public MapString, Object getBusinessReportData() throws Exception{ String today DateUtils.parseDate2String(DateUtils.getToday()); String thisWeekMonday DateUtils.parseDate2String(DateUtils.getThisWeekMonday()); String firstDay4ThisMonth DateUtils.parseDate2String(DateUtils.getFirstDay4ThisMonth()); Integer todayNewMember memberDao.findMemberCountByDate(today); Integer totalMember memberDao.findMemberTotalCount(); Integer thisWeekNewMember memberDao.findMemberCountAfterDate(thisWeekMonday); Integer thisMonthNewMember memberDao.findMemberCountAfterDate(firstDay4ThisMonth); Integer todayOrderNumber orderDao.findOrderCountByDate(today); Integer thisWeekOrderNumber orderDao.findOrderCountAfterDate(thisWeekMonday); Integer thisMonthOrderNumber orderDao.findOrderCountAfterDate(firstDay4ThisMonth); Integer todayVisitsNumber orderDao.findVisitsCountByDate(today); Integer thisWeekVisitsNumber orderDao.findVisitsCountAfterDate(thisWeekMonday); Integer thisMonthVisitsNumber orderDao.findVisitsCountAfterDate(firstDay4ThisMonth); ListMap hotSetmeal orderDao.findHotSetmeal(); MapString,Object map new HashMap(); map.put(reportDate,today); map.put(todayNewMember,todayNewMember); map.put(totalMember,totalMember); map.put(thisWeekNewMember,thisWeekNewMember); map.put(thisMonthNewMember,thisMonthNewMember); map.put(todayOrderNumber,todayOrderNumber); map.put(thisWeekOrderNumber,thisWeekOrderNumber); map.put(thisMonthOrderNumber,thisMonthOrderNumber); map.put(todayVisitsNumber,todayVisitsNumber); map.put(thisWeekVisitsNumber,thisWeekVisitsNumber); map.put(thisMonthVisitsNumber,thisMonthVisitsNumber); map.put(hotSetmeal,hotSetmeal); return map; } }4.3.4 Dao 与 MapperOrderDaojavaInteger findOrderCountByDate(String today); Integer findOrderCountAfterDate(String thisWeekMonday); Integer findVisitsCountByDate(String today); Integer findVisitsCountAfterDate(String thisWeekMonday); ListMap findHotSetmeal();MemberDaojavaInteger findMemberCountByDate(String today); Integer findMemberTotalCount(); Integer findMemberCountAfterDate(String thisWeekMonday);SQL 语句关键xml!-- 热门套餐取前4 -- select idfindHotSetmeal resultTypemap SELECT s.name, COUNT(o.id) setmeal_count, COUNT(o.id)/(SELECT COUNT(id) FROM t_order) proportion FROM t_order o,t_setmeal s WHERE s.ido.setmeal_id GROUP BY o.setmeal_id ORDER BY setmeal_count DESC LIMIT 0,4; /select五、POI 实现 Excel 报表导出5.1 实现思路使用 Excel 模板文件提前设置样式、合并单元格后台读取模板并填充数据通过输出流实现浏览器下载。5.2 准备工作准备模板文件report_template.xlsx放入项目template目录5.3 后台导出接口javaRequestMapping(/exportBusinessReport) public Result exportBusinessReport(HttpServletRequest request, HttpServletResponse response){ try{ MapString,Object result reportService.getBusinessReportData(); String reportDate (String) result.get(reportDate); Integer todayNewMember (Integer) result.get(todayNewMember); Integer totalMember (Integer) result.get(totalMember); Integer thisWeekNewMember (Integer) result.get(thisWeekNewMember); Integer thisMonthNewMember (Integer) result.get(thisMonthNewMember); Integer todayOrderNumber (Integer) result.get(todayOrderNumber); Integer thisWeekOrderNumber (Integer) result.get(thisWeekOrderNumber); Integer thisMonthOrderNumber (Integer) result.get(thisMonthOrderNumber); Integer todayVisitsNumber (Integer) result.get(todayVisitsNumber); Integer thisWeekVisitsNumber (Integer) result.get(thisWeekVisitsNumber); Integer thisMonthVisitsNumber (Integer) result.get(thisMonthVisitsNumber); ListMap hotSetmeal (ListMap) result.get(hotSetmeal); String filePath request.getSession().getServletContext().getRealPath(template) File.separatorreport_template.xlsx; XSSFWorkbook excel new XSSFWorkbook(new FileInputStream(new File(filePath))); XSSFSheet sheet excel.getSheetAt(0); XSSFRow row sheet.getRow(2); row.getCell(5).setCellValue(reportDate); row sheet.getRow(4); row.getCell(5).setCellValue(todayNewMember); row.getCell(7).setCellValue(totalMember); row sheet.getRow(5); row.getCell(5).setCellValue(thisWeekNewMember); row.getCell(7).setCellValue(thisMonthNewMember); row sheet.getRow(7); row.getCell(5).setCellValue(todayOrderNumber); row.getCell(7).setCellValue(todayVisitsNumber); row sheet.getRow(8); row.getCell(5).setCellValue(thisWeekOrderNumber); row.getCell(7).setCellValue(thisWeekVisitsNumber); row sheet.getRow(9); row.getCell(5).setCellValue(thisMonthOrderNumber); row.getCell(7).setCellValue(thisMonthVisitsNumber); int rowNum 12; for(Map map : hotSetmeal){ String name (String) map.get(name); Long setmeal_count (Long) map.get(setmeal_count); BigDecimal proportion (BigDecimal) map.get(proportion); row sheet.getRow(rowNum ); row.getCell(4).setCellValue(name); row.getCell(5).setCellValue(setmeal_count); row.getCell(6).setCellValue(proportion.doubleValue()); } OutputStream out response.getOutputStream(); response.setContentType(application/vnd.ms-excel); response.setHeader(content-Disposition, attachment;filenamereport.xlsx); excel.write(out); out.flush(); out.close(); excel.close(); return null; }catch (Exception e){ return new Result(false,MessageConstant.GET_BUSINESS_REPORT_FAIL); } }六、总结本文完整实现了ECharts 折线图展示会员月度增长趋势ECharts 饼图展示套餐预约占比运营数据表格统计会员、预约、到诊、热门套餐POI Excel 导出基于模板快速生成美观报表整套方案适用于医疗、电商、管理后台等需要数据可视化与报表导出的 SpringBoot 项目代码结构清晰、可直接移植使用。

更多文章