Bläddra i källkod

!15 0.9.4
Merge pull request !15 from Foming/dev

Foming 4 år sedan
förälder
incheckning
3334ce7672
100 ändrade filer med 3465 tillägg och 292 borttagningar
  1. 26 0
      NOTICE
  2. 5 4
      README.md
  3. 5 2
      doc/docs/.vuepress/config.js
  4. 1 1
      doc/docs/guide/README.md
  5. 29 0
      doc/docs/guide/authmanager.md
  6. 0 0
      doc/docs/guide/community/AC1688/搭建aj-report开发环境.md
  7. 14 0
      doc/docs/guide/community/report.md
  8. BIN
      doc/docs/guide/community/report/img.png
  9. BIN
      doc/docs/guide/community/report/img_1.png
  10. 24 15
      doc/docs/guide/dashboard.md
  11. 5 3
      doc/docs/guide/datasource.md
  12. 27 0
      doc/docs/guide/execl.md
  13. 4 0
      doc/docs/guide/importexport.md
  14. 3 0
      doc/docs/guide/reportmanager.md
  15. BIN
      doc/docs/picture/authmanager/img.png
  16. BIN
      doc/docs/picture/authmanager/img_1.png
  17. BIN
      doc/docs/picture/authmanager/img_2.png
  18. BIN
      doc/docs/picture/authmanager/img_3.png
  19. BIN
      doc/docs/picture/authmanager/img_4.png
  20. BIN
      doc/docs/picture/authmanager/img_5.png
  21. BIN
      doc/docs/picture/authmanager/img_6.png
  22. BIN
      doc/docs/picture/authmanager/img_7.png
  23. BIN
      doc/docs/picture/dashboard/img.png
  24. BIN
      doc/docs/picture/dashboard/img22.png
  25. BIN
      doc/docs/picture/dashboard/img23.png
  26. BIN
      doc/docs/picture/dashboard/img_14.png
  27. BIN
      doc/docs/picture/dashboard/img_19.png
  28. BIN
      doc/docs/picture/dashboard/img_20.png
  29. BIN
      doc/docs/picture/dashboard/img_21.png
  30. BIN
      doc/docs/picture/datasource/img_2.png
  31. BIN
      doc/docs/picture/datasource/img_3.png
  32. BIN
      doc/docs/picture/execl/img.png
  33. BIN
      doc/docs/picture/execl/img_1.png
  34. BIN
      doc/docs/picture/execl/img_2.png
  35. BIN
      doc/docs/picture/execl/img_3.png
  36. BIN
      doc/docs/picture/execl/img_4.png
  37. BIN
      doc/docs/picture/execl/img_5.png
  38. BIN
      doc/docs/picture/reportmanager/img.png
  39. 27 0
      report-core/pom.xml
  40. 2 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/config/DatabaseInitializer.java
  41. 73 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/enums/ExcelCenterStyleEnum.java
  42. 30 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/enums/ExportTypeEnum.java
  43. 10 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/filter/TokenFilter.java
  44. 2 2
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/accessauthority/dao/entity/AccessAuthority.java
  45. 2 2
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/accessrole/dao/entity/AccessRole.java
  46. 1 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/accessrole/dao/entity/AccessRoleAuthority.java
  47. 10 2
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/accessrole/service/impl/AccessRoleServiceImpl.java
  48. 2 2
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/accessuser/dao/entity/AccessUser.java
  49. 2 2
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/accessuser/dao/entity/AccessUserRole.java
  50. 28 6
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/accessuser/service/impl/AccessUserServiceImpl.java
  51. 2 2
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dashboard/controller/ReportDashboardController.java
  52. 1 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dashboard/dao/entity/ReportDashboard.java
  53. 18 6
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dashboard/service/impl/ReportDashboardServiceImpl.java
  54. 1 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dashboardwidget/dao/entity/ReportDashboardWidget.java
  55. 3 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataset/controller/dto/DataSetDto.java
  56. 1 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataset/dao/entity/DataSet.java
  57. 13 6
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataset/service/impl/DataSetServiceImpl.java
  58. 1 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasetparam/dao/entity/DataSetParam.java
  59. 1 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasetparam/service/DataSetParamService.java
  60. 20 4
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasetparam/service/impl/DataSetParamServiceImpl.java
  61. 1 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasettransform/dao/entity/DataSetTransform.java
  62. 1 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/dao/entity/DataSource.java
  63. 26 5
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/service/impl/DataSourceServiceImpl.java
  64. 1 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dict/dao/entity/GaeaDict.java
  65. 1 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dict/dao/entity/GaeaDictItem.java
  66. 1 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/file/entity/GaeaFile.java
  67. 1 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/file/service/impl/GaeaFileServiceImpl.java
  68. 1 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/report/dao/entity/Report.java
  69. 3 3
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/report/service/impl/ReportServiceImpl.java
  70. 78 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/controller/ReportExcelController.java
  71. 58 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/controller/dto/GridRecordDataModel.java
  72. 61 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/controller/dto/ReportExcelDto.java
  73. 18 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/controller/param/ReportExcelParam.java
  74. 11 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/dao/ReportExcelMapper.java
  75. 33 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/dao/entity/ReportExcel.java
  76. 43 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/service/ReportExcelService.java
  77. 362 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/service/impl/ReportExcelServiceImpl.java
  78. 134 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/util/ColorUtil.java
  79. 313 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/util/ConstantUtil.java
  80. 28 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/util/MSExcelUtil.java
  81. 860 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/util/XlsSheetUtil.java
  82. 453 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/util/XlsUtil.java
  83. 5 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportshare/controller/dto/ReportShareDto.java
  84. 11 4
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportshare/dao/entity/ReportShare.java
  85. 16 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportshare/service/impl/ReportShareServiceImpl.java
  86. 17 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/util/JwtUtil.java
  87. 22 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/util/UuidUtil.java
  88. 2 1
      report-core/src/main/resources/bootstrap.yml
  89. 33 0
      report-core/src/main/resources/db/migration/V1.0.12__create_excel.sql
  90. 22 0
      report-core/src/main/resources/mapper/ReportExcelMapper.xml
  91. 22 0
      report-core/src/test/java/com/anjiplus/template/gaea/business/modules/reportexcel/util/XlsSheetUtilTest.java
  92. 1 1
      report-ui/config/dev.env.js
  93. 16 0
      report-ui/index.html
  94. 2 0
      report-ui/package.json
  95. 3 4
      report-ui/src/assets/iconfont/iconfont.css
  96. 393 167
      report-ui/src/components/AnjiPlus/anji-crud/anji-crud.vue
  97. 5 0
      report-ui/src/components/AnjiPlus/anji-select.vue
  98. 8 7
      report-ui/src/components/Dictionary/index.vue
  99. 3 25
      report-ui/src/mixins/queryform.js
  100. 3 1
      report-ui/src/router/index.js

+ 26 - 0
NOTICE

@@ -143,3 +143,29 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 
+------
+This product has a bundle Luckysheet
+The source code of Luckysheet can be found at https://gitee.com/mengshukeji/Luckysheet.
+
+The MIT License (MIT)
+
+Copyright (c) 2020-present, Mengshukeji
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+

+ 5 - 4
README.md

@@ -1,7 +1,7 @@
 ## 简介
 
 &emsp; &emsp; AJ-Report由 [安吉加加](http://www.anji-plus.com) 开源的一个BI平台,酷炫大屏展示,能随时随地掌控业务动态,让每个决策都有数据支撑。<br>
-&emsp; &emsp; 多数据源支持,内置mysql、elasticsearch、kudu驱动,支持自定义数据集省去数据接口开发,支持17种大屏组件,不会开发,照着设计稿也可以制作大屏。<br>
+&emsp; &emsp; 多数据源支持,内置mysql、elasticsearch、kudu驱动,支持自定义数据集省去数据接口开发,支持17+种大屏组件,不会开发,照着设计稿也可以制作大屏。<br>
 &emsp; &emsp; 三步轻松完成大屏设计:配置数据源---->写SQL配置数据集---->拖拽配置大屏---->保存发布。欢迎体验。
 
 ## 在线体验
@@ -85,6 +85,7 @@
 - [vue-echarts](https://www.npmjs.com/package/vue-echarts/): vue-echarts是封装后的vue插件,基于 ECharts v4.0.1+ 开发
 - [vue-superslide](https://www.npmjs.com/package/vue-super-slider/): Vue-SuperSlide(Github) 是 SuperSlide 的 Vue 封装版本
 - [vuedraggable](https://github.com/SortableJS/Vue.Draggable/): 是一款基于Sortable.js实现的vue拖拽插件。
+- [luckysheet](https://gitee.com/mengshukeji/Luckysheet): Luckysheet ,一款纯前端类似excel的在线表格,功能强大、配置简单、完全开源。
 
 ## 编译打包
 
@@ -142,11 +143,11 @@ sql文件的目录在:report-core --> src --> main --> resources -- > db.migra
 已知以下版本存在兼容性问题
 - Node.js V16
 - Jdk 11
-- Mysql 8.0(8.0.26版本没有问题,8.0.21版本存在问题)
+- Mysql 8.0(8.0.23/26版本没有问题,8.0.21版本存在问题)
 - Windows 11
 
-AJ-Report 使用Druid,版本为1.2.6,如果你觉得你配置都是正常但是数据源测试不过,请尝试修改pom文件降低Druid版本。
-例如:MSSQLSERVER 2014,请将Druid版本降低为1.2.1以下
+AJ-Report 使用Druid,版本为1.2.6,如果你觉得你配置都是正常但是数据源测试不过,请尝试修改pom文件降低Druid版本。 <br>
+例如:MSSQLSERVER 2014,请将Druid版本降低为1.2.1以下。 <br>
 
 ## 商业授权
 AJ-Report使用[Apache2.0开源协议](http://www.apache.org/licenses/LICENSE-2.0.html) <br>

+ 5 - 2
doc/docs/.vuepress/config.js

@@ -50,7 +50,9 @@ module.exports = {
                     children: [
                         {title: '数据源', path: '/guide/datasource'},
                         {title: '数据集', path: '/guide/dataset'},
-                        {title: '大屏设计', path: '/guide/dashboard'},
+                        {title: '报表管理', path: '/guide/reportmanager'},
+                        {title: '大屏报表', path: '/guide/dashboard'},
+                        {title: '表格报表', path: '/guide/execl'},
                         {title: '导入导出', path: '/guide/importexport'},
                     ]
                 },
@@ -65,7 +67,8 @@ module.exports = {
                     title: '社区提供',
                     collapsable: false,
                     children: [
-                        {title: '搭建AJ-Report开发环境', path: '/community/AC1688/'}
+                        {title: '说明', path: '/guide/community/report'},
+                        {title: '搭建AJ-Report开发环境', path: '/guide/community/AC1688/搭建aj-report开发环境'}
                     ]
                 }
                 ],

+ 1 - 1
doc/docs/guide/README.md

@@ -1,5 +1,5 @@
 &emsp;  &emsp;  AJ-Report是一个完全开源的BI平台,酷炫大屏展示,能随时随地掌控业务动态,让每个决策都有数据支撑。<br>
-&emsp;  &emsp;  多数据源支持,内置mysql、elasticsearch、kudu等多种驱动,支持自定义数据集省去数据接口开发,支持17+大屏组件,不会开发,照着设计稿也可以制作大屏。<br>
+&emsp;  &emsp;  多数据源支持,内置mysql、elasticsearch、kudu等多种驱动,支持自定义数据集省去数据接口开发,支持17+大屏组件,不会开发,照着设计稿也可以制作大屏。<br>
 &emsp;  &emsp;  三步轻松完成大屏设计:配置数据源---->写SQL配置数据集---->拖拽配置大屏---->保存发布。欢迎体验。
 
 ## 系统特性

+ 29 - 0
doc/docs/guide/authmanager.md

@@ -0,0 +1,29 @@
+## 新增用户
+![img](../picture/authmanager/img.png) <br>
+
+![img](../picture/authmanager/img_1.png) <br>
+新增用户的默认密码是在bootstrap.yml文件中配置的 <br>
+![img](../picture/authmanager/img_2.png) <br>
+
+## 用户授权
+**注意**:新建用户完成后需要给用户授权,否则新用户登陆是啥也看不到。<br>
+![img](../picture/authmanager/img_3.png) <br>
+
+![img](../picture/authmanager/img_4.png) <br>
+**注**:这里没有给新用户赋予默认角色的原因是,在角色管理中角色是可以被删除和修改的,因此在新建用户时需要手动的去授权角色 <br>
+
+## 角色管理
+![img](../picture/authmanager/img_5.png) <br>
+
+## 权限分配
+为角色分配权限,可看已有角色示例 <br>
+![img](../picture/authmanager/img_6.png) <br>
+
+## 导入导出权限
+**注**:现在guest用户的权限是底层写死只有访问权限,无实质操作权限。<br>
+导入导出的权限是在 角色 --> 分配权限中控制。 <br>
+![img](../picture/authmanager/img_7.png) <br>
+用户绑定了角色,角色则绑定了权限,是这样一层关系。 <br>
+
+
+

+ 0 - 0
doc/docs/community/AC1688/README.md → doc/docs/guide/community/AC1688/搭建aj-report开发环境.md


+ 14 - 0
doc/docs/guide/community/report.md

@@ -0,0 +1,14 @@
+# 社区用户提交文档PR的简易说明
+
+## 提交位置
+doc -- > docs --> guide -- > community 目录 <br>
+![img](../../guide/community/report/img.png) <br>
+
+## 具体操作
+- 请在community目录下新建属于你自己的文件目录,命名方式可以使用自己在gitee的名字作为命名,注意中文命令可能会存在的一些问题。<br>
+- 在你的个人目录下,你可以新建MD文件,需要用到图片可以直接放一个目录,如果md多,图片也多,建议再建下级目录存放。<br>
+
+最后别忘了在config.js中添加配置,如图示。<br>
+![img](../../guide/community/report/img_1.png) <br>
+
+

BIN
doc/docs/guide/community/report/img.png


BIN
doc/docs/guide/community/report/img_1.png


+ 24 - 15
doc/docs/guide/dashboard.md

@@ -1,16 +1,21 @@
-## 介绍
-## 新增大屏
-![img.png](../picture/dashboard/img.png)
+## 设计大屏
+进入大屏设计方法1: <br>
+从报表管理模块选择需要设计的大屏,按图示进入大屏设计界面 <br>
+![img](../picture/dashboard/img22.png) <br>
+
+进入大屏设计方法2: <br>
+从大屏报表模块选择需要设计的大屏,按图示进入大屏设计界面 <br>
+![img](../picture/dashboard/img23.png) <br>
 
 ## 大屏简介
-![img_2.png](../picture/dashboard/img_2.png)
+![img_2.png](../picture/dashboard/img_2.png) <br>
 
 ## 工具栏
 ### 文本框
-![img_3.png](../picture/dashboard/img_3.png)
-![img_4.png](../picture/dashboard/img_4.png)
-![img_5.png](../picture/dashboard/img_5.png)
-![img_6.png](../picture/dashboard/img_6.png)
+![img_3.png](../picture/dashboard/img_3.png) <br>
+![img_4.png](../picture/dashboard/img_4.png) <br>
+![img_5.png](../picture/dashboard/img_5.png) <br>
+![img_6.png](../picture/dashboard/img_6.png) <br>
 
 ### 滚动文本
 已支持动态数据,请参考文本框的操作 <br>
@@ -73,7 +78,7 @@
 
 ### 仪表盘
 数据集只能有一个字段,且字典选择“文本数字” <br>
-![img14](../picture/dashboard/img_14.png) <br>
+![img21](../picture/dashboard/img_21.png) <br>
 **如有问题,请提交 [Issue](https://gitee.com/anji-plus/report/issues) <br>**
 
 ### 中国地图
@@ -81,16 +86,20 @@
 气泡地图动态数据集,和饼图一样,对应字典值需要选择一个“Name”、“Value”,且name的字段值要和echarts图表里面的值能对应上,可参考静态数据 <br>
 ![img15](../picture/dashboard/img_15.png) <br>
 
-
 ### 百分百图
 数据集只能有一个字段,且字典选择“文本数字” <br>
 ![img16](../picture/dashboard/img_16.png) <br>
 **如有问题,请提交 [Issue](https://gitee.com/anji-plus/report/issues) <br>**
 
-### 散点图
-**规划中** <br>
-
 ### 对比图
-柱状对比图数据集需要3个字段,其中一个作为对比的字段只能为2种值,只有2种值作为对比的字段要选择“y轴字段”字典。因为底层的解析用的是堆叠图的解析,这里的y轴字段并不是指的图表上面的y轴,还请注意,有强迫症可以自行修改源码的解析,剩下的2个字段对应字典看图<br>
-![img18](../picture/dashboard/img_18.png)
+柱状对比图: <br>
+数据集需要3个字段,其中一个作为对比的字段只能为2种值,只有2种值作为对比的字段要选择“y轴字段”字典。因为底层的解析用的是堆叠图的解析,这里的y轴字段并不是指的图表上面的y轴,还请注意,有强迫症可以自行修改源码的解析,剩下的2个字段对应字典看图<br>
+![img18](../picture/dashboard/img_18.png) <br>
+**如有问题,请提交 [Issue](https://gitee.com/anji-plus/report/issues) <br>**
+
+折线对比图: <br>
+数据集需要3个字段,其中一个作为对比的字段只能为2种值,只有2种值作为对比的字段要选择“y轴字段”字典,剩下的字典对应看图<br>
+![img19](../picture/dashboard/img_19.png) <br>
+**注**:如果提示语设置选择“十字形”,请注意需要选择 “X轴颜色、上Y轴颜色、下Y轴颜色”,不然预览图表鼠标选择是全白色,还请注意。<br>
+![img20](../picture/dashboard/img_20.png)
 **如有问题,请提交 [Issue](https://gitee.com/anji-plus/report/issues) <br>**

+ 5 - 3
doc/docs/guide/datasource.md

@@ -4,10 +4,12 @@
 ![source.png](../picture/datasource/img_1.png)
 
 ## 数据源类型
-- 没有找到新增数据源类型?<br>
+查看已有的数据源类型 <br>
+![img2](../picture/datasource/img_2.png)  <br>
+![img3](../picture/datasource/img_3.png)  <br>
 ```text
-字典管理目前暂未有页面维护,可自行去数据库中增加你需要的数据源类型。
-表aj_report.gaea_dict,字段dict_type:SOURCE_TYPE
+可以在此页面进行新增数据源配置,也可以去数据库中增加你需要的数据源类型。
+表aj_report.gaea_dict_item,字段dict_code:SOURCE_TYPE
 表aj_report.gaea_dict_item, item_extend字段是下拉选择后动态表单渲染的json数据
 ```
 

+ 27 - 0
doc/docs/guide/execl.md

@@ -0,0 +1,27 @@
+# 介绍
+execl报表基于Luckysheet开发,[Luckysheet](https://gitee.com/mengshukeji/Luckysheet) 一款纯前端类似excel的在线表格,功能强大、配置简单、完全开源。
+**注意:** execl报表目前只是简单集成,如果你遇到了一些问题请在此[Issue](https://gitee.com/anji-plus/report/issues/I4CEWV) 下面进行回复。<br>
+## 表格报表设计
+进入表格设计方法1: <br>
+从报表管理模块选择需要设计的大屏,按图示进入大屏设计界面 <br>
+![img](../picture/execl/img.png) <br>
+
+进入大屏设计方法2: <br>
+从大屏报表模块选择需要设计的大屏,按图示进入大屏设计界面 <br>
+![img2](../picture/execl/img_1.png) <br>
+
+## 简介
+![img3](../picture/execl/img_2.png) <br>
+
+## 使用
+**注**:不建议一列中同时存在俩个字段数据,同时一列值也建议不要存到超大数据量,肯定无法显示的<br>
+![img4](../picture/execl/img_3.png) <br>
+
+## 预览/保存
+点击保存,则会将数据写入到库中。<br>
+点击预览,则进入预览界面。<br>
+![img](../picture/execl/img_4.png) <br>
+
+## 预览界面
+可以进行导出操作。<br>
+![img](../picture/execl/img_5.png) <br>

+ 4 - 0
doc/docs/guide/importexport.md

@@ -15,3 +15,7 @@
 ![img1](../picture/imexport/img_1.png) <br>
 选择一个导出的zip文件导入即可。注意,导入会覆盖当前大屏,请新建一张空白的大屏进行导入。<br>
 **注:如果你导入的大屏中含有你当前系统不存在的图表,整个大屏是不会显示的。** <br>
+
+
+## 导入导出权限
+请查看权限控制模块中关于导入导出权限的说明。 <br>

+ 3 - 0
doc/docs/guide/reportmanager.md

@@ -0,0 +1,3 @@
+## 新增报表
+![img](../picture/reportmanager/img.png) <br>
+状态默认为已启用。

BIN
doc/docs/picture/authmanager/img.png


BIN
doc/docs/picture/authmanager/img_1.png


BIN
doc/docs/picture/authmanager/img_2.png


BIN
doc/docs/picture/authmanager/img_3.png


BIN
doc/docs/picture/authmanager/img_4.png


BIN
doc/docs/picture/authmanager/img_5.png


BIN
doc/docs/picture/authmanager/img_6.png


BIN
doc/docs/picture/authmanager/img_7.png


BIN
doc/docs/picture/dashboard/img.png


BIN
doc/docs/picture/dashboard/img22.png


BIN
doc/docs/picture/dashboard/img23.png


BIN
doc/docs/picture/dashboard/img_14.png


BIN
doc/docs/picture/dashboard/img_19.png


BIN
doc/docs/picture/dashboard/img_20.png


BIN
doc/docs/picture/dashboard/img_21.png


BIN
doc/docs/picture/datasource/img_2.png


BIN
doc/docs/picture/datasource/img_3.png


BIN
doc/docs/picture/execl/img.png


BIN
doc/docs/picture/execl/img_1.png


BIN
doc/docs/picture/execl/img_2.png


BIN
doc/docs/picture/execl/img_3.png


BIN
doc/docs/picture/execl/img_4.png


BIN
doc/docs/picture/execl/img_5.png


BIN
doc/docs/picture/reportmanager/img.png


+ 27 - 0
report-core/pom.xml

@@ -105,6 +105,33 @@
             <version>1.18.10</version>
             <optional>true</optional>
         </dependency>
+
+        <dependency>
+            <groupId>com.itextpdf</groupId>
+            <artifactId>itextpdf</artifactId>
+            <version>5.5.13.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.itextpdf</groupId>
+            <artifactId>itext-asian</artifactId>
+            <version>5.2.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+            <version>4.1.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>4.1.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml-schemas</artifactId>
+            <version>4.1.2</version>
+        </dependency>
     </dependencies>
 
     <developers>

+ 2 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/config/DatabaseInitializer.java

@@ -3,6 +3,7 @@ package com.anjiplus.template.gaea.business.config;
 import com.zaxxer.hikari.HikariDataSource;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.autoconfigure.flyway.FlywayProperties;
 import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
 import org.springframework.stereotype.Component;
@@ -18,6 +19,7 @@ import java.sql.Statement;
 @Slf4j
 @Component
 @AllArgsConstructor
+@ConditionalOnProperty(value = {"spring.flyway.enabled"})
 public class DatabaseInitializer {
 
     private final FlywayProperties flywayProperties;

+ 73 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/enums/ExcelCenterStyleEnum.java

@@ -0,0 +1,73 @@
+package com.anjiplus.template.gaea.business.enums;
+
+/**
+ * @author zhouhang
+ * @description EXCEL居中方式
+ * @date 2021/4/26
+ */
+public enum ExcelCenterStyleEnum {
+    /**
+     * 左对齐
+     */
+    LEFT((short) 1, 1, "左对齐"),
+    /**
+     * 右对齐
+     */
+    RIGHT((short) 3, 2, "右对齐"),
+    /**
+     * 居中
+     */
+    CENTER((short) 2, 0, "居中"),
+    ;
+
+    /**
+     * excel居中code
+     */
+    private final short excelCode;
+
+    /**
+     * 在线文档居中code
+     */
+    private final Integer onlineExcelCode;
+
+    /**
+     * 名称
+     */
+    private final String name;
+
+
+    public Integer getOnlineExcelCode() {
+        return onlineExcelCode;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public short getExcelCode() {
+        return excelCode;
+    }
+
+    ExcelCenterStyleEnum(short excelCode, Integer onlineExcelCode, String name) {
+        this.excelCode = excelCode;
+        this.onlineExcelCode = onlineExcelCode;
+        this.name = name;
+    }
+
+    /**
+     * @param code excel居中样式code
+     * @return Enum_ExcelCenterStyle
+     * @description 根据excel居中样式获取在线文档居中样式
+     * @author zhouhang
+     * @date 2021/4/26
+     */
+    public static ExcelCenterStyleEnum getExcelCenterStyleByExcelCenterCode(short code) {
+        for (ExcelCenterStyleEnum value : ExcelCenterStyleEnum.values()) {
+            if (code == value.getExcelCode()) {
+                return value;
+            }
+        }
+        return CENTER;
+    }
+
+}

+ 30 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/enums/ExportTypeEnum.java

@@ -0,0 +1,30 @@
+package com.anjiplus.template.gaea.business.enums;
+
+/**
+ * Created by raodeming on 2021/9/3.
+ */
+public enum ExportTypeEnum {
+
+    /**gaea_excel*/
+    GAEA_TEMPLATE_EXCEL("gaea_template_excel", "gaea_template_excel"),
+    /**gaea_pdf*/
+    GAEA_TEMPLATE_PDF("gaea_template_pdf", "gaea_template_pdf"),
+    ;
+
+    private String codeValue;
+    private String codeDesc;
+
+    private ExportTypeEnum(String codeValue, String codeDesc) {
+        this.codeValue = codeValue;
+        this.codeDesc = codeDesc;
+    }
+
+    public String getCodeValue() {
+        return this.codeValue;
+    }
+
+    public String getCodeDesc() {
+        return this.codeDesc;
+    }
+
+}

+ 10 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/filter/TokenFilter.java

@@ -8,6 +8,7 @@ import com.anji.plus.gaea.utils.JwtBean;
 import com.anjiplus.template.gaea.business.constant.BusinessConstant;
 import com.anjiplus.template.gaea.business.util.JwtUtil;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.http.entity.ContentType;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.core.annotation.Order;
@@ -57,6 +58,12 @@ public class TokenFilter implements Filter {
         HttpServletResponse response = (HttpServletResponse) servletResponse;
         String uri = request.getRequestURI();
 
+        //OPTIONS直接放行
+        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
+            filterChain.doFilter(request, response);
+            return;
+        }
+
         if (SLASH.equals(uri)) {
             response.sendRedirect("/index.html");
             return;
@@ -123,9 +130,11 @@ public class TokenFilter implements Filter {
             if (HttpMethod.POST.name().equalsIgnoreCase(method)
                     || HttpMethod.PUT.name().equalsIgnoreCase(method)
                     || HttpMethod.DELETE.name().equalsIgnoreCase(method)
+                    || uri.contains("/reportDashboard/export")
             ) {
                 ResponseBean responseBean = ResponseBean.builder().code("50001")
                         .message("在线体验版本,不允许此操作。请自行下载本地运行").build();
+                response.setContentType(ContentType.APPLICATION_JSON.getMimeType());
                 response.getWriter().print(JSONObject.toJSONString(responseBean));
                 return;
             }
@@ -167,6 +176,7 @@ public class TokenFilter implements Filter {
 
     private void error(HttpServletResponse response) throws IOException {
         ResponseBean responseBean = ResponseBean.builder().code("50014").message("The Token has expired").build();
+        response.setContentType(ContentType.APPLICATION_JSON.getMimeType());
         response.getWriter().print(JSONObject.toJSONString(responseBean));
     }
 }

+ 2 - 2
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/accessauthority/dao/entity/AccessAuthority.java

@@ -12,7 +12,7 @@ import java.util.Date;
 * @author 木子李·De <lide1202@hotmail.com>
 * @date 2019-02-17 08:50:10.009
 **/
-@TableName(value="access_authority")
+@TableName(keepGlobalPrefix=true, value="access_authority")
 @Data
 public class AccessAuthority extends GaeaBaseEntity {
     /** 父菜单代码 */
@@ -40,4 +40,4 @@ public class AccessAuthority extends GaeaBaseEntity {
 
 
 
-}
+}

+ 2 - 2
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/accessrole/dao/entity/AccessRole.java

@@ -11,7 +11,7 @@ import java.util.Date;
 * @author 木子李·De <lide1202@hotmail.com>
 * @date 2019-02-17 08:50:14.136
 **/
-@TableName(value="access_role")
+@TableName(keepGlobalPrefix=true, value="access_role")
 @Data
 public class AccessRole extends GaeaBaseEntity {
 
@@ -27,4 +27,4 @@ public class AccessRole extends GaeaBaseEntity {
     /** 0--已禁用 1--已启用  DIC_NAME=ENABLE_FLAG */
     private Integer enableFlag;
 
-}
+}

+ 1 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/accessrole/dao/entity/AccessRoleAuthority.java

@@ -14,7 +14,7 @@ import java.util.Date;
  * @author 木子李·De <lide1202@hotmail.com>
  * @date 2019-02-17 08:50:14.136
  **/
-@TableName(value="access_role_authority")
+@TableName(keepGlobalPrefix=true, value="access_role_authority")
 @Data
 public class AccessRoleAuthority extends GaeaBaseEntity {
 

+ 10 - 2
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/accessrole/service/impl/AccessRoleServiceImpl.java

@@ -21,6 +21,7 @@ import org.springframework.stereotype.Service;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
 * @desc AccessRole 角色管理服务实现
@@ -52,7 +53,14 @@ public class AccessRoleServiceImpl implements AccessRoleService {
         List<TreeNode> treeData = accessAuthorityService.getAuthorityTree(operator, true);
 
         // 该角色已选中的菜单及按钮
-        List<String> checkedKeys = accessRoleMapper.checkedAuthoritys(roleCode);
+//        List<String> checkedKeys = accessRoleMapper.checkedAuthoritys(roleCode);
+
+        LambdaQueryWrapper<AccessRoleAuthority> accessRoleAuthorityWrapper = Wrappers.lambdaQuery();
+        accessRoleAuthorityWrapper.select(AccessRoleAuthority::getTarget, AccessRoleAuthority::getAction);
+        accessRoleAuthorityWrapper.eq(AccessRoleAuthority::getRoleCode, roleCode);
+        List<AccessRoleAuthority> accessRoleAuthorities = accessRoleAuthorityMapper.selectList(accessRoleAuthorityWrapper);
+        List<String> checkedKeys = accessRoleAuthorities.stream()
+                .map(accessRoleAuthority -> accessRoleAuthority.getTarget().concat("_").concat(accessRoleAuthority.getAction())).distinct().collect(Collectors.toList());
 
         result.put("treeData", treeData);
         result.put("checkedKeys", checkedKeys);
@@ -90,4 +98,4 @@ public class AccessRoleServiceImpl implements AccessRoleService {
         });
         return true;
     }
-}
+}

+ 2 - 2
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/accessuser/dao/entity/AccessUser.java

@@ -11,7 +11,7 @@ import java.util.Date;
 * @author 木子李·De <lide1202@hotmail.com>
 * @date 2019-02-17 08:50:11.902
 **/
-@TableName(value="access_user")
+@TableName(keepGlobalPrefix=true, value="access_user")
 @Data
 public class AccessUser extends GaeaBaseEntity {
 
@@ -45,4 +45,4 @@ public class AccessUser extends GaeaBaseEntity {
     /** 最后一次登陆时间 */
     private Date lastLoginTime;
 
-}
+}

+ 2 - 2
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/accessuser/dao/entity/AccessUserRole.java

@@ -13,7 +13,7 @@ import java.util.Date;
 * @author 木子李·De <lide1202@hotmail.com>
 * @date 2019-02-17 08:50:11.902
 **/
-@TableName(value="access_user_role")
+@TableName(keepGlobalPrefix=true, value="access_user_role")
 @Data
 public class AccessUserRole extends GaeaBaseEntity {
 
@@ -37,4 +37,4 @@ public class AccessUserRole extends GaeaBaseEntity {
 
     @TableField(exist = false)
     private Integer version;
-}
+}

+ 28 - 6
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/accessuser/service/impl/AccessUserServiceImpl.java

@@ -13,8 +13,10 @@ import com.anji.plus.gaea.utils.GaeaUtils;
 import com.anji.plus.gaea.utils.JwtBean;
 import com.anjiplus.template.gaea.business.code.ResponseCode;
 import com.anjiplus.template.gaea.business.constant.BusinessConstant;
+import com.anjiplus.template.gaea.business.modules.accessrole.dao.AccessRoleAuthorityMapper;
 import com.anjiplus.template.gaea.business.modules.accessrole.dao.AccessRoleMapper;
 import com.anjiplus.template.gaea.business.modules.accessrole.dao.entity.AccessRole;
+import com.anjiplus.template.gaea.business.modules.accessrole.dao.entity.AccessRoleAuthority;
 import com.anjiplus.template.gaea.business.modules.accessuser.controller.dto.AccessUserDto;
 import com.anjiplus.template.gaea.business.modules.accessuser.controller.dto.GaeaUserDto;
 import com.anjiplus.template.gaea.business.modules.accessuser.controller.dto.UpdatePasswordDto;
@@ -31,10 +33,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -54,6 +53,9 @@ public class AccessUserServiceImpl implements AccessUserService {
     @Autowired
     private AccessUserRoleMapper accessUserRoleMapper;
 
+    @Autowired
+    private AccessRoleAuthorityMapper accessRoleAuthorityMapper;
+
     @Value("${customer.user.default.password:'123456'}")
     private String defaultPassword;
 
@@ -168,11 +170,31 @@ public class AccessUserServiceImpl implements AccessUserService {
         // 4.读取用户最新人权限主信息
         String userKey = String.format(BusinessConstant.GAEA_SECURITY_LOGIN_USER, loginName);
 
-        List<String> authorities = accessUserMapper.queryAuthoritiesByLoginName(loginName);
+        //为了兼容底层其他数据库,不再写自定义sql
+//        List<String> authorities = accessUserMapper.queryAuthoritiesByLoginName(loginName);
+
+        //当前用户的roleCode集合
+        LambdaQueryWrapper<AccessUserRole> accessUserWrapper = Wrappers.lambdaQuery();
+        accessUserWrapper.select(AccessUserRole::getRoleCode);
+        accessUserWrapper.eq(AccessUserRole::getLoginName, loginName);
+        List<AccessUserRole> accessUserRoles = accessUserRoleMapper.selectList(accessUserWrapper);
+        Set<String> roleCodeSet = accessUserRoles.stream().map(AccessUserRole::getRoleCode).collect(Collectors.toSet());
+        if (roleCodeSet.size() < 1) {
+            gaeaUser.setAuthorities(new ArrayList<>());
+        }else {
+            LambdaQueryWrapper<AccessRoleAuthority> accessRoleAuthorityWrapper = Wrappers.lambdaQuery();
+            accessRoleAuthorityWrapper.select(AccessRoleAuthority::getTarget, AccessRoleAuthority::getAction);
+            accessRoleAuthorityWrapper.in(AccessRoleAuthority::getRoleCode, roleCodeSet);
+            List<AccessRoleAuthority> accessRoleAuthorities = accessRoleAuthorityMapper.selectList(accessRoleAuthorityWrapper);
+            List<String> authorities = accessRoleAuthorities.stream()
+                    .map(accessRoleAuthority -> accessRoleAuthority.getTarget().concat(":").concat(accessRoleAuthority.getAction())).distinct().collect(Collectors.toList());
+            gaeaUser.setAuthorities(authorities);
+        }
+
         gaeaUser.setLoginName(loginName);
         gaeaUser.setRealName(accessUser.getRealName());
         gaeaUser.setToken(token);
-        gaeaUser.setAuthorities(authorities);
+
         String gaeaUserStr = JSONObject.toJSONString(gaeaUser);
         cacheHelper.stringSetExpire(userKey, gaeaUserStr, 3600);
 

+ 2 - 2
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dashboard/controller/ReportDashboardController.java

@@ -74,7 +74,7 @@ public class ReportDashboardController {
      * @return
      */
     @GetMapping("/export")
-    @Permission(code = "view", name = "导出大屏")
+    @Permission(code = "export", name = "导出大屏")
     public ResponseEntity<byte[]> exportDashboard(HttpServletRequest request, HttpServletResponse response,
                                                   @RequestParam("reportCode") String reportCode, @RequestParam(value = "showDataSet",required = false, defaultValue = "1") Integer showDataSet) {
         return reportDashboardService.exportDashboard(request, response, reportCode, showDataSet);
@@ -87,7 +87,7 @@ public class ReportDashboardController {
      * @return
      */
     @PostMapping("/import/{reportCode}")
-    @Permission(code = "design", name = "导入大屏")
+    @Permission(code = "import", name = "导入大屏")
     public ResponseBean importDashboard(@RequestParam("file") MultipartFile file, @PathVariable("reportCode") String reportCode) {
         reportDashboardService.importDashboard(file, reportCode);
         return ResponseBean.builder().build();

+ 1 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dashboard/dao/entity/ReportDashboard.java

@@ -13,7 +13,7 @@ import lombok.Data;
 * @author Raod
 * @date 2021-04-12 14:52:21.761
 **/
-@TableName(value="gaea_report_dashboard")
+@TableName(keepGlobalPrefix=true, value="gaea_report_dashboard")
 @Data
 public class ReportDashboard extends GaeaBaseEntity {
     @ApiModelProperty(value = "报表编码")

+ 18 - 6
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dashboard/service/impl/ReportDashboardServiceImpl.java

@@ -159,7 +159,7 @@ public class ReportDashboardServiceImpl implements ReportDashboardService, Initi
                 .lambda().eq(ReportDashboardWidget::getReportCode, reportCode));
         List<ReportDashboardWidgetDto> widgets = dto.getWidgets();
 
-        List<ReportDashboardWidget> reportDashboardWidgetList = new ArrayList<>();
+//        List<ReportDashboardWidget> reportDashboardWidgetList = new ArrayList<>();
         for (int i = 0; i < widgets.size(); i++) {
             ReportDashboardWidget reportDashboardWidget = new ReportDashboardWidget();
             ReportDashboardWidgetDto reportDashboardWidgetDto = widgets.get(i);
@@ -175,9 +175,13 @@ public class ReportDashboardServiceImpl implements ReportDashboardService, Initi
             reportDashboardWidget.setEnableFlag(1);
             reportDashboardWidget.setDeleteFlag(0);
             reportDashboardWidget.setSort((long) (i + 1));
-            reportDashboardWidgetList.add(reportDashboardWidget);
+
+            //兼容底层,不采用批量插入
+            reportDashboardWidgetService.insert(reportDashboardWidget);
+
+//            reportDashboardWidgetList.add(reportDashboardWidget);
         }
-        reportDashboardWidgetService.insertBatch(reportDashboardWidgetList);
+//        reportDashboardWidgetService.insertBatch(reportDashboardWidgetList);
 
     }
 
@@ -348,6 +352,9 @@ public class ReportDashboardServiceImpl implements ReportDashboardService, Initi
 
 
     private String replaceUrl(String imageAddress, Map<String, String> fileMap) {
+        if (StringUtils.isBlank(imageAddress)) {
+            return "";
+        }
         String fileId = imageAddress.substring(imageAddress.trim().length() - 36);
         String orDefault = fileMap.getOrDefault(fileId, null);
         if (StringUtils.isBlank(orDefault)) {
@@ -443,14 +450,19 @@ public class ReportDashboardServiceImpl implements ReportDashboardService, Initi
             return data;
         }
         //获取时间轴字段和解析时间颗粒度
-        chartProperties.forEach((key, value) -> {
+
+        for (Map.Entry<String, String> entry : chartProperties.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
             dto.setParticles(value);
             setTimeLineFormat(dto);
             if (StringUtils.isNotBlank(dto.getDataTimeFormat())) {
                 dto.setTimeLineFiled(key);
-                return;
+                break;
             }
-        });
+
+        }
+
 
         if (StringUtils.isBlank(dto.getDataTimeFormat())) {
             return data;

+ 1 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dashboardwidget/dao/entity/ReportDashboardWidget.java

@@ -11,7 +11,7 @@ import lombok.Data;
 * @author Raod
 * @date 2021-04-12 15:12:43.724
 **/
-@TableName(value="gaea_report_dashboard_widget")
+@TableName(keepGlobalPrefix=true, value="gaea_report_dashboard_widget")
 @Data
 public class ReportDashboardWidget extends GaeaBaseEntity {
     @ApiModelProperty(value = "报表编码")

+ 3 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataset/controller/dto/DataSetDto.java

@@ -55,4 +55,7 @@ public class DataSetDto extends GaeaBaseDTO implements Serializable {
 
     private Set<String> setParamList;
 
+    /**指定字段*/
+    private String fieldLabel;
+
 }

+ 1 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataset/dao/entity/DataSet.java

@@ -13,7 +13,7 @@ import lombok.Data;
 * @author Raod
 * @date 2021-03-18 12:11:31.150755900
 **/
-@TableName(value="gaea_report_data_set")
+@TableName(keepGlobalPrefix=true, value="gaea_report_data_set")
 @Data
 public class DataSet extends GaeaBaseEntity {
     @ApiModelProperty(value = "数据集编码")

+ 13 - 6
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataset/service/impl/DataSetServiceImpl.java

@@ -226,11 +226,13 @@ public class DataSetServiceImpl implements DataSetService {
         DataSource dataSource = dataSourceService.selectOne("source_code", dataSetDto.getSourceCode());
         //3.参数替换
         //3.1参数校验
+        log.debug("参数校验替换前:{}", dto.getContextData());
         boolean verification = dataSetParamService.verification(dataSetDto.getDataSetParamDtoList(), dto.getContextData());
         if (!verification) {
             throw BusinessExceptionBuilder.build(ResponseCode.RULE_FIELDS_CHECK_ERROR);
         }
         String dynSentence = dataSetParamService.transform(dto.getContextData(), dataSetDto.getDynSentence());
+        log.debug("参数校验替换后:{}", dto.getContextData());
         //4.获取数据
         DataSourceDto dataSourceDto = new DataSourceDto();
         BeanUtils.copyProperties(dataSource, dataSourceDto);
@@ -300,6 +302,7 @@ public class DataSetServiceImpl implements DataSetService {
         LambdaQueryWrapper<DataSet> wrapper = Wrappers.lambdaQuery();
         wrapper.select(DataSet::getSetCode, DataSet::getSetName, DataSet::getSetDesc, DataSet::getId)
                 .eq(DataSet::getEnableFlag, Enabled.YES.getValue());
+        wrapper.orderByDesc(DataSet::getUpdateTime);
         return dataSetMapper.selectList(wrapper);
     }
 
@@ -312,14 +315,16 @@ public class DataSetServiceImpl implements DataSetService {
         if (null == dataSetParamDtoList || dataSetParamDtoList.size() <= 0) {
             return;
         }
-        List<DataSetParam> dataSetParamList = new ArrayList<>();
+//        List<DataSetParam> dataSetParamList = new ArrayList<>();
         dataSetParamDtoList.forEach(dataSetParamDto -> {
             DataSetParam dataSetParam = new DataSetParam();
             BeanUtils.copyProperties(dataSetParamDto, dataSetParam);
             dataSetParam.setSetCode(setCode);
-            dataSetParamList.add(dataSetParam);
+            //不采用批量
+            dataSetParamService.insert(dataSetParam);
+//            dataSetParamList.add(dataSetParam);
         });
-        dataSetParamService.insertBatch(dataSetParamList);
+//        dataSetParamService.insertBatch(dataSetParamList);
 
     }
 
@@ -332,15 +337,17 @@ public class DataSetServiceImpl implements DataSetService {
         if (null == dataSetTransformDtoList || dataSetTransformDtoList.size() <= 0) {
             return;
         }
-        List<DataSetTransform> dataSetTransformList = new ArrayList<>();
+//        List<DataSetTransform> dataSetTransformList = new ArrayList<>();
         for (int i = 0; i < dataSetTransformDtoList.size(); i++) {
             DataSetTransform dataSetTransform = new DataSetTransform();
             BeanUtils.copyProperties(dataSetTransformDtoList.get(i), dataSetTransform);
             dataSetTransform.setOrderNum(i + 1);
             dataSetTransform.setSetCode(setCode);
-            dataSetTransformList.add(dataSetTransform);
+            //不采用批量
+            dataSetTransformService.insert(dataSetTransform);
+//            dataSetTransformList.add(dataSetTransform);
         }
-        dataSetTransformService.insertBatch(dataSetTransformList);
+//        dataSetTransformService.insertBatch(dataSetTransformList);
     }
 
 }

+ 1 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasetparam/dao/entity/DataSetParam.java

@@ -11,7 +11,7 @@ import lombok.Data;
 * @author Raod
 * @date 2021-03-18 12:12:33.108033200
 **/
-@TableName(value="gaea_report_data_set_param")
+@TableName(keepGlobalPrefix=true, value="gaea_report_data_set_param")
 @Data
 public class DataSetParam extends GaeaBaseEntity {
     @ApiModelProperty(value = "数据集编码")

+ 1 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasetparam/service/DataSetParamService.java

@@ -39,7 +39,7 @@ public interface DataSetParamService extends GaeaBaseService<DataSetParamParam,
      * @param dataSetParamDto
      * @return
      */
-    boolean verification(DataSetParamDto dataSetParamDto);
+    Object verification(DataSetParamDto dataSetParamDto);
 
     /**
      * 参数校验  js脚本

+ 20 - 4
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasetparam/service/impl/DataSetParamServiceImpl.java

@@ -93,7 +93,7 @@ public class DataSetParamServiceImpl implements DataSetParamService {
      * @return
      */
     @Override
-    public boolean verification(DataSetParamDto dataSetParamDto) {
+    public Object verification(DataSetParamDto dataSetParamDto) {
 
         String validationRules = dataSetParamDto.getValidationRules();
         if (StringUtils.isNotBlank(validationRules)) {
@@ -103,7 +103,12 @@ public class DataSetParamServiceImpl implements DataSetParamService {
                     Invocable invocable = (Invocable) engine;
                     Object exec = invocable.invokeFunction("verification", dataSetParamDto);
                     ObjectMapper objectMapper = new ObjectMapper();
-                    return objectMapper.convertValue(exec, Boolean.class);
+                    if (exec instanceof Boolean) {
+                        return objectMapper.convertValue(exec, Boolean.class);
+                    }else {
+                        return objectMapper.convertValue(exec, String.class);
+                    }
+
                 }
 
             } catch (Exception ex) {
@@ -131,9 +136,20 @@ public class DataSetParamServiceImpl implements DataSetParamService {
                 String value = contextData.getOrDefault(dataSetParamDto.getParamName(), "").toString();
                 dataSetParamDto.setSampleItem(value);
             }
-            if (!verification(dataSetParamDto)) {
-                return false;
+
+            Object verification = verification(dataSetParamDto);
+            if (verification instanceof Boolean) {
+                if (!(Boolean) verification) {
+                    return false;
+                }
+            }else {
+                //将得到的值重新赋值给contextData
+                if (null != contextData) {
+                    contextData.put(dataSetParamDto.getParamName(), verification);
+                }
+                dataSetParamDto.setSampleItem(verification.toString());
             }
+
         }
         return true;
     }

+ 1 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasettransform/dao/entity/DataSetTransform.java

@@ -11,7 +11,7 @@ import lombok.Data;
 * @author Raod
 * @date 2021-03-18 12:13:15.591309400
 **/
-@TableName(value="gaea_report_data_set_transform")
+@TableName(keepGlobalPrefix=true, value="gaea_report_data_set_transform")
 @Data
 public class DataSetTransform extends GaeaBaseEntity {
     @ApiModelProperty(value = "数据集编码")

+ 1 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/dao/entity/DataSource.java

@@ -13,7 +13,7 @@ import lombok.Data;
 * @author Raod
 * @date 2021-03-18 12:09:57.728203200
 **/
-@TableName(value="gaea_report_data_source")
+@TableName(keepGlobalPrefix=true, value="gaea_report_data_source")
 @Data
 public class DataSource extends GaeaBaseEntity {
     @ApiModelProperty(value = "数据源编码")

+ 26 - 5
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/service/impl/DataSourceServiceImpl.java

@@ -33,11 +33,9 @@ import org.springframework.web.client.RestClientException;
 import org.springframework.web.client.RestTemplate;
 
 import javax.annotation.Resource;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
+import java.sql.*;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
@@ -77,6 +75,7 @@ public class DataSourceServiceImpl implements DataSourceService {
         LambdaQueryWrapper<DataSource> wrapper = Wrappers.lambdaQuery();
         wrapper.select(DataSource::getSourceCode, DataSource::getSourceName)
                 .eq(DataSource::getEnableFlag, Enabled.YES.getValue());
+        wrapper.orderByDesc(DataSource::getUpdateTime);
         return dataSourceMapper.selectList(wrapper);
     }
 
@@ -243,7 +242,9 @@ public class DataSourceServiceImpl implements DataSourceService {
                 columns.forEach(t -> {
                     try {
                         Object value = rs.getObject(t);
-                        jo.put(t, value);
+                        //数据类型转换
+                        Object result = dealResult(value);
+                        jo.put(t, result);
                     } catch (SQLException throwable) {
                         log.error("error",throwable);
                         throw BusinessExceptionBuilder.build(ResponseCode.EXECUTE_SQL_ERROR, throwable.getMessage());
@@ -267,6 +268,26 @@ public class DataSourceServiceImpl implements DataSourceService {
         }
     }
 
+    /**
+     * 解决sql返回值 类型问题
+     * (through reference chain: java.util.HashMap["pageData"]->java.util.ArrayList[0]->java.util.HashMap["UPDATE_TIME"]->oracle.sql.TIMESTAMP["stream"])
+     * @param result
+     * @return
+     * @throws SQLException
+     */
+    private Object dealResult(Object result) throws SQLException {
+        if (null == result) {
+            return result;
+        }
+        String type = result.getClass().getName();
+        if ("oracle.sql.TIMESTAMP".equals(type)) {
+            //oracle.sql.TIMESTAMP处理逻辑
+            return new Date((Long) JSONObject.toJSON(result));
+        }
+
+        return result;
+    }
+
     /**
      * http 执行获取数据
      *

+ 1 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dict/dao/entity/GaeaDict.java

@@ -13,7 +13,7 @@ import java.io.Serializable;
  * @author lr
  * @since 2021-02-23 10:01:02
  */
-@TableName("gaea_dict")
+@TableName(keepGlobalPrefix=true, value = "gaea_dict")
 public class GaeaDict extends GaeaBaseEntity implements Serializable {
     /**
      * 字典名称

+ 1 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dict/dao/entity/GaeaDictItem.java

@@ -15,7 +15,7 @@ import java.io.Serializable;
  * @author lirui
  * @since 2021-03-09 15:52:41
  */
-@TableName("gaea_dict_item")
+@TableName(keepGlobalPrefix=true,value = "gaea_dict_item")
 @UnionUniqueCode(group = BusinessConstant.DICT_ITEM_EXIST_GROUP, code = ResponseCode.DICT_ITEM_REPEAT)
 public class GaeaDictItem extends GaeaBaseEntity implements Serializable {
 

+ 1 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/file/entity/GaeaFile.java

@@ -13,7 +13,7 @@ import java.io.Serializable;
  * @author peiyanni
  * @since 2021-02-18 14:48:20
  */
-@TableName("gaea_file")
+@TableName(keepGlobalPrefix=true, value = "gaea_file")
 @Data
 public class GaeaFile extends GaeaBaseEntity implements Serializable {
 

+ 1 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/file/service/impl/GaeaFileServiceImpl.java

@@ -99,7 +99,7 @@ public class GaeaFileServiceImpl implements GaeaFileService {
             String newFileName = fileId + suffixName;
             // 本地文件保存路径
             String filePath = dictPath + newFileName;
-            String urlPath = fileDownloadPath + java.io.File.separator + fileId;
+            String urlPath = fileDownloadPath + "/" + fileId;
 
             GaeaFile gaeaFile = new GaeaFile();
             gaeaFile.setFilePath(filePath);

+ 1 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/report/dao/entity/Report.java

@@ -13,7 +13,7 @@ import lombok.Data;
  * @author chenkening
  * @date 2021/3/26 10:20
  */
-@TableName(value="gaea_report")
+@TableName(keepGlobalPrefix=true, value="gaea_report")
 @Data
 public class Report extends GaeaBaseEntity {
 

+ 3 - 3
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/report/service/impl/ReportServiceImpl.java

@@ -11,7 +11,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 /**
- * TODO
  *
  * @author chenkening
  * @date 2021/3/26 10:35
@@ -31,11 +30,12 @@ public class ReportServiceImpl implements ReportService {
     @Override
     public void delReport(ReportDto reportDto) {
         deleteById(reportDto.getId());
+        //删除gaea_report_excel、gaea_report_dashboard、gaea_report_dashboard_widget
+        //...
     }
 
     @Override
     public void processBeforeOperation(Report entity, BaseOperationEnum operationEnum) throws BusinessException {
-        //目前只有大屏一种类型
-        entity.setReportType("report_screen");
+
     }
 }

+ 78 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/controller/ReportExcelController.java

@@ -0,0 +1,78 @@
+package com.anjiplus.template.gaea.business.modules.reportexcel.controller;
+
+import com.anji.plus.gaea.annotation.Permission;
+import com.anji.plus.gaea.annotation.log.GaeaAuditLog;
+import com.anji.plus.gaea.bean.ResponseBean;
+import com.anji.plus.gaea.code.ResponseCode;
+import com.anji.plus.gaea.curd.controller.GaeaBaseController;
+import com.anji.plus.gaea.curd.service.GaeaBaseService;
+import com.anjiplus.template.gaea.business.modules.reportexcel.controller.dto.ReportExcelDto;
+import com.anjiplus.template.gaea.business.modules.reportexcel.controller.param.ReportExcelParam;
+import com.anjiplus.template.gaea.business.modules.reportexcel.dao.entity.ReportExcel;
+import com.anjiplus.template.gaea.business.modules.reportexcel.service.ReportExcelService;
+import io.swagger.annotations.Api;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author chenkening
+ * @date 2021/4/13 15:12
+ */
+@RestController
+@Api(tags = "报表表格管理")
+@Permission(code = "excelManage", name = "报表管理")
+@RequestMapping("/reportExcel")
+public class ReportExcelController extends GaeaBaseController<ReportExcelParam, ReportExcel, ReportExcelDto> {
+
+    @Autowired
+    private ReportExcelService reportExcelService;
+
+    @Override
+    public GaeaBaseService<ReportExcelParam, ReportExcel> getService() {
+        return reportExcelService;
+    }
+
+    @Override
+    public ReportExcel getEntity() {
+        return new ReportExcel();
+    }
+
+    @Override
+    public ReportExcelDto getDTO() {
+        return new ReportExcelDto();
+    }
+
+    @GetMapping("/detailByReportCode/{reportCode}")
+    @Permission(code = "query", name = "详情")
+    @GaeaAuditLog(pageTitle = "详情")
+    public ResponseBean detailByReportCode(@PathVariable String reportCode) {
+        ReportExcelDto reportExcelDto = reportExcelService.detailByReportCode(reportCode);
+        return ResponseBean.builder().data(reportExcelDto).build();
+    }
+
+    @PostMapping("/preview")
+    @Permission(code = "view", name = "预览")
+    @GaeaAuditLog(pageTitle = "预览")
+    public ResponseBean preview(@RequestBody ReportExcelDto reportExcelDto) {
+        ReportExcelDto result = reportExcelService.preview(reportExcelDto);
+        return ResponseBean.builder().data(result).build();
+    }
+
+
+    @PostMapping("/exportExcel")
+    @Permission(code = "export", name = "导出")
+    @GaeaAuditLog(pageTitle = "报表导出")
+    public ResponseBean exportExcel(@RequestBody ReportExcelDto reportExcelDto) {
+
+        return ResponseBean.builder().code(ResponseCode.SUCCESS_CODE)
+                .data(reportExcelService.exportExcel(reportExcelDto))
+                .message("导出成功,请稍后在文件管理中查看").build();
+    }
+
+//    @PostMapping("/exportPdf")
+//    public ResponseBean exportPdf(@RequestBody ReportExcelDto reportExcelDto) {
+//        reportExcelService.exportPdf(reportExcelDto);
+//        return ResponseBean.builder().code(ResponseCode.SUCCESS_CODE)
+//                .build();
+//    }
+}

+ 58 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/controller/dto/GridRecordDataModel.java

@@ -0,0 +1,58 @@
+package com.anjiplus.template.gaea.business.modules.reportexcel.controller.dto;
+
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 存储对象类
+ *
+ * @author Administrator
+ */
+@Data
+public class GridRecordDataModel {
+    /**
+     * 记录序列
+     */
+    Long id;
+    /**
+     * 文档ID
+     */
+    String list_id;
+    /**
+     * 本记录的行_列
+     */
+    String row_col;
+    /**
+     * sheet序号
+     */
+    String index;
+    /**
+     * 状态是否当前sheet页
+     */
+    Integer status;
+    /**
+     * 块编号 第一块 fblock
+     */
+    String block_id;
+    /**
+     * json串
+     */
+    JSONObject json_data;
+    /**
+     * 排序位置
+     */
+    Integer order;
+    /**
+     * 是否删除
+     */
+    Integer is_delete;
+
+    /**
+     * sheet页数据 未编号分组
+     */
+    List<JSONObject> dataList;
+
+}

+ 61 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/controller/dto/ReportExcelDto.java

@@ -0,0 +1,61 @@
+
+package com.anjiplus.template.gaea.business.modules.reportexcel.controller.dto;
+
+import com.anji.plus.gaea.curd.dto.GaeaBaseDTO;
+import lombok.Data;
+
+import java.io.Serializable;
+
+
+/**
+ * @author chenkening
+ * @date 2021/4/13 15:12
+ */
+@Data
+public class ReportExcelDto extends GaeaBaseDTO implements Serializable {
+    /**
+     * 报表名称
+     */
+    private String reportName;
+
+    /**
+     * 报表编码
+     */
+    private String reportCode;
+
+    /**
+     * 数据集编码,以|分割
+     */
+    private String setCodes;
+
+    /**
+     * 分组
+     */
+    private String reportGroup;
+
+    /**
+     * 数据集查询参数
+     */
+    private String setParam;
+
+    /**
+     * 报表json字符串
+     */
+    private String jsonStr;
+
+    /**
+     * 报表类型
+     */
+    private String reportType;
+
+    /**
+     * 数据总计
+     */
+    private long total;
+
+    /**
+     * 导出类型
+     */
+    private String exportType;
+
+}

+ 18 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/controller/param/ReportExcelParam.java

@@ -0,0 +1,18 @@
+
+package com.anjiplus.template.gaea.business.modules.reportexcel.controller.param;
+
+import com.anji.plus.gaea.curd.params.PageParam;
+import lombok.Data;
+
+import java.io.Serializable;
+
+
+/**
+ * @author chenkening
+ * @date 2021/4/13 15:12
+ */
+@Data
+public class ReportExcelParam extends PageParam implements Serializable {
+
+
+}

+ 11 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/dao/ReportExcelMapper.java

@@ -0,0 +1,11 @@
+package com.anjiplus.template.gaea.business.modules.reportexcel.dao;
+
+import com.anji.plus.gaea.curd.mapper.GaeaBaseMapper;
+import com.anjiplus.template.gaea.business.modules.reportexcel.dao.entity.ReportExcel;
+
+/**
+ * @author chenkening
+ * @date 2021/4/13 15:11
+ */
+public interface ReportExcelMapper extends GaeaBaseMapper<ReportExcel> {
+}

+ 33 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/dao/entity/ReportExcel.java

@@ -0,0 +1,33 @@
+package com.anjiplus.template.gaea.business.modules.reportexcel.dao.entity;
+
+import com.anji.plus.gaea.curd.entity.GaeaBaseEntity;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @author chenkening
+ * @date 2021/4/13 15:11
+ */
+@TableName(value = "gaea_report_excel")
+@Data
+public class ReportExcel extends GaeaBaseEntity {
+
+    @ApiModelProperty(value = "报表编码")
+    private String reportCode;
+
+    @ApiModelProperty(value = "数据集编码,以|分割")
+    private String setCodes;
+
+    @ApiModelProperty(value = "数据集查询参数")
+    private String setParam;
+
+    @ApiModelProperty(value = "报表json字符串")
+    private String jsonStr;
+
+    @ApiModelProperty(value = "0--已禁用 1--已启用  DIC_NAME=ENABLE_FLAG")
+    private Integer enableFlag;
+
+    @ApiModelProperty(value = "0--未删除 1--已删除 DIC_NAME=DELETE_FLAG")
+    private Integer deleteFlag;
+}

+ 43 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/service/ReportExcelService.java

@@ -0,0 +1,43 @@
+package com.anjiplus.template.gaea.business.modules.reportexcel.service;
+
+
+import com.anji.plus.gaea.curd.service.GaeaBaseService;
+import com.anjiplus.template.gaea.business.modules.reportexcel.controller.dto.ReportExcelDto;
+import com.anjiplus.template.gaea.business.modules.reportexcel.controller.param.ReportExcelParam;
+import com.anjiplus.template.gaea.business.modules.reportexcel.dao.entity.ReportExcel;
+
+/**
+ * TODO
+ *
+ * @author chenkening
+ * @date 2021/4/13 15:14
+ */
+public interface ReportExcelService extends GaeaBaseService<ReportExcelParam, ReportExcel> {
+
+    /**
+     * 根据报表编码查询详情
+     *
+     * @param reportCode
+     * @return
+     */
+    ReportExcelDto detailByReportCode(String reportCode);
+
+    /**
+     * 报表预览
+     *
+     * @param reportExcelDto
+     * @return
+     */
+    ReportExcelDto preview(ReportExcelDto reportExcelDto);
+
+
+    /**
+     * 导出为excel
+     *
+     * @param reportExcelDto
+     * @return
+     */
+    Boolean exportExcel(ReportExcelDto reportExcelDto);
+
+//    Boolean exportPdf(ReportExcelDto reportExcelDto);
+}

+ 362 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/service/impl/ReportExcelServiceImpl.java

@@ -0,0 +1,362 @@
+package com.anjiplus.template.gaea.business.modules.reportexcel.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.anji.plus.gaea.constant.BaseOperationEnum;
+import com.anji.plus.gaea.curd.mapper.GaeaBaseMapper;
+import com.anji.plus.gaea.exception.BusinessException;
+import com.anji.plus.gaea.utils.GaeaAssert;
+import com.anji.plus.gaea.utils.GaeaBeanUtils;
+import com.anjiplus.template.gaea.business.code.ResponseCode;
+import com.anjiplus.template.gaea.business.enums.ExportTypeEnum;
+import com.anjiplus.template.gaea.business.modules.dataset.controller.dto.DataSetDto;
+import com.anjiplus.template.gaea.business.modules.dataset.controller.dto.OriginalDataDto;
+import com.anjiplus.template.gaea.business.modules.dataset.service.DataSetService;
+import com.anjiplus.template.gaea.business.modules.file.dao.GaeaFileMapper;
+import com.anjiplus.template.gaea.business.modules.file.entity.GaeaFile;
+import com.anjiplus.template.gaea.business.modules.report.dao.ReportMapper;
+import com.anjiplus.template.gaea.business.modules.report.dao.entity.Report;
+import com.anjiplus.template.gaea.business.modules.reportexcel.controller.dto.ReportExcelDto;
+import com.anjiplus.template.gaea.business.modules.reportexcel.dao.ReportExcelMapper;
+import com.anjiplus.template.gaea.business.modules.reportexcel.dao.entity.ReportExcel;
+import com.anjiplus.template.gaea.business.modules.reportexcel.service.ReportExcelService;
+import com.anjiplus.template.gaea.business.modules.reportexcel.util.XlsSheetUtil;
+import com.anjiplus.template.gaea.business.modules.reportexcel.util.XlsUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * TODO
+ *
+ * @author chenkening
+ * @date 2021/4/13 15:14
+ */
+@Service
+public class ReportExcelServiceImpl implements ReportExcelService {
+
+    private Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @Autowired
+    private ReportExcelMapper reportExcelMapper;
+
+    @Autowired
+    private DataSetService dataSetService;
+
+
+    @Autowired
+    private ReportMapper reportMapper;
+
+    @Value("${customer.file.dist-path:''}")
+    private String dictPath;
+
+    @Value("${customer.file.downloadPath:''}")
+    private String fileDownloadPath;
+
+    @Autowired
+    private GaeaFileMapper gaeaFileMapper;
+
+
+    @Override
+    public GaeaBaseMapper<ReportExcel> getMapper() {
+        return reportExcelMapper;
+    }
+
+    @Override
+    public ReportExcelDto detailByReportCode(String reportCode) {
+        QueryWrapper<ReportExcel> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("report_code", reportCode);
+        ReportExcel reportExcel = reportExcelMapper.selectOne(queryWrapper);
+        if (reportExcel != null) {
+            ReportExcelDto dto = new ReportExcelDto();
+            BeanUtils.copyProperties(reportExcel, dto);
+            return dto;
+        }
+        return null;
+    }
+
+    /**
+     * 操作前处理
+     *
+     * @param entity        前端传递的对象
+     * @param operationEnum 操作类型
+     * @throws BusinessException 阻止程序继续执行或回滚事务
+     */
+    @Override
+    public void processBeforeOperation(ReportExcel entity, BaseOperationEnum operationEnum) throws BusinessException {
+        if (operationEnum.equals(BaseOperationEnum.INSERT)) {
+            String reportCode = entity.getReportCode();
+            ReportExcel report = this.selectOne("report_code", reportCode);
+            if (null != report) {
+                this.deleteById(report.getId());
+            }
+        }
+    }
+
+    /**
+     * 报表预览
+     */
+    @Override
+    public ReportExcelDto preview(ReportExcelDto reportExcelDto) {
+        // 根据id查询 报表详情
+        ReportExcel reportExcel = selectOne("report_code", reportExcelDto.getReportCode());
+        QueryWrapper<Report> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("report_code", reportExcelDto.getReportCode());
+        Report report = reportMapper.selectOne(queryWrapper);
+        GaeaAssert.notNull(reportExcel, ResponseCode.RULE_CONTENT_NOT_EXIST, "reportExcel");
+        String setParam = reportExcelDto.getSetParam();
+
+        GaeaBeanUtils.copyAndFormatter(reportExcel, reportExcelDto);
+        if (StringUtils.isNotBlank(setParam)) {
+            reportExcelDto.setSetParam(setParam);
+        }
+        reportExcelDto.setReportName(report.getReportName());
+        // 数据集解析
+        String jsonStr = analysisReportData(reportExcelDto);
+        reportExcelDto.setJsonStr(jsonStr);
+//        reportExcelDto.setTotal(jsonObject.getJSONObject("rows").size());
+        return reportExcelDto;
+    }
+
+    @Override
+    public Boolean exportExcel(ReportExcelDto reportExcelDto) {
+        String reportCode = reportExcelDto.getReportCode();
+        String exportType = reportExcelDto.getExportType();
+        logger.error("导出...");
+        if (exportType.equals(ExportTypeEnum.GAEA_TEMPLATE_EXCEL.getCodeValue())) {
+            ReportExcelDto report = detailByReportCode(reportCode);
+            reportExcelDto.setJsonStr(report.getJsonStr());
+            String jsonStr = analysisReportData(reportExcelDto);
+            List<JSONObject> lists=(List<JSONObject> ) JSON.parse(jsonStr);
+            OutputStream out;
+            try {
+                String fileId = UUID.randomUUID().toString();
+                String filePath = dictPath + File.separator + fileId + ".xlsx";
+                String urlPath = fileDownloadPath + java.io.File.separator + fileId;
+
+                GaeaFile gaeaFile = new GaeaFile();
+                gaeaFile.setFilePath(filePath);
+                gaeaFile.setFileId(fileId);
+                gaeaFile.setUrlPath(urlPath);
+                gaeaFile.setFileType("xlsx");
+                gaeaFile.setFileInstruction(reportCode + ".xlsx");
+
+                out = new FileOutputStream(filePath);
+                XlsUtil.exportXlsFile(out, true, lists);
+
+                gaeaFileMapper.insert(gaeaFile);
+                logger.info("导出成功:{}", gaeaFile);
+            } catch (IOException e) {
+                logger.error("导出失败", e);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 解析报表数据,动态插入列表数据和对象数据
+     */
+    private String analysisReportData(ReportExcelDto reportExcelDto) {
+
+        String jsonStr = reportExcelDto.getJsonStr();
+        String setParam = reportExcelDto.getSetParam();
+        List<JSONObject> dbObjectList = (List<JSONObject>) JSON.parse(jsonStr);
+
+        if (dbObjectList != null && dbObjectList.size() > 0) {
+            for (int x = 0; x < dbObjectList.size(); x++) {
+                analysisSheetCellData(dbObjectList.get(x), setParam);
+            }
+        }
+        //fastjson $ref 循环引用
+        return JSONObject.toJSONString(dbObjectList, SerializerFeature.DisableCircularReferenceDetect);
+    }
+
+    /**
+     * 解析单sheet data
+     *
+     * @param dbObject
+     */
+    private void analysisSheet(JSONObject dbObject, String setParma) {
+        //data是一个二维数组
+        if (dbObject.containsKey("data") && null != dbObject.get("data")) {
+            List<JSONArray> data = (List<JSONArray>) dbObject.get("data");
+
+
+            //行
+            for (int r = 0; r < data.size(); r++) {
+                JSONArray jsonArray = data.get(r);
+                //列
+                for (int c = 0; c < jsonArray.size(); c++) {
+                    //单元格
+                    JSONObject cell = jsonArray.getJSONObject(c);
+                    if (null != cell && cell.containsKey("v") && StringUtils.isNotBlank(cell.getString("v"))) {
+                        String v = cell.getString("v");
+                        DataSetDto dataSet = getDataSet(v, setParma);
+                        if (null != dataSet) {
+                            OriginalDataDto originalDataDto = dataSetService.getData(dataSet);
+                            if (null != originalDataDto.getData()) {
+                                if (originalDataDto.getData().size() == 1) {
+                                    //对象
+                                    JSONObject jsonObject = originalDataDto.getData().get(0);
+                                    String fieldLabel = jsonObject.getString(dataSet.getFieldLabel());
+
+                                    String replace = v.replace("#{".concat(dataSet.getSetCode()).concat(".").concat(dataSet.getFieldLabel()).concat("}"), fieldLabel);
+                                    dbObject.getJSONArray("data").getJSONArray(r).getJSONObject(c).put("v", replace);
+                                    dbObject.getJSONArray("data").getJSONArray(r).getJSONObject(c).put("m", replace);
+
+                                } else {
+                                    //集合
+                                    JSONObject jsonObject = originalDataDto.getData().get(0);
+                                    String fieldLabel = jsonObject.getString(dataSet.getFieldLabel());
+
+                                    String replace = v.replace("#{".concat(dataSet.getSetCode()).concat(".").concat(dataSet.getFieldLabel()).concat("}"), fieldLabel);
+                                    dbObject.getJSONArray("data").getJSONArray(r).getJSONObject(c).put("v", replace);
+                                    dbObject.getJSONArray("data").getJSONArray(r).getJSONObject(c).put("m", replace);
+                                }
+                            }
+
+                        }
+                    }
+
+
+
+                }
+            }
+
+
+            System.out.println("aaaa");
+
+
+        }
+
+
+    }
+
+    /**
+     * 解析单sheet celldata
+     *
+     * @param dbObject
+     */
+    private void analysisSheetCellData(JSONObject dbObject, String setParam) {
+        //清空data值
+        dbObject.remove("data");
+        //celldata是一个一维数组
+        if (dbObject.containsKey("celldata") && null != dbObject.get("celldata")) {
+            List<JSONObject> celldata = new ArrayList<>();
+            celldata.addAll((List<JSONObject>) dbObject.get("celldata"));
+            // 遍历已存在的单元格,查看是否存在动态参数
+            for (int i = 0; i < celldata.size(); i++) {
+                //单元格对象
+                JSONObject cellObj = celldata.get(i);
+                //fastjson深拷贝问题
+                String cellStr = cellObj.toJSONString();
+
+                //行号
+                Integer r = cellObj.getInteger("r");
+                //列号
+                Integer c = cellObj.getInteger("c");
+                JSONObject cell = cellObj.getJSONObject("v");
+                if (null != cell && cell.containsKey("v") && StringUtils.isNotBlank(cell.getString("v"))) {
+                    String v = cell.getString("v");
+                    DataSetDto dataSet = getDataSet(v, setParam);
+                    if (null != dataSet) {
+                        OriginalDataDto originalDataDto = dataSetService.getData(dataSet);
+                        if (null != originalDataDto.getData()) {
+                            List<JSONObject> data = originalDataDto.getData();
+
+                            for (int j = 0; j < data.size(); j++) {
+                                if (j == 0) {
+                                    //处理当前行
+                                    //第一行,作为渲染参照数据
+                                    JSONObject jsonObject = data.get(j);
+                                    String fieldLabel = jsonObject.getString(dataSet.getFieldLabel());
+
+                                    String replace = v.replace("#{".concat(dataSet.getSetCode()).concat(".").concat(dataSet.getFieldLabel()).concat("}"), fieldLabel);
+                                    dbObject.getJSONArray("celldata").getJSONObject(i).getJSONObject("v").put("v", replace);
+                                    dbObject.getJSONArray("celldata").getJSONObject(i).getJSONObject("v").put("m", replace);
+                                } else {
+                                    //新增的行数据
+                                    JSONObject addCell = data.get(j);
+                                    //字段
+                                    String fieldLabel = addCell.getString(dataSet.getFieldLabel());
+                                    String replace = v.replace("#{".concat(dataSet.getSetCode()).concat(".").concat(dataSet.getFieldLabel()).concat("}"), fieldLabel);
+
+                                    //转字符串,解决深拷贝问题
+                                    JSONObject addCellData = JSONObject.parseObject(cellStr);
+
+                                    addCellData.put("r", r + j);
+                                    addCellData.put("c", c);
+                                    addCellData.getJSONObject("v").put("v", replace);
+                                    addCellData.getJSONObject("v").put("m", replace);
+                                    dbObject.getJSONArray("celldata").add(addCellData);
+
+                                }
+
+                            }
+
+                        }
+
+                    }
+                }
+            }
+
+        }
+
+
+    }
+
+
+    /**
+     * 解析 #{xxxx.xxxxx} 数据
+     * @param v
+     * @return
+     */
+    private DataSetDto getDataSet(String v, String setParam) {
+
+        DataSetDto dto = new DataSetDto();
+        if (v.contains("#{") && v.contains("}")) {
+            int start = v.indexOf("#{") + 2;
+            int end = v.indexOf("}");
+            if (start < end) {
+                String substring = v.substring(start, end);
+                if (substring.contains(".")) {
+                    String[] split = substring.split("\\.");
+                    dto.setSetCode( split[0]);
+                    dto.setFieldLabel(split[1]);
+                    getContextData(setParam, dto);
+                    return dto;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 动态参数替换
+     * @param setParam
+     * @param dto
+     */
+    private void getContextData(String setParam, DataSetDto dto) {
+        if (StringUtils.isNotBlank(setParam)) {
+            JSONObject setParamJson = JSONObject.parseObject(setParam);
+            Map<String, Object> map = new HashMap<>();
+            // 查询条件
+            if (setParamJson.containsKey(dto.getSetCode())) {
+                JSONObject paramCondition = setParamJson.getJSONObject(dto.getSetCode());
+                paramCondition.forEach(map::put);
+            }
+            dto.setContextData(map);
+        }
+    }
+
+}

+ 134 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/util/ColorUtil.java

@@ -0,0 +1,134 @@
+package com.anjiplus.template.gaea.business.modules.reportexcel.util;
+
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.hssf.usermodel.HSSFPalette;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.util.HSSFColor;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.Color;
+import org.apache.poi.xssf.usermodel.XSSFColor;
+
+
+/**
+ * 来自:https://github.com/mengshukeji/LuckysheetServer
+ *
+ * @author Administrator
+ */
+@Slf4j
+public class ColorUtil {
+
+    private static final String S = "0123456789ABCDEF";
+
+    public static Short getColorByStr(String colorStr) {
+        HSSFWorkbook workbook = new HSSFWorkbook();
+        HSSFPalette palette = workbook.getCustomPalette();
+
+        if (colorStr.toLowerCase().startsWith("rgb")) {
+            colorStr = colorStr.toLowerCase().replace("rgb(", "").replace(")", "");
+            String[] colors = colorStr.split(",");
+            if (colors.length == 3) {
+                try {
+                    int red = Integer.parseInt(colors[0].trim(), 16);
+                    int green = Integer.parseInt(colors[1].trim(), 16);
+                    int blue = Integer.parseInt(colors[2].trim(), 16);
+
+                    HSSFColor hssfColor = palette.findSimilarColor(red, green, blue);
+                    return hssfColor.getIndex();
+                } catch (Exception ex) {
+                    log.error(ex.toString());
+                    return null;
+                }
+            }
+            return null;
+        }
+
+        if (colorStr.equals("#000")) {
+            colorStr = "#000000";
+        }
+        if (colorStr != null && colorStr.length() >= 6) {
+            try {
+                if (colorStr.length() == 8) {
+                    colorStr = colorStr.substring(2);
+                }
+                if (colorStr.length() == 7) {
+                    colorStr = colorStr.substring(1);
+                }
+                String str2 = colorStr.substring(0, 2);
+                String str3 = colorStr.substring(2, 4);
+                String str4 = colorStr.substring(4, 6);
+                int red = Integer.parseInt(str2, 16);
+                int green = Integer.parseInt(str3, 16);
+                int blue = Integer.parseInt(str4, 16);
+
+                HSSFColor hssfColor = palette.findSimilarColor(red, green, blue);
+                return hssfColor.getIndex();
+            } catch (Exception ex) {
+                log.error(ex.toString());
+                return null;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * RGB转换成十六进制
+     *
+     * @param r
+     * @param g
+     * @param b
+     * @return
+     */
+    public static String convertRGBToHex(short r, short g, short b) {
+        String hex = "";
+        if (r >= 0 && r < 256 && g >= 0 && g < 256 && b >= 0 && b < 256) {
+            int x, y, z;
+            x = r % 16;
+            r = (short) ((r - x) / 16);
+            y = g % 16;
+            g = (short) ((g - y) / 16);
+            z = b % 16;
+            b = (short) ((b - z) / 16);
+            hex = "#" + S.charAt(r) + S.charAt(x) + S.charAt(g) + S.charAt(y) + S.charAt(b) + S.charAt(z);
+        }
+        return hex;
+    }
+
+    /**
+     * @param cell 单元格
+     * @return 转换RGB颜色值
+     * @description tint转换RBG
+     * @author zhouhang
+     * @date 2021/4/26
+     */
+    public static String getFillColorHex(Cell cell) {
+        String fillColorString = null;
+        if (cell != null) {
+            CellStyle cellStyle = cell.getCellStyle();
+            Color color = cellStyle.getFillForegroundColorColor();
+            if (color instanceof XSSFColor) {
+                XSSFColor xssfColor = (XSSFColor) color;
+                byte[] argb = xssfColor.getARGB();
+                fillColorString = convertRGBToHex((short) (argb[1] & 0xFF), (short) (argb[2] & 0xFF), (short) (argb[3] & 0xFF));
+                // TODO: 2021/4/26 添加透明度
+//                if (xssfColor.hasTint()) {
+//                    fillColorString += " * " + xssfColor.getTint();
+//                    byte[] rgb = xssfColor.getRGBWithTint();
+//                    fillColorString += " = [" + (argb[0] & 0xFF) + ", " + (rgb[0] & 0xFF) + ", " + (rgb[1] & 0xFF) + ", " + (rgb[2] & 0xFF) + "]";
+//                }
+            } else if (color instanceof HSSFColor) {
+                HSSFColor hssfColor = (HSSFColor) color;
+                short[] rgb = hssfColor.getTriplet();
+                fillColorString = convertRGBToHex((short) (rgb[0] & 0xFF), (short) (rgb[1] & 0xFF), (short) (rgb[2] & 0xFF));
+                //去除黑色背景
+                if (StringUtils.equals("#000000", fillColorString)) {
+                    return null;
+                }
+            }
+        }
+        return fillColorString;
+    }
+
+}

+ 313 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/util/ConstantUtil.java

@@ -0,0 +1,313 @@
+package com.anjiplus.template.gaea.business.modules.reportexcel.util;
+
+
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * 来自:https://github.com/mengshukeji/LuckysheetServer
+ *
+ * @author cr
+ */
+public class ConstantUtil {
+    /**
+     * 导出。字体转换
+     */
+    public static Map<Integer, String> ff_IntegerToName = new HashMap<Integer, String>();
+    /**
+     * 导入。字体转换
+     */
+    public static Map<String, Integer> ff_NameToInteger = new HashMap<String, Integer>();
+
+    /**
+     * 导入 36种数字格式。注意官方文档的编号不是连续的,22后面直接是37,所以数组中间补14个空值
+     */
+    public static String[] number_type = null;
+    /**
+     * 导入 36种格式的定义字符串
+     */
+    public static String[] number_format = null;
+    /**
+     * 数据类型
+     */
+    public static Map<String, Integer> number_format_map = new HashMap<String, Integer>();
+
+    static {
+        //格式
+        nf();
+        //字体
+        ff();
+    }
+
+    private static void nf() {
+        number_type = new String[]{
+                "General", "Decimal", "Decimal", "Decimal", "Decimal", "Currency", "Currency", "Currency", "Currency",
+                "Percentage", "Percentage", "Scientific", "Fraction", "Fraction", "Date", "Date", "Date", "Date",
+                "Time", "Time", "Time", "Time", "Time",
+                "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+                "Accounting", "Accounting", "Accounting", "Accounting", "Accounting",
+                "Currency", "Accounting", "Currency", "Time", "Time", "Time", "Scientific", "Text"
+        };
+
+        number_format = new String[]{
+                "General", "0", "0.00", "#,##0", "#,##0.00", "$#,##0;($#,##0)", "$#,##0;[Red]($#,##0)", "$#,##0.00;($#,##0.00)", "$#,##0.00;[Red]($#,##0.00)",
+                "0%", "0.00%", "0.00E+00", "# ?/?", "# ??/??", "m/d/yyyy", "d-mmm-yy", "d-mmm", "mmm-yy",
+                "h:mm AM/PM", "h:mm:ss AM/PM", "h:mm", "h:mm:ss", "m/d/yyyy h:mm",
+                "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+                "#,##0;(#,##0)", "#,##0;[Red](#,##0)", "#,##0.00;(#,##0.00)", "#,##0.00;[Red](#,##0.00)", "_ * #,##0_ ;_ * (#,##0)_ ;_ * \"-\"_ ;_ @_",
+                "_ $* #,##0_ ;_ $* (#,##0)_ ;_ $* \"-\"_ ;_ @_", "_ * #,##0.00_ ;_ * (#,##0.00)_ ;_ * \"-\"??_ ;_ @_", "_ $* #,##0.00_ ;_ $* (#,##0.00)_ ;_ $* \"-\"??_ ;_ @_", "mm:ss", "[h]:mm:ss", "mm:ss.0", "##0.0E+00", "@"
+        };
+        for (int x = 0; x < number_format.length; x++) {
+            if (number_format[x].length() > 0) {
+                number_format_map.put(number_format[x].toLowerCase(), x);
+            }
+        }
+    }
+
+    private static void ff() {
+        //0 微软雅黑、1 宋体(Song)、2 黑体(ST Heiti)、3 楷体(ST Kaiti)、 4仿宋(ST FangSong)、 5 新宋体(ST Song)、
+        // 6 华文新魏、 7华文行楷、 8 华文隶书、 9 Arial、 10 Times New Roman 、11 Tahoma 、12 Verdana
+        ff_IntegerToName.put(0, "微软雅黑");
+        ff_IntegerToName.put(1, "宋体");
+        ff_IntegerToName.put(2, "黑体");
+        ff_IntegerToName.put(3, "楷体");
+        ff_IntegerToName.put(4, "仿宋");
+        ff_IntegerToName.put(5, "新宋体");
+        ff_IntegerToName.put(6, "华文新魏");
+        ff_IntegerToName.put(7, "华文行楷");
+        ff_IntegerToName.put(8, "华文隶书");
+        ff_IntegerToName.put(9, "Arial");
+        ff_IntegerToName.put(10, "Times New Roman");
+        ff_IntegerToName.put(11, "Tahoma");
+        ff_IntegerToName.put(12, "Verdana");
+
+        //0 微软雅黑、1 宋体(Song)、2 黑体(ST Heiti)、3 楷体(ST Kaiti)、 4仿宋(ST FangSong)、 5 新宋体(ST Song)、
+        // 6 华文新魏、 7华文行楷、 8 华文隶书、 9 Arial、 10 Times New Roman 、11 Tahoma 、12 Verdana
+        ff_NameToInteger.put("微软雅黑", 0);
+        ff_NameToInteger.put("宋体", 1);
+        ff_NameToInteger.put("Song", 1);
+        ff_NameToInteger.put("黑体", 2);
+        ff_NameToInteger.put("ST Heiti", 2);
+        ff_NameToInteger.put("楷体", 3);
+        ff_NameToInteger.put("ST Kaiti", 3);
+        ff_NameToInteger.put("仿宋", 4);
+        ff_NameToInteger.put("ST FangSong", 4);
+        ff_NameToInteger.put("新宋体", 5);
+        ff_NameToInteger.put("ST Song", 5);
+        ff_NameToInteger.put("华文新魏", 6);
+        ff_NameToInteger.put("华文行楷", 7);
+        ff_NameToInteger.put("华文隶书", 8);
+        ff_NameToInteger.put("Arial", 9);
+        ff_NameToInteger.put("Times New Roman", 10);
+        ff_NameToInteger.put("Tahoma", 11);
+        ff_NameToInteger.put("Verdana", 12);
+    }
+
+    private static void borderType() {
+        //"border-left" | "border-right" | "border-top" | "border-bottom" | "border-all"
+        // | "border-outside" | "border-inside" | "border-horizontal" | "border-vertical" | "border-none"
+        ff_IntegerToName.put(0, "微软雅黑");
+        ff_IntegerToName.put(1, "宋体");
+        ff_IntegerToName.put(2, "黑体");
+        ff_IntegerToName.put(3, "楷体");
+        ff_IntegerToName.put(4, "仿宋");
+        ff_IntegerToName.put(5, "新宋体");
+        ff_IntegerToName.put(6, "华文新魏");
+        ff_IntegerToName.put(7, "华文行楷");
+        ff_IntegerToName.put(8, "华文隶书");
+        ff_IntegerToName.put(9, "Arial");
+        ff_IntegerToName.put(10, "Times New Roman");
+        ff_IntegerToName.put(11, "Tahoma");
+        ff_IntegerToName.put(12, "Verdana");
+
+        //0 微软雅黑、1 宋体(Song)、2 黑体(ST Heiti)、3 楷体(ST Kaiti)、 4仿宋(ST FangSong)、 5 新宋体(ST Song)、
+        // 6 华文新魏、 7华文行楷、 8 华文隶书、 9 Arial、 10 Times New Roman 、11 Tahoma 、12 Verdana
+        ff_NameToInteger.put("微软雅黑", 0);
+        ff_NameToInteger.put("宋体", 1);
+        ff_NameToInteger.put("Song", 1);
+        ff_NameToInteger.put("黑体", 2);
+        ff_NameToInteger.put("ST Heiti", 2);
+        ff_NameToInteger.put("楷体", 3);
+        ff_NameToInteger.put("ST Kaiti", 3);
+        ff_NameToInteger.put("仿宋", 4);
+        ff_NameToInteger.put("ST FangSong", 4);
+        ff_NameToInteger.put("新宋体", 5);
+        ff_NameToInteger.put("ST Song", 5);
+        ff_NameToInteger.put("华文新魏", 6);
+        ff_NameToInteger.put("华文行楷", 7);
+        ff_NameToInteger.put("华文隶书", 8);
+        ff_NameToInteger.put("Arial", 9);
+        ff_NameToInteger.put("Times New Roman", 10);
+        ff_NameToInteger.put("Tahoma", 11);
+        ff_NameToInteger.put("Verdana", 12);
+    }
+
+
+    /**
+     * 按自定义格式
+     *
+     * @param fa
+     * @return
+     */
+    public static Integer getNumberFormatMap(String fa) {
+        if (number_format_map.containsKey(fa.toLowerCase())) {
+            return number_format_map.get(fa.toLowerCase());
+        }
+        return -1;
+    }
+
+    /**
+     * 获取poi表格垂直对齐  0 中间、1 上、2下
+     *
+     * @param i
+     * @return
+     */
+    public static VerticalAlignment getVerticalType(int i) {
+        if (0 == i) {
+            return VerticalAlignment.CENTER;
+        } else if (1 == i) {
+            return VerticalAlignment.TOP;
+        } else if (2 == i) {
+            return VerticalAlignment.BOTTOM;
+        }
+        //默认居中
+        return VerticalAlignment.CENTER;
+    }
+
+    /**
+     * 获取poi表格水平对齐 0 居中、1 左、2右
+     *
+     * @param i
+     * @return
+     */
+    public static HorizontalAlignment getHorizontaltype(int i) {
+        if (2 == i) {
+            return HorizontalAlignment.RIGHT;
+        } else if (1 == i) {
+            return HorizontalAlignment.LEFT;
+        } else if (0 == i) {
+            return HorizontalAlignment.CENTER;
+        }
+        //默认右
+        return HorizontalAlignment.RIGHT;
+    }
+
+    /**
+     * 文字旋转
+     * 文字旋转角度(0=0,1=45,2=-45,3=竖排文字,4=90,5=-90)
+     *
+     * @param i
+     * @return
+     */
+    public static short getRotation(int i) {
+        short t = 0;
+        switch (i) {
+            case 1:
+                t = 45;
+                break;
+            case 2:
+                t = -45;
+                break;
+            case 3:
+                t = 255;
+                break;
+            case 4:
+                t = 90;
+                break;
+            case 5:
+                t = -90;
+                break;
+
+            default:
+                t = 0;
+        }
+        return t;
+    }
+
+
+    private static SimpleDateFormat df_DateTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+    public static Date stringToDateTime(String date) {
+        if (date == null || date.length() == 0) {
+            return null;
+        }
+        try {
+            return df_DateTime.parse(date);
+        } catch (ParseException e) {
+            return null;
+        }
+    }
+
+    private static SimpleDateFormat df_Date = new SimpleDateFormat("yyyy-MM-dd");
+
+    public static Date stringToDate(String date) {
+        if (date == null || date.length() == 0) {
+            return null;
+        }
+        try {
+            return df_Date.parse(date);
+        } catch (ParseException e) {
+            return null;
+        }
+    }
+
+    public static Date toDate(String numberString) {
+        try {
+            Double _d = Double.parseDouble(numberString);
+            String _s = toDate(_d, "yyyy-MM-dd HH:mm:ss");
+            if (numberString.indexOf(".") > -1) {
+                return stringToDate(_s);
+            } else {
+                return stringToDateTime(_s);
+            }
+
+        } catch (Exception ex) {
+            System.out.println(ex.toString() + " " + numberString);
+        }
+        return null;
+    }
+
+    private static final int SECONDS_PER_MINUTE = 60;
+    private static final int MINUTES_PER_HOUR = 60;
+    private static final int HOURS_PER_DAY = 24;
+    private static final int SECONDS_PER_DAY = (HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
+    /**
+     * 一天的毫秒数
+     **/
+    private static final long DAY_MILLISECONDS = SECONDS_PER_DAY * 1000L;
+
+    /**
+     * 转换方法
+     *
+     * @parma numberString 要转换的浮点数
+     * @parma format 要获得的格式 例如"hh:mm:ss"
+     **/
+    public static String toDate(double numberString, String format) {
+        SimpleDateFormat sdFormat = new SimpleDateFormat(format);
+        int wholeDays = (int) Math.floor(numberString);
+        int millisecondsInday = (int) ((numberString - wholeDays) * DAY_MILLISECONDS + 0.5);
+        Calendar calendar = new GregorianCalendar();
+        setCalendar(calendar, wholeDays, millisecondsInday, false);
+        return sdFormat.format(calendar.getTime());
+    }
+
+    private static void setCalendar(Calendar calendar, int wholeDays,
+                                    int millisecondsInDay, boolean use1904windowing) {
+        int startYear = 1900;
+        int dayAdjust = -1; // Excel thinks 2/29/1900 is a valid date, which it isn't
+        if (use1904windowing) {
+            startYear = 1904;
+            dayAdjust = 1; // 1904 date windowing uses 1/2/1904 as the first day
+        } else if (wholeDays < 61) {
+            // Date is prior to 3/1/1900, so adjust because Excel thinks 2/29/1900 exists
+            // If Excel date == 2/29/1900, will become 3/1/1900 in Java representation
+            dayAdjust = 0;
+        }
+        calendar.set(startYear, 0, wholeDays + dayAdjust, 0, 0, 0);
+        calendar.set(GregorianCalendar.MILLISECOND, millisecondsInDay);
+    }
+}

+ 28 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/util/MSExcelUtil.java

@@ -0,0 +1,28 @@
+package com.anjiplus.template.gaea.business.modules.reportexcel.util;
+
+public class MSExcelUtil {
+    public static final short EXCEL_COLUMN_WIDTH_FACTOR = 256;
+    public static final short EXCEL_ROW_HEIGHT_FACTOR = 20;
+    public static final int UNIT_OFFSET_LENGTH = 7;
+    public static final int[] UNIT_OFFSET_MAP = new int[]{0, 36, 73, 109, 146, 182, 219};
+
+    public static short pixel2WidthUnits(int pxs) {
+        short widthUnits = (short) (EXCEL_COLUMN_WIDTH_FACTOR * (pxs / UNIT_OFFSET_LENGTH));
+        widthUnits += UNIT_OFFSET_MAP[(pxs % UNIT_OFFSET_LENGTH)];
+        return widthUnits;
+    }
+
+    public static int widthUnits2Pixel(short widthUnits) {
+        int pixels = (widthUnits / EXCEL_COLUMN_WIDTH_FACTOR) * UNIT_OFFSET_LENGTH;
+        int offsetWidthUnits = widthUnits % EXCEL_COLUMN_WIDTH_FACTOR;
+        pixels += Math.floor((float) offsetWidthUnits / ((float) EXCEL_COLUMN_WIDTH_FACTOR / UNIT_OFFSET_LENGTH));
+        return pixels;
+    }
+
+    public static int heightUnits2Pixel(short heightUnits) {
+        int pixels = (heightUnits / EXCEL_ROW_HEIGHT_FACTOR);
+        int offsetWidthUnits = heightUnits % EXCEL_ROW_HEIGHT_FACTOR;
+        pixels += Math.floor((float) offsetWidthUnits / ((float) EXCEL_ROW_HEIGHT_FACTOR / UNIT_OFFSET_LENGTH));
+        return pixels;
+    }
+}

+ 860 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/util/XlsSheetUtil.java

@@ -0,0 +1,860 @@
+package com.anjiplus.template.gaea.business.modules.reportexcel.util;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+
+import java.util.*;
+
+/**
+ * 来自:https://github.com/mengshukeji/LuckysheetServer
+ * sheet操作
+ *
+ * @author Administrator
+ */
+@Slf4j
+public class XlsSheetUtil {
+    /**
+     * 导出sheet
+     *
+     * @param wb
+     * @param sheetNum
+     * @param dbObject
+     */
+    public static void exportSheet(Workbook wb, int sheetNum, JSONObject dbObject) {
+        Sheet sheet = wb.createSheet();
+
+        //设置sheet位置,名称
+        if (dbObject.containsKey("name") && dbObject.get("name") != null) {
+            wb.setSheetName(sheetNum, dbObject.get("name").toString());
+        } else {
+            wb.setSheetName(sheetNum, "sheet" + sheetNum);
+        }
+        //是否隐藏
+        if (dbObject.containsKey("hide") && dbObject.get("hide").toString().equals("1")) {
+            wb.setSheetHidden(sheetNum, true);
+        }
+        //是否当前选中页
+        if (dbObject.containsKey("status") && dbObject.get("status").toString().equals("1")) {
+            sheet.setSelected(true);
+        }
+
+
+        //循环数据
+        if (dbObject.containsKey("celldata") && dbObject.get("celldata") != null) {
+            //取到所有单元格集合
+            List<JSONObject> cells_json = (List<JSONObject>) dbObject.get("celldata");
+            Map<Integer, List<JSONObject>> cellMap = cellGroup(cells_json);
+            //循环每一行
+            for (Integer r : cellMap.keySet()) {
+                Row row = sheet.createRow(r);
+                //循环每一列
+                for (JSONObject col : cellMap.get(r)) {
+                    createCell(wb, sheet, row, col);
+                }
+            }
+        }
+
+        if (dbObject.containsKey("config") && dbObject.getJSONObject("config").containsKey("borderInfo")) {
+            JSONArray borderInfo = dbObject.getJSONObject("config").getJSONArray("borderInfo");
+            setCellBoard(wb, borderInfo, sheet);
+        }
+
+
+        setColumAndRow(dbObject, sheet);
+
+    }
+
+    /**
+     * 每一个单元格
+     *
+     * @param row
+     * @param dbObject
+     */
+    private static void createCell(Workbook wb, Sheet sheet, Row row, JSONObject dbObject) {
+        if (dbObject.containsKey("c")) {
+            Integer c = getStrToInt(dbObject.get("c"));
+            if (c != null) {
+                Cell cell = row.createCell(c);
+                //取单元格中的v_json
+                if (dbObject.containsKey("v")) {
+                    //获取v对象
+                    Object obj = dbObject.get("v");
+                    if (obj == null) {
+                        //没有内容
+                        return;
+                    }
+                    //如果v对象直接是字符串
+                    if (obj instanceof String) {
+                        if (((String) obj).length() > 0) {
+                            cell.setCellValue(obj.toString());
+                        }
+                        return;
+                    }
+
+                    //转换v为对象(v是一个对象)
+                    JSONObject v_json = (JSONObject) obj;
+                    //样式
+                    CellStyle style = wb.createCellStyle();
+                    cell.setCellStyle(style);
+
+
+                    //合并单元格
+                    //参数1:起始行 参数2:终止行 参数3:起始列 参数4:终止列
+                    //CellRangeAddress region1 = new CellRangeAddress(rowNumber, rowNumber, (short) 0, (short) 11);
+
+                    //mc 合并单元格
+                    if (v_json.containsKey("mc")) {
+                        //是合并的单元格
+                        JSONObject mc = v_json.getJSONObject("mc");
+                        if (mc.containsKey("rs") && mc.containsKey("cs")) {
+                            //合并的第一个单元格
+                            if (mc.containsKey("r") && mc.containsKey("c")) {
+                                Integer _rs = getIntByDBObject(mc, "rs") - 1;
+                                Integer _cs = getIntByDBObject(mc, "cs") - 1;
+                                Integer _r = getIntByDBObject(mc, "r");
+                                Integer _c = getIntByDBObject(mc, "c");
+
+                                CellRangeAddress region = new CellRangeAddress(_r.shortValue(), (_r.shortValue() + _rs.shortValue()), _c.shortValue(), (_c.shortValue() + _cs.shortValue()));
+                                sheet.addMergedRegion(region);
+                            }
+                        } else {
+                            //不是合并的第一个单元格
+                            return;
+                        }
+                    }
+
+
+                    //取v值 (在数据类型中处理)
+                    //ct 单元格值格式 (fa,t)
+                    setFormatByCt(wb, cell, style, v_json);
+
+                    //font设置
+                    setCellStyleFont(wb, style, v_json);
+
+                    //bg 背景颜色
+                    if (v_json.containsKey("bg")) {
+                        String _v = getByDBObject(v_json, "bg");
+                        Short _color = ColorUtil.getColorByStr(_v);
+                        if (_color != null) {
+                            style.setFillForegroundColor(_color);
+                            style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+                        }
+                    }
+
+                    //vt 垂直对齐    垂直对齐方式(0=居中,1=上,2=下)
+                    if (v_json.containsKey("vt")) {
+                        Integer _v = getIntByDBObject(v_json, "vt");
+                        if (_v != null && _v >= 0 && _v <= 2) {
+                            style.setVerticalAlignment(ConstantUtil.getVerticalType(_v));
+                        }
+                    } else {
+                        //默认设置居中
+                        style.setVerticalAlignment(ConstantUtil.getVerticalType(0));
+                    }
+
+                    //ht 水平对齐   水平对齐方式(0=居中,1=左对齐,2=右对齐)
+                    if (v_json.containsKey("ht")) {
+                        Integer _v = getIntByDBObject(v_json, "ht");
+                        if (_v != null && _v >= 0 && _v <= 2) {
+                            style.setAlignment(ConstantUtil.getHorizontaltype(_v));
+                        }
+                    } else {
+                        //默认设置左对齐
+                        style.setAlignment(ConstantUtil.getHorizontaltype(1));
+                    }
+
+                    //tr 文字旋转 文字旋转角度(0=0,1=45,2=-45,3=竖排文字,4=90,5=-90)
+                    if (v_json.containsKey("tr")) {
+                        Integer _v = getIntByDBObject(v_json, "tr");
+                        if (_v != null) {
+                            style.setRotation(ConstantUtil.getRotation(_v));
+                        }
+                    }
+
+                    //tb  文本换行    0 截断、1溢出、2 自动换行
+                    //   2:setTextWrapped     0和1:IsTextWrapped = true
+                    if (v_json.containsKey("tb")) {
+                        Integer _v = getIntByDBObject(v_json, "tb");
+                        if (_v != null) {
+                            if (_v >= 0 && _v <= 1) {
+                                style.setWrapText(false);
+                            } else {
+                                style.setWrapText(true);
+                            }
+                        }
+                    }
+
+                    //f  公式
+                    if (v_json.containsKey("f")) {
+                        String _v = getByDBObject(v_json, "f");
+                        if (_v.length() > 0) {
+                            try {
+                                if (_v.startsWith("=")) {
+                                    cell.setCellFormula(_v.substring(1));
+                                } else {
+                                    cell.setCellFormula(_v);
+                                }
+                            } catch (Exception ex) {
+                                log.error("公式 {};Error:{}", _v, ex.toString());
+                            }
+                        }
+                    }
+
+
+                }
+
+            }
+        }
+    }
+
+    /**
+     * 设置边框
+     *
+     * @param borderInfo
+     * @param sheet
+     */
+    private static void setCellBoard(Workbook wb, JSONArray borderInfo, Sheet sheet) {
+
+
+        //一定要通过 cell.getCellStyle()  不然的话之前设置的样式会丢失
+        //设置边框
+        for (int i = 0; i < borderInfo.size(); i++) {
+            JSONObject borderInfoObject = (JSONObject) borderInfo.get(i);
+            if (borderInfoObject.get("rangeType").equals("cell")) {//单个单元格
+                JSONObject borderValueObject = borderInfoObject.getJSONObject("value");
+
+                JSONObject l = borderValueObject.getJSONObject("l");
+                JSONObject r = borderValueObject.getJSONObject("r");
+                JSONObject t = borderValueObject.getJSONObject("t");
+                JSONObject b = borderValueObject.getJSONObject("b");
+
+
+                int row_ = borderValueObject.getInteger("row_index");
+                int col_ = borderValueObject.getInteger("col_index");
+
+                Row row = sheet.getRow(row_);
+                if (null == row) {
+                    row = sheet.createRow(row_);
+                }
+                Cell cell = row.getCell(col_);
+                CellStyle style;
+                if (null == cell) {
+                    style = wb.createCellStyle();
+                    cell = row.createCell(col_);
+                    cell.setCellStyle(style);
+                } else {
+                    style = cell.getCellStyle();
+                }
+
+
+                if (l != null) {
+                    style.setBorderLeft(BorderStyle.valueOf(l.getShort("style"))); //左边框
+                    Short color = ColorUtil.getColorByStr(l.getString("color"));
+                    if (null != color) {
+                        style.setLeftBorderColor(color);//左边框颜色
+                    }
+
+                }
+                if (r != null) {
+                    style.setBorderRight(BorderStyle.valueOf(r.getShort("style"))); //右边框
+                    Short color = ColorUtil.getColorByStr(r.getString("color"));
+                    if (null != color) {
+                        style.setRightBorderColor(color);//右边框颜色
+                    }
+
+                }
+                if (t != null) {
+                    style.setBorderTop(BorderStyle.valueOf(t.getShort("style"))); //顶部边框
+                    Short _vcolor = ColorUtil.getColorByStr(t.getString("color"));
+                    if (null != _vcolor) {
+                        style.setTopBorderColor(_vcolor);//顶部边框颜色
+                    }
+
+                }
+                if (b != null) {
+                    style.setBorderBottom(BorderStyle.valueOf(b.getShort("style"))); //底部边框
+                    Short _vcolor = ColorUtil.getColorByStr(b.getString("color"));
+                    if (_vcolor != null) {
+                        //底部边框颜色
+                        style.setBottomBorderColor(_vcolor);
+                    }
+                }
+
+            } else if (borderInfoObject.get("rangeType").equals("range")) {
+                //选区
+                Short style_ = borderInfoObject.getShort("style");
+                String borderType = borderInfoObject.getString("borderType");
+                Short color = ColorUtil.getColorByStr(borderInfoObject.getString("color"));
+                JSONObject rangObject = (JSONObject) ((JSONArray) (borderInfoObject.get("range"))).get(0);
+
+                JSONArray rowList = rangObject.getJSONArray("row");
+                JSONArray columnList = rangObject.getJSONArray("column");
+
+
+                for (int row_ = rowList.getInteger(0); row_ < rowList.getInteger(rowList.size() - 1) + 1; row_++) {
+                    for (int col_ = columnList.getInteger(0); col_ < columnList.getInteger(columnList.size() - 1) + 1; col_++) {
+                        Row row = sheet.getRow(row_);
+                        if (null == row) {
+                            row = sheet.createRow(row_);
+                        }
+                        Cell cell = row.getCell(col_);
+                        CellStyle style;
+                        if (null == cell) {
+                            style = wb.createCellStyle();
+                            cell = row.createCell(col_);
+                            cell.setCellStyle(style);
+                        } else {
+                            style = cell.getCellStyle();
+                        }
+
+                        //"border-left" | "border-right" | "border-top" | "border-bottom" | "border-all" | "border-horizontal" | "border-vertical"
+                        // "border-outside" | "border-inside" |  | "border-none"
+                        if ("border-left".equals(borderType) || "border-all".equals(borderType)) {
+                            style.setBorderLeft(BorderStyle.valueOf(style_)); //左边框
+                            style.setLeftBorderColor(color);//左边框颜色
+                        }
+                        if ("border-right".equals(borderType) || "border-all".equals(borderType)) {
+                            style.setBorderRight(BorderStyle.valueOf(style_)); //右边框
+                            style.setRightBorderColor(color);//右边框颜色
+                        }
+
+                        if ("border-top".equals(borderType) || "border-all".equals(borderType)) {
+                            style.setBorderTop(BorderStyle.valueOf(style_)); //顶部边框
+                            style.setTopBorderColor(color);//顶部边框颜色
+                        }
+
+                        if ("border-bottom".equals(borderType) || "border-all".equals(borderType)) {
+                            style.setBorderBottom(BorderStyle.valueOf(style_)); //底部边框
+                            style.setBottomBorderColor(color);//底部边框颜色 }
+                        }
+
+                        if ("border-outside".equals(borderType)) {
+                            //外圈边框
+                            if (row_ == rowList.getInteger(0)) {
+                                style.setBorderTop(BorderStyle.valueOf(style_)); //顶部边框
+                                style.setTopBorderColor(color);//顶部边框颜色
+                            }
+                            if (col_ == columnList.getInteger(0)) {
+                                style.setBorderLeft(BorderStyle.valueOf(style_)); //左边框
+                                style.setLeftBorderColor(color);//左边框颜色
+                            }
+                            if (row_ == rowList.getInteger(rowList.size() - 1)) {
+                                style.setBorderBottom(BorderStyle.valueOf(style_)); //底部边框
+                                style.setBottomBorderColor(color);//底部边框颜色 }
+                            }
+                            if (col_ == columnList.getInteger(columnList.size() - 1)) {
+                                style.setBorderRight(BorderStyle.valueOf(style_)); //右边框
+                                style.setRightBorderColor(color);//右边框颜色
+                            }
+
+                        }
+
+                        if ("border-horizontal".equals(borderType) || "border-inside".equals(borderType)) {
+                            //内部横线
+                            if (row_ >= rowList.getInteger(0)
+                                    && row_ < rowList.getInteger(rowList.size() - 1)
+                                    && col_ >= columnList.getInteger(0)
+                                    && col_ <= columnList.getInteger(columnList.size() - 1)) {
+                                style.setBorderBottom(BorderStyle.valueOf(style_)); //底部边框
+                                style.setBottomBorderColor(color);//底部边框颜色 }
+                            }
+                        }
+
+                        if ("border-vertical".equals(borderType) || "border-inside".equals(borderType)) {
+                            //内部竖线
+                            if (row_ >= rowList.getInteger(0)
+                                    && row_ <= rowList.getInteger(rowList.size() - 1)
+                                    && col_ >= columnList.getInteger(0)
+                                    && col_ < columnList.getInteger(columnList.size() - 1)) {
+                                style.setBorderRight(BorderStyle.valueOf(style_)); //右边框
+                                style.setRightBorderColor(color);//右边框颜色
+                            }
+                        }
+
+                        if ("border-none".equals(borderType)) {
+                            style.setBorderLeft(BorderStyle.NONE); //左边框
+                            style.setBorderRight(BorderStyle.NONE); //左边框
+                            style.setBorderTop(BorderStyle.NONE); //左边框
+                            style.setBorderBottom(BorderStyle.NONE); //左边框
+                        }
+
+                    }
+                }
+
+
+            }
+        }
+
+    }
+
+
+    /**
+     * 设置单元格,宽、高
+     *
+     * @param dbObject
+     * @param sheet
+     */
+    private static void setColumAndRow(JSONObject dbObject, Sheet sheet) {
+        if (dbObject.containsKey("config")) {
+            JSONObject config = dbObject.getJSONObject("config");
+
+            if (config.containsKey("columnlen")) {
+                JSONObject columnlen = config.getJSONObject("columnlen");
+                if (columnlen != null) {
+                    for (String k : columnlen.keySet()) {
+                        Integer _i = getStrToInt(k);
+                        Integer _v = getStrToInt(columnlen.get(k).toString());
+                        if (_i != null && _v != null) {
+//                            sheet.setColumnWidth(_i, MSExcelUtil.heightUnits2Pixel(_v.shortValue()));
+                            // TODO 乘以32,有待商榷
+                            sheet.setColumnWidth(_i, _v * 32);
+                        }
+                    }
+                }
+            }
+            if (config.containsKey("rowlen")) {
+                JSONObject rowlen = config.getJSONObject("rowlen");
+                if (rowlen != null) {
+                    for (String k : rowlen.keySet()) {
+                        Integer _i = getStrToInt(k);
+                        Integer _v = getStrToInt(rowlen.get(k).toString());
+                        if (_i != null && _v != null) {
+                            Row row = sheet.getRow(_i);
+                            if (row != null) {
+//                                row.setHeightInPoints(MSExcelUtil.pixel2WidthUnits(_v.shortValue()));
+                                row.setHeightInPoints(_v.shortValue());
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 单元格字体相关样式
+     *
+     * @param wb
+     * @param style
+     * @param dbObject
+     */
+    private static void setCellStyleFont(Workbook wb, CellStyle style, JSONObject dbObject) {
+        Font font = wb.createFont();
+        style.setFont(font);
+
+        //ff 字体
+        if (dbObject.containsKey("ff")) {
+            if (dbObject.get("ff") instanceof Integer) {
+                Integer _v = getIntByDBObject(dbObject, "ff");
+                if (_v != null && ConstantUtil.ff_IntegerToName.containsKey(_v)) {
+                    font.setFontName(ConstantUtil.ff_IntegerToName.get(_v));
+                }
+            } else if (dbObject.get("ff") instanceof String) {
+                font.setFontName(getByDBObject(dbObject, "ff"));
+            }
+        }
+        //fc 字体颜色
+        if (dbObject.containsKey("fc")) {
+            String _v = getByDBObject(dbObject, "fc");
+            Short _color = ColorUtil.getColorByStr(_v);
+            if (_color != null) {
+                font.setColor(_color);
+            }
+        }
+        //bl 粗体
+        if (dbObject.containsKey("bl")) {
+            Integer _v = getIntByDBObject(dbObject, "bl");
+            if (_v != null) {
+                if (_v.equals(1)) {
+                    //是否粗体显示
+                    font.setBold(true);
+                } else {
+                    font.setBold(false);
+                }
+            }
+        }
+        //it 斜体
+        if (dbObject.containsKey("it")) {
+            Integer _v = getIntByDBObject(dbObject, "it");
+            if (_v != null) {
+                if (_v.equals(1)) {
+                    font.setItalic(true);
+                } else {
+                    font.setItalic(false);
+                }
+            }
+        }
+        //fs 字体大小
+        if (dbObject.containsKey("fs")) {
+            Integer _v = getStrToInt(getObjectByDBObject(dbObject, "fs"));
+            if (_v != null) {
+                font.setFontHeightInPoints(_v.shortValue());
+            }
+        }
+        //cl 删除线 (导入没有)   0 常规 、 1 删除线
+        if (dbObject.containsKey("cl")) {
+            Integer _v = getIntByDBObject(dbObject, "cl");
+            if (_v != null) {
+                if (_v.equals(1)) {
+                    font.setStrikeout(true);
+                }
+            }
+        }
+        //ul 下划线
+        if (dbObject.containsKey("ul")) {
+            Integer _v = getIntByDBObject(dbObject, "ul");
+            if (_v != null) {
+                if (_v.equals(1)) {
+                    font.setUnderline(Font.U_SINGLE);
+                } else {
+                    font.setUnderline(Font.U_NONE);
+                }
+            }
+        }
+
+    }
+
+    /**
+     * 设置cell边框颜色样式
+     *
+     * @param style    样式
+     * @param dbObject json对象
+     * @param bs       样式
+     * @param bc       样式
+     */
+    private static void setBorderStyle(CellStyle style, JSONObject dbObject, String bs, String bc) {
+        //bs 边框样式
+        if (dbObject.containsKey(bs)) {
+            Integer _v = getStrToInt(getByDBObject(dbObject, bs));
+            if (_v != null) {
+                //边框没有,不作改变
+                if (bs.equals("bs") || bs.equals("bs_t")) {
+                    style.setBorderTop(BorderStyle.valueOf(_v.shortValue()));
+                }
+                if (bs.equals("bs") || bs.equals("bs_b")) {
+                    style.setBorderBottom(BorderStyle.valueOf(_v.shortValue()));
+                }
+                if (bs.equals("bs") || bs.equals("bs_l")) {
+                    style.setBorderLeft(BorderStyle.valueOf(_v.shortValue()));
+                }
+                if (bs.equals("bs") || bs.equals("bs_r")) {
+                    style.setBorderRight(BorderStyle.valueOf(_v.shortValue()));
+                }
+
+                //bc 边框颜色
+                String _vcolor = getByDBObject(dbObject, bc);
+                if (_vcolor != null) {
+                    Short _color = ColorUtil.getColorByStr(_vcolor);
+                    if (_color != null) {
+                        if (bc.equals("bc") || bc.equals("bc_t")) {
+                            style.setTopBorderColor(_color);
+                        }
+                        if (bc.equals("bc") || bc.equals("bc_b")) {
+                            style.setBottomBorderColor(_color);
+                        }
+                        if (bc.equals("bc") || bc.equals("bc_l")) {
+                            style.setLeftBorderColor(_color);
+                        }
+                        if (bc.equals("bc") || bc.equals("bc_r")) {
+                            style.setRightBorderColor(_color);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
+     * 设置单元格格式  ct 单元格值格式 (fa,t)
+     *
+     * @param cell
+     * @param style
+     * @param dbObject
+     */
+    private static void setFormatByCt(Workbook wb, Cell cell, CellStyle style, JSONObject dbObject) {
+
+        if (!dbObject.containsKey("v") && dbObject.containsKey("ct")) {
+            /* 处理以下数据结构
+             {
+                "celldata": [{
+                    "c": 0,
+                    "r": 8,
+                    "v": {
+                        "ct": {
+                            "s": [{
+                                "v": "sdsdgdf\r\ndfgdfg\r\ndsfgdfgdf\r\ndsfgdfg"
+                            }],
+                            "t": "inlineStr",
+                            "fa": "General"
+                        }
+                    }
+                }]
+            }
+             */
+            JSONObject ct = dbObject.getJSONObject("ct");
+            if (ct.containsKey("s")) {
+                Object s = ct.get("s");
+                if (s instanceof List && ((List) s).size() > 0) {
+                    JSONObject _s1 = (JSONObject) ((List) s).get(0);
+                    if (_s1.containsKey("v") && _s1.get("v") instanceof String) {
+                        dbObject.put("v", _s1.get("v"));
+                        style.setWrapText(true);
+                    }
+                }
+
+            }
+        }
+
+        //String v = "";  //初始化
+        if (dbObject.containsKey("v")) {
+            //v = v_json.get("v").toString();
+            //取到v后,存到poi单元格对象
+            //设置该单元格值
+            //cell.setValue(v);
+
+            //String v=getByDBObject(v_json,"v");
+            //cell.setValue(v);
+            Object obj = getObjectByDBObject(dbObject, "v");
+            if (obj instanceof Number) {
+                cell.setCellValue(Double.valueOf(obj.toString()));
+            } else if (obj instanceof Double) {
+                cell.setCellValue((Double) obj);
+            } else if (obj instanceof Date) {
+                cell.setCellValue((Date) obj);
+            } else if (obj instanceof Calendar) {
+                cell.setCellValue((Calendar) obj);
+            } else if (obj instanceof RichTextString) {
+                cell.setCellValue((RichTextString) obj);
+            } else if (obj instanceof String) {
+                cell.setCellValue((String) obj);
+            } else {
+                cell.setCellValue(obj.toString());
+            }
+
+        }
+
+        if (dbObject.containsKey("ct")) {
+            JSONObject ct = dbObject.getJSONObject("ct");
+            if (ct.containsKey("fa") && ct.containsKey("t")) {
+                //t 0=bool,1=datetime,2=error,3=null,4=numeric,5=string,6=unknown
+                String fa = getByDBObject(ct, "fa"); //单元格格式format定义串
+                String t = getByDBObject(ct, "t"); //单元格格式type类型
+
+                Integer _i = ConstantUtil.getNumberFormatMap(fa);
+                switch (t) {
+                    case "s": {
+                        //字符串
+                        if (_i >= 0) {
+                            style.setDataFormat(_i.shortValue());
+                        } else {
+                            style.setDataFormat((short) 0);
+                        }
+                        cell.setCellType(CellType.STRING);
+                        break;
+                    }
+                    case "d": {
+                        //日期
+                        Date _d = null;
+                        String v = getByDBObject(dbObject, "m");
+                        if (v.length() == 0) {
+                            v = getByDBObject(dbObject, "v");
+                        }
+                        if (v.length() > 0) {
+                            if (v.indexOf("-") > -1) {
+                                if (v.indexOf(":") > -1) {
+                                    _d = ConstantUtil.stringToDateTime(v);
+                                } else {
+                                    _d = ConstantUtil.stringToDate(v);
+                                }
+                            } else {
+                                _d = ConstantUtil.toDate(v);
+                            }
+                        }
+                        if (_d != null) {
+                            //能转换为日期
+                            cell.setCellValue(_d);
+                            DataFormat format = wb.createDataFormat();
+                            style.setDataFormat(format.getFormat(fa));
+
+                        } else {
+                            //不能转换为日期
+                            if (_i >= 0) {
+                                style.setDataFormat(_i.shortValue());
+                            } else {
+                                style.setDataFormat((short) 0);
+                            }
+                        }
+                        break;
+                    }
+                    case "b": {
+                        //逻辑
+                        cell.setCellType(CellType.BOOLEAN);
+                        if (_i >= 0) {
+                            style.setDataFormat(_i.shortValue());
+                        } else {
+                            DataFormat format = wb.createDataFormat();
+                            style.setDataFormat(format.getFormat(fa));
+                        }
+                        break;
+                    }
+                    case "n": {
+                        //数值
+//                        cell.setCellType(CellType.NUMERIC);
+                        //数字转字符串
+                        cell.setCellType(CellType.STRING);
+                        if (_i >= 0) {
+                            style.setDataFormat(_i.shortValue());
+                        } else {
+                            DataFormat format = wb.createDataFormat();
+                            style.setDataFormat(format.getFormat(fa));
+                        }
+                        break;
+                    }
+                    case "u":
+                    case "g": {
+                        //general 自动类型
+                        //cell.setCellType(CellType._NONE);
+                        if (_i >= 0) {
+                            style.setDataFormat(_i.shortValue());
+                        } else {
+                            DataFormat format = wb.createDataFormat();
+                            style.setDataFormat(format.getFormat(fa));
+                        }
+                        break;
+                    }
+                    case "e": {
+                        //错误
+                        cell.setCellType(CellType.ERROR);
+                        if (_i >= 0) {
+                            style.setDataFormat(_i.shortValue());
+                        } else {
+                            DataFormat format = wb.createDataFormat();
+                            style.setDataFormat(format.getFormat(fa));
+                        }
+                        break;
+                    }
+
+                }
+
+            }
+
+        }
+    }
+
+    /**
+     * 内容按行分组
+     *
+     * @param cells
+     * @return
+     */
+    private static Map<Integer, List<JSONObject>> cellGroup(List<JSONObject> cells) {
+        Map<Integer, List<JSONObject>> cellMap = new HashMap<>(100);
+        for (JSONObject dbObject : cells) {
+            //行号
+            if (dbObject.containsKey("r")) {
+                Integer r = getStrToInt(dbObject.get("r"));
+                if (r != null) {
+                    if (cellMap.containsKey(r)) {
+                        cellMap.get(r).add(dbObject);
+                    } else {
+                        List<JSONObject> list = new ArrayList<>(10);
+                        list.add(dbObject);
+                        cellMap.put(r, list);
+                    }
+                }
+            }
+
+        }
+        return cellMap;
+    }
+
+
+    /**
+     * 获取一个k的值
+     *
+     * @param b
+     * @param k
+     * @return
+     */
+    public static String getByDBObject(JSONObject b, String k) {
+        if (b.containsKey(k)) {
+            if (b.get(k) != null && b.get(k) instanceof String) {
+                return b.getString(k);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 获取一个k的值
+     *
+     * @param b
+     * @param k
+     * @return
+     */
+    public static Object getObjectByDBObject(JSONObject b, String k) {
+        if (b.containsKey(k)) {
+            if (b.get(k) != null) {
+                return b.get(k);
+            }
+        }
+        return "";
+    }
+
+    /**
+     * 没有/无法转换 返回null
+     *
+     * @param b
+     * @param k
+     * @return
+     */
+    public static Integer getIntByDBObject(JSONObject b, String k) {
+        if (b.containsKey(k)) {
+            if (b.get(k) != null) {
+                try {
+                    String _s = b.getString(k).replace("px", "");
+                    Double _d = Double.parseDouble(_s);
+                    return _d.intValue();
+                } catch (Exception ex) {
+                    log.error(ex.getMessage());
+                    return null;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 转int
+     *
+     * @param str
+     * @return
+     */
+    private static Integer getStrToInt(Object str) {
+        try {
+            if (str != null) {
+                return Integer.parseInt(str.toString());
+            }
+            return null;
+        } catch (Exception ex) {
+            log.error("String:{};Error:{}", str, ex.getMessage());
+            return null;
+        }
+    }
+
+    private static Short getStrToShort(Object str) {
+        try {
+            if (str != null) {
+                return Short.parseShort(str.toString());
+            }
+            return null;
+        } catch (Exception ex) {
+            log.error("String:{};Error:{}", str, ex.getMessage());
+            return null;
+        }
+    }
+}

+ 453 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportexcel/util/XlsUtil.java

@@ -0,0 +1,453 @@
+package com.anjiplus.template.gaea.business.modules.reportexcel.util;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.anjiplus.template.gaea.business.enums.ExcelCenterStyleEnum;
+import com.anjiplus.template.gaea.business.modules.reportexcel.controller.dto.GridRecordDataModel;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+import org.apache.poi.hssf.usermodel.HSSFFont;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.util.HSSFColor;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.XSSFCellStyle;
+import org.apache.poi.xssf.usermodel.XSSFColor;
+import org.apache.poi.xssf.usermodel.XSSFFont;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import javax.validation.constraints.NotNull;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.*;
+
+/**
+ * 来自:https://github.com/mengshukeji/LuckysheetServer
+ * 使用poi导出xls
+ *
+ * @author Administrator
+ */
+public class XlsUtil {
+
+    private final static String MODEL = "{\"c\":0,\"r\":0,\"v\":{\"m\":\"模板\",\"v\":\"模板\",\"bl\":1,\"ct\":{\"t\":\"g\",\"fa\":\"General\"}}}";
+    private final static String BORDER_MODEL = "{\"rangeType\":\"cell\",\"value\":{\"b\":{\"color\":\"rgb(0, 0, 0)\",\"style\":1},\"r\":{\"color\":\"rgb(0, 0, 0)\",\"style\":1},\"col_index\":5,\"t\":{\"color\":\"rgb(0, 0, 0)\",\"style\":1},\"row_index\":7,\"l\":{\"color\":\"rgb(0, 0, 0)\",\"style\":1}}}";
+    /**
+     * 默认行数
+     */
+    private static final int DEFAULT_ROW_INDEX = 84;
+    /**
+     * 默认列数
+     */
+    private static final int DEFAULT_COLUMN_INDEX = 64;
+
+    /**
+     * 输出文件流
+     *
+     * @param outputStream 流
+     * @param isXlsx       是否是xlsx
+     * @param dbObjectList 数据
+     */
+    public static void exportXlsFile(OutputStream outputStream, Boolean isXlsx, List<JSONObject> dbObjectList) throws IOException {
+        Workbook wb = null;
+        if (isXlsx) {
+            wb = new XSSFWorkbook();
+        } else {
+            wb = new HSSFWorkbook();
+        }
+        if (dbObjectList != null && dbObjectList.size() > 0) {
+            for (int x = 0; x < dbObjectList.size(); x++) {
+                XlsSheetUtil.exportSheet(wb, x, dbObjectList.get(x));
+            }
+        }
+        wb.write(outputStream);
+    }
+
+    /**
+     * @param workbook 工作簿
+     * @return Map
+     * @description 读取excel
+     * @author zhouhang
+     * @date 2021/4/20
+     */
+    public static List<GridRecordDataModel> readExcel(Workbook workbook) {
+        List<GridRecordDataModel> list = new ArrayList<>();
+        Iterator<Sheet> sheetIterator = workbook.sheetIterator();
+        int sheetIndex = 0;
+        while (sheetIterator.hasNext()) {
+            Sheet sheet = sheetIterator.next();
+            //生成默认MODEL
+            GridRecordDataModel model;
+            if (Objects.equals(0, sheetIndex)) {
+                model = strToModel("", (sheetIndex + 1) + "", 1, sheetIndex);
+            } else {
+                model = strToModel("", (sheetIndex + 1) + "", 0, sheetIndex);
+            }
+            sheetIndex++;
+            //读取sheet页
+            readSheet(sheet, model, workbook);
+            //设置sheet页名称
+            model.getJson_data().put("name", sheet.getSheetName());
+            list.add(model);
+        }
+        return list;
+    }
+
+    public static GridRecordDataModel strToModel(String list_id, String index, int status, int order) {
+        String strSheet = "{\"row\":84,\"name\":\"reSheetName\",\"chart\":[],\"color\":\"\",\"index\":\"reIndex\",\"order\":reOrder,\"column\":60,\"config\":{},\"status\":reStatus,\"celldata\":[],\"ch_width\":4748,\"rowsplit\":[],\"rh_height\":1790,\"scrollTop\":0,\"scrollLeft\":0,\"visibledatarow\":[],\"visibledatacolumn\":[],\"jfgird_select_save\":[],\"jfgrid_selection_range\":{}}";
+        strSheet = strSheet.replace("reSheetName", "Sheet" + index).replace("reIndex", index).replace("reOrder", order + "").replace("reStatus", status + "");
+
+        JSONObject bson = JSONObject.parseObject(strSheet);
+        GridRecordDataModel model = new GridRecordDataModel();
+        model.setBlock_id("fblock");
+        model.setRow_col("5_5");
+        model.setIndex(index);
+        model.setIs_delete(0);
+        model.setJson_data(bson);
+        model.setStatus(status);
+        model.setOrder(order);
+        model.setList_id(list_id);
+        return model;
+    }
+
+    /**
+     * @param sheet    sheet页
+     * @param model    数据存储
+     * @param workbook excel
+     * @description 读取单个sheet页
+     * @author zhouhang
+     * @date 2021/4/20
+     */
+    private static void readSheet(Sheet sheet, GridRecordDataModel model, Workbook workbook) {
+        //excel数据集合
+        List<JSONObject> dataList = new ArrayList<>();
+        model.setDataList(dataList);
+        //获取行迭代器
+        Iterator<Row> rowIterator = sheet.rowIterator();
+        //获取合并单元格信息
+        Map<String, String> rangeMap = getRangeMap(sheet);
+        //记录最大列
+        int maxCellNumber = 0;
+        int maxRowNumber = 0;
+        //列宽
+        JSONObject columnLenObj = new JSONObject();
+        //行高
+        JSONObject rowLenObj = new JSONObject();
+        //读取文档
+        while (rowIterator.hasNext()) {
+            Row row = rowIterator.next();
+            int rowLen = ((int) row.getHeight()) / 20;
+            if (rowLen == 0) {
+                rowLen = 30;
+            }
+            rowLenObj.put(row.getRowNum() + "", rowLen);
+            Iterator<Cell> cellIterator = row.cellIterator();
+            maxRowNumber = row.getRowNum();
+            while (cellIterator.hasNext()) {
+                //"{\"c\":0,\"r\":0,\"v\":{\"m\":\"模板\",\"v\":\"模板\",\"bl\":1,\"ct\":{\"t\":\"g\",\"fa\":\"General\"}}}";
+                JSONObject dataModel = JSONObject.parseObject(MODEL);
+                //初始化默认单元格内容
+                Cell cell = cellIterator.next();
+                int columnLen = sheet.getColumnWidth(cell.getColumnIndex()) / 25;
+                if (columnLen == 0) {
+                    columnLen = 73;
+                }
+                columnLenObj.put(cell.getColumnIndex() + "", columnLen);
+                //修改最大列
+                maxCellNumber = Math.max(cell.getColumnIndex(), maxCellNumber);
+                //设置行列
+                dataModel.put("c", cell.getColumnIndex());
+                dataModel.put("r", row.getRowNum());
+                //获取单元格内容
+                switch (cell.getCellType()) {
+                    case STRING:
+                        dataModel.getJSONObject("v").put("m", cell.getStringCellValue());
+                        dataModel.getJSONObject("v").put("v", cell.getStringCellValue());
+                        break;
+                    case NUMERIC:
+                        dataModel.getJSONObject("v").put("m", cell.getNumericCellValue());
+                        dataModel.getJSONObject("v").put("v", cell.getNumericCellValue());
+                        break;
+                    case BLANK:
+                        dataModel.getJSONObject("v").put("m", "");
+                        dataModel.getJSONObject("v").put("v", "");
+                        break;
+                    case BOOLEAN:
+                        dataModel.getJSONObject("v").put("m", cell.getBooleanCellValue());
+                        dataModel.getJSONObject("v").put("v", cell.getBooleanCellValue());
+                        break;
+                    case ERROR:
+                        dataModel.getJSONObject("v").put("m", cell.getErrorCellValue());
+                        dataModel.getJSONObject("v").put("v", cell.getErrorCellValue());
+                        break;
+                    default:
+                        dataModel.getJSONObject("v").put("m", "");
+                        dataModel.getJSONObject("v").put("v", "");
+                }
+                //设置单元格合并标记
+                dealWithCellMarge(rangeMap, row, cell, dataModel);
+                //设置单元格样式、合并单元格信息
+                dealWithExcelStyle(model, dataModel, cell, sheet, workbook);
+                dataList.add(dataModel);
+            }
+        }
+        //设置最大行、列
+        model.getJson_data().put("column", Math.max(maxCellNumber, DEFAULT_COLUMN_INDEX));
+        model.getJson_data().put("row", Math.max(maxRowNumber, DEFAULT_ROW_INDEX));
+        //设置行高、列宽
+        model.getJson_data().getJSONObject("config").put("columnlen", columnLenObj);
+        model.getJson_data().getJSONObject("config").put("rowlen", rowLenObj);
+    }
+
+    /**
+     * @param sheet sheet页信息
+     * @return Map<String, String> 单元格合并信息
+     * @description 获取合并单元格信息 所有合并单元的MAP
+     * @author zhouhang
+     * @date 2021/4/21
+     */
+    @NotNull
+    private static Map<String, String> getRangeMap(Sheet sheet) {
+        List<CellRangeAddress> rangeAddressList = sheet.getMergedRegions();
+        Map<String, String> rangeMap = new HashMap<>(rangeAddressList.size() * 5);
+        for (CellRangeAddress cellAddresses : rangeAddressList) {
+            for (int i = cellAddresses.getFirstRow(); i <= cellAddresses.getLastRow(); i++) {
+                for (int j = cellAddresses.getFirstColumn(); j <= cellAddresses.getLastColumn(); j++) {
+                    if (i == cellAddresses.getFirstRow() && j == cellAddresses.getFirstColumn()) {
+                        //单元格合并初始值特殊标记
+                        rangeMap.put(i + "_" + j, cellAddresses.getFirstRow() + "_" + cellAddresses.getFirstColumn() + "_" + cellAddresses.getLastRow() + "_" + cellAddresses.getLastColumn());
+                    } else {
+                        rangeMap.put(i + "_" + j, cellAddresses.getFirstRow() + "_" + cellAddresses.getFirstColumn());
+                    }
+                }
+            }
+        }
+        return rangeMap;
+    }
+
+    /**
+     * @param rangeMap  合并信息
+     * @param row       行信息
+     * @param cell      单元格
+     * @param dataModel 单元格数据存储信息
+     * @description 设置单元格合并标记
+     * @author zhouhang
+     * @date 2021/4/21
+     */
+    private static void dealWithCellMarge(Map<String, String> rangeMap, Row row, Cell cell, JSONObject dataModel) {
+        if (rangeMap.containsKey(row.getRowNum() + "_" + cell.getColumnIndex())) {
+            String margeValue = rangeMap.get(row.getRowNum() + "_" + cell.getColumnIndex());
+            JSONObject mcData = new JSONObject();
+            String[] s = margeValue.split("_");
+            mcData.put("r", Integer.parseInt(s[0]));
+            mcData.put("c", Integer.parseInt(s[1]));
+            if (s.length == 4) {
+                mcData.put("rs", Integer.parseInt(s[2]) - Integer.parseInt(s[0]) + 1);
+                mcData.put("cs", Integer.parseInt(s[3]) - Integer.parseInt(s[1]) + 1);
+            }
+            dataModel.getJSONObject("v").put("mc", mcData);
+        }
+    }
+
+    /**
+     * @param model     sheet页信息
+     * @param dataModel 单元格信息
+     * @param cell      单元格
+     * @param sheet     sheet页数据
+     * @param workbook  excel
+     * @description 获取单元格样式,设置单元格样式
+     * @author zhouhang
+     * @date 2021/4/21
+     */
+    private static void dealWithExcelStyle(GridRecordDataModel model, JSONObject dataModel, Cell cell, Sheet sheet, Workbook workbook) {
+        //设置单元格合并信息
+        dealWithExcelMerge(model, sheet);
+        //设置字体样式
+        setFontStyle(dataModel, workbook, cell);
+        //设置单元格样式
+        dealWithBorderStyle(model, cell, workbook);
+    }
+
+    /**
+     * @param model    在线表格存储单元
+     * @param cell     cell
+     * @param workbook workbook
+     * @description 设置单元格样式
+     * @author zhouhang
+     * @date 2021/4/22
+     */
+    private static void dealWithBorderStyle(GridRecordDataModel model, Cell cell, Workbook workbook) {
+        CellStyle cellStyle = cell.getCellStyle();
+        //判断是否存在边框
+        if (cellStyle.getBorderTop().getCode() > 0 || cellStyle.getBorderBottom().getCode() > 0 ||
+                cellStyle.getBorderLeft().getCode() > 0 || cellStyle.getBorderRight().getCode() > 0) {
+            JSONObject border = JSONObject.parseObject(BORDER_MODEL);
+            border.getJSONObject("value").put("row_index", cell.getRowIndex());
+            border.getJSONObject("value").put("col_index", cell.getColumnIndex());
+            //xlsx
+            if (cellStyle instanceof XSSFCellStyle) {
+                XSSFCellStyle xssfCellStyle = (XSSFCellStyle) cellStyle;
+                if (Objects.equals((short) 0, cellStyle.getBorderTop().getCode())) {
+                    border.getJSONObject("value").remove("t");
+                } else {
+                    border.getJSONObject("value").getJSONObject("t").put("color", dealWithRbg(xssfCellStyle.getTopBorderXSSFColor().getRGB()));
+                }
+                if (Objects.equals((short) 0, cellStyle.getBorderRight().getCode())) {
+                    border.getJSONObject("value").remove("r");
+                } else {
+                    border.getJSONObject("value").getJSONObject("r").put("color", dealWithRbg(xssfCellStyle.getRightBorderXSSFColor().getRGB()));
+                }
+                if (Objects.equals((short) 0, cellStyle.getBorderLeft().getCode())) {
+                    border.getJSONObject("value").remove("l");
+                } else {
+                    border.getJSONObject("value").getJSONObject("l").put("color", dealWithRbg(xssfCellStyle.getLeftBorderXSSFColor().getRGB()));
+                }
+                if (Objects.equals((short) 0, cellStyle.getBorderBottom().getCode())) {
+                    border.getJSONObject("value").remove("b");
+                } else {
+                    border.getJSONObject("value").getJSONObject("b").put("color", dealWithRbg(xssfCellStyle.getBottomBorderXSSFColor().getRGB()));
+                }
+            } else if (cellStyle instanceof HSSFCellStyle) {
+                //xls
+                HSSFWorkbook hssfWorkbook = (HSSFWorkbook) workbook;
+                HSSFCellStyle hssfCellStyle = (HSSFCellStyle) cellStyle;
+                if (Objects.equals((short) 0, cellStyle.getBorderTop().getCode())) {
+                    border.getJSONObject("value").remove("t");
+                } else {
+                    HSSFColor color = hssfWorkbook.getCustomPalette().getColor(hssfCellStyle.getTopBorderColor());
+                    border.getJSONObject("value").getJSONObject("t").put("color", dealWithRbgShort(color.getTriplet()));
+                }
+                if (Objects.equals((short) 0, cellStyle.getBorderRight().getCode())) {
+                    border.getJSONObject("value").remove("r");
+                } else {
+                    HSSFColor color = hssfWorkbook.getCustomPalette().getColor(hssfCellStyle.getRightBorderColor());
+                    border.getJSONObject("value").getJSONObject("r").put("color", dealWithRbgShort(color.getTriplet()));
+                }
+                if (Objects.equals((short) 0, cellStyle.getBorderLeft().getCode())) {
+                    border.getJSONObject("value").remove("l");
+                } else {
+                    HSSFColor color = hssfWorkbook.getCustomPalette().getColor(hssfCellStyle.getLeftBorderColor());
+                    border.getJSONObject("value").getJSONObject("l").put("color", dealWithRbgShort(color.getTriplet()));
+                }
+                if (Objects.equals((short) 0, cellStyle.getBorderBottom().getCode())) {
+                    border.getJSONObject("value").remove("b");
+                } else {
+                    HSSFColor color = hssfWorkbook.getCustomPalette().getColor(hssfCellStyle.getBottomBorderColor());
+                    border.getJSONObject("value").getJSONObject("b").put("color", dealWithRbgShort(color.getTriplet()));
+                }
+            }
+            JSONArray borderInfo = model.getJson_data().getJSONObject("config").getJSONArray("borderInfo");
+            if (Objects.isNull(borderInfo)) {
+                borderInfo = new JSONArray();
+                model.getJson_data().getJSONObject("config").put("borderInfo", borderInfo);
+            }
+            borderInfo.add(border);
+        }
+    }
+
+    /**
+     * @param rgb RBG short
+     * @return rbg(0, 0, 0)
+     * @description 转换RBG rbg(0,0,0)
+     * @author zhouhang
+     * @date 2021/4/26
+     */
+    private static String dealWithRbgShort(short[] rgb) {
+        return getRbg(Objects.nonNull(rgb), rgb[0], rgb[1], rgb[2]);
+    }
+
+    @NotNull
+    private static String getRbg(boolean b2, short r, short b, short g) {
+        if (b2) {
+            return "rgb(" + (r & 0xFF) + ", " + (b & 0xFF) + ", " + (g & 0xFF) + ")";
+        } else {
+            return "rgb(0, 0, 0)";
+        }
+    }
+
+    /**
+     * @param rgb RBG byte
+     * @return rbg(0, 0, 0)
+     * @description 转换RBG rbg(0,0,0)
+     * @author zhouhang
+     * @date 2021/4/26
+     */
+    private static String dealWithRbg(byte[] rgb) {
+        if (Objects.isNull(rgb)) {
+            return "rgb(0, 0, 0)";
+        }
+        short[] shorts = new short[]{rgb[0], rgb[1], rgb[2]};
+        return getRbg(true, shorts[0], shorts[1], shorts[2]);
+    }
+
+    /**
+     * @param dataModel 单元格内容
+     * @param workbook  workbook
+     * @param cell      cell
+     * @description s设置字体样式
+     * @author zhouhang
+     * @date 2021/4/21
+     */
+    private static void setFontStyle(JSONObject dataModel, Workbook workbook, Cell cell) {
+        CellStyle cellStyle = cell.getCellStyle();
+        Font font = workbook.getFontAt(cellStyle.getFontIndexAsInt());
+        JSONObject v = dataModel.getJSONObject("v");
+        //ht 水平对齐   水平对齐方式(0=居中,1=左对齐,2=右对齐)   excel:左:1  中:2  右:3 未设置:0
+        v.put("ht", ExcelCenterStyleEnum.getExcelCenterStyleByExcelCenterCode(cellStyle.getAlignment().getCode()).getOnlineExcelCode());
+        //bl 字体加粗设置
+        v.put("bl", font.getBold() ? 1 : 0);
+        //lt 斜体
+        v.put("it", font.getItalic() ? 1 : 0);
+        //ff 字体
+        v.put("ff", font.getFontName());
+        //fc 字体颜色
+        if (font instanceof HSSFFont) {
+            HSSFFont hssfFont = (HSSFFont) font;
+            HSSFColor hssfColor = hssfFont.getHSSFColor((HSSFWorkbook) workbook);
+            if (Objects.nonNull(hssfColor)) {
+                v.put("fc", ColorUtil.convertRGBToHex(hssfColor.getTriplet()[0], hssfColor.getTriplet()[1], hssfColor.getTriplet()[2]));
+            }
+        } else {
+            XSSFFont xssfFont = (XSSFFont) font;
+            XSSFColor xssfColor = xssfFont.getXSSFColor();
+            if (Objects.nonNull(xssfColor)) {
+                v.put("fc", "#" + xssfColor.getARGBHex().substring(2));
+            }
+        }
+        //fs 字体大小
+        v.put("fs", font.getFontHeightInPoints());
+        //cl 删除线
+        v.put("cl", font.getStrikeout() ? 1 : 0);
+        //ul 下划线
+        v.put("un", font.getUnderline());
+        //背景色
+        String fillColorHex = ColorUtil.getFillColorHex(cell);
+        if (Objects.nonNull(fillColorHex)) {
+            v.put("bg", fillColorHex);
+        }
+    }
+
+    /**
+     * @param model sheet页信息
+     * @param sheet sheet页
+     * @description 设置单元格合并信息
+     * @author zhouhang
+     * @date 2021/4/21
+     */
+    private static void dealWithExcelMerge(GridRecordDataModel model, Sheet sheet) {
+        if (CollectionUtils.isNotEmpty(sheet.getMergedRegions())) {
+            //{"color":"","list_id":"","column":60,"index":"1","jfgird_select_save":[],"rh_height":1790,"visibledatacolumn":[],"scrollTop":0,"block_id":"fblock","rowsplit":[],"visibledatarow":[],"jfgrid_selection_range":{},"name":"Sheet1","celldata":[],"ch_width":4748,"row":84,"scrollLeft":0,"id":364598,"chart":[],"config":{},"order":0,"status":1}
+            JSONObject jsonObject = model.getJson_data();
+            JSONObject config = jsonObject.getJSONObject("config");
+            JSONObject merge = new JSONObject();
+            for (CellRangeAddress mergedRegion : sheet.getMergedRegions()) {
+                JSONObject mergeBase = new JSONObject();
+                mergeBase.put("r", mergedRegion.getFirstRow());
+                mergeBase.put("c", mergedRegion.getFirstColumn());
+                mergeBase.put("rs", mergedRegion.getLastRow() - mergedRegion.getFirstRow() + 1);
+                mergeBase.put("cs", mergedRegion.getLastColumn() - mergedRegion.getFirstColumn() + 1);
+                merge.put(mergedRegion.getFirstRow() + "_" + mergedRegion.getFirstColumn(), mergeBase);
+            }
+            config.put("merge", merge);
+        }
+    }
+}

+ 5 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportshare/controller/dto/ReportShareDto.java

@@ -53,4 +53,9 @@ public class ReportShareDto extends GaeaBaseDTO implements Serializable {
     @ApiModelProperty(value = "0--未删除 1--已删除 DIC_NAME=DELETE_FLAG")
     private Integer deleteFlag;
 
+    /** 分享码 */
+    private String sharePassword;
+
+    private boolean sharePasswordFlag = false;
+
 }

+ 11 - 4
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportshare/dao/entity/ReportShare.java

@@ -1,18 +1,18 @@
 
 package com.anjiplus.template.gaea.business.modules.reportshare.dao.entity;
 
-import lombok.Data;
-import io.swagger.annotations.ApiModelProperty;
 import com.anji.plus.gaea.curd.entity.GaeaBaseEntity;
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableName;
-import javax.validation.constraints.*;
+import lombok.Data;
+
 import java.util.Date;
 /**
 * @description 报表分享 entity
 * @author Raod
 * @date 2021-08-18 13:37:26.663
 **/
-@TableName(value="gaea_report_share")
+@TableName(keepGlobalPrefix=true, value="gaea_report_share")
 @Data
 public class ReportShare extends GaeaBaseEntity {
     /** 分享编码,系统生成,默认UUID */
@@ -39,5 +39,12 @@ public class ReportShare extends GaeaBaseEntity {
     /** 0--未删除 1--已删除 DIC_NAME=DELETE_FLAG */
     private Integer deleteFlag;
 
+    /** 分享码 */
+    @TableField(exist = false)
+    private String sharePassword;
+
+    @TableField(exist = false)
+    private boolean sharePasswordFlag;
+
 
 }

+ 16 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/reportshare/service/impl/ReportShareServiceImpl.java

@@ -13,9 +13,11 @@ import com.anjiplus.template.gaea.business.modules.reportshare.dao.entity.Report
 import com.anjiplus.template.gaea.business.modules.reportshare.service.ReportShareService;
 import com.anjiplus.template.gaea.business.util.DateUtil;
 import com.anjiplus.template.gaea.business.util.JwtUtil;
+import com.anjiplus.template.gaea.business.util.MD5Util;
 import com.anjiplus.template.gaea.business.util.UuidUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -51,12 +53,18 @@ public class ReportShareServiceImpl implements ReportShareService {
 
     @Override
     public ReportShareDto insertShare(ReportShareDto dto) {
+        //设置分享码
+        if (dto.isSharePasswordFlag()) {
+            dto.setSharePassword(UuidUtil.getRandomPwd(4));
+        }
+
         ReportShareDto reportShareDto = new ReportShareDto();
         ReportShare entity = new ReportShare();
         BeanUtils.copyProperties(dto, entity);
         insert(entity);
         //将分享链接返回
         reportShareDto.setShareUrl(entity.getShareUrl());
+        reportShareDto.setSharePassword(dto.getSharePassword());
         return reportShareDto;
     }
 
@@ -69,6 +77,12 @@ public class ReportShareServiceImpl implements ReportShareService {
         if (null == reportShare) {
             throw BusinessExceptionBuilder.build(ResponseCode.REPORT_SHARE_LINK_INVALID);
         }
+        //解析jwt token,获取密码
+        String password = JwtUtil.getPassword(reportShare.getShareToken());
+        if (StringUtils.isNotBlank(password)) {
+            //md5加密返回
+            reportShare.setSharePassword(MD5Util.encrypt(password));
+        }
         return reportShare;
     }
 
@@ -101,7 +115,8 @@ public class ReportShareServiceImpl implements ReportShareService {
         } else {
             entity.setShareUrl(entity.getShareUrl() + SHARE_FLAG + shareCode);
         }
+
         entity.setShareValidTime(DateUtil.getFutureDateTmdHms(entity.getShareValidType()));
-        entity.setShareToken(JwtUtil.createToken(entity.getReportCode(), shareCode, entity.getShareValidTime()));
+        entity.setShareToken(JwtUtil.createToken(entity.getReportCode(), shareCode, entity.getSharePassword(), entity.getShareValidTime()));
     }
 }

+ 17 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/util/JwtUtil.java

@@ -7,6 +7,7 @@ import com.auth0.jwt.JWTVerifier;
 import com.auth0.jwt.algorithms.Algorithm;
 import com.auth0.jwt.interfaces.Claim;
 import com.auth0.jwt.interfaces.DecodedJWT;
+import org.apache.commons.lang3.StringUtils;
 
 import java.util.Date;
 import java.util.Map;
@@ -19,11 +20,16 @@ public class JwtUtil {
     private static final String JWT_SECRET = "aj-report";
 
     public static String createToken(String reportCode, String shareCode, Date expires) {
+        return createToken(reportCode, shareCode, null, expires);
+    }
+
+    public static String createToken(String reportCode, String shareCode, String password, Date expires) {
         String token = JWT.create()
                 .withIssuedAt(new Date())
                 .withExpiresAt(expires)
                 .withClaim("reportCode", reportCode)
                 .withClaim("shareCode", shareCode)
+                .withClaim("sharePassword", password)
                 .sign(Algorithm.HMAC256(JWT_SECRET));
         return token;
     }
@@ -55,4 +61,15 @@ public class JwtUtil {
         return claim.asString();
     }
 
+    public static String getPassword(String token) {
+        Claim claim = getClaim(token).get("sharePassword");
+        if (null == claim) {
+            return null;
+        }
+        if (StringUtils.isNotBlank(claim.asString())) {
+            return claim.asString();
+        }
+        return null;
+    }
+
 }

+ 22 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/util/UuidUtil.java

@@ -1,5 +1,6 @@
 package com.anjiplus.template.gaea.business.util;
 
+import java.security.SecureRandom;
 import java.util.UUID;
 
 /**
@@ -31,6 +32,24 @@ public class UuidUtil {
 
     }
 
+    /**
+     * 获取随机小写密码
+     * @param num
+     * @return
+     */
+    public static String getRandomPwd(int num) {
+        StringBuilder builder = new StringBuilder();
+        // 因为已经把 4 种字符放进list了,所以 i 取值从 4开始
+        // 产生随机数用于随机调用生成字符的函数
+        for (int i = 0; i < num; i++) {
+            SecureRandom random = new SecureRandom();
+            int funNum = random.nextInt(chars.length);
+            builder.append(chars[funNum]);
+        }
+
+        return builder.toString().toLowerCase();
+    }
+
 
     public static String generateUuid() {
         return UUID.randomUUID().toString().replace("-", "");
@@ -38,7 +57,9 @@ public class UuidUtil {
 
     public static void main(String[] args) {
         for (int i = 0; i < 100; i++) {
-            System.out.println(generateShortUuid());
+//            System.out.println(generateShortUuid());
+            System.out.println(getRandomPwd(4));
         }
+
     }
 }

+ 2 - 1
report-core/src/main/resources/bootstrap.yml

@@ -37,6 +37,7 @@ spring:
     breakAfterAcquireFailure: true # 数据库服务宕机自动重连机制
     timeBetweenConnectErrorMillis: 300000 # 连接出错后重试时间间隔
   flyway:
+    enabled: true    #是否开启flyway,默认true.
     baseline-on-migrate: true
     #数据库连接配置
     url: ${spring.datasource.url}
@@ -48,7 +49,7 @@ spring:
 
 mybatis-plus:
   configuration:
-    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启sql打印
     call-setters-on-nulls: true
   mapperLocations:
     - classpath*:/mapper/**/*.xml

+ 33 - 0
report-core/src/main/resources/db/migration/V1.0.12__create_excel.sql

@@ -0,0 +1,33 @@
+use
+aj_report;
+
+
+CREATE TABLE `gaea_report_excel`
+(
+    `id`          bigint(11) NOT NULL AUTO_INCREMENT,
+    `report_code` varchar(100)  DEFAULT NULL COMMENT '报表编码',
+    `set_codes`   varchar(255)  DEFAULT NULL COMMENT '数据集编码,以|分割',
+    `set_param`   varchar(1024) DEFAULT NULL COMMENT '数据集查询参数',
+    `json_str`    text COMMENT '报表json串',
+    `enable_flag` int(1) DEFAULT '1' COMMENT '0--已禁用 1--已启用  DIC_NAME=ENABLE_FLAG',
+    `delete_flag` int(1) DEFAULT '0' COMMENT '0--未删除 1--已删除 DIC_NAME=DELETE_FLAG',
+    `create_by`   varchar(255)  DEFAULT NULL COMMENT '创建人',
+    `create_time` datetime      DEFAULT NULL COMMENT '创建时间',
+    `update_by`   varchar(255)  DEFAULT NULL COMMENT '更新人',
+    `update_time` datetime      DEFAULT NULL COMMENT '更新时间',
+    `version`     int(8) DEFAULT NULL COMMENT '版本号',
+    PRIMARY KEY (`id`) USING BTREE,
+    UNIQUE KEY `UNIQUE_REPORT_CODE` (`report_code`) USING BTREE
+) ENGINE=InnoDB AUTO_INCREMENT=215 DEFAULT CHARSET=utf8;
+
+
+INSERT INTO `aj_report`.`access_authority`(`id`, `parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES (234, 'report', 'bigScreenManage', '大屏报表', 'export', '导出大屏', 234, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1);
+INSERT INTO `aj_report`.`access_authority`(`id`, `parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES (235, 'report', 'bigScreenManage', '大屏报表', 'import', '导入大屏', 235, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1);
+INSERT INTO `aj_report`.`access_authority`(`id`, `parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES (236, 'report', 'excelManage', '表格报表', 'query', '查询报表', 236, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1);
+
+INSERT INTO `aj_report`.`access_role_authority`(`id`, `role_code`, `target`, `action`) VALUES (default , 'root', 'bigScreenManage', 'export');
+INSERT INTO `aj_report`.`access_role_authority`(`id`, `role_code`, `target`, `action`) VALUES (default , 'root', 'bigScreenManage', 'import');
+
+
+UPDATE `aj_report`.`gaea_report` SET `report_type` = 'report_screen' WHERE `report_code` in ('log_ajreport', 'car_ajreport', 'acc_ajreport');
+

+ 22 - 0
report-core/src/main/resources/mapper/ReportExcelMapper.xml

@@ -0,0 +1,22 @@
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.anjiplus.template.gaea.business.modules.data.reportexcel.dao.ReportExcelMapper">
+
+    <resultMap type="com.anjiplus.template.gaea.business.modules.reportexcel.dao.entity.ReportExcel" id="ReportMap">
+        <!--jdbcType="{column.columnType}"-->
+        <result property="id" column="id"  />
+        <result property="reportCode" column="report_code"  />
+        <result property="setCodes" column="set_codes"  />
+        <result property="setParam" column="set_param"  />
+        <result property="jsonStr" column="json_str"  />
+        <result property="enableFlag" column="enable_flag"  />
+        <result property="deleteFlag" column="delete_flag"  />
+        <result property="createBy" column="create_by"  />
+        <result property="createTime" column="create_time"  />
+        <result property="updateBy" column="update_by"  />
+        <result property="updateTime" column="update_time"  />
+        <result property="version" column="version"  />
+
+    </resultMap>
+
+
+</mapper>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 22 - 0
report-core/src/test/java/com/anjiplus/template/gaea/business/modules/reportexcel/util/XlsSheetUtilTest.java


+ 1 - 1
report-ui/config/dev.env.js

@@ -5,5 +5,5 @@ const prodEnv = require('./prod.env')
 module.exports = merge(prodEnv, {
   NODE_ENV: '"development"',
   BASE_API: '"http://127.0.0.1:9095"'
-   //BASE_API: '"http://10.108.26.197:9095"'
+  // BASE_API: '"http://10.108.26.197:9095"'
 })

+ 16 - 0
report-ui/index.html

@@ -5,6 +5,22 @@
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width,initial-scale=1.0">
   <title>AJ-Report</title>
+  <link rel='stylesheet' href='./static/luckysheet/plugins/css/pluginsCss.css' />
+  <link rel='stylesheet' href='./static/luckysheet/plugins/plugins.css' />
+  <link rel='stylesheet' href='./static/luckysheet/css/luckysheet.css' />
+  <!-- <link rel='stylesheet' href='./static/luckysheet/assets/iconfont/iconfont.css' /> -->
+  <script src="./static/luckysheet/plugins/js/plugin.js"></script>
+  <script src="./static/luckysheet/luckysheet.umd.js"></script>
+
+<!--  <link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet/dist/plugins/css/pluginsCss.css' />-->
+<!--  <link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet/dist/plugins/plugins.css' />-->
+<!--  <link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet/dist/css/luckysheet.css' />-->
+<!--  <link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet/dist/assets/iconfont/iconfont.css' />-->
+<!--  <script src="https://cdn.jsdelivr.net/npm/luckysheet/dist/plugins/js/plugin.js"></script>-->
+<!--  <script src="https://cdn.jsdelivr.net/npm/luckysheet/dist/luckysheet.umd.js"></script>-->
+
+
+
 </head>
 
 <body>

+ 2 - 0
report-ui/package.json

@@ -22,11 +22,13 @@
     "echarts-gl": "^1.1.1",
     "element-ui": "^2.9.2",
     "js-cookie": "2.2.0",
+    "jsbarcode": "^3.11.4",
     "miment": "^0.0.9",
     "moment": "^2.29.1",
     "monaco-editor": "^0.20.0",
     "normalize.css": "7.0.0",
     "nprogress": "0.2.0",
+    "qrcodejs2": "0.0.2",
     "sortablejs": "^1.10.2",
     "uninstall": "0.0.0",
     "v-chart": "^1.0.0",

+ 3 - 4
report-ui/src/assets/iconfont/iconfont.css

@@ -1,8 +1,8 @@
 @font-face {
   font-family: "iconfont"; /* Project id 1513211 */
-  src: url('iconfont.woff2?t=1629797734566') format('woff2'),
-       url('iconfont.woff?t=1629797734566') format('woff'),
-       url('iconfont.ttf?t=1629797734566') format('truetype');
+  src: url("iconfont.woff2?t=1629797734566") format("woff2"),
+    url("iconfont.woff?t=1629797734566") format("woff"),
+    url("iconfont.ttf?t=1629797734566") format("truetype");
 }
 
 .iconfont {
@@ -488,4 +488,3 @@
 .iconjiantou-copy-copy:before {
   content: "\e654";
 }
-

+ 393 - 167
report-ui/src/components/AnjiPlus/anji-crud/anji-crud.vue

@@ -1,34 +1,111 @@
 <template>
   <div :class="[hasTreeFieldInQueryForm ? 'page-container' : 'app-container']">
     <div v-if="hasTreeFieldInQueryForm" class="left-container">
-      <AnjiTree ref="queryFormTree" v-model.trim="queryParams[queryFormTreeField.field]" :is-open="queryFormTreeField.anjiTreeOption.isOpen" :enable-filter="queryFormTreeField.anjiTreeOption.enableFilter" :label-name="queryFormTreeField.label" :url="queryFormTreeField.anjiTreeOption.url" @node-click="handleTreeNodeCheck" />
+      <AnjiTree
+        ref="queryFormTree"
+        v-model.trim="queryParams[queryFormTreeField.field]"
+        :is-open="queryFormTreeField.anjiTreeOption.isOpen"
+        :enable-filter="queryFormTreeField.anjiTreeOption.enableFilter"
+        :label-name="queryFormTreeField.label"
+        :url="queryFormTreeField.anjiTreeOption.url"
+        @node-click="handleTreeNodeCheck"
+      />
     </div>
     <div class="right-container">
       <!-- 查询表单开始 -->
-      <el-form ref="formSearch" :model="queryParams" label-width="100px" v-permission="option.buttons.query.permission">
+      <el-form
+        ref="formSearch"
+        :model="queryParams"
+        label-width="100px"
+        v-permission="option.buttons.query.permission"
+      >
         <el-row>
-          <el-col v-for="(item, index) in queryFormFieldExcludeTree" :key="item.field" :span="queryFormFieldSpan(item)">
-            <el-form-item v-if="index <= 2 || (index > 2 && queryParams.showMoreSearch)" :label="item.label" :rules="item.rules" :prop="item.field">
+          <el-col
+            v-for="(item, index) in queryFormFieldExcludeTree"
+            :key="item.field"
+            :span="queryFormFieldSpan(item)"
+          >
+            <el-form-item
+              v-if="index <= 2 || (index > 2 && queryParams.showMoreSearch)"
+              :label="item.label"
+              :rules="item.rules"
+              :prop="item.field"
+            >
               <!-- 输入框 -->
-              <el-input v-if="item.inputType == 'input' || item.inputType == 'input-number'" v-model.trim="queryParams[item.field]" :placeholder="item.placeholder || '请输入'" :clearable="item.clearable !== false" :disabled="item.disabled" @change="(value) => queryFormChange(item.field, value)" />
+              <el-input
+                v-if="
+                  item.inputType == 'input' || item.inputType == 'input-number'
+                "
+                v-model.trim="queryParams[item.field]"
+                :placeholder="item.placeholder || '请输入'"
+                :clearable="item.clearable !== false"
+                :disabled="item.disabled"
+                @change="value => queryFormChange(item.field, value)"
+              />
               <!-- 开关 -->
-              <el-switch v-else-if="item.inputType == 'switch'" v-model.trim="queryParams[item.field]" :disabled="item.disabled" :active-value="item.switchOption.disableValue" :inactive-value="item.switchOption.enableValue" active-color="#5887fb" inactive-color="#ccc" @change="(value) => queryFormChange(item.field, value)" />
+              <el-switch
+                v-else-if="item.inputType == 'switch'"
+                v-model.trim="queryParams[item.field]"
+                :disabled="item.disabled"
+                :active-value="item.switchOption.disableValue"
+                :inactive-value="item.switchOption.enableValue"
+                active-color="#5887fb"
+                inactive-color="#ccc"
+                @change="value => queryFormChange(item.field, value)"
+              />
               <!-- 下拉框 -->
-              <anji-select v-else-if="item.inputType == 'anji-select'" v-model.trim="queryParams[item.field]" :multiple="item.anjiSelectOption.multiple" :dict-code="item.anjiSelectOption.dictCode" :url="item.anjiSelectOption.url" :method="item.anjiSelectOption.method" :query-param="item.anjiSelectOption.queryParam" :option="item.anjiSelectOption.option" :label="item.anjiSelectOption.label" :disabled-options="item.anjiSelectOption.disabledOptions" :disabled="item.disabled"
-                :merge-label="item.anjiSelectOption.mergeLabel" @change="(value) => queryFormChange(item.field, value)" />
+              <anji-select
+                v-else-if="item.inputType == 'anji-select'"
+                v-model.trim="queryParams[item.field]"
+                :multiple="item.anjiSelectOption.multiple"
+                :dict-code="item.anjiSelectOption.dictCode"
+                :url="item.anjiSelectOption.url"
+                :method="item.anjiSelectOption.method"
+                :query-param="item.anjiSelectOption.queryParam"
+                :option="item.anjiSelectOption.option"
+                :label="item.anjiSelectOption.label"
+                :disabled-options="item.anjiSelectOption.disabledOptions"
+                :disabled="item.disabled"
+                :merge-label="item.anjiSelectOption.mergeLabel"
+                @change="value => queryFormChange(item.field, value)"
+              />
               <!-- 日期时间框  -->
-              <el-date-picker v-else-if="item.inputType.indexOf('date') >= 0" v-model="queryParams[item.field]" style="width: 100%" :placeholder="item.placeholder || '请选择'" :type="item.inputType" :clearable="item.clearable !== false" @change="(value) => queryFormChange(item.field, value)" />
+              <el-date-picker
+                v-else-if="item.inputType.indexOf('date') >= 0"
+                v-model="queryParams[item.field]"
+                style="width: 100%"
+                :placeholder="item.placeholder || '请选择'"
+                :type="item.inputType"
+                :clearable="item.clearable !== false"
+                @change="value => queryFormChange(item.field, value)"
+              />
               <!-- 待扩展的表单类型,请自行扩展 -->
-              <el-input v-else placeholder="组件不支持此类型表单请至组件内部自行扩展" disabled />
+              <el-input
+                v-else
+                placeholder="组件不支持此类型表单请至组件内部自行扩展"
+                disabled
+              />
             </el-form-item>
           </el-col>
 
           <el-col :span="6" style="text-align: center">
-            <el-button type="primary" @click="handleQueryForm('query')">查询</el-button>
+            <el-button type="primary" @click="handleQueryForm('query')"
+              >查询</el-button
+            >
             <el-button type="danger" @click="handleResetForm()">重置</el-button>
-            <a style="margin-left: 8px" @click="handleToggleMoreSearch">
-              {{ queryParams.showMoreSearch == true ? '收起' : '展开' }}
-              <i :class="queryParams.showMoreSearch ? 'el-icon-arrow-up' : 'el-icon-arrow-down'" />
+            <a
+              v-if="queryFormFieldExcludeTree.length > 3"
+              style="margin-left: 8px"
+              @click="handleToggleMoreSearch"
+            >
+              {{ queryParams.showMoreSearch == true ? "收起" : "展开" }}
+              <i
+                :class="
+                  queryParams.showMoreSearch
+                    ? 'el-icon-arrow-up'
+                    : 'el-icon-arrow-down'
+                "
+              />
             </a>
           </el-col>
         </el-row>
@@ -37,17 +114,50 @@
 
       <!-- 批量操作 -->
       <slot name="buttonLeftOnTable" />
-      <el-button v-if="option.buttons.add.isShow == undefined ? true : option.buttons.add.isShow" v-permission="option.buttons.add.permission" type="primary" icon="el-icon-plus" @click="handleOpenEditView('add')">新增</el-button>
-      <el-button v-if="option.buttons.delete.isShow == undefined ? true : option.buttons.delete.isShow" v-permission="option.buttons.delete.permission" :disabled="disableBatchDelete" type="danger" icon="el-icon-delete" @click="handleDeleteBatch()">删除</el-button>
+      <el-button
+        v-if="
+          option.buttons.add.isShow == undefined
+            ? true
+            : option.buttons.add.isShow
+        "
+        v-permission="option.buttons.add.permission"
+        type="primary"
+        icon="el-icon-plus"
+        @click="handleOpenEditView('add')"
+        >新增</el-button
+      >
+      <el-button
+        v-if="
+          option.buttons.delete.isShow == undefined
+            ? true
+            : option.buttons.delete.isShow
+        "
+        v-permission="option.buttons.delete.permission"
+        :disabled="disableBatchDelete"
+        type="danger"
+        icon="el-icon-delete"
+        @click="handleDeleteBatch()"
+        >删除</el-button
+      >
 
       <!-- 表格开始 -->
-      <el-table :data="records" border @selection-change="handleSelectionChange" @sort-change="handleSortChange">
+      <el-table
+        class="anji_curd_table"
+        :data="records"
+        border
+        @selection-change="handleSelectionChange"
+        @sort-change="handleSortChange"
+      >
         <!--多选-->
         <el-table-column fixed type="selection" width="50" align="center" />
         <!--隐藏列-->
         <el-table-column v-if="tableExpandColumns.length > 0" type="expand">
           <template slot-scope="scope">
-            <p v-for="item in tableExpandColumns" :key="item.field" class="table-expand-item">
+            <p
+              v-for="item in tableExpandColumns"
+              :key="item.field"
+              class="table-expand-item"
+            >
               <span class="titel"> {{ item.label }}: </span>
               <span>{{ scope.row[item.field] }}</span>
             </p>
@@ -56,28 +166,65 @@
         <!--序号-->
         <el-table-column label="序号" min-width="50" align="center">
           <template slot-scope="scope">
-            {{ queryParams.pageSize * (queryParams.pageNumber - 1) + scope.$index + 1 }}
+            {{
+              queryParams.pageSize * (queryParams.pageNumber - 1) +
+                scope.$index +
+                1
+            }}
           </template>
         </el-table-column>
 
         <template v-for="item in option.columns">
-          <el-table-column v-if="item.tableHide != true && item.columnType != 'expand'" :key="item.field" :prop="item.field" :label="fieldLabel(item)" :min-width="item.minWidth || 110" :sortable="item.sortable" :show-overflow-tooltip="true" align="center">
+          <el-table-column
+            v-if="item.tableHide != true && item.columnType != 'expand'"
+            :key="item.field"
+            :prop="item.field"
+            :label="fieldLabel(item)"
+            :min-width="item.minWidth || 110"
+            :sortable="item.sortable"
+            :show-overflow-tooltip="true"
+            align="center"
+          >
             <template slot-scope="scope">
               <div v-if="item.columnType == 'imgPreview'">
                 <!-- 图片缩略图-->
-                <el-image style="width: 25%; height: 50%" fit="contain" :src="scope.row[item.field]" :preview-src-list="[scope.row[item.field]]" />
+                <el-image
+                  style="width: 25%; height: 50%"
+                  fit="contain"
+                  :src="scope.row[item.field]"
+                  :preview-src-list="[scope.row[item.field]]"
+                />
               </div>
               <div v-else>
                 <span v-if="item.inputType == 'switch' && !item.colorStyle">
-                  <el-switch v-model.trim="scope.row[item.field]" :active-value="1" :inactive-value="0" active-color="#5887fb" inactive-color="#ccc" @change="switchChange(scope.row, item.switchOption)" />
+                  <el-switch
+                    v-model.trim="scope.row[item.field]"
+                    :active-value="1"
+                    :inactive-value="0"
+                    active-color="#5887fb"
+                    inactive-color="#ccc"
+                    @change="switchChange(scope.row, item.switchOption)"
+                  />
                 </span>
                 <!-- 带单位 -->
-                <span v-else-if="item.inputType == 'anji-input'">{{ fieldValueByAnjiInput(scope.row[item.field], item) }}</span>
+                <span v-else-if="item.inputType == 'anji-input'">{{
+                  fieldValueByAnjiInput(scope.row[item.field], item)
+                }}</span>
                 <!--表格 a 合并 b上-->
-                <span v-else-if="item.mergeColumn">{{ scope.row[item.field] }}({{ scope.row[item.mergeColumn] }})</span>
+                <span v-else-if="item.mergeColumn"
+                  >{{ scope.row[item.field] }}({{
+                    scope.row[item.mergeColumn]
+                  }})</span
+                >
                 <!-- 没有单位 -->
-                <span v-else-if="item.colorStyle" :class="item.colorStyle[scope.row[item.editField]]">{{ fieldValueByRowRenderer(scope.row, item) }}</span>
-                <span v-else>{{ fieldValueByRowRenderer(scope.row, item) }}</span>
+                <span
+                  v-else-if="item.colorStyle"
+                  :class="item.colorStyle[scope.row[item.editField]]"
+                  >{{ fieldValueByRowRenderer(scope.row, item) }}</span
+                >
+                <span v-else>{{
+                  fieldValueByRowRenderer(scope.row, item)
+                }}</span>
                 <!-- 正常展示模式
                 <div v-if="!item.custom">
                   是第一列数据 && 需要高亮字段不为false 高亮并且可以点击
@@ -92,18 +239,76 @@
           </el-table-column>
         </template>
         <!--操作栏-->
-        <el-table-column align="center" fixed="right" label="操作" :width="option.buttons.customButton && option.buttons.customButton.operationWidth ? option.buttons.customButton.operationWidth : 100">
+        <el-table-column
+          align="center"
+          fixed="right"
+          label="操作"
+          :width="
+            option.buttons.customButton &&
+            option.buttons.customButton.operationWidth
+              ? option.buttons.customButton.operationWidth
+              : 100
+          "
+        >
           <template slot-scope="scope">
             <slot name="edit" :msg="scope.row" />
-            <el-button v-if="(option.buttons.query.isShow == undefined ? true : option.buttons.query.isShow) && hasPermission(option.buttons.edit.permission) == false" type="text" size="small" @click="handleOpenEditView('view', scope.row)" v-permission="option.buttons.query.permission" >查看</el-button>
-            <el-button v-if="option.buttons.edit.isShow == undefined ? true : option.buttons.edit.isShow" type="text" size="small" @click="handleOpenEditView('edit', scope.row)" v-permission="option.buttons.edit.permission" >编辑</el-button>
-            <el-button v-if="hasRowCustomButton == false && option.buttons.delete.isShow == undefined ? true : option.buttons.edit.isShow" type="text" size="small" @click="handleDeleteBatch(scope.row)" v-permission="option.buttons.delete.permission" >删除</el-button>
+            <el-button
+              v-if="
+                (option.buttons.query.isShow == undefined
+                  ? true
+                  : option.buttons.query.isShow) &&
+                  hasPermission(option.buttons.edit.permission) == false
+              "
+              type="text"
+              size="small"
+              @click="handleOpenEditView('view', scope.row)"
+              v-permission="option.buttons.query.permission"
+              >查看</el-button
+            >
+            <el-button
+              v-if="
+                option.buttons.edit.isShow == undefined
+                  ? true
+                  : option.buttons.edit.isShow
+              "
+              type="text"
+              size="small"
+              @click="handleOpenEditView('edit', scope.row)"
+              v-permission="option.buttons.edit.permission"
+              >编辑</el-button
+            >
+            <el-button
+              v-if="
+                hasRowCustomButton == false &&
+                option.buttons.delete.isShow == undefined
+                  ? true
+                  : option.buttons.edit.isShow
+              "
+              type="text"
+              size="small"
+              @click="handleDeleteBatch(scope.row)"
+              v-permission="option.buttons.delete.permission"
+              >删除</el-button
+            >
             <el-dropdown v-if="hasRowCustomButton" trigger="click">
-              <span class="el-dropdown-link"> 更多<i class="el-icon-caret-bottom el-icon--right" /> </span>
+              <span class="el-dropdown-link">
+                更多<i class="el-icon-caret-bottom el-icon--right" />
+              </span>
               <el-dropdown-menu slot="dropdown">
                 <el-dropdown-item class="clearfix">
                   <slot name="rowButton" :msg="scope.row" />
-                  <el-button v-if="option.buttons.delete.isShow == undefined ? true : option.buttons.edit.isShow" type="text" size="small" @click="handleDeleteBatch(scope.row)" v-permission="option.buttons.delete.permission" >删除</el-button>
+                  <el-button
+                    v-if="
+                      option.buttons.delete.isShow == undefined
+                        ? true
+                        : option.buttons.edit.isShow
+                    "
+                    type="text"
+                    size="small"
+                    @click="handleDeleteBatch(scope.row)"
+                    v-permission="option.buttons.delete.permission"
+                    >删除</el-button
+                  >
                 </el-dropdown-item>
               </el-dropdown-menu>
             </el-dropdown>
@@ -111,7 +316,17 @@
         </el-table-column>
       </el-table>
       <div class="pagination">
-        <el-pagination v-show="total > 0" background :current-page.sync="queryParams.pageNumber" :page-sizes="$pageSizeAll" :page-size="queryParams.pageSize" layout="total, prev, pager, next, jumper, sizes" :total="total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
+        <el-pagination
+          v-show="total > 0"
+          background
+          :current-page.sync="queryParams.pageNumber"
+          :page-sizes="$pageSizeAll"
+          :page-size="queryParams.pageSize"
+          layout="total, prev, pager, next, jumper, sizes"
+          :total="total"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
         <div>
           <slot name="tableSelectionBtn" :selection="checkRecords" />
         </div>
@@ -119,7 +334,14 @@
 
       <!-- 表格结束 -->
 
-      <EditDialog ref="edit" :option="option" :model-type="editDialogModelType" :visible="editDialogOpen" :row-data="editDialogRowData" @closeEvent="editDialogClosedEvent">
+      <EditDialog
+        ref="edit"
+        :option="option"
+        :model-type="editDialogModelType"
+        :visible="editDialogOpen"
+        :row-data="editDialogRowData"
+        @closeEvent="editDialogClosedEvent"
+      >
         <template v-slot:customCard>
           <slot name="cardInEditPage" />
         </template>
@@ -132,13 +354,13 @@
   </div>
 </template>
 <script>
-import AnjiTree from '@/components/AnjiPlus/anji-tree.vue'
-import EditDialog from './edit'
-import request from '@/utils/request'
+import AnjiTree from "@/components/AnjiPlus/anji-tree.vue";
+import EditDialog from "./edit";
+import request from "@/utils/request";
 export default {
   components: {
     EditDialog,
-    AnjiTree,
+    AnjiTree
   },
   props: {
     option: {
@@ -153,14 +375,14 @@ export default {
             query: {},
             edit: {},
             delete: {},
-            add: {},
+            add: {}
           },
           // 表格列
           columns: [],
-          queryFormChange: (fileName, val) => {},
-        }
-      },
-    },
+          queryFormChange: (fileName, val) => {}
+        };
+      }
+    }
   },
   data() {
     return {
@@ -169,8 +391,8 @@ export default {
         showMoreSearch: false, // 是否展开更多搜索条件
         pageNumber: 1,
         pageSize: 10,
-        order: '',
-        sort: '',
+        order: "",
+        sort: ""
       },
 
       checkRecords: [], // 表格中当前选中的记录
@@ -180,73 +402,73 @@ export default {
       // 编辑详情弹框
       editDialogOpen: false, // 新建时主动打开编辑弹框
       editDialogRowData: {}, // 编辑时的主键
-      editDialogModelType: 'view', // 编辑 查看
+      editDialogModelType: "view", // 编辑 查看
 
-      hasRowCustomButton: false, // 除了编辑删除外,还有自定义的行按钮
-    }
+      hasRowCustomButton: false // 除了编辑删除外,还有自定义的行按钮
+    };
   },
   computed: {
     // 左侧树形查询条件
     queryFormTreeField() {
       var treeField = this.option.queryFormFields.find(
-        (item) => item['inputType'] == 'anji-tree'
-      )
-      return treeField
+        item => item["inputType"] == "anji-tree"
+      );
+      return treeField;
     },
     // 查询条件里是否有树形控件
     hasTreeFieldInQueryForm() {
-      return this.isNotBlank(this.queryFormTreeField)
+      return this.isNotBlank(this.queryFormTreeField);
     },
     // 不包含树形控件的查询条件
     queryFormFieldExcludeTree() {
       var treeFields = this.option.queryFormFields.filter(
-        (item) => item['inputType'] != 'anji-tree'
-      )
-      return treeFields
+        item => item["inputType"] != "anji-tree"
+      );
+      return treeFields;
     },
     // 主键的列名
     primaryKeyFieldName() {
       var primaryKey = this.option.columns.find(
-        (item) => item['primaryKey'] == true
-      )
+        item => item["primaryKey"] == true
+      );
       if (primaryKey != null) {
-        return primaryKey['field']
+        return primaryKey["field"];
       } else {
-        return null
+        return null;
         console.warn(
-          '在columns中查找primaryKey=true失败,会导致查询详情和删除失败'
-        )
+          "在columns中查找primaryKey=true失败,会导致查询详情和删除失败"
+        );
       }
     },
 
     // 表格中可展开的列
     tableExpandColumns() {
       var expandColumns = this.option.columns.filter(
-        (item) => item['columnType'] == 'expand'
-      )
-      return expandColumns
+        item => item["columnType"] == "expand"
+      );
+      return expandColumns;
     },
 
     // 是否可以批量删除
     disableBatchDelete() {
-      return this.checkRecords.length <= 0
-    },
+      return this.checkRecords.length <= 0;
+    }
   },
   created() {
     // 为查询框中所有input加上默认值
-    this.option.queryFormFields.forEach((item) => {
+    this.option.queryFormFields.forEach(item => {
       // 动态添加属性
-      this.$set(this.queryParams, item.field, item.defaultValue || null)
-    })
+      this.$set(this.queryParams, item.field, item.defaultValue || null);
+    });
     // 查询列表
-    this.handleQueryForm('query')
-    this.queryFormChange()
+    this.handleQueryForm("query");
+    this.queryFormChange();
   },
   mounted() {
-    if (this.$scopedSlots['rowButton'] != null) {
-      this.hasRowCustomButton = true
+    if (this.$scopedSlots["rowButton"] != null) {
+      this.hasRowCustomButton = true;
     } else {
-      this.hasRowCustomButton = false
+      this.hasRowCustomButton = false;
     }
   },
   methods: {
@@ -254,9 +476,9 @@ export default {
       // console.log(item)
 
       if (item.span != null) {
-        return item.span
+        return item.span;
       } else {
-        return 6
+        return 6;
       }
       // let rowLength = this.option.queryFormFields.length;
       // console.log(rowLength, "ss")
@@ -272,169 +494,169 @@ export default {
     },
     // 切换更多搜索条件
     handleToggleMoreSearch() {
-      this.queryParams.showMoreSearch = !this.queryParams.showMoreSearch
+      this.queryParams.showMoreSearch = !this.queryParams.showMoreSearch;
     },
     // 列上排序切换
     handleSortChange(column) {
       // {column: {…}, prop: "orgCode", order: "ascending"}
       if (column == null || column.prop == null) {
-        console.warn('排序字段名prop为空,无法排序')
-        return
+        console.warn("排序字段名prop为空,无法排序");
+        return;
       }
-      var sort = column.prop // 列表查询默认排序列
-      var order = column.order == 'ascending' ? 'ASC' : 'DESC'
-      this.queryParams['sort'] = sort
-      this.queryParams['order'] = order
-      this.handleQueryForm('query')
+      var sort = column.prop; // 列表查询默认排序列
+      var order = column.order == "ascending" ? "ASC" : "DESC";
+      this.queryParams["sort"] = sort;
+      this.queryParams["order"] = order;
+      this.handleQueryForm("query");
     },
     // 查询按钮
     handleQueryForm(from) {
       // 如果是点查询按钮,把树的查询属性去掉
-      if (from == 'query') {
+      if (from == "query") {
         if (this.hasTreeFieldInQueryForm) {
-          delete this.queryParams[this.queryFormTreeField.field]
+          delete this.queryParams[this.queryFormTreeField.field];
         }
       }
       // 如果是点树查询,把查询区里的属性去掉
-      if (from == 'tree') {
+      if (from == "tree") {
         if (this.hasTreeFieldInQueryForm) {
-          var treeVal = this.queryParams[this.queryFormTreeField.field]
+          var treeVal = this.queryParams[this.queryFormTreeField.field];
           this.queryParams = {
             pageNumber: 1,
-            pageSize: 10,
-          }
-          this.queryParams[this.queryFormTreeField.field] = treeVal
+            pageSize: 10
+          };
+          this.queryParams[this.queryFormTreeField.field] = treeVal;
         }
       }
       // 默认的排序
       if (
-        this.isBlank(this.queryParams['order']) &&
+        this.isBlank(this.queryParams["order"]) &&
         this.isNotBlank(this.option.buttons.query.order)
       ) {
-        this.queryParams['sort'] = this.option.buttons.query.sort
-        this.queryParams['order'] = this.option.buttons.query.order
+        this.queryParams["sort"] = this.option.buttons.query.sort;
+        this.queryParams["order"] = this.option.buttons.query.order;
       }
-      this.queryParams.pageNumber = 1
-      this.handleQueryPageList()
+      this.queryParams.pageNumber = 1;
+      this.handleQueryPageList();
     },
     // 列表查询
     async handleQueryPageList() {
-      var params = this.queryParams
+      var params = this.queryParams;
       // 将特殊参数值urlcode处理 var params = this.urlEncodeObject(this.queryParams, 'order,sort')
-      const { data, code } = await this.option.buttons.query.api(params)
-      if (code != '200') return
-      this.records = data.records
-      this.total = data.total
+      const { data, code } = await this.option.buttons.query.api(params);
+      if (code != "200") return;
+      this.records = data.records;
+      this.total = data.total;
     },
     // 重置
     handleResetForm() {
       this.queryParams = {
         pageNumber: 1,
-        pageSize: 10,
-      }
+        pageSize: 10
+      };
       // this.$refs['queryForm'].resetFields()
       // this.records = []
       // this.total = 0
     },
     // 树形查询条件点击回调
     handleTreeNodeCheck() {
-      this.handleQueryForm('tree')
+      this.handleQueryForm("tree");
       // 为新建页面的对应属性值,绑定上对应的默认值
-      var treeFieldName = this.queryFormTreeField['field']
+      var treeFieldName = this.queryFormTreeField["field"];
       for (var i = 0; i < this.option.columns.length; i++) {
-        var item = this.option.columns[i]
+        var item = this.option.columns[i];
         if (
-          item['editField'] == treeFieldName ||
-          item['field'] == treeFieldName
+          item["editField"] == treeFieldName ||
+          item["field"] == treeFieldName
         ) {
           this.$set(
             this.option.columns[i],
-            'defaultValue',
+            "defaultValue",
             this.queryParams[treeFieldName]
-          )
-          break
+          );
+          break;
         }
       }
     },
     // 编辑和查看操作
     handleOpenEditView(modelType, row) {
-      if (modelType == 'view' || modelType == 'edit') {
-        this.editDialogRowData = row
+      if (modelType == "view" || modelType == "edit") {
+        this.editDialogRowData = row;
       }
-      this.editDialogModelType = modelType
-      if (modelType == 'add') {
+      this.editDialogModelType = modelType;
+      if (modelType == "add") {
         // 新增模式,不需要查询数据详情,直接打开
-        this.editDialogOpen = true
+        this.editDialogOpen = true;
       }
       const obj = {
         type: modelType,
-        value: row,
-      }
-      this.$emit('handleCustomValue', obj)
+        value: row
+      };
+      this.$emit("handleCustomValue", obj);
     },
     // 弹框被关闭时的回调事件
     editDialogClosedEvent(value) {
       // 把列表页中弹框打开标记改成已关闭
-      this.editDialogOpen = false
+      this.editDialogOpen = false;
       // 关闭弹出框时,如果有树,刷新下
       if (
         this.hasTreeFieldInQueryForm &&
         this.$refs.queryFormTree != null &&
         !value
       ) {
-        this.$refs.queryFormTree.queryData()
+        this.$refs.queryFormTree.queryData();
       }
-      this.handleQueryPageList()
+      this.handleQueryPageList();
       // 关闭时 清空表单的验证规则
-      this.$refs.edit.$refs.mainForm.$refs.editForm.resetFields()
+      this.$refs.edit.$refs.mainForm.$refs.editForm.resetFields();
     },
     // 批量删除
     handleDeleteBatch(row) {
-      var ids = []
+      var ids = [];
       if (row != null) {
-        ids.push(row[this.primaryKeyFieldName]) // 删除指定的行
+        ids.push(row[this.primaryKeyFieldName]); // 删除指定的行
       } else {
         // 批量删除选中的行
-        ids = this.checkRecords.map((item) => item[this.primaryKeyFieldName])
+        ids = this.checkRecords.map(item => item[this.primaryKeyFieldName]);
       }
-      this.$confirm('删除确认', '确认要删除吗?', {
-        type: 'warning',
-        confirmButtonClass: 'delete_sure',
-        cancelButtonClass: 'el-button--danger is-plain',
+      this.$confirm("删除确认", "确认要删除吗?", {
+        type: "warning",
+        confirmButtonClass: "delete_sure",
+        cancelButtonClass: "el-button--danger is-plain"
       })
         .then(() => {
-          this.option.buttons.delete.api(ids).then((res) => {
+          this.option.buttons.delete.api(ids).then(res => {
             // {code: "200", message: "操作成功", data: true}
-            this.checkRecords = []
+            this.checkRecords = [];
             // 关闭弹出框时,如果有树,刷新下
             if (
               this.hasTreeFieldInQueryForm &&
               this.$refs.queryFormTree != null
             ) {
-              this.$refs.queryFormTree.queryData()
+              this.$refs.queryFormTree.queryData();
             }
-            this.handleQueryPageList()
-          })
-        })
-        .catch((e) => {
-          e
+            this.handleQueryPageList();
+          });
         })
+        .catch(e => {
+          e;
+        });
     },
 
     // 选择项改变时
     handleSelectionChange(val) {
-      this.checkRecords = val
+      this.checkRecords = val;
     },
     // 页码改变
     handleCurrentChange(pageNumber) {
-      this.queryParams.pageNumber = pageNumber
-      this.handleQueryPageList()
+      this.queryParams.pageNumber = pageNumber;
+      this.handleQueryPageList();
     },
     // 每页size改变时
     handleSizeChange(val) {
-      this.queryParams.pageNumber = 1
-      this.queryParams.pageSize = val
-      this.handleQueryPageList()
+      this.queryParams.pageNumber = 1;
+      this.queryParams.pageSize = val;
+      this.handleQueryPageList();
     },
     // table列文件缩略图
     thumbnailUrl(row, field) {
@@ -453,66 +675,66 @@ export default {
     // 带单位的列,需要转换
     fieldLabel(columnConfig) {
       if (columnConfig == null) {
-        return ''
+        return "";
       }
       if (
-        columnConfig.inputType == 'anji-input' &&
+        columnConfig.inputType == "anji-input" &&
         columnConfig.anjiInput != null
       ) {
-        return `${columnConfig.label}(${columnConfig.anjiInput.unit})`
+        return `${columnConfig.label}(${columnConfig.anjiInput.unit})`;
       } else {
-        return columnConfig.label
+        return columnConfig.label;
       }
     },
     // 带单位的输入框
     fieldValueByAnjiInput(value, columnConfig) {
       if (columnConfig == null) {
-        return value
+        return value;
       }
       if (
-        columnConfig.inputType == 'anji-input' &&
+        columnConfig.inputType == "anji-input" &&
         columnConfig.anjiInput != null
       ) {
-        return value / columnConfig.anjiInput.conversion
+        return value / columnConfig.anjiInput.conversion;
       } else {
-        return value
+        return value;
       }
     },
     // 带表格列格式化的值
     fieldValueByRowRenderer(row, columnConfig) {
       if (
         columnConfig == null ||
-        typeof columnConfig.fieldTableRowRenderer != 'function'
+        typeof columnConfig.fieldTableRowRenderer != "function"
       ) {
-        return row[columnConfig.field]
+        return row[columnConfig.field];
       } else {
-        return columnConfig.fieldTableRowRenderer(row)
+        return columnConfig.fieldTableRowRenderer(row);
       }
     },
     // 暴露给外部crud页面,回传saveForm的值
     getMainEntity() {
-      return this.$refs.edit.getSaveForm()
+      return this.$refs.edit.getSaveForm();
     },
     setMainEntity(object) {
-      this.$refs.edit.setSaveForm(object)
+      this.$refs.edit.setSaveForm(object);
     },
     async switchChange(val, api) {
       request({
         url: api.url,
-        method: 'put',
+        method: "put",
         headers: { noPrompt: false },
-        data: [val.id],
-      }).then((response) => {
-        this.handleQueryPageList()
-      })
+        data: [val.id]
+      }).then(response => {
+        this.handleQueryPageList();
+      });
     },
     queryFormChange(fileName, fieldVal) {
-      if (typeof this.option.queryFormChange == 'function') {
-        this.option.queryFormChange(this.queryParams, fileName, fieldVal)
+      if (typeof this.option.queryFormChange == "function") {
+        this.option.queryFormChange(this.queryParams, fileName, fieldVal);
       }
-    },
-  },
-}
+    }
+  }
+};
 </script>
 
 <style scoped lang="scss">
@@ -578,5 +800,9 @@ export default {
   display: flex;
   flex-direction: row;
   justify-content: space-between;
+  margin-top: 20px;
+}
+.anji_curd_table {
+  margin-top: 20px;
 }
 </style>

+ 5 - 0
report-ui/src/components/AnjiPlus/anji-select.vue

@@ -316,3 +316,8 @@ export default {
   }
 };
 </script>
+<style scoped>
+.filter-item {
+  width: 100%;
+}
+</style>

+ 8 - 7
report-ui/src/components/Dictionary/index.vue

@@ -28,7 +28,8 @@ export default {
   name: "GetDictionary",
   props: {
     dictKey: String, // 字典code
-    updataDict: String // 回显绑定的值
+    updataDict: String, // 回显绑定的值
+    value: String
   },
   data() {
     return {
@@ -43,18 +44,18 @@ export default {
         this.getSystem();
       }
     },
-    updataDict: {
-      immediate: true,
-      handler() {
-        this.dictionary = this.updataDict;
-      }
+    value: {
+      handler(val) {
+        this.dictionary = val;
+      },
+      deep: true
     }
   },
   created() {
     this.getSystem();
   },
   mounted() {
-    this.dictionary = this.updataDict;
+    this.dictionary = this.value;
   },
   methods: {
     // 获取数据字典

+ 3 - 25
report-ui/src/mixins/queryform.js

@@ -106,7 +106,6 @@ export default {
     analysisChartsData(params, data) {
       // widget-barchart 柱线图、widget-linechart 折线图、 widget-barlinechart 柱线图
       // widget-piechart 饼图、widget-funnel 漏斗图
-      // widget-gauge 仪表盘
       // widget-text 文本框
       // widge-table 表格(数据不要转)
       // widget-stackchart 堆叠图
@@ -122,9 +121,7 @@ export default {
         chartType == "widget-funnel"
       ) {
         return this.piechartFn(params.chartProperties, data);
-      } else if (chartType == "widget-gauge") {
-        return this.gaugeFn(params.chartProperties, data);
-      } else if (chartType == "widget-text") {
+      }  else if (chartType == "widget-text") {
         return this.widgettext(params.chartProperties, data)
       } else if (chartType == "widget-stackchart") {
         return this.stackChartFn(params.chartProperties, data)
@@ -169,11 +166,11 @@ export default {
       //x轴字段、y轴字段名
       const xAxisField = Object.keys(chartProperties)[types.indexOf('xAxis')]
       const yAxisField = Object.keys(chartProperties)[types.indexOf('yAxis')]
+      const dataField = Object.keys(chartProperties)[types.indexOf('bar')]
       //x轴数值去重,y轴去重
       const xAxisList = this.setUnique(data.map(item => item[xAxisField]))
       const yAxisList = this.setUnique(data.map(item => item[yAxisField]))
       const dataGroup = this.setGroupBy(data, yAxisField)
-
       for (const key in chartProperties) {
         if (chartProperties[key] !== 'yAxis' && !chartProperties[key].startsWith('xAxis')) {
           Object.keys(dataGroup).forEach(item => {
@@ -184,7 +181,7 @@ export default {
             series.push({
               name: yAxisList[item],
               type: chartProperties[key],
-              data,
+              data: data,
             })
           })
         }
@@ -210,25 +207,6 @@ export default {
       }
       return ananysicData;
     },
-    gaugeFn(chartProperties, data) {
-      const ananysicData = [];
-      for (let i = 0; i < data.length; i++) {
-        const obj = {};
-        for (const key in chartProperties) {
-          const value = chartProperties[key];
-          if (value === "name") {
-            obj["name"] = data[i][key];
-          } else {
-            obj["value"] = data[i][key];
-          }
-        }
-        if (!obj["unit"]) {
-          obj["unit"] = "%";
-        }
-        ananysicData.push(obj);
-      }
-      return ananysicData[0];
-    },
     widgettext(chartProperties, data) {
       const ananysicData = [];
       for (let i = 0; i < data.length; i++) {

+ 3 - 1
report-ui/src/router/index.js

@@ -49,7 +49,7 @@ export const constantRouterMap = [
       { path: 'resultset', name: 'resultset', component: () => import('@/views/report/resultset/index'), meta: { title: '数据集', icon: 'iconAPIwangguan', keepAlive: true, requireAuth: true, permission: 'resultsetManage'} },
       { path: 'report', name: 'reportIndex', component: () => import('@/views/report/report/index'), meta: { title: '报表管理', icon: 'iconnavicon-ywcs', keepAlive: true, requireAuth: true, permission: 'reportManage'} },
       { path: 'bigscreen', name: 'bigscreen', component: () => import('@/views/report/bigscreen/index'), meta: { title: '大屏报表', icon: 'iconchufaqipeizhi-hui', keepAlive: true, requireAuth: true, permission: 'bigScreenManage'},       },
-      //{ path: 'excelreport', name: 'excelreport', component: () => import('@/views/report/excelreport/index'), meta: { title: '表格报表', icon: 'iconliebiao', keepAlive: true, requireAuth: true, permission: 'excelManage'} },
+      { path: 'excelreport', name: 'excelreport', component: () => import('@/views/report/excelreport/index'), meta: { title: '表格报表', icon: 'iconliebiao', keepAlive: true, requireAuth: true, permission: 'excelManage'} },
     ]
   },
   {
@@ -62,6 +62,8 @@ export const constantRouterMap = [
   },
   { path: '/bigscreen/viewer', component: () => import('@/views/report/bigscreen/viewer'), hidden: true, meta: { requireAuth: true }},
   { path: '/bigscreen/designer', component: () => import('@/views/report/bigscreen/designer'), hidden: true, meta: { requireAuth: true }},
+  { path: '/excelreport/viewer', component: () => import('@/views/report/excelreport/viewer'), hidden: true, meta: { requireAuth: true }},
+  { path: '/excelreport/designer', component: () => import('@/views/report/excelreport/designer'), hidden: true, meta: { requireAuth: true }},
   { path: '/404', component: () => import('@/views/404'), hidden: true },
   { path: '*', redirect: '/login', hidden: true },
   /*

Vissa filer visades inte eftersom för många filer har ändrats