若依(ruoyi)前后端分离项目集成积木报表
致敬:
1:致敬若依开源项目(本文使用的是前后端分离版本:3.8.9)
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>若依官网:https://www.ruoyi.vip/
复制代码 2:致敬积木开源项目
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>积木报表官网:https://www.jimureport.com/
复制代码 3:致敬CSDN大神的文章(我是根据他的原文一步步设置的,前端设置有一些改动,如有版权问题,可随时接洽本人删除)
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>原文地址:https://blog.csdn.net/qq_55896432/article/details/145060090
复制代码 背景:
写在前面:
搭建这个项目是因为自己想记录日常使用的一些数据(练琴统计,钢琴课程数量,花费等等这些),然后找了一些BI发现都不是很得当,目前积木是最得当的(开源且集成程度高),所以记录了一下整体的操纵步骤。
目前整体使用感觉照旧可以的,数据源数据集设置都很方便,必要的控件也都够用。
项目运行的条件条件:
能正常运行若依前后端分离版本就OK.
结果(部分截图)
平凡报表管理菜单
平凡报表设计
数据源&数据集设置
大屏报表管理菜单
大屏设计
数据源&数据集设置
后端设置
ruoyi-common模块下pom.xml文件添加依赖
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template><dependency>
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template> <groupId>org.jeecgframework.jimureport</groupId>
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template> <artifactId>jimureport-spring-boot-starter</artifactId>
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template> <version>1.9.2</version>
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template></dependency>
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template><dependency>
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template> <groupId>org.jeecgframework.jimureport</groupId>
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template> <artifactId>jimubi-spring-boot-starter</artifactId>
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template> <version>1.9.1</version>
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template></dependency>
复制代码
添加完重新加载maven
ruoyi-admin模块修改application.yml文件,新增积木报表相关设置
- jeecg :
- # 权限配置
- permission:
- # 报表权限配置
- report:
- # 查询权限符号,配置此权限代表只能查看报表
- query: jeecg:report:query
- # 修改权限符号,配置此权限代表拥有报表所有权限
- edit: jeecg:report:edit
- # 大屏权限配置
- drag:
- # 查询权限符号,配置此权限代表只能查看大屏
- query: jeecg:drag:query
- # 修改权限符号,配置此权限代表拥有大屏所有权限
- edit: jeecg:drag:edit
- jmreport:
- #自定义项目前缀
- # 现在是开发环境下的前端访问前缀,部署项目时,需要切换到生成环境访问前缀
- customPrePath: /dev-api
复制代码- 注意:
- 1.目前报表和大屏分别设置两个类型的权限,其中query权限表示只能查看,edit权限表示用于一切权限。
- 2.customPrePath是用来配置前端访问后端的接口的前缀,当前是开发环境配置为/dev-api没问题。
- 3.项目发布时,一定将这个配置改为生产环境下访问前缀,如:/prod-api。
复制代码 允许匿名访问ruoyi-framework模块下修改SecurityConfig.java文件
增加积木报表和大屏匿名访问权限。
- , "/jmreport/**", "/drag/**"
复制代码
新建积木报表相关设置类
在com.ruoyi.framework包下增加包report,在report包下新增config包,在cofig包下新增ReportConfig.java类,并在此类中添加以下代码。
- package com.ruoyi.framework.report.config;
-
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Component;
-
- @Component
- public class ReportConfig {
-
- // 报表查询权限
- @Value("${jeecg.permission.report.query}")
- private String reportQueryPermission;
-
- // 报表修改权限
- @Value("${jeecg.permission.report.edit}")
- private String reportEditPermission;
-
- // 大屏查看权限
- @Value("${jeecg.permission.drag.query}")
- private String dragQueryPermission;
-
- //大屏修改权限
- @Value("${jeecg.permission.drag.edit}")
- private String dragEditPermission;
-
- public String getReportQueryPermission() {
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>return reportQueryPermission;
- }
-
- public void setReportQueryPermission(String reportQueryPermission) {
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>this.reportQueryPermission = reportQueryPermission;
- }
-
- public String getReportEditPermission() {
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>return reportEditPermission;
- }
-
- public void setReportEditPermission(String reportEditPermission) {
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>this.reportEditPermission = reportEditPermission;
- }
-
- public String getDragQueryPermission() {
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>return dragQueryPermission;
- }
-
- public void setDragQueryPermission(String dragQueryPermission) {
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>this.dragQueryPermission = dragQueryPermission;
- }
-
- public String getDragEditPermission() {
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>return dragEditPermission;
- }
-
- public void setDragEditPermission(String dragEditPermission) {
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>this.dragEditPermission = dragEditPermission;
- }
- }
复制代码
重写若依框架getLoginUser()方法
打开web.service包下的TokenService.java文件,将自带的getLoginUser方法解释掉
新增代码
在report包下新增service包下,在service包下新增ReportTokenService.java类,并且在此类中添加以下代码

贴一下原作者的注意事项:
设置拦截器
在report包下新增interceptor包,在interceptor包下新增ReportInterceptor.java类,并且在此类中添加以下代码

原作者说的注意事项:
打开config包下的ResourcesConfig.java文件,注入ReportInterceptor拦截器,并且注册拦截器和设置拦截规则
修改addInterceptors()方法- /**
- * 自定义拦截规则
- */
- @Override
- public void addInterceptors(InterceptorRegistry registry)
- {
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**")
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template><template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>// 积木报表新增:不拦截静态资源
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template><template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>.excludePathPatterns("/*.**, /**/*.html, /**/*.css, /**/*.js, /**/*.png, /**/*.jpg, /**/*.woff, /**/*.woff2, /**/*.ttf, /**/*.svg, /**/*.ico, /**/*.map" )
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>;
- }
复制代码 假如出现未设置的静态资源,必要自己进行设置。
实现数据格式转换器
在report包下新增adapter包,在adapter包下,新增ReportDataConvertAdapter.java文件,并且在此类中添加以下代码
- package com.ruoyi.framework.report.adapter;
-
- import com.alibaba.fastjson.JSONObject;
- import org.jeecg.modules.jmreport.desreport.render.handler.convert.ApiDataConvertAdapter;
- import org.springframework.stereotype.Component;
-
- @Component("reportDataConvertAdapter")
- public class ReportDataConvertAdapter implements ApiDataConvertAdapter {
- @Override
- public String getData(JSONObject jsonObject) {
-
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>if(jsonObject.containsKey("data")){
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template> String data = jsonObject.getString("data");
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template> return data;
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>}else if(jsonObject.containsKey("rows")){
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template> return jsonObject.getString("rows");
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>}else {
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template> return jsonObject.toJSONString();
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>}
- }
- }
-
复制代码
去官网拿积木的数据结构,在数据库执行
官网地址:- https://github.com/jeecgboot/jimureport/blob/master/db/jimureport.mysql5.7.create.sql
复制代码 前端设置
新建jeecg文件夹,并且jeecg文件夹下,新增request.js文件,并且在此文件中添加以下代码
- import axios from 'axios'
- import { Notification, MessageBox, Message } from 'element-ui'
- import store from '@/store'
- import { getToken } from '@/utils/auth'
- import errorCode from '@/utils/errorCode'
- import { tansParams } from "@/utils/ruoyi";
- import cache from '@/plugins/cache'
- // 是否显示重新登录
- export let isRelogin = { show: false };
- axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
- // 创建axios实例
- const service = axios.create({
- // axios中请求配置有baseURL选项,表示请求URL公共部分
- baseURL: process.env.VUE_APP_BASE_API,
- // 超时
- timeout: 10000
- })
- // request拦截器
- service.interceptors.request.use(config => {
- // 是否需要设置 token
- const isToken = (config.headers || {}).isToken === false
- // 是否需要防止数据重复提交
- const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
- if (getToken() && !isToken) {
- config.headers['X-Access-Token'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
- }
- // get请求映射params参数
- if (config.method === 'get' && config.params) {
- let url = config.url + '?' + tansParams(config.params);
- url = url.slice(0, -1);
- config.params = {};
- config.url = url;
- }
- if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
- const requestObj = {
- url: config.url,
- data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
- time: new Date().getTime()
- }
- const sessionObj = cache.session.getJSON('sessionObj')
- if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
- cache.session.setJSON('sessionObj', requestObj)
- } else {
- const s_url = sessionObj.url;<template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template><template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template> // 请求地址
- const s_data = sessionObj.data;<template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template><template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>// 请求数据
- const s_time = sessionObj.time;<template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template><template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>// 请求时间
- const interval = 1000;<template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template><template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template><template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template> // 间隔时间(ms),小于此时间视为重复提交
- if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>const message = '数据正在处理,请勿重复提交';
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>console.warn(`[${s_url}]: ` + message)
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>return Promise.reject(new Error(message))
- } else {
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>cache.session.setJSON('sessionObj', requestObj)
- }
- }
- }
- return config
- }, error => {
- console.log(error)
- Promise.reject(error)
- })
- // 响应拦截器
- service.interceptors.response.use(res => {
- // 未设置状态码则默认成功状态
- const code = res.data.code || 200;
- // 获取错误信息
- const msg = errorCode[code] || res.data.msg || res.data.message || errorCode['default']
- // 二进制数据则直接返回
- if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
- return res
- }
- if (code === 401) {
- if (!isRelogin.show) {
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>isRelogin.show = true;
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template> isRelogin.show = false;
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template> store.dispatch('LogOut').then(() => {
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template> location.href = '/index';
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template> })
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>}).catch(() => {
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template> isRelogin.show = false;
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>});
- }
- return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
- } else if (code === 500) {
- Message({ message: msg, type: 'error' })
- return Promise.reject(new Error(msg))
- } else if (code === 601) {
- Message({ message: msg, type: 'warning' })
- return Promise.reject('error')
- } else if (code !== 200) {
- Notification.error({ title: msg })
- return Promise.reject('error')
- } else {
- return res.data
- }
- },
- error => {
- console.log('err' + error)
- let { message } = error;
- if (message == "Network Error") {
- message = "后端接口连接异常";
- } else if (message.includes("timeout")) {
- message = "系统接口请求超时";
- } else if (message.includes("Request failed with status code")) {
- message = "系统接口" + message.substr(message.length - 3) + "异常";
- }
- Message({ message: message, type: 'error', duration: 5 * 1000 })
- return Promise.reject(error)
- }
- )
- export default service
复制代码
在jeecg文件夹下,新增report.js文件,并且在此文件中添加以下代码
- import request from "@/api/jeecg/request";
- let jmreportUrl = "/jmreport";
- import { getToken } from "@/utils/auth";
- let paramObj = {
- token: "Bearer " + getToken()
- }
- // 获取填报报表
- export function listReport(queryParams) {
- let params = {
- ...paramObj,
- ...queryParams
- }
- return request({
- url: jmreportUrl + "/excelQuery",
- method: 'get',
- params: params
- })
- }
- // 获取填报报表列表
- export function listFillReport(queryParams) {
- paramObj["reportType"] = "1011126161407836160";
- let params = {
- ...paramObj,
- ...queryParams
- }
- return request({
- url: jmreportUrl + "/excelQuery",
- method: 'get',
- params: params
- })
- }
复制代码
在src/view文件夹下新增jeecg文件夹,在jeecg文件夹下新增report文件夹,在report文件夹下新增index.vue文件,并且在此文件中添加以下代码
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>
复制代码
在report文件夹下新增view文件夹,在view文件夹下新增index.vue文件,并且在此文件中添加以下代码。
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>
复制代码
在jeecg文件夹下新增drag文件夹,在drag文件夹下新增index.vue文件,并且在此文件中添加以下代码
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>
复制代码
在drag文件夹下新增view文件夹,在view文件夹下新增index.vue文件,并且在此文件中添加以下代码。
- <template>
-
- <i-frame :src="reportUrl"></i-frame>
-
- </template>
复制代码
执行创建菜单的SQL
- -- 主目录
- INSERT INTO `lucas`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `route_name`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1083, '报表管理', 0, 7, 'jeecg', NULL, NULL, '', 1, 0, 'M', '0', '0', NULL, 'chart', 'admin', '2025-03-27 01:49:40', '', NULL, '');
- -- 基础报表菜单
- INSERT INTO `lucas`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `route_name`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1084, '报表管理', 1083, 0, 'report', 'jeecg/report/index', NULL, '', 1, 0, 'C', '0', '0', 'jeecg:report:edit', 'druid', 'admin', '2025-03-27 01:51:44', 'admin', '2025-03-27 05:12:06', '');
- -- 大屏报表菜单
- INSERT INTO `lucas`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `route_name`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1085, '大屏管理', 1083, 1, 'drag', 'jeecg/drag/index', NULL, '', 1, 0, 'C', '0', '0', 'jeecg:drag:edit', 'druid', 'admin', '2025-03-27 01:53:17', 'admin', '2025-03-27 05:12:13', '');
- -- 基础报表查看菜单
- INSERT INTO `lucas`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `route_name`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1086, '报表查看', 1083, 2, 'report/view', 'jeecg/report/view/index', NULL, '', 1, 0, 'C', '1', '0', 'jeecg:report:query', 'eye-open', 'admin', '2025-03-27 01:54:41', 'admin', '2025-03-27 05:12:19', '');
- -- 大屏报表查看菜单
- INSERT INTO `lucas`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `route_name`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1087, '大屏查看', 1083, 3, 'drag/view', 'jeecg/drag/view/index', NULL, '', 1, 0, 'C', '1', '0', 'jeecg:drag:query', 'eye-open', 'admin', '2025-03-27 01:55:46', 'admin', '2025-03-27 05:12:24', '');
复制代码 设置结束
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |