Przeglądaj źródła

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

Foming 4 lat temu
rodzic
commit
3334ce7672
100 zmienionych plików z 3465 dodań i 292 usunięć
  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.
 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; 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配置数据集---->拖拽配置大屏---->保存发布。欢迎体验。
 &emsp; &emsp; 三步轻松完成大屏设计:配置数据源---->写SQL配置数据集---->拖拽配置大屏---->保存发布。欢迎体验。
 
 
 ## 在线体验
 ## 在线体验
@@ -85,6 +85,7 @@
 - [vue-echarts](https://www.npmjs.com/package/vue-echarts/): vue-echarts是封装后的vue插件,基于 ECharts v4.0.1+ 开发
 - [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 封装版本
 - [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拖拽插件。
 - [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
 - Node.js V16
 - Jdk 11
 - Jdk 11
-- Mysql 8.0(8.0.26版本没有问题,8.0.21版本存在问题)
+- Mysql 8.0(8.0.23/26版本没有问题,8.0.21版本存在问题)
 - Windows 11
 - 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>
 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: [
                     children: [
                         {title: '数据源', path: '/guide/datasource'},
                         {title: '数据源', path: '/guide/datasource'},
                         {title: '数据集', path: '/guide/dataset'},
                         {title: '数据集', path: '/guide/dataset'},
-                        {title: '大屏设计', path: '/guide/dashboard'},
+                        {title: '报表管理', path: '/guide/reportmanager'},
+                        {title: '大屏报表', path: '/guide/dashboard'},
+                        {title: '表格报表', path: '/guide/execl'},
                         {title: '导入导出', path: '/guide/importexport'},
                         {title: '导入导出', path: '/guide/importexport'},
                     ]
                     ]
                 },
                 },
@@ -65,7 +67,8 @@ module.exports = {
                     title: '社区提供',
                     title: '社区提供',
                     collapsable: false,
                     collapsable: false,
                     children: [
                     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;  AJ-Report是一个完全开源的BI平台,酷炫大屏展示,能随时随地掌控业务动态,让每个决策都有数据支撑。<br>
-&emsp;  &emsp;  多数据源支持,内置mysql、elasticsearch、kudu等多种驱动,支持自定义数据集省去数据接口开发,支持17+大屏组件,不会开发,照着设计稿也可以制作大屏。<br>
+&emsp;  &emsp;  多数据源支持,内置mysql、elasticsearch、kudu等多种驱动,支持自定义数据集省去数据接口开发,支持17+大屏组件,不会开发,照着设计稿也可以制作大屏。<br>
 &emsp;  &emsp;  三步轻松完成大屏设计:配置数据源---->写SQL配置数据集---->拖拽配置大屏---->保存发布。欢迎体验。
 &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>
 已支持动态数据,请参考文本框的操作 <br>
@@ -73,7 +78,7 @@
 
 
 ### 仪表盘
 ### 仪表盘
 数据集只能有一个字段,且字典选择“文本数字” <br>
 数据集只能有一个字段,且字典选择“文本数字” <br>
-![img14](../picture/dashboard/img_14.png) <br>
+![img21](../picture/dashboard/img_21.png) <br>
 **如有问题,请提交 [Issue](https://gitee.com/anji-plus/report/issues) <br>**
 **如有问题,请提交 [Issue](https://gitee.com/anji-plus/report/issues) <br>**
 
 
 ### 中国地图
 ### 中国地图
@@ -81,16 +86,20 @@
 气泡地图动态数据集,和饼图一样,对应字典值需要选择一个“Name”、“Value”,且name的字段值要和echarts图表里面的值能对应上,可参考静态数据 <br>
 气泡地图动态数据集,和饼图一样,对应字典值需要选择一个“Name”、“Value”,且name的字段值要和echarts图表里面的值能对应上,可参考静态数据 <br>
 ![img15](../picture/dashboard/img_15.png) <br>
 ![img15](../picture/dashboard/img_15.png) <br>
 
 
-
 ### 百分百图
 ### 百分百图
 数据集只能有一个字段,且字典选择“文本数字” <br>
 数据集只能有一个字段,且字典选择“文本数字” <br>
 ![img16](../picture/dashboard/img_16.png) <br>
 ![img16](../picture/dashboard/img_16.png) <br>
 **如有问题,请提交 [Issue](https://gitee.com/anji-plus/report/issues) <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>**
 **如有问题,请提交 [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)
 ![source.png](../picture/datasource/img_1.png)
 
 
 ## 数据源类型
 ## 数据源类型
-- 没有找到新增数据源类型?<br>
+查看已有的数据源类型 <br>
+![img2](../picture/datasource/img_2.png)  <br>
+![img3](../picture/datasource/img_3.png)  <br>
 ```text
 ```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数据
 表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>
 ![img1](../picture/imexport/img_1.png) <br>
 选择一个导出的zip文件导入即可。注意,导入会覆盖当前大屏,请新建一张空白的大屏进行导入。<br>
 选择一个导出的zip文件导入即可。注意,导入会覆盖当前大屏,请新建一张空白的大屏进行导入。<br>
 **注:如果你导入的大屏中含有你当前系统不存在的图表,整个大屏是不会显示的。** <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>
             <version>1.18.10</version>
             <optional>true</optional>
             <optional>true</optional>
         </dependency>
         </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>
     </dependencies>
 
 
     <developers>
     <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 com.zaxxer.hikari.HikariDataSource;
 import lombok.AllArgsConstructor;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.autoconfigure.flyway.FlywayProperties;
 import org.springframework.boot.autoconfigure.flyway.FlywayProperties;
 import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
 import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
@@ -18,6 +19,7 @@ import java.sql.Statement;
 @Slf4j
 @Slf4j
 @Component
 @Component
 @AllArgsConstructor
 @AllArgsConstructor
+@ConditionalOnProperty(value = {"spring.flyway.enabled"})
 public class DatabaseInitializer {
 public class DatabaseInitializer {
 
 
     private final FlywayProperties flywayProperties;
     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.constant.BusinessConstant;
 import com.anjiplus.template.gaea.business.util.JwtUtil;
 import com.anjiplus.template.gaea.business.util.JwtUtil;
 import org.apache.commons.lang3.StringUtils;
 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.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.core.annotation.Order;
 import org.springframework.core.annotation.Order;
@@ -57,6 +58,12 @@ public class TokenFilter implements Filter {
         HttpServletResponse response = (HttpServletResponse) servletResponse;
         HttpServletResponse response = (HttpServletResponse) servletResponse;
         String uri = request.getRequestURI();
         String uri = request.getRequestURI();
 
 
+        //OPTIONS直接放行
+        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
+            filterChain.doFilter(request, response);
+            return;
+        }
+
         if (SLASH.equals(uri)) {
         if (SLASH.equals(uri)) {
             response.sendRedirect("/index.html");
             response.sendRedirect("/index.html");
             return;
             return;
@@ -123,9 +130,11 @@ public class TokenFilter implements Filter {
             if (HttpMethod.POST.name().equalsIgnoreCase(method)
             if (HttpMethod.POST.name().equalsIgnoreCase(method)
                     || HttpMethod.PUT.name().equalsIgnoreCase(method)
                     || HttpMethod.PUT.name().equalsIgnoreCase(method)
                     || HttpMethod.DELETE.name().equalsIgnoreCase(method)
                     || HttpMethod.DELETE.name().equalsIgnoreCase(method)
+                    || uri.contains("/reportDashboard/export")
             ) {
             ) {
                 ResponseBean responseBean = ResponseBean.builder().code("50001")
                 ResponseBean responseBean = ResponseBean.builder().code("50001")
                         .message("在线体验版本,不允许此操作。请自行下载本地运行").build();
                         .message("在线体验版本,不允许此操作。请自行下载本地运行").build();
+                response.setContentType(ContentType.APPLICATION_JSON.getMimeType());
                 response.getWriter().print(JSONObject.toJSONString(responseBean));
                 response.getWriter().print(JSONObject.toJSONString(responseBean));
                 return;
                 return;
             }
             }
@@ -167,6 +176,7 @@ public class TokenFilter implements Filter {
 
 
     private void error(HttpServletResponse response) throws IOException {
     private void error(HttpServletResponse response) throws IOException {
         ResponseBean responseBean = ResponseBean.builder().code("50014").message("The Token has expired").build();
         ResponseBean responseBean = ResponseBean.builder().code("50014").message("The Token has expired").build();
+        response.setContentType(ContentType.APPLICATION_JSON.getMimeType());
         response.getWriter().print(JSONObject.toJSONString(responseBean));
         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>
 * @author 木子李·De <lide1202@hotmail.com>
 * @date 2019-02-17 08:50:10.009
 * @date 2019-02-17 08:50:10.009
 **/
 **/
-@TableName(value="access_authority")
+@TableName(keepGlobalPrefix=true, value="access_authority")
 @Data
 @Data
 public class AccessAuthority extends GaeaBaseEntity {
 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>
 * @author 木子李·De <lide1202@hotmail.com>
 * @date 2019-02-17 08:50:14.136
 * @date 2019-02-17 08:50:14.136
 **/
 **/
-@TableName(value="access_role")
+@TableName(keepGlobalPrefix=true, value="access_role")
 @Data
 @Data
 public class AccessRole extends GaeaBaseEntity {
 public class AccessRole extends GaeaBaseEntity {
 
 
@@ -27,4 +27,4 @@ public class AccessRole extends GaeaBaseEntity {
     /** 0--已禁用 1--已启用  DIC_NAME=ENABLE_FLAG */
     /** 0--已禁用 1--已启用  DIC_NAME=ENABLE_FLAG */
     private Integer enableFlag;
     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>
  * @author 木子李·De <lide1202@hotmail.com>
  * @date 2019-02-17 08:50:14.136
  * @date 2019-02-17 08:50:14.136
  **/
  **/
-@TableName(value="access_role_authority")
+@TableName(keepGlobalPrefix=true, value="access_role_authority")
 @Data
 @Data
 public class AccessRoleAuthority extends GaeaBaseEntity {
 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.HashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 
 /**
 /**
 * @desc AccessRole 角色管理服务实现
 * @desc AccessRole 角色管理服务实现
@@ -52,7 +53,14 @@ public class AccessRoleServiceImpl implements AccessRoleService {
         List<TreeNode> treeData = accessAuthorityService.getAuthorityTree(operator, true);
         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("treeData", treeData);
         result.put("checkedKeys", checkedKeys);
         result.put("checkedKeys", checkedKeys);
@@ -90,4 +98,4 @@ public class AccessRoleServiceImpl implements AccessRoleService {
         });
         });
         return true;
         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>
 * @author 木子李·De <lide1202@hotmail.com>
 * @date 2019-02-17 08:50:11.902
 * @date 2019-02-17 08:50:11.902
 **/
 **/
-@TableName(value="access_user")
+@TableName(keepGlobalPrefix=true, value="access_user")
 @Data
 @Data
 public class AccessUser extends GaeaBaseEntity {
 public class AccessUser extends GaeaBaseEntity {
 
 
@@ -45,4 +45,4 @@ public class AccessUser extends GaeaBaseEntity {
     /** 最后一次登陆时间 */
     /** 最后一次登陆时间 */
     private Date lastLoginTime;
     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>
 * @author 木子李·De <lide1202@hotmail.com>
 * @date 2019-02-17 08:50:11.902
 * @date 2019-02-17 08:50:11.902
 **/
 **/
-@TableName(value="access_user_role")
+@TableName(keepGlobalPrefix=true, value="access_user_role")
 @Data
 @Data
 public class AccessUserRole extends GaeaBaseEntity {
 public class AccessUserRole extends GaeaBaseEntity {
 
 
@@ -37,4 +37,4 @@ public class AccessUserRole extends GaeaBaseEntity {
 
 
     @TableField(exist = false)
     @TableField(exist = false)
     private Integer version;
     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.anji.plus.gaea.utils.JwtBean;
 import com.anjiplus.template.gaea.business.code.ResponseCode;
 import com.anjiplus.template.gaea.business.code.ResponseCode;
 import com.anjiplus.template.gaea.business.constant.BusinessConstant;
 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.AccessRoleMapper;
 import com.anjiplus.template.gaea.business.modules.accessrole.dao.entity.AccessRole;
 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.AccessUserDto;
 import com.anjiplus.template.gaea.business.modules.accessuser.controller.dto.GaeaUserDto;
 import com.anjiplus.template.gaea.business.modules.accessuser.controller.dto.GaeaUserDto;
 import com.anjiplus.template.gaea.business.modules.accessuser.controller.dto.UpdatePasswordDto;
 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.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 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;
 import java.util.stream.Collectors;
 
 
 /**
 /**
@@ -54,6 +53,9 @@ public class AccessUserServiceImpl implements AccessUserService {
     @Autowired
     @Autowired
     private AccessUserRoleMapper accessUserRoleMapper;
     private AccessUserRoleMapper accessUserRoleMapper;
 
 
+    @Autowired
+    private AccessRoleAuthorityMapper accessRoleAuthorityMapper;
+
     @Value("${customer.user.default.password:'123456'}")
     @Value("${customer.user.default.password:'123456'}")
     private String defaultPassword;
     private String defaultPassword;
 
 
@@ -168,11 +170,31 @@ public class AccessUserServiceImpl implements AccessUserService {
         // 4.读取用户最新人权限主信息
         // 4.读取用户最新人权限主信息
         String userKey = String.format(BusinessConstant.GAEA_SECURITY_LOGIN_USER, loginName);
         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.setLoginName(loginName);
         gaeaUser.setRealName(accessUser.getRealName());
         gaeaUser.setRealName(accessUser.getRealName());
         gaeaUser.setToken(token);
         gaeaUser.setToken(token);
-        gaeaUser.setAuthorities(authorities);
+
         String gaeaUserStr = JSONObject.toJSONString(gaeaUser);
         String gaeaUserStr = JSONObject.toJSONString(gaeaUser);
         cacheHelper.stringSetExpire(userKey, gaeaUserStr, 3600);
         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
      * @return
      */
      */
     @GetMapping("/export")
     @GetMapping("/export")
-    @Permission(code = "view", name = "导出大屏")
+    @Permission(code = "export", name = "导出大屏")
     public ResponseEntity<byte[]> exportDashboard(HttpServletRequest request, HttpServletResponse response,
     public ResponseEntity<byte[]> exportDashboard(HttpServletRequest request, HttpServletResponse response,
                                                   @RequestParam("reportCode") String reportCode, @RequestParam(value = "showDataSet",required = false, defaultValue = "1") Integer showDataSet) {
                                                   @RequestParam("reportCode") String reportCode, @RequestParam(value = "showDataSet",required = false, defaultValue = "1") Integer showDataSet) {
         return reportDashboardService.exportDashboard(request, response, reportCode, showDataSet);
         return reportDashboardService.exportDashboard(request, response, reportCode, showDataSet);
@@ -87,7 +87,7 @@ public class ReportDashboardController {
      * @return
      * @return
      */
      */
     @PostMapping("/import/{reportCode}")
     @PostMapping("/import/{reportCode}")
-    @Permission(code = "design", name = "导入大屏")
+    @Permission(code = "import", name = "导入大屏")
     public ResponseBean importDashboard(@RequestParam("file") MultipartFile file, @PathVariable("reportCode") String reportCode) {
     public ResponseBean importDashboard(@RequestParam("file") MultipartFile file, @PathVariable("reportCode") String reportCode) {
         reportDashboardService.importDashboard(file, reportCode);
         reportDashboardService.importDashboard(file, reportCode);
         return ResponseBean.builder().build();
         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
 * @author Raod
 * @date 2021-04-12 14:52:21.761
 * @date 2021-04-12 14:52:21.761
 **/
 **/
-@TableName(value="gaea_report_dashboard")
+@TableName(keepGlobalPrefix=true, value="gaea_report_dashboard")
 @Data
 @Data
 public class ReportDashboard extends GaeaBaseEntity {
 public class ReportDashboard extends GaeaBaseEntity {
     @ApiModelProperty(value = "报表编码")
     @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));
                 .lambda().eq(ReportDashboardWidget::getReportCode, reportCode));
         List<ReportDashboardWidgetDto> widgets = dto.getWidgets();
         List<ReportDashboardWidgetDto> widgets = dto.getWidgets();
 
 
-        List<ReportDashboardWidget> reportDashboardWidgetList = new ArrayList<>();
+//        List<ReportDashboardWidget> reportDashboardWidgetList = new ArrayList<>();
         for (int i = 0; i < widgets.size(); i++) {
         for (int i = 0; i < widgets.size(); i++) {
             ReportDashboardWidget reportDashboardWidget = new ReportDashboardWidget();
             ReportDashboardWidget reportDashboardWidget = new ReportDashboardWidget();
             ReportDashboardWidgetDto reportDashboardWidgetDto = widgets.get(i);
             ReportDashboardWidgetDto reportDashboardWidgetDto = widgets.get(i);
@@ -175,9 +175,13 @@ public class ReportDashboardServiceImpl implements ReportDashboardService, Initi
             reportDashboardWidget.setEnableFlag(1);
             reportDashboardWidget.setEnableFlag(1);
             reportDashboardWidget.setDeleteFlag(0);
             reportDashboardWidget.setDeleteFlag(0);
             reportDashboardWidget.setSort((long) (i + 1));
             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) {
     private String replaceUrl(String imageAddress, Map<String, String> fileMap) {
+        if (StringUtils.isBlank(imageAddress)) {
+            return "";
+        }
         String fileId = imageAddress.substring(imageAddress.trim().length() - 36);
         String fileId = imageAddress.substring(imageAddress.trim().length() - 36);
         String orDefault = fileMap.getOrDefault(fileId, null);
         String orDefault = fileMap.getOrDefault(fileId, null);
         if (StringUtils.isBlank(orDefault)) {
         if (StringUtils.isBlank(orDefault)) {
@@ -443,14 +450,19 @@ public class ReportDashboardServiceImpl implements ReportDashboardService, Initi
             return data;
             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);
             dto.setParticles(value);
             setTimeLineFormat(dto);
             setTimeLineFormat(dto);
             if (StringUtils.isNotBlank(dto.getDataTimeFormat())) {
             if (StringUtils.isNotBlank(dto.getDataTimeFormat())) {
                 dto.setTimeLineFiled(key);
                 dto.setTimeLineFiled(key);
-                return;
+                break;
             }
             }
-        });
+
+        }
+
 
 
         if (StringUtils.isBlank(dto.getDataTimeFormat())) {
         if (StringUtils.isBlank(dto.getDataTimeFormat())) {
             return data;
             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
 * @author Raod
 * @date 2021-04-12 15:12:43.724
 * @date 2021-04-12 15:12:43.724
 **/
 **/
-@TableName(value="gaea_report_dashboard_widget")
+@TableName(keepGlobalPrefix=true, value="gaea_report_dashboard_widget")
 @Data
 @Data
 public class ReportDashboardWidget extends GaeaBaseEntity {
 public class ReportDashboardWidget extends GaeaBaseEntity {
     @ApiModelProperty(value = "报表编码")
     @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 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
 * @author Raod
 * @date 2021-03-18 12:11:31.150755900
 * @date 2021-03-18 12:11:31.150755900
 **/
 **/
-@TableName(value="gaea_report_data_set")
+@TableName(keepGlobalPrefix=true, value="gaea_report_data_set")
 @Data
 @Data
 public class DataSet extends GaeaBaseEntity {
 public class DataSet extends GaeaBaseEntity {
     @ApiModelProperty(value = "数据集编码")
     @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());
         DataSource dataSource = dataSourceService.selectOne("source_code", dataSetDto.getSourceCode());
         //3.参数替换
         //3.参数替换
         //3.1参数校验
         //3.1参数校验
+        log.debug("参数校验替换前:{}", dto.getContextData());
         boolean verification = dataSetParamService.verification(dataSetDto.getDataSetParamDtoList(), dto.getContextData());
         boolean verification = dataSetParamService.verification(dataSetDto.getDataSetParamDtoList(), dto.getContextData());
         if (!verification) {
         if (!verification) {
             throw BusinessExceptionBuilder.build(ResponseCode.RULE_FIELDS_CHECK_ERROR);
             throw BusinessExceptionBuilder.build(ResponseCode.RULE_FIELDS_CHECK_ERROR);
         }
         }
         String dynSentence = dataSetParamService.transform(dto.getContextData(), dataSetDto.getDynSentence());
         String dynSentence = dataSetParamService.transform(dto.getContextData(), dataSetDto.getDynSentence());
+        log.debug("参数校验替换后:{}", dto.getContextData());
         //4.获取数据
         //4.获取数据
         DataSourceDto dataSourceDto = new DataSourceDto();
         DataSourceDto dataSourceDto = new DataSourceDto();
         BeanUtils.copyProperties(dataSource, dataSourceDto);
         BeanUtils.copyProperties(dataSource, dataSourceDto);
@@ -300,6 +302,7 @@ public class DataSetServiceImpl implements DataSetService {
         LambdaQueryWrapper<DataSet> wrapper = Wrappers.lambdaQuery();
         LambdaQueryWrapper<DataSet> wrapper = Wrappers.lambdaQuery();
         wrapper.select(DataSet::getSetCode, DataSet::getSetName, DataSet::getSetDesc, DataSet::getId)
         wrapper.select(DataSet::getSetCode, DataSet::getSetName, DataSet::getSetDesc, DataSet::getId)
                 .eq(DataSet::getEnableFlag, Enabled.YES.getValue());
                 .eq(DataSet::getEnableFlag, Enabled.YES.getValue());
+        wrapper.orderByDesc(DataSet::getUpdateTime);
         return dataSetMapper.selectList(wrapper);
         return dataSetMapper.selectList(wrapper);
     }
     }
 
 
@@ -312,14 +315,16 @@ public class DataSetServiceImpl implements DataSetService {
         if (null == dataSetParamDtoList || dataSetParamDtoList.size() <= 0) {
         if (null == dataSetParamDtoList || dataSetParamDtoList.size() <= 0) {
             return;
             return;
         }
         }
-        List<DataSetParam> dataSetParamList = new ArrayList<>();
+//        List<DataSetParam> dataSetParamList = new ArrayList<>();
         dataSetParamDtoList.forEach(dataSetParamDto -> {
         dataSetParamDtoList.forEach(dataSetParamDto -> {
             DataSetParam dataSetParam = new DataSetParam();
             DataSetParam dataSetParam = new DataSetParam();
             BeanUtils.copyProperties(dataSetParamDto, dataSetParam);
             BeanUtils.copyProperties(dataSetParamDto, dataSetParam);
             dataSetParam.setSetCode(setCode);
             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) {
         if (null == dataSetTransformDtoList || dataSetTransformDtoList.size() <= 0) {
             return;
             return;
         }
         }
-        List<DataSetTransform> dataSetTransformList = new ArrayList<>();
+//        List<DataSetTransform> dataSetTransformList = new ArrayList<>();
         for (int i = 0; i < dataSetTransformDtoList.size(); i++) {
         for (int i = 0; i < dataSetTransformDtoList.size(); i++) {
             DataSetTransform dataSetTransform = new DataSetTransform();
             DataSetTransform dataSetTransform = new DataSetTransform();
             BeanUtils.copyProperties(dataSetTransformDtoList.get(i), dataSetTransform);
             BeanUtils.copyProperties(dataSetTransformDtoList.get(i), dataSetTransform);
             dataSetTransform.setOrderNum(i + 1);
             dataSetTransform.setOrderNum(i + 1);
             dataSetTransform.setSetCode(setCode);
             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
 * @author Raod
 * @date 2021-03-18 12:12:33.108033200
 * @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
 @Data
 public class DataSetParam extends GaeaBaseEntity {
 public class DataSetParam extends GaeaBaseEntity {
     @ApiModelProperty(value = "数据集编码")
     @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
      * @param dataSetParamDto
      * @return
      * @return
      */
      */
-    boolean verification(DataSetParamDto dataSetParamDto);
+    Object verification(DataSetParamDto dataSetParamDto);
 
 
     /**
     /**
      * 参数校验  js脚本
      * 参数校验  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
      * @return
      */
      */
     @Override
     @Override
-    public boolean verification(DataSetParamDto dataSetParamDto) {
+    public Object verification(DataSetParamDto dataSetParamDto) {
 
 
         String validationRules = dataSetParamDto.getValidationRules();
         String validationRules = dataSetParamDto.getValidationRules();
         if (StringUtils.isNotBlank(validationRules)) {
         if (StringUtils.isNotBlank(validationRules)) {
@@ -103,7 +103,12 @@ public class DataSetParamServiceImpl implements DataSetParamService {
                     Invocable invocable = (Invocable) engine;
                     Invocable invocable = (Invocable) engine;
                     Object exec = invocable.invokeFunction("verification", dataSetParamDto);
                     Object exec = invocable.invokeFunction("verification", dataSetParamDto);
                     ObjectMapper objectMapper = new ObjectMapper();
                     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) {
             } catch (Exception ex) {
@@ -131,9 +136,20 @@ public class DataSetParamServiceImpl implements DataSetParamService {
                 String value = contextData.getOrDefault(dataSetParamDto.getParamName(), "").toString();
                 String value = contextData.getOrDefault(dataSetParamDto.getParamName(), "").toString();
                 dataSetParamDto.setSampleItem(value);
                 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;
         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
 * @author Raod
 * @date 2021-03-18 12:13:15.591309400
 * @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
 @Data
 public class DataSetTransform extends GaeaBaseEntity {
 public class DataSetTransform extends GaeaBaseEntity {
     @ApiModelProperty(value = "数据集编码")
     @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
 * @author Raod
 * @date 2021-03-18 12:09:57.728203200
 * @date 2021-03-18 12:09:57.728203200
 **/
 **/
-@TableName(value="gaea_report_data_source")
+@TableName(keepGlobalPrefix=true, value="gaea_report_data_source")
 @Data
 @Data
 public class DataSource extends GaeaBaseEntity {
 public class DataSource extends GaeaBaseEntity {
     @ApiModelProperty(value = "数据源编码")
     @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 org.springframework.web.client.RestTemplate;
 
 
 import javax.annotation.Resource;
 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.ArrayList;
+import java.util.Date;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
 
 
@@ -77,6 +75,7 @@ public class DataSourceServiceImpl implements DataSourceService {
         LambdaQueryWrapper<DataSource> wrapper = Wrappers.lambdaQuery();
         LambdaQueryWrapper<DataSource> wrapper = Wrappers.lambdaQuery();
         wrapper.select(DataSource::getSourceCode, DataSource::getSourceName)
         wrapper.select(DataSource::getSourceCode, DataSource::getSourceName)
                 .eq(DataSource::getEnableFlag, Enabled.YES.getValue());
                 .eq(DataSource::getEnableFlag, Enabled.YES.getValue());
+        wrapper.orderByDesc(DataSource::getUpdateTime);
         return dataSourceMapper.selectList(wrapper);
         return dataSourceMapper.selectList(wrapper);
     }
     }
 
 
@@ -243,7 +242,9 @@ public class DataSourceServiceImpl implements DataSourceService {
                 columns.forEach(t -> {
                 columns.forEach(t -> {
                     try {
                     try {
                         Object value = rs.getObject(t);
                         Object value = rs.getObject(t);
-                        jo.put(t, value);
+                        //数据类型转换
+                        Object result = dealResult(value);
+                        jo.put(t, result);
                     } catch (SQLException throwable) {
                     } catch (SQLException throwable) {
                         log.error("error",throwable);
                         log.error("error",throwable);
                         throw BusinessExceptionBuilder.build(ResponseCode.EXECUTE_SQL_ERROR, throwable.getMessage());
                         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 执行获取数据
      * 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
  * @author lr
  * @since 2021-02-23 10:01:02
  * @since 2021-02-23 10:01:02
  */
  */
-@TableName("gaea_dict")
+@TableName(keepGlobalPrefix=true, value = "gaea_dict")
 public class GaeaDict extends GaeaBaseEntity implements Serializable {
 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
  * @author lirui
  * @since 2021-03-09 15:52:41
  * @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)
 @UnionUniqueCode(group = BusinessConstant.DICT_ITEM_EXIST_GROUP, code = ResponseCode.DICT_ITEM_REPEAT)
 public class GaeaDictItem extends GaeaBaseEntity implements Serializable {
 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
  * @author peiyanni
  * @since 2021-02-18 14:48:20
  * @since 2021-02-18 14:48:20
  */
  */
-@TableName("gaea_file")
+@TableName(keepGlobalPrefix=true, value = "gaea_file")
 @Data
 @Data
 public class GaeaFile extends GaeaBaseEntity implements Serializable {
 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 newFileName = fileId + suffixName;
             // 本地文件保存路径
             // 本地文件保存路径
             String filePath = dictPath + newFileName;
             String filePath = dictPath + newFileName;
-            String urlPath = fileDownloadPath + java.io.File.separator + fileId;
+            String urlPath = fileDownloadPath + "/" + fileId;
 
 
             GaeaFile gaeaFile = new GaeaFile();
             GaeaFile gaeaFile = new GaeaFile();
             gaeaFile.setFilePath(filePath);
             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
  * @author chenkening
  * @date 2021/3/26 10:20
  * @date 2021/3/26 10:20
  */
  */
-@TableName(value="gaea_report")
+@TableName(keepGlobalPrefix=true, value="gaea_report")
 @Data
 @Data
 public class Report extends GaeaBaseEntity {
 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;
 import org.springframework.stereotype.Service;
 
 
 /**
 /**
- * TODO
  *
  *
  * @author chenkening
  * @author chenkening
  * @date 2021/3/26 10:35
  * @date 2021/3/26 10:35
@@ -31,11 +30,12 @@ public class ReportServiceImpl implements ReportService {
     @Override
     @Override
     public void delReport(ReportDto reportDto) {
     public void delReport(ReportDto reportDto) {
         deleteById(reportDto.getId());
         deleteById(reportDto.getId());
+        //删除gaea_report_excel、gaea_report_dashboard、gaea_report_dashboard_widget
+        //...
     }
     }
 
 
     @Override
     @Override
     public void processBeforeOperation(Report entity, BaseOperationEnum operationEnum) throws BusinessException {
     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")
     @ApiModelProperty(value = "0--未删除 1--已删除 DIC_NAME=DELETE_FLAG")
     private Integer deleteFlag;
     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;
 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.anji.plus.gaea.curd.entity.GaeaBaseEntity;
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.baomidou.mybatisplus.annotation.TableName;
-import javax.validation.constraints.*;
+import lombok.Data;
+
 import java.util.Date;
 import java.util.Date;
 /**
 /**
 * @description 报表分享 entity
 * @description 报表分享 entity
 * @author Raod
 * @author Raod
 * @date 2021-08-18 13:37:26.663
 * @date 2021-08-18 13:37:26.663
 **/
 **/
-@TableName(value="gaea_report_share")
+@TableName(keepGlobalPrefix=true, value="gaea_report_share")
 @Data
 @Data
 public class ReportShare extends GaeaBaseEntity {
 public class ReportShare extends GaeaBaseEntity {
     /** 分享编码,系统生成,默认UUID */
     /** 分享编码,系统生成,默认UUID */
@@ -39,5 +39,12 @@ public class ReportShare extends GaeaBaseEntity {
     /** 0--未删除 1--已删除 DIC_NAME=DELETE_FLAG */
     /** 0--未删除 1--已删除 DIC_NAME=DELETE_FLAG */
     private Integer deleteFlag;
     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.modules.reportshare.service.ReportShareService;
 import com.anjiplus.template.gaea.business.util.DateUtil;
 import com.anjiplus.template.gaea.business.util.DateUtil;
 import com.anjiplus.template.gaea.business.util.JwtUtil;
 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.anjiplus.template.gaea.business.util.UuidUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
@@ -51,12 +53,18 @@ public class ReportShareServiceImpl implements ReportShareService {
 
 
     @Override
     @Override
     public ReportShareDto insertShare(ReportShareDto dto) {
     public ReportShareDto insertShare(ReportShareDto dto) {
+        //设置分享码
+        if (dto.isSharePasswordFlag()) {
+            dto.setSharePassword(UuidUtil.getRandomPwd(4));
+        }
+
         ReportShareDto reportShareDto = new ReportShareDto();
         ReportShareDto reportShareDto = new ReportShareDto();
         ReportShare entity = new ReportShare();
         ReportShare entity = new ReportShare();
         BeanUtils.copyProperties(dto, entity);
         BeanUtils.copyProperties(dto, entity);
         insert(entity);
         insert(entity);
         //将分享链接返回
         //将分享链接返回
         reportShareDto.setShareUrl(entity.getShareUrl());
         reportShareDto.setShareUrl(entity.getShareUrl());
+        reportShareDto.setSharePassword(dto.getSharePassword());
         return reportShareDto;
         return reportShareDto;
     }
     }
 
 
@@ -69,6 +77,12 @@ public class ReportShareServiceImpl implements ReportShareService {
         if (null == reportShare) {
         if (null == reportShare) {
             throw BusinessExceptionBuilder.build(ResponseCode.REPORT_SHARE_LINK_INVALID);
             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;
         return reportShare;
     }
     }
 
 
@@ -101,7 +115,8 @@ public class ReportShareServiceImpl implements ReportShareService {
         } else {
         } else {
             entity.setShareUrl(entity.getShareUrl() + SHARE_FLAG + shareCode);
             entity.setShareUrl(entity.getShareUrl() + SHARE_FLAG + shareCode);
         }
         }
+
         entity.setShareValidTime(DateUtil.getFutureDateTmdHms(entity.getShareValidType()));
         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.algorithms.Algorithm;
 import com.auth0.jwt.interfaces.Claim;
 import com.auth0.jwt.interfaces.Claim;
 import com.auth0.jwt.interfaces.DecodedJWT;
 import com.auth0.jwt.interfaces.DecodedJWT;
+import org.apache.commons.lang3.StringUtils;
 
 
 import java.util.Date;
 import java.util.Date;
 import java.util.Map;
 import java.util.Map;
@@ -19,11 +20,16 @@ public class JwtUtil {
     private static final String JWT_SECRET = "aj-report";
     private static final String JWT_SECRET = "aj-report";
 
 
     public static String createToken(String reportCode, String shareCode, Date expires) {
     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()
         String token = JWT.create()
                 .withIssuedAt(new Date())
                 .withIssuedAt(new Date())
                 .withExpiresAt(expires)
                 .withExpiresAt(expires)
                 .withClaim("reportCode", reportCode)
                 .withClaim("reportCode", reportCode)
                 .withClaim("shareCode", shareCode)
                 .withClaim("shareCode", shareCode)
+                .withClaim("sharePassword", password)
                 .sign(Algorithm.HMAC256(JWT_SECRET));
                 .sign(Algorithm.HMAC256(JWT_SECRET));
         return token;
         return token;
     }
     }
@@ -55,4 +61,15 @@ public class JwtUtil {
         return claim.asString();
         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;
 package com.anjiplus.template.gaea.business.util;
 
 
+import java.security.SecureRandom;
 import java.util.UUID;
 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() {
     public static String generateUuid() {
         return UUID.randomUUID().toString().replace("-", "");
         return UUID.randomUUID().toString().replace("-", "");
@@ -38,7 +57,9 @@ public class UuidUtil {
 
 
     public static void main(String[] args) {
     public static void main(String[] args) {
         for (int i = 0; i < 100; i++) {
         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 # 数据库服务宕机自动重连机制
     breakAfterAcquireFailure: true # 数据库服务宕机自动重连机制
     timeBetweenConnectErrorMillis: 300000 # 连接出错后重试时间间隔
     timeBetweenConnectErrorMillis: 300000 # 连接出错后重试时间间隔
   flyway:
   flyway:
+    enabled: true    #是否开启flyway,默认true.
     baseline-on-migrate: true
     baseline-on-migrate: true
     #数据库连接配置
     #数据库连接配置
     url: ${spring.datasource.url}
     url: ${spring.datasource.url}
@@ -48,7 +49,7 @@ spring:
 
 
 mybatis-plus:
 mybatis-plus:
   configuration:
   configuration:
-    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启sql打印
     call-setters-on-nulls: true
     call-setters-on-nulls: true
   mapperLocations:
   mapperLocations:
     - classpath*:/mapper/**/*.xml
     - 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>

Plik diff jest za duży
+ 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, {
 module.exports = merge(prodEnv, {
   NODE_ENV: '"development"',
   NODE_ENV: '"development"',
   BASE_API: '"http://127.0.0.1:9095"'
   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 charset="utf-8">
   <meta name="viewport" content="width=device-width,initial-scale=1.0">
   <meta name="viewport" content="width=device-width,initial-scale=1.0">
   <title>AJ-Report</title>
   <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>
 </head>
 
 
 <body>
 <body>

+ 2 - 0
report-ui/package.json

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

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

@@ -1,8 +1,8 @@
 @font-face {
 @font-face {
   font-family: "iconfont"; /* Project id 1513211 */
   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 {
 .iconfont {
@@ -488,4 +488,3 @@
 .iconjiantou-copy-copy:before {
 .iconjiantou-copy-copy:before {
   content: "\e654";
   content: "\e654";
 }
 }
-

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

@@ -1,34 +1,111 @@
 <template>
 <template>
   <div :class="[hasTreeFieldInQueryForm ? 'page-container' : 'app-container']">
   <div :class="[hasTreeFieldInQueryForm ? 'page-container' : 'app-container']">
     <div v-if="hasTreeFieldInQueryForm" class="left-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>
     <div class="right-container">
     <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-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-form-item>
           </el-col>
           </el-col>
 
 
           <el-col :span="6" style="text-align: center">
           <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>
             <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>
             </a>
           </el-col>
           </el-col>
         </el-row>
         </el-row>
@@ -37,17 +114,50 @@
 
 
       <!-- 批量操作 -->
       <!-- 批量操作 -->
       <slot name="buttonLeftOnTable" />
       <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 fixed type="selection" width="50" align="center" />
         <!--隐藏列-->
         <!--隐藏列-->
         <el-table-column v-if="tableExpandColumns.length > 0" type="expand">
         <el-table-column v-if="tableExpandColumns.length > 0" type="expand">
           <template slot-scope="scope">
           <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 class="titel"> {{ item.label }}: </span>
               <span>{{ scope.row[item.field] }}</span>
               <span>{{ scope.row[item.field] }}</span>
             </p>
             </p>
@@ -56,28 +166,65 @@
         <!--序号-->
         <!--序号-->
         <el-table-column label="序号" min-width="50" align="center">
         <el-table-column label="序号" min-width="50" align="center">
           <template slot-scope="scope">
           <template slot-scope="scope">
-            {{ queryParams.pageSize * (queryParams.pageNumber - 1) + scope.$index + 1 }}
+            {{
+              queryParams.pageSize * (queryParams.pageNumber - 1) +
+                scope.$index +
+                1
+            }}
           </template>
           </template>
         </el-table-column>
         </el-table-column>
 
 
         <template v-for="item in option.columns">
         <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">
             <template slot-scope="scope">
               <div v-if="item.columnType == 'imgPreview'">
               <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>
               <div v-else>
               <div v-else>
                 <span v-if="item.inputType == 'switch' && !item.colorStyle">
                 <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>
                 <!-- 带单位 -->
                 <!-- 带单位 -->
-                <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上-->
                 <!--表格 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">
                 <div v-if="!item.custom">
                   是第一列数据 && 需要高亮字段不为false 高亮并且可以点击
                   是第一列数据 && 需要高亮字段不为false 高亮并且可以点击
@@ -92,18 +239,76 @@
           </el-table-column>
           </el-table-column>
         </template>
         </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">
           <template slot-scope="scope">
             <slot name="edit" :msg="scope.row" />
             <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">
             <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-menu slot="dropdown">
                 <el-dropdown-item class="clearfix">
                 <el-dropdown-item class="clearfix">
                   <slot name="rowButton" :msg="scope.row" />
                   <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-item>
               </el-dropdown-menu>
               </el-dropdown-menu>
             </el-dropdown>
             </el-dropdown>
@@ -111,7 +316,17 @@
         </el-table-column>
         </el-table-column>
       </el-table>
       </el-table>
       <div class="pagination">
       <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>
         <div>
           <slot name="tableSelectionBtn" :selection="checkRecords" />
           <slot name="tableSelectionBtn" :selection="checkRecords" />
         </div>
         </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>
         <template v-slot:customCard>
           <slot name="cardInEditPage" />
           <slot name="cardInEditPage" />
         </template>
         </template>
@@ -132,13 +354,13 @@
   </div>
   </div>
 </template>
 </template>
 <script>
 <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 {
 export default {
   components: {
   components: {
     EditDialog,
     EditDialog,
-    AnjiTree,
+    AnjiTree
   },
   },
   props: {
   props: {
     option: {
     option: {
@@ -153,14 +375,14 @@ export default {
             query: {},
             query: {},
             edit: {},
             edit: {},
             delete: {},
             delete: {},
-            add: {},
+            add: {}
           },
           },
           // 表格列
           // 表格列
           columns: [],
           columns: [],
-          queryFormChange: (fileName, val) => {},
-        }
-      },
-    },
+          queryFormChange: (fileName, val) => {}
+        };
+      }
+    }
   },
   },
   data() {
   data() {
     return {
     return {
@@ -169,8 +391,8 @@ export default {
         showMoreSearch: false, // 是否展开更多搜索条件
         showMoreSearch: false, // 是否展开更多搜索条件
         pageNumber: 1,
         pageNumber: 1,
         pageSize: 10,
         pageSize: 10,
-        order: '',
-        sort: '',
+        order: "",
+        sort: ""
       },
       },
 
 
       checkRecords: [], // 表格中当前选中的记录
       checkRecords: [], // 表格中当前选中的记录
@@ -180,73 +402,73 @@ export default {
       // 编辑详情弹框
       // 编辑详情弹框
       editDialogOpen: false, // 新建时主动打开编辑弹框
       editDialogOpen: false, // 新建时主动打开编辑弹框
       editDialogRowData: {}, // 编辑时的主键
       editDialogRowData: {}, // 编辑时的主键
-      editDialogModelType: 'view', // 编辑 查看
+      editDialogModelType: "view", // 编辑 查看
 
 
-      hasRowCustomButton: false, // 除了编辑删除外,还有自定义的行按钮
-    }
+      hasRowCustomButton: false // 除了编辑删除外,还有自定义的行按钮
+    };
   },
   },
   computed: {
   computed: {
     // 左侧树形查询条件
     // 左侧树形查询条件
     queryFormTreeField() {
     queryFormTreeField() {
       var treeField = this.option.queryFormFields.find(
       var treeField = this.option.queryFormFields.find(
-        (item) => item['inputType'] == 'anji-tree'
-      )
-      return treeField
+        item => item["inputType"] == "anji-tree"
+      );
+      return treeField;
     },
     },
     // 查询条件里是否有树形控件
     // 查询条件里是否有树形控件
     hasTreeFieldInQueryForm() {
     hasTreeFieldInQueryForm() {
-      return this.isNotBlank(this.queryFormTreeField)
+      return this.isNotBlank(this.queryFormTreeField);
     },
     },
     // 不包含树形控件的查询条件
     // 不包含树形控件的查询条件
     queryFormFieldExcludeTree() {
     queryFormFieldExcludeTree() {
       var treeFields = this.option.queryFormFields.filter(
       var treeFields = this.option.queryFormFields.filter(
-        (item) => item['inputType'] != 'anji-tree'
-      )
-      return treeFields
+        item => item["inputType"] != "anji-tree"
+      );
+      return treeFields;
     },
     },
     // 主键的列名
     // 主键的列名
     primaryKeyFieldName() {
     primaryKeyFieldName() {
       var primaryKey = this.option.columns.find(
       var primaryKey = this.option.columns.find(
-        (item) => item['primaryKey'] == true
-      )
+        item => item["primaryKey"] == true
+      );
       if (primaryKey != null) {
       if (primaryKey != null) {
-        return primaryKey['field']
+        return primaryKey["field"];
       } else {
       } else {
-        return null
+        return null;
         console.warn(
         console.warn(
-          '在columns中查找primaryKey=true失败,会导致查询详情和删除失败'
-        )
+          "在columns中查找primaryKey=true失败,会导致查询详情和删除失败"
+        );
       }
       }
     },
     },
 
 
     // 表格中可展开的列
     // 表格中可展开的列
     tableExpandColumns() {
     tableExpandColumns() {
       var expandColumns = this.option.columns.filter(
       var expandColumns = this.option.columns.filter(
-        (item) => item['columnType'] == 'expand'
-      )
-      return expandColumns
+        item => item["columnType"] == "expand"
+      );
+      return expandColumns;
     },
     },
 
 
     // 是否可以批量删除
     // 是否可以批量删除
     disableBatchDelete() {
     disableBatchDelete() {
-      return this.checkRecords.length <= 0
-    },
+      return this.checkRecords.length <= 0;
+    }
   },
   },
   created() {
   created() {
     // 为查询框中所有input加上默认值
     // 为查询框中所有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() {
   mounted() {
-    if (this.$scopedSlots['rowButton'] != null) {
-      this.hasRowCustomButton = true
+    if (this.$scopedSlots["rowButton"] != null) {
+      this.hasRowCustomButton = true;
     } else {
     } else {
-      this.hasRowCustomButton = false
+      this.hasRowCustomButton = false;
     }
     }
   },
   },
   methods: {
   methods: {
@@ -254,9 +476,9 @@ export default {
       // console.log(item)
       // console.log(item)
 
 
       if (item.span != null) {
       if (item.span != null) {
-        return item.span
+        return item.span;
       } else {
       } else {
-        return 6
+        return 6;
       }
       }
       // let rowLength = this.option.queryFormFields.length;
       // let rowLength = this.option.queryFormFields.length;
       // console.log(rowLength, "ss")
       // console.log(rowLength, "ss")
@@ -272,169 +494,169 @@ export default {
     },
     },
     // 切换更多搜索条件
     // 切换更多搜索条件
     handleToggleMoreSearch() {
     handleToggleMoreSearch() {
-      this.queryParams.showMoreSearch = !this.queryParams.showMoreSearch
+      this.queryParams.showMoreSearch = !this.queryParams.showMoreSearch;
     },
     },
     // 列上排序切换
     // 列上排序切换
     handleSortChange(column) {
     handleSortChange(column) {
       // {column: {…}, prop: "orgCode", order: "ascending"}
       // {column: {…}, prop: "orgCode", order: "ascending"}
       if (column == null || column.prop == null) {
       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) {
     handleQueryForm(from) {
       // 如果是点查询按钮,把树的查询属性去掉
       // 如果是点查询按钮,把树的查询属性去掉
-      if (from == 'query') {
+      if (from == "query") {
         if (this.hasTreeFieldInQueryForm) {
         if (this.hasTreeFieldInQueryForm) {
-          delete this.queryParams[this.queryFormTreeField.field]
+          delete this.queryParams[this.queryFormTreeField.field];
         }
         }
       }
       }
       // 如果是点树查询,把查询区里的属性去掉
       // 如果是点树查询,把查询区里的属性去掉
-      if (from == 'tree') {
+      if (from == "tree") {
         if (this.hasTreeFieldInQueryForm) {
         if (this.hasTreeFieldInQueryForm) {
-          var treeVal = this.queryParams[this.queryFormTreeField.field]
+          var treeVal = this.queryParams[this.queryFormTreeField.field];
           this.queryParams = {
           this.queryParams = {
             pageNumber: 1,
             pageNumber: 1,
-            pageSize: 10,
-          }
-          this.queryParams[this.queryFormTreeField.field] = treeVal
+            pageSize: 10
+          };
+          this.queryParams[this.queryFormTreeField.field] = treeVal;
         }
         }
       }
       }
       // 默认的排序
       // 默认的排序
       if (
       if (
-        this.isBlank(this.queryParams['order']) &&
+        this.isBlank(this.queryParams["order"]) &&
         this.isNotBlank(this.option.buttons.query.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() {
     async handleQueryPageList() {
-      var params = this.queryParams
+      var params = this.queryParams;
       // 将特殊参数值urlcode处理 var params = this.urlEncodeObject(this.queryParams, 'order,sort')
       // 将特殊参数值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() {
     handleResetForm() {
       this.queryParams = {
       this.queryParams = {
         pageNumber: 1,
         pageNumber: 1,
-        pageSize: 10,
-      }
+        pageSize: 10
+      };
       // this.$refs['queryForm'].resetFields()
       // this.$refs['queryForm'].resetFields()
       // this.records = []
       // this.records = []
       // this.total = 0
       // this.total = 0
     },
     },
     // 树形查询条件点击回调
     // 树形查询条件点击回调
     handleTreeNodeCheck() {
     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++) {
       for (var i = 0; i < this.option.columns.length; i++) {
-        var item = this.option.columns[i]
+        var item = this.option.columns[i];
         if (
         if (
-          item['editField'] == treeFieldName ||
-          item['field'] == treeFieldName
+          item["editField"] == treeFieldName ||
+          item["field"] == treeFieldName
         ) {
         ) {
           this.$set(
           this.$set(
             this.option.columns[i],
             this.option.columns[i],
-            'defaultValue',
+            "defaultValue",
             this.queryParams[treeFieldName]
             this.queryParams[treeFieldName]
-          )
-          break
+          );
+          break;
         }
         }
       }
       }
     },
     },
     // 编辑和查看操作
     // 编辑和查看操作
     handleOpenEditView(modelType, row) {
     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 = {
       const obj = {
         type: modelType,
         type: modelType,
-        value: row,
-      }
-      this.$emit('handleCustomValue', obj)
+        value: row
+      };
+      this.$emit("handleCustomValue", obj);
     },
     },
     // 弹框被关闭时的回调事件
     // 弹框被关闭时的回调事件
     editDialogClosedEvent(value) {
     editDialogClosedEvent(value) {
       // 把列表页中弹框打开标记改成已关闭
       // 把列表页中弹框打开标记改成已关闭
-      this.editDialogOpen = false
+      this.editDialogOpen = false;
       // 关闭弹出框时,如果有树,刷新下
       // 关闭弹出框时,如果有树,刷新下
       if (
       if (
         this.hasTreeFieldInQueryForm &&
         this.hasTreeFieldInQueryForm &&
         this.$refs.queryFormTree != null &&
         this.$refs.queryFormTree != null &&
         !value
         !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) {
     handleDeleteBatch(row) {
-      var ids = []
+      var ids = [];
       if (row != null) {
       if (row != null) {
-        ids.push(row[this.primaryKeyFieldName]) // 删除指定的行
+        ids.push(row[this.primaryKeyFieldName]); // 删除指定的行
       } else {
       } 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(() => {
         .then(() => {
-          this.option.buttons.delete.api(ids).then((res) => {
+          this.option.buttons.delete.api(ids).then(res => {
             // {code: "200", message: "操作成功", data: true}
             // {code: "200", message: "操作成功", data: true}
-            this.checkRecords = []
+            this.checkRecords = [];
             // 关闭弹出框时,如果有树,刷新下
             // 关闭弹出框时,如果有树,刷新下
             if (
             if (
               this.hasTreeFieldInQueryForm &&
               this.hasTreeFieldInQueryForm &&
               this.$refs.queryFormTree != null
               this.$refs.queryFormTree != null
             ) {
             ) {
-              this.$refs.queryFormTree.queryData()
+              this.$refs.queryFormTree.queryData();
             }
             }
-            this.handleQueryPageList()
-          })
-        })
-        .catch((e) => {
-          e
+            this.handleQueryPageList();
+          });
         })
         })
+        .catch(e => {
+          e;
+        });
     },
     },
 
 
     // 选择项改变时
     // 选择项改变时
     handleSelectionChange(val) {
     handleSelectionChange(val) {
-      this.checkRecords = val
+      this.checkRecords = val;
     },
     },
     // 页码改变
     // 页码改变
     handleCurrentChange(pageNumber) {
     handleCurrentChange(pageNumber) {
-      this.queryParams.pageNumber = pageNumber
-      this.handleQueryPageList()
+      this.queryParams.pageNumber = pageNumber;
+      this.handleQueryPageList();
     },
     },
     // 每页size改变时
     // 每页size改变时
     handleSizeChange(val) {
     handleSizeChange(val) {
-      this.queryParams.pageNumber = 1
-      this.queryParams.pageSize = val
-      this.handleQueryPageList()
+      this.queryParams.pageNumber = 1;
+      this.queryParams.pageSize = val;
+      this.handleQueryPageList();
     },
     },
     // table列文件缩略图
     // table列文件缩略图
     thumbnailUrl(row, field) {
     thumbnailUrl(row, field) {
@@ -453,66 +675,66 @@ export default {
     // 带单位的列,需要转换
     // 带单位的列,需要转换
     fieldLabel(columnConfig) {
     fieldLabel(columnConfig) {
       if (columnConfig == null) {
       if (columnConfig == null) {
-        return ''
+        return "";
       }
       }
       if (
       if (
-        columnConfig.inputType == 'anji-input' &&
+        columnConfig.inputType == "anji-input" &&
         columnConfig.anjiInput != null
         columnConfig.anjiInput != null
       ) {
       ) {
-        return `${columnConfig.label}(${columnConfig.anjiInput.unit})`
+        return `${columnConfig.label}(${columnConfig.anjiInput.unit})`;
       } else {
       } else {
-        return columnConfig.label
+        return columnConfig.label;
       }
       }
     },
     },
     // 带单位的输入框
     // 带单位的输入框
     fieldValueByAnjiInput(value, columnConfig) {
     fieldValueByAnjiInput(value, columnConfig) {
       if (columnConfig == null) {
       if (columnConfig == null) {
-        return value
+        return value;
       }
       }
       if (
       if (
-        columnConfig.inputType == 'anji-input' &&
+        columnConfig.inputType == "anji-input" &&
         columnConfig.anjiInput != null
         columnConfig.anjiInput != null
       ) {
       ) {
-        return value / columnConfig.anjiInput.conversion
+        return value / columnConfig.anjiInput.conversion;
       } else {
       } else {
-        return value
+        return value;
       }
       }
     },
     },
     // 带表格列格式化的值
     // 带表格列格式化的值
     fieldValueByRowRenderer(row, columnConfig) {
     fieldValueByRowRenderer(row, columnConfig) {
       if (
       if (
         columnConfig == null ||
         columnConfig == null ||
-        typeof columnConfig.fieldTableRowRenderer != 'function'
+        typeof columnConfig.fieldTableRowRenderer != "function"
       ) {
       ) {
-        return row[columnConfig.field]
+        return row[columnConfig.field];
       } else {
       } else {
-        return columnConfig.fieldTableRowRenderer(row)
+        return columnConfig.fieldTableRowRenderer(row);
       }
       }
     },
     },
     // 暴露给外部crud页面,回传saveForm的值
     // 暴露给外部crud页面,回传saveForm的值
     getMainEntity() {
     getMainEntity() {
-      return this.$refs.edit.getSaveForm()
+      return this.$refs.edit.getSaveForm();
     },
     },
     setMainEntity(object) {
     setMainEntity(object) {
-      this.$refs.edit.setSaveForm(object)
+      this.$refs.edit.setSaveForm(object);
     },
     },
     async switchChange(val, api) {
     async switchChange(val, api) {
       request({
       request({
         url: api.url,
         url: api.url,
-        method: 'put',
+        method: "put",
         headers: { noPrompt: false },
         headers: { noPrompt: false },
-        data: [val.id],
-      }).then((response) => {
-        this.handleQueryPageList()
-      })
+        data: [val.id]
+      }).then(response => {
+        this.handleQueryPageList();
+      });
     },
     },
     queryFormChange(fileName, fieldVal) {
     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>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
@@ -578,5 +800,9 @@ export default {
   display: flex;
   display: flex;
   flex-direction: row;
   flex-direction: row;
   justify-content: space-between;
   justify-content: space-between;
+  margin-top: 20px;
+}
+.anji_curd_table {
+  margin-top: 20px;
 }
 }
 </style>
 </style>

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

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

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

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

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików