Browse Source

update

大屏设计器,先这样。。。。
qianlishi 3 years ago
parent
commit
d03af19e96
45 changed files with 5723 additions and 21 deletions
  1. 49 3
      report-ui/src/assets/iconfont/demo_index.html
  2. 11 3
      report-ui/src/assets/iconfont/iconfont.css
  3. 0 0
      report-ui/src/assets/iconfont/iconfont.js
  4. 14 0
      report-ui/src/assets/iconfont/iconfont.json
  5. BIN
      report-ui/src/assets/iconfont/iconfont.ttf
  6. BIN
      report-ui/src/assets/iconfont/iconfont.woff
  7. BIN
      report-ui/src/assets/iconfont/iconfont.woff2
  8. 237 0
      report-ui/src/assets/styles/screenDesigner.scss
  9. 6 0
      report-ui/src/router/index.js
  10. 13 13
      report-ui/src/views/bigscreenDesigner/designer/index.vue
  11. 2 2
      report-ui/src/views/bigscreenDesigner/designer/tools/index.js
  12. 71 0
      report-ui/src/views/screenDesigner/components/colorPicker.vue
  13. 106 0
      report-ui/src/views/screenDesigner/components/contentMenu.vue
  14. 191 0
      report-ui/src/views/screenDesigner/components/customColorComponents.vue
  15. 101 0
      report-ui/src/views/screenDesigner/components/customUpload.vue
  16. 178 0
      report-ui/src/views/screenDesigner/components/dynamicAddTable.vue
  17. 161 0
      report-ui/src/views/screenDesigner/components/dynamicComponents.vue
  18. 442 0
      report-ui/src/views/screenDesigner/components/dynamicForm.vue
  19. 678 0
      report-ui/src/views/screenDesigner/config/barCharts/widget-barchart.js
  20. 30 0
      report-ui/src/views/screenDesigner/config/configs.js
  21. 10 0
      report-ui/src/views/screenDesigner/config/index.js
  22. 67 0
      report-ui/src/views/screenDesigner/config/texts/screenConfig.js
  23. 158 0
      report-ui/src/views/screenDesigner/config/texts/widget-href.js
  24. 73 0
      report-ui/src/views/screenDesigner/config/texts/widget-iframe.js
  25. 112 0
      report-ui/src/views/screenDesigner/config/texts/widget-image.js
  26. 177 0
      report-ui/src/views/screenDesigner/config/texts/widget-marquee.js
  27. 88 0
      report-ui/src/views/screenDesigner/config/texts/widget-slider.js
  28. 305 0
      report-ui/src/views/screenDesigner/config/texts/widget-table.js
  29. 183 0
      report-ui/src/views/screenDesigner/config/texts/widget-text.js
  30. 144 0
      report-ui/src/views/screenDesigner/config/texts/widget-time.js
  31. 73 0
      report-ui/src/views/screenDesigner/config/texts/widget-video.js
  32. 865 0
      report-ui/src/views/screenDesigner/index.vue
  33. 19 0
      report-ui/src/views/screenDesigner/node.md
  34. 1 0
      report-ui/src/views/screenDesigner/preview.vue
  35. 23 0
      report-ui/src/views/screenDesigner/util/common.js
  36. 109 0
      report-ui/src/views/screenDesigner/widget/index.vue
  37. 80 0
      report-ui/src/views/screenDesigner/widget/texts/widgetHref.vue
  38. 60 0
      report-ui/src/views/screenDesigner/widget/texts/widgetIframe.vue
  39. 79 0
      report-ui/src/views/screenDesigner/widget/texts/widgetImage.vue
  40. 100 0
      report-ui/src/views/screenDesigner/widget/texts/widgetMarquee.vue
  41. 128 0
      report-ui/src/views/screenDesigner/widget/texts/widgetSlider.vue
  42. 257 0
      report-ui/src/views/screenDesigner/widget/texts/widgetTable.vue
  43. 98 0
      report-ui/src/views/screenDesigner/widget/texts/widgetText.vue
  44. 163 0
      report-ui/src/views/screenDesigner/widget/texts/widgetTime.vue
  45. 61 0
      report-ui/src/views/screenDesigner/widget/texts/widgetVideo.vue

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

@@ -54,6 +54,18 @@
       <div class="content unicode" style="display: block;">
       <div class="content unicode" style="display: block;">
           <ul class="icon_lists dib-box">
           <ul class="icon_lists dib-box">
           
           
+            <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">
             <li class="dib">
               <span class="icon iconfont">&#xe7af;</span>
               <span class="icon iconfont">&#xe7af;</span>
                 <div class="name">词云图</div>
                 <div class="name">词云图</div>
@@ -792,9 +804,9 @@
 <pre><code class="language-css"
 <pre><code class="language-css"
 >@font-face {
 >@font-face {
   font-family: 'iconfont';
   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=1646807984187') format('woff2'),
+       url('iconfont.woff?t=1646807984187') format('woff'),
+       url('iconfont.ttf?t=1646807984187') format('truetype');
 }
 }
 </code></pre>
 </code></pre>
           <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
           <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -820,6 +832,24 @@
       <div class="content font-class">
       <div class="content font-class">
         <ul class="icon_lists dib-box">
         <ul class="icon_lists dib-box">
           
           
+          <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">
           <li class="dib">
             <span class="icon iconfont iconciyuntu"></span>
             <span class="icon iconfont iconciyuntu"></span>
             <div class="name">
             <div class="name">
@@ -1927,6 +1957,22 @@
       <div class="content symbol">
       <div class="content symbol">
           <ul class="icon_lists dib-box">
           <ul class="icon_lists dib-box">
           
           
+            <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">
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
                 <svg class="icon svg-icon" aria-hidden="true">
                   <use xlink:href="#iconciyuntu"></use>
                   <use xlink:href="#iconciyuntu"></use>

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

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

File diff suppressed because it is too large
+ 0 - 0
report-ui/src/assets/iconfont/iconfont.js


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

@@ -5,6 +5,20 @@
   "css_prefix_text": "icon",
   "css_prefix_text": "icon",
   "description": "",
   "description": "",
   "glyphs": [
   "glyphs": [
+    {
+      "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",
       "icon_id": "23043843",
       "name": "词云图",
       "name": "词云图",

BIN
report-ui/src/assets/iconfont/iconfont.ttf


BIN
report-ui/src/assets/iconfont/iconfont.woff


BIN
report-ui/src/assets/iconfont/iconfont.woff2


+ 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;
+}

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

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

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

@@ -288,7 +288,7 @@ import draggable from "vuedraggable";
 import VueRulerTool from "vue-ruler-tool"; // 大屏设计页面的标尺插件
 import VueRulerTool from "vue-ruler-tool"; // 大屏设计页面的标尺插件
 import contentMenu from "./components/contentMenu";
 import contentMenu from "./components/contentMenu";
 import { getToken } from "@/utils/auth";
 import { getToken } from "@/utils/auth";
-import { Revoke } from '@/utils/revoke'    //处理历史记录 2022-02-22
+import { Revoke } from "@/utils/revoke"; //处理历史记录 2022-02-22
 
 
 export default {
 export default {
   name: "Login",
   name: "Login",
@@ -425,15 +425,15 @@ export default {
         this.handlerLayerWidget(val);
         this.handlerLayerWidget(val);
         //以下部分是记录历史
         //以下部分是记录历史
         this.$nextTick(() => {
         this.$nextTick(() => {
-          this.revoke.push(this.widgets)
-        })
+          this.revoke.push(this.widgets);
+        });
       },
       },
       deep: true
       deep: true
     }
     }
   },
   },
   created() {
   created() {
     /* 以下是记录历史的 */
     /* 以下是记录历史的 */
-    this.revoke = new Revoke()
+    this.revoke = new Revoke();
   },
   },
   mounted() {
   mounted() {
     // 如果是新的设计工作台
     // 如果是新的设计工作台
@@ -444,17 +444,17 @@ export default {
     });
     });
   },
   },
   methods: {
   methods: {
-          /**
+    /**
      * @description: 恢复
      * @description: 恢复
      * @param {*}
      * @param {*}
      * @return {*}
      * @return {*}
      */
      */
     handleUndo() {
     handleUndo() {
-      const record = this.revoke.undo()
+      const record = this.revoke.undo();
       if (!record) {
       if (!record) {
-        return false
+        return false;
       }
       }
-      this.widgets = record
+      this.widgets = record;
     },
     },
     /**
     /**
      * @description: 重做
      * @description: 重做
@@ -462,11 +462,11 @@ export default {
      * @return {*}
      * @return {*}
      */
      */
     handleRedo() {
     handleRedo() {
-      const record = this.revoke.redo()
+      const record = this.revoke.redo();
       if (!record) {
       if (!record) {
-        return false
+        return false;
       }
       }
-      this.widgets = record
+      this.widgets = record;
     },
     },
     handlerLayerWidget(val) {
     handlerLayerWidget(val) {
       const layerWidgetArr = [];
       const layerWidgetArr = [];
@@ -712,9 +712,9 @@ export default {
 
 
       //2022年02月22日 修复:可以拖拽放到鼠标的位置
       //2022年02月22日 修复:可以拖拽放到鼠标的位置
       widgetJsonValue.value.position.left =
       widgetJsonValue.value.position.left =
-        x - widgetJsonValue.value.position.width / 2
+        x - widgetJsonValue.value.position.width / 2;
       widgetJsonValue.value.position.top =
       widgetJsonValue.value.position.top =
-        y - widgetJsonValue.value.position.height / 2
+        y - widgetJsonValue.value.position.height / 2;
 
 
       // 将选中的复制组件,放到工作区中去
       // 将选中的复制组件,放到工作区中去
       this.widgets.push(this.deepClone(widgetJsonValue));
       this.widgets.push(this.deepClone(widgetJsonValue));

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

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

+ 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>

+ 678 - 0
report-ui/src/views/screenDesigner/config/barCharts/widget-barchart.js

@@ -0,0 +1,678 @@
+/*
+ * @Descripttion: 柱状图json
+ * @version:
+ * @Author: qianlishi
+ * @Date: 2021-08-29 07:21:45
+ * @LastEditors: qianlishi
+ * @LastEditTime: 2022-03-11 11:40:29
+ */
+export const widgetBarchart = {
+  code: 'widget-barchart',
+  type: 'barChart',
+  tabName: '柱状图',
+  label: '柱状图',
+  icon: 'iconzhuzhuangtu',
+  options: {
+    // 配置
+    setup: [
+      {
+        type: 'el-input-text',
+        label: '图层名称',
+        name: 'layerName',
+        required: false,
+        placeholder: '',
+        value: '柱状图',
+      },
+      {
+        type: 'el-switch',
+        label: '竖展示',
+        name: 'verticalShow',
+        required: false,
+        placeholder: '',
+        value: false,
+      },
+      {
+        type: 'vue-color',
+        label: '背景颜色',
+        name: 'background',
+        required: false,
+        placeholder: '',
+        value: ''
+      },
+      [
+        {
+          name: '柱体设置',
+          list: [
+            {
+              type: 'el-slider',
+              label: '最大宽度',
+              name: 'maxWidth',
+              required: false,
+              placeholder: '',
+              value: 10,
+            },
+            {
+              type: 'el-slider',
+              label: '圆角',
+              name: 'radius',
+              require: false,
+              placeholder: '',
+              value: 5,
+            },
+            {
+              type: 'el-slider',
+              label: '最小高度',
+              name: 'minHeight',
+              require: false,
+              placeholder: '',
+              value: 0,
+            },
+          ],
+        },
+        {
+          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'
+            },
+            {
+              type: 'el-input-text',
+              label: '副标题',
+              name: 'subText',
+              required: false,
+              placeholder: '',
+              value: ''
+            },
+            {
+              type: 'vue-color',
+              label: '字体颜色',
+              name: 'subTextColor',
+              required: false,
+              placeholder: '',
+              value: 'rgba(30, 144, 255, 1)'
+            },
+            {
+              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: 20
+            },
+          ],
+        },
+        {
+          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: 'hideX',
+              required: false,
+              placeholder: '',
+              value: true,
+            },
+            {
+              type: 'el-input-text',
+              label: '坐标名',
+              name: 'xName',
+              required: false,
+              placeholder: '',
+              value: ''
+            },
+            {
+              type: 'vue-color',
+              label: '坐标名颜色',
+              name: 'nameColorX',
+              required: false,
+              placeholder: '',
+              value: '#fff'
+            },
+            {
+              type: 'el-input-number',
+              label: '坐标字号',
+              name: 'nameFontSizeX',
+              required: false,
+              placeholder: '',
+              value: 14
+            },
+            {
+              type: 'vue-color',
+              label: '数值颜色',
+              name: 'Xcolor',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+            },
+            {
+              type: 'el-input-number',
+              label: '数值字号',
+              name: 'fontSizeX',
+              required: false,
+              placeholder: '',
+              value: 14,
+            },
+            {
+              type: 'el-slider',
+              label: '数值角度',
+              name: 'textAngle',
+              required: false,
+              placeholder: '',
+              value: 0
+            },
+            {
+              type: 'el-input-number',
+              label: '数值间隔',
+              name: 'textInterval',
+              required: false,
+              placeholder: '',
+              value: ''
+            },
+            {
+              type: 'el-switch',
+              label: '轴反转',
+              name: 'reversalX',
+              required: false,
+              placeholder: '',
+              value: false
+            },
+            {
+              type: 'vue-color',
+              label: '轴颜色',
+              name: 'lineColorX',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+            },
+            {
+              type: 'el-switch',
+              label: '分割线显示',
+              name: 'isShowSplitLineX',
+              require: false,
+              placeholder: '',
+              value: false,
+            },
+            {
+              type: 'vue-color',
+              label: '分割线颜色',
+              name: 'splitLineColorX',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+
+            }
+          ],
+        },
+        {
+          name: 'Y轴设置',
+          list: [
+            {
+              type: 'el-switch',
+              label: '显示',
+              name: 'isShowY',
+              require: false,
+              placeholder: '',
+              value: true,
+            },
+            {
+              type: 'el-input-text',
+              label: '坐标名',
+              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: 'vue-color',
+              label: '数值颜色',
+              name: 'colorY',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+            },
+            {
+              type: 'el-input-number',
+              label: '数值字号',
+              name: 'fontSizeY',
+              required: false,
+              placeholder: '',
+              value: 14,
+            },
+            {
+              type: 'el-slider',
+              label: '数值角度',
+              name: 'ytextAngle',
+              required: false,
+              placeholder: '',
+              value: 0
+            },
+            {
+              type: 'el-switch',
+              label: '缩放',
+              name: 'scale',
+              require: false,
+              placeholder: '',
+              value: false,
+            },
+            {
+              type: 'el-input-number',
+              label: '均分',
+              name: 'splitNumber',
+              required: false,
+              placeholder: '',
+              value: ''
+            },
+            {
+              type: 'el-switch',
+              label: '轴反转',
+              name: 'reversalY',
+              required: false,
+              placeholder: '',
+              value: false
+            },
+            {
+              type: 'vue-color',
+              label: '轴颜色',
+              name: 'lineColorY',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+            }, {
+              type: 'el-switch',
+              label: '分割线显示',
+              name: 'isShowSplitLineY',
+              require: false,
+              placeholder: '',
+              value: false,
+            }, {
+              type: 'vue-color',
+              label: '分割线颜色',
+              name: 'splitLineColorY',
+              required: false,
+              placeholder: '',
+              value: '#fff',
+
+            }
+          ],
+        },
+        {
+          name: '数值设定',
+          list: [
+            {
+              type: 'el-switch',
+              label: '显示',
+              name: 'isShow',
+              required: false,
+              placeholder: '',
+              value: true
+            },
+            {
+              type: 'el-input-number',
+              label: '距离',
+              name: 'distance',
+              required: false,
+              placeholder: '',
+              value: 5
+            },
+            {
+              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: 'tipFontSize',
+              required: false,
+              placeholder: '',
+            },
+            {
+              type: 'vue-color',
+              label: '字体颜色',
+              name: 'lineColor',
+              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: 'customColor',
+              label: '',
+              name: 'customColor',
+              required: false,
+              value: [{color: '#ff7f50'}, {color: '#87cefa'}, {color: '#da70d6'}, {color: '#32cd32'}, {color: '#6495ed'}],
+            },
+          ],
+        },
+      ],
+    ],
+    // 数据
+    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": "苹果", "data": 1000},
+          {"axis": "三星", "data": 2229},
+          {"axis": "小米", "data": 3879},
+          {"axis": "oppo", "data": 2379},
+          {"axis": "vivo", "data": 4079},
+        ],
+      },
+      {
+        type: 'dycustComponents',
+        label: '',
+        name: 'dynamicData',
+        required: false,
+        placeholder: '',
+        relactiveDom: 'dataType',
+        relactiveDomValue: 'dynamicData',
+        chartType: 'widget-barchart',
+        dictKey: 'BAR_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: 400,
+      },
+      {
+        type: 'el-input-number',
+        label: '高度',
+        name: 'height',
+        required: false,
+        placeholder: '该容器在1080px大屏中的高度',
+        value: 200,
+      },
+    ],
+  }
+}

+ 30 - 0
report-ui/src/views/screenDesigner/config/configs.js

@@ -0,0 +1,30 @@
+/**
+ *  文本栏:{type: 'text',tabName: '文本栏'}
+ *  柱状图:{type: 'barChart',tabName: '柱状图'}
+ * **/ 
+
+import {widgetHref} from "./texts/widget-href"
+import {widgetIframe} from "./texts/widget-iframe"
+import {widgetImage} from "./texts/widget-image"
+import {widgetMarquee} from "./texts/widget-marquee"
+import {widgetSliders} from "./texts/widget-slider"
+import {widgetTable} from "./texts/widget-table"
+import {widgetText} from "./texts/widget-text"
+import { widgetTime } from "./texts/widget-time"
+import {widgetVideo} from "./texts/widget-video"
+import {widgetBarchart} from './barCharts/widget-barchart'
+
+
+
+export const widgetTool = [
+    widgetHref,
+    widgetIframe,
+    widgetImage,
+    widgetMarquee,
+    // widgetSliders,
+    widgetTable,
+    widgetText,
+    widgetTime,
+    widgetVideo,
+    widgetBarchart
+]

+ 10 - 0
report-ui/src/views/screenDesigner/config/index.js

@@ -0,0 +1,10 @@
+import {converArr} from '../util/common'
+import { widgetTool } from "./configs"
+
+export const widgetTools =  converArr([...widgetTool])
+
+export const getToolByCode = (code) => {
+  return [...widgetTool].find((item) => {
+    return item.code = code
+  })
+}

+ 67 - 0
report-ui/src/views/screenDesigner/config/texts/screenConfig.js

@@ -0,0 +1,67 @@
+/*
+ * @Descripttion: 大屏配置
+ * @version: 
+ * @Author: qianlishi
+ * @Date: 2022-03-09 19:45:37
+ * @LastEditors: qianlishi
+ * @LastEditTime: 2022-03-09 19:59:18
+ */
+export const screenConfig = {
+  code: 'screen',
+  type: 'screen',
+  label: '大屏设置',
+  icon: '',
+  options: {
+    setup: [
+      {
+        type: 'el-input-number',
+        label: '大屏宽度',
+        name: 'width',
+        required: false,
+        placeholder: 'px',
+        value: '1920',
+      },
+      {
+        type: 'el-input-number',
+        label: '大屏高度',
+        name: 'height',
+        required: false,
+        placeholder: 'px',
+        value: '1080',
+      },
+      {
+        type: 'el-input-text',
+        label: '标题',
+        name: 'title',
+        require: false,
+        placeholder: '',
+        value: '大屏',
+      },
+      {
+        type: 'el-input-textarea',
+        label: '大屏简介',
+        name: 'description',
+        required: false,
+        placeholder: '',
+      },
+      {
+        type: 'vue-color',
+        label: '背景颜色',
+        name: 'backgroundColor',
+        required: false,
+        placeholder: '',
+        value: '#000',
+      },
+      {
+        type: 'custom-upload',
+        label: '图片地址',
+        name: 'backgroundImage',
+        required: false,
+        placeholder: '',
+        value: 'https://report.anji-plus.com/file/download/bf566e48-ccad-40e1-8ee9-228427e5466b',
+      },
+    ],
+    data: [],
+    position: [],
+  }
+}

+ 158 - 0
report-ui/src/views/screenDesigner/config/texts/widget-href.js

@@ -0,0 +1,158 @@
+/*
+ * @Descripttion: 超链接文本
+ * @version: 
+ * @Author: qianlishi
+ * @Date: 2021-08-29 07:03:58
+ * @LastEditors: qianlishi
+ * @LastEditTime: 2022-03-11 10:37:53
+ */
+export const widgetHref =  {
+    code: 'widget-href',
+    type: 'text',
+    tabName: '文本栏',
+    label: '超链接',
+    icon: 'iconchaolianjie',
+    options: {
+      // 配置
+      setup: [
+        {
+          type: 'el-input-text',
+          label: '图层名称',
+          name: 'layerName',
+          required: false,
+          placeholder: '',
+          value: '超链接',
+        },
+        {
+          type: 'el-input-text',
+          label: '文本内容',
+          name: 'text',
+          required: false,
+          placeholder: '',
+          value: '超链接',
+        },
+        {
+          type: 'el-input-number',
+          label: '字体大小',
+          name: 'fontSize',
+          required: false,
+          placeholder: '',
+          value: '26',
+        },
+        {
+          type: 'vue-color',
+          label: '字体颜色',
+          name: 'color',
+          required: false,
+          placeholder: '',
+          value: '#FAD400',
+        },
+        {
+          type: 'el-input-number',
+          label: '字体间距',
+          name: 'letterSpacing',
+          required: false,
+          placeholder: '',
+          value: '0',
+        },
+        {
+          type: 'vue-color',
+          label: '字体背景',
+          name: 'background',
+          required: false,
+          placeholder: '',
+          value: 'rgba(115,170,229,.5)',
+        },
+        {
+          type: 'el-select',
+          label: '文字粗细',
+          name: 'fontWeight',
+          required: false,
+          placeholder: '',
+          selectOptions: [
+            {code: 'normal', name: '正常'},
+            {code: 'bold', name: '粗体'},
+            {code: 'bolder', name: '特粗体'},
+            {code: 'lighter', name: '细体'}
+          ],
+          value: 'normal'
+        },
+        {
+          type: 'el-select',
+          label: '对齐方式',
+          name: 'textAlign',
+          required: false,
+          placeholder: '',
+          selectOptions: [
+            {code: 'center', name: '居中'},
+            {code: 'left', name: '左对齐'},
+            {code: 'right', name: '右对齐'},
+          ],
+          value: 'center'
+        },
+        {
+          type: 'el-radio-group',
+          label: '跳转方式',
+          name: 'jumpMode',
+          required: false,
+          placeholder: '',
+          selectOptions: [
+            {
+              code: 'self',
+              name: '本窗口',
+            },
+            {
+              code: 'other',
+              name: '新窗口',
+            },
+          ],
+          value: 'self',
+        },
+        {
+          type: 'el-input-text',
+          label: '超链地址',
+          name: 'linkAdress',
+          required: false,
+          placeholder: '',
+          value: 'http://www.baidu.com',
+        },
+      ],
+      // 数据
+      data: [],
+      // 坐标
+      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: 100,
+        },
+        {
+          type: 'el-input-number',
+          label: '高度',
+          name: 'height',
+          required: false,
+          placeholder: '该容器在1080px大屏中的高度',
+          value: 40,
+        },
+      ],
+    }
+  }

+ 73 - 0
report-ui/src/views/screenDesigner/config/texts/widget-iframe.js

@@ -0,0 +1,73 @@
+/*
+ * @Descripttion: iframe json
+ * @version: 
+ * @Author: qianlishi
+ * @Date: 2021-08-29 07:17:55
+ * @LastEditors: qianlishi
+ * @LastEditTime: 2022-03-11 10:37:58
+ */
+export const widgetIframe =  {
+    code: 'widget-iframe',
+    type: 'text',
+    tabName: '文本栏',
+    label: '内联框架',
+    icon: 'iconkuangjia',
+    options: {
+      // 配置
+      setup: [
+        {
+          type: 'el-input-text',
+          label: '图层名称',
+          name: 'layerName',
+          required: false,
+          placeholder: '',
+          value: 'iframe',
+        },
+        {
+          type: 'el-input-text',
+          label: '地址',
+          name: 'iframeAdress',
+          required: false,
+          placeholder: '',
+          value: 'https://report.anji-plus.com/index.html',
+        },
+      ],
+      // 数据
+      data: [],
+      // 坐标
+      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: 300,
+        },
+        {
+          type: 'el-input-number',
+          label: '高度',
+          name: 'height',
+          required: false,
+          placeholder: '该容器在1080px大屏中的高度',
+          value: 200,
+        },
+      ],
+    }
+  }

+ 112 - 0
report-ui/src/views/screenDesigner/config/texts/widget-image.js

@@ -0,0 +1,112 @@
+/*
+ * @Descripttion: 图片json
+ * @version:
+ * @Author: qianlishi
+ * @Date: 2021-08-29 07:07:23
+ * @LastEditors: qianlishi
+ * @LastEditTime: 2022-03-11 10:38:01
+ */
+export const widgetImage = {
+    code: 'widget-image',
+    type: 'text',
+    tabName: '文本栏',
+    label: '图片',
+    icon: 'icontupian',
+    options: {
+      // 配置
+      setup: [
+        {
+          type: 'el-input-text',
+          label: '图层名称',
+          name: 'layerName',
+          required: false,
+          placeholder: '',
+          value: '图片',
+        },
+        {
+          type: 'el-switch',
+          label: '开启旋转',
+          name: 'startRotate',
+          required: false,
+          placeholder: '',
+          value: false,
+        },
+        {
+          type: 'el-slider',
+          label: '旋转速度',
+          name: 'rotationSpeed',
+          required: false,
+          placeholder: '',
+          value: 70
+        },
+        {
+          type: 'el-slider',
+          label: '透明度',
+          name: 'transparency',
+          required: false,
+          placeholder: '',
+          value: 100
+        },
+        {
+          type: 'el-input-number',
+          label: '圆角',
+          name: 'borderRadius',
+          required: false,
+          placeholder: '',
+          value: '0'
+        },
+        {
+          type: 'custom-upload',
+          label: '图片地址',
+          name: 'imageAdress',
+          required: false,
+          placeholder: '',
+          value: 'http://10.108.26.197:9095/file/download/fd20d563-00aa-45e2-b5db-aff951f814ec',
+        },
+        {
+          type: 'vue-color',
+          label: '背景颜色',
+          name: 'background',
+          required: false,
+          placeholder: '',
+        },
+      ],
+      // 数据
+      data: [],
+      // 坐标
+      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: 300,
+        },
+        {
+          type: 'el-input-number',
+          label: '高度',
+          name: 'height',
+          required: false,
+          placeholder: '该容器在1080px大屏中的高度',
+          value: 200,
+        },
+      ],
+    }
+  }

+ 177 - 0
report-ui/src/views/screenDesigner/config/texts/widget-marquee.js

@@ -0,0 +1,177 @@
+/*
+ * @Descripttion: 滚动文件json
+ * @version: 
+ * @Author: qianlishi
+ * @Date: 2021-08-29 07:00:00
+ * @LastEditors: qianlishi
+ * @LastEditTime: 2022-03-11 10:38:04
+ */
+export const widgetMarquee = {
+    code: 'widget-marquee',
+     type: 'text',
+    tabName: '文本栏',
+    label: '滚动文本',
+    icon: 'iconhengxiangwenzi',
+    options: {
+      // 配置
+      setup: [
+        {
+          type: 'el-input-text',
+          label: '图层名称',
+          name: 'layerName',
+          required: false,
+          placeholder: '',
+          value: '跑马灯',
+        },
+        {
+          type: 'el-input-text',
+          label: '文本内容',
+          name: 'text',
+          required: false,
+          placeholder: '',
+          value: '滚动文本',
+        },
+        {
+          type: 'el-input-number',
+          label: '字体大小',
+          name: 'fontSize',
+          required: false,
+          placeholder: '',
+          value: '26',
+        },
+        {
+          type: 'vue-color',
+          label: '字体颜色',
+          name: 'color',
+          required: false,
+          placeholder: '',
+          value: '#FAD400',
+        },
+        {
+          type: 'el-input-number',
+          label: '字体间距',
+          name: 'letterSpacing',
+          required: false,
+          placeholder: '',
+          value: '0',
+        },
+        {
+          type: 'vue-color',
+          label: '字体背景',
+          name: 'background',
+          required: false,
+          placeholder: '',
+          value: 'rgba(115,170,229,.5)',
+        },
+        {
+          type: 'el-select',
+          label: '文字粗细',
+          name: 'fontWeight',
+          required: false,
+          placeholder: '',
+          selectOptions: [
+            {code: 'normal', name: '正常'},
+            {code: 'bold', name: '粗体'},
+            {code: 'bolder', name: '特粗体'},
+            {code: 'lighter', name: '细体'}
+          ],
+          value: 'normal'
+        },
+/*        {
+          type: 'el-input-number',
+          label: '滚动速度',
+          name: 'jScrollPane',
+          //required: false,
+          placeholder: '',
+          value: '50',
+        }*/
+      ],
+      // 数据
+      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: '文本框',
+        },
+        {
+          type: 'dycustComponents',
+          label: '',
+          name: 'dynamicData',
+          required: false,
+          placeholder: '',
+          relactiveDom: 'dataType',
+          relactiveDomValue: 'dynamicData',
+          chartType: 'widget-text',
+          dictKey: 'TEXT_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: 100,
+        },
+        {
+          type: 'el-input-number',
+          label: '高度',
+          name: 'height',
+          required: false,
+          placeholder: '该容器在1080px大屏中的高度',
+          value: 40,
+        },
+      ],
+    }
+  }

+ 88 - 0
report-ui/src/views/screenDesigner/config/texts/widget-slider.js

@@ -0,0 +1,88 @@
+/*
+ * @Descripttion: 轮播图
+ * @version: 
+ * @Author: qianlishi
+ * @Date: 2021-08-29 07:08:53
+ * @LastEditors: qianlishi
+ * @LastEditTime: 2022-03-11 10:38:08
+ */
+export const widgetSliders =  {
+    code: 'widget-slider',
+    type: 'text',
+    tabName: '文本栏',
+    label: '轮播图片',
+    icon: 'slider',
+    options: {
+      // 配置
+      setup: [
+        {
+          type: 'el-input-text',
+          label: '图层名称',
+          name: 'layerName',
+          required: false,
+          placeholder: '',
+        },
+        {
+          type: 'el-switch',
+          label: '隐藏图层',
+          name: 'hideLayer',
+          required: false,
+          placeholder: '',
+        },
+        {
+          type: 'el-select',
+          label: '轮播方向',
+          name: 'tabDirection',
+          required: false,
+          placeholder: '',
+        },
+        {
+          type: 'el-select',
+          label: '选择器',
+          name: 'tabSelector',
+          required: false,
+          placeholder: '',
+        },
+        {
+          type: 'el-input-number',
+          label: '轮播时间',
+          name: 'tabTime',
+          required: false,
+          placeholder: '',
+        },
+      ],
+      // 数据
+      data: [],
+      // 坐标
+      position: [
+        {
+          type: 'el-input-number',
+          label: '左边距',
+          name: 'left',
+          required: true,
+          placeholder: '',
+        },
+        {
+          type: 'el-input-number',
+          label: '上边距',
+          name: 'top',
+          required: true,
+          placeholder: '',
+        },
+        {
+          type: 'el-input-number',
+          label: '宽度',
+          name: 'width',
+          required: true,
+          placeholder: '该容器在1920px大屏中的宽度',
+        },
+        {
+          type: 'el-input-number',
+          label: '高度',
+          name: 'height',
+          required: true,
+          placeholder: '该容器在1080px大屏中的高度',
+        },
+      ],
+    }
+  }

+ 305 - 0
report-ui/src/views/screenDesigner/config/texts/widget-table.js

@@ -0,0 +1,305 @@
+/*
+ * @Descripttion: 表格json
+ * @version:
+ * @Author: qianlishi
+ * @Date: 2021-08-29 07:16:10
+ * @LastEditors: qianlishi
+ * @LastEditTime: 2022-03-11 10:38:11
+ */
+export const widgetTable = {
+    code: 'widget-table',
+    type: 'text',
+    tabName: '文本栏',
+    label: '表格',
+    icon: 'iconbiaoge',
+    options: {
+      setup: [
+        {
+          type: 'el-input-text',
+          label: '图层名称',
+          name: 'layerName',
+          required: false,
+          placeholder: '',
+          value: '表格',
+        },
+        {
+          type: 'el-select',
+          label: '字体位置',
+          name: 'textAlign',
+          required: false,
+          placeholder: '',
+          selectOptions: [
+            {code: 'center', name: '居中'},
+            {code: 'left', name: '左对齐'},
+            {code: 'right', name: '右对齐'},
+          ],
+          value: 'center'
+        },
+        {
+          type: 'el-input-number',
+          label: '字体大小',
+          name: 'fontSize',
+          required: false,
+          placeholder: '',
+          value: '16'
+        },
+        {
+          type: 'el-input-number',
+          label: '显示行数',
+          name: 'vis',
+          required: false,
+          placeholder: '',
+          value: '5'
+        },
+        {
+          type: 'el-input-number',
+          label: '行高',
+          name: 'rowHeight',
+          required: false,
+          placeholder: '',
+          value: '50'
+        },
+        {
+          type: 'el-switch',
+          label: '开启滚动',
+          name: 'isRoll',
+          required: false,
+          placeholder: '',
+          value: true
+        },
+        {
+          type: 'el-select',
+          label: '动画效果',
+          name: 'effect',
+          required: false,
+          placeholder: '',
+          selectOptions: [
+            {code: 'top', name: '上滚动'},
+            {code: 'topLoop', name: '上循环滚动'},
+          ],
+          value: 'topLoop'
+        },
+        {
+          type: 'el-input-number',
+          label: '滚动间隔(毫秒)',
+          name: 'interTime',
+          required: false,
+          placeholder: '',
+          value: 2500
+        },
+        {
+          type: 'el-input-number',
+          label: '动效时间(毫秒)',
+          name: 'delayTime',
+          required: false,
+          placeholder: '',
+          value: 500
+        },
+        {
+          type: 'el-input-number',
+          label: '滚动个数',
+          name: 'scroll',
+          required: false,
+          placeholder: '',
+          value: 1
+        },
+        {
+          type: 'el-switch',
+          label: '边框线',
+          name: 'isLine',
+          required: false,
+          placeholder: '',
+          value: false
+        },
+        {
+          type: 'el-input-number',
+          label: '边框宽度',
+          name: 'borderWidth',
+          required: false,
+          placeholder: '',
+          value: 1
+        },
+        {
+          type: 'vue-color',
+          label: '边框颜色',
+          name: 'borderColor',
+          required: false,
+          placeholder: '',
+          value: '#fff'
+        },
+        [
+          {
+            name: '表头设置',
+            list: [
+              {
+                type: 'el-switch',
+                label: '表头显隐',
+                name: 'isHeader',
+                required: false,
+                placeholder: '',
+                value: true,
+              },
+              {
+                type: 'vue-color',
+                label: '表头颜色',
+                name: 'headColor',
+                require: false,
+                placeholder: '',
+                value: '#fff',
+              },
+              {
+                type: 'vue-color',
+                label: '表头背景',
+                name: 'headBackColor',
+                require: false,
+                placeholder: '',
+                value: '#0a73ff',
+              },
+            ],
+          },
+          {
+            name: '表体设置',
+            list: [
+              {
+                type: 'vue-color',
+                label: '文字颜色',
+                name: 'bodyColor',
+                required: false,
+                placeholder: '',
+                value: '#fff',
+              },
+              {
+                type: 'vue-color',
+                label: '表格背景色',
+                name: 'tableBgColor',
+                require: false,
+                placeholder: '',
+                value: '',
+              },
+              {
+                type: 'vue-color',
+                label: '奇行颜色',
+                name: 'oldColor',
+                require: false,
+                placeholder: '',
+                value: '#0a2732',
+              },
+              {
+                type: 'vue-color',
+                label: '偶行颜色',
+                name: 'eventColor',
+                required: false,
+                placeholder: '',
+                value: '#003b51'
+              }
+            ],
+          },
+        ],
+        {
+          type: 'dynamic-add-table',
+          label: '',
+          name: 'dynamicAddTable',
+          required: false,
+          placeholder: '',
+          value: [
+            {name: '日期', key: 'date', width: '50%'},
+            {name: '姓名', key: 'name', width: '50%'},
+            {name: '地址', key: 'address', width: '200%',
+          }]
+        }
+      ],
+      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: 30000
+        },
+        {
+          type: 'el-button',
+          label: '静态数据',
+          name: 'staticData',
+          required: false,
+          placeholder: '',
+          relactiveDom: 'dataType',
+          relactiveDomValue: 'staticData',
+          value: [
+            {date: '2016-05-02', name: '王小虎', address: '上海市普陀区金沙江路 1518 弄0001'},
+            {date: '2016-05-02', name: '王小虎', address: '上海市普陀区金沙江路 1518 弄0002'},
+            {date: '2016-05-02', name: '王小虎', address: '上海市普陀区金沙江路 1518 弄0003'},
+            {date: '2016-05-02', name: '王小虎', address: '上海市普陀区金沙江路 1518 弄0004'},
+            {date: '2016-05-02', name: '王小虎', address: '上海市普陀区金沙江路 1518 弄0005'},
+            {date: '2016-05-02', name: '王小虎', address: '上海市普陀区金沙江路 1518 弄0006'},
+            {date: '2016-05-02', name: '王小虎', address: '上海市普陀区金沙江路 1518 弄0007'},
+          ],
+        },
+        {
+          type: 'dycustComponents',
+          label: '',
+          name: 'dynamicData',
+          required: false,
+          placeholder: '',
+          relactiveDom: 'dataType',
+          relactiveDomValue: 'dynamicData',
+          chartType: 'widget-table',
+          dictKey: 'TEXT_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: 600,
+        },
+        {
+          type: 'el-input-number',
+          label: '高度',
+          name: 'height',
+          required: false,
+          placeholder: '该容器在1080px大屏中的高度',
+          value: 300,
+        },
+      ]
+    }
+  }

+ 183 - 0
report-ui/src/views/screenDesigner/config/texts/widget-text.js

@@ -0,0 +1,183 @@
+/*
+ * @Descripttion: 文本json文件
+ * @version: 
+ * @Author: qianlishi
+ * @Date: 2021-08-29 06:52:13
+ * @LastEditors: qianlishi
+ * @LastEditTime: 2022-03-11 10:38:13
+ */
+export const widgetText =  {
+    code: 'widget-text',
+    type: 'text',
+    tabName: '文本栏',
+    label: '文本',
+    icon: 'iconziyuan',
+    options: {
+      // 配置
+      setup: [
+        {
+          type: 'el-input-text',
+          label: '图层名称',
+          name: 'layerName',
+          required: false,
+          placeholder: '',
+          value: '文本框',
+        },
+        {
+          type: 'el-input-text',
+          label: '文本内容',
+          name: 'text',
+          required: false,
+          placeholder: '',
+          value: '文本框',
+        },
+        {
+          type: 'el-input-number',
+          label: '字体大小',
+          name: 'fontSize',
+          required: false,
+          placeholder: '',
+          value: '26',
+        },
+        {
+          type: 'vue-color',
+          label: '字体颜色',
+          name: 'color',
+          required: false,
+          placeholder: '',
+          value: '#FAD400',
+        },
+        {
+          type: 'el-input-number',
+          label: '字体间距',
+          name: 'letterSpacing',
+          required: false,
+          placeholder: '',
+          value: '0',
+        },
+        {
+          type: 'vue-color',
+          label: '字体背景',
+          name: 'background',
+          required: false,
+          placeholder: '',
+          value: 'rgba(115,170,229,.0)',
+        },
+        {
+          type: 'el-select',
+          label: '文字粗细',
+          name: 'fontWeight',
+          required: false,
+          placeholder: '',
+          selectOptions: [
+            {code: 'normal', name: '正常'},
+            {code: 'bold', name: '粗体'},
+            {code: 'bolder', name: '特粗体'},
+            {code: 'lighter', name: '细体'}
+          ],
+          value: 'normal'
+        },
+        {
+          type: 'el-select',
+          label: '对齐方式',
+          name: 'textAlign',
+          required: false,
+          placeholder: '',
+          selectOptions: [
+            {code: 'center', name: '居中'},
+            {code: 'left', name: '左对齐'},
+            {code: 'right', name: '右对齐'},
+          ],
+          value: 'center'
+        },
+      ],
+      // 数据
+      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: '文本框',
+        },
+        {
+          type: 'dycustComponents',
+          label: '',
+          name: 'dynamicData',
+          required: false,
+          placeholder: '',
+          relactiveDom: 'dataType',
+          relactiveDomValue: 'dynamicData',
+          chartType: 'widget-text',
+          dictKey: 'TEXT_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: 100,
+        },
+        {
+          type: 'el-input-number',
+          label: '高度',
+          name: 'height',
+          required: false,
+          placeholder: '该容器在1080px大屏中的高度',
+          value: 40
+        },
+      ],
+    }
+  }

+ 144 - 0
report-ui/src/views/screenDesigner/config/texts/widget-time.js

@@ -0,0 +1,144 @@
+/*
+ * @Descripttion: 时间控件json
+ * @version: 
+ * @Author: qianlishi
+ * @Date: 2021-08-29 07:05:52
+ * @LastEditors: qianlishi
+ * @LastEditTime: 2022-03-11 10:38:16
+ */
+export const widgetTime =  {
+    code: 'widget-time',
+    type: 'text',
+    tabName: '文本栏',
+    label: '当前时间',
+    icon: 'iconshijian',
+    options: {
+      // 配置
+      setup: [
+        {
+          type: 'el-input-text',
+          label: '图层名称',
+          name: 'layerName',
+          required: false,
+          placeholder: '',
+          value: '当前时间',
+        },
+        {
+          type: 'el-select',
+          label: '时间格式',
+          name: 'timeFormat',
+          required: false,
+          placeholder: '',
+          selectOptions: [
+            {code: 'yyyy-MM-dd', name: '日期'},
+            {code: 'yyyy-MM-dd hh:mm', name: '日期+时分'},
+            {code: 'yyyy-MM-dd hh:mm:ss', name: '日期+时分秒'},
+            {code: 'MM-dd', name: '日期无年'},
+            {code: 'hh:mm', name: '时分'},
+            {code: 'hh:mm:ss', name: '时分秒'},
+            {code: 'year-week', name: '日期+星期'},
+            {code: 'year-h-m-week', name: '日期+时分+星期'},
+            {code: 'year-h-m-s-week', name: '日期+时分秒+星期'},
+            {code: 'week', name: '星期'}
+          ],
+          value: 'yyyy-MM-dd hh:mm:ss'
+        },
+        {
+          type: 'el-input-number',
+          label: '字体间距',
+          name: 'letterSpacing',
+          required: false,
+          placeholder: '',
+          value: '0'
+        },
+        {
+          type: 'el-input-number',
+          label: '字体大小',
+          name: 'fontSize',
+          required: false,
+          placeholder: '',
+          value: '26'
+        },
+        {
+          type: 'vue-color',
+          label: '字体颜色',
+          name: 'color',
+          required: false,
+          placeholder: '',
+          value: '#FAD400'
+        },
+        {
+          type: 'vue-color',
+          label: '字体背景',
+          name: 'background',
+          required: false,
+          placeholder: '',
+          value: 'rgba(115,170,229,.5)'
+        },
+        {
+          type: 'el-select',
+          label: '文字粗细',
+          name: 'fontWeight',
+          required: false,
+          placeholder: '',
+          selectOptions: [
+            {code: 'normal', name: '正常'},
+            {code: 'bold', name: '粗体'},
+            {code: 'bolder', name: '特粗体'},
+            {code: 'lighter', name: '细体'}
+          ],
+          value: 'normal'
+        },
+        {
+          type: 'el-select',
+          label: '对齐方式',
+          name: 'textAlign',
+          required: false,
+          placeholder: '',
+          selectOptions: [
+            {code: 'center', name: '居中'},
+            {code: 'left', name: '左对齐'},
+            {code: 'right', name: '右对齐'},
+          ],
+          value: 'left'
+        },
+      ],
+      // 数据
+      data: [],
+      // 坐标
+      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: 300,
+        },
+        {
+          type: 'el-input-number',
+          label: '高度',
+          name: 'height',
+          required: false,
+          placeholder: '该容器在1080px大屏中的高度',
+          value: 100,
+        },
+      ],
+    }
+  }

+ 73 - 0
report-ui/src/views/screenDesigner/config/texts/widget-video.js

@@ -0,0 +1,73 @@
+/*
+ * @Descripttion: 视频json
+ * @version: 
+ * @Author: qianlishi
+ * @Date: 2021-08-29 07:10:22
+ * @LastEditors: qianlishi
+ * @LastEditTime: 2022-03-11 10:38:19
+ */
+export const widgetVideo =  {
+    code: 'widget-video',
+    type: 'text',
+    tabName: '文本栏',
+    label: '视频',
+    icon: 'iconshipin',
+    options: {
+      // 配置
+      setup: [
+        {
+          type: 'el-input-text',
+          label: '图层名称',
+          name: 'layerName',
+          required: false,
+          placeholder: '',
+          value: 'video',
+        },
+        {
+          type: 'el-input-text',
+          label: '地址',
+          name: 'videoAdress',
+          required: false,
+          placeholder: '',
+          value: 'https://www.w3school.com.cn//i/movie.ogg',
+        },
+      ],
+      // 数据
+      data: [],
+      // 坐标
+      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: 300,
+        },
+        {
+          type: 'el-input-number',
+          label: '高度',
+          name: 'height',
+          required: false,
+          placeholder: '该容器在1080px大屏中的高度',
+          value: 200,
+        },
+      ],
+    }
+  }

+ 865 - 0
report-ui/src/views/screenDesigner/index.vue

@@ -0,0 +1,865 @@
+<template>
+  <div class="layout">
+    <!-- 操作栏 -->
+    <div class="layout-bar">
+      <div class="bar-item"><i class="iconfont iconsave"></i>保存</div>
+      <div class="bar-item"><i class="iconfont iconyulan"></i>预览</div>
+      <div class="bar-item"><i class="iconfont iconundo"></i>撤销</div>
+      <div class="bar-item"><i class="iconfont iconhuifubeifen"></i>恢复</div>
+      <div class="bar-item">
+        <!-- <el-upload
+          class="el-upload"
+          ref="upload"
+          :action="uploadUrl"
+          :headers="headers"
+          accept=".zip"
+          :on-success="handleUpload"
+          :on-error="handleError"
+          :show-file-list="false"
+          :limit="1"
+        >
+          <i class="iconfont icondaoru"></i>
+        </el-upload> -->
+        导入
+      </div>
+      <div class="bar-item">
+        <i class="iconfont icondaochu"></i>
+        <el-dropdown>
+          <span class="el-dropdown-link">
+            导出<i class="el-icon-arrow-down el-icon--right"></i>
+          </span>
+          <el-dropdown-menu slot="dropdown">
+            <el-dropdown-item>导出(包含数据集)</el-dropdown-item>
+            <el-dropdown-item>导出(不包含数据集)</el-dropdown-item>
+          </el-dropdown-menu>
+        </el-dropdown>
+      </div>
+    </div>
+
+    <div class="layout-container">
+      <!-- 图表 -->
+      <el-tabs class="layout-left" type="border-card">
+        <el-tab-pane>
+          <span slot="label"><i class="el-icon-date icon"></i>工具栏</span>
+          <div class="chart-type">
+            <el-tabs class="type-left" tab-position="left">
+              <el-tab-pane
+                v-for="(item, index) in widgetTools"
+                :key="index"
+                :label="item.name"
+              >
+                <draggable
+                  v-for="(it, idx) in item.list"
+                  :key="idx"
+                  @end="evt => widgetOnDragged(evt, it.code)"
+                >
+                  <div class="tools-item">
+                    <span class="tools-item-icon">
+                      <i class="iconfont" :class="it.icon"></i>
+                    </span>
+                    <span class="tools-item-text">{{ it.label }}</span>
+                  </div>
+                </draggable>
+              </el-tab-pane>
+            </el-tabs>
+          </div>
+        </el-tab-pane>
+        <el-tab-pane>
+          <span slot="label" class="icon"
+            ><i class="el-icon-date icon"></i>图层</span
+          >
+          <div class="tools-item">
+            <span class="tools-item-icon">
+              <i class="iconfont iconkuangjia"></i>
+            </span>
+            <span class="tools-item-text">内联框架</span>
+          </div>
+          <div class="tools-item">
+            <span class="tools-item-icon">
+              <i class="iconfont iconkuangjia"></i>
+            </span>
+            <span class="tools-item-text">内联框架</span>
+          </div>
+        </el-tab-pane>
+      </el-tabs>
+
+      <!-- 设计器 -->
+      <div
+        class="layout-middle"
+        :style="{ width: middleWidth + 'px', height: middleHeight + 'px' }"
+      >
+        <div
+          class="workbench-container"
+          :style="{
+            width: bigscreenWidthInWorkbench + 'px',
+            height: bigscreenHeightInWorkbench + 'px'
+          }"
+          @mousedown="handleMouseDown"
+        >
+          <vue-ruler-tool
+            v-model="dashboard.presetLine"
+            class="vueRuler"
+            :step-length="50"
+            :parent="true"
+            :position="'relative'"
+            :is-scale-revise="true"
+            :visible.sync="dashboard.presetLineVisible"
+          >
+            <div
+              id="workbench"
+              class="workbench"
+              :style="{
+                transform: workbenchTransform,
+                width: bigscreenWidth + 'px',
+                height: bigscreenHeight + 'px',
+                'background-color': dashboard.backgroundColor,
+                'background-image': 'url(' + dashboard.backgroundImage + ')',
+                'background-position': '0% 0%',
+                'background-size': '100% 100%',
+                'background-repeat': 'initial',
+                'background-attachment': 'initial',
+                'background-origin': 'initial',
+                'background-clip': 'initial'
+              }"
+              @click.self="setOptionsOnClickScreen"
+            >
+              <div v-if="grade" class="bg-grid"></div>
+              <widget
+                ref="widgets"
+                v-for="(widget, index) in widgets"
+                :key="index"
+                v-model="widget.value"
+                :index="index"
+                :step="step"
+                :type="widget.type"
+                :bigscreen="{ bigscreenWidth, bigscreenHeight }"
+                @onActivated="setOptionsOnClickWidget"
+                @contextmenu.prevent.native="rightClick($event, index)"
+                @mousedown.prevent.native="widgetsClick(index)"
+                @mouseup.prevent.native="widgetsMouseup"
+              />
+            </div>
+          </vue-ruler-tool>
+        </div>
+      </div>
+
+      <!-- 设置 -->
+      <div class="layout-right">
+        <el-tabs v-model="activeName" type="border-card" :stretch="true">
+          <el-tab-pane
+            v-if="
+              isNotNull(widgetOptions.setup) ||
+                isNotNull(widgetOptions.collapse)
+            "
+            name="first"
+            label="配置"
+          >
+            <dynamicForm
+              ref="formData"
+              :options="widgetOptions.setup"
+              @onChanged="val => widgetValueChanged('setup', val)"
+            />
+          </el-tab-pane>
+          <el-tab-pane
+            v-if="isNotNull(widgetOptions.data)"
+            name="second"
+            label="数据"
+          >
+            <dynamicForm
+              ref="formData"
+              :options="widgetOptions.data"
+              @onChanged="val => widgetValueChanged('data', val)"
+            />
+          </el-tab-pane>
+          <el-tab-pane
+            v-if="isNotNull(widgetOptions.position)"
+            name="third"
+            label="坐标"
+          >
+            <dynamicForm
+              ref="formData"
+              :options="widgetOptions.position"
+              @onChanged="val => widgetValueChanged('position', val)"
+            />
+          </el-tab-pane>
+        </el-tabs>
+      </div>
+    </div>
+
+    <content-menu
+      :visible.sync="visibleContentMenu"
+      :style-obj="styleObj"
+      @deletelayer="deletelayer"
+      @copylayer="copylayer"
+      @istopLayer="istopLayer"
+      @setlowLayer="setlowLayer"
+      @moveupLayer="moveupLayer"
+      @movedownLayer="movedownLayer"
+    />
+  </div>
+</template>
+<script>
+import {
+  insertDashboard,
+  detailDashboard,
+  importDashboard,
+  exportDashboard
+} from "@/api/bigscreen";
+import { screenConfig } from "./config/texts/screenConfig.js";
+import { widgetTools, getToolByCode } from "./config/index.js";
+import VueRulerTool from "vue-ruler-tool"; // 大屏设计页面的标尺插件
+import widget from "./widget/index.vue";
+import dynamicForm from "./components/dynamicForm.vue";
+import draggable from "vuedraggable";
+import contentMenu from "./components/contentMenu";
+import { getToken } from "@/utils/auth";
+import { Revoke } from "@/utils/revoke"; //处理历史记录 2022-02-22
+export default {
+  components: {
+    VueRulerTool,
+    widget,
+    dynamicForm,
+    draggable,
+    contentMenu
+  },
+  data() {
+    return {
+      uploadUrl:
+        process.env.BASE_API +
+        "/reportDashboard/import/" +
+        this.$route.query.reportCode,
+      grade: false,
+      layerWidget: [],
+      widgetTools: widgetTools, // 左侧工具栏的组件图标,将js变量加入到当前作用域
+      widthLeftForTools: 200, // 左侧工具栏宽度
+      widthLeftForToolsHideButton: 15, // 左侧工具栏折叠按钮宽度
+      widthLeftForOptions: 300, // 右侧属性配置区
+      widthPaddingTools: 18,
+      toolIsShow: true, // 左侧工具栏是否显示
+
+      bigscreenWidth: 1920, // 大屏设计的大小
+      bigscreenHeight: 1080,
+      revoke: null, //处理历史记录 2022-02-22
+
+      // 工作台大屏画布,保存到表gaea_report_dashboard中
+      dashboard: {
+        id: null,
+        title: "", // 大屏页面标题
+        width: 1920, // 大屏设计宽度
+        height: 1080, // 大屏设计高度
+        backgroundColor: "", // 大屏背景色
+        backgroundImage: "", // 大屏背景图片
+        refreshSeconds: null, // 大屏刷新时间间隔
+        presetLine: [], // 辅助线
+        presetLineVisible: true // 辅助线是否显示
+      },
+      // 大屏的标记
+      screenCode: "",
+      // 大屏画布中的组件
+      widgets: [
+        {
+          // type和value最终存到数据库中去,保存到gaea_report_dashboard_widget中
+          type: "widget-text",
+          value: {
+            setup: {},
+            data: {},
+            position: {
+              width: 100,
+              height: 100,
+              left: 0,
+              top: 0,
+              zIndex: 0
+            }
+          },
+          // options属性是从工具栏中拿到的tools中拿到
+          options: []
+        }
+      ], // 工作区中拖放的组件
+
+      // 当前激活组件
+      widgetIndex: 0,
+      // 当前激活组件右侧配置属性
+      widgetOptions: {
+        setup: [], // 配置
+        data: [], // 数据
+        position: [] // 坐标
+      },
+      flagWidgetClickStopPropagation: false, // 点击组件时阻止事件冒泡传递到画布click事件上
+      styleObj: {
+        left: 0,
+        top: 0
+      },
+      visibleContentMenu: false,
+      rightClickIndex: -1,
+      activeName: "first"
+    };
+  },
+  computed: {
+    step() {
+      return Number(100 / (this.bigscreenScaleInWorkbench * 100));
+    },
+    headers() {
+      return {
+        Authorization: getToken() // 直接从本地获取token就行
+      };
+    },
+    // 左侧折叠切换时,动态计算中间区的宽度
+    middleWidth() {
+      let widthLeftAndRight = 0;
+      if (this.toolIsShow) {
+        widthLeftAndRight += this.widthLeftForTools; // 左侧工具栏宽度
+      }
+      widthLeftAndRight += this.widthLeftForToolsHideButton; // 左侧工具栏折叠按钮宽度
+      widthLeftAndRight += this.widthLeftForOptions; // 右侧配置栏宽度
+
+      let middleWidth = this.bodyWidth - widthLeftAndRight;
+      return middleWidth;
+    },
+    middleHeight() {
+      return this.bodyHeight;
+    },
+    // 设计台按大屏的缩放比例
+    bigscreenScaleInWorkbench() {
+      let widthScale =
+        (this.middleWidth - this.widthPaddingTools) / this.bigscreenWidth;
+      let heightScale =
+        (this.middleHeight - this.widthPaddingTools) / this.bigscreenHeight;
+      return Math.min(widthScale, heightScale);
+    },
+    workbenchTransform() {
+      return `scale(${this.bigscreenScaleInWorkbench}, ${
+        this.bigscreenScaleInWorkbench
+      })`;
+    },
+    // 大屏在设计模式的大小
+    bigscreenWidthInWorkbench() {
+      return this.getPXUnderScale(this.bigscreenWidth) + this.widthPaddingTools;
+    },
+    bigscreenHeightInWorkbench() {
+      return (
+        this.getPXUnderScale(this.bigscreenHeight) + this.widthPaddingTools
+      );
+    }
+  },
+  watch: {
+    widgets: {
+      handler(val) {
+        this.handlerLayerWidget(val);
+        //以下部分是记录历史
+        this.$nextTick(() => {
+          this.revoke.push(this.widgets);
+        });
+      },
+      deep: true
+    }
+  },
+  created() {
+    /* 以下是记录历史的 */
+    this.revoke = new Revoke();
+  },
+  mounted() {
+    this.initScreen();
+    // 如果是新的设计工作台
+    // this.initEchartData();
+    this.widgets = [];
+    window.addEventListener("mouseup", () => {
+      this.grade = false;
+    });
+  },
+  methods: {
+    // 初始化大屏
+    initScreen() {
+      this.widgetOptions = screenConfig["options"];
+    },
+
+    /**
+     * @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++) {
+        const obj = {};
+        obj.icon = getToolByCode(val[i].type).icon;
+        const options = val[i].options["setup"];
+        options.forEach(el => {
+          if (el.name == "layerName") {
+            obj.label = el.value;
+          }
+        });
+        layerWidgetArr.push(obj);
+      }
+      this.layerWidget = layerWidgetArr;
+    },
+    async initEchartData() {
+      const reportCode = this.$route.query.reportCode;
+      const { code, data } = await detailDashboard(reportCode);
+      if (code != 200) return;
+      const processData = this.handleInitEchartsData(data);
+      const screenData = this.handleBigScreen(data.dashboard);
+      this.widgets = processData;
+      this.dashboard = screenData;
+      this.bigscreenWidth = this.dashboard.width;
+      this.bigscreenHeight = this.dashboard.height;
+    },
+    handleBigScreen(data) {
+      const optionScreen = getToolByCode("screen").options;
+      const setup = optionScreen.setup;
+      for (const key in data) {
+        for (let i = 0; i < setup.length; i++) {
+          if (key == setup[i].name) {
+            setup[i].value = data[key];
+          }
+        }
+      }
+      this.setOptionsOnClickScreen();
+      return {
+        backgroundColor: (data && data.backgroundColor) || "",
+        backgroundImage: (data && data.backgroundImage) || "",
+        height: (data && data.height) || "1080",
+        title: (data && data.title) || "",
+        width: (data && data.width) || "1920"
+      };
+    },
+    handleInitEchartsData(data) {
+      const widgets = data.dashboard ? data.dashboard.widgets : [];
+      const widgetsData = [];
+      for (let i = 0; i < widgets.length; i++) {
+        let obj = {};
+        obj.type = widgets[i].type;
+        obj.value = {
+          setup: widgets[i].value.setup,
+          data: widgets[i].value.data,
+          position: widgets[i].value.position
+        };
+        const tool = this.deepClone(getToolByCode(widgets[i].type));
+        const option = tool.options;
+        const options = this.handleOptionsData(widgets[i].value, option);
+        obj.options = options;
+        widgetsData.push(obj);
+      }
+      return widgetsData;
+    },
+    handleOptionsData(data, option) {
+      for (const key in data.setup) {
+        for (let i = 0; i < option.setup.length; i++) {
+          let item = option.setup[i];
+          if (Object.prototype.toString.call(item) == "[object Object]") {
+            if (key == option.setup[i].name) {
+              option.setup[i].value = data.setup[key];
+            }
+          } else if (Object.prototype.toString.call(item) == "[object Array]") {
+            for (let j = 0; j < item.length; j++) {
+              const list = item[j].list;
+              list.forEach(el => {
+                if (key == el.name) {
+                  el.value = data.setup[key];
+                }
+              });
+            }
+          }
+        }
+      }
+      // position
+      for (const key in data.position) {
+        for (let i = 0; i < option.position.length; i++) {
+          if (key == option.position[i].name) {
+            option.position[i].value = data.position[key];
+          }
+        }
+      }
+      // data
+      for (const key in data.data) {
+        for (let i = 0; i < option.data.length; i++) {
+          if (key == option.data[i].name) {
+            option.data[i].value = data.data[key];
+          }
+        }
+      }
+      return option;
+    },
+    // 保存数据
+    async saveData() {
+      if (!this.widgets || this.widgets.length == 0) {
+        this.$message.error("请添加组件");
+        return;
+      }
+      const screenData = {
+        reportCode: this.$route.query.reportCode,
+        dashboard: {
+          title: this.dashboard.title,
+          width: this.dashboard.width,
+          height: this.dashboard.height,
+          backgroundColor: this.dashboard.backgroundColor,
+          backgroundImage: this.dashboard.backgroundImage
+        },
+        widgets: this.widgets
+      };
+      const { code, data } = await insertDashboard(screenData);
+      if (code == "200") {
+        this.$message.success("保存成功!");
+      }
+    },
+    // 预览
+    viewScreen() {
+      let routeUrl = this.$router.resolve({
+        path: "/bigscreen/viewer",
+        query: { reportCode: this.$route.query.reportCode }
+      });
+      window.open(routeUrl.href, "_blank");
+    },
+    //  导出
+    async exportDashboard(val) {
+      const fileName = this.$route.query.reportCode + ".zip";
+
+      const param = {
+        reportCode: this.$route.query.reportCode,
+        showDataSet: val
+      };
+      exportDashboard(param).then(res => {
+        const that = this;
+        const type = res.type;
+        if (type == "application/json") {
+          let reader = new FileReader();
+          reader.readAsText(res, "utf-8");
+          reader.onload = function() {
+            const data = JSON.parse(reader.result);
+            that.$message.error(data.message);
+          };
+          return;
+        }
+
+        const blob = new Blob([res], { type: "application/octet-stream" });
+        if (window.navigator.msSaveOrOpenBlob) {
+          //msSaveOrOpenBlob方法返回bool值
+          navigator.msSaveBlob(blob, fileName); //本地保存
+        } else {
+          const link = document.createElement("a"); //a标签下载
+          link.href = window.URL.createObjectURL(blob);
+          link.download = fileName;
+          link.click();
+          window.URL.revokeObjectURL(link.href);
+        }
+      });
+    },
+    // 上传成功的回调
+    handleUpload(response, file, fileList) {
+      //清除el-upload组件中的文件
+      this.$refs.upload.clearFiles();
+      //刷新大屏页面
+      this.initEchartData();
+      if (response.code == "200") {
+        this.$message({
+          message: "导入成功!",
+          type: "success"
+        });
+      } else {
+        this.$message({
+          message: response.message,
+          type: "error"
+        });
+      }
+    },
+    handleError(err) {
+      this.$message({
+        message: "上传失败!",
+        type: "error"
+      });
+    },
+
+    // 在缩放模式下的大小
+    getPXUnderScale(px) {
+      return this.bigscreenScaleInWorkbench * px;
+    },
+
+    // 拖动一个组件放到工作区中去,在拖动结束时,放到工作区对应的坐标点上去
+    widgetOnDragged(evt, widgetCode) {
+      let widgetType = widgetCode;
+
+      // 获取结束坐标和列名
+      let eventX = evt.originalEvent.clientX; // 结束在屏幕的x坐标
+      let eventY = evt.originalEvent.clientY; // 结束在屏幕的y坐标
+
+      let workbenchPosition = this.getDomTopLeftById("workbench");
+      let widgetTopInWorkbench = eventY - workbenchPosition.top;
+      let widgetLeftInWorkbench = eventX - workbenchPosition.left;
+
+      // 计算在缩放模式下的x y
+      let x = widgetLeftInWorkbench / this.bigscreenScaleInWorkbench;
+      let y = widgetTopInWorkbench / this.bigscreenScaleInWorkbench;
+
+      // 复制一个组件
+      let tool = getToolByCode(widgetType);
+      let widgetJson = {
+        type: widgetType,
+        value: {
+          setup: {},
+          data: {},
+          position: {
+            width: 0,
+            height: 0,
+            left: 0,
+            top: 0,
+            zIndex: 0
+          }
+        },
+        options: tool.options
+      };
+      // 处理默认值
+      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));
+      // 激活新组件的配置属性
+      this.setOptionsOnClickWidget(this.widgets.length - 1);
+    },
+
+    // 对组件默认值处理
+    handleDefaultValue(widgetJson) {
+      for (const key in widgetJson) {
+        if (key == "options") {
+          // collapse、data、position、setup
+          // setup 处理
+          for (let i = 0; i < widgetJson.options.setup.length; i++) {
+            const item = widgetJson.options.setup[i];
+            if (Object.prototype.toString.call(item) == "[object Object]") {
+              widgetJson.value.setup[item.name] = item.value;
+            } else if (
+              Object.prototype.toString.call(item) == "[object Array]"
+            ) {
+              for (let j = 0; j < item.length; j++) {
+                const list = item[j].list;
+                list.forEach(el => {
+                  widgetJson.value.setup[el.name] = el.value;
+                });
+              }
+            }
+          }
+          // position
+          for (let i = 0; i < widgetJson.options.position.length; i++) {
+            const item = widgetJson.options.position[i];
+            if (item.value) {
+              widgetJson.value.position[item.name] = item.value;
+            }
+          }
+          // data 处理
+          if (widgetJson.options.data && widgetJson.options.data.length > 0) {
+            for (let i = 0; i < widgetJson.options.data.length; i++) {
+              const item = widgetJson.options.data[i];
+              if (item.value) {
+                widgetJson.value.data[item.name] = item.value;
+              }
+            }
+          }
+        }
+      }
+      return widgetJson;
+    },
+    layerClick(index) {
+      this.widgetIndex = index;
+      this.widgetsClick(index);
+    },
+    // 如果是点击大屏设计器中的底层,加载大屏底层属性
+    setOptionsOnClickScreen() {
+      this.screenCode = "screen";
+      // 选中不同的组件 右侧都显示第一栏
+      this.activeName = "first";
+      this.widgetOptions = getToolByCode("screen")["options"];
+    },
+
+    // 如果是点击某个组件,获取该组件的配置项
+    setOptionsOnClickWidget(obj) {
+      this.screenCode = "";
+      if (typeof obj == "number") {
+        this.widgetOptions = this.deepClone(this.widgets[obj]["options"]);
+        return;
+      }
+      if (obj.index < 0 || obj.index >= this.widgets.length) {
+        return;
+      }
+      this.widgetIndex = obj.index;
+      this.widgets[obj.index].value.position = obj;
+      this.widgets[obj.index].options.position.forEach(el => {
+        for (const key in obj) {
+          if (el.name == key) {
+            el.value = obj[key];
+          }
+        }
+      });
+      this.widgetOptions = this.deepClone(this.widgets[obj.index]["options"]);
+    },
+    widgetsClick(index) {
+      const draggableArr = this.$refs.widgets;
+      for (let i = 0; i < draggableArr.length; i++) {
+        if (i == index) {
+          this.$refs.widgets[i].$refs.draggable.setActive(true);
+        } else {
+          this.$refs.widgets[i].$refs.draggable.setActive(false);
+        }
+      }
+      this.setOptionsOnClickWidget(index);
+      this.grade = true;
+    },
+    widgetsMouseup(e) {
+      this.grade = false;
+    },
+    handleMouseDown() {
+      const draggableArr = this.$refs.widgets;
+      for (let i = 0; i < draggableArr.length; i++) {
+        this.$refs.widgets[i].$refs.draggable.setActive(false);
+      }
+    },
+    // 将当前选中的组件,右侧属性值更新
+    widgetValueChanged(key, val) {
+      if (this.screenCode == "screen") {
+        let newSetup = new Array();
+        this.dashboard = this.deepClone(val);
+        if (this.bigscreenWidth != this.dashboard.width) {
+          this.bigscreenWidth = this.dashboard.width;
+        }
+        if (this.bigscreenHeight != this.dashboard.height) {
+          this.bigscreenHeight = this.dashboard.height;
+        }
+        this.widgetOptions.setup.forEach(el => {
+          if (el.name == "width") {
+            el.value = this.bigscreenWidth;
+          } else if (el.name == "height") {
+            el.value = this.bigscreenHeight;
+          }
+          newSetup.push(el);
+        });
+        this.widgetOptions.setup = newSetup;
+      } else {
+        for (let i = 0; i < this.widgets.length; i++) {
+          if (this.widgetIndex == i) {
+            this.widgets[i].value[key] = this.deepClone(val);
+            this.setDefaultValue(this.widgets[i].options[key], val);
+          }
+        }
+      }
+    },
+    rightClick(event, index) {
+      this.rightClickIndex = index;
+      const left = event.clientX;
+      const top = event.clientY;
+      if (left || top) {
+        this.styleObj = {
+          left: left + "px",
+          top: top + "px",
+          display: "block"
+        };
+      }
+      this.visibleContentMenu = true;
+      return false;
+    },
+    setDefaultValue(options, val) {
+      for (let i = 0; i < options.length; i++) {
+        if (Object.prototype.toString.call(options[i]) == "[object Object]") {
+          for (const k in val) {
+            if (options[i].name == k) {
+              options[i].value = val[k];
+            }
+          }
+        } else if (
+          Object.prototype.toString.call(options[i]) == "[object Array]"
+        ) {
+          for (let j = 0; j < options[i].length; j++) {
+            const list = options[i][j].list;
+            for (let z = 0; z < list.length; z++) {
+              for (const k in val) {
+                if (list[z].name == k) {
+                  list[z].value = val[k];
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    datadragEnd(evt) {
+      evt.preventDefault();
+      this.widgets = this.swapArr(this.widgets, evt.oldIndex, evt.newIndex);
+    },
+    // 数组 元素互换位置
+    swapArr(arr, oldIndex, newIndex) {
+      arr[oldIndex] = arr.splice(newIndex, 1, arr[oldIndex])[0];
+      return arr;
+    },
+    // 删除
+    deletelayer() {
+      this.widgets.splice(this.rightClickIndex, 1);
+    },
+    // 复制
+    copylayer() {
+      const obj = this.deepClone(this.widgets[this.rightClickIndex]);
+      this.widgets.splice(this.widgets.length, 0, obj);
+    },
+    // 置顶
+    istopLayer() {
+      if (this.rightClickIndex + 1 < this.widgets.length) {
+        const temp = this.widgets.splice(this.rightClickIndex, 1)[0];
+        this.widgets.push(temp);
+      }
+    },
+    // 置底
+    setlowLayer() {
+      if (this.rightClickIndex != 0) {
+        this.widgets.unshift(this.widgets.splice(this.rightClickIndex, 1)[0]);
+      }
+    },
+    // 上移一层
+    moveupLayer() {
+      if (this.rightClickIndex != 0) {
+        this.widgets[this.rightClickIndex] = this.widgets.splice(
+          this.rightClickIndex - 1,
+          1,
+          this.widgets[this.rightClickIndex]
+        )[0];
+      } else {
+        this.widgets.push(this.widgets.shift());
+      }
+    },
+    // 下移一层
+    movedownLayer() {
+      if (this.rightClickIndex != this.widgets.length - 1) {
+        this.widgets[this.rightClickIndex] = this.widgets.splice(
+          this.rightClickIndex + 1,
+          1,
+          this.widgets[this.rightClickIndex]
+        )[0];
+      } else {
+        this.widgets.unshift(this.widgets.splice(this.rightClickIndex, 1)[0]);
+      }
+    }
+  }
+};
+</script>
+<style scoped lang="scss">
+@import "../../assets/styles/screenDesigner.scss";
+</style>

+ 19 - 0
report-ui/src/views/screenDesigner/node.md

@@ -0,0 +1,19 @@
+### 目录结构
+
+├── screenDesigner
+│ ├── components(文件夹) 公共组件目录
+│ ├── config(文件夹) 配置 json
+│ │ ├── texts(文件夹) (文本、滚动文本、超链接、当前时间、图片、视频、表格、内联框架)
+│ │ ├── barCharts(文件夹) (柱状图)
+│ │ ├── lineCharts(文件夹) (折线图)
+│ │ ├── pieCharts(文件夹) (饼图)
+│ │ ├── mapCharts(文件夹) (地图)
+│ ├── util(文件夹) 公共 js
+│ ├── widget(文件夹) 图表组件
+│ │ ├── texts(文件夹) (文本、滚动文本、超链接、当前时间、图片、视频、表格、内联框架)
+│ │ ├── barCharts(文件夹) (柱状图)
+│ │ ├── lineCharts(文件夹) (折线图)
+│ │ ├── pieCharts(文件夹) (饼图)
+│ │ ├── mapCharts(文件夹) (地图)
+│ ├── index.vue 大屏设计器
+│ ├── preview.vue 预览大屏

+ 1 - 0
report-ui/src/views/screenDesigner/preview.vue

@@ -0,0 +1 @@
+

+ 23 - 0
report-ui/src/views/screenDesigner/util/common.js

@@ -0,0 +1,23 @@
+
+export const converArr = (data) => {
+  console.log(data)
+  let tempArr = [], newArr = []
+  for (let i = 0; i < data.length; i++) {
+    const item = data[i]
+    if (tempArr.indexOf(item.type) === -1) {
+      newArr.push({
+        name: item.tabName,
+        type: item.type,
+        list: [item]
+      })
+      tempArr.push(item.type);
+    } else {
+      for (let j = 0; j < newArr.length; j++) {
+        if (newArr[j].type == item.type) {
+          newArr[j].list.push(item)
+        }
+      }
+    }
+  }
+  return newArr
+}

+ 109 - 0
report-ui/src/views/screenDesigner/widget/index.vue

@@ -0,0 +1,109 @@
+<template>
+  <avue-draggable
+    :step="step"
+    :width="widgetsWidth"
+    :height="widgetsHeight"
+    :left="widgetsLeft"
+    :top="widgetsTop"
+    ref="draggable"
+    :index="index"
+    :z-index="-1"
+    @blur="handleBlur"
+  >
+    <component :is="type" :value="value" />
+  </avue-draggable>
+</template>
+
+<script>
+import widgetHref from "./texts/widgetHref.vue";
+import WidgetIframe from "./texts/widgetIframe.vue";
+import widgetImage from "./texts/widgetImage.vue";
+import WidgetMarquee from "./texts/widgetMarquee.vue";
+import widgetSlider from "./texts/widgetSlider.vue";
+import widgetTable from "./texts/widgetTable.vue";
+import widgetText from "./texts/widgetText.vue";
+import widgetTime from "./texts/widgetTime.vue";
+import widgetVideo from "./texts/widgetVideo.vue";
+
+export default {
+  name: "Widget",
+  components: {
+    widgetHref,
+    WidgetIframe,
+    widgetImage,
+    WidgetMarquee,
+    widgetSlider,
+    widgetTable,
+    widgetText,
+    widgetTime,
+    widgetVideo
+  },
+  model: {
+    prop: "value",
+    event: "input"
+  },
+  props: {
+    /*
+    widget-text widget-marquee widget-href widget-time widget-image widget-slider widget-video widget-table widget-iframe widget-universal
+    widget-linechart widget-barlinechart widget-piechart widget-hollow-piechart widget-funnel widget-gauge widget-china-map
+    */
+    index: Number, // 当前组件,在工作区变量widgetInWorkbench中的索引
+    type: String,
+    bigscreen: Object,
+    value: {
+      type: [Object],
+      default: () => {}
+    },
+    step: Number
+  },
+  data() {
+    return {
+      data: {
+        setup: {},
+        data: {},
+        position: {}
+      }
+    };
+  },
+  computed: {
+    widgetsWidth() {
+      return this.value.position.width;
+    },
+    widgetsHeight() {
+      return this.value.position.height;
+    },
+    widgetsLeft() {
+      return this.value.position.left;
+    },
+    widgetsTop() {
+      return this.value.position.top;
+    },
+    widgetsZIndex() {
+      return this.value.position.zIndex || 1;
+    }
+  },
+  mounted() {},
+  methods: {
+    handleBlur({ index, left, top, width, height }) {
+      this.$emit("onActivated", { index, left, top, width, height });
+      this.$refs.draggable.setActive(true);
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.vue-draggalbe {
+  position: absolute;
+}
+
+.widget-active {
+  cursor: move;
+  border: 1px dashed #09f;
+  background-color: rgba(115, 170, 229, 0.5);
+}
+
+.avue-draggable {
+  padding: 0 !important;
+}
+</style>

+ 80 - 0
report-ui/src/views/screenDesigner/widget/texts/widgetHref.vue

@@ -0,0 +1,80 @@
+<!--
+ * @Author: lide1202@hotmail.com
+ * @Date: 2021-3-13 11:04:24
+ * @Last Modified by:   lide1202@hotmail.com
+ * @Last Modified time: 2021-3-13 11:04:24
+ !-->
+<template>
+  <a
+    :href="styleColor.linkAdress"
+    :style="styleColor"
+    :target="styleColor.jumpMode"
+    >{{ styleColor.text }}</a
+  >
+</template>
+
+<script>
+export default {
+  name: "WidgetHref",
+  components: {},
+  props: {
+    value: Object,
+    ispreview: Boolean
+  },
+  data() {
+    return {
+      options: {}
+    };
+  },
+  computed: {
+    transStyle() {
+      return this.objToOne(this.options);
+    },
+    styleColor() {
+      return {
+        position: this.ispreview ? "absolute" : "static",
+        color: this.transStyle.color || "#fff",
+        "font-weight": this.transStyle.fontWeight || "600",
+        text: this.transStyle.text || "超链接",
+        "font-size": this.transStyle.fontSize + "px" || "12px",
+        "letter-spacing": this.transStyle.letterSpacing + "em",
+        background: this.transStyle.background,
+        "text-align": this.transStyle.textAlign,
+        display:
+          this.transStyle.hideLayer == undefined
+            ? "block"
+            : this.transStyle.hideLayer
+            ? "none"
+            : "block",
+        width: this.transStyle.width + "px",
+        height: this.transStyle.height + "px",
+        left: this.transStyle.left + "px",
+        top: this.transStyle.top + "px",
+        right: this.transStyle.right + "px",
+        linkAdress: this.transStyle.linkAdress,
+        jumpMode: this.transStyle.jumpMode == "other" ? "_blank" : "_self"
+      };
+    }
+  },
+  watch: {
+    value: {
+      handler(val) {
+        this.options = val;
+      },
+      deep: true
+    }
+  },
+  mounted() {
+    this.options = this.value;
+  },
+  methods: {}
+};
+</script>
+
+<style scoped lang="scss">
+a {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+}
+</style>

+ 60 - 0
report-ui/src/views/screenDesigner/widget/texts/widgetIframe.vue

@@ -0,0 +1,60 @@
+<template>
+  <iframe
+    :style="styleColor"
+    :src="styleColor.iframeAdress"
+    width="100%"
+    height="100%"
+  />
+</template>
+
+<script>
+export default {
+  name: "WidgetIframe",
+  components: {},
+  props: {
+    value: Object,
+    ispreview: Boolean
+  },
+  data() {
+    return {
+      options: {}
+    };
+  },
+  computed: {
+    transStyle() {
+      return this.objToOne(this.options);
+    },
+    styleColor() {
+      return {
+        position: this.ispreview ? "absolute" : "static",
+        width: this.transStyle.width + "px",
+        height: this.transStyle.height + "px",
+        left: this.transStyle.left + "px",
+        top: this.transStyle.top + "px",
+        right: this.transStyle.right + "px",
+        iframeAdress: this.transStyle.iframeAdress
+      };
+    }
+  },
+  watch: {
+    value: {
+      handler(val) {
+        this.options = val;
+      },
+      deep: true
+    }
+  },
+  mounted() {
+    this.options = this.value;
+  },
+  methods: {}
+};
+</script>
+
+<style scoped lang="scss">
+iframe {
+  width: 100%;
+  height: 100%;
+  border: none;
+}
+</style>

+ 79 - 0
report-ui/src/views/screenDesigner/widget/texts/widgetImage.vue

@@ -0,0 +1,79 @@
+<template>
+  <div class="imagebox" :style="styleColor">
+    <img
+      :class="transStyle.startRotate ? 'startImg' : ''"
+      :style="imgStyle"
+      :src="imgStyle.imageAdress"
+      alt=""
+    />
+  </div>
+</template>
+<script>
+export default {
+  name: "WidgetImage",
+  components: {},
+  props: {
+    value: Object,
+    ispreview: Boolean
+  },
+  data() {
+    return {
+      options: {}
+    };
+  },
+  computed: {
+    transStyle() {
+      return this.objToOne(this.options);
+    },
+    styleColor() {
+      return {
+        position: this.ispreview ? "absolute" : "static",
+        background: this.transStyle.background,
+        "text-align": this.transStyle.textAlign,
+        width: this.transStyle.width + "px",
+        height: this.transStyle.height + "px",
+        left: this.transStyle.left + "px",
+        top: this.transStyle.top + "px",
+        right: this.transStyle.right + "px"
+      };
+    },
+    imgStyle() {
+      return {
+        imageAdress: this.transStyle.imageAdress,
+        "border-radius": this.transStyle.borderRadius + "px",
+        opacity: this.transStyle.transparency / 100,
+        animation: this.transStyle.startRotate? "turn "+(101-this.transStyle.rotationSpeed)/10+"s linear infinite":"none"
+      };
+    }
+  },
+  watch: {
+    value: {
+      handler(val) {
+        this.options = val;
+      },
+      deep: true
+    }
+  },
+  created() {
+    this.options = this.value;
+  },
+  mounted() {},
+  methods: {}
+};
+</script>
+
+<style scoped lang="scss">
+.imagebox {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+}
+.imagebox img {
+  width: 100%;
+  height: 100%;
+}
+.startImg {
+  animation: turn 1s linear infinite;
+}
+
+</style>

+ 100 - 0
report-ui/src/views/screenDesigner/widget/texts/widgetMarquee.vue

@@ -0,0 +1,100 @@
+<template>
+  <div class="text" :style="styleColor">
+    <marquee behavior="" :direction="styleColor.direction">{{
+      styleColor.text
+    }}</marquee>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "WidgetMarquee",
+  components: {},
+  props: {
+    value: Object,
+    ispreview: Boolean
+  },
+  data() {
+    return {
+      options: {}
+    };
+  },
+  computed: {
+    transStyle() {
+      return this.objToOne(this.options);
+    },
+    styleColor() {
+      return {
+        position: this.ispreview ? "absolute" : "static",
+        color: this.transStyle.color,
+        "font-weight": this.transStyle.fontWeight,
+        text: this.transStyle.text,
+        "font-size": this.transStyle.fontSize + "px",
+        "letter-spacing": this.transStyle.letterSpacing + "em",
+        background: this.transStyle.background,
+        "text-align": this.transStyle.textAlign,
+        width: this.transStyle.width + "px",
+        height: this.transStyle.height + "px",
+        left: this.transStyle.left + "px",
+        top: this.transStyle.top + "px",
+        right: this.transStyle.right + "px",
+        marqueeSet: this.transStyle.marqueeSet,
+        styleColor: this.transStyle.marqueeQuit
+      };
+    },
+    isBehavior() {
+      return this.styleColor.marqueeSet ? "start()" : "stop()";
+    }
+  },
+  watch: {
+    value: {
+      handler(val) {
+        this.options = val;
+        this.optionsData = val.data;
+        this.setOptionsData();
+      },
+      deep: true
+    }
+  },
+  mounted() {
+    this.options = this.value;
+    this.optionsData = this.value.data;
+    this.setOptionsData();
+  },
+  methods: {
+    // 数据解析
+    setOptionsData() {
+      const optionsData = this.optionsData; // 数据类型 静态 or 动态
+      if (optionsData.dataType == "dynamicData") {
+        this.dynamicDataFn(optionsData.dynamicData, optionsData.refreshTime);
+      } else {};
+    },
+    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.styleColor.text = res[0].value;
+        this.$forceUpdate();
+      });
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.text {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+}
+</style>

+ 128 - 0
report-ui/src/views/screenDesigner/widget/texts/widgetSlider.vue

@@ -0,0 +1,128 @@
+<template>
+  <superslide :options="options" class="slideBox">
+    <!-- slides -->
+    <div class="bd">
+      <ul>
+        <li>
+          <img src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=717294809,2494697366&fm=26&gp=0.jpg" alt="">
+        </li>
+        <li>
+          <img src="https://dss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2090090414,3038344648&fm=26&gp=0.jpg" alt="">
+        </li>
+      </ul>
+    </div>
+    <!-- Optional controls slots -->
+    <!-- slot="titCell" -->
+    <div slot="titCell" class="hd">
+      <ul>
+        <li class="on">1</li>
+        <li class="">2</li>
+        <li class="">3</li>
+      </ul>
+    </div>
+    <!-- slot="prev" -->
+    <a slot="prev" class="prev" href="javascript:void(0)" />
+    <!-- slot="next" -->
+    <a slot="next" class="next" href="javascript:void(0)" />
+    <!-- slot="pageStateCell" -->
+    <span slot="pageStateCell" class="pageState" />
+  </superslide>
+</template>
+<script>
+export default {
+  name: 'WidgetSlider',
+  components: {},
+  data() {
+    return {
+      options: {
+        mainCell: '.bd ul',
+        autoPlay: true,
+        effect: 'leftLoop',
+      },
+    }
+  },
+  mounted() {},
+  methods: {},
+}
+</script>
+
+<style scoped lang="scss">
+.slideBox {
+  width: 450px;
+  height: 230px;
+  overflow: hidden;
+  position: relative;
+}
+.slideBox .hd {
+  height: 15px;
+  overflow: hidden;
+  position: absolute;
+  right: 5px;
+  bottom: 5px;
+  z-index: 1;
+}
+.slideBox .hd ul {
+  overflow: hidden;
+  zoom: 1;
+  float: left;
+}
+.slideBox .hd ul li {
+  float: left;
+  margin-right: 2px;
+  width: 15px;
+  height: 15px;
+  line-height: 14px;
+  text-align: center;
+  background: #fff;
+  cursor: pointer;
+}
+.slideBox .hd ul li.on {
+  background: #f00;
+  color: #fff;
+}
+.slideBox .bd {
+  position: relative;
+  height: 100%;
+  z-index: 0;
+}
+.slideBox .bd li {
+  zoom: 1;
+  vertical-align: middle;
+}
+.slideBox .bd img {
+  width: 450px;
+  height: 230px;
+  display: block;
+}
+/* 下面是前/后按钮代码,如果不需要删除即可 */
+.slideBox .prev,
+.slideBox .next {
+  position: absolute;
+  left: 3%;
+  top: 50%;
+  margin-top: -25px;
+  display: block;
+  width: 32px;
+  height: 40px;
+  background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOsAAAAgCAMAAADE+3+QAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MkYyNjE4RUVDRjgxMTFFMjk1ODhDQkIzNDg3MjdGNUEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MkYyNjE4RUZDRjgxMTFFMjk1ODhDQkIzNDg3MjdGNUEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoyRjI2MThFQ0NGODExMUUyOTU4OENCQjM0ODcyN0Y1QSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDoyRjI2MThFRENGODExMUUyOTU4OENCQjM0ODcyN0Y1QSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PmLDzTcAAAAJUExURcHBwf///////wCiYf4AAAADdFJOU///ANfKDUEAAACjSURBVHja5JnRDUQhDMNM9h/6STfCgeKPsIBjhGgpnHPy7wLyYLUicC5IPJGtJfi5ngsSL1Q7AYgsW8QTV7YJJ6psFU1M2S6YiLJlLPFk21CiydaRaOT+7mKxhYOERDcuCBy+cheiJHBqHEYGqZzPug6d4aG7aajmDPUSQz3iUO8/9KYbeqsPzWCGZmtDM9OhWfjSH8fY31UuSMkT2UqET4ABAHo9NyVXKF5RAAAAAElFTkSuQmCC) -110px
+    5px no-repeat;
+  filter: alpha(opacity=50);
+  opacity: 0.5;
+}
+.slideBox .next {
+  left: auto;
+  right: 3%;
+  background-position: 8px 5px;
+}
+.slideBox .prev:hover,
+.slideBox .next:hover {
+  filter: alpha(opacity=100);
+  opacity: 1;
+}
+.slideBox .prevStop {
+  display: none;
+}
+.slideBox .nextStop {
+  display: none;
+}
+</style>

+ 257 - 0
report-ui/src/views/screenDesigner/widget/texts/widgetTable.vue

@@ -0,0 +1,257 @@
+<template>
+  <div :style="styleObj">
+    <superslide v-if="hackReset" :options="options" class="txtScroll-top">
+      <!--表头-->
+      <div class="title">
+        <div
+          v-for="(item, index) in header"
+          :style="[headerTableStlye, tableFiledWidth(index), tableRowHeight()]"
+          :key="index"
+        >
+          {{ item.name }}
+        </div>
+      </div>
+      <!--数据-->
+      <div class="bd">
+        <ul class="infoList">
+          <li
+            v-for="(item, index) in list"
+            :key="index"
+            :style="tableRowHeight()"
+          >
+            <div
+              v-for="(itemChild, idx) in header"
+              :key="idx"
+              :style="[
+                bodyTableStyle,
+                bodyTable(index),
+                tableFiledWidth(idx),
+                tableRowHeight()
+              ]"
+            >
+              {{ item[itemChild.key] }}
+            </div>
+          </li>
+        </ul>
+      </div>
+    </superslide>
+  </div>
+</template>
+<script>
+import vue from "vue";
+import VueSuperSlide from "vue-superslide";
+
+vue.use(VueSuperSlide);
+export default {
+  props: {
+    value: Object,
+    ispreview: Boolean
+  },
+  data() {
+    return {
+      hackReset: true,
+      options: {
+        titCell: ".hd ul",
+        mainCell: ".bd ul",
+        effect: "topLoop",
+        autoPage: true,
+        //effect: "top",
+        autoPlay: true,
+        vis: 5,
+        rowHeight: "50px"
+      },
+      header: [],
+      list: [],
+      optionsSetUp: {},
+      optionsPosition: {},
+      optionsData: {}
+    };
+  },
+  computed: {
+    styleObj() {
+      console.log(this.optionsSetUp);
+      const allStyle = this.optionsPosition;
+      return {
+        position: this.ispreview ? "absolute" : "static",
+        width: allStyle.width + "px",
+        height: allStyle.height + "px",
+        left: allStyle.left + "px",
+        top: allStyle.top + "px",
+        background: this.optionsSetUp.tableBgColor
+      };
+    },
+    headerTableStlye() {
+      const headStyle = this.optionsSetUp;
+      return {
+        "text-align": headStyle.textAlign,
+        "font-size": headStyle.fontSize + "px",
+        "border-style": headStyle.isLine ? "solid" : "none",
+        "border-width": headStyle.borderWidth + "px",
+        "border-color": headStyle.borderColor,
+        display: headStyle.isHeader ? "block" : "none",
+        color: headStyle.headColor,
+        "background-color": headStyle.headBackColor
+      };
+    },
+    bodyTableStyle() {
+      const bodyStyle = this.optionsSetUp;
+      return {
+        "text-align": bodyStyle.textAlign,
+        "font-size": bodyStyle.fontSize + "px",
+        "border-style": bodyStyle.isLine ? "solid" : "none",
+        "border-width": bodyStyle.borderWidth + "px",
+        "border-color": bodyStyle.borderColor,
+        color: bodyStyle.bodyColor,
+        "background-color": bodyStyle.tableBgColor
+      };
+    }
+  },
+  watch: {
+    value: {
+      handler(val) {
+        this.optionsSetUp = val.setup;
+        this.optionsPosition = val.position;
+        this.optionsData = val.data;
+        this.initData();
+      },
+      deep: true
+    }
+  },
+  mounted() {
+    this.optionsSetUp = this.value.setup;
+    this.optionsPosition = this.value.position;
+    this.optionsData = this.value.data;
+    this.initData();
+  },
+  methods: {
+    initData() {
+      this.handlerRollFn();
+      this.handlerHead();
+      this.handlerData();
+      this.visConfig();
+    },
+    visConfig() {
+      this.options.vis = this.optionsSetUp.vis;
+    },
+    handlerRollFn() {
+      const options = this.options;
+      const rollSet = this.optionsSetUp;
+      options.autoPlay = rollSet.isRoll;
+      options.effect = rollSet.effect;
+      options.interTime = rollSet.interTime;
+      options.delayTime = rollSet.delayTime;
+      options.scroll = rollSet.scroll;
+      this.options = options;
+      this.hackResetFun();
+    },
+    handlerHead() {
+      const head = this.optionsSetUp.dynamicAddTable;
+      this.header = head;
+    },
+    handlerData() {
+      const tableData = this.optionsData;
+      tableData.dataType == "staticData"
+        ? this.handlerStaticData(tableData.staticData)
+        : this.handlerDynamicData(tableData.dynamicData, tableData.refreshTime);
+    },
+    handlerStaticData(data) {
+      this.list = data;
+    },
+    handlerDynamicData(data, refreshTime) {
+      if (!data) return;
+      if (this.ispreview) {
+        this.getEchartData(data);
+        this.flagInter = setInterval(() => {
+          this.getEchartData(data);
+        }, refreshTime);
+      } else {
+        this.getEchartData(data);
+      }
+    },
+    getEchartData(val) {
+      const data = this.queryEchartsData(val);
+      data.then(res => {
+        this.list = res;
+        this.hackResetFun();
+      });
+    },
+    // vue hack 之强制刷新组件
+    hackResetFun() {
+      this.hackReset = false;
+      this.$nextTick(() => {
+        this.hackReset = true;
+      });
+    },
+    // 计算 奇偶背景色
+    bodyTable(index) {
+      let styleJson = {};
+      if (index % 2) {
+        styleJson["background-color"] = this.optionsSetUp.eventColor;
+      } else {
+        styleJson["background-color"] = this.optionsSetUp.oldColor;
+      }
+      return styleJson;
+    },
+    tableRowHeight() {
+      let styleJson = {};
+      if (this.optionsSetUp.rowHeight) {
+        styleJson["height"] = this.optionsSetUp.rowHeight + "px";
+        styleJson["line-height"] = this.optionsSetUp.rowHeight + "px";
+      } else {
+        styleJson["height"] = this.options.rowHeight;
+        styleJson["line-height"] = this.optionsSetUp.rowHeight + "px";
+      }
+      return styleJson;
+    },
+    tableFiledWidth(index) {
+      let styleJson = {};
+      if (this.optionsSetUp.dynamicAddTable[index].width) {
+        styleJson["width"] = this.optionsSetUp.dynamicAddTable[index].width;
+      }
+      return styleJson;
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+/* 本例子css */
+.txtScroll-top {
+  overflow: hidden;
+  position: relative;
+}
+
+.title {
+  display: flex;
+  flex-direction: row;
+  width: 100%;
+}
+
+.title > div {
+  height: 50px;
+  line-height: 50px;
+  width: 100%;
+}
+
+.txtScroll-top .bd {
+  width: 100%;
+}
+
+.txtScroll-top .infoList li {
+  height: 50px;
+  line-height: 50px;
+  display: flex;
+  flex-direction: row;
+}
+
+.txtScroll-top .infoList li > div {
+  width: 100%;
+}
+
+/*.txtScroll-top .infoList li:nth-child(n) {
+  background: rgb(0, 59, 81);
+}
+
+.txtScroll-top .infoList li:nth-child(2n) {
+  background: rgb(10, 39, 50);
+}*/
+</style>

+ 98 - 0
report-ui/src/views/screenDesigner/widget/texts/widgetText.vue

@@ -0,0 +1,98 @@
+<!--
+ * @Author: lide1202@hotmail.com
+ * @Date: 2021-3-13 11:04:24
+ * @Last Modified by:   lide1202@hotmail.com
+ * @Last Modified time: 2021-3-13 11:04:24
+ !-->
+<template>
+  <div class="text" :style="styleColor">{{ styleColor.text }}</div>
+</template>
+
+<script>
+export default {
+  name: "WidgetText",
+  components: {},
+  props: {
+    value: Object,
+    ispreview: Boolean
+  },
+  data() {
+    return {
+      options: {},
+      optionsData: {}
+    };
+  },
+  computed: {
+    transStyle() {
+      return this.objToOne(this.options);
+    },
+    styleColor() {
+      return {
+        position: this.ispreview ? "absolute" : "static",
+        color: this.transStyle.color,
+        "font-weight": this.transStyle.fontWeight,
+        text: this.transStyle.text,
+        "font-size": this.transStyle.fontSize + "px",
+        "letter-spacing": this.transStyle.letterSpacing + "em",
+        background: this.transStyle.background,
+        "text-align": this.transStyle.textAlign,
+        width: this.transStyle.width + "px",
+        height: this.transStyle.height + "px",
+        left: this.transStyle.left + "px",
+        top: this.transStyle.top + "px",
+        right: this.transStyle.right + "px"
+      };
+    }
+  },
+  watch: {
+    value: {
+      handler(val) {
+        this.options = val;
+        this.optionsData = val.data;
+        this.setOptionsData();
+      },
+      deep: true
+    }
+  },
+  mounted() {
+    this.options = this.value;
+    this.optionsData = this.value.data;
+    this.setOptionsData();
+  },
+  methods: {
+    // 数据解析
+    setOptionsData() {
+      const optionsData = this.optionsData; // 数据类型 静态 or 动态
+      if (optionsData.dataType == "dynamicData") {
+        this.dynamicDataFn(optionsData.dynamicData, optionsData.refreshTime);
+      } else {};
+    },
+    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.styleColor.text = res[0].value;
+        this.$forceUpdate();
+      });
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.text {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+}
+</style>

+ 163 - 0
report-ui/src/views/screenDesigner/widget/texts/widgetTime.vue

@@ -0,0 +1,163 @@
+<template>
+  <div class="text" :style="styleColor">{{ timestr }}</div>
+</template>
+<script>
+export default {
+  name: "WidgetTime",
+  components: {},
+  props: {
+    value: Object,
+    ispreview: Boolean
+  },
+  data() {
+    return {
+      timestr: "",
+      options: {}
+    };
+  },
+  computed: {
+    transStyle() {
+      console.log(this.objToOne(this.options));
+      return this.objToOne(this.options);
+    },
+    styleColor() {
+      return {
+        position: this.ispreview ? "absolute" : "static",
+        color: this.transStyle.color || "#fff",
+        text: this.transStyle.text || "文本框",
+        "font-size": this.transStyle.fontSize + "px" || "30px",
+        "letter-spacing": this.transStyle.letterSpacing + "em",
+        background: this.transStyle.background,
+        "font-weight": this.transStyle.fontWeight,
+        "text-align": this.transStyle.textAlign,
+        display:
+          this.transStyle.hideLayer == undefined
+            ? "block"
+            : this.transStyle.hideLayer
+            ? "none"
+            : "block",
+        width: this.transStyle.width + "px",
+        height: this.transStyle.height + "px",
+        left: this.transStyle.left + "px",
+        top: this.transStyle.top + "px",
+        right: this.transStyle.right + "px"
+      };
+    }
+  },
+  watch: {
+    value: {
+      handler(val) {
+        this.options = val;
+      },
+      deep: true
+    }
+  },
+  mounted() {
+    this.options = this.value;
+    this.getTime();
+  },
+  methods: {
+    formatFunction(date, fmt) {
+      if (/(y+)/.test(fmt)) {
+        fmt = fmt.replace(
+          RegExp.$1,
+          (date.getFullYear() + "").substr(4 - RegExp.$1.length)
+        );
+      }
+      const o = {
+        "M+": date.getMonth() + 1, // 月份
+        "d+": date.getDate(), // 日
+        "h+": date.getHours(), // 小时
+        "m+": date.getMinutes(), // 分
+        "s+": date.getSeconds(), // 秒
+        "q+": Math.floor((date.getMonth() + 3) / 3), // 季度
+        S: date.getMilliseconds() // 毫秒
+      };
+      for (let k in o) {
+        if (new RegExp("(" + k + ")").test(fmt)) {
+          fmt = fmt.replace(
+            RegExp.$1,
+            RegExp.$1.length == 1
+              ? o[k]
+              : ("00" + o[k]).substr(("" + o[k]).length)
+          );
+        }
+      }
+      return fmt;
+    },
+    check(val) {
+      if (val < 10) {
+        return "0" + val;
+      } else {
+        return val;
+      }
+    },
+    formatWeek(date, fmt) {
+      const year = date.getFullYear();
+      const month = date.getMonth() + 1;
+      const day = date.getDate();
+      const hours = date.getHours();
+      const minutes = date.getMinutes();
+      const seconds = date.getSeconds();
+      let dayCycle = date.getDay();
+      const dayCycleArray = ["日", "一", "二", "三", "四", "五", "六"];
+      for (let i = 0; i < 7; i++) {
+        if (dayCycle == i) {
+          dayCycle = " 星期" + dayCycleArray[i];
+        }
+      }
+      if (fmt == "year-week") {
+        return year + "-" + month + "-" + day + dayCycle;
+      } else if (fmt == "year-h-m-week") {
+        return (
+          year +
+          "-" +
+          month +
+          "-" +
+          day +
+          " " +
+          hours +
+          ":" +
+          minutes +
+          dayCycle
+        );
+      } else if (fmt == "year-h-m-s-week") {
+        return (
+          year +
+          "-" +
+          month +
+          "-" +
+          day +
+          " " +
+          hours +
+          ":" +
+          minutes +
+          ":" +
+          seconds +
+          dayCycle
+        );
+      } else if (fmt == "week") {
+        return dayCycle;
+      }
+    },
+    displayTime() {
+      this.timestr =
+        this.transStyle.timeFormat.indexOf("week") > -1
+          ? this.formatWeek(new Date(), this.transStyle.timeFormat)
+          : this.formatFunction(new Date(), this.transStyle.timeFormat);
+    },
+    getTime() {
+      setInterval(() => {
+        this.displayTime();
+      }, 1000);
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+.text {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+}
+</style>

+ 61 - 0
report-ui/src/views/screenDesigner/widget/texts/widgetVideo.vue

@@ -0,0 +1,61 @@
+<template>
+  <video :style="styleColor" :src="styleColor.videoAdress" controls="controls">
+    您的浏览器不支持 video 标签。
+  </video>
+</template>
+
+<script>
+export default {
+  name: "WidgetVideo",
+  components: {},
+  props: {
+    value: Object,
+    ispreview: Boolean
+  },
+  data() {
+    return {
+      options: {}
+    };
+  },
+  computed: {
+    transStyle() {
+      return this.objToOne(this.options);
+    },
+    styleColor() {
+      return {
+        position: this.ispreview ? "absolute" : "static",
+        width: this.transStyle.width + "px",
+        height: this.transStyle.height + "px",
+        left: this.transStyle.left + "px",
+        top: this.transStyle.top + "px",
+        right: this.transStyle.right + "px",
+        videoAdress: this.transStyle.videoAdress
+      };
+    }
+  },
+  watch: {
+    value: {
+      handler(val) {
+        this.options = val;
+      },
+      deep: true
+    }
+  },
+  mounted() {
+    this.options = this.value;
+  },
+  methods: {}
+};
+</script>
+
+<style scoped lang="scss">
+.container {
+  width: 100%;
+  height: 100%;
+}
+video {
+  width: 100%;
+  height: 100%;
+  display: block;
+}
+</style>

Some files were not shown because too many files changed in this diff