瀏覽代碼

!68 update0.9.7
Merge pull request !68 from Foming/dev

Foming 3 年之前
父節點
當前提交
df1ba494b4
共有 100 個文件被更改,包括 5124 次插入871 次删除
  1. 2 1
      README.md
  2. 86 0
      build/index.js
  3. 217 0
      build/pom.xml
  4. 34 0
      deploy.sh
  5. 4 1
      doc/docs/guide/briefSupport.md
  6. 15 1
      doc/docs/guide/charts.md
  7. 1 0
      doc/docs/guide/quicklySeparate.md
  8. 9 3
      doc/docs/guide/quicklySource.md
  9. 二進制
      doc/docs/picture/charts/img_21.png
  10. 二進制
      doc/docs/picture/charts/img_22.png
  11. 二進制
      doc/docs/picture/charts/img_23.png
  12. 二進制
      doc/docs/picture/qiwei.png
  13. 7 0
      report-core/pom.xml
  14. 1 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/code/ResponseCode.java
  15. 5 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/constant/BusinessConstant.java
  16. 33 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/enums/ReportTypeEnum.java
  17. 76 57
      report-core/src/main/java/com/anjiplus/template/gaea/business/filter/TokenFilter.java
  18. 14 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasettransform/service/IGroovyHandler.java
  19. 65 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasettransform/service/impl/GroovyTransformServiceImpl.java
  20. 1 0
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasettransform/service/impl/JsTransformServiceImpl.java
  21. 9 6
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/service/impl/JdbcServiceImpl.java
  22. 6 9
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/report/controller/ReportController.java
  23. 6 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/report/service/ReportService.java
  24. 113 4
      report-core/src/main/java/com/anjiplus/template/gaea/business/modules/report/service/impl/ReportServiceImpl.java
  25. 11 1
      report-core/src/main/java/com/anjiplus/template/gaea/business/util/FileUtil.java
  26. 3 2
      report-core/src/main/resources/bootstrap.yml
  27. 1 0
      report-core/src/main/resources/db/migration/V1.0.15__update_javaBean.sql
  28. 5 0
      report-core/src/main/resources/db/migration/V1.0.16__update_admin_authority.sql
  29. 7 0
      report-core/src/main/resources/db/migration/V1.0.17__add_dict_coord.sql
  30. 16 0
      report-core/src/main/resources/db/migration/V1.0.18__update_role_authority.sql
  31. 1 0
      report-core/src/main/resources/i18n/messages_en_US.properties
  32. 1 0
      report-core/src/main/resources/i18n/messages_zh_CN.properties
  33. 18 0
      report-core/src/test/java/com/DemoGroovyHandler.java
  34. 58 0
      report-core/src/test/java/com/GroovyTest.java
  35. 1 1
      report-ui/src/api/dict-data.js
  36. 8 0
      report-ui/src/api/reportmanage.js
  37. 72 3
      report-ui/src/assets/iconfont/demo_index.html
  38. 15 3
      report-ui/src/assets/iconfont/iconfont.css
  39. 0 0
      report-ui/src/assets/iconfont/iconfont.js
  40. 21 0
      report-ui/src/assets/iconfont/iconfont.json
  41. 二進制
      report-ui/src/assets/iconfont/iconfont.ttf
  42. 二進制
      report-ui/src/assets/iconfont/iconfont.woff
  43. 二進制
      report-ui/src/assets/iconfont/iconfont.woff2
  44. 二進制
      report-ui/src/assets/images/home-logo.png
  45. 二進制
      report-ui/src/assets/images/login.jpg
  46. 18 0
      report-ui/src/assets/styles/anji.scss
  47. 237 0
      report-ui/src/assets/styles/screenDesigner.scss
  48. 1 1
      report-ui/src/components/AnjiPlus/anji-cascader.vue
  49. 1 1
      report-ui/src/components/AnjiPlus/anji-checkbox.vue
  50. 76 79
      report-ui/src/components/AnjiPlus/anji-crud/anji-crud.vue
  51. 2 2
      report-ui/src/components/AnjiPlus/anji-select.vue
  52. 24 102
      report-ui/src/components/AnjiPlus/anji-upload.vue
  53. 1 0
      report-ui/src/components/Dictionary/index.vue
  54. 3 2
      report-ui/src/mixins/common.js
  55. 27 3
      report-ui/src/mixins/queryform.js
  56. 14 0
      report-ui/src/router/index.js
  57. 3 3
      report-ui/src/utils/auth.js
  58. 102 0
      report-ui/src/utils/revoke.js
  59. 42 4
      report-ui/src/views/accessAuthority/index.vue
  60. 48 14
      report-ui/src/views/accessRole/index.vue
  61. 48 13
      report-ui/src/views/accessUser/index.vue
  62. 1 1
      report-ui/src/views/bigscreenDesigner/designer/components/customUpload.vue
  63. 0 0
      report-ui/src/views/bigscreenDesigner/designer/designer.js
  64. 86 9
      report-ui/src/views/bigscreenDesigner/designer/index.vue
  65. 2 83
      report-ui/src/views/bigscreenDesigner/designer/tools/configure/widget-barchart.js
  66. 0 82
      report-ui/src/views/bigscreenDesigner/designer/tools/configure/widget-gradient-barchart.js
  67. 688 0
      report-ui/src/views/bigscreenDesigner/designer/tools/configure/widget-heatmap.js
  68. 9 1
      report-ui/src/views/bigscreenDesigner/designer/tools/configure/widget-image.js
  69. 3 85
      report-ui/src/views/bigscreenDesigner/designer/tools/configure/widget-linechart.js
  70. 2 2
      report-ui/src/views/bigscreenDesigner/designer/tools/index.js
  71. 3 1
      report-ui/src/views/bigscreenDesigner/designer/tools/main.js
  72. 1 1
      report-ui/src/views/bigscreenDesigner/designer/widget/bar/widgetBarCompareChart.vue
  73. 1 1
      report-ui/src/views/bigscreenDesigner/designer/widget/bar/widgetBarStackChart.vue
  74. 1 46
      report-ui/src/views/bigscreenDesigner/designer/widget/bar/widgetBarchart.vue
  75. 1 46
      report-ui/src/views/bigscreenDesigner/designer/widget/bar/widgetGradientColorBarchart.vue
  76. 367 0
      report-ui/src/views/bigscreenDesigner/designer/widget/heatmap/widgetHeatmap.vue
  77. 1 1
      report-ui/src/views/bigscreenDesigner/designer/widget/line/widgetLineCompareChart.vue
  78. 1 1
      report-ui/src/views/bigscreenDesigner/designer/widget/line/widgetLineStackChart.vue
  79. 1 46
      report-ui/src/views/bigscreenDesigner/designer/widget/line/widgetLinechart.vue
  80. 3 1
      report-ui/src/views/bigscreenDesigner/designer/widget/temp.vue
  81. 9 10
      report-ui/src/views/bigscreenDesigner/designer/widget/widget.vue
  82. 3 18
      report-ui/src/views/bigscreenDesigner/designer/widget/widgetImage.vue
  83. 43 23
      report-ui/src/views/datasource/index.vue
  84. 41 3
      report-ui/src/views/dict/dict-item.vue
  85. 62 7
      report-ui/src/views/dict/index.vue
  86. 36 8
      report-ui/src/views/fileManagement/index.vue
  87. 1 1
      report-ui/src/views/layout/components/Sidebar/index.vue
  88. 1 1
      report-ui/src/views/login.vue
  89. 84 27
      report-ui/src/views/reportManage/index.vue
  90. 42 22
      report-ui/src/views/resultset/components/EditDataSet.vue
  91. 43 27
      report-ui/src/views/resultset/index.vue
  92. 71 0
      report-ui/src/views/screenDesigner/components/colorPicker.vue
  93. 106 0
      report-ui/src/views/screenDesigner/components/contentMenu.vue
  94. 191 0
      report-ui/src/views/screenDesigner/components/customColorComponents.vue
  95. 101 0
      report-ui/src/views/screenDesigner/components/customUpload.vue
  96. 178 0
      report-ui/src/views/screenDesigner/components/dynamicAddTable.vue
  97. 161 0
      report-ui/src/views/screenDesigner/components/dynamicComponents.vue
  98. 442 0
      report-ui/src/views/screenDesigner/components/dynamicForm.vue
  99. 90 0
      report-ui/src/views/screenDesigner/components/temp.vue
  100. 659 0
      report-ui/src/views/screenDesigner/config/barCharts/widget-bar-compare.js

+ 2 - 1
README.md

@@ -189,7 +189,8 @@ sql文件的目录在:report-core --> src --> main --> resources -- > db.migra
 - openJdk
 - Jdk 11
 - Mysql 8.0(8.0.23/26版本没有问题,8.0.21版本存在问题)
-  **[常见问题](https://ajreport.beliefteam.cn/report-doc/guide/question.html)**
+
+**[常见问题](https://ajreport.beliefteam.cn/report-doc/guide/question.html)**
 
 ## 商业授权
 

+ 86 - 0
build/index.js

@@ -0,0 +1,86 @@
+'use strict'
+// Template version: 1.2.6
+// see http://vuejs-templates.github.io/webpack for documentation.
+
+const path = require('path')
+
+module.exports = {
+  dev: {
+    // Paths
+    assetsSubDirectory: 'static',
+    assetsPublicPath: '/',
+    proxyTable: {},
+
+    // Various Dev Server settings
+    host: 'localhost', // can be overwritten by process.env.HOST
+    port: 9528, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
+    autoOpenBrowser: true,
+    errorOverlay: true,
+    notifyOnErrors: false,
+    poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
+
+    // Use Eslint Loader?
+    // If true, your code will be linted during bundling and
+    // linting errors and warnings will be shown in the console.
+    useEslint: true,
+    // If true, eslint errors and warnings will also be shown in the error overlay
+    // in the browser.
+    showEslintErrorsInOverlay: false,
+
+    /**
+     * Source Maps
+     */
+
+    // https://webpack.js.org/configuration/devtool/#development
+    devtool: 'cheap-source-map',
+
+    // CSS Sourcemaps off by default because relative paths are "buggy"
+    // with this option, according to the CSS-Loader README
+    // (https://github.com/webpack/css-loader#sourcemaps)
+    // In our experience, they generally work as expected,
+    // just be aware of this issue when enabling this option.
+    cssSourceMap: false
+  },
+
+  build: {
+    // Template for index.html
+    index: path.resolve(__dirname, '../dist/index.html'),
+
+    // Paths
+    assetsRoot: path.resolve(__dirname, '../dist'),
+    assetsSubDirectory: 'static',
+
+    /**
+     * You can set by youself according to actual condition
+     * You will need to set this if you plan to deploy your site under a sub path,
+     * for example GitHub pages. If you plan to deploy your site to https://foo.github.io/bar/,
+     * then assetsPublicPath should be set to "/bar/".
+     * In most cases please use '/' !!!
+     */
+    assetsPublicPath: '/report/',
+
+    /**
+     * Source Maps
+     */
+
+    productionSourceMap: false,
+    // https://webpack.js.org/configuration/devtool/#production
+    devtool: 'source-map',
+
+    // Gzip off by default as many popular static hosts such as
+    // Surge or Netlify already gzip all static assets for you.
+    // Before setting to `true`, make sure to:
+    // npm install --save-dev compression-webpack-plugin
+    productionGzip: false,
+    productionGzipExtensions: ['js', 'css'],
+
+    // Run the build command with an extra argument to
+    // View the bundle analyzer report after build finishes:
+    // `npm run build --report`
+    // Set to `true` or `false` to always turn it on or off
+    bundleAnalyzerReport: process.env.npm_config_report || false,
+
+    // `npm run build:prod --generate_report`
+    generateAnalyzerReport: process.env.npm_config_generate_report || false
+  }
+}

+ 217 - 0
build/pom.xml

@@ -0,0 +1,217 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.3.5.RELEASE</version>
+        <relativePath/>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.anji-plus.otwb</groupId>
+	<artifactId>product-report-starter</artifactId>
+    <version>1.2.0-SNAPSHOT</version>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <additionalparam>-Xdoclint:none</additionalparam>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <maven.test.skip>true</maven.test.skip>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.logging.log4j</groupId>
+                    <artifactId>log4j-to-slf4j</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-to-slf4j</artifactId>
+            <version>2.15.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-api</artifactId>
+            <version>2.15.0</version>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-cache</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-context</artifactId>
+            <version>2.2.6.RELEASE</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>com.anji-plus</groupId>
+            <artifactId>spring-boot-gaea</artifactId>
+            <version>2.0.3.RELEASE</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-data-redis</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>3.3.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid</artifactId>
+            <version>1.2.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.flywaydb</groupId>
+            <artifactId>flyway-core</artifactId>
+            <version>5.2.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>net.sf.ehcache</groupId>
+            <artifactId>ehcache</artifactId>
+            <version>2.10.6</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.10</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.10</version>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>com.itextpdf</groupId>
+            <artifactId>itextpdf</artifactId>
+            <version>5.5.13.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.itextpdf</groupId>
+            <artifactId>itext-asian</artifactId>
+            <version>5.2.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+            <version>4.1.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>4.1.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml-schemas</artifactId>
+            <version>4.1.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.codehaus.groovy</groupId>
+            <artifactId>groovy</artifactId>
+            <version>3.0.9</version>
+        </dependency>
+
+    </dependencies>
+	
+
+    <!-- 上传到公司私服, mvn clean deploy -Dmaven.test.skip=true -->
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.3</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-resources-plugin</artifactId>
+                <version>2.4</version>
+                <configuration>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <version>2.2.1</version>
+                <executions>
+                    <execution>
+                        <id>attach-sources</id>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <version>2.7</version>
+                <configuration>
+                    <updateReleaseInfo>true</updateReleaseInfo>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <distributionManagement>
+        <repository>
+            <id>nexus-releases</id>
+            <url>http://10.108.10.53:8081/repository/maven-releases/</url>
+        </repository>
+        <snapshotRepository>
+            <id>nexus-snapshots</id>
+            <url>http://10.108.10.53:8081/repository/maven-snapshots/</url>
+        </snapshotRepository>
+    </distributionManagement>
+</project>

+ 34 - 0
deploy.sh

@@ -0,0 +1,34 @@
+#!/bin/bash
+#该脚本为私有化脚本,打包成内含网页的product-report-starter放在内网私服
+
+#判断node.js mvn是否存在
+command -v npm >/dev/null 2>&1 || { echo >&2 "I require node.js v14.16.0+ but it's not installed.  Aborting."; sleep 5; exit 1; }
+command -v mvn >/dev/null 2>&1 || { echo >&2 "I require maven 3.5 + but it's not installed.  Aborting."; sleep 5; exit 1; }
+
+cd `dirname $0`
+BuildDir=`pwd` #工程根目录
+
+#重置pom和index.js
+cat $BuildDir/build/index.js > $BuildDir/report-ui/config/index.js
+cat $BuildDir/build/pom.xml > $BuildDir/report-core/pom.xml
+
+echo "build web"
+cd $BuildDir/report-ui
+rm -rf dist
+npm install >/dev/null 2>&1
+npm run build:prod
+
+echo "publish web to springboot src/main/resources/static"
+rm -rf $BuildDir/report-core/src/main/resources/db/migration
+rm -rf $BuildDir/report-core/src/main/resources/static
+mkdir -p $BuildDir/report-core/src/main/resources/static
+mv $BuildDir/report-ui/dist/* $BuildDir/report-core/src/main/resources/static/
+
+
+echo "build springboot"
+cd $BuildDir/report-core
+
+mvn clean deploy -DskipTests 
+
+rm -rf $BuildDir/report-core/src/main/resources/static
+git reset --hard

+ 4 - 1
doc/docs/guide/briefSupport.md

@@ -1,6 +1,9 @@
 **如有问题,请提交 [Issue](https://gitee.com/anji-plus/report/issues) <br>**
 
-**微信群: <br>**
+个人企业微信:加微信进群备注AJ-Report或者Report <br>
+如果不是为了进群,请直接说明来意,每天加群的都很多,企微也是我们自己的工作企微,会有很多消息会被刷下去,如果半天没有回复你,请发送多次 <br>
+
 ![weixin.png](../picture/weixin.jpg)
 
+
 #### 开源不易,劳烦各位star ☺

+ 15 - 1
doc/docs/guide/charts.md

@@ -205,4 +205,18 @@
 
 ### 数据格式
 
-和饼图、南丁格尔玫瑰图数据保持一致。<br>
+和饼图、南丁格尔玫瑰图数据保持一致。<br>
+
+## 热力图
+
+热力图是类似坐标轴一样的数据,当前的热力图数据集需要3个字段值,对应的字典是选择“X轴”,“Y轴”,“数值”,不明白可以看静态数据<br>
+![img21](../picture/charts/img_21.png) <br>
+
+注意:图设置功能中的最大最小值将会对热力图中的数值产生反应,主要根据设定的颜色来反应,数值越靠近最大值,颜色将更深 <br>
+![img22](../picture/charts/img_22.png) <br>
+
+### 数据格式
+
+![img23](../picture/charts/img_23.png) <br>
+
+**如有问题,请提交 [Issue](https://gitee.com/anji-plus/report/issues) <br>**

+ 1 - 0
doc/docs/guide/quicklySeparate.md

@@ -51,6 +51,7 @@ report-core --> src --> main --> resources --> bootstrap.yml <br>
 **打包之前如果系统用的不止mysql数据源,需要自己在pom文件中加入对应的数据库的驱动,登陆系统之后,数据源提示无驱动,则选择通用JDBC数据源,这里不做演示了** <br>
 使用 maven package <br>
 **注**:不要使用maven install <br>
+**注**:此方式不会打包 lib目录下的驱动,详情可查看 **数据源 扩展** <br>
 ![img10](../picture/quickly/img_10.png) <br>
 
 ### linux启动jar包

+ 9 - 3
doc/docs/guide/quicklySource.md

@@ -15,7 +15,9 @@ http://serverip:9095
 
 ## 编译环境
 
-请在Linux上先准备好maven、node.js、jdk
+请在Linux上先准备好maven、node.js、jdk <br>
+如果在Win10上部署,还需要下载一个 Git 软件,软件名就是 Git <br>
+以下内容需要特别注意的地方会有对应提示。<br>
 
 - [Apache Maven] 3.5 <br>
 - [Node.js] v14.16.0 <br>
@@ -34,6 +36,9 @@ git clone https://gitee.com/anji-plus/report.git <br>
 ![img_4.png](../picture/quickly/img_4.png) <br>
 编译完成后是放在当前目录下的build文件夹中:aj-report-xxxx.zip <br>
 
+**注:** 如果Win10部署的话,如图用git执行sh build.sh就行了。Linux就直接去report目录下执行sh build.sh就行。 <br>
+**特别注意:** 如果是Win10编译,那么几个启动脚本的格式则是win的格式,放linux上执行会报错的,反之放linux编译在win10启动也会报错,需要转格式。 <br>
+
 ## 修改mysql连接
 
 解压aj-report-xxxx.zip,找到bootstrap.yml <br>
@@ -56,11 +61,12 @@ linux启动: <br>
 aj-report-XXX --> bin --> start.sh <br>
 sh bin/start.sh <br>
 
-注意啊,如果你在linux打包,然后在win上执行,要转化start.bat文件的格式,反之也是一样。
+win10启动:<br>
+aj-report-XXX --> bin --> start.bat <br>
+双击start.bat启动 <br>
 
 ## 日志位置
 
-看到控制台提示“The AJ-Report started!”,说明report正在启动,可以看看日志以确定程序启动到哪里了。<br>
 日志的位置是:report-xxx/logs/aj-report.log <br>
 
 ## 访问

二進制
doc/docs/picture/charts/img_21.png


二進制
doc/docs/picture/charts/img_22.png


二進制
doc/docs/picture/charts/img_23.png


二進制
doc/docs/picture/qiwei.png


+ 7 - 0
report-core/pom.xml

@@ -151,6 +151,13 @@
             <artifactId>poi-ooxml-schemas</artifactId>
             <version>4.1.2</version>
         </dependency>
+
+        <dependency>
+            <groupId>org.codehaus.groovy</groupId>
+            <artifactId>groovy</artifactId>
+            <version>3.0.9</version>
+        </dependency>
+
     </dependencies>
 
     <developers>

+ 1 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/code/ResponseCode.java

@@ -131,6 +131,7 @@ public interface ResponseCode {
     String SET_CODE_ISEXIST = "4008";
     String SOURCE_CODE_ISEXIST = "4009";
     String CLASS_NOT_FOUND = "4010";
+    String EXECUTE_GROOVY_ERROR = "4011";
 
     String REPORT_SHARE_LINK_INVALID = "report.share.link.invalid";
 

+ 5 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/constant/BusinessConstant.java

@@ -11,6 +11,11 @@ public interface BusinessConstant {
     String RIGTH_BIG_BOAST = "}";
     String LEFT_MIDDLE_BOAST = "[";
     String RIGHT_MIDDLE_BOAST = "]";
+    String SLASH = "/";
+
+    String USER_GUEST = "guest";
+    String USER_ADMIN = "admin";
+
 
     /**
      * 字典项重复

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

@@ -0,0 +1,33 @@
+package com.anjiplus.template.gaea.business.enums;
+
+/**
+ * Created by raodeming on 2022/5/8.
+ */
+public enum ReportTypeEnum {
+
+    /**report_screen*/
+    report_screen("report_screen", "大屏报表"),
+    /**report_excel*/
+    report_excel("report_excel", "excel报表"),
+    ;
+
+    private String codeValue;
+    private String codeDesc;
+
+    ReportTypeEnum() {
+    }
+
+    private ReportTypeEnum(String codeValue, String codeDesc) {
+        this.codeValue = codeValue;
+        this.codeDesc = codeDesc;
+    }
+
+    public String getCodeValue() {
+        return this.codeValue;
+    }
+
+    public String getCodeDesc() {
+        return this.codeDesc;
+    }
+
+}

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

@@ -1,6 +1,5 @@
 package com.anjiplus.template.gaea.business.filter;
 
-
 import com.alibaba.fastjson.JSONObject;
 import com.anji.plus.gaea.bean.ResponseBean;
 import com.anji.plus.gaea.cache.CacheHelper;
@@ -14,10 +13,8 @@ import org.apache.http.entity.ContentType;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.core.annotation.Order;
-import org.springframework.http.HttpMethod;
 import org.springframework.stereotype.Component;
 import org.springframework.util.AntPathMatcher;
-import org.springframework.util.CollectionUtils;
 
 import javax.servlet.*;
 import javax.servlet.http.HttpServletRequest;
@@ -25,9 +22,9 @@ import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.regex.Pattern;
-import java.util.stream.Collectors;
 
 import static com.anji.plus.gaea.constant.GaeaConstant.URL_REPLACEMENT;
 
@@ -40,8 +37,9 @@ import static com.anji.plus.gaea.constant.GaeaConstant.URL_REPLACEMENT;
 @Order(Integer.MIN_VALUE + 99)
 public class TokenFilter implements Filter {
     private static final Pattern PATTERN = Pattern.compile(".*().*");
-    private static final String USER_GUEST = "guest";
-    private static final String SLASH = "/";
+
+    @Value("${server.servlet.context-path:/}")
+    private String SLASH = "/";
     private AntPathMatcher antPathMatcher = new AntPathMatcher();
 
     @Autowired
@@ -49,12 +47,13 @@ public class TokenFilter implements Filter {
     @Autowired
     private JwtBean jwtBean;
 
-    /** 跳过token验证和权限验证的url清单*/
-    @Value("#{'${customer.skip-authenticate-urls}'.split(',')}")
+    /**
+     * 跳过token验证和权限验证的url清单
+     */
+    @Value("#{'${customer.skip-authenticate-urls:}'.split(',')}")
     private List<String> skipAuthenticateUrls;
     private Pattern skipAuthenticatePattern;
 
-
     @Override
     public void init(FilterConfig filterConfig) throws ServletException {
         // 生成匹配正则,跳过token验证和权限验证的url
@@ -68,14 +67,26 @@ public class TokenFilter implements Filter {
         HttpServletResponse response = (HttpServletResponse) servletResponse;
         String uri = request.getRequestURI();
 
+        // TODO 暂时先不校验 直接放行
+        /*if (true) {
+            filterChain.doFilter(request, response);
+            return;
+        }*/
+
         //OPTIONS直接放行
         if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
             filterChain.doFilter(request, response);
             return;
         }
 
-        if (SLASH.equals(uri)) {
-            response.sendRedirect("/index.html");
+        // swagger相关的直接放行
+        if (uri.contains("swagger-ui") || uri.contains("swagger-resources")) {
+            filterChain.doFilter(request, response);
+            return;
+        }
+
+        if (SLASH.equals(uri) || SLASH.concat(BusinessConstant.SLASH).equals(uri)) {
+            response.sendRedirect(SLASH + "/index.html");
             return;
         }
 
@@ -119,59 +130,23 @@ public class TokenFilter implements Filter {
             error(response);
             return;
         }
-        if (!cacheHelper.exist(userKey)) {
-            error(response);
-            return;
-        }
-        String gaeaUserJsonStr = cacheHelper.stringGet(userKey);
 
-        //判断接口权限
-        //请求路径
-        String requestUrl = request.getRequestURI();
-        String methodValue = request.getMethod();
-        //请求方法+#+请求路径
-        String urlKey = methodValue + GaeaConstant.URL_SPLIT + requestUrl;
+        String gaeaUserJsonStr = cacheHelper.stringGet(userKey);
 
-        GaeaUserDto gaeaUserDto = JSONObject.parseObject(gaeaUserJsonStr, GaeaUserDto.class);
-        List<String> authorities = gaeaUserDto.getAuthorities();
-        Map<String, String> applicationNameAllAuthorities = cacheHelper.hashGet(BusinessConstant.GAEA_SECURITY_AUTHORITIES);
-        AtomicBoolean authFlag = new AtomicBoolean(false);
-        //查询当前请求是否在对应的权限里。即:先精确匹配(保证当前路由是需要精确匹配还是模糊匹配,防止精确匹配的被模糊匹配)
-        // 比如:/user/info和/user/**同时存在,/user/info,被/user/**匹配掉
-        if (applicationNameAllAuthorities.containsKey(urlKey)) {
-            String permissionCode = applicationNameAllAuthorities.get(urlKey);
-            if (authorities.contains(permissionCode)) {
-                authFlag.set(true);
-            }
-        } else {
-            List<String> collect = applicationNameAllAuthorities.keySet().stream()
-                    .filter(key -> StringUtils.isNotBlank(key) && key.contains(URL_REPLACEMENT))
-                    .filter(key -> antPathMatcher.match(key, urlKey)).collect(Collectors.toList());
-            if (CollectionUtils.isEmpty(collect)) {
-                authFlag.set(true);
-            }else {
-                collect.forEach(key -> {
-                    String permissionCode = applicationNameAllAuthorities.getOrDefault(key, "");
-                    if (authorities.contains(permissionCode)) {
-                        authFlag.set(true);
-                    }
-                });
+        // 判断用户是否有该url的权限
+        if (!BusinessConstant.USER_ADMIN.equals(loginName)) {
+            AtomicBoolean authorizeFlag = authorize(request, gaeaUserJsonStr);
+            if (!authorizeFlag.get()) {
+                authError(response);//无权限
+                return;
             }
         }
 
-        if (!authFlag.get()) {
-            //无权限
-            authError(response);
-            return;
-        }
-
-
-
-
         // 延长有效期
         cacheHelper.stringSetExpire(tokenKey, token, 3600);
         cacheHelper.stringSetExpire(userKey, gaeaUserJsonStr, 3600);
 
+
         //执行
         filterChain.doFilter(request, response);
     }
@@ -206,14 +181,58 @@ public class TokenFilter implements Filter {
         return Pattern.compile(patternString.toString());
     }
 
+    /** 判断用户是否有该接口的权限
+     * @return
+     */
+    private AtomicBoolean authorize(HttpServletRequest request, String gaeaUserJsonStr){
+
+        //判断接口权限
+        //请求路径
+        String requestUrl = request.getRequestURI();
+        if (!BusinessConstant.SLASH.equals(SLASH)) {
+            requestUrl = requestUrl.substring(SLASH.length());
+        }
+        String methodValue = request.getMethod();
+        //请求方法+#+请求路径
+        String path = methodValue + GaeaConstant.URL_SPLIT + requestUrl;
+
+        GaeaUserDto gaeaUserDto = JSONObject.parseObject(gaeaUserJsonStr, GaeaUserDto.class);
+        List<String> userAuthorities = gaeaUserDto.getAuthorities();
+        Map<String, String> authoritiesAllMap = cacheHelper.hashGet(BusinessConstant.GAEA_SECURITY_AUTHORITIES);
+
+        AtomicBoolean authFlag = new AtomicBoolean(false);
+
+        // 接口GET#/gaeaDictItem/pageList
+        if(authoritiesAllMap.containsKey(path)){
+            String permissionCode = authoritiesAllMap.get(path);
+            boolean flag = userAuthorities.contains(permissionCode);
+            authFlag.set(flag);
+            return authFlag;
+        }
+
+        // 接口GET#/accessUser/roleTree/**
+        Optional<String> optionalMatchKey = authoritiesAllMap.keySet().stream()
+                .filter(key -> StringUtils.isNotBlank(key) && key.contains(URL_REPLACEMENT))
+                .filter(key -> antPathMatcher.match(key, path)).findFirst();
+        if(optionalMatchKey.isPresent() == false){
+            authFlag.set(true);
+            return authFlag;
+        }
+        String authoritieKey = optionalMatchKey.get();
+        String needPermission = authoritiesAllMap.get(authoritieKey);
+        boolean flag = userAuthorities.contains(needPermission);
+        authFlag.set(flag);
+        return authFlag;
+    }
+
     private void error(HttpServletResponse response) throws IOException {
-        ResponseBean responseBean = ResponseBean.builder().code("50014").message("The Token has expired").build();
+        ResponseBean responseBean = ResponseBean.builder().code("50008").message("The Token has expired").build();
         response.setContentType(ContentType.APPLICATION_JSON.getMimeType());
         response.getWriter().print(JSONObject.toJSONString(responseBean));
     }
 
     private void authError(HttpServletResponse response) throws IOException {
-        ResponseBean responseBean = ResponseBean.builder().code("User.no.authority").message("没有权限").build();
+        ResponseBean responseBean = ResponseBean.builder().code("User.no.authority").message("no auth").build();
         response.setContentType(ContentType.APPLICATION_JSON.getMimeType());
         response.getWriter().print(JSONObject.toJSONString(responseBean));
     }

+ 14 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasettransform/service/IGroovyHandler.java

@@ -0,0 +1,14 @@
+package com.anjiplus.template.gaea.business.modules.datasettransform.service;
+
+import com.alibaba.fastjson.JSONObject;
+
+import java.util.List;
+
+/**
+ * @author: Raod
+ * @since: 2022-02-23
+ */
+public interface IGroovyHandler {
+
+    List<JSONObject> transform(List<JSONObject> data);
+}

+ 65 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasettransform/service/impl/GroovyTransformServiceImpl.java

@@ -0,0 +1,65 @@
+package com.anjiplus.template.gaea.business.modules.datasettransform.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.anji.plus.gaea.exception.BusinessExceptionBuilder;
+import com.anjiplus.template.gaea.business.code.ResponseCode;
+import com.anjiplus.template.gaea.business.modules.datasettransform.controller.dto.DataSetTransformDto;
+import com.anjiplus.template.gaea.business.modules.datasettransform.service.IGroovyHandler;
+import com.anjiplus.template.gaea.business.modules.datasettransform.service.TransformStrategy;
+import groovy.lang.GroovyClassLoader;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import javax.script.Invocable;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import java.util.List;
+
+/**
+ * Created by raodeming on 2021/3/23.
+ */
+@Component
+@Slf4j
+public class GroovyTransformServiceImpl implements TransformStrategy {
+
+    private GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
+
+    /**
+     * 数据清洗转换 类型
+     *
+     * @return
+     */
+    @Override
+    public String type() {
+        return "javaBean";
+    }
+
+    /***
+     * 清洗转换算法接口
+     * @param def
+     * @param data
+     * @return
+     */
+    @Override
+    public List<JSONObject> transform(DataSetTransformDto def, List<JSONObject> data) {
+        String transformScript = def.getTransformScript();
+        Class<?> clazz = groovyClassLoader.parseClass(transformScript);
+        if (clazz != null) {
+            try {
+                Object instance = clazz.newInstance();
+                if (instance!=null) {
+                    if (instance instanceof IGroovyHandler) {
+                        IGroovyHandler handler = (IGroovyHandler) instance;
+                        return handler.transform(data);
+                    } else {
+                        System.err.println("转换失败!");
+                    }
+                }
+            } catch (Exception e) {
+                log.info("执行javaBean异常", e);
+                throw BusinessExceptionBuilder.build(ResponseCode.EXECUTE_GROOVY_ERROR, e.getMessage());
+            }
+        }
+        return data;
+    }
+}

+ 1 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasettransform/service/impl/JsTransformServiceImpl.java

@@ -58,6 +58,7 @@ public class JsTransformServiceImpl implements TransformStrategy {
             }
 
         } catch (Exception ex) {
+            log.info("执行js异常", ex);
             throw BusinessExceptionBuilder.build(ResponseCode.EXECUTE_JS_ERROR, ex.getMessage());
         }
         return null;

+ 9 - 6
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/service/impl/JdbcServiceImpl.java

@@ -26,18 +26,21 @@ public class JdbcServiceImpl implements JdbcService {
     /**
      * 所有数据源的连接池存在map里
      */
-    static Map<Long, DruidDataSource> map = new ConcurrentHashMap<>();
+    private Map<Long, DruidDataSource> map = new ConcurrentHashMap<>();
+    private Object lock = new Object();
 
     public DruidDataSource getJdbcConnectionPool(DataSourceDto dataSource) {
         if (map.containsKey(dataSource.getId())) {
             return map.get(dataSource.getId());
         } else {
             try {
-                if (!map.containsKey(dataSource.getId())) {
-                    DruidDataSource pool = druidProperties.dataSource(dataSource.getJdbcUrl(),
-                            dataSource.getUsername(), dataSource.getPassword(), dataSource.getDriverName());
-                    map.put(dataSource.getId(), pool);
-                    log.info("创建连接池成功:{}", dataSource.getJdbcUrl());
+                synchronized (lock) {
+                    if (!map.containsKey(dataSource.getId())) {
+                        DruidDataSource pool = druidProperties.dataSource(dataSource.getJdbcUrl(),
+                                dataSource.getUsername(), dataSource.getPassword(), dataSource.getDriverName());
+                        map.put(dataSource.getId(), pool);
+                        log.info("创建连接池成功:{}", dataSource.getJdbcUrl());
+                    }
                 }
                 return map.get(dataSource.getId());
             } finally {

+ 6 - 9
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/report/controller/ReportController.java

@@ -11,10 +11,7 @@ import com.anjiplus.template.gaea.business.modules.report.dao.entity.Report;
 import com.anjiplus.template.gaea.business.modules.report.service.ReportService;
 import io.swagger.annotations.Api;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 /**
  * TODO
@@ -46,11 +43,11 @@ public class ReportController extends GaeaBaseController<ReportParam, Report, Re
         return new ReportDto();
     }
 
-    @DeleteMapping("/delReport")
-    @Permission(code = "delete", name = "删除")
-    @GaeaAuditLog(pageTitle = "删除")
-    public ResponseBean delReport(@RequestBody ReportDto reportDto) {
-        reportService.delReport(reportDto);
+    @GetMapping("/copy")
+    @Permission(code = "copy", name = "复制")
+    @GaeaAuditLog(pageTitle = "复制")
+    public ResponseBean copy(@RequestParam("reportId") Long reportId) {
+        reportService.copy(reportId);
         return ResponseBean.builder().build();
     }
 }

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

@@ -12,11 +12,16 @@ import com.anjiplus.template.gaea.business.modules.report.dao.entity.Report;
  */
 public interface ReportService extends GaeaBaseService<ReportParam, Report> {
 
-    void delReport(ReportDto reportDto);
 
     /**
      * 下载次数+1
      * @param reportCode
      */
     void downloadStatistics(String reportCode);
+
+    /**
+     * 复制大屏
+     * @param reportId
+     */
+    void copy(Long reportId);
 }

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

@@ -3,12 +3,26 @@ package com.anjiplus.template.gaea.business.modules.report.service.impl;
 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.GaeaBeanUtils;
+import com.anjiplus.template.gaea.business.enums.ReportTypeEnum;
+import com.anjiplus.template.gaea.business.modules.dashboard.dao.entity.ReportDashboard;
+import com.anjiplus.template.gaea.business.modules.dashboard.service.ReportDashboardService;
+import com.anjiplus.template.gaea.business.modules.dashboardwidget.dao.entity.ReportDashboardWidget;
+import com.anjiplus.template.gaea.business.modules.dashboardwidget.service.ReportDashboardWidgetService;
 import com.anjiplus.template.gaea.business.modules.report.controller.dto.ReportDto;
 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.report.service.ReportService;
+import com.anjiplus.template.gaea.business.modules.reportexcel.dao.entity.ReportExcel;
+import com.anjiplus.template.gaea.business.modules.reportexcel.service.ReportExcelService;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import java.util.Collections;
+import java.util.List;
 
 /**
  *
@@ -20,6 +34,12 @@ public class ReportServiceImpl implements ReportService {
 
     @Autowired
     private ReportMapper reportMapper;
+    @Autowired
+    private ReportDashboardService reportDashboardService;
+    @Autowired
+    private ReportDashboardWidgetService reportDashboardWidgetService;
+    @Autowired
+    private ReportExcelService reportExcelService;
 
     @Override
     public GaeaBaseMapper<Report> getMapper() {
@@ -28,10 +48,41 @@ public class ReportServiceImpl implements ReportService {
 
 
     @Override
-    public void delReport(ReportDto reportDto) {
-        deleteById(reportDto.getId());
-        //删除gaea_report_excel、gaea_report_dashboard、gaea_report_dashboard_widget
-        //...
+    public void processBatchBeforeOperation(List<Report> entities, BaseOperationEnum operationEnum) throws BusinessException {
+        ReportService.super.processBatchAfterOperation(entities, operationEnum);
+        switch (operationEnum) {
+            case DELETE_BATCH:
+                entities.forEach(report -> {
+                    Long id = report.getId();
+                    Report delReport = selectOne(id);
+                    if (null == delReport) {
+                        return;
+                    }
+                    String reportCode = delReport.getReportCode();
+                    String reportType = delReport.getReportType();
+                    switch (ReportTypeEnum.valueOf(reportType)) {
+                        case report_screen:
+                            LambdaQueryWrapper<ReportDashboard> reportDashboardLambdaQueryWrapper = Wrappers.lambdaQuery();
+                            reportDashboardLambdaQueryWrapper.eq(ReportDashboard::getReportCode, reportCode);
+                            reportDashboardService.delete(reportDashboardLambdaQueryWrapper);
+
+                            LambdaQueryWrapper<ReportDashboardWidget> reportDashboardWidgetLambdaQueryWrapper = Wrappers.lambdaQuery();
+                            reportDashboardWidgetLambdaQueryWrapper.eq(ReportDashboardWidget::getReportCode, reportCode);
+                            reportDashboardWidgetService.delete(reportDashboardWidgetLambdaQueryWrapper);
+
+                            break;
+                        case report_excel:
+                            LambdaQueryWrapper<ReportExcel> reportExcelLambdaQueryWrapper = Wrappers.lambdaQuery();
+                            reportExcelLambdaQueryWrapper.eq(ReportExcel::getReportCode, reportCode);
+                            reportExcelService.delete(reportExcelLambdaQueryWrapper);
+                            break;
+                        default:
+                    }
+                });
+                break;
+            default:
+
+        }
     }
 
     /**
@@ -55,6 +106,64 @@ public class ReportServiceImpl implements ReportService {
 
     }
 
+    @Override
+    public void copy(Long reportId) {
+        Report report = selectOne(reportId);
+        String reportCode = report.getReportCode();
+        Report copyReport = copyReport(report);
+        //复制主表数据
+        insert(copyReport);
+        String copyReportCode = copyReport.getReportCode();
+        String reportType = report.getReportType();
+        switch (ReportTypeEnum.valueOf(reportType)) {
+            case report_screen:
+                //查询看板
+                ReportDashboard reportDashboard = reportDashboardService.selectOne("report_code", reportCode);
+                if (null != reportDashboard) {
+                    reportDashboard.setId(null);
+                    reportDashboard.setReportCode(copyReportCode);
+                    reportDashboardService.insert(reportDashboard);
+                }
+
+                //查询组件
+                List<ReportDashboardWidget> reportDashboardWidgetList = reportDashboardWidgetService.list("report_code", reportCode);
+                if (!CollectionUtils.isEmpty(reportDashboardWidgetList)) {
+                    String finalCopyReportCode = copyReportCode;
+                    reportDashboardWidgetList.forEach(reportDashboardWidget -> {
+                        reportDashboardWidget.setId(null);
+                        reportDashboardWidget.setReportCode(finalCopyReportCode);
+                    });
+                    reportDashboardWidgetService.insertBatch(reportDashboardWidgetList);
+                }
+
+                break;
+            case report_excel:
+                ReportExcel reportExcel = reportExcelService.selectOne("report_code", reportCode);
+                if (null != reportExcel) {
+                    reportExcel.setId(null);
+                    reportExcel.setReportCode(copyReportCode);
+                    reportExcelService.insert(reportExcel);
+                }
+
+                break;
+            default:
+        }
+    }
+
+    private Report copyReport(Report report){
+        //复制主表数据
+        Report copyReport = new Report();
+        GaeaBeanUtils.copyAndFormatter(report, copyReport);
+        copyReport.setId(null);
+        String copyReportCode = copyReport.getReportCode().concat("_").concat(String.valueOf(System.currentTimeMillis()));
+        if (copyReportCode.length() >= 100) {
+            copyReportCode = copyReportCode.substring(0, 100);
+        }
+        copyReport.setReportCode(copyReportCode);
+        copyReport.setReportName(copyReport.getReportName().concat("_copy"));
+        return copyReport;
+    }
+
     @Override
     public void processBeforeOperation(Report entity, BaseOperationEnum operationEnum) throws BusinessException {
 

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

@@ -8,6 +8,7 @@ import org.springframework.web.multipart.MultipartFile;
 import java.io.*;
 import java.net.URL;
 import java.nio.channels.FileChannel;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.*;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.Enumeration;
@@ -116,9 +117,11 @@ public class FileUtil {
      */
     public static String readFile(File file) {
         BufferedReader reader = null;
+        InputStreamReader isr = null;
         StringBuilder sbf = new StringBuilder();
         try {
-            reader = new BufferedReader(new FileReader(file));
+            isr = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
+            reader = new BufferedReader(isr);
             String tempStr;
             while ((tempStr = reader.readLine()) != null) {
                 sbf.append(tempStr);
@@ -129,6 +132,13 @@ public class FileUtil {
             log.error("读文件失败", e);
             throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
         } finally {
+            if (null != isr) {
+                try {
+                    isr.close();
+                } catch (IOException e) {
+                    throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage());
+                }
+            }
             if (reader != null) {
                 try {
                     reader.close();

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

@@ -66,9 +66,10 @@ logging:
 customer:
   # 开发测试用本地文件,如果是生产,请考虑使用对象存储
   file:
-    #上传对应本地全路径,路径必须是真实存在的
+    #上传对应本地全路径,目录必须是真实存在的,注意 Win是 \ 且有盘符,linux是 / 无盘符
     dist-path: /app/disk/upload/
-    white-list: .png|.jpg|.gif|.icon|.pdf|.xlsx|.xls|.csv|.mp4|.avi
+    #dist-path: D:\Download
+    white-list: .png|.jpg|.jpeg|.gif|.icon|.pdf|.xlsx|.xls|.csv|.mp4|.avi
     excelSuffix: .xlsx|.xls|.csv
     #上传对应下载的下载链接路径 http://serverip:9095/file/download
     downloadPath: http://10.108.26.197:9095/file/download

+ 1 - 0
report-core/src/main/resources/db/migration/V1.0.15__update_javaBean.sql

@@ -0,0 +1 @@
+UPDATE `aj_report`.`gaea_dict_item` SET `dict_code` = 'TRANSFORM_TYPE', `item_name` = 'java脚本', `item_value` = 'javaBean', `item_extend` = NULL, `enabled` = 1, `locale` = 'zh', `remark` = NULL, `sort` = 2, `create_by` = 'admin', `create_time` = '2021-03-23 10:54:08', `update_by` = 'admin', `update_time` = '2021-03-23 10:54:08', `version` = 1 WHERE `id` = 151;

+ 5 - 0
report-core/src/main/resources/db/migration/V1.0.16__update_admin_authority.sql

@@ -0,0 +1,5 @@
+-- 补充admin对于execl表格权限
+
+INSERT INTO `aj_report`.`access_role_authority`(`role_code`,`target`,`action`) SELECT "root","excelManage","insert" FROM DUAL WHERE NOT EXISTS(SELECT `role_code`,`target`,`action` FROM `aj_report`.`access_role_authority` WHERE `role_code`="root" AND `target`="excelManage" AND `action`="insert");
+
+INSERT INTO `aj_report`.`access_role_authority`(`role_code`,`target`,`action`) SELECT "root","excelManage","update" FROM DUAL WHERE NOT EXISTS(SELECT `role_code`,`target`,`action` FROM `aj_report`.`access_role_authority` WHERE `role_code`="root" AND `target`="excelManage" AND `action`="update");

+ 7 - 0
report-core/src/main/resources/db/migration/V1.0.17__add_dict_coord.sql

@@ -0,0 +1,7 @@
+-- 新增坐标轴字典
+
+INSERT INTO `aj_report`.`gaea_dict`(`dict_name`, `dict_code`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('XY坐标属性', 'COORD_PROPERTIES', 'XY坐标属性', 'admin', NOW(), 'admin', NOW(), 1);
+
+INSERT INTO `aj_report`.`gaea_dict_item`(`dict_code`, `item_name`, `item_value`, `item_extend`, `enabled`, `locale`, `remark`, `sort`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('COORD_PROPERTIES', '数据', 'series', NULL, 1, 'zh', NULL, NULL, 'admin', NOW(), 'admin', NOW(), 1);
+INSERT INTO `aj_report`.`gaea_dict_item`(`dict_code`, `item_name`, `item_value`, `item_extend`, `enabled`, `locale`, `remark`, `sort`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('COORD_PROPERTIES', 'X轴', 'xAxis', NULL, 1, 'zh', NULL, NULL, 'admin', NOW(), 'admin', NOW(), 1);
+INSERT INTO `aj_report`.`gaea_dict_item`(`dict_code`, `item_name`, `item_value`, `item_extend`, `enabled`, `locale`, `remark`, `sort`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('COORD_PROPERTIES', 'Y轴', 'yAxis', NULL, 1, 'zh', NULL, NULL, 'admin', NOW(), 'admin', NOW(), 1);

+ 16 - 0
report-core/src/main/resources/db/migration/V1.0.18__update_role_authority.sql

@@ -0,0 +1,16 @@
+-- 角色权限调整
+-- access_authority
+REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('access', 'authorityManage', '权限管理', 'detail', '权限明细', 101, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1);
+REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('access', 'roleManage', '角色管理', 'detail', '角色明细', 105, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2021-07-17 20:41:46', 2);
+REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('access', 'userManage', '用户管理', 'detail', '用户明细', 110, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1);
+REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('report', 'datasourceManage', '数据源管理', 'detail', '数据源明细', 200, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1);
+REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('report', 'resultsetManage', '数据集管理', 'detail', '数据集明细', 204, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1);
+REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('report', 'reportManage', '报表管理', 'detail', '报表明细', 221, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1);
+REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('report', 'bigScreenManage', '大屏报表', 'detail', '大屏明细', 231, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1);
+REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('report', 'excelManage', '表格报表', 'detail', 'excel明细', 234, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1);
+REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('system', 'fileManage', '文件管理', 'detail', '文件明细', 300, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1);
+REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('system', 'dictManage', '数据字典', 'detail', '数据字典明细', 300, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1);
+REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('system', 'dictItemManage', '数据字典项', 'detail', '数据字典项明细', 300, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1);
+REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('report', 'bigScreenManage', '大屏报表', 'copy', '复制大屏', 236, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1);
+
+UPDATE `aj_report`.`access_authority` SET `parent_target` = 'report', `target` = 'bigScreenManage', `target_name` = '大屏报表', `action` = 'view', `action_name` = '查看大屏', `sort` = 232, `enable_flag` = 1, `delete_flag` = 0, `create_by` = 'admin', `create_time` = '2019-07-23 15:59:40', `update_by` = 'admin', `update_time` = '2019-07-23 15:59:40', `version` = 1 WHERE `parent_target` = 'report' AND `target` = 'bigScreenManage' AND `action` = 'view';

+ 1 - 0
report-core/src/main/resources/i18n/messages_en_US.properties

@@ -47,5 +47,6 @@ Component.load.check.error={0} Component not load
 4008=The set code does not allow duplication
 4009=The source code does not allow duplication
 4010=Can't auto find match driver class
+4011=execute javaBean error
 
 report.share.link.invalid=report share link invalid

+ 1 - 0
report-core/src/main/resources/i18n/messages_zh_CN.properties

@@ -48,6 +48,7 @@ Component.load.check.error={0}\u7EC4\u4EF6\u672A\u52A0\u8F7D
 4008=\u6570\u636E\u96C6\u7F16\u7801\u4E0D\u5141\u8BB8\u91CD\u590D
 4009=\u6570\u636E\u6E90\u7F16\u7801\u4E0D\u5141\u8BB8\u91CD\u590D
 4010=\u9A71\u52A8\u5305\u4E0D\u5B58\u5728
+4011=\u6267\u884CjavaBean\u5931\u8D25
 6001={0}
 
 7001=\u89E3\u6790\u5931\u8D25

+ 18 - 0
report-core/src/test/java/com/DemoGroovyHandler.java

@@ -0,0 +1,18 @@
+package com;
+
+import com.alibaba.fastjson.JSONObject;
+import com.anjiplus.template.gaea.business.modules.datasettransform.service.IGroovyHandler;
+
+import java.util.List;
+
+/**
+ * 建议在idea写好复制整个类到此处,位置report-core/src/test/java/com/DemoGroovyHandler.java
+ */
+public class DemoGroovyHandler implements IGroovyHandler {
+
+    @Override
+    public List<JSONObject> transform(List<JSONObject> data) {
+
+        return data;
+    }
+}

+ 58 - 0
report-core/src/test/java/com/GroovyTest.java

@@ -0,0 +1,58 @@
+package com;
+
+import com.alibaba.fastjson.JSONObject;
+import com.anjiplus.template.gaea.business.modules.datasettransform.service.IGroovyHandler;
+import groovy.lang.GroovyClassLoader;
+
+import java.util.List;
+
+/**
+ * @author: Raod
+ * @since: 2022-02-23
+ */
+public class GroovyTest {
+
+
+    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
+        // codeSource来自DemoGroovyHandler
+        String codeSource = "package com;\n" +
+                "\n" +
+                "import com.alibaba.fastjson.JSONObject;\n" +
+                "import com.anjiplus.template.gaea.business.modules.datasettransform.service.IGroovyHandler;\n" +
+                "\n" +
+                "import java.util.ArrayList;\n" +
+                "import java.util.List;\n" +
+                "\n" +
+                "/**\n" +
+                " * @author: Raod\n" +
+                " * @since: 2022-02-23\n" +
+                " */\n" +
+                "public class DemoGroovyHandler implements IGroovyHandler {\n" +
+                "\n" +
+                "    @Override\n" +
+                "    public List<JSONObject> transform(List<JSONObject> data) {\n" +
+                "        List<JSONObject> result = new ArrayList<>();\n" +
+                "        JSONObject jsonObject = new JSONObject();\n" +
+                "        jsonObject.put(\"test\", \"demo\");\n" +
+                "        result.add(jsonObject);\n" +
+                "        return result;\n" +
+                "    }\n" +
+                "}";
+        GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
+        Class<?> clazz = groovyClassLoader.parseClass(codeSource);
+        if (clazz != null) {
+            Object instance = clazz.newInstance();
+            if (instance!=null) {
+                if (instance instanceof IGroovyHandler) {
+                    IGroovyHandler handler = (IGroovyHandler) instance;
+                    List<JSONObject> transform = handler.transform(null);
+                    System.out.println(JSONObject.toJSONString(transform));
+
+                } else {
+                    System.err.println("转换失败!");
+                }
+            }
+        }
+    }
+
+}

+ 1 - 1
report-ui/src/api/dict-data.js

@@ -71,7 +71,7 @@ export function initDictToLocalstorage(callback) {
     }
 
     // 保存数据字典到localStorage
-    setStorageItem('gaeaDict', res.data)
+    setStorageItem('AJReportDict', res.data)
     if (callback != null) {
       callback()
     }

+ 8 - 0
report-ui/src/api/reportmanage.js

@@ -39,4 +39,12 @@ export function reportDetail(data) {
   })
 }
 
+export function reportCopy(data) {
+  return request({
+    url: '/report/copy',
+    method: 'get',
+    params: { reportId: data.id }
+  })
+}
+
 export default { reportList, reportAdd, reportDeleteBatch, reportUpdate, reportDetail }

+ 72 - 3
report-ui/src/assets/iconfont/demo_index.html

@@ -54,6 +54,24 @@
       <div class="content unicode" style="display: block;">
           <ul class="icon_lists dib-box">
           
+            <li class="dib">
+              <span class="icon iconfont">&#xe683;</span>
+                <div class="name">热力图</div>
+                <div class="code-name">&amp;#xe683;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe60d;</span>
+                <div class="name">恢复备份</div>
+                <div class="code-name">&amp;#xe60d;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe60e;</span>
+                <div class="name">撤销</div>
+                <div class="code-name">&amp;#xe60e;</div>
+              </li>
+          
             <li class="dib">
               <span class="icon iconfont">&#xe7af;</span>
                 <div class="name">词云图</div>
@@ -792,9 +810,9 @@
 <pre><code class="language-css"
 >@font-face {
   font-family: 'iconfont';
-  src: url('iconfont.woff2?t=1643094287456') format('woff2'),
-       url('iconfont.woff?t=1643094287456') format('woff'),
-       url('iconfont.ttf?t=1643094287456') format('truetype');
+  src: url('iconfont.woff2?t=1650520683161') format('woff2'),
+       url('iconfont.woff?t=1650520683161') format('woff'),
+       url('iconfont.ttf?t=1650520683161') format('truetype');
 }
 </code></pre>
           <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -820,6 +838,33 @@
       <div class="content font-class">
         <ul class="icon_lists dib-box">
           
+          <li class="dib">
+            <span class="icon iconfont iconrelitu"></span>
+            <div class="name">
+              热力图
+            </div>
+            <div class="code-name">.iconrelitu
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconhuifubeifen"></span>
+            <div class="name">
+              恢复备份
+            </div>
+            <div class="code-name">.iconhuifubeifen
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconundo"></span>
+            <div class="name">
+              撤销
+            </div>
+            <div class="code-name">.iconundo
+            </div>
+          </li>
+          
           <li class="dib">
             <span class="icon iconfont iconciyuntu"></span>
             <div class="name">
@@ -1927,6 +1972,30 @@
       <div class="content symbol">
           <ul class="icon_lists dib-box">
           
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconrelitu"></use>
+                </svg>
+                <div class="name">热力图</div>
+                <div class="code-name">#iconrelitu</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconhuifubeifen"></use>
+                </svg>
+                <div class="name">恢复备份</div>
+                <div class="code-name">#iconhuifubeifen</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconundo"></use>
+                </svg>
+                <div class="name">撤销</div>
+                <div class="code-name">#iconundo</div>
+            </li>
+          
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
                   <use xlink:href="#iconciyuntu"></use>

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

@@ -1,8 +1,8 @@
 @font-face {
   font-family: "iconfont"; /* Project id 1513211 */
-  src: url('iconfont.woff2?t=1643094287456') format('woff2'),
-       url('iconfont.woff?t=1643094287456') format('woff'),
-       url('iconfont.ttf?t=1643094287456') format('truetype');
+  src: url('iconfont.woff2?t=1650520683161') format('woff2'),
+       url('iconfont.woff?t=1650520683161') format('woff'),
+       url('iconfont.ttf?t=1650520683161') format('truetype');
 }
 
 .iconfont {
@@ -13,6 +13,18 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.iconrelitu:before {
+  content: "\e683";
+}
+
+.iconhuifubeifen:before {
+  content: "\e60d";
+}
+
+.iconundo:before {
+  content: "\e60e";
+}
+
 .iconciyuntu:before {
   content: "\e7af";
 }

文件差異過大導致無法顯示
+ 0 - 0
report-ui/src/assets/iconfont/iconfont.js


+ 21 - 0
report-ui/src/assets/iconfont/iconfont.json

@@ -5,6 +5,27 @@
   "css_prefix_text": "icon",
   "description": "",
   "glyphs": [
+    {
+      "icon_id": "19004935",
+      "name": "热力图",
+      "font_class": "relitu",
+      "unicode": "e683",
+      "unicode_decimal": 59011
+    },
+    {
+      "icon_id": "15047024",
+      "name": "恢复备份",
+      "font_class": "huifubeifen",
+      "unicode": "e60d",
+      "unicode_decimal": 58893
+    },
+    {
+      "icon_id": "19657550",
+      "name": "撤销",
+      "font_class": "undo",
+      "unicode": "e60e",
+      "unicode_decimal": 58894
+    },
     {
       "icon_id": "23043843",
       "name": "词云图",

二進制
report-ui/src/assets/iconfont/iconfont.ttf


二進制
report-ui/src/assets/iconfont/iconfont.woff


二進制
report-ui/src/assets/iconfont/iconfont.woff2


二進制
report-ui/src/assets/images/home-logo.png


二進制
report-ui/src/assets/images/login.jpg


+ 18 - 0
report-ui/src/assets/styles/anji.scss

@@ -77,3 +77,21 @@
 .el-table td {
   padding: 6px 0;
 }
+
+@keyframes turn {
+  0% {
+    -webkit-transform: rotate(0deg);
+  }
+  25% {
+    -webkit-transform: rotate(90deg);
+  }
+  50% {
+    -webkit-transform: rotate(180deg);
+  }
+  75% {
+    -webkit-transform: rotate(270deg);
+  }
+  100% {
+    -webkit-transform: rotate(360deg);
+  }
+}

+ 237 - 0
report-ui/src/assets/styles/screenDesigner.scss

@@ -0,0 +1,237 @@
+.layout {
+  width: 100%;
+  height: 100%;
+  background: #242a30;
+  color: #fff;
+  .layout-bar {
+    height: 40px;
+    line-height: 40px;
+    font-size: 12px;
+    padding: 0 10px;
+    display: flex;
+    flex-direction: row;
+    overflow: hidden;
+    .bar-item {
+      margin-right: 20px;
+      cursor: pointer;
+      .iconfont {
+        font-size: 12px;
+        margin-right: 4px;
+      }
+      .el-dropdown-link {
+        color: #fff;
+        cursor: pointer;
+      }
+    }
+  }
+  .layout-container {
+    width: 100%;
+    height: calc(100vh - 40px);
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    overflow: hidden;
+    .layout-left {
+      width: 200px;
+      background: #242a30;
+      overflow-x: hidden;
+      overflow-y: auto;
+      .chart-type {
+        display: flex;
+        flex-direction: row;
+        overflow: hidden;
+        .type-left {
+          width: 100%;
+          height: calc(100vh - 80px);
+          text-align: center;
+          /deep/.el-tabs__header {
+            width: 30%;
+            margin-right: 0;
+            .el-tabs__nav-wrap {
+              &::after {
+                background: transparent;
+              }
+              .el-tabs__item {
+                text-align: center;
+                width: 100%;
+                color: #fff;
+                padding: 0;
+              }
+            }
+          }
+          /deep/.el-tabs__content {
+            width: 70%;
+          }
+        }
+      }
+      //工具栏一个元素
+      .tools-item {
+        display: flex;
+        position: relative;
+        width: 100%;
+        height: 48px;
+        align-items: center;
+        -webkit-box-align: center;
+        padding: 0 6px;
+        cursor: pointer;
+        font-size: 12px;
+        margin-bottom: 1px;
+
+        .tools-item-icon {
+          color: #409eff;
+          margin-right: 10px;
+          width: 53px;
+          height: 30px;
+          line-height: 30px;
+          text-align: center;
+          display: block;
+          border: 1px solid #3a4659;
+          background: #282a30;
+        }
+        .tools-item-text {
+        }
+      }
+      /deep/.el-tabs__content {
+        padding: 0;
+      }
+    }
+    .layout-middle {
+      // display: flex;
+      position: relative;
+      //width: calc(100% - 445px);
+      height: 100%;
+      background-color: rgb(36, 42, 48);
+      box-sizing: border-box;
+      -webkit-box-sizing: border-box;
+      border: 1px solid rgb(36, 42, 48);
+      align-items: center;
+      vertical-align: middle;
+      text-align: center;
+      .workbench-container {
+        position: relative;
+        -webkit-transform-origin: 0 0;
+        transform-origin: 0 0;
+        -webkit-box-sizing: border-box;
+        box-sizing: border-box;
+        margin: 0;
+        padding: 0;
+
+        .vueRuler {
+          width: 100%;
+          padding: 18px 0px 0px 18px;
+        }
+
+        .workbench {
+          background-color: #1e1e1e;
+          position: relative;
+          -webkit-user-select: none;
+          -moz-user-select: none;
+          -ms-user-select: none;
+          user-select: none;
+          -webkit-transform-origin: 0 0;
+          transform-origin: 0 0;
+          margin: 0;
+          padding: 0;
+        }
+
+        .bg-grid {
+          position: absolute;
+          top: 0;
+          left: 0;
+          width: 100%;
+          height: 100%;
+          background-size: 30px 30px, 30px 30px;
+          background-image: linear-gradient(
+              hsla(0, 0%, 100%, 0.1) 1px,
+              transparent 0
+            ),
+            linear-gradient(90deg, hsla(0, 0%, 100%, 0.1) 1px, transparent 0);
+          // z-index: 2;
+        }
+      }
+    }
+
+    .layout-right {
+      width: 300px;
+    }
+
+    /deep/ .el-tabs--border-card {
+      border: 0;
+      .el-tabs__header {
+        background: transparent;
+        .el-tabs__nav {
+          width: 100%;
+          .el-tabs__item {
+            background-color: #242f3b;
+            border: 0px;
+            font-size: 12px;
+            width: 50%;
+            .icon {
+              margin-right: 4px;
+            }
+          }
+
+          .el-tabs__item.is-active {
+            background-color: #31455d;
+          }
+        }
+      }
+
+      .el-tabs__content {
+        background-color: #242a30;
+        height: calc(100vh - 80px);
+        overflow-x: hidden;
+        overflow-y: auto;
+        .el-tab-pane {
+          color: #bfcbd9;
+        }
+
+        &::-webkit-scrollbar {
+          width: 5px;
+          height: 14px;
+        }
+
+        &::-webkit-scrollbar-track,
+        &::-webkit-scrollbar-thumb {
+          border-radius: 1px;
+          border: 0 solid transparent;
+        }
+
+        &::-webkit-scrollbar-track-piece {
+          /*修改滚动条的背景和圆角*/
+          background: #29405c;
+          -webkit-border-radius: 7px;
+        }
+
+        &::-webkit-scrollbar-track {
+          box-shadow: 1px 1px 5px rgba(116, 148, 170, 0.5) inset;
+        }
+
+        &::-webkit-scrollbar-thumb {
+          min-height: 20px;
+          background-clip: content-box;
+          box-shadow: 0 0 0 5px rgba(116, 148, 170, 0.5) inset;
+        }
+
+        &::-webkit-scrollbar-corner {
+          background: transparent;
+        }
+
+        /*修改垂直滚动条的样式*/
+        &::-webkit-scrollbar-thumb:vertical {
+          background-color: #00113a;
+          -webkit-border-radius: 7px;
+        }
+
+        /*修改水平滚动条的样式*/
+        &::-webkit-scrollbar-thumb:horizontal {
+          background-color: #00113a;
+          -webkit-border-radius: 7px;
+        }
+      }
+    }
+  }
+}
+/deep/.el-dropdown-menu__item {
+  max-width: none;
+}

+ 1 - 1
report-ui/src/components/AnjiPlus/anji-cascader.vue

@@ -127,7 +127,7 @@ export default {
     },
     queryData() {
       axios({
-        url: process.env.VUE_APP_BASE_API + this.url,
+        url: process.env.BASE_API + this.url,
         methods: "get",
         headers: {
           Authorization: getToken(),

+ 1 - 1
report-ui/src/components/AnjiPlus/anji-checkbox.vue

@@ -80,7 +80,7 @@ export default {
     },
     // 从本地localStorage取 gaeaDict
     getOptionsFromLocalStorage() {
-      let dicts = JSON.parse(localStorage.getItem("gaeaDict"));
+      let dicts = JSON.parse(localStorage.getItem("AJReportDict"));
       let options = [];
       if (!dicts.hasOwnProperty(this.dictCode)) {
         return [];

+ 76 - 79
report-ui/src/components/AnjiPlus/anji-crud/anji-crud.vue

@@ -148,36 +148,19 @@
         <!-- 查询表单结束 -->
         <!-- 批量操作 -->
         <div style="padding-bottom: 8px">
-          <slot name="buttonLeftOnTable" :selection="checkRecords" />
+          <slot name="tableButtons" :selection="checkRecords" />
           <el-button
-            v-if="
-              option.buttons.add.isShow == undefined
-                ? true
-                : option.buttons.add.isShow
-            "
-            v-permission="option.buttons.add.permission"
-            class="button"
-            plain
-            icon="el-icon-plus"
-            @click="handleOpenEditView('add')"
-            >新增</el-button
+            v-for="(item, index) in option.tableButtons"
+            :key="index"
+            v-permission="item.permission"
+            class="tableButton"
+            :plain="item.plain"
+            :icon="item.icon"
+            :type="item.type"
+            :disabled="isDisabledButton(item, checkRecords)"
+            @click="item.click(checkRecords)"
+            >{{ handlegetLable(checkRecords, item.label) }}</el-button
           >
-          <el-button
-            v-if="
-              option.buttons.delete.isShow == undefined
-                ? true
-                : option.buttons.delete.isShow
-            "
-            v-permission="option.buttons.delete.permission"
-            class="button"
-            plain
-            :disabled="disableBatchDelete"
-            type="danger"
-            icon="el-icon-delete"
-            @click="handleDeleteBatch()"
-            >删除</el-button
-          >
-          <slot name="buttonRightOnTable" :selection="checkRecords" />
         </div>
       </div>
 
@@ -282,59 +265,58 @@
               align="center"
               fixed="right"
               label="操作"
-              :width="
-                option.buttons.customButton &&
-                option.buttons.customButton.operationWidth
-                  ? option.buttons.customButton.operationWidth
-                  : 100
-              "
+              :width="option.buttons.rowButtonsWidth || 100"
             >
               <template slot-scope="scope">
-                <slot name="rowButton" :msg="scope.row" />
-                <el-button
-                  v-if="
-                    option.buttons.edit.isShow == undefined
-                      ? true
-                      : option.buttons.edit.isShow
-                  "
-                  type="text"
-                  size="small"
-                  @click="handleOpenEditView('edit', scope.row)"
-                  >编辑</el-button
-                >
-                <el-button
-                  v-if="
-                    hasCustomButtonInRowMore == false &&
-                    option.buttons.delete.isShow == undefined
-                      ? true
-                      : option.buttons.edit.isShow
-                  "
-                  type="text"
-                  size="small"
-                  @click="handleDeleteBatch(scope.row)"
-                  >删除</el-button
-                >
-                <el-dropdown v-if="hasCustomButtonInRowMore" trigger="click">
-                  <span class="el-dropdown-link"
-                    >更多<i class="el-icon-caret-bottom el-icon--right" />
-                  </span>
-                  <el-dropdown-menu slot="dropdown">
-                    <el-dropdown-item class="clearfix">
-                      <slot name="rowButtonInMore" :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)"
-                        >删除</el-button
-                      >
-                    </el-dropdown-item>
-                  </el-dropdown-menu>
-                </el-dropdown>
+                <div v-if="option.rowButtons.length <= 2">
+                  <el-button
+                    v-for="(item, index) in option.rowButtons"
+                    :key="index"
+                    v-permission="item.permission"
+                    :disabled="isDisabledButton(item, scope.row)"
+                    :type="item.type || 'text'"
+                    size="small"
+                    @click="item.click(scope.row)"
+                    >{{ handlegetLable(scope.row, item.label) }}</el-button
+                  >
+                </div>
+                <div v-else>
+                  <el-button
+                    v-permission="option.rowButtons[0].permission"
+                    :type="option.rowButtons[0].type || 'text'"
+                    :disabled="
+                      isDisabledButton(option.rowButtons[0], scope.row)
+                    "
+                    @click="option.rowButtons[0].click(scope.row)"
+                    >{{
+                      handlegetLable(scope.row, option.rowButtons[0].label)
+                    }}</el-button
+                  >
+                  <el-dropdown trigger="click">
+                    <span class="el-dropdown-link">
+                      更多
+                      <i class="el-icon-caret-bottom el-icon--right" />
+                    </span>
+                    <el-dropdown-menu slot="dropdown">
+                      <el-dropdown-item class="clearfix">
+                        <el-button
+                          v-for="(item, index) in option.rowButtons.filter(
+                            (el, index) => index > 0
+                          )"
+                          :key="index"
+                          v-permission="item.permission"
+                          :type="item.type || 'text'"
+                          :disabled="isDisabledButton(item, scope.row)"
+                          size="small"
+                          @click="item.click(scope.row)"
+                          >{{
+                            handlegetLable(scope.row, item.label)
+                          }}</el-button
+                        >
+                      </el-dropdown-item>
+                    </el-dropdown-menu>
+                  </el-dropdown>
+                </div>
               </template>
             </el-table-column>
           </el-table>
@@ -668,6 +650,21 @@ export default {
       };
       this.$emit("handleCustomValue", obj);
     },
+    handlegetLable(item, label) {
+      if (typeof label == "function") {
+        return label(item);
+      } else {
+        return label;
+      }
+    },
+    // 是否disabled
+    isDisabledButton(item, row) {
+      if (typeof item.isDisable === "function") {
+        return item.isDisable(row);
+      } else {
+        return !!item.disabled;
+      }
+    },
     // 弹框被关闭时的回调事件
     editDialogClosedEvent(value) {
       // 把列表页中弹框打开标记改成已关闭

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

@@ -244,9 +244,9 @@ export default {
       }
       return result;
     },
-    // 从本地localStorage取 gaeaDict
+    // 从本地localStorage取 AJReportDict
     getOptionsFromLocalStorage() {
-      let dicts = JSON.parse(localStorage.getItem("gaeaDict"));
+      let dicts = JSON.parse(localStorage.getItem("AJReportDict"));
       let options = [];
       if (!dicts.hasOwnProperty(this.dictCode)) {
         return [];

+ 24 - 102
report-ui/src/components/AnjiPlus/anji-upload.vue

@@ -6,39 +6,16 @@
       :action="requestUrl"
       list-type="picture-card"
       :file-list="fileList"
-      :on-preview="handlePictureCardPreview"
       :on-remove="handleRemove"
+      :on-exceed="handleExceed"
       :on-success="handleSuccess"
       :show-file-list="true"
       :before-upload="handleBeforeUpload"
-      :class="fileList && fileList.length >= limit ? 'hide_box' : ''"
     >
       <i slot="default" class="el-icon-plus" />
       <div slot="file" slot-scope="{ file }" class="imgBox">
-        <img
-          v-if="typeImgShow(file)"
-          class="el-upload-list__item-thumbnail"
-          :src="file.url"
-          alt=""
-        />
-        <svg-icon
-          v-else-if="typePdfShow(file)"
-          icon-class="PDF"
-          class="iconFont"
-        />
-        <svg-icon
-          v-else-if="typeExcelShow(file)"
-          icon-class="Excel"
-          class="iconFont"
-        />
+        <img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
         <span class="el-upload-list__item-actions">
-          <span
-            v-if="typeImgShow(file)"
-            class="el-upload-list__item-preview"
-            @click="handlePictureCardPreview(file)"
-          >
-            <i class="el-icon-zoom-in" />
-          </span>
           <span
             class="el-upload-list__item-delete"
             @click="handleDownload(file)"
@@ -51,9 +28,6 @@
         </span>
       </div>
     </el-upload>
-    <el-dialog :visible.sync="dialogVisibleImageUpload" :modal="false">
-      <img width="100%" :src="imageUploadUrl" alt="" />
-    </el-dialog>
   </div>
 </template>
 <script>
@@ -79,22 +53,12 @@ export default {
       }
     },
     value: {
-      type: Array,
-      default: () => {
-        return [];
-      }
-    },
-    uploadType: {
-      type: String,
-      default: () => {
-        return "img";
-      }
+      type: Array | String
     }
   },
   data() {
     return {
       imageUploadUrl: "",
-      dialogVisibleImageUpload: false,
       fileList: [],
       modeString: ""
     };
@@ -102,9 +66,9 @@ export default {
   computed: {
     requestUrl() {
       if (this.upLoadUrl != null && this.upLoadUrl.trim() != "") {
-        return process.env.VUE_APP_BASE_API + this.upLoadUrl;
+        return process.env.BASE_API + this.upLoadUrl;
       } else {
-        return process.env.VUE_APP_BASE_API + "/meta/file/upload";
+        return process.env.BASE_API + "/file/upload";
       }
     },
     headers() {
@@ -114,60 +78,22 @@ export default {
     }
   },
   watch: {
-    value: {
-      handler(val) {
-        this.echoUpload(val);
-      },
-      immediate: true
+    value(val) {
+      this.echoUpload(val);
     }
   },
-  mounted() {},
+  mounted() {
+    this.echoUpload(this.value);
+  },
   methods: {
-    // 图片
-    typeImgShow(file) {
-      if (!file.fileType) return;
-      const fileType = file.fileType.toLowerCase();
-      if (
-        fileType == "jpg" ||
-        fileType == "png" ||
-        fileType == "gif" ||
-        fileType == "icon"
-      ) {
-        return true;
-      }
-      return false;
-    },
-    // pdf
-    typePdfShow(file) {
-      if (!file.fileType) return;
-      const fileType = file.fileType.toLowerCase();
-      if (fileType == "pdf") {
-        return true;
-      }
-      return false;
-    },
-    // excel
-    typeExcelShow(file) {
-      if (!file.fileType) return;
-      const fileType = file.fileType.toLowerCase();
-      if (fileType == "xlsx" || fileType == "xls" || fileType == "csv") {
-        return true;
-      }
-      return false;
-    },
     handleRemove(file) {
-      const fileList = [];
-      this.fileList.forEach(el => {
-        if (el.fileId != file.fileId) {
-          fileList.push(el);
-        }
-      });
-      this.fileList = fileList;
+      this.fileList = [];
+      console.log(this.fileList);
+      console.log(this.limit);
       this.change();
     },
-    handlePictureCardPreview(file) {
-      this.imageUploadUrl = file.url;
-      this.dialogVisibleImageUpload = true;
+    handleExceed() {
+      this.$message.warning(`只能上传${this.limit}个文件`);
     },
     // 下载
     handleDownload(file) {
@@ -175,7 +101,6 @@ export default {
     },
     // 上传成功的回调
     handleSuccess(response, file, fileList) {
-      console.log(fileList);
       if (response.code != 200) {
         this.$message.error("上传失败");
         return;
@@ -185,11 +110,13 @@ export default {
         fileId: file.response.data.fileId,
         fileType: file.response.data.fileType
       });
+      console.log(this.fileList);
       this.change();
     },
     // 回传出去
     change() {
       const fileList = this.fileList;
+      console.log(fileList);
       this.$emit("input", fileList);
       this.$emit("change", fileList);
     },
@@ -219,18 +146,13 @@ export default {
     },
     // 回显
     echoUpload(val) {
+      console.log(val);
       if (val && val.length > 0) {
-        const fileList = [];
-        for (let i = 0; i < val.length; i++) {
-          const obj = {};
-          obj.url = val[i].urlPath || val[i].url;
-          obj.fileType = val[i].fileType;
-          obj.fileId = val[i].fileId;
-          fileList.push(obj);
-        }
-        fileList.forEach((el, index) => {
-          this.$set(this.fileList, index, el);
-        });
+        this.fileList = [
+          {
+            url: val
+          }
+        ];
       } else {
         this.fileList = [];
       }
@@ -248,7 +170,7 @@ export default {
   width: 60px;
   height: 60px;
 }
-.hide_box .el-upload--picture-card {
+.hide_box /deep/.el-upload--picture-card {
   display: none;
 }
 .el-upload-list__item {

+ 1 - 0
report-ui/src/components/Dictionary/index.vue

@@ -66,6 +66,7 @@ export default {
     },
     selectChange(val) {
       this.$emit("input", val);
+      this.$emit("change", val);
     }
   }
 };

+ 3 - 2
report-ui/src/mixins/common.js

@@ -272,6 +272,7 @@ export default {
       return { top: top, left: left }
     },
     objToOne (obj) {
+      console.log(obj)
       let tmpData = {}
       for (let index in obj) {
         if (typeof obj[index] == 'object' && !this.isArrayFn(obj[index])) {
@@ -316,7 +317,7 @@ export default {
 
     // 根据数据字典,查询指定字典dict指定值code的,返回整个dictItem{id, text, extend}
     getDictItemByCode (dict, code) {
-      let dicts = getStorageItem('gaeaDict')
+      let dicts = getStorageItem('AJReportDict')
       if (!dicts.hasOwnProperty(dict)) {
         return null
       }
@@ -354,7 +355,7 @@ export default {
       return dictItem['extend']
     },
     getSettingByName(settingName) {
-      let gaeaSetting = JSON.parse(localStorage.getItem('gaeaDict'))
+      let gaeaSetting = JSON.parse(localStorage.getItem('AJReportDict'))
       if (gaeaSetting[settingName] != null) {
         return gaeaSetting[settingName]
       } else {

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

@@ -109,6 +109,7 @@ export default {
       // widget-text 文本框
       // widge-table 表格(数据不要转)
       // widget-stackchart 堆叠图
+      // widget-heatmap 热力图
       const chartType = params.chartType
       if (
         chartType == "widget-barchart" ||
@@ -121,10 +122,12 @@ export default {
         chartType == "widget-funnel"
       ) {
         return this.piechartFn(params.chartProperties, data);
-      }  else if (chartType == "widget-text") {
+      } else if (chartType == "widget-text") {
         return this.widgettext(params.chartProperties, data)
       } else if (chartType == "widget-stackchart") {
         return this.stackChartFn(params.chartProperties, data)
+      } else if (chartType == "widget-coord") {
+        return this.coordChartFn(params.chartProperties, data)
       } else {
         return data
       }
@@ -175,9 +178,9 @@ export default {
       for (const key in chartProperties) {
         if (chartProperties[key] !== 'yAxis' && !chartProperties[key].startsWith('xAxis')) {
           Object.keys(dataGroup).forEach(item => {
-            const data = new Array(yAxisList.length).fill(0)
+            const data = new Array(xAxisList.length).fill(0)
             dataGroup[item].forEach(res => {
-              data[xAxisList.indexOf(res[xAxisField])]= res[key]
+              data[xAxisList.indexOf(res[xAxisField])] = res[key]
             })
             series.push({
               name: yAxisList[item],
@@ -223,6 +226,27 @@ export default {
       }
       return ananysicData;
     },
+    // 坐标系数据解析
+    coordChartFn(chartProperties, data) {
+      const ananysicData = {};
+      let series = [];
+      //全部字段字典值
+      const types = Object.values(chartProperties)
+      //x轴字段、y轴字段、数值字段名
+      const xAxisField = Object.keys(chartProperties)[types.indexOf('xAxis')]
+      const yAxisField = Object.keys(chartProperties)[types.indexOf('yAxis')]
+      const dataField = Object.keys(chartProperties)[types.indexOf('series')]
+      //x轴数值去重,y轴去重
+      const xAxisList = this.setUnique(data.map(item => item[xAxisField]))
+      const yAxisList = this.setUnique(data.map(item => item[yAxisField]))
+      ananysicData["xAxis"] = xAxisList;
+      ananysicData["yAxis"] = yAxisList;
+      for (const i in data) {
+        series[i] = [data[i][xAxisField], data[i][yAxisField], data[i][dataField]];
+      }
+      ananysicData["series"] = series;
+      return ananysicData;
+    },
     setUnique(arr) {
       let newArr = [];
       arr.forEach(item => {

+ 14 - 0
report-ui/src/router/index.js

@@ -260,6 +260,20 @@ export const constantRouterMap = [
       requireAuth: true 
     }
   },
+  // 重写大屏
+  {
+    path: '/screenDesigner', 
+    component: () => import('@/views/screenDesigner/index'),
+    name: 'screenDesigner', 
+  },
+  {
+    path: '/screen/preview', 
+    component: () => import('@/views/screenDesigner/preview'), 
+    hidden: true, 
+    meta: { 
+      requireAuth: true 
+    }
+  },
   { 
     path: '/404', 
     component: () => import('@/views/404'), 

+ 3 - 3
report-ui/src/utils/auth.js

@@ -1,8 +1,8 @@
 import { getStorageItem, setStorageItem, delStorageItem } from '@/utils/storage'
 
-const TokenKey = 'token'
-const ShareTokenKey = 'shareToken'
-const AccessUserKey = 'gaeaUser'
+const TokenKey = 'AJReportToken'
+const ShareTokenKey = 'AJReportShareToken'
+const AccessUserKey = 'AJReportUser'
 
 export function getToken() {
   return getStorageItem(TokenKey)

+ 102 - 0
report-ui/src/utils/revoke.js

@@ -0,0 +1,102 @@
+/**
+  撤销重做功能
+ * @Author: eyas66
+ * @Mail: 33955341@qq.com
+ * @Date: 2021-12-13 10:09:23
+ * @Last Modified by:   eyas66
+ * @Last Modified time: 2021-12-13 10:09:23
+ */
+export class Revoke {
+    // 历史记录
+    recordList = [];
+
+    // 撤销记录,用于重做
+    redoList = [];
+
+    // 当前记录用currentRecord变量暂时存储,当用户修改时,再存放到recordList
+    currentRecord = null;
+
+    // 上次插入数据时间
+    time = 0;
+
+    /**
+     * @description: 插入历史记录
+     * @param {object}record
+     * @return {boolean}
+     */
+    push(record) {
+        const nowTime = Date.now();
+        // 防止添加重复的时间,当添加间隔小于100ms时,则替换当前记录并取消执行添加
+        if (this.time + 100 > nowTime) {
+            this.currentRecord = JSON.stringify(record);
+            return false;
+        }
+
+        this.time = nowTime;
+
+        // 判断之前是否已经存在currentRecord记录,有则存储到recordList
+        if (this.currentRecord) {
+            this.recordList.push(this.currentRecord);
+            //(清空记录)增加记录后则应该清空重做记录
+            //splice() 方法向/从数组添加/删除项目,并返回删除的项目。
+            this.redoList.splice(0, this.redoList.length);
+        }
+
+        // 将json转成字符串存储
+        this.currentRecord = JSON.stringify(record);
+
+        // 最多存储2000条记录,超过2000条记录则删除之前的记录
+        if (this.length > 2000) {
+            //unshift() 方法将新项添加到数组的开头,并返回新的长度。
+            this.recordList.unshift();
+        }
+
+        return true;
+    }
+
+    /**
+     * @description: 撤销操作
+     * @param {*}
+     * @return {object}
+     */
+    undo() {
+        // 没有记录时,返回false
+        // 新建的recordList里面,不知为什么会存在一条记录,未找到原因,所以就判断长度为1时就不能撤销了。
+        if (this.recordList.length === 1 ) {
+            return false;
+        }
+        //pop(): 方法用于删除并返回数组的最后一个元素。
+        const record = this.recordList.pop();
+
+        // 将当前记录添加到重做记录里面
+        if (this.currentRecord) {
+            this.redoList.push(this.currentRecord);
+        }
+        // 丢弃当前记录,防止重复添加
+        this.currentRecord = null;
+        //返回撤销的记录
+        return JSON.parse(record);
+    }
+
+    /**
+     * @description: 重做操作
+     * @param {*}
+     * @return {*}
+     */
+    redo() {
+        // 没有重做记录时,返回false
+        if (this.redoList.length === 0) {
+            return false;
+        }
+        //pop(): 方法用于删除并返回数组的最后一个元素。
+        const record = this.redoList.pop();
+        // 添加到重做记录里面
+        if (this.currentRecord) {
+            this.recordList.push(this.currentRecord);
+        }
+        // 丢弃当前记录,防止重复添加
+        this.currentRecord = null;
+
+        return JSON.parse(record);
+    }
+}

+ 42 - 4
report-ui/src/views/accessAuthority/index.vue

@@ -4,7 +4,7 @@
  * @Author: qianlishi
  * @Date: 2021-12-11 14:48:27
  * @LastEditors: qianlishi
- * @LastEditTime: 2021-12-13 12:20:56
+ * @LastEditTime: 2022-03-09 09:22:40
 -->
 <template>
   <anji-crud ref="listPage" :option="crudOption" />
@@ -67,6 +67,46 @@ export default {
             field: "actionName"
           }
         ],
+        // 表头按钮
+        tableButtons: [
+          {
+            label: "新增",
+            type: "", // primary、success、info、warning、danger
+            permission: "authorityManage:insert", // 按钮权限码
+            icon: "el-icon-plus",
+            plain: true,
+            click: () => {
+              return this.$refs.listPage.handleOpenEditView("add");
+            }
+          },
+          {
+            label: "删除",
+            type: "danger",
+            permission: "authorityManage:delete",
+            icon: "el-icon-delete",
+            plain: false,
+            click: () => {
+              return this.$refs.listPage.handleDeleteBatch();
+            }
+          }
+        ],
+        // 表格行按钮
+        rowButtons: [
+          {
+            label: "编辑",
+            permission: "authorityManage:update",
+            click: row => {
+              return this.$refs.listPage.handleOpenEditView("edit", row);
+            }
+          },
+          {
+            label: "删除",
+            permission: "authorityManage:delete",
+            click: row => {
+              return this.$refs.listPage.handleDeleteBatch(row);
+            }
+          }
+        ],
         // 操作按钮
         buttons: {
           query: {
@@ -89,9 +129,7 @@ export default {
             api: accessAuthorityUpdate,
             permission: "authorityManage:update"
           },
-          customButton: {
-            operationWidth: "150px"
-          }
+          rowButtonsWidth: 150 // row自定义按钮表格宽度
         },
         // 表格列
         columns: [

+ 48 - 14
report-ui/src/views/accessRole/index.vue

@@ -4,19 +4,10 @@
  * @Author: qianlishi
  * @Date: 2021-12-11 14:48:27
  * @LastEditors: qianlishi
- * @LastEditTime: 2021-12-13 12:20:46
+ * @LastEditTime: 2022-03-09 09:40:01
 -->
 <template>
   <anji-crud ref="listPage" :option="crudOption">
-    <template slot="rowButtonInMore" slot-scope="props">
-      <el-button
-        type="text"
-        @click="handleOpenDialogSetAuthorityForRole(props)"
-        v-permission="'roleManage:grantAuthority'"
-        >分配权限</el-button
-      >
-    </template>
-    <!--自定义的卡片插槽,将在编辑详情页面,出现在底部新卡片-->
     <template v-slot:pageSection>
       <RoleAuthority
         :role-code="roleCode"
@@ -52,6 +43,51 @@ export default {
         title: "角色管理",
         // 详情页中输入框左边文字宽度
         labelWidth: "160px",
+        // 表头按钮
+        tableButtons: [
+          {
+            label: "新增",
+            type: "", // primary、success、info、warning、danger
+            permission: "roleManage:insert", // 按钮权限码
+            icon: "el-icon-plus",
+            plain: true,
+            click: () => {
+              return this.$refs.listPage.handleOpenEditView("add");
+            }
+          },
+          {
+            label: "删除",
+            type: "danger",
+            permission: "roleManage:delete",
+            icon: "el-icon-delete",
+            plain: false,
+            click: () => {
+              return this.$refs.listPage.handleDeleteBatch();
+            }
+          }
+        ],
+        // 表格行按钮
+        rowButtons: [
+          {
+            label: "编辑",
+            permission: "roleManage:update",
+            click: row => {
+              return this.$refs.listPage.handleOpenEditView("edit", row);
+            }
+          },
+          {
+            label: "分配权限",
+            permission: "roleManage:grantAuthority",
+            click: this.handleOpenDialogSetAuthorityForRole
+          },
+          {
+            label: "删除",
+            permission: "roleManage:delete",
+            click: row => {
+              return this.$refs.listPage.handleDeleteBatch(row);
+            }
+          }
+        ],
         // 查询表单条件
         queryFormFields: [
           {
@@ -95,9 +131,7 @@ export default {
             api: accessRoleUpdate,
             permission: "roleManage:update"
           },
-          customButton: {
-            operationWidth: "160px"
-          }
+          rowButtonsWidth: 140 // row自定义按钮表格宽度
         },
         // 表格列
         columns: [
@@ -200,7 +234,7 @@ export default {
   },
   methods: {
     handleOpenDialogSetAuthorityForRole(props) {
-      this.roleCode = props.msg.roleCode;
+      this.roleCode = props.roleCode;
       this.dialogVisibleSetAuthorityForRole = true;
     }
   }

+ 48 - 13
report-ui/src/views/accessUser/index.vue

@@ -4,18 +4,10 @@
  * @Author: qianlishi
  * @Date: 2021-12-11 14:48:27
  * @LastEditors: qianlishi
- * @LastEditTime: 2021-12-13 12:20:35
+ * @LastEditTime: 2022-03-09 09:40:56
 -->
 <template>
   <anji-crud ref="listPage" :option="crudOption">
-    <template slot="rowButtonInMore" slot-scope="props">
-      <el-button
-        type="text"
-        @click="handleOpenDialogSetRoleForUser(props)"
-        v-permission="'userManage:grantRole'"
-        >分配角色</el-button
-      >
-    </template>
     <template v-slot:pageSection>
       <UserRole
         :login-name="loginName"
@@ -77,6 +69,51 @@ export default {
             field: "phone"
           }
         ],
+        // 表头按钮
+        tableButtons: [
+          {
+            label: "新增",
+            type: "roleManage:insert", // primary、success、info、warning、danger
+            permission: "userManage:insert", // 按钮权限码
+            icon: "el-icon-plus",
+            plain: true,
+            click: () => {
+              return this.$refs.listPage.handleOpenEditView("add");
+            }
+          },
+          {
+            label: "删除",
+            type: "danger",
+            permission: "userManage:delete",
+            icon: "el-icon-delete",
+            plain: false,
+            click: () => {
+              return this.$refs.listPage.handleDeleteBatch();
+            }
+          }
+        ],
+        // 表格行按钮
+        rowButtons: [
+          {
+            label: "编辑",
+            permission: "userManage:update",
+            click: row => {
+              return this.$refs.listPage.handleOpenEditView("edit", row);
+            }
+          },
+          {
+            label: "分配权限",
+            permission: "userManage:grantRole",
+            click: this.handleOpenDialogSetRoleForUser
+          },
+          {
+            label: "删除",
+            permission: "userManage:delete",
+            click: row => {
+              return this.$refs.listPage.handleDeleteBatch(row);
+            }
+          }
+        ],
         // 操作按钮
         buttons: {
           query: {
@@ -99,9 +136,7 @@ export default {
             api: accessUserUpdate,
             permission: "userManage:update"
           },
-          customButton: {
-            operationWidth: "150px"
-          }
+          rowButtonsWidth: 150 // row自定义按钮表格宽度
         },
         // 表格列
         columns: [
@@ -261,7 +296,7 @@ export default {
   },
   methods: {
     handleOpenDialogSetRoleForUser(props) {
-      this.loginName = props.msg.loginName;
+      this.loginName = props.loginName;
       this.dialogVisibleSetRoleForUser = true;
     }
   }

+ 1 - 1
report-ui/src/views/bigscreenDesigner/designer/components/customUpload.vue

@@ -47,7 +47,7 @@ export default {
       if (type === "image") {
         this.upload(file);
       } else {
-        this.$message.warn("只能上图片格式");
+        this.$message.warn("只能上图片格式");
       }
     },
     upload(imgUrl) {

+ 0 - 0
report-ui/src/views/bigscreenDesigner/designer/designer.js


+ 86 - 9
report-ui/src/views/bigscreenDesigner/designer/index.vue

@@ -16,10 +16,12 @@
         <!-- 左侧组件栏-->
         <el-tab-pane label="工具栏">
           <!-- <el-divider content-position="center">html</el-divider>-->
-          <draggable
+          <li
             v-for="widget in widgetTools"
             :key="widget.code"
-            @end="evt => widgetOnDragged(evt, widget.code)"
+            draggable="true"
+            @dragstart="dragStart(widget.code)"
+            @dragend="dragEnd()"
           >
             <div class="tools-item">
               <span class="tools-item-icon">
@@ -27,7 +29,7 @@
               </span>
               <span class="tools-item-text">{{ widget.label }}</span>
             </div>
-          </draggable>
+          </li>
         </el-tab-pane>
         <!-- 左侧图层-->
         <el-tab-pane label="图层">
@@ -88,6 +90,29 @@
             <i class="iconfont iconyulan" @click="viewScreen"></i>
           </el-tooltip>
         </span>
+
+        <span class="btn">
+          <el-tooltip
+            class="item"
+            effect="dark"
+            content="撤销"
+            placement="bottom"
+          >
+            <i class="iconfont iconundo"></i>
+          </el-tooltip>
+        </span>
+
+        <span class="btn">
+          <el-tooltip
+            class="item"
+            effect="dark"
+            content="恢复"
+            placement="bottom"
+          >
+            <i class="iconfont iconhuifubeifen"></i>
+          </el-tooltip>
+        </span>
+
         <span class="btn" v-permission="'bigScreenManage:export'">
           <el-tooltip
             class="item"
@@ -174,7 +199,7 @@
               'background-origin': 'initial',
               'background-clip': 'initial'
             }"
-            @click.self="setOptionsOnClickScreen"
+            @click.self="setOptionsOnClickScreen"  @drop="widgetOnDragged($event)" @dragover="dragOver($event)"
           >
             <div v-if="grade" class="bg-grid"></div>
             <widget
@@ -263,6 +288,7 @@ import draggable from "vuedraggable";
 import VueRulerTool from "vue-ruler-tool"; // 大屏设计页面的标尺插件
 import contentMenu from "./components/contentMenu";
 import { getToken } from "@/utils/auth";
+import { Revoke } from "@/utils/revoke"; //处理历史记录 2022-02-22
 
 export default {
   name: "Login",
@@ -290,6 +316,7 @@ export default {
 
       bigscreenWidth: 1920, // 大屏设计的大小
       bigscreenHeight: 1080,
+      revoke: null, //处理历史记录 2022-02-22
 
       // 工作台大屏画布,保存到表gaea_report_dashboard中
       dashboard: {
@@ -305,6 +332,7 @@ export default {
       },
       // 大屏的标记
       screenCode: "",
+      dragWidgetCode:'',   //从工具栏拖拽的组件code
       // 大屏画布中的组件
       widgets: [
         {
@@ -395,10 +423,18 @@ export default {
     widgets: {
       handler(val) {
         this.handlerLayerWidget(val);
+        //以下部分是记录历史
+        this.$nextTick(() => {
+          this.revoke.push(this.widgets);
+        });
       },
       deep: true
     }
   },
+  created() {
+    /* 以下是记录历史的 */
+    this.revoke = new Revoke();
+  },
   mounted() {
     // 如果是新的设计工作台
     this.initEchartData();
@@ -408,6 +444,30 @@ export default {
     });
   },
   methods: {
+    /**
+     * @description: 恢复
+     * @param {*}
+     * @return {*}
+     */
+    handleUndo() {
+      const record = this.revoke.undo();
+      if (!record) {
+        return false;
+      }
+      this.widgets = record;
+    },
+    /**
+     * @description: 重做
+     * @param {*}
+     * @return {*}
+     */
+    handleRedo() {
+      const record = this.revoke.redo();
+      if (!record) {
+        return false;
+      }
+      this.widgets = record;
+    },
     handlerLayerWidget(val) {
       const layerWidgetArr = [];
       for (let i = 0; i < val.length; i++) {
@@ -603,14 +663,24 @@ export default {
     getPXUnderScale(px) {
       return this.bigscreenScaleInWorkbench * px;
     },
-
+    dragStart( widgetCode) {
+        this.dragWidgetCode =widgetCode;
+    },
+    dragEnd() {
+        this.dragWidgetCode=''
+    },
+    dragOver(evt){
+      evt.preventDefault()
+      evt.stopPropagation()
+      evt.dataTransfer.dropEffect = 'copy'
+    },
     // 拖动一个组件放到工作区中去,在拖动结束时,放到工作区对应的坐标点上去
-    widgetOnDragged(evt, widgetCode) {
-      let widgetType = widgetCode;
+    widgetOnDragged(evt) {
+      let widgetType = this.dragWidgetCode;
 
       // 获取结束坐标和列名
-      let eventX = evt.originalEvent.clientX; // 结束在屏幕的x坐标
-      let eventY = evt.originalEvent.clientY; // 结束在屏幕的y坐标
+      let eventX = evt.clientX; // 结束在屏幕的x坐标
+      let eventY = evt.clientY; // 结束在屏幕的y坐标
 
       let workbenchPosition = this.getDomTopLeftById("workbench");
       let widgetTopInWorkbench = eventY - workbenchPosition.top;
@@ -639,6 +709,13 @@ export default {
       };
       // 处理默认值
       const widgetJsonValue = this.handleDefaultValue(widgetJson);
+
+      //2022年02月22日 修复:可以拖拽放到鼠标的位置
+      widgetJsonValue.value.position.left =
+        x - widgetJsonValue.value.position.width / 2;
+      widgetJsonValue.value.position.top =
+        y - widgetJsonValue.value.position.height / 2;
+
       // 将选中的复制组件,放到工作区中去
       this.widgets.push(this.deepClone(widgetJsonValue));
       // 激活新组件的配置属性

+ 2 - 83
report-ui/src/views/bigscreenDesigner/designer/tools/configure/widget-barchart.js

@@ -170,88 +170,6 @@ export const widgetBarchart = {
             },
           ],
         },
-        {
-          name: '图例操作',
-          list: [
-            {
-              type: 'el-switch',
-              label: '图例显示',
-              name: 'isShowLegend',
-              required: false,
-              placeholder: '',
-              value: true,
-            },
-            {
-              type: 'el-input-text',
-              label: '图例名称',
-              name: 'legendName',
-              required: false,
-              placeholder: '',
-              value: ''
-            },
-            {
-              type: 'vue-color',
-              label: '字体颜色',
-              name: 'lengedColor',
-              required: false,
-              placeholder: '',
-              value: '#fff',
-            },
-            {
-              type: 'el-input-number',
-              label: '字体字号',
-              name: 'lengedFontSize',
-              required: false,
-              placeholder: '',
-              value: 12,
-            },
-            {
-              type: 'el-input-number',
-              label: '图例宽度',
-              name: 'lengedWidth',
-              required: false,
-              placeholder: '',
-              value: 12,
-            },
-            {
-              type: 'el-select',
-              label: '横向位置',
-              name: 'lateralPosition',
-              required: false,
-              placeholder: '',
-              selectOptions: [
-                {code: 'center', name: '居中'},
-                {code: 'left', name: '左对齐'},
-                {code: 'right', name: '右对齐'},
-              ],
-              value: 'center'
-            },
-            {
-              type: 'el-select',
-              label: '纵向位置',
-              name: 'longitudinalPosition',
-              required: false,
-              placeholder: '',
-              selectOptions: [
-                {code: 'top', name: '顶部'},
-                {code: 'bottom', name: '底部'},
-              ],
-              value: 'top'
-            },
-            {
-              type: 'el-select',
-              label: '布局前置',
-              name: 'layoutFront',
-              required: false,
-              placeholder: '',
-              selectOptions: [
-                {code: 'vertical', name: '竖排'},
-                {code: 'horizontal', name: '横排'},
-              ],
-              value: 'horizontal'
-            },
-          ],
-        },
         {
           name: 'X轴设置',
           list: [
@@ -519,9 +437,10 @@ export const widgetBarchart = {
             {
               type: 'el-input-number',
               label: '字体字号',
-              name: 'tipFontSize',
+              name: 'tipsFontSize',
               required: false,
               placeholder: '',
+              value: 16
             },
             {
               type: 'vue-color',

+ 0 - 82
report-ui/src/views/bigscreenDesigner/designer/tools/configure/widget-gradient-barchart.js

@@ -162,88 +162,6 @@ export const widgetGradientBarchart = {
             },
           ],
         },
-        {
-          name: '图例操作',
-          list: [
-            {
-              type: 'el-switch',
-              label: '图例显示',
-              name: 'isShowLegend',
-              required: false,
-              placeholder: '',
-              value: true,
-            },
-            {
-              type: 'el-input-text',
-              label: '图例名称',
-              name: 'legendName',
-              required: false,
-              placeholder: '',
-              value: ''
-            },
-            {
-              type: 'vue-color',
-              label: '字体颜色',
-              name: 'lengedColor',
-              required: false,
-              placeholder: '',
-              value: '#fff',
-            },
-            {
-              type: 'el-input-number',
-              label: '字体字号',
-              name: 'lengedFontSize',
-              required: false,
-              placeholder: '',
-              value: 12,
-            },
-            {
-              type: 'el-input-number',
-              label: '图例宽度',
-              name: 'lengedWidth',
-              required: false,
-              placeholder: '',
-              value: 12,
-            },
-            {
-              type: 'el-select',
-              label: '横向位置',
-              name: 'lateralPosition',
-              required: false,
-              placeholder: '',
-              selectOptions: [
-                {code: 'center', name: '居中'},
-                {code: 'left', name: '左对齐'},
-                {code: 'right', name: '右对齐'},
-              ],
-              value: 'center'
-            },
-            {
-              type: 'el-select',
-              label: '纵向位置',
-              name: 'longitudinalPosition',
-              required: false,
-              placeholder: '',
-              selectOptions: [
-                {code: 'top', name: '顶部'},
-                {code: 'bottom', name: '底部'},
-              ],
-              value: 'top'
-            },
-            {
-              type: 'el-select',
-              label: '布局前置',
-              name: 'layoutFront',
-              required: false,
-              placeholder: '',
-              selectOptions: [
-                {code: 'vertical', name: '竖排'},
-                {code: 'horizontal', name: '横排'},
-              ],
-              value: 'horizontal'
-            },
-          ],
-        },
         {
           name: 'X轴设置',
           list: [

+ 688 - 0
report-ui/src/views/bigscreenDesigner/designer/tools/configure/widget-heatmap.js

@@ -0,0 +1,688 @@
+/*
+ * @Descripttion: 热力图
+ * @version:
+ * @Author: whw
+ * @Date: 2021-11-3
+ * @LastEditors: whw
+ * @LastEditTime: 2021-11-3
+ */
+export const widgetHeatmap = {
+  code: 'widget-heatmap',
+  type: 'chart',
+  label: '热力图',
+  icon: 'iconrelitu',
+  options: {
+    // 配置
+    setup: [
+      {
+        type: 'el-input-text',
+        label: '图层名称',
+        name: 'layerName',
+        required: false,
+        placeholder: '',
+        value: '热力图',
+      },
+      {
+        type: 'vue-color',
+        label: '背景颜色',
+        name: 'background',
+        required: false,
+        placeholder: '',
+        value: ''
+      },
+      [
+        {
+          name: '标题设置',
+          list: [
+            {
+              type: 'el-switch',
+              label: '标题',
+              name: 'isNoTitle',
+              required: false,
+              placeholder: '',
+              value: true
+            },
+            {
+              type: 'el-input-text',
+              label: '标题',
+              name: 'titleText',
+              required: false,
+              placeholder: '',
+              value: ''
+            },
+            {
+              type: 'vue-color',
+              label: '字体颜色',
+              name: 'textColor',
+              required: false,
+              placeholder: '',
+              value: '#fff'
+            },
+            {
+              type: 'el-select',
+              label: '字体粗细',
+              name: 'textFontWeight',
+              required: false,
+              placeholder: '',
+              selectOptions: [
+                {code: 'normal', name: '正常'},
+                {code: 'bold', name: '粗体'},
+                {code: 'bolder', name: '特粗体'},
+                {code: 'lighter', name: '细体'}
+              ],
+              value: 'normal'
+            },
+            {
+              type: 'el-input-number',
+              label: '字体大小',
+              name: 'textFontSize',
+              required: false,
+              placeholder: '',
+              value: 16
+            },
+            {
+              type: 'el-select',
+              label: '字体位置',
+              name: 'textAlign',
+              required: false,
+              placeholder: '',
+              selectOptions: [
+                {code: 'center', name: '居中'},
+                {code: 'left', name: '左对齐'},
+                {code: 'right', name: '右对齐'},
+              ],
+              value: 'center'
+            },
+            {
+              type: 'el-input-text',
+              label: '副标题',
+              name: 'subText',
+              required: false,
+              placeholder: '',
+              value: ''
+            },
+            {
+              type: 'vue-color',
+              label: '字体颜色',
+              name: 'subTextColor',
+              required: false,
+              placeholder: '',
+              value: ''
+            },
+            {
+              type: 'el-select',
+              label: '字体粗细',
+              name: 'subTextFontWeight',
+              required: false,
+              placeholder: '',
+              selectOptions: [
+                {code: 'normal', name: '正常'},
+                {code: 'bold', name: '粗体'},
+                {code: 'bolder', name: '特粗体'},
+                {code: 'lighter', name: '细体'}
+              ],
+              value: 'normal'
+            },
+            {
+              type: 'el-input-number',
+              label: '字体大小',
+              name: 'subTextFontSize',
+              required: false,
+              placeholder: '',
+              value: 16
+            },
+          ],
+        },
+        {
+          name: 'X轴设置',
+          list: [
+            {
+              type: 'el-switch',
+              label: '显示',
+              name: 'hideX',
+              required: false,
+              placeholder: '',
+              value: true,
+            },
+            {
+              type: 'el-input-text',
+              label: 'X轴别名',
+              name: 'xName',
+              required: false,
+              placeholder: '',
+              value: ''
+            },
+            {
+              type: 'vue-color',
+              label: '别名颜色',
+              name: 'xNameColor',
+              required: false,
+              placeholder: '',
+              value: '#fff'
+            },
+            {
+              type: 'el-input-number',
+              label: '别名字号',
+              name: 'xNameFontSize',
+              required: false,
+              placeholder: '',
+              value: 14
+            },
+            {
+              type: 'el-switch',
+              label: '轴反转',
+              name: 'reversalX',
+              required: false,
+              placeholder: '',
+              value: false
+            },
+            {
+              type: 'el-slider',
+              label: '文字角度',
+              name: 'textAngleX',
+              required: false,
+              placeholder: '',
+              value: 0
+            },
+            {
+              type: 'el-input-number',
+              label: '文字间隔',
+              name: 'textInterval',
+              required: false,
+              placeholder: '',
+              value: ''
+            },
+            {
+              type: 'vue-color',
+              label: '文字颜色',
+              name: 'Xcolor',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+            },
+            {
+              type: 'el-input-number',
+              label: '文字字号',
+              name: 'fontSizeX',
+              required: false,
+              placeholder: '',
+              value: 14,
+            },
+            {
+              type: 'vue-color',
+              label: '轴颜色',
+              name: 'lineColorX',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+            },
+          ],
+        },
+        {
+          name: 'Y轴设置',
+          list: [
+            {
+              type: 'el-switch',
+              label: '显示',
+              name: 'isShowY',
+              require: false,
+              placeholder: '',
+              value: true,
+            },
+            {
+              type: 'el-input-text',
+              label: 'Y轴别名',
+              name: 'textNameY',
+              require: false,
+              placeholder: '',
+              value: ''
+            },
+            {
+              type: 'vue-color',
+              label: '别名颜色',
+              name: 'NameColorY',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+            },
+            {
+              type: 'el-input-number',
+              label: '别名字号',
+              name: 'NameFontSizeY',
+              required: false,
+              placeholder: '',
+              value: 14,
+            },
+            {
+              type: 'el-switch',
+              label: '轴反转',
+              name: 'reversalY',
+              required: false,
+              placeholder: '',
+              value: false
+            },
+            {
+              type: 'el-slider',
+              label: '文字角度',
+              name: 'textAngleY',
+              required: false,
+              placeholder: '',
+              value: 0
+            },
+            {
+              type: 'vue-color',
+              label: '文字颜色',
+              name: 'colorY',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+            },
+            {
+              type: 'el-input-number',
+              label: '文字字号',
+              name: 'fontSizeY',
+              required: false,
+              placeholder: '',
+              value: 14,
+            },
+            {
+              type: 'vue-color',
+              label: '轴颜色',
+              name: 'lineColorY',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+            },
+          ],
+        },
+        {
+          name: '数值设定',
+          list: [
+            {
+              type: 'el-switch',
+              label: '显示',
+              name: 'isShow',
+              required: false,
+              placeholder: '',
+              value: true
+            },
+            {
+              type: 'el-input-number',
+              label: '字体字号',
+              name: 'fontSize',
+              required: false,
+              placeholder: '',
+              value: 14
+            },
+            {
+              type: 'vue-color',
+              label: '字体颜色',
+              name: 'subTextColor',
+              required: false,
+              placeholder: '',
+              value: '#fff'
+            },
+            {
+              type: 'el-select',
+              label: '字体粗细',
+              name: 'fontWeight',
+              required: false,
+              placeholder: '',
+              selectOptions: [
+                {code: 'normal', name: '正常'},
+                {code: 'bold', name: '粗体'},
+                {code: 'bolder', name: '特粗体'},
+                {code: 'lighter', name: '细体'}
+              ],
+              value: 'normal'
+            },
+          ],
+        },
+        {
+          name: '提示语设置',
+          list: [
+            {
+              type: 'el-input-number',
+              label: '字体字号',
+              name: 'tipsFontSize',
+              required: false,
+              placeholder: '',
+              value: 16
+            },
+            {
+              type: 'vue-color',
+              label: '字体颜色',
+              name: 'tipsLineColor',
+              required: false,
+              placeholder: '',
+            },
+          ],
+        },
+        {
+          name: '坐标轴边距设置',
+          list: [
+            {
+              type: 'el-slider',
+              label: '左边距(像素)',
+              name: 'marginLeft',
+              required: false,
+              placeholder: '',
+              value: 10,
+            }, {
+              type: 'el-slider',
+              label: '顶边距(像素)',
+              name: 'marginTop',
+              required: false,
+              placeholder: '',
+              value: 50,
+            }, {
+              type: 'el-slider',
+              label: '右边距(像素)',
+              name: 'marginRight',
+              required: false,
+              placeholder: '',
+              value: 40,
+            }, {
+              type: 'el-slider',
+              label: '底边距(像素)',
+              name: 'marginBottom',
+              required: false,
+              placeholder: '',
+              value: 10,
+            },
+          ],
+        },
+        {
+          name: '图设置',
+          list: [
+            {
+              type: 'el-switch',
+              label: '图例',
+              name: 'isShowLegend',
+              required: false,
+              placeholder: '',
+              value: false,
+            },
+            {
+              type: 'el-input-number',
+              label: '最小值',
+              name: 'dataMin',
+              required: false,
+              placeholder: '',
+              value: 0,
+            },
+            {
+              type: 'el-input-number',
+              label: '最大值',
+              name: 'dataMax',
+              required: false,
+              placeholder: '',
+              value: 5000,
+            },
+            {
+              type: 'vue-color',
+              label: '数值颜色',
+              name: 'lengedColor',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+            },
+            {
+              type: 'el-input-number',
+              label: '图例大小',
+              name: 'lengedFontSize',
+              required: false,
+              placeholder: '',
+              value: 12,
+            },
+            {
+              type: 'el-input-number',
+              label: '图例宽度',
+              name: 'lengedWidth',
+              required: false,
+              placeholder: '',
+              value: 12,
+            },
+            {
+              type: 'el-select',
+              label: '横向位置',
+              name: 'lateralPosition',
+              required: false,
+              placeholder: '',
+              selectOptions: [
+                {code: 'center', name: '居中'},
+                {code: 'left', name: '左对齐'},
+                {code: 'right', name: '右对齐'},
+              ],
+              value: 'center'
+            },
+            {
+              type: 'el-select',
+              label: '纵向位置',
+              name: 'longitudinalPosition',
+              required: false,
+              placeholder: '',
+              selectOptions: [
+                {code: 'top', name: '顶部'},
+                {code: 'bottom', name: '底部'},
+              ],
+              value: 'top'
+            },
+            {
+              type: 'el-select',
+              label: '布局前置',
+              name: 'layoutFront',
+              required: false,
+              placeholder: '',
+              selectOptions: [
+                {code: 'vertical', name: '竖排'},
+                {code: 'horizontal', name: '横排'},
+              ],
+              value: 'horizontal'
+            },
+          ],
+        },
+        {
+          name: '自定义配色',
+          list: [
+            {
+              type: 'customColor',
+              label: '',
+              name: 'lengedColorList',
+              required: false,
+              value: [{color: '#abd9e9'}, {color: '#74add1'}, {color: '#4575b4'}, {color: '#313695'}],
+            },
+          ],
+        },
+      ],
+    ],
+    // 数据
+    data: [
+      {
+        type: 'el-radio-group',
+        label: '数据类型',
+        name: 'dataType',
+        require: false,
+        placeholder: '',
+        selectValue: true,
+        selectOptions: [
+          {
+            code: 'staticData',
+            name: '静态数据',
+          },
+          {
+            code: 'dynamicData',
+            name: '动态数据',
+          },
+        ],
+        value: 'staticData',
+      },
+      {
+        type: 'el-input-number',
+        label: '刷新时间(毫秒)',
+        name: 'refreshTime',
+        relactiveDom: 'dataType',
+        relactiveDomValue: 'dynamicData',
+        value: 5000
+      },
+      {
+        type: 'el-button',
+        label: '静态数据',
+        name: 'staticData',
+        required: false,
+        placeholder: '',
+        relactiveDom: 'dataType',
+        relactiveDomValue: 'staticData',
+        value: [
+          {"axis": "0", "yaxis": "0", "num": 3320},
+          {"axis": "0", "yaxis": "1", "num": 1561},
+          {"axis": "0", "yaxis": "2", "num": 3194},
+          {"axis": "0", "yaxis": "3", "num": 2899},
+          {"axis": "0", "yaxis": "4", "num": 2363},
+          {"axis": "0", "yaxis": "5", "num": 3945},
+          {"axis": "0", "yaxis": "6", "num": 2051},
+          {"axis": "0", "yaxis": "7", "num": 3657},
+          {"axis": "0", "yaxis": "8", "num": 3304},
+          {"axis": "0", "yaxis": "9", "num": 2990},
+          {"axis": "1", "yaxis": "9", "num": 2663},
+          {"axis": "1", "yaxis": "0", "num": 378},
+          {"axis": "1", "yaxis": "1", "num": 4076},
+          {"axis": "1", "yaxis": "2", "num": 3178},
+          {"axis": "1", "yaxis": "3", "num": 1501},
+          {"axis": "1", "yaxis": "4", "num": 1660},
+          {"axis": "1", "yaxis": "5", "num": 726},
+          {"axis": "1", "yaxis": "6", "num": 4148},
+          {"axis": "1", "yaxis": "7", "num": 720},
+          {"axis": "1", "yaxis": "8", "num": 430},
+          {"axis": "2", "yaxis": "9", "num": 2983},
+          {"axis": "2", "yaxis": "0", "num": 1917},
+          {"axis": "2", "yaxis": "1", "num": 1188},
+          {"axis": "2", "yaxis": "2", "num": 3581},
+          {"axis": "2", "yaxis": "3", "num": 1781},
+          {"axis": "2", "yaxis": "4", "num": 4725},
+          {"axis": "2", "yaxis": "5", "num": 4077},
+          {"axis": "2", "yaxis": "6", "num": 299},
+          {"axis": "2", "yaxis": "7", "num": 4828},
+          {"axis": "2", "yaxis": "8", "num": 1778},
+          {"axis": "3", "yaxis": "9", "num": 3171},
+          {"axis": "3", "yaxis": "0", "num": 2944},
+          {"axis": "3", "yaxis": "1", "num": 763},
+          {"axis": "3", "yaxis": "2", "num": 1678},
+          {"axis": "3", "yaxis": "3", "num": 1765},
+          {"axis": "3", "yaxis": "4", "num": 2949},
+          {"axis": "3", "yaxis": "5", "num": 966},
+          {"axis": "3", "yaxis": "6", "num": 4622},
+          {"axis": "3", "yaxis": "7", "num": 2818},
+          {"axis": "3", "yaxis": "8", "num": 3913},
+          {"axis": "4", "yaxis": "9", "num": 4382},
+          {"axis": "4", "yaxis": "0", "num": 1670},
+          {"axis": "4", "yaxis": "1", "num": 4532},
+          {"axis": "4", "yaxis": "2", "num": 2116},
+          {"axis": "4", "yaxis": "3", "num": 2383},
+          {"axis": "4", "yaxis": "4", "num": 510},
+          {"axis": "4", "yaxis": "5", "num": 33},
+          {"axis": "4", "yaxis": "6", "num": 4974},
+          {"axis": "4", "yaxis": "7", "num": 3627},
+          {"axis": "4", "yaxis": "8", "num": 2737},
+          {"axis": "5", "yaxis": "9", "num": 656},
+          {"axis": "5", "yaxis": "0", "num": 3689},
+          {"axis": "5", "yaxis": "1", "num": 713},
+          {"axis": "5", "yaxis": "2", "num": 3551},
+          {"axis": "5", "yaxis": "3", "num": 3159},
+          {"axis": "5", "yaxis": "4", "num": 4150},
+          {"axis": "5", "yaxis": "5", "num": 1416},
+          {"axis": "5", "yaxis": "6", "num": 3021},
+          {"axis": "5", "yaxis": "7", "num": 1778},
+          {"axis": "5", "yaxis": "8", "num": 863},
+          {"axis": "6", "yaxis": "9", "num": 772},
+          {"axis": "6", "yaxis": "0", "num": 1675},
+          {"axis": "6", "yaxis": "1", "num": 1323},
+          {"axis": "6", "yaxis": "2", "num": 2023},
+          {"axis": "6", "yaxis": "3", "num": 43},
+          {"axis": "6", "yaxis": "4", "num": 4964},
+          {"axis": "6", "yaxis": "5", "num": 4781},
+          {"axis": "6", "yaxis": "6", "num": 2608},
+          {"axis": "6", "yaxis": "7", "num": 2278},
+          {"axis": "6", "yaxis": "8", "num": 3285},
+          {"axis": "7", "yaxis": "9", "num": 1977},
+          {"axis": "7", "yaxis": "0", "num": 882},
+          {"axis": "7", "yaxis": "1", "num": 2434},
+          {"axis": "7", "yaxis": "2", "num": 4694},
+          {"axis": "7", "yaxis": "3", "num": 3022},
+          {"axis": "7", "yaxis": "4", "num": 1798},
+          {"axis": "7", "yaxis": "5", "num": 2503},
+          {"axis": "7", "yaxis": "6", "num": 693},
+          {"axis": "7", "yaxis": "7", "num": 275},
+          {"axis": "7", "yaxis": "8", "num": 3774},
+          {"axis": "8", "yaxis": "9", "num": 1386},
+          {"axis": "8", "yaxis": "0", "num": 1212},
+          {"axis": "8", "yaxis": "1", "num": 1982},
+          {"axis": "8", "yaxis": "2", "num": 1509},
+          {"axis": "8", "yaxis": "3", "num": 94},
+          {"axis": "8", "yaxis": "4", "num": 2082},
+          {"axis": "8", "yaxis": "5", "num": 3930},
+          {"axis": "8", "yaxis": "6", "num": 4528},
+          {"axis": "8", "yaxis": "7", "num": 1861},
+          {"axis": "8", "yaxis": "8", "num": 4582},
+          {"axis": "9", "yaxis": "9", "num": 3038},
+          {"axis": "9", "yaxis": "0", "num": 4038},
+          {"axis": "9", "yaxis": "1", "num": 357},
+          {"axis": "9", "yaxis": "2", "num": 306},
+          {"axis": "9", "yaxis": "3", "num": 479},
+          {"axis": "9", "yaxis": "4", "num": 823},
+          {"axis": "9", "yaxis": "5", "num": 3442},
+          {"axis": "9", "yaxis": "6", "num": 904},
+          {"axis": "9", "yaxis": "7", "num": 399},
+          {"axis": "9", "yaxis": "8", "num": 4869},
+        ]
+      },
+      {
+        type: 'dycustComponents',
+        label: '',
+        name: 'dynamicData',
+        required: false,
+        placeholder: '',
+        relactiveDom: 'dataType',
+        chartType: 'widget-coord',
+        relactiveDomValue: 'dynamicData',
+        dictKey: 'COORD_PROPERTIES',
+        value: '',
+      },
+    ],
+    // 坐标
+    position: [
+      {
+        type: 'el-input-number',
+        label: '左边距',
+        name: 'left',
+        required: false,
+        placeholder: '',
+        value: 0,
+      },
+      {
+        type: 'el-input-number',
+        label: '上边距',
+        name: 'top',
+        required: false,
+        placeholder: '',
+        value: 0,
+      },
+      {
+        type: 'el-input-number',
+        label: '宽度',
+        name: 'width',
+        required: false,
+        placeholder: '该容器在1920px大屏中的宽度',
+        value: 700,
+      },
+      {
+        type: 'el-input-number',
+        label: '高度',
+        name: 'height',
+        required: false,
+        placeholder: '该容器在1080px大屏中的高度',
+        value: 300,
+      },
+    ],
+  }
+}

+ 9 - 1
report-ui/src/views/bigscreenDesigner/designer/tools/configure/widget-image.js

@@ -1,6 +1,6 @@
 /*
  * @Descripttion: 图片json
- * @version: 
+ * @version:
  * @Author: qianlishi
  * @Date: 2021-08-29 07:07:23
  * @LastEditors: qianlishi
@@ -30,6 +30,14 @@ export const widgetImage = {
           placeholder: '',
           value: false,
         },
+        {
+          type: 'el-slider',
+          label: '旋转速度',
+          name: 'rotationSpeed',
+          required: false,
+          placeholder: '',
+          value: 70
+        },
         {
           type: 'el-slider',
           label: '透明度',

+ 3 - 85
report-ui/src/views/bigscreenDesigner/designer/tools/configure/widget-linechart.js

@@ -186,88 +186,6 @@ export const widgetLinechart = {
             },
           ],
         },
-        {
-          name: '图例操作',
-          list: [
-            {
-              type: 'el-switch',
-              label: '图例显示',
-              name: 'isShowLegend',
-              required: false,
-              placeholder: '',
-              value: true,
-            },
-            {
-              type: 'el-input-text',
-              label: '图例名称',
-              name: 'legendName',
-              required: false,
-              placeholder: '',
-              value: ''
-            },
-            {
-              type: 'vue-color',
-              label: '字体颜色',
-              name: 'lengedColor',
-              required: false,
-              placeholder: '',
-              value: '#fff',
-            },
-            {
-              type: 'el-input-number',
-              label: '字体字号',
-              name: 'lengedFontSize',
-              required: false,
-              placeholder: '',
-              value: 12,
-            },
-            {
-              type: 'el-input-number',
-              label: '图例宽度',
-              name: 'lengedWidth',
-              required: false,
-              placeholder: '',
-              value: 12,
-            },
-            {
-              type: 'el-select',
-              label: '横向位置',
-              name: 'lateralPosition',
-              required: false,
-              placeholder: '',
-              selectOptions: [
-                {code: 'center', name: '居中'},
-                {code: 'left', name: '左对齐'},
-                {code: 'right', name: '右对齐'},
-              ],
-              value: 'center'
-            },
-            {
-              type: 'el-select',
-              label: '纵向位置',
-              name: 'longitudinalPosition',
-              required: false,
-              placeholder: '',
-              selectOptions: [
-                {code: 'top', name: '顶部'},
-                {code: 'bottom', name: '底部'},
-              ],
-              value: 'top'
-            },
-            {
-              type: 'el-select',
-              label: '布局前置',
-              name: 'layoutFront',
-              required: false,
-              placeholder: '',
-              selectOptions: [
-                {code: 'vertical', name: '竖排'},
-                {code: 'horizontal', name: '横排'},
-              ],
-              value: 'horizontal'
-            },
-          ],
-        },
         {
           name: 'X轴设置',
           list: [
@@ -525,12 +443,12 @@ export const widgetLinechart = {
           name: '提示语设置',
           list: [
             {
-              type: 'el-input-text',
+              type: 'el-input-number',
               label: '字体大小',
-              name: 'fontSize',
+              name: 'tipsFontSize',
               required: false,
               placeholder: '',
-              value: ''
+              value: 16
             },
             {
               type: 'vue-color',

+ 2 - 2
report-ui/src/views/bigscreenDesigner/designer/tools/index.js

@@ -4,7 +4,7 @@
  * @Author: qianlishi
  * @Date: 2021-08-29 06:43:07
  * @LastEditors: qianlishi
- * @LastEditTime: 2021-12-13 10:22:37
+ * @LastEditTime: 2022-03-11 10:35:35
  */
 import { widgetTool } from "./main"
 const screenConfig = {
@@ -81,5 +81,5 @@ const getToolByCode = function (code) {
   })
   return item
 }
-
+console.log(widgetTools)
 export {widgetTools, getToolByCode}

+ 3 - 1
report-ui/src/views/bigscreenDesigner/designer/tools/main.js

@@ -35,6 +35,7 @@ import {widgetLineCompare} from "./configure/widget-line-compare"
 import {widgetDecoratePie} from "./configure/widget-decorate-pie";
 import {widgetMoreBarLine} from "./configure/widget-more-bar-line";
 import {widgetWordCloud} from "./configure/widget-word-cloud";
+import {widgetHeatmap} from "./configure/widget-heatmap";
 
 export const widgetTool = [
   // type=html类型的组件
@@ -65,5 +66,6 @@ export const widgetTool = [
   widgetLineCompare,
   widgetDecoratePie,
   widgetMoreBarLine,
-  widgetWordCloud
+  widgetWordCloud,
+  widgetHeatmap
 ]

+ 1 - 1
report-ui/src/views/bigscreenDesigner/designer/widget/bar/widgetBarCompareChart.vue

@@ -572,7 +572,7 @@ export default {
       xAxisList = this.setUnique(xAxisList);
       yAxisList = this.setUnique(yAxisList);
       for (const i in yAxisList) {
-        const data = new Array(yAxisList.length).fill(0);
+        const data = new Array(xAxisList.length).fill(0);
         for (const j in xAxisList) {
           for (const k in val) {
             if (val[k].name == yAxisList[i]) {

+ 1 - 1
report-ui/src/views/bigscreenDesigner/designer/widget/bar/widgetBarStackChart.vue

@@ -314,7 +314,7 @@ export default {
       xAxisList = this.setUnique(xAxisList);
       yAxisList = this.setUnique(yAxisList);
       for (const i in yAxisList) {
-        const data = new Array(yAxisList.length).fill(0);
+        const data = new Array(xAxisList.length).fill(0);
         for (const j in xAxisList) {
           for (const k in val) {
             if (val[k].name == yAxisList[i]) {

+ 1 - 46
report-ui/src/views/bigscreenDesigner/designer/widget/bar/widgetBarchart.vue

@@ -98,7 +98,6 @@ export default {
       this.setOptionsTop();
       this.setOptionsTooltip();
       this.setOptionsMargin();
-      this.setOptionsLegend();
       this.setOptionsColor();
       this.setOptionsData();
     },
@@ -236,7 +235,7 @@ export default {
         show: true,
         textStyle: {
           color: optionsSetup.lineColor,
-          fontSize: optionsSetup.tipFontSize
+          fontSize: optionsSetup.tipsFontSize
         }
       };
       this.options.tooltip = tooltip;
@@ -253,42 +252,6 @@ export default {
       };
       this.options.grid = grid;
     },
-    // 图例操作 legend
-    setOptionsLegend() {
-      const optionsSetup = this.optionsSetup;
-      const legend = this.options.legend;
-      legend.show = optionsSetup.isShowLegend;
-      legend.left = optionsSetup.lateralPosition;
-      legend.right = optionsSetup.lateralPosition;
-      legend.top = optionsSetup.longitudinalPosition;
-      legend.bottom =
-        optionsSetup.longitudinalPosition;
-      legend.orient = optionsSetup.layoutFront;
-      legend.textStyle = {
-        color: optionsSetup.lengedColor,
-        fontSize: optionsSetup.fontSize
-      };
-      legend.itemWidth = optionsSetup.lengedWidth;
-    },
-    // 图例名称设置
-    setOptionsLegendName(name){
-      const optionsSetup = this.optionsSetup;
-      const series = this.options.series;
-      const legendName = optionsSetup.legendName;
-      // 图例没有手动写则显示原值,写了则显示新值
-      if (null == legendName || legendName == '') {
-        for (let i = 0; i < name.length; i++) {
-          series[i].name = name[i];
-        }
-        this.options.legend['data'] = name;
-      }else {
-        const arr = legendName.split('|');
-        for (let i = 0; i < arr.length; i++) {
-          series[i].name = arr[i];
-        }
-        this.options.legend['data'] = arr
-      }
-    },
     // 图例颜色修改
     setOptionsColor() {
       const optionsSetup = this.optionsSetup;
@@ -350,10 +313,6 @@ export default {
       if (series[0].type == "bar") {
         series[0].data = data;
       }
-      const legendName = [];
-      legendName.push('销售量')
-      this.options.legend['data'] = legendName;
-      this.setOptionsLegendName(legendName);
     },
     // 动态数据
     dynamicDataFn(val, refreshTime, optionsSetup) {
@@ -388,16 +347,12 @@ export default {
       }
       // series
       const series = this.options.series;
-      const legendName = [];
       for (const i in series) {
         if (series[i].type == "bar") {
           series[i].name = val.series[i].name;
           series[i].data = val.series[i].data;
         }
-        legendName.push(val.series[i].name);
       }
-      this.options.legend['data'] = legendName;
-      this.setOptionsLegendName(legendName);
     }
   }
 };

+ 1 - 46
report-ui/src/views/bigscreenDesigner/designer/widget/bar/widgetGradientColorBarchart.vue

@@ -167,7 +167,6 @@ export default {
       this.setOptionsX();
       this.setOptionsY();
       this.setOptionsTop();
-      this.setOptionsLegend();
       this.setOptionsMargin();
       this.setOptionsColor();
       this.setOptionsData();
@@ -302,7 +301,7 @@ export default {
         show: true,
         textStyle: {
           color: optionsSetup.lineColor,
-          fontSize: optionsSetup.fontSize
+          fontSize: optionsSetup.tipsFontSize
         }
       };
       this.options.tooltip = tooltip;
@@ -319,42 +318,6 @@ export default {
       };
       this.options.grid = grid;
     },
-    // 图例操作 legend
-    setOptionsLegend() {
-      const optionsSetup = this.optionsSetup;
-      const legend = this.options.legend;
-      legend.show = optionsSetup.isShowLegend;
-      legend.left = optionsSetup.lateralPosition;
-      legend.right = optionsSetup.lateralPosition;
-      legend.top = optionsSetup.longitudinalPosition;
-      legend.bottom =
-        optionsSetup.longitudinalPosition;
-      legend.orient = optionsSetup.layoutFront;
-      legend.textStyle = {
-        color: optionsSetup.lengedColor,
-        fontSize: optionsSetup.fontSize
-      };
-      legend.itemWidth = optionsSetup.lengedWidth;
-    },
-    // 图例名称设置
-    setOptionsLegendName(name){
-      const optionsSetup = this.optionsSetup;
-      const series = this.options.series;
-      const legendName = optionsSetup.legendName;
-      // 图例没有手动写则显示原值,写了则显示新值
-      if (null == legendName || legendName == '') {
-        for (let i = 0; i < name.length; i++) {
-          series[i].name = name[i];
-        }
-        this.options.legend['data'] = name;
-      }else {
-        const arr = legendName.split('|');
-        for (let i = 0; i < arr.length; i++) {
-          series[i].name = arr[i];
-        }
-        this.options.legend['data'] = arr
-      }
-    },
     // 渐变色
     setOptionsColor() {
       const optionsSetup = this.optionsSetup;
@@ -436,10 +399,6 @@ export default {
       if (series[0].type == "bar") {
         series[0].data = data;
       }
-      const legendName = [];
-      legendName.push('销售量')
-      this.options.legend['data'] = legendName;
-      this.setOptionsLegendName(legendName);
     },
     // 动态数据
     dynamicDataFn(val, refreshTime, optionsSetup) {
@@ -475,16 +434,12 @@ export default {
 
       // series
       const series = this.options.series;
-      const legendName = [];
       for (const i in series) {
         if (series[i].type == "bar") {
           series[i].name = val.series[i].name;
           series[i].data = val.series[i].data;
         }
-        legendName.push(val.series[i].name);
       }
-      this.options.legend['data'] = legendName;
-      this.setOptionsLegendName(legendName);
     }
   }
 };

+ 367 - 0
report-ui/src/views/bigscreenDesigner/designer/widget/heatmap/widgetHeatmap.vue

@@ -0,0 +1,367 @@
+<template>
+  <div :style="styleObj">
+    <v-chart :options="options" autoresize/>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "widgetHeatmap",
+  components: {},
+  props: {
+    value: Object,
+    ispreview: Boolean,
+  },
+  data() {
+    return {
+      options: {
+        title: {
+          text: "",
+          left: "center",
+          textStyle: {
+            color: "#fff",
+          },
+        },
+        tooltip: {
+          position: "top",
+          show: true,
+          textStyle: {},
+        },
+        grid: {
+          height: "90%",
+          width: "90%",
+          top: 10,
+          left: 20,
+        },
+        xAxis: {
+          name: "",
+          type: "category",
+          axisLabel: {
+            show: true,
+            color: "#0f0",
+          },
+          data: [],
+          splitArea: {
+            show: false,
+          },
+          nameTextStyle: {
+            color: "",
+            fontSize: 14,
+          },
+        },
+        yAxis: {
+          name: "",
+          type: "category",
+          axisLabel: {
+            show: true,
+            color: "#0f0",
+          },
+          data: [],
+          splitArea: {
+            show: false,
+          },
+          nameTextStyle: {
+            color: "",
+            fontSize: 14,
+          },
+        },
+        visualMap: {
+          show: true,
+          min: 0,
+          max: 5000,
+          calculable: true,
+          orient: "horizontal",
+          left: "center",
+          bottom: 0,
+          inRange: {
+            color: [],
+          },
+          textStyle: {
+            fontSize: 14,
+            color: "#fff",
+          },
+        },
+        series: [
+          {
+            name: "",
+            type: "heatmap",
+            data: [],
+            label: {
+              show: false,
+              fontSize: 16,
+            },
+            emphasis: {
+              itemStyle: {
+                shadowBlur: 10,
+                shadowColor: "rgba(0, 0, 0, 0.5)",
+              },
+            },
+          },
+        ],
+      },
+      optionsStyle: {}, // 样式
+      optionsData: {}, // 数据
+      optionsCollapse: {}, // 图标属性
+      optionsSetup: {},
+    };
+  },
+  computed: {
+    styleObj() {
+      return {
+        position: this.ispreview ? "absolute" : "static",
+        width: this.optionsStyle.width + "px",
+        height: this.optionsStyle.height + "px",
+        left: this.optionsStyle.left + "px",
+        top: this.optionsStyle.top + "px",
+        background: this.optionsSetup.background,
+      };
+    },
+  },
+  watch: {
+    value: {
+      handler(val) {
+        this.optionsStyle = val.position;
+        this.optionsData = val.data;
+        this.optionsCollapse = val.collapse;
+        this.optionsSetup = val.setup;
+        this.editorOptions();
+      },
+      deep: true,
+    },
+  },
+  created() {
+    this.optionsStyle = this.value.position;
+    this.optionsData = this.value.data;
+    this.optionsCollapse = this.value.collapse;
+    this.optionsSetup = this.value.setup;
+    this.editorOptions();
+  },
+  methods: {
+    // 修改图标options属性
+    editorOptions() {
+      this.setOptionsTitle();
+      this.setOptionsX();
+      this.setOptionsY();
+      this.setOptionsSeries();
+      this.setOptionsMargin();
+      this.setOptionsVisualMap();
+      this.setOptionsData();
+    },
+    // 标题修改
+    setOptionsTitle() {
+      const optionsSetup = this.optionsSetup;
+      const title = {
+        text: optionsSetup.titleText,
+        show: optionsSetup.isNoTitle,
+        left: optionsSetup.textAlign,
+        textStyle: {
+          color: optionsSetup.textColor,
+          fontSize: optionsSetup.textFontSize,
+          fontWeight: optionsSetup.textFontWeight
+        },
+        subtext: optionsSetup.subText,
+        subtextStyle: {
+          color: optionsSetup.subTextColor,
+          fontWeight: optionsSetup.subTextFontWeight,
+          fontSize: optionsSetup.subTextFontSize
+        },
+      };
+      this.options.title = title;
+    },
+    // X轴设置
+    setOptionsX() {
+      const optionsSetup = this.optionsSetup;
+      const xAxis = {
+        type: "category",
+        // 坐标轴是否显示
+        show: optionsSetup.hideX,
+        // 坐标轴名称
+        name: optionsSetup.xName,
+        nameTextStyle: {
+          color: optionsSetup.xNameColor,
+          fontSize: optionsSetup.xNameFontSize
+        },
+        // 轴反转
+        inverse: optionsSetup.reversalX,
+        axisLabel: {
+          show: true,
+          // 文字间隔
+          interval: optionsSetup.textInterval,
+          // 文字角度
+          rotate: optionsSetup.textAngleX,
+          textStyle: {
+            // 坐标文字颜色
+            color: optionsSetup.Xcolor,
+            fontSize: optionsSetup.fontSizeX
+          }
+        },
+        axisLine: {
+          show: true,
+          lineStyle: {
+            color: optionsSetup.lineColorX
+          }
+        },
+        splitArea: {
+          show: false,
+        },
+      };
+      this.options.xAxis = xAxis;
+    },
+    // Y轴设置
+    setOptionsY() {
+      const optionsSetup = this.optionsSetup;
+      const yAxis = {
+        type: "category",
+        // 坐标轴是否显示
+        show: optionsSetup.isShowY,
+        // 坐标轴名称
+        name: optionsSetup.textNameY,
+        nameTextStyle: {
+          color: optionsSetup.NameColorY,
+          fontSize: optionsSetup.NameFontSizeY
+        },
+        // y轴反转
+        inverse: optionsSetup.reversalY,
+        axisLabel: {
+          show: true,
+          // 文字角度
+          rotate: optionsSetup.textAngleY,
+          textStyle: {
+            // 坐标文字颜色
+            color: optionsSetup.colorY,
+            fontSize: optionsSetup.fontSizeY
+          }
+        },
+        axisLine: {
+          show: true,
+          lineStyle: {
+            color: optionsSetup.lineColorY
+          }
+        },
+        splitArea: {
+          show: false,
+        },
+      };
+      this.options.yAxis = yAxis;
+    },
+    // 数值设定
+    setOptionsSeries() {
+      const optionsSetup = this.optionsSetup;
+      const lable = {
+        show: optionsSetup.isShow,
+        textStyle: {
+          fontSize: optionsSetup.fontSize,
+          color: optionsSetup.subTextColor,
+          fontWeight: optionsSetup.fontWeight
+        }
+      }
+      this.options.series[0].label = lable;
+    },
+    // 边距设置
+    setOptionsMargin() {
+      const optionsSetup = this.optionsSetup;
+      const grid = {
+        left: optionsSetup.marginLeft,
+        right: optionsSetup.marginRight,
+        bottom: optionsSetup.marginBottom,
+        top: optionsSetup.marginTop,
+        containLabel: true
+      };
+      this.options.grid = grid;
+    },
+    // tooltip 设置
+    setOptionsTooltip() {
+      const optionsSetup = this.optionsSetup;
+      const tooltip = {
+        trigger: "item",
+        position: "top",
+        show: true,
+        textStyle: {
+          color: optionsSetup.lineColor,
+          fontSize: optionsSetup.tipsLineColor
+        }
+      };
+      this.options.tooltip = tooltip;
+    },
+    // 图设置
+    setOptionsVisualMap() {
+      const optionsSetup = this.optionsSetup;
+      const visualMap = this.options.visualMap;
+      visualMap.show = optionsSetup.isShowLegend;
+      visualMap.min = optionsSetup.dataMin;
+      visualMap.max = optionsSetup.dataMax;
+      visualMap.textStyle = {
+        fontSize : optionsSetup.lengedFontSize,
+        color : optionsSetup.lengedColor
+      };
+      visualMap.inRange.color = optionsSetup.lengedColorList.map((x) => {
+        return x.color;
+      });
+      visualMap.left = optionsSetup.lateralPosition;
+      visualMap.top = optionsSetup.longitudinalPosition;
+      visualMap.bottom = optionsSetup.longitudinalPosition;
+      visualMap.orient = optionsSetup.layoutFront;
+      visualMap.itemWidth = optionsSetup.lengedWidth;
+    },
+    setOptionsData() {
+      const optionsData = this.optionsData; // 数据类型 静态 or 动态
+      optionsData.dataType == "staticData"
+        ? this.staticDataFn(optionsData.staticData)
+        : this.dynamicDataFn(optionsData.dynamicData, optionsData.refreshTime);
+    },
+    //去重
+    setUnique(arr) {
+      let newArr = [];
+      arr.forEach(item => {
+        return newArr.includes(item) ? '' : newArr.push(item);
+      });
+      return newArr;
+    },
+    staticDataFn(val) {
+      const data = [];
+      let xAxisList = [];
+      let yAxisList = [];
+      for (const i in val) {
+        xAxisList[i] = val[i].axis;
+        yAxisList[i] = val[i].yaxis;
+        data[i] = [val[i].axis,val[i].yaxis,val[i].num]
+      }
+      xAxisList = this.setUnique(xAxisList);
+      yAxisList = this.setUnique(yAxisList);
+      this.options.xAxis.data = xAxisList;
+      this.options.yAxis.data = yAxisList;
+      this.options.series[0].data = data;
+    },
+    dynamicDataFn(val, refreshTime) {
+      if (!val) return;
+      if (this.ispreview) {
+        this.getEchartData(val);
+        this.flagInter = setInterval(() => {
+          this.getEchartData(val);
+        }, refreshTime);
+      } else {
+        this.getEchartData(val);
+      }
+    },
+    getEchartData(val) {
+      const data = this.queryEchartsData(val);
+      data.then((res) => {
+        this.renderingFn(res);
+      });
+    },
+    renderingFn(val) {
+      this.options.xAxis.data = val.xAxis;
+      this.options.yAxis.data = val.yAxis;
+      this.options.series[0].data = val.series;
+    },
+  },
+};
+</script>
+
+<style scoped lang="scss">
+.echarts {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+}
+</style>

+ 1 - 1
report-ui/src/views/bigscreenDesigner/designer/widget/line/widgetLineCompareChart.vue

@@ -618,7 +618,7 @@ export default {
       xAxisList = this.setUnique(xAxisList);
       yAxisList = this.setUnique(yAxisList);
       for (const i in yAxisList) {
-        const data = new Array(yAxisList.length).fill(0);
+        const data = new Array(xAxisList.length).fill(0);
         for (const j in xAxisList) {
           for (const k in val) {
             if (val[k].name == yAxisList[i]) {

+ 1 - 1
report-ui/src/views/bigscreenDesigner/designer/widget/line/widgetLineStackChart.vue

@@ -332,7 +332,7 @@ export default {
       xAxisList = this.setUnique(xAxisList);
       yAxisList = this.setUnique(yAxisList);
       for (const i in yAxisList) {
-        const data = new Array(yAxisList.length).fill(0);
+        const data = new Array(xAxisList.length).fill(0);
         for (const j in xAxisList) {
           for (const k in val) {
             if (val[k].name == yAxisList[i]) {

+ 1 - 46
report-ui/src/views/bigscreenDesigner/designer/widget/line/widgetLinechart.vue

@@ -105,7 +105,6 @@ export default {
       this.setOptionsTooltip();
       this.setOptionsData();
       this.setOptionsMargin();
-      this.setOptionsLegend();
       this.setOptionsColor();
     },
     // 标题修改
@@ -244,7 +243,7 @@ export default {
         show: true,
         textStyle: {
           color: optionsSetup.lineColor,
-          fontSize: optionsSetup.fontSize
+          fontSize: optionsSetup.tipsFontSize
         }
       };
       this.options.tooltip = tooltip;
@@ -261,42 +260,6 @@ export default {
       };
       this.options.grid = grid;
     },
-    // 图例操作 legend
-    setOptionsLegend() {
-      const optionsSetup = this.optionsSetup;
-      const legend = this.options.legend;
-      legend.show = optionsSetup.isShowLegend;
-      legend.left = optionsSetup.lateralPosition;
-      legend.right = optionsSetup.lateralPosition;
-      legend.top = optionsSetup.longitudinalPosition;
-      legend.bottom =
-        optionsSetup.longitudinalPosition;
-      legend.orient = optionsSetup.layoutFront;
-      legend.textStyle = {
-        color: optionsSetup.lengedColor,
-        fontSize: optionsSetup.fontSize
-      };
-      legend.itemWidth = optionsSetup.lengedWidth;
-    },
-    // 图例名称设置
-    setOptionsLegendName(name){
-      const optionsSetup = this.optionsSetup;
-      const series = this.options.series;
-      const legendName = optionsSetup.legendName;
-      // 图例没有手动写则显示原值,写了则显示新值
-      if (null == legendName || legendName == '') {
-        for (let i = 0; i < name.length; i++) {
-          series[i].name = name[i];
-        }
-        this.options.legend['data'] = name;
-      }else {
-        const arr = legendName.split('|');
-        for (let i = 0; i < arr.length; i++) {
-          series[i].name = arr[i];
-        }
-        this.options.legend['data'] = arr
-      }
-    },
     // 图例颜色修改
     setOptionsColor() {
       const optionsSetup = this.optionsSetup;
@@ -332,10 +295,6 @@ export default {
           series[i].data = data;
         }
       }
-      const legendName = [];
-      legendName.push('销售量')
-      this.options.legend['data'] = legendName;
-      this.setOptionsLegendName(legendName);
     },
     dynamicDataFn(val, refreshTime) {
       if (!val) return;
@@ -359,15 +318,11 @@ export default {
       this.options.xAxis.data = val.xAxis;
       // series
       const series = this.options.series;
-      const legendName = [];
       for (const i in series) {
         if (series[i].type == "line") {
           series[i].data = val.series[i].data;
         }
-        legendName.push(val.series[i].name);
       }
-      this.options.legend['data'] = legendName;
-      this.setOptionsLegendName(legendName);
     }
   }
 };

+ 3 - 1
report-ui/src/views/bigscreenDesigner/designer/widget/temp.vue

@@ -38,6 +38,7 @@ import widgetLineCompareChart from "./line/widgetLineCompareChart";
 import widgetDecoratePieChart from "./decorate/widgetDecoratePieChart";
 import widgetMoreBarLineChart from "./bar/widgetMoreBarLineChart";
 import widgetWordCloud from "./wordcloud/widgetWordCloud";
+import widgetHeatmap from "./heatmap/widgetHeatmap";
 
 export default {
   name: "WidgetTemp",
@@ -68,7 +69,8 @@ export default {
     widgetLineCompareChart,
     widgetDecoratePieChart,
     widgetMoreBarLineChart,
-    widgetWordCloud
+    widgetWordCloud,
+    widgetHeatmap
   },
   model: {
     prop: "value",

+ 9 - 10
report-ui/src/views/bigscreenDesigner/designer/widget/widget.vue

@@ -17,7 +17,7 @@
     @focus="handleFocus"
     @blur="handleBlur"
   >
-    <component :is="type" :value="value"/>
+    <component :is="type" :value="value" />
   </avue-draggable>
 </template>
 
@@ -49,6 +49,7 @@ import widgetLineCompareChart from "./line/widgetLineCompareChart";
 import widgetDecoratePieChart from "./decorate/widgetDecoratePieChart";
 import widgetMoreBarLineChart from "./bar/widgetMoreBarLineChart";
 import widgetWordCloud from "./wordcloud/widgetWordCloud";
+import widgetHeatmap from "./heatmap/widgetHeatmap";
 
 export default {
   name: "Widget",
@@ -79,7 +80,8 @@ export default {
     widgetLineCompareChart,
     widgetDecoratePieChart,
     widgetMoreBarLineChart,
-    widgetWordCloud
+    widgetWordCloud,
+    widgetHeatmap
   },
   model: {
     prop: "value",
@@ -95,8 +97,7 @@ export default {
     bigscreen: Object,
     value: {
       type: [Object],
-      default: () => {
-      }
+      default: () => {}
     },
     step: Number
   },
@@ -126,13 +127,11 @@ export default {
       return this.value.position.zIndex || 1;
     }
   },
-  mounted() {
-  },
+  mounted() {},
   methods: {
-    handleFocus({index, left, top, width, height}) {
-    },
-    handleBlur({index, left, top, width, height}) {
-      this.$emit("onActivated", {index, left, top, width, height});
+    handleFocus({ index, left, top, width, height }) {},
+    handleBlur({ index, left, top, width, height }) {
+      this.$emit("onActivated", { index, left, top, width, height });
       this.$refs.draggable.setActive(true);
     }
   }

+ 3 - 18
report-ui/src/views/bigscreenDesigner/designer/widget/widgetImage.vue

@@ -41,7 +41,8 @@ export default {
       return {
         imageAdress: this.transStyle.imageAdress,
         "border-radius": this.transStyle.borderRadius + "px",
-        opacity: this.transStyle.transparency / 100
+        opacity: this.transStyle.transparency / 100,
+        animation: this.transStyle.startRotate? "turn "+(101-this.transStyle.rotationSpeed)/10+"s linear infinite":"none"
       };
     }
   },
@@ -74,21 +75,5 @@ export default {
 .startImg {
   animation: turn 1s linear infinite;
 }
-@keyframes turn {
-  0% {
-    -webkit-transform: rotate(0deg);
-  }
-  25% {
-    -webkit-transform: rotate(90deg);
-  }
-  50% {
-    -webkit-transform: rotate(180deg);
-  }
-  75% {
-    -webkit-transform: rotate(270deg);
-  }
-  100% {
-    -webkit-transform: rotate(360deg);
-  }
-}
+
 </style>

+ 43 - 23
report-ui/src/views/datasource/index.vue

@@ -4,28 +4,10 @@
  * @Author: qianlishi
  * @Date: 2021-12-11 14:48:27
  * @LastEditors: qianlishi
- * @LastEditTime: 2021-12-24 14:00:47
+ * @LastEditTime: 2022-03-09 09:43:31
 -->
 <template>
   <anji-crud ref="listPage" :option="crudOption">
-    <template v-slot:buttonLeftOnTable>
-      <el-button
-        type="primary"
-        icon="el-icon-plus"
-        @click="operateDatasource('add')"
-        v-permission="'datasourceManage:insert'"
-        >新增</el-button
-      >
-    </template>
-
-    <template slot="rowButton" slot-scope="props">
-      <el-button
-        type="text"
-        @click="operateDatasource('edit', props)"
-        v-permission="'datasourceManage:update'"
-        >编辑</el-button
-      >
-    </template>
     <template v-slot:pageSection>
       <EditDataSource
         ref="EditDataSource"
@@ -60,6 +42,46 @@ export default {
         title: "数据源",
         // 详情页中输入框左边文字宽度
         labelWidth: "120px",
+        // 表头按钮
+        tableButtons: [
+          {
+            label: "新增",
+            type: "", // primary、success、info、warning、danger
+            permission: "datasourceManage:insert", // 按钮权限码
+            icon: "el-icon-plus",
+            plain: true,
+            click: () => {
+              return this.operateDatasource("add");
+            }
+          },
+          {
+            label: "删除",
+            type: "danger",
+            permission: "datasourceManage:delete",
+            icon: "el-icon-delete",
+            plain: false,
+            click: () => {
+              return this.$refs.listPage.handleDeleteBatch();
+            }
+          }
+        ],
+        // 表格行按钮
+        rowButtons: [
+          {
+            label: "编辑",
+            permission: "datasourceManage:update",
+            click: row => {
+              return this.operateDatasource("edit", row);
+            }
+          },
+          {
+            label: "删除",
+            permission: "datasourceManage:delete",
+            click: row => {
+              return this.$refs.listPage.handleDeleteBatch(row);
+            }
+          }
+        ],
         // 查询表单条件
         queryFormFields: [
           {
@@ -83,9 +105,7 @@ export default {
         ],
         // 操作按钮
         buttons: {
-          customButton: {
-            operationWidth: 150
-          },
+          rowButtonsWidth: 150, // row自定义按钮表格宽度
           query: {
             api: reportDataSourceList,
             permission: "datasourceManage:query",
@@ -218,7 +238,7 @@ export default {
     operateDatasource(type, prop) {
       this.dialogVisibleSetDataSource = true;
       if (prop) {
-        this.dataSource = prop.msg;
+        this.dataSource = prop;
       } else {
         this.dataSource = {};
       }

+ 41 - 3
report-ui/src/views/dict/dict-item.vue

@@ -64,6 +64,46 @@ export default {
             field: "locale"
           }
         ],
+        // 表头按钮
+        tableButtons: [
+          {
+            label: "新增",
+            type: "", // primary、success、info、warning、danger
+            permission: "dictItemManage:insert", // 按钮权限码
+            icon: "el-icon-plus",
+            plain: true,
+            click: () => {
+              return this.$refs.listPage.handleOpenEditView("add");
+            }
+          },
+          {
+            label: "删除",
+            type: "danger",
+            permission: "dictItemManage:delete",
+            icon: "el-icon-delete",
+            plain: false,
+            click: () => {
+              return this.$refs.listPage.handleDeleteBatch();
+            }
+          }
+        ],
+        // 表格行按钮
+        rowButtons: [
+          {
+            label: "编辑",
+            permission: "dictItemManage:update",
+            click: row => {
+              return this.$refs.listPage.handleOpenEditView("edit", row);
+            }
+          },
+          {
+            label: "删除",
+            permission: "dictItemManage:delete",
+            click: row => {
+              return this.$refs.listPage.handleDeleteBatch(row);
+            }
+          }
+        ],
         // 操作按钮
         buttons: {
           query: {
@@ -86,9 +126,7 @@ export default {
             api: dictItemEdit,
             permission: "dictItemManage:update"
           },
-          customButton: {
-            operationWidth: "150px"
-          }
+          rowButtonsWidth: 150 // row自定义按钮表格宽度
         },
         // 表格列
         columns: [

+ 62 - 7
report-ui/src/views/dict/index.vue

@@ -4,7 +4,7 @@
  * @Author: qianlishi
  * @Date: 2021-12-11 14:48:27
  * @LastEditors: qianlishi
- * @LastEditTime: 2021-12-13 13:46:33
+ * @LastEditTime: 2022-03-09 10:02:26
 -->
 <template>
   <anji-crud ref="listPage" :option="crudOption">
@@ -75,6 +75,64 @@ export default {
             that.$store.commit("user/SET_PROJECT", queryForm["project"]);
           }
         },
+        // 表头按钮
+        tableButtons: [
+          {
+            label: "刷新字典项",
+            type: "primary", // primary、success、info、warning、danger
+            permission: "dictManage:fresh", // 按钮权限码
+            icon: "el-icon-edit",
+            plain: true,
+            click: this.dictRefresh
+          },
+          {
+            label: "新增",
+            type: "", // primary、success、info、warning、danger
+            permission: "dictManage:insert", // 按钮权限码
+            icon: "el-icon-plus",
+            plain: true,
+            click: () => {
+              return this.$refs.listPage.handleOpenEditView("add");
+            }
+          },
+          {
+            label: "删除",
+            type: "danger",
+            permission: "dictManage:delete",
+            icon: "el-icon-delete",
+            plain: false,
+            click: () => {
+              return this.$refs.listPage.handleDeleteBatch();
+            }
+          }
+        ],
+        // 表格行按钮
+        rowButtons: [
+          {
+            label: "编辑",
+            permission: "dictManage:update",
+            click: row => {
+              return this.$refs.listPage.handleOpenEditView("edit", row);
+            }
+          },
+          {
+            label: "编辑字典项",
+            permission: "dictItemManage:query",
+            click: this.editItem
+          },
+          {
+            label: "刷新字典项",
+            permission: "dictManage:fresh",
+            click: this.itemRefresh
+          },
+          {
+            label: "删除",
+            permission: "dictManage:delete",
+            click: row => {
+              return this.$refs.listPage.handleDeleteBatch(row);
+            }
+          }
+        ],
         // 操作按钮
         buttons: {
           query: {
@@ -97,10 +155,7 @@ export default {
             api: dictEdit,
             permission: "dictManage:update"
           },
-          // 自定义按钮
-          customButton: {
-            operationWidth: 160 // row自定义按钮表格宽度
-          }
+          rowButtonsWidth: 150 // row自定义按钮表格宽度
         },
         // 表格列
         columns: [
@@ -200,7 +255,7 @@ export default {
     },
     // 刷新某个字典
     async itemRefresh(val) {
-      const selectedList = val.msg;
+      const selectedList = val;
       let dictCodes = [];
       if (selectedList.length > 0) {
         dictCodes = selectedList.map(item => item.dictCode);
@@ -214,7 +269,7 @@ export default {
       this.$router.push({
         path: "/system/dictItem",
         query: {
-          dictCode: val.msg.dictCode,
+          dictCode: val.dictCode,
           project: this.$store.state.user.project
         }
       });

+ 36 - 8
report-ui/src/views/fileManagement/index.vue

@@ -4,11 +4,11 @@
  * @Author: qianlishi
  * @Date: 2021-12-11 14:48:27
  * @LastEditors: qianlishi
- * @LastEditTime: 2021-12-13 13:47:02
+ * @LastEditTime: 2022-03-09 09:57:17
 -->
 <template>
   <anji-crud ref="listPage" :option="crudOption">
-    <template v-slot:buttonLeftOnTable>
+    <template v-slot:tableButtons>
       <el-upload
         class="el-upload"
         ref="upload"
@@ -61,6 +61,37 @@ export default {
             field: "filePath"
           }
         ],
+        // 表头按钮
+        tableButtons: [
+          {
+            label: "删除",
+            type: "danger",
+            permission: "fileManage:delete",
+            icon: "el-icon-delete",
+            plain: false,
+            click: () => {
+              return this.$refs.listPage.handleDeleteBatch();
+            }
+          }
+        ],
+        // 表格行按钮
+        rowButtons: [
+          {
+            label: "复制url",
+            click: this.copyUrlPath
+          },
+          {
+            label: "下载",
+            click: this.customButtom
+          },
+          {
+            label: "删除",
+            permission: "fileManage:delete",
+            click: row => {
+              return this.$refs.listPage.handleDeleteBatch(row);
+            }
+          }
+        ],
         // 操作按钮
         buttons: {
           query: {
@@ -87,10 +118,7 @@ export default {
             permission: "fileManage:update",
             isShow: false
           },
-          // 自定义按钮
-          customButton: {
-            operationWidth: 160 // row自定义按钮表格宽度
-          }
+          rowButtonsWidth: 150 // row自定义按钮表格宽度
         },
         // 表格列
         columns: [
@@ -233,10 +261,10 @@ export default {
       window.open(row.urlPath);
     },
     customButtom(val) {
-      this.downloadFile(val.msg);
+      this.downloadFile(val);
     },
     copyUrlPath(val) {
-      this.copyToClip(val.msg.urlPath);
+      this.copyToClip(val.urlPath);
       this.$message({
         message: "已将url路径复制至剪切板!",
         type: "success"

+ 1 - 1
report-ui/src/views/layout/components/Sidebar/index.vue

@@ -3,7 +3,7 @@
     <div class="admin-title" @click="goBigScreen">
       <div class="con">
         <img src="../../../../../static/logo-dp.png" width="50" />
-        <span class="version">V0.9.6.1</span>
+        <span class="version">V0.9.7</span>
       </div>
     </div>
     <el-menu

+ 1 - 1
report-ui/src/views/login.vue

@@ -13,7 +13,7 @@
       <img src="@/assets/images/home-logo.png" alt="logo" />
     </div>
     <div class="login_contant">
-      <img src="@/assets/images/login.png" alt="image" class="login_img" />
+      <img src="@/assets/images/login.jpg" alt="image" class="login_img" />
       <el-form
         ref="loginForm"
         :model="loginForm"

+ 84 - 27
report-ui/src/views/reportManage/index.vue

@@ -4,31 +4,10 @@
  * @Author: qianlishi
  * @Date: 2021-12-11 14:48:27
  * @LastEditors: qianlishi
- * @LastEditTime: 2021-12-13 12:20:46
+ * @LastEditTime: 2022-05-15 10:44:58
 -->
 <template>
   <anji-crud ref="listPage" :option="crudOption">
-    <template slot="rowButtonInMore" slot-scope="props">
-      <el-button
-        type="text"
-        @click="preview(props.msg)"
-        v-permission="'bigScreenManage:view'"
-        >预览</el-button
-      >
-      <el-button
-        type="text"
-        @click="design(props.msg)"
-        v-permission="'bigScreenManage:design'"
-        >设计</el-button
-      >
-      <el-button
-        type="text"
-        @click="shareReport(props.msg)"
-        v-permission="'bigScreenManage:share'"
-        >分享</el-button
-      >
-    </template>
-
     <template v-slot:pageSection>
       <Share
         :visib="visibleForShareDialog"
@@ -45,10 +24,12 @@ import {
   reportAdd,
   reportDeleteBatch,
   reportUpdate,
-  reportDetail
+  reportDetail,
+  reportCopy
 } from "@/api/reportmanage";
 import Share from "./components/share";
 import { validateEngOrNum } from "@/utils/validate";
+import { verificationSet } from "@/api/report";
 export default {
   name: "Report",
   components: {
@@ -92,6 +73,66 @@ export default {
             field: "reportAuthor"
           }
         ],
+        // 表头按钮
+        tableButtons: [
+          {
+            label: "新增",
+            type: "", // primary、success、info、warning、danger
+            permission: "reportManage:insert", // 按钮权限码
+            icon: "el-icon-plus",
+            plain: true,
+            click: () => {
+              return this.$refs.listPage.handleOpenEditView("add");
+            }
+          },
+          {
+            label: "删除",
+            type: "danger",
+            permission: "reportManage:delete",
+            icon: "el-icon-delete",
+            plain: false,
+            click: () => {
+              return this.$refs.listPage.handleDeleteBatch();
+            }
+          }
+        ],
+        // 表格行按钮
+        rowButtons: [
+          {
+            label: "编辑",
+            permission: "reportManage:update",
+            click: row => {
+              return this.$refs.listPage.handleOpenEditView("edit", row);
+            }
+          },
+          {
+            label: "预览",
+            permission: "bigScreenManage:view",
+            click: this.preview
+          },
+          {
+            label: "设计",
+            permission: "bigScreenManage:design",
+            click: this.design
+          },
+          {
+            label: "分享",
+            permission: "bigScreenManage:share",
+            click: this.shareReport
+          },
+          {
+            label: "复制",
+            permission: "bigScreenManage:copy",
+            click: this.copyReport
+          },
+          {
+            label: "删除",
+            permission: "reportManage:delete",
+            click: row => {
+              return this.$refs.listPage.handleDeleteBatch(row);
+            }
+          }
+        ],
         // 操作按钮
         buttons: {
           query: {
@@ -116,9 +157,7 @@ export default {
             api: reportUpdate,
             permission: "reportManage:update"
           },
-          customButton: {
-            operationWidth: "150px"
-          }
+          rowButtonsWidth: 150 // row自定义按钮表格宽度
         },
         // 表格列
         columns: [
@@ -257,7 +296,16 @@ export default {
         // fieldName 触发修改的input name
         // fieldVal input最新值
         // fieldExtend 对于select型的扩展值
-        formChange: (formData, fieldName, fieldVal, fieldExtend) => {}
+        formChange: (formData, fieldName, fieldVal, fieldExtend) => {
+          console.log(formData);
+          if (fieldName == "reportImage") {
+            if (fieldVal.length > 0) {
+              formData["reportImage"] = fieldVal && fieldVal[0].url;
+            } else {
+              formData["reportImage"] = "";
+            }
+          }
+        }
       }
     };
   },
@@ -307,6 +355,15 @@ export default {
       this.reportCodeForShareDialog = val.reportCode;
       this.reportNameForShareDialog = val.reportName;
       this.visibleForShareDialog = true;
+    },
+    //复制
+    async copyReport(val) {
+      const { code } = await reportCopy(val);
+      if (code != "200") {
+        return;
+      }
+      this.$message.success("复制成功");
+      this.$refs.listPage.handleQueryForm("query");
     }
   }
 };

+ 42 - 22
report-ui/src/views/resultset/components/EditDataSet.vue

@@ -219,6 +219,7 @@
                               v-model="item.transformType"
                               :updata-dict="item.transformType"
                               :dict-key="'TRANSFORM_TYPE'"
+                              @change="changeForm"
                             />
                             <el-button
                               type="text"
@@ -229,7 +230,8 @@
                             <el-button
                               v-if="
                                 item.transformType == 'js' ||
-                                  item.transformType == 'dict'
+                                  item.transformType == 'dict' ||
+                                  item.transformType == 'javaBean'
                               "
                               type="text"
                               class="editor"
@@ -251,10 +253,16 @@
                           min-height="400px"
                           append-to-body
                         >
-                          <div v-if="isItemFilterType.transformType == 'js'">
+                          <div
+                            v-if="
+                              isItemFilterType.transformType == 'js' ||
+                                isItemFilterType.transformType == 'javaBean'
+                            "
+                          >
                             <div class="codemirror">
                               <!-- //自定义高级规则? -->
                               <monaco-editor
+                                v-if="jsScriptVisible"
                                 v-model.trim="transformScript"
                                 language="javascript"
                                 style="height: 500px"
@@ -482,6 +490,7 @@ export default {
       dialogFormVisibleTitle: "",
       dialogPermissionVisible: false,
       dialogSwitchVisible: false,
+      jsScriptVisible: false,
       permissionTextarea: "",
       isItemFilterType: "", // 选中的转换类型id
       itemFilterList: [
@@ -533,7 +542,7 @@ export default {
       ],
       isRowData: {},
       tableData2: [],
-      dialogTitle: "js脚本填写",
+      dialogTitle: "脚本填写",
       isShowPagination: false,
       updataDisabled: false,
       dialogCaseResult: false,
@@ -662,6 +671,12 @@ export default {
         this.isShowPagination = false;
       }
     },
+    changeForm(val) {
+      if (!val) {
+        this.dialogSwitchVisible = false;
+        this.jsScriptVisible = false
+      }
+    },
     // 关闭模态框
     closeDialog() {
       this.$emit("handleClose");
@@ -743,6 +758,7 @@ export default {
       console.log(item);
       this.isItemFilterType = item;
       this.dialogSwitchVisible = true;
+      this.jsScriptVisible = true
       if (item.transformType == "js") {
         this.itemFilterScriptId = item.itemFilterSort;
         const fnCont = `function dataTransform(data){\n\t//自定义脚本内容\n\treturn data;\n}`;
@@ -750,25 +766,29 @@ export default {
           ? item.transformScript
           : fnCont;
       } else if (item.transformType == "dict") {
-        // this.dialogTitle = '字典翻译'
-        // this.itemFilterScriptId = item.itemFilterSort
-        // const { code, data } = await getDictList('TRANSFORM_TYPE')
-        // if (code != '200') return
-        // const extend = data.find(function (y) {
-        //   if (y.id == item.transformType) {
-        //     return y
-        //   }
-        // })
-        // const extendArry = []
-        // const extendObj = JSON.parse(extend.extend)
-        // for (const i in extendObj) {
-        //   const children = []
-        //   for (const y in extendObj[i]) {
-        //     children.push({ name: y, value: extendObj[i][y] })
-        //   }
-        //   extendArry.push({ name: i, children: children })
-        // }
-        // this.tableData2 = extendArry
+      } else if (item.transformType == "javaBean") {
+        this.itemFilterScriptId = item.itemFilterSort;
+        const fnCont = `package com;
+
+import com.alibaba.fastjson.JSONObject;
+import com.anjiplus.template.gaea.business.modules.datasettransform.service.IGroovyHandler;
+
+import java.util.List;
+
+/**
+ * 建议在idea写好复制整个类到此处,位置report-core/src/test/java/com/DemoGroovyHandler.java
+ */
+public class DemoGroovyHandler implements IGroovyHandler {
+
+    @Override
+    public List<JSONObject> transform(List<JSONObject> data) {
+
+        return data;
+    }
+}`;
+        this.transformScript = item.transformScript
+          ? item.transformScript
+          : fnCont;
       }
     },
     // js 脚本编辑确定

+ 43 - 27
report-ui/src/views/resultset/index.vue

@@ -4,11 +4,11 @@
  * @Author: qianlishi
  * @Date: 2021-12-11 14:48:27
  * @LastEditors: qianlishi
- * @LastEditTime: 2021-12-24 14:01:19
+ * @LastEditTime: 2022-04-14 18:48:24
 -->
 <template>
   <anji-crud ref="listPage" :option="crudOption">
-    <template v-slot:buttonLeftOnTable>
+    <template v-slot:tableButtons>
       <el-dropdown placement="bottom" @command="operateDataset">
         <el-button type="primary" icon="el-icon-plus">
           新增
@@ -21,23 +21,6 @@
       </el-dropdown>
     </template>
 
-    <template slot="rowButton" slot-scope="props">
-      <el-button
-        type="text"
-        @click="operateDataset('edit', props)"
-        v-permission="'resultsetManage:update'"
-        >编辑
-      </el-button>
-    </template>
-
-    <template slot="rowButtonInMore" slot-scope="props">
-      <el-button
-        type="text"
-        @click="dataView(props)"
-        v-permission="'resultsetManage:query'"
-        >数据预览
-      </el-button>
-    </template>
     <template v-slot:pageSection>
       <EditDataSet
         ref="EditDataSet"
@@ -113,11 +96,44 @@ export default {
             field: "setType"
           }
         ],
+        // 表头按钮
+        tableButtons: [
+          {
+            label: "删除",
+            type: "danger",
+            permission: "resultsetManage:delete",
+            icon: "el-icon-delete",
+            plain: false,
+            click: () => {
+              return this.$refs.listPage.handleDeleteBatch();
+            }
+          }
+        ],
+        // 表格行按钮
+        rowButtons: [
+          {
+            label: "编辑",
+            permission: "resultsetManage:update",
+            click: row => {
+              return this.operateDataset("edit", row);
+            }
+          },
+          {
+            label: "数据预览",
+            permission: "resultsetManage:query",
+            click: this.dataView
+          },
+          {
+            label: "删除",
+            permission: "resultsetManage:delete",
+            click: row => {
+              return this.$refs.listPage.handleDeleteBatch(row);
+            }
+          }
+        ],
         // 操作按钮
         buttons: {
-          customButton: {
-            operationWidth: 180
-          },
+          rowButtonsWidth: 180, // row自定义按钮表格宽度
           query: {
             api: reportDataSetList,
             permission: "resultsetManage:query",
@@ -275,9 +291,9 @@ export default {
   methods: {
     operateDataset(type, prop) {
       this.dialogVisibleSetDataSet = true;
-      if (prop && prop.msg) {
-        this.dataSet = prop.msg;
-        type = prop.msg.setType;
+      if (prop && prop.setType) {
+        this.dataSet = prop;
+        type = prop.setType;
       } else {
         this.dataSet = {};
       }
@@ -289,8 +305,8 @@ export default {
     dataView(prop) {
       this.dialogCaseResult = true;
       this.$refs.DataView.dataViewPreview(
-        prop.msg.setName,
-        JSON.parse(prop.msg.caseResult)
+        prop.setName,
+        JSON.parse(prop.caseResult)
       );
     }
   }

+ 71 - 0
report-ui/src/views/screenDesigner/components/colorPicker.vue

@@ -0,0 +1,71 @@
+<template>
+  <el-input
+    clearable
+    v-model="colorValue"
+    placeholder="请输入颜色"
+    size="mini"
+    @change="changeColor"
+  >
+    <template slot="append">
+      <el-color-picker
+        v-model="colorValue"
+        :predefine="predefineColors"
+        show-alpha
+        size="mini"
+        @change="changeColor"
+      />
+    </template>
+  </el-input>
+</template>
+
+<script>
+export default {
+  name: "ColorPicker",
+  model: {
+    prop: "value",
+    event: "input"
+  },
+  props: {
+    value: {
+      type: [String],
+      default: ""
+    }
+  },
+  data() {
+    return {
+      predefineColors: [
+        "#ff4500",
+        "#ff8c00",
+        "#ffd700",
+        "#90ee90",
+        "#00ced1",
+        "#1e90ff",
+        "#c71585"
+      ],
+      colorValue: ""
+    };
+  },
+  watch: {
+    value(val) {
+      this.colorValue = val || "";
+    }
+  },
+  mounted() {
+    this.colorValue = this.value;
+  },
+  methods: {
+    changeColor(val) {
+      this.colorValue = val || "";
+      this.$emit("input", this.colorValue);
+      this.$emit("change", this.colorValue);
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+/deep/.el-color-picker--mini,
+/deep/.el-color-picker--mini .el-color-picker__trigger {
+  width: 23px;
+  height: 23px;
+}
+</style>

+ 106 - 0
report-ui/src/views/screenDesigner/components/contentMenu.vue

@@ -0,0 +1,106 @@
+<template>
+  <div v-show="visible" class="contentmenu" :style="styleObj">
+    <div class="contentmenu__item" @click="deleteLayer">
+      <i class="iconfont iconguanbi"></i> 删除图层
+    </div>
+    <div class="contentmenu__item" @click="copyLayer">
+      <i class="iconfont iconfuzhi1"></i> 复制图层
+    </div>
+    <div class="contentmenu__item" @click="istopLayer">
+      <i class="iconfont iconjinlingyingcaiwangtubiao01"></i> 置顶图层
+    </div>
+    <div class="contentmenu__item" @click="setlowLayer">
+      <i class="iconfont iconleft-copy"></i> 置底图层
+    </div>
+    <div class="contentmenu__item" @click="moveupLayer">
+      <i class="iconfont iconjinlingyingcaiwangtubiao01"></i> 上移一层
+    </div>
+    <div class="contentmenu__item" @click="movedownLayer">
+      <i class="iconfont iconleft-copy"></i> 下移一层
+    </div>
+  </div>
+</template>
+<script>
+export default {
+  props: {
+    styleObj: Object,
+    visible: Boolean
+  },
+  data() {
+    return {};
+  },
+  watch: {
+    visible(value) {
+      if (value) {
+        document.body.addEventListener("click", this.closeMenu);
+      } else {
+        document.body.removeEventListener("click", this.closeMenu);
+      }
+    }
+  },
+  methods: {
+    closeMenu() {
+      this.$emit("update:visible", false);
+    },
+    deleteLayer() {
+      this.$confirm("是否删除所选图层?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      })
+        .then(() => {
+          this.$emit("deletelayer");
+          this.$message({
+            type: "success",
+            message: "删除成功!"
+          });
+        })
+        .catch(() => {
+          this.$message({
+            type: "info",
+            message: "已取消删除"
+          });
+        });
+    },
+    copyLayer() {
+      this.$emit("copylayer");
+    },
+    istopLayer() {
+      this.$emit("istopLayer");
+    },
+    setlowLayer() {
+      this.$emit("setlowLayer");
+    },
+    moveupLayer() {
+      this.$emit("moveupLayer");
+    },
+    movedownLayer() {
+      this.$emit("movedownLayer");
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+.contentmenu {
+  width: 160px;
+  position: fixed;
+  z-index: 99999;
+  list-style: none;
+  -webkit-box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
+  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
+  padding: 0;
+  background: #27343e;
+  color: #bcc9d4;
+  .contentmenu__item {
+    z-index: 10000;
+    list-style: none;
+    padding: 8px 12px;
+    cursor: pointer;
+    position: relative;
+    font-size: 12px;
+  }
+  .iconfont {
+    font-size: 12px;
+  }
+}
+</style>

+ 191 - 0
report-ui/src/views/screenDesigner/components/customColorComponents.vue

@@ -0,0 +1,191 @@
+<template>
+  <div>
+    <el-button
+      type="primary"
+      size="mini"
+      icon="el-icon-plus"
+      plain
+      @click="handleAddClick"
+      >新增</el-button
+    >
+    <el-table :data="formData" style="width: 100%">
+      <el-table-column prop="color" label="颜色" align="center">
+        <template slot-scope="scope">
+          <span class="color-box" :style="{ background: scope.row.color }" />
+        </template>
+      </el-table-column>
+      <el-table-column label="位置" align="center">
+        <template slot-scope="scope">
+          <span
+            class="editor"
+            @click="handleEditorClick(scope.$index, scope.row)"
+          >
+            <i class="el-icon-edit" /> 编辑
+          </span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center">
+        <template slot-scope="scope">
+          <span
+            class="editor"
+            @click="handleDeleteClick(scope.$index, scope.row)"
+          >
+            <i class="el-icon-delete" /> 删除
+          </span>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <el-dialog
+      title="新增"
+      :visible.sync="dialogVisible"
+      width="30%"
+      :before-close="handleClose"
+    >
+      <el-form>
+        <el-form-item label="颜色">
+          <el-input
+            v-model="colorValue"
+            style="width: 200px"
+            placeholder="请输入颜色"
+            size="mini"
+          >
+            <template slot="append">
+              <el-color-picker
+                v-model="colorValue"
+                :predefine="predefineColors"
+                size="mini"
+              />
+            </template>
+          </el-input>
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button size="mini" @click="dialogVisible = false">取 消</el-button>
+        <el-button size="mini" type="primary" @click="handleSaveClick"
+          >确 定</el-button
+        >
+      </span>
+    </el-dialog>
+  </div>
+</template>
+<script>
+export default {
+  name: "CustomColorComponents",
+  model: {
+    prop: "formData",
+    event: "input"
+  },
+  props: {
+    formData: Array
+  },
+  data() {
+    return {
+      predefineColors: [
+        "#ff4500",
+        "#ff8c00",
+        "#ffd700",
+        "#90ee90",
+        "#00ced1",
+        "#1e90ff",
+        "#c71585"
+      ],
+      colorValue: "",
+      dialogVisible: false,
+      flag: true, // true 新增, false 编辑
+      indexEditor: -1 // 编辑第几个数据
+    };
+  },
+  mounted() {},
+  methods: {
+    // 弹出框关闭
+    handleClose() {
+      this.dialogVisible = false;
+      this.colorValue = "";
+    },
+    // 新增
+    handleAddClick() {
+      this.colorValue = "";
+      this.flag = true;
+      this.dialogVisible = true;
+    },
+    // 确定
+    handleSaveClick() {
+      if (this.flag) {
+        // 新增
+        const obj = {
+          color: this.colorValue
+        };
+        this.formData.push(obj);
+        this.dialogVisible = false;
+      } else {
+        // 编辑
+        this.formData[this.indexEditor].color = this.colorValue;
+        this.dialogVisible = false;
+      }
+      this.$emit("input", this.formData);
+      this.$emit("change", this.formData);
+    },
+    // 编辑
+    handleEditorClick(index, row) {
+      this.flag = false;
+      this.colorValue = row.color;
+      this.dialogVisible = true;
+      this.indexEditor = index;
+    },
+    // 删除
+    handleDeleteClick(index) {
+      this.formData.splice(index, 1);
+      this.$emit("input", this.formData);
+      this.$emit("change", this.formData);
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+.color-box {
+  .title {
+    display: flex;
+    flex-direction: row;
+  }
+}
+/deep/.el-table,
+/deep/.el-table__expanded-cell,
+/deep/.el-table th,
+/deep/.el-table tr {
+  background-color: transparent !important;
+  color: #859094 !important;
+}
+/deep/.el-table td,
+/deep/.el-table th.is-leaf {
+  border-bottom: none;
+  line-height: 26px;
+}
+/deep/.el-table tbody tr:hover > td {
+  background-color: #263445 !important;
+}
+/deep/.el-table::before {
+  height: 0;
+}
+/deep/.el-color-picker--mini,
+/deep/.el-color-picker--mini .el-color-picker__trigger {
+  width: 23px;
+  height: 23px;
+}
+/deep/.el-dialog {
+  background: #1b1e25;
+  .el-dialog__title {
+    color: #fff;
+  }
+}
+.color-box {
+  display: inline-block;
+  width: 20px;
+  height: 20px;
+  border-radius: 5px;
+}
+.editor {
+  color: #409eff;
+  cursor: pointer;
+}
+</style>

+ 101 - 0
report-ui/src/views/screenDesigner/components/customUpload.vue

@@ -0,0 +1,101 @@
+<template>
+  <div>
+    <el-input
+      clearable
+      v-model.trim="uploadImgUrl"
+      size="mini"
+      @change="changeInput"
+    >
+      <template slot="append">
+        <i class="iconfont iconfolder-o"></i>
+        <input type="file" class="file" ref="files" @change="getImages" />
+      </template>
+    </el-input>
+  </div>
+</template>
+<script>
+import axios from "axios";
+import { getToken } from "@/utils/auth";
+export default {
+  model: {
+    prop: "value",
+    event: "input"
+  },
+  props: {
+    value: {
+      type: "",
+      default: ""
+    }
+  },
+  data() {
+    return {
+      requestUrl: process.env.BASE_API + "/file/upload",
+      headers: {
+        Authorization: getToken()
+      },
+      fileList: [],
+      uploadImgUrl: ""
+    };
+  },
+  created() {
+    this.uploadImgUrl = this.value;
+  },
+  methods: {
+    getImages(el) {
+      let file = el.target.files[0];
+      let type = file.type.split("/")[0];
+      if (type === "image") {
+        this.upload(file);
+      } else {
+        this.$message.warn("只能上传图片格式");
+      }
+    },
+    upload(imgUrl) {
+      let that = this;
+      console.log(that.headers);
+      let formdata = new FormData();
+      formdata.append("file", imgUrl);
+      axios
+        .post(this.requestUrl, formdata, {
+          headers: that.headers
+        })
+        .then(response => {
+          let res = response.data;
+          if (res.code == "200") {
+            that.uploadImgUrl = res.data.urlPath;
+            that.$emit("input", that.uploadImgUrl);
+            that.$emit("change", that.uploadImgUrl);
+          }
+        });
+    },
+    changeInput(e) {
+      if (e) {
+        this.uploadImgUrl = e;
+      } else {
+        this.$refs.files.value = "";
+        this.uploadImgUrl = "";
+      }
+      this.$emit("input", this.uploadImgUrl);
+      this.$emit("change", this.uploadImgUrl);
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+.file {
+  position: absolute;
+  width: 100%;
+  padding: 100%;
+  right: 0;
+  top: 0;
+  opacity: 0;
+}
+/deep/.el-input-group__append,
+/deep/.el-input-group__prepend {
+  padding: 0 10px !important;
+  overflow: hidden;
+}
+.iconfont {
+  font-size: 12px;
+}
+</style>

+ 178 - 0
report-ui/src/views/screenDesigner/components/dynamicAddTable.vue

@@ -0,0 +1,178 @@
+<template>
+  <div>
+    <el-button
+      type="primary"
+      size="small"
+      icon="el-icon-plus"
+      plain
+      @click="handleAddClick"
+    >新增</el-button
+    >
+    <el-table :data="formData" style="width: 100%">
+      <el-table-column prop="name" label="名称" width="60" />
+      <el-table-column prop="key" label="key值" width="70" />
+      <el-table-column prop="width" label="宽度" width="50" />
+      <el-table-column label="操作" width="100">
+        <template slot-scope="scope">
+          <div class="button-group">
+            <el-button
+              @click="handleEditorClick(scope.$index, scope.row)"
+              type="text"
+              size="small"
+            >编辑</el-button
+            >
+            <el-button
+              type="text"
+              size="small"
+              @click="handleDeleteClick(scope.$index, scope.row)"
+            >删除</el-button
+            >
+          </div>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <el-dialog
+      title="新增"
+      :visible.sync="dialogVisible"
+      width="30%"
+      :before-close="handleClose"
+    >
+      <el-form :model="rowFormData" label-width="50px">
+        <el-form-item label="名称:">
+          <el-input
+            v-model.trim="rowFormData['name']"
+            placeholder="请输入名称"
+            size="mini"
+          >
+          </el-input>
+        </el-form-item>
+        <el-form-item label="key值:">
+          <el-input
+            v-model.trim="rowFormData['key']"
+            placeholder="请输入key值"
+            size="mini"
+          >
+          </el-input>
+        </el-form-item>
+        <el-form-item label="宽度:">
+          <el-input
+            v-model.trim="rowFormData['width']"
+            placeholder="请输入宽度"
+            size="mini"
+          >
+          </el-input>
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button size="mini" @click="dialogVisible = false">取 消</el-button>
+        <el-button size="mini" type="primary" @click="handleSaveClick"
+        >确 定</el-button
+        >
+      </span>
+    </el-dialog>
+  </div>
+</template>
+<script>
+export default {
+  model: {
+    prop: "formData",
+    event: "input"
+  },
+  props: {
+    formData: Array
+  },
+  data() {
+    return {
+      dialogVisible: false,
+      rowFormData: {
+        name: "",
+        key: "",
+        width: ""
+      },
+      flag: true, // true 新增, false 编辑
+      indexEditor: -1, // 编辑第几个数据
+      tableData: []
+    };
+  },
+  methods: {
+    // 新增
+    handleAddClick() {
+      this.rowFormData = {};
+      this.flag = true;
+      this.dialogVisible = true;
+    },
+    // 编辑
+    handleEditorClick(index, row) {
+      this.flag = false;
+      this.rowFormData = this.deepClone(row);
+      this.indexEditor = index;
+      this.dialogVisible = true;
+    },
+    // 关闭
+    handleClose() {
+      this.dialogVisible = false;
+    },
+    // 保存
+    handleSaveClick() {
+      if (this.flag) {
+        // 新增
+        this.formData.push(this.rowFormData);
+        this.dialogVisible = false;
+      } else {
+        // 编辑
+        this.formData[this.indexEditor] = this.rowFormData;
+        this.$set(this.formData, this.indexEditor, this.rowFormData);
+        this.dialogVisible = false;
+      }
+      this.$emit("input", this.formData);
+      this.$emit("change", this.formData);
+    },
+    // 删除
+    handleDeleteClick(index) {
+      this.formData.splice(index, 1);
+      this.$emit("input", this.formData);
+      this.$emit("change", this.formData);
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+/deep/::-webkit-scrollbar-track-piece {
+  background-color: transparent;
+}
+/deep/ .el-table__body-wrapper::-webkit-scrollbar {
+  width: 0; // 横向滚动条
+  height: 8px; // 纵向滚动条 必写
+}
+// 滚动条的滑块
+/deep/ .el-table__body-wrapper::-webkit-scrollbar-thumb {
+  border-radius: 5px;
+  background-color: rgba(144, 146, 152, 0.3);
+}
+/deep/.el-table,
+/deep/.el-table__expanded-cell,
+/deep/.el-table th,
+/deep/.el-table tr {
+  background-color: transparent !important;
+  color: #859094 !important;
+  font-size: 12px !important;
+}
+/deep/.el-table td,
+/deep/.el-table th.is-leaf {
+  border-bottom: none;
+  line-height: 26px;
+}
+/deep/.el-table tbody tr:hover {
+  background-color: #263445 !important;
+}
+/deep/.el-table tbody tr:hover > td {
+  background-color: #263445 !important;
+}
+/deep/.el-table::before {
+  height: 0;
+}
+.button-group .el-button {
+  padding: 0;
+}
+</style>

+ 161 - 0
report-ui/src/views/screenDesigner/components/dynamicComponents.vue

@@ -0,0 +1,161 @@
+<template>
+  <div>
+    <el-form label-width="100px" label-position="left">
+      <el-form-item label="数据集">
+        <el-select
+          size="mini"
+          v-model="dataSetValue"
+          filterable
+          placeholder="请选择"
+          @change="selectDataSet"
+        >
+          <el-option
+            v-for="item in dataSet"
+            :key="item.code"
+            :label="item.setName"
+            :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item
+        v-for="(item, index) in userNameList"
+        :key="index"
+        :label="item.paramName"
+      >
+        <el-input v-model.trim="item.sampleItem" size="mini" />
+      </el-form-item>
+      <el-form-item v-for="item in setParamList" :key="item" :label="item">
+        <Dictionary
+          v-model="params[item]"
+          :dict-key="getDictKey()"
+          @input="selectParams($event, item)"
+        />
+      </el-form-item>
+      <el-button
+        style="width: 100%"
+        type="primary"
+        plain
+        size="mini"
+        @click="saveDataBtn"
+        >刷新</el-button
+      >
+    </el-form>
+  </div>
+</template>
+<script>
+import { queryAllDataSet, detailBysetId } from "@/api/bigscreen";
+import Dictionary from "@/components/Dictionary/index";
+
+export default {
+  name: "DynamicComponents",
+  components: {
+    Dictionary
+  },
+  model: {
+    prop: "formData",
+    event: "input"
+  },
+  props: {
+    chartType: String,
+    dictKey: String,
+    formData: Object
+  },
+  data() {
+    return {
+      dataSetValue: "",
+      dataSet: [], // 数据集
+      userNameList: [], // 用户
+      setParamList: [], // 对应的不同的图形数据参数
+      params: {},
+      chartProperties: {}
+    };
+  },
+  watch: {
+    formData: {
+      handler(val) {
+        this.echoDataSet(val);
+      },
+      deep: true
+    }
+  },
+  computed: {
+    setCode() {
+      let code = "";
+      this.dataSet.forEach(el => {
+        if (el.id == this.dataSetValue) {
+          code = el.setCode;
+        }
+      });
+      return code;
+    }
+  },
+  mounted() {
+    this.loadDataSet();
+    this.echoDataSet(this.formData);
+  },
+  methods: {
+    async loadDataSet() {
+      const { code, data } = await queryAllDataSet();
+      this.dataSet = data;
+      if (code != "200") return;
+    },
+    async selectDataSet() {
+      const { code, data } = await detailBysetId(this.dataSetValue);
+      this.userNameList = data.dataSetParamDtoList;
+      this.setParamList = data.setParamList;
+      if (code != "200") return;
+    },
+    async saveDataBtn() {
+      const contextData = {};
+      for (let i = 0; i < this.userNameList.length; i++) {
+        contextData[this.userNameList[i].paramName] = this.userNameList[
+          i
+        ].sampleItem;
+      }
+      const params = {
+        chartType: this.chartType,
+        setCode: this.setCode,
+        chartProperties: this.chartProperties,
+        contextData
+      };
+      this.$emit("input", params);
+      this.$emit("change", params);
+    },
+    selectParams(val, key) {
+      this.chartProperties[key] = val;
+    },
+    getDictKey() {
+      return this.dictKey == null ? "CHART_PROPERTIES" : this.dictKey;
+    },
+    // 数据集回显
+    async echoDataSet(val) {
+      if (!val) return;
+      const setCode = val.setCode;
+
+      await this.loadDataSet();
+
+      this.dataSetValue = this.dataSet.filter(
+        el => setCode == el.setCode
+      )[0].id;
+
+      await this.selectDataSet();
+      this.echoDynamicData(val);
+    },
+    echoDynamicData(val) {
+      const chartProperties = this.deepClone(val.chartProperties);
+      this.chartProperties = chartProperties;
+      if (this.userNameList.length > 0) {
+      }
+      if (this.setParamList.length > 0) {
+        for (let i = 0; i < this.setParamList.length; i++) {
+          const item = this.setParamList[i];
+          if (chartProperties.hasOwnProperty(item)) {
+            this.params[item] = chartProperties[item];
+          }
+        }
+      }
+    }
+  }
+};
+</script>
+<style lang="sass" scoped></style>

+ 442 - 0
report-ui/src/views/screenDesigner/components/dynamicForm.vue

@@ -0,0 +1,442 @@
+<template>
+  <div class="collapse-input-style">
+    <el-form label-width="100px" label-position="left">
+      <template v-for="(item, index) in options">
+        <div v-if="isShowForm(item, '[object Object]')" :key="index">
+          <el-form-item
+            v-if="
+              inputShow[item.name] &&
+                item.type != 'dycustComponents' &&
+                item.type != 'dynamic-add-table'
+            "
+            :label="item.label"
+            :prop="item.name"
+            :required="item.required"
+          >
+            <el-input-number
+              v-if="item.type == 'el-input-number'"
+              size="mini"
+              style="width:100%"
+              v-model.trim="formData[item.name]"
+              controls-position="right"
+              @change="changed($event, item.name)"
+            />
+
+            <el-input
+              v-if="item.type == 'el-input-text'"
+              v-model.trim="formData[item.name]"
+              type="text"
+              size="mini"
+              placeholder="请输入内容"
+              clearable
+              @change="changed($event, item.name)"
+            />
+
+            <el-input
+              v-if="item.type == 'el-input-textarea'"
+              v-model.trim="formData[item.name]"
+              type="textarea"
+              size="mini"
+              rows="2"
+              placeholder="请输入内容"
+              @change="changed($event, item.name)"
+            />
+
+            <el-switch
+              v-if="item.type == 'el-switch'"
+              v-model="formData[item.name]"
+              size="mini"
+              placeholder="请输入内容"
+              @change="changed($event, item.name)"
+            />
+            <ColorPicker
+              v-if="item.type == 'vue-color'"
+              v-model="formData[item.name]"
+              @change="val => changed(val, item.name)"
+            />
+            <customUpload
+              v-if="item.type == 'custom-upload'"
+              v-model="formData[item.name]"
+              @change="changed($event, item.name)"
+            />
+
+            <el-radio-group
+              v-if="item.type == 'el-radio-group'"
+              v-model="formData[item.name]"
+              @change="val => changed(val, item.name)"
+            >
+              <el-radio
+                v-for="itemChild in item.selectOptions"
+                :key="itemChild.code"
+                :label="itemChild.code"
+                >{{ itemChild.name }}</el-radio
+              >
+            </el-radio-group>
+
+            <el-select
+              v-if="item.type == 'el-select'"
+              size="mini"
+              v-model="formData[item.name]"
+              clearable
+              placeholder="请选择"
+              @change="val => changed(val, item.name)"
+            >
+              <el-option
+                v-for="itemChild in item.selectOptions"
+                :key="itemChild.code"
+                :label="itemChild.name"
+                :value="itemChild.code"
+              />
+            </el-select>
+
+            <el-slider
+              v-if="item.type == 'el-slider'"
+              v-model="formData[item.name]"
+              @change="val => changed(val, item.name)"
+            />
+
+            <el-button
+              v-if="item.type == 'el-button'"
+              type="primary"
+              size="mini"
+              plain
+              @click="addStaticData"
+              >编辑</el-button
+            >
+
+            <!-- 弹窗 -->
+            <el-dialog
+              title="代码编辑"
+              :visible.sync="dialogVisibleStaticData"
+              width="50%"
+              :before-close="handleClose"
+            >
+              <vue-json-editor
+                v-model="formData[item.name]"
+                :show-btns="false"
+                :mode="'code'"
+                lang="zh"
+                class="my-editor"
+              />
+              <span slot="footer" class="dialog-footer">
+                <el-button @click="dialogVisibleStaticData = false"
+                  >取 消</el-button
+                >
+                <el-button type="primary" @click="saveData">确 定</el-button>
+              </span>
+            </el-dialog>
+          </el-form-item>
+          <dynamicComponents
+            v-if="item.type == 'dycustComponents' && inputShow[item.name]"
+            v-model="formData[item.name]"
+            :chart-type="item.chartType"
+            :dict-key="item.dictKey"
+            @change="changed($event, item.name)"
+          />
+          <dynamic-add-table
+            v-if="item.type == 'dynamic-add-table' && inputShow[item.name]"
+            v-model="formData[item.name]"
+            :chart-type="item.chartType"
+            @change="changed($event, item.name)"
+          />
+        </div>
+        <div v-else-if="isShowForm(item, '[object Array]')" :key="'a-' + index">
+          <el-collapse accordion>
+            <el-collapse-item
+              v-for="(itemChild, indexChild) in item"
+              :key="indexChild"
+              :title="itemChild.name"
+              :name="indexChild"
+            >
+              <template v-for="(itemChildList, idx) in itemChild.list">
+                <el-form-item
+                  :key="idx"
+                  :label="itemChildList.label"
+                  :prop="itemChildList.name"
+                  :required="itemChildList.required"
+                >
+                  <el-input-number
+                    v-if="itemChildList.type == 'el-input-number'"
+                    size="mini"
+                    style="width:100%"
+                    v-model="formData[itemChildList.name]"
+                    controls-position="right"
+                    :placeholder="itemChildList.placeholder"
+                    @change="changed($event, itemChildList.name)"
+                  />
+
+                  <el-input
+                    v-if="itemChildList.type == 'el-input-text'"
+                    v-model.trim="formData[itemChildList.name]"
+                    type="text"
+                    size="mini"
+                    placeholder="请输入内容"
+                    clearable
+                    @change="changed($event, itemChildList.name)"
+                  />
+
+                  <el-input
+                    v-if="itemChildList.type == 'el-input-textarea'"
+                    v-model.trim="formData[itemChildList.name]"
+                    size="mini"
+                    type="textarea"
+                    rows="2"
+                    placeholder="请输入内容"
+                    @change="changed($event, itemChildList.name)"
+                  />
+
+                  <el-switch
+                    v-if="itemChildList.type == 'el-switch'"
+                    v-model="formData[itemChildList.name]"
+                    placeholder="请输入内容"
+                    size="mini"
+                    @change="changed($event, itemChildList.name)"
+                  />
+
+                  <ColorPicker
+                    v-if="itemChildList.type == 'vue-color'"
+                    v-model="formData[itemChildList.name]"
+                    @change="val => changed(val, itemChildList.name)"
+                  />
+
+                  <el-upload
+                    v-if="itemChildList.type == 'el-upload-picture'"
+                    size="mini"
+                    action="https://jsonplaceholder.typicode.com/posts/"
+                    list-type="picture-card"
+                  />
+
+                  <el-radio-group
+                    v-if="itemChildList.type == 'el-radio-group'"
+                    v-model="formData[itemChildList.name]"
+                    @change="val => changed(val, itemChildList.name)"
+                  >
+                    <el-radio
+                      v-for="it in itemChildList.selectOptions"
+                      :key="it.code"
+                      :label="it.code"
+                      >{{ it.name }}</el-radio
+                    >
+                  </el-radio-group>
+
+                  <el-select
+                    v-if="itemChildList.type == 'el-select'"
+                    size="mini"
+                    v-model="formData[itemChildList.name]"
+                    clearable
+                    placeholder="请选择"
+                    @change="val => changed(val, itemChildList.name)"
+                  >
+                    <el-option
+                      v-for="it in itemChildList.selectOptions"
+                      :key="it.code"
+                      :label="it.name"
+                      :value="it.code"
+                    />
+                  </el-select>
+
+                  <el-slider
+                    v-if="itemChildList.type == 'el-slider'"
+                    v-model="formData[itemChildList.name]"
+                    @change="val => changed(val, itemChildList.name)"
+                  />
+                </el-form-item>
+                <customColorComponents
+                  v-if="itemChildList.type == 'customColor'"
+                  :key="'b-' + idx"
+                  v-model="formData[itemChildList.name]"
+                  @change="changed($event, itemChildList.name)"
+                />
+              </template>
+            </el-collapse-item>
+          </el-collapse>
+        </div>
+      </template>
+    </el-form>
+  </div>
+</template>
+
+<script>
+import ColorPicker from "./colorPicker.vue";
+import vueJsonEditor from "vue-json-editor";
+import "codemirror/lib/codemirror.css"; // 核心样式
+import "codemirror/theme/cobalt.css"; // 引入主题后还需要在 options 中指定主题才会生效
+// language
+import "codemirror/mode/vue/vue.js";
+import "codemirror/mode/javascript/javascript.js";
+import "codemirror/mode/sql/sql.js";
+import "codemirror/mode/shell/shell.js";
+import dynamicComponents from "./dynamicComponents.vue";
+import customColorComponents from "./customColorComponents";
+import dynamicAddTable from "./dynamicAddTable.vue";
+import customUpload from "./customUpload.vue";
+export default {
+  name: "DynamicForm",
+  components: {
+    ColorPicker,
+    vueJsonEditor,
+    dynamicComponents,
+    customColorComponents,
+    dynamicAddTable,
+    customUpload
+  },
+  model: {
+    prop: "value",
+    event: "input"
+  },
+  props: {
+    options: Array,
+    value: {
+      type: [Object],
+      default: () => {}
+    }
+  },
+  data() {
+    return {
+      formData: {},
+      inputShow: {}, // 控制表单是否显示
+      dialogVisibleStaticData: false,
+      validationRules: "",
+      optionsJavascript: {
+        mode: "text/javascript",
+        tabSize: 2, // 缩进格式
+        lineNumbers: true, // 显示行号
+        line: true,
+        styleActiveLine: true, // 高亮选中行
+
+        hintOptions: {
+          completeSingle: true // 当匹配只有一项的时候是否自动补全
+        }
+      }
+    };
+  },
+  watch: {
+    value(newValue, oldValue) {
+      this.formData = newValue || {};
+    },
+    options(val) {
+      this.setDefaultValue();
+      this.isShowData();
+    }
+  },
+  created() {
+    this.isShowData();
+    this.setDefaultValue();
+  },
+  mounted() {},
+  methods: {
+    onJsonChange(val) {
+      console.log(val);
+    },
+    onJsonSave(val) {
+      console.log(val);
+    },
+    // 无论哪个输入框改变 都需要触发事件 将值回传
+    changed(val, key) {
+      if (val.extend) {
+        this.$set(this.formData, key, val.value);
+      } else {
+        this.$set(this.formData, key, val);
+      }
+      this.$emit("onChanged", this.formData);
+      // key为当前用户操作的表单组件
+      for (let i = 0; i < this.options.length; i++) {
+        let item = this.options[i];
+        if (item.relactiveDom == key) {
+          this.inputShow[item.name] = val == item.relactiveDomValue;
+          this.inputShow = Object.assign({}, this.inputShow);
+        }
+      }
+    },
+    saveData() {
+      this.$emit("onChanged", this.formData);
+      this.dialogVisibleStaticData = false;
+    },
+    // 静态数据
+    addStaticData() {
+      this.dialogVisibleStaticData = true;
+    },
+    handleClose() {
+      this.dialogVisibleStaticData = false;
+    },
+    // 组件属性 数据是否展示动态还是静态数据
+    isShowData() {
+      let currentData = {};
+      const data = [];
+      for (let i = 0; i < this.options.length; i++) {
+        // 添加默认的inputShow值
+        this.inputShow[this.options[i].name] = true;
+        if (this.options[i].selectValue) {
+          currentData = this.options[i];
+        } else {
+          data.push(this.options[i]);
+        }
+      }
+      data.forEach(el => {
+        if (el.relactiveDomValue != currentData.value) {
+          this.inputShow[el.name] = false;
+        }
+      });
+    },
+    // 组件拖入时 赋值
+    setDefaultValue() {
+      if (this.options && this.options.length > 0) {
+        for (let i = 0; i < this.options.length; i++) {
+          const obj = this.options[i];
+          if (Object.prototype.toString.call(obj) == "[object Object]") {
+            this.formData[this.options[i].name] = this.deepClone(
+              this.options[i].value
+            );
+          } else if (Object.prototype.toString.call(obj) == "[object Array]") {
+            for (let j = 0; j < obj.length; j++) {
+              const list = obj[j].list;
+              list.forEach(el => {
+                this.formData[el.name] = el.value;
+              });
+            }
+          }
+        }
+        this.formData = Object.assign({}, this.formData);
+      }
+    },
+    // 是否显示 那种格式
+    isShowForm(val, type) {
+      return Object.prototype.toString.call(val) == type;
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+/deep/ .el-form-item {
+  margin-bottom: 5px;
+}
+/deep/ .el-form-item__label {
+  font-size: 12px;
+  color: #fff;
+}
+.code-mirror {
+  width: 100%;
+  height: 100% !important;
+}
+.el-collapse {
+  border-top: none;
+  border-bottom: none;
+}
+/deep/.el-collapse-item__header {
+  height: 40px;
+  line-height: 40px;
+  background: transparent;
+  color: #bcc9d4;
+  font-weight: 300;
+  font-size: 12px;
+  border-color: #282e3a;
+}
+/deep/.el-collapse-item__wrap {
+  background: transparent;
+  border: none;
+}
+/deep/.el-collapse-item__content {
+  padding-bottom: 0;
+}
+</style>

+ 90 - 0
report-ui/src/views/screenDesigner/components/temp.vue

@@ -0,0 +1,90 @@
+<!--
+ * @Descripttion:
+ * @version:
+ * @Author: qianlishi
+ * @Date: 2022-04-28 12:20:28
+ * @LastEditors: qianlishi
+ * @LastEditTime: 2022-04-28 12:31:14
+-->
+<template>
+  <div>
+    <component :is="type" :value="value" :ispreview="true"/>
+  </div>
+</template>
+
+<script>
+import widgetHref from "../widget/texts/widgetHref.vue";
+import WidgetIframe from "../widget/texts/widgetIframe.vue";
+import widgetImage from "../widget/texts/widgetImage.vue";
+import WidgetMarquee from "../widget/texts/widgetMarquee.vue";
+import widgetSlider from "../widget/texts/widgetSlider.vue";
+import widgetTable from "../widget/texts/widgetTable.vue";
+import widgetText from "../widget/texts/widgetText.vue";
+import widgetTime from "../widget/texts/widgetTime.vue";
+import widgetVideo from "../widget/texts/widgetVideo.vue";
+import widgetBarchart from "../widget/barCharts/widgetBarchart.vue";
+import widgetGradientColorBarchart from "../widget/barCharts/widgetGradientColorBarchart.vue";
+import widgetLinechart from "../widget/lineCharts/widgetLinechart.vue";
+import widgetBarlinechart from "../widget/barlineCharts/widgetBarlinechart";
+import WidgetPiechart from "../widget/pieCharts/widgetPiechart.vue";
+import WidgetFunnel from "../widget/funnelCharts/widgetFunnel.vue";
+import WidgetGauge from "../widget/percentCharts/widgetGauge.vue";
+import WidgetPieNightingaleRoseArea from "../widget/pieCharts/widgetPieNightingaleRose";
+import widgetMap from "../widget/mapCharts/widgetMap.vue";
+import widgetPiePercentageChart from "../widget/percentCharts/widgetPiePercentageChart";
+import widgetAirBubbleMap from "../widget/mapCharts/widgetAirBubbleMap";
+import widgetBarStackChart from "../widget/barCharts/widgetBarStackChart";
+import widgetLineStackChart from "../widget/lineCharts/widgetLineStackChart";
+import widgetBarCompareChart from "../widget/barCharts/widgetBarCompareChart";
+import widgetLineCompareChart from "../widget/lineCharts/widgetLineCompareChart";
+import widgetDecoratePieChart from "../widget/decorateCharts/widgetDecoratePieChart";
+import widgetMoreBarLineChart from "../widget/barlineCharts/widgetMoreBarLineChart";
+import widgetWordCloud from "../widget/wordcloudCharts/widgetWordCloud";
+import widgetHeatmap from "../widget/heatmap/widgetHeatmap";
+
+export default {
+  name: "WidgetTemp",
+  components: {
+    widgetHref,
+    WidgetIframe,
+    widgetImage,
+    WidgetMarquee,
+    widgetSlider,
+    widgetTable,
+    widgetText,
+    widgetTime,
+    widgetVideo,
+    widgetBarchart,
+    widgetGradientColorBarchart,
+    widgetLinechart,
+    widgetBarlinechart,
+    WidgetPiechart,
+    WidgetFunnel,
+    WidgetGauge,
+    WidgetPieNightingaleRoseArea,
+    widgetMap,
+    widgetPiePercentageChart,
+    widgetAirBubbleMap,
+    widgetBarStackChart,
+    widgetLineStackChart,
+    widgetBarCompareChart,
+    widgetLineCompareChart,
+    widgetDecoratePieChart,
+    widgetMoreBarLineChart,
+    widgetWordCloud,
+    widgetHeatmap
+  },
+  model: {
+    prop: "value",
+    event: "input"
+  },
+  props: {
+    type: String,
+    value: {
+      type: [Object],
+      default: () => {
+      }
+    }
+  }
+};
+</script>

+ 659 - 0
report-ui/src/views/screenDesigner/config/barCharts/widget-bar-compare.js

@@ -0,0 +1,659 @@
+/*
+ * @Descripttion: 柱状对比图 json
+ * @version:
+ * @Author: qianlishi
+ * @Date: 2021-08-29 07:39:35
+ * @LastEditors: qianlishi
+ * @LastEditTime: 2021-09-28 14:09:58
+ */
+export const widgetBarCompare = {
+  code: 'widgetBarCompareChart',
+  type: 'barChart',
+  tabName: '柱状图',
+  label: '柱状对比图',
+  icon: 'iconduibitupu',
+  options: {
+    // 配置
+    setup: [
+      {
+        type: 'el-input-text',
+        label: '图层名称',
+        name: 'layerName',
+        required: false,
+        placeholder: '',
+        value: '柱状对比图',
+      },
+      {
+        type: 'vue-color',
+        label: '背景颜色',
+        name: 'background',
+        required: false,
+        placeholder: '',
+        value: ''
+      },
+      [
+        {
+          name: '柱体设置',
+          list: [
+            {
+              type: 'el-slider',
+              label: '最大宽度',
+              name: 'maxWidth',
+              required: false,
+              placeholder: '',
+              value: 15,
+            },
+            {
+              type: 'el-slider',
+              label: '圆角',
+              name: 'radius',
+              require: false,
+              placeholder: '',
+              value: 5,
+            },
+          ],
+        },
+        {
+          name: '标题设置',
+          list: [
+            {
+              type: 'el-switch',
+              label: '标题',
+              name: 'isNoTitle',
+              required: false,
+              placeholder: '',
+              value: true,
+            },
+            {
+              type: 'el-input-text',
+              label: '标题',
+              name: 'titleText',
+              required: false,
+              placeholder: '',
+              value: '',
+            },
+            {
+              type: 'vue-color',
+              label: '字体颜色',
+              name: 'textColor',
+              required: false,
+              placeholder: '',
+              value: '#FFD700'
+            },
+            {
+              type: 'el-select',
+              label: '字体粗细',
+              name: 'textFontWeight',
+              required: false,
+              placeholder: '',
+              selectOptions: [
+                {code: 'normal', name: '正常'},
+                {code: 'bold', name: '粗体'},
+                {code: 'bolder', name: '特粗体'},
+                {code: 'lighter', name: '细体'}
+              ],
+              value: 'normal'
+            },
+            {
+              type: 'el-input-number',
+              label: '字体大小',
+              name: 'textFontSize',
+              required: false,
+              placeholder: '',
+              value: 20
+            },
+            {
+              type: 'el-select',
+              label: '字体位置',
+              name: 'textAlign',
+              required: false,
+              placeholder: '',
+              selectOptions: [
+                {code: 'center', name: '居中'},
+                {code: 'left', name: '左对齐'},
+                {code: 'right', name: '右对齐'},
+              ],
+              value: 'center'
+            },
+          ],
+        },
+        {
+          name: '图例操作',
+          list: [
+            {
+              type: 'el-switch',
+              label: '图例显示',
+              name: 'isShowLegend',
+              required: false,
+              placeholder: '',
+              value: true,
+            },
+            {
+              type: 'el-input-text',
+              label: '图例名称',
+              name: 'legendName',
+              required: false,
+              placeholder: '多值以' | '隔开',
+              value: ''
+            },
+            {
+              type: 'vue-color',
+              label: '字体颜色',
+              name: 'lengedColor',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+            },
+            {
+              type: 'el-input-number',
+              label: '字体字号',
+              name: 'lengedFontSize',
+              required: false,
+              placeholder: '',
+              value: 12,
+            },
+            {
+              type: 'el-input-number',
+              label: '图例宽度',
+              name: 'lengedWidth',
+              required: false,
+              placeholder: '',
+              value: 12,
+            },
+            {
+              type: 'el-select',
+              label: '横向位置',
+              name: 'lateralPosition',
+              required: false,
+              placeholder: '',
+              selectOptions: [
+                {code: 'center', name: '居中'},
+                {code: 'left', name: '左对齐'},
+                {code: 'right', name: '右对齐'},
+              ],
+              value: 'center'
+            },
+            {
+              type: 'el-select',
+              label: '纵向位置',
+              name: 'longitudinalPosition',
+              required: false,
+              placeholder: '',
+              selectOptions: [
+                {code: 'top', name: '顶部'},
+                {code: 'bottom', name: '底部'},
+              ],
+              value: 'top'
+            },
+            {
+              type: 'el-select',
+              label: '布局前置',
+              name: 'layoutFront',
+              required: false,
+              placeholder: '',
+              selectOptions: [
+                {code: 'vertical', name: '竖排'},
+                {code: 'horizontal', name: '横排'},
+              ],
+              value: 'horizontal'
+            },
+          ],
+        },
+        {
+          name: '左X轴设置',
+          list: [
+            {
+              type: 'el-switch',
+              label: '显示',
+              name: 'hideXLeft',
+              required: false,
+              placeholder: '',
+              value: true,
+            },
+            {
+              type: 'el-input-number',
+              label: '数值间隔',
+              name: 'splitNumberLeft',
+              required: false,
+              placeholder: '',
+              value: ''
+            },
+            {
+              type: 'vue-color',
+              label: '数值颜色',
+              name: 'XcolorLeft',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+            },
+            {
+              type: 'el-input-number',
+              label: '数值字号',
+              name: 'fontSizeXLeft',
+              required: false,
+              placeholder: '',
+              value: 14,
+            },
+            {
+              type: 'el-switch',
+              label: '刻度线',
+              name: 'tickLineLeft',
+              require: false,
+              placeholder: '',
+              value: false,
+            },
+            {
+              type: 'el-switch',
+              label: 'X轴线',
+              name: 'xLineLeft',
+              require: false,
+              placeholder: '',
+              value: false,
+            },
+            {
+              type: 'vue-color',
+              label: '轴颜色',
+              name: 'lineColorXLeft',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+            },
+            {
+              type: 'el-switch',
+              label: '竖分割线',
+              name: 'SplitLineLeft',
+              require: false,
+              placeholder: '',
+              value: false,
+            },
+            {
+              type: 'vue-color',
+              label: '分割线颜色',
+              name: 'SplitLineColorLeft',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+            },
+            {
+              type: 'el-input-number',
+              label: '分割线宽度',
+              name: 'SplitLinefontSizeLeft',
+              required: false,
+              placeholder: '',
+              value: 1,
+            },
+            {
+              type: 'el-switch',
+              label: '边框线',
+              name: 'frameLineLeft',
+              require: false,
+              placeholder: '',
+              value: false,
+            },
+          ],
+        },
+        {
+          name: '右X轴设置',
+          list: [
+            {
+              type: 'el-switch',
+              label: '显示',
+              name: 'hideXRight',
+              required: false,
+              placeholder: '',
+              value: true,
+            },
+            {
+              type: 'el-input-number',
+              label: '数值间隔',
+              name: 'splitNumberRight',
+              required: false,
+              placeholder: '',
+              value: ''
+            },
+            {
+              type: 'vue-color',
+              label: '数值颜色',
+              name: 'XcolorRight',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+            },
+            {
+              type: 'el-input-number',
+              label: '数值字号',
+              name: 'fontSizeXRight',
+              required: false,
+              placeholder: '',
+              value: 14,
+            },
+            {
+              type: 'el-switch',
+              label: '刻度线',
+              name: 'tickLineRight',
+              require: false,
+              placeholder: '',
+              value: false,
+            },
+            {
+              type: 'el-switch',
+              label: 'X轴线',
+              name: 'xLineRight',
+              require: false,
+              placeholder: '',
+              value: false,
+            },
+            {
+              type: 'vue-color',
+              label: '轴颜色',
+              name: 'lineColorXRight',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+            },
+            {
+              type: 'el-switch',
+              label: '竖分割线',
+              name: 'SplitLineRight',
+              require: false,
+              placeholder: '',
+              value: false,
+            },
+            {
+              type: 'vue-color',
+              label: '分割线颜色',
+              name: 'SplitLineColorRight',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+            },
+            {
+              type: 'el-input-number',
+              label: '分割线宽度',
+              name: 'SplitLinefontSizeRight',
+              required: false,
+              placeholder: '',
+              value: 1,
+            },
+            {
+              type: 'el-switch',
+              label: '边框线',
+              name: 'frameLineRight',
+              require: false,
+              placeholder: '',
+              value: false,
+            },
+          ],
+        },
+        {
+          name: 'Y轴设置',
+          list: [
+            {
+              type: 'el-switch',
+              label: '显示',
+              name: 'hideY',
+              required: false,
+              placeholder: '',
+              value: true,
+            },
+            {
+              type: 'vue-color',
+              label: '数值颜色',
+              name: 'colorY',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+            },
+            {
+              type: 'el-input-number',
+              label: '数值字号',
+              name: 'fontSizeY',
+              required: false,
+              placeholder: '',
+              value: 14,
+            },
+            {
+              type: 'el-select',
+              label: '数值对齐',
+              name: 'textAlign',
+              required: false,
+              placeholder: '',
+              selectOptions: [
+                {code: 'center', name: '居中'},
+                {code: 'left', name: '左对齐'},
+                {code: 'right', name: '右对齐'},
+              ],
+              value: 'center'
+            },
+            {
+              type: 'el-switch',
+              label: '刻度线',
+              name: 'tickLineY',
+              require: false,
+              placeholder: '',
+              value: false,
+            },
+            {
+              type: 'el-switch',
+              label: 'Y轴线',
+              name: 'lineY',
+              require: false,
+              placeholder: '',
+              value: false,
+            },
+            {
+              type: 'vue-color',
+              label: '轴颜色',
+              name: 'lineColorY',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+            },
+          ],
+        },
+        {
+          name: '数值设定',
+          list: [
+            {
+              type: 'el-switch',
+              label: '显示',
+              name: 'isShow',
+              required: false,
+              placeholder: '',
+              value: true
+            },
+            {
+              type: 'el-input-number',
+              label: '字体大小',
+              name: 'fontSize',
+              required: false,
+              placeholder: '',
+              value: 14
+            },
+            {
+              type: 'vue-color',
+              label: '字体颜色',
+              name: 'subTextColor',
+              required: false,
+              placeholder: '',
+              value: '#fff'
+            },
+            {
+              type: 'el-select',
+              label: '字体粗细',
+              name: 'fontWeight',
+              required: false,
+              placeholder: '',
+              selectOptions: [
+                {code: 'normal', name: '正常'},
+                {code: 'bold', name: '粗体'},
+                {code: 'bolder', name: '特粗体'},
+                {code: 'lighter', name: '细体'}
+              ],
+              value: 'normal'
+            },
+          ],
+        },
+        {
+          name: '提示语设置',
+          list: [
+            {
+              type: 'el-input-number',
+              label: '字体大小',
+              name: 'tipsFontSize',
+              required: false,
+              placeholder: '',
+              value: 16
+            },
+            {
+              type: 'vue-color',
+              label: '字体颜色',
+              name: 'lineColor',
+              required: false,
+              placeholder: '',
+            },
+          ],
+        },
+        {
+          name: '坐标轴边距设置',
+          list: [
+            {
+              type: 'el-slider',
+              label: '左右边距(像素)',
+              name: 'marginLeftRight',
+              required: false,
+              placeholder: '',
+              value: 10,
+            },
+            {
+              type: 'el-slider',
+              label: '顶边距(像素)',
+              name: 'marginTop',
+              required: false,
+              placeholder: '',
+              value: 40,
+            },
+            {
+              type: 'el-slider',
+              label: '底边距(像素)',
+              name: 'marginBottom',
+              required: false,
+              placeholder: '',
+              value: 10,
+            },
+          ],
+        },
+        {
+          name: '自定义配色',
+          list: [
+            {
+              type: 'customColor',
+              label: '',
+              name: 'customColor',
+              required: false,
+              value: [{color: '#36c5e7'}, {color: '#e68b55'}],
+            },
+          ],
+        },
+      ],
+    ],
+    // 数据
+    data: [
+      {
+        type: 'el-radio-group',
+        label: '数据类型',
+        name: 'dataType',
+        require: false,
+        placeholder: '',
+        selectValue: true,
+        selectOptions: [
+          {
+            code: 'staticData',
+            name: '静态数据',
+          },
+          {
+            code: 'dynamicData',
+            name: '动态数据',
+          },
+        ],
+        value: 'staticData',
+      },
+      {
+        type: 'el-input-number',
+        label: '刷新时间(毫秒)',
+        name: 'refreshTime',
+        relactiveDom: 'dataType',
+        relactiveDomValue: 'dynamicData',
+        value: 5000
+      },
+      {
+        type: 'el-button',
+        label: '静态数据',
+        name: 'staticData',
+        required: false,
+        placeholder: '',
+        relactiveDom: 'dataType',
+        relactiveDomValue: 'staticData',
+        value: [
+          {"axis": "07-25", "name": "success", "data": "2"},
+          {"axis": "07-25", "name": "fail", "data": "10"},
+          {"axis": "07-26", "name": "success", "data": "5"},
+          {"axis": "07-26", "name": "fail", "data": "20"},
+          {"axis": "07-27", "name": "success", "data": "15"},
+          {"axis": "07-27", "name": "fail", "data": "30"},
+          {"axis": "07-28", "name": "success", "data": "10"},
+          {"axis": "07-28", "name": "fail", "data": "12"},
+          {"axis": "07-29", "name": "success", "data": "9"},
+          {"axis": "07-29", "name": "fail", "data": "16"},
+        ],
+      },
+      {
+        type: 'dycustComponents',
+        label: '',
+        name: 'dynamicData',
+        required: false,
+        placeholder: '',
+        relactiveDom: 'dataType',
+        relactiveDomValue: 'dynamicData',
+        chartType: 'widget-stackchart',
+        dictKey: 'STACK_PROPERTIES',
+        value: '',
+      },
+    ],
+    // 坐标
+    position: [
+      {
+        type: 'el-input-number',
+        label: '左边距',
+        name: 'left',
+        required: false,
+        placeholder: '',
+        value: 0,
+      },
+      {
+        type: 'el-input-number',
+        label: '上边距',
+        name: 'top',
+        required: false,
+        placeholder: '',
+        value: 0,
+      },
+      {
+        type: 'el-input-number',
+        label: '宽度',
+        name: 'width',
+        required: false,
+        placeholder: '该容器在1920px大屏中的宽度',
+        value: 500,
+      },
+      {
+        type: 'el-input-number',
+        label: '高度',
+        name: 'height',
+        required: false,
+        placeholder: '该容器在1080px大屏中的高度',
+        value: 250,
+      },
+    ],
+  }
+}

部分文件因文件數量過多而無法顯示