Browse Source

fix:文本组件实现值可通过其他组件计算得来

liu.shiyi 1 year ago
parent
commit
bb13f1bc94

+ 11 - 10
data-room-ui/packages/BasicComponents/Texts/index.vue

@@ -38,16 +38,17 @@ export default {
   },
   methods: {
     // 通过表达式计算得来的值
-    // getDataByExpression (config) {
-    //   if (this.config.expression) {
-    //   // eslint-disable-next-line no-new-func
-    //     const result = new Function('dataset', this.config.expression)
-    //     config.customize.title = result(this.dataset)
-    //     // 同时将计算得来的值保存到公共的数据存储的地方
-    //     this.updateDataset({ code: config.code, title: config.title, data: [{ title: config.customize.title }] })
-    //     this.changeChartConfig(config)
-    //   }
-    // },
+    getDataByExpression (config) {
+      // 如果表达式是由其他组件的值构成的
+      if (this.config.expressionCodes && this.config.expressionCodes.length) {
+      // eslint-disable-next-line no-new-func
+        const result = new Function('dataset', 'computedDatas', this.config.expression)
+        config.customize.title = result(this.dataset, this.computedDatas)
+        // 同时将计算得来的值保存到公共的数据存储的地方
+        this.updateComputedDatas({ code: config.code, title: config.title, data: config.customize.title })
+      // this.changeChartConfig(config)
+      }
+    },
     dataFormatting (config, data) {
       // 文本数据配置原则:选择数据集则以后端返回的数据为主,否则以设置面板中标题设置为准
       if (config.dataSource.businessKey) {

+ 1 - 1
data-room-ui/packages/BasicComponents/Texts/setting.vue

@@ -21,7 +21,7 @@
           prop="title"
         >
           <el-input
-            v-model="config.customize.title"
+            v-model="config.title"
             placeholder="请输入标题"
             clearable
           />

+ 12 - 1
data-room-ui/packages/BasicComponents/Texts/settingConfig.js

@@ -23,6 +23,14 @@ export const settingConfig = {
       label: '维度', // 维度/查询字段
       enable: false,
       multiple: true // 是否多选
+    },
+    text: { // 文本占位符
+      label: '文本内容', // 维度/查询字段
+      enable: true
+    },
+    expression: { // 文本占位符
+      label: '表达式', // 维度/查询字段
+      enable: true
     }
   }
 }
@@ -32,7 +40,10 @@ const customConfig = {
     version: '2023071001',
     url: 'https://www.runoob.com/',
     expression: 'return ',
-    expressionCodes: []
+    expressionCodes: [],
+    rotateX: 0,
+    rotateY: 0,
+    rotateZ: 0
   },
   customize: {
     title: '文本标签占位符',

+ 37 - 2
data-room-ui/packages/BigScreenDesign/RightSetting/DataSetting.vue

@@ -38,6 +38,25 @@
                 </template>
               </data-set-select>
             </el-form-item>
+            <el-form-item
+              v-if="config.option.displayOption.text && config.option.displayOption.text.enable"
+              label="文本内容"
+            >
+              <el-input
+                v-model="config.customize.title"
+                placeholder="请输入文本内容"
+                clearable
+              />
+            </el-form-item>
+            <el-form-item
+              v-if="config.option.displayOption.expression && config.option.displayOption.expression.enable"
+              label="表达式"
+            >
+              <i
+                class="el-icon-edit expression"
+                @click="openExpression"
+              />
+            </el-form-item>
           </div>
         </div>
         <div
@@ -575,6 +594,10 @@ data.forEach(item => {
           :config="config"
           :source-field-list="sourceFieldList"
         />
+        <expression-dialog
+          ref="expressionDialog"
+          :config="config"
+        />
       </div>
     </el-form>
   </div>
@@ -589,13 +612,15 @@ import ComponentBinding from 'data-room-ui/BigScreenDesign/RightSetting/Componen
 import dataSetSelect from 'data-room-ui/DataSetSetting/index.vue'
 import { mapState } from 'vuex'
 import { getDataSetDetails } from 'data-room-ui/js/api/bigScreenApi'
+import ExpressionDialog from 'data-room-ui/BigScreenDesign/RightSetting/ExpressionDialog.vue'
 export default {
   name: 'DataSetting',
   components: {
     ComponentRelation,
     ComponentBinding,
     dataSetSelect,
-    ElDragSelect
+    ElDragSelect,
+    ExpressionDialog
   },
   data () {
     return {
@@ -720,6 +745,10 @@ export default {
     }
   },
   methods: {
+    // 打开表达式弹窗
+    openExpression () {
+      this.$refs.expressionDialog.init()
+    },
     // 切换前后端分页
     serverPaginationChange (val) {
       this.config.customize.webPagination = !val
@@ -750,7 +779,7 @@ export default {
           this.fieldsList = res.fields
           // 初始化时以组件本来的参数设置为主
           if (type === 'initial') {
-            let deleteKeys = []
+            const deleteKeys = []
             for (const key in this.config.dataSource.params) {
               const param = res?.params?.find(field => field.name === key)
               // 如果组件参数在数据集中找不到,说明参数已经被删除,不需要再显示
@@ -925,6 +954,12 @@ export default {
       justify-content: center;
     }
   }
+  //表达式样式
+  .expression{
+    &:hover{
+      cursor: pointer;
+    }
+  }
   // 修改设置面板样式
   .data-setting-box{
     .data-setting-data-box{

+ 221 - 0
data-room-ui/packages/BigScreenDesign/RightSetting/ExpressionDialog.vue

@@ -0,0 +1,221 @@
+<template>
+  <el-dialog
+    :close-on-click-modal="false"
+    title="表达式"
+    width="60%"
+    :visible.sync="formVisible"
+    :append-to-body="false"
+    class="bs-dialog-wrap bs-el-dialog"
+  >
+    <div class="main-box">
+      <div class="left-box">
+        <el-tree
+          ref="tree"
+          :data="treeData"
+          :indent="0"
+          :props="{ label: 'label', children: 'children' }"
+          :default-expand-all="true"
+          :highlight-current="true"
+          :expand-on-click-node="false"
+          class="bs-el-tree tree-box"
+          @node-click="handleNodeClick"
+        />
+      </div>
+      <div class="right-box">
+        <div class="codemirror-wrap">
+          <codemirror
+            ref="codemirrorRef"
+            v-model="currentConfig.expression"
+            class="codemirror-box"
+            :options="codemirrorOption"
+            @dragover.prevent
+          />
+          <el-button
+            class="btn-box"
+            type="primary"
+            @click="executeScript"
+          >
+            运行脚本
+          </el-button>
+        </div>
+        <div class="script-content-box">
+          {{ scriptContent }}
+        </div>
+      </div>
+    </div>
+    <div
+      slot="footer"
+      class="dialog-footer"
+    >
+      <el-button
+        class="bs-el-button-default cancel"
+        @click="cancel"
+      >
+        取消
+      </el-button>
+      <el-button
+        type="primary"
+        @click="sure"
+      >
+        确定
+      </el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { codemirror } from 'vue-codemirror'
+import 'codemirror/mode/javascript/javascript'
+import 'codemirror/lib/codemirror.css'
+import 'codemirror/theme/nord.css'
+import { mapMutations, mapState } from 'vuex'
+import cloneDeep from 'lodash/cloneDeep'
+export default {
+  name: 'ExpressionDialog',
+  components: {
+    codemirror
+  },
+  props: {
+    config: {
+      type: Object,
+      default: () => {
+      }
+    }
+  },
+  data () {
+    return {
+      scriptContent: '', // 脚本执行的内容
+      expression: '123',
+      formVisible: false,
+      codemirrorOption: {
+        mode: 'text/javascript',
+        lineNumbers: true,
+        lineWrapping: true,
+        theme: 'nord',
+        extraKey: { Ctrl: 'autocomplete' },
+        hintOptions: {
+          completeSingle: true
+        }
+      }
+    }
+  },
+  computed: {
+    ...mapState({
+      dataset: state => state.bigScreen.dataset,
+      computedDatas: state => state.bigScreen.computedDatas
+    }),
+    // 获取树节点数据
+    treeData () {
+      const list = []
+      for (const item in this.dataset) {
+        const fields = Object.keys(this.dataset[item][0])
+        const children = fields.map((field) => {
+          return {
+            label: field,
+            code: item,
+            value: `dataset.${item}[0].${field}`
+          }
+        })
+        list.push({
+          label: item,
+          code: item,
+          value: `dataset.${item}`,
+          children
+        })
+      }
+      for (const item in this.computedDatas) {
+        list.push({
+          label: item,
+          code: item,
+          value: `computedDatas.${item}`
+        })
+      }
+      return list
+    },
+    currentConfig () {
+      return cloneDeep(this.config)
+    }
+  },
+  watch: {},
+  created () {},
+  mounted () {},
+  methods: {
+    ...mapMutations({
+      changeChartConfig: 'bigScreen/changeChartConfig'
+    }),
+    init () {
+      this.formVisible = true
+    },
+    // 运行脚本
+    executeScript () {
+      // eslint-disable-next-line no-new-func
+      const result = new Function('dataset', 'computedDatas', this.currentConfig.expression)
+      this.scriptContent = result(this.dataset, this.computedDatas)
+    },
+    // 点击树节点将数据添加到脚本编辑器中
+    handleNodeClick (node, data, nodeObj) {
+      console.log(node, data, nodeObj)
+      const str = node.value
+      const code = node.code
+      this.$refs.codemirrorRef.codemirror.setValue(this.currentConfig.expression + ' +  ' + str)
+      // 同时将点击的数据存在expressionCodes中
+      if (this.currentConfig.expressionCodes && Array.isArray(this.currentConfig.expressionCodes)) {
+        this.currentConfig.expressionCodes.push(code)
+      }
+    },
+    cancel () {
+      this.formVisible = false
+    },
+    sure () {
+      this.formVisible = false
+      this.changeChartConfig(this.currentConfig)
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+@import '../../assets/style/bsTheme.scss';
+.bs-dialog-wrap{
+  ::v-deep.el-dialog__body{
+    min-height: 500px;
+  }
+}
+  .main-box{
+    width: 100%;
+    height: 500px;
+    display: flex;
+    .left-box{
+      flex: 1;
+      height: 100%;
+      .tree-box{
+        height: 100%;
+        overflow-y: auto;
+      }
+    }
+    .right-box{;
+      flex: 3 ;
+      height: 100%;
+      .codemirror-wrap{
+        height: 50%;
+        position: relative;
+        .btn-box{
+          position: absolute;
+          right: 10px;
+          bottom: 10px;
+        }
+      }
+      .codemirror-box {
+        height: 100% !important;
+        ::v-deep .CodeMirror {
+          height: 100% !important;
+          font-family: Helvetica, Tahoma;
+        }
+      }
+      .script-content-box{
+        padding:10px
+      }
+    }
+  }
+
+</style>

+ 6 - 1
data-room-ui/packages/BigScreenDesign/index.vue

@@ -284,7 +284,9 @@ export default {
       'saveTimeLine',
       'changeIframeDialog',
       'changePageInfo',
-      'changeActiveItemConfig'
+      'changeActiveItemConfig',
+      'emptyDataset',
+      'emptyComputedDatas'
     ]),
     // 判断页面权限
     permission () {
@@ -426,6 +428,9 @@ export default {
       })
         .then(() => {
           this.changeLayout([])
+          // 清空缓存的数据库的内容
+          this.emptyDataset()
+          this.emptyComputedDatas()
           this.resetPresetLine()
           this.saveTimeLine('清空画布')
         })

+ 74 - 1
data-room-ui/packages/js/mixins/commonMixins.js

@@ -19,12 +19,70 @@ export default {
       dataLoading: false
     }
   },
+  watch: {
+    'config.expression': { // 表达式发生变化
+      handler (val) {
+        this.getDataByExpression(this.config)
+      }
+    },
+    currentDataset: { // 关联的数据发生变化
+      handler (val) {
+        if (val && Object.keys(val).length) {
+          this.getDataByExpression(this.config)
+        }
+      },
+      deep: true,
+      immediate: true
+    },
+    currentComputedDatas: { // 关联的数据发生变化
+      handler (val) {
+        if (val && Object.keys(val).length) {
+          this.getDataByExpression(this.config)
+        }
+      },
+      deep: true,
+      immediate: true
+    }
+  },
   computed: {
     ...mapState({
       pageCode: state => state.bigScreen.pageInfo.code,
       customTheme: state => state.bigScreen.pageInfo.pageConfig.customTheme,
       activeCode: state => state.bigScreen.activeCode
+      // dataset: state => state.bigScreen.dataset
     }),
+    // 所有组件的数据集合
+    dataset () {
+      return this.$store.state.bigScreen.dataset
+    },
+    // 所有组件的数据集合
+    computedDatas () {
+      return this.$store.state.bigScreen.computedDatas
+    },
+    // 跟当前组件计算表达式关联的组件的数据集合
+    currentDataset () {
+      // ['RiTkJGDa','PEKwsHbf']this.config.expressionCodes
+      const newDataset = this.config.expressionCodes?.map(code => {
+        return this.dataset[code]
+      })
+      if (newDataset?.some(item => !item)) {
+        return null
+      } else {
+        return newDataset
+      }
+    },
+    // 跟当前组件计算表达式关联的组件的数据集合
+    currentComputedDatas () {
+      // ['RiTkJGDa','PEKwsHbf']this.config.expressionCodes
+      const newDataset = this.config.expressionCodes?.map(code => {
+        return this.computedDatas[code]
+      })
+      if (newDataset?.some(item => !item)) {
+        return null
+      } else {
+        return newDataset
+      }
+    },
     // 组件数据加载时的背景颜色
     loadingBackground () {
       return this.customTheme === 'light' ? '#ffffff' : '#151A26'
@@ -33,6 +91,7 @@ export default {
       return (this.$route.path === window?.BS_CONFIG?.routers?.previewUrl) || (this.$route.path === '/big-screen/preview')
     }
   },
+
   mounted () {
     if (!['tables', 'flyMap', 'map'].includes(this.config.type)) {
       this.chartInit()
@@ -42,7 +101,9 @@ export default {
   methods: {
     ...mapMutations({
       changeChartConfig: 'bigScreen/changeChartConfig',
-      changeActiveItemConfig: 'bigScreen/changeActiveItemConfig'
+      changeActiveItemConfig: 'bigScreen/changeActiveItemConfig',
+      updateDataset: 'bigScreen/updateDataset',
+      updateComputedDatas: 'bigScreen/updateComputedDatas'
     }),
     /**
      * 初始化组件
@@ -110,6 +171,10 @@ export default {
               }
             }
           }
+          // 将后端返回的数据保存
+          if (_res.success) {
+            this.updateDataset({ code: config.code, title: config.title, data: _res?.data })
+          }
           config = this.dataFormatting(config, _res)
           this.changeChartConfig(config)
         }).catch((err) => {
@@ -173,6 +238,10 @@ export default {
               }
             }
           }
+          // 将后端返回的数据保存
+          if (_res.success) {
+            this.updateDataset({ code: config.code, title: config.title, data: _res?.data })
+          }
           config = this.dataFormatting(config, _res)
           if (this.chart) {
             // 单指标组件和多指标组件的changeData传参不同
@@ -209,6 +278,10 @@ export default {
     newChart (option) {
       // 覆盖
     },
+    // 通过表达式计算获取组件的值
+    getDataByExpression (config) {
+      // 覆盖
+    },
     changeStyle (config) {
       config = { ...this.config, ...config }
       // 样式改变时更新主题配置

+ 31 - 2
data-room-ui/packages/js/store/mutations.js

@@ -163,16 +163,18 @@ export default {
         EventBus.$emit('deleteComponent', codes)
       }
       state.pageInfo.chartList = state.pageInfo.chartList.filter(chart => codes !== chart.code)
+      // 删除组件时,将该组件的缓存数据库中的数据也删除
+      deldataset(state, 'dataset', codes)
+      deldataset(state, 'computedDatas', codes)
     }
     // 存储删除后的状态
     saveTimeLineFunc(state, '删除组件')
-    if (state.pageInfo.chartList.findIndex(item=>item.code===state.activeCode)==-1) {
+    if (state.pageInfo.chartList.findIndex(item => item.code === state.activeCode) == -1) {
       state.activeItemConfig = null
       state.activeCode = null
       EventBus.$emit('closeRightPanel')
     }
     // 发送事件,关闭配置面板
-
   },
   changePageConfig (state, pageConfig) {
     Vue.set(state.pageInfo, 'pageConfig', cloneDeep(pageConfig))
@@ -367,6 +369,33 @@ export default {
     })
     // 将复制的组件添加到chartList中
     state.pageInfo.chartList = [...copyCharts, ...state.pageInfo.chartList]
+  },
+  // 更新数据集库中的内容
+  updateDataset (state, res) {
+    Vue.set(state.dataset, res.title + res.code, res.data)
+  },
+  // 更新数据集库中的内容
+  updateComputedDatas (state, res) {
+    Vue.set(state.computedDatas, res.title + res.code, res.data)
+  },
+  // 清空数据集库
+  emptyDataset (state) {
+    state.dataset = {}
+  },
+  // 清空数据集库
+  emptyComputedDatas (state) {
+    state.computedDatas = {}
+  }
+}
+function deldataset (state, type, codes) {
+  const datasets = state[type]
+  for (const code of codes) {
+    for (const key in datasets) {
+      if (key.endsWith(code)) {
+        delete state[type][key]
+        break // 找到匹配的属性后,退出内层循环
+      }
+    }
   }
 }
 function changeZIndexFuc (state, list) {

+ 5 - 1
data-room-ui/packages/js/store/state.js

@@ -64,7 +64,11 @@ export const defaultData = {
   zoom: 100,
   // 自适应下的缩放比例
   fitZoom: 100,
-  iframeDialog: false
+  iframeDialog: false,
+  // 页面上所有组件的数据集的数据信息
+  dataset: {},
+  // 页面上所有组件的数据集的数据信息
+  computedDatas: {}
 }
 
 export default () => ({