浏览代码

!208 --Button按钮组件控制多条件联动图表实现
Merge pull request !208 from JiangHH/dev

Foming 3 月之前
父节点
当前提交
fe856d9b6e

二进制
doc/docs/guide/community/JiangHH/picture/img_12.png


二进制
doc/docs/guide/community/JiangHH/picture/img_13.png


二进制
doc/docs/guide/community/JiangHH/picture/img_14.png


二进制
doc/docs/guide/community/JiangHH/picture/img_15.png


二进制
doc/docs/guide/community/JiangHH/picture/img_16.png


二进制
doc/docs/guide/community/JiangHH/picture/img_17.png


二进制
doc/docs/guide/community/JiangHH/picture/img_18.png


二进制
doc/docs/guide/community/JiangHH/picture/img_19.png


二进制
doc/docs/guide/community/JiangHH/picture/img_20.png


二进制
doc/docs/guide/community/JiangHH/picture/img_21.png


二进制
doc/docs/guide/community/JiangHH/picture/img_22.png


+ 116 - 0
doc/docs/guide/community/JiangHH/按钮组件控制多条件组件联动图表实现.md

@@ -0,0 +1,116 @@
+
+## 1.需求:
+    多个下拉框联动一个图表,图表只查询最后一次选择的下拉框内容
+    https://gitee.com/anji-plus/report/issues/IC2TFP
+
+## 2.分析:
+    
+    由于目前设计的组件组件联动,都是在条件组件完成选择或者输入之后就直接触发联动了,
+    针对多个条件组件共同作用同一个图表的情况下,用户可能想要在最后一个组件完成输入的情况下才会触发联动
+
+    所以,设计一个按钮组件,在条件组件都输入完成的情况下,有按钮组件触发联动。
+    
+    图标组件所需要的参数均有按钮组件提供,而按钮组件的参数有条件组件传递,形成一个提交表单。
+
+    按钮组件的数据集跟图表的数据集选择保持一致,即参数保持一直.
+
+    ** 原先联动逻辑无需改变 **
+    1.按钮组件 联动  图表组件;
+    2.条件组件 联动  按钮组件;
+
+## 3.设计示例:
+#### 1.测试数据库表  test_user_expenses
+
+```sql
+create table test_user_expenses
+(
+    id       bigint auto_increment comment '主键'
+        primary key,
+    user     varchar(32) not null comment '用户',
+    year     int         null comment '年份',
+    rq       date        null comment '日期',
+    category varchar(50) null comment '分类',
+    expenses decimal     null comment '支出'
+)
+    comment '测试表' collate = utf8mb4_unicode_ci;
+```
+#### 2.测试数据
+```sql
+INSERT INTO test_user_expenses (id, category, expenses, year, user, rq)VALUES (1, '住房物业', 23460, 2023, 'jhh', '2023-04-01');
+INSERT INTO test_user_expenses (id, category, expenses, year, user, rq)VALUES (2, '日用百货', 6496, 2023, 'jhh', '2023-04-15');
+INSERT INTO test_user_expenses (id, category, expenses, year, user, rq)VALUES (3, '交通出行', 5592, 2023, 'jhh', '2023-05-01');
+INSERT INTO test_user_expenses (id, category, expenses, year, user, rq)VALUES (4, '餐饮美食', 3218, 2023, 'jhh', '2023-06-01');
+INSERT INTO test_user_expenses (id, category, expenses, year, user, rq)VALUES (5, '充值缴费', 1953, 2023, 'jhh', '2023-08-08');
+INSERT INTO test_user_expenses (id, category, expenses, year, user, rq)VALUES (7, '其他', 1888, 2023, 'jhh', '2023-10-01');
+INSERT INTO test_user_expenses (id, category, expenses, year, user, rq)VALUES (8, '住房物业', 23550, 2022, 'jhh', '2022-04-01');
+INSERT INTO test_user_expenses (id, category, expenses, year, user, rq)VALUES (9, '日用百货', 2846, 2022, 'jhh', '2022-04-15');
+INSERT INTO test_user_expenses (id, category, expenses, year, user, rq)VALUES (10, '交通出行', 2108, 2022, 'jhh', '2022-05-01');
+INSERT INTO test_user_expenses (id, category, expenses, year, user, rq)VALUES (11, '餐饮美食', 2634, 2022, 'jhh', '2022-06-01');
+INSERT INTO test_user_expenses (id, category, expenses, year, user, rq)VALUES (12, '充值缴费', 5280, 2022, 'jhh', '2022-08-08');
+INSERT INTO test_user_expenses (id, category, expenses, year, user, rq)VALUES (13, '其他', 11553, 2022, 'jhh', '2022-10-01');
+INSERT INTO test_user_expenses (id, category, expenses, year, user, rq)VALUES (14, '住房物业', 40000, 2024, 'jhh', '2024-04-01');
+INSERT INTO test_user_expenses (id, category, expenses, year, user, rq)VALUES (15, '日用百货', 5000, 2024, 'jhh', '2024-04-15');
+INSERT INTO test_user_expenses (id, category, expenses, year, user, rq)VALUES (16, '交通出行', 3000, 2024, 'jhh', '2024-05-01');
+INSERT INTO test_user_expenses (id, category, expenses, year, user, rq)VALUES (17, '餐饮美食', 3000, 2024, 'jhh', '2024-06-01');
+INSERT INTO test_user_expenses (id, category, expenses, year, user, rq)VALUES (18, '充值缴费', 5000, 2024, 'jhh', '2024-08-08');
+INSERT INTO test_user_expenses (id, category, expenses, year, user, rq)VALUES (19, '其他', 10000, 2024, 'jhh', '2024-10-01');
+```
+
+#### 3.测试用的存储过程
+```sql
+create
+    definer = root@localhost procedure get_data(IN p_year varchar(255), IN p_category varchar(255))
+BEGIN
+    SET @sql = 'SELECT id, user, year,rq, category,expenses FROM test_user_expenses WHERE 1 = 1';
+
+    IF p_year IS NOT NULL AND p_year != '' AND p_year != '全部' THEN
+        SET @sql = CONCAT(@sql, ' AND year = "', p_year, '"');
+    END IF;
+
+    IF p_category IS NOT NULL AND p_category != '' AND p_category != '全部' THEN
+        SET @sql = CONCAT(@sql, ' AND category = "', p_category, '"');
+    END IF;
+
+    PREPARE stmt FROM @sql;
+    EXECUTE stmt;
+    DEALLOCATE PREPARE stmt;
+END;
+```
+
+#### 4.数据集
+数据集设计
+![](./picture/img_12.png)
+
+### 5.大屏设计
+
+![](./picture/img_13.png)
+
+按钮组件
+
+![](./picture/img_14.png)
+
+按钮配置
+![](./picture/img_15.png)
+
+按钮联动图标
+![](./picture/img_16.png)
+按钮联动图标参数信息
+![](./picture/img_17.png)
+
+条件组件联动按钮
+![](./picture/img_18.png)
+![](./picture/img_20.png)
+条件组件联动按钮参数信息
+![](./picture/img_19.png)
+![](./picture/img_21.png)
+
+### 6.测试
+![](./picture/img_22.png)
+
+### 7. 注意事项
+    
+    (1) http方式的数据集参数拼接的方式,不允许传递的参数为空,后端请求url解析后可能会带{}会报错,所以要求所有的条件输入都必须有值,不清楚最新不能把支不支持。
+    (2) 建议增加默认联动开关,看需求自行二次修改。
+    (3) 这里做了表单校验,如果存在未输入的,点击按钮会提示,提示语信息,有开发能力的自行增加配置信息。
+
+

+ 17 - 0
report-ui/src/views/bigscreenDesigner/designer/components/componentLinkage.vue

@@ -47,6 +47,7 @@
             size="mini"
             clearable
             placeholder="请选择"
+            @change="handleChange"
           >
             <el-option
               v-for="(item, index) in layerWidget"
@@ -185,6 +186,22 @@ export default {
         })
       }
     },
+    handleChange(val){
+      //判断当前源联动组件是否是按钮
+      if('widget-button'=== this.layerWidget[this.widgetIndex].code){
+        //根据目标组件的参数个数来确定按钮组件的参数个数
+        this.linkageForm.paramsConfig = []; //每次切换都置空
+        let paramKeys = Object.keys(this.widgetParamsConfig[this.targetIndex].dynamicData.contextData);
+        paramKeys.forEach(param=>{
+          this.linkageForm.paramsConfig.push({
+            originKey: param,
+            targetKey: ''
+          });
+        });
+        //把参数传给button组件的表单
+        //this.layerWidget[this.widgetIndex].setFormData(paramKeys);
+      }
+    },
     // 弹出框关闭
     handleClose() {
       this.dialogVisible = false

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

@@ -53,6 +53,11 @@ export const lickageParamsConfig = [
     code: 'WidgetPieNightingaleRoseArea',
     paramsKey: ['name', 'value']
   },
+  {
+    name: '按钮',
+    code: 'widget-button',
+    paramsKey: [] //按钮需要的key来源于要联动的组件所需要的参数
+  },
 ]
 
 export const getOneConfigByCode = function (code) {
@@ -135,6 +140,10 @@ export const targetWidgetLinkageLogic = function (self) {
     if (item.index !== -1 && item.linkageArr.length) {
       item.linkageArr.some(obj => {
         if (obj.targetId === self.value.setup.widgetId) {
+          //如果当前对象是button按钮,需要把传递参数设置给他,方便点击的时候校验表单输入情况
+          if (self.value.setup.widgetCode === 'widget-button') {
+            self.setFormData(obj.paramsConfig); //把要校验的参数传给button表单
+          }
           self.hasLinkage = true
           busEvents.push({
             eventName: `bus_${obj.originId}_${obj.targetId}`,

+ 228 - 0
report-ui/src/views/bigscreenDesigner/designer/tools/configure/texts/widget-button.js

@@ -0,0 +1,228 @@
+/*
+ * @Descripttion: 超链接文本
+ * @version:
+ * @Author: qianlishi
+ * @Date: 2021-08-29 07:03:58
+ * @LastEditors: qianlishi qianlishi@anji-plus.com
+ * @LastEditTime: 2023-01-11 13:16:21
+ */
+export const widgetButton = {
+  code: 'widget-button',
+  type: 'text',
+  tabName: '文本栏',
+  label: '按钮',
+  icon: 'iconanniu',
+  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: 'vue-color',
+        label: '背景颜色',
+        name: 'backgroundColor',
+        required: false,
+        placeholder: '',
+        value: '#0000FF',
+      },
+      {
+        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-select',
+        label: '是否圆角边框',
+        name: 'isBorderRadius',
+        required: false,
+        placeholder: '',
+        selectOptions: [
+          { code: 'true',  name: '是' },
+          { code: 'false', name: '否' },
+        ],
+        value: 'false',
+      },
+      {
+        type: 'el-input-number',
+        label: '圆角边框设置',
+        name: 'borderRadius',
+        required: false,
+        placeholder: '单位px',
+        value: '10',
+      },
+     [
+       {
+         name: '组件联动',
+         list: [
+           {
+             type: 'componentLinkage',
+             label: '',
+             name: 'componentLinkage',
+             required: false,
+             value: []
+           }
+         ]
+       }
+     ]
+    ],
+    // 数据
+    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: [
+          { paramKey: 'key1', paramValue: 'value1'},
+          { paramKey: 'key2', paramValue: 'value2'},
+          { paramKey: 'key3', paramValue: 'value3'}
+        ],
+      },
+      {
+        type: 'dycustComponents',
+        label: '',
+        name: 'dynamicData',
+        required: false,
+        placeholder: '',
+        relactiveDom: 'dataType',
+        relactiveDomValue: 'dynamicData',
+        chartType: 'widget-button',
+        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,
+      },
+    ],
+  }
+}

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

@@ -12,6 +12,7 @@ import {widgetMarquee} from "./configure/texts/widget-marquee"
 import {widgetHref} from "./configure/texts/widget-href"
 import {widgetTime} from "./configure/texts/widget-time"
 import {widgetImage} from "./configure/texts/widget-image"
+import {widgetButton} from "./configure/texts/widget-button"
 import {widgetSliders} from "./configure/texts/widget-slider"
 import {widgetVideo} from "./configure/texts/widget-video"
 import {widgetTable} from "./configure/texts/widget-table"
@@ -64,6 +65,7 @@ export const widgetTool = [
   widgetHref,
   widgetTime,
   widgetImage,
+  widgetButton,
   //  widgetSliders,
   widgetVideo,
   widgetTable,

+ 2 - 0
report-ui/src/views/bigscreenDesigner/designer/widget/temp.vue

@@ -12,6 +12,7 @@
 <script>
 import widgetHref from "./texts/widgetHref.vue";
 import widgetText from "./texts/widgetText.vue";
+import widgetButton from "./texts/widgetButton.vue";
 import WidgetMarquee from "./texts/widgetMarquee.vue";
 import widgetTime from "./texts/widgetTime.vue";
 import widgetImage from "./texts/widgetImage.vue";
@@ -61,6 +62,7 @@ export default {
   components: {
     widgetHref,
     widgetText,
+    widgetButton,
     widgetBorder,
     widgetDecorateFlowLine,
     widgetDecoration,

+ 163 - 0
report-ui/src/views/bigscreenDesigner/designer/widget/texts/widgetButton.vue

@@ -0,0 +1,163 @@
+<!--
+ * @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="button">
+      <el-button
+        :style="{
+                      position:styleColor.position,
+                      width: styleColor.width,
+                      height: styleColor.height,
+                      left: styleColor.left,
+                      right: styleColor.right,
+                      top: styleColor.top,
+                      color: styleColor.color,
+                      borderRadius: styleColor.borderRadius,
+                      'line-height': styleColor['line-height'],
+                      'text-align': styleColor['text-align'],
+                      'font-weight': styleColor['font-weight'],
+                      'font-size': styleColor['font-size'],
+                      'letter-spacing': styleColor['letter-spacing'],
+                      'background-color': styleColor['background-color'],
+                      display: styleColor.display
+               }"
+        @click="click"
+      >
+        {{styleColor.text}}
+      </el-button>
+  </div>
+
+</template>
+
+<script>
+import {
+  originWidgetLinkageLogic,
+  targetWidgetLinkageLogic,
+} from "@/views/bigscreenDesigner/designer/linkageLogic";
+export default {
+  name: "WidgetButton",
+  components: {},
+  props: {
+    value: Object,
+    ispreview: Boolean,
+    widgetIndex: {
+      type: Number,
+      default: 0,
+    }, // 当前组件,在工作区变量widgetInWorkbench中的索引
+  },
+  data() {
+    return {
+      options: {},
+      formData: {} , //要提交的参数表单
+    };
+  },
+  computed: {
+    transStyle() {
+      return this.objToOne(this.options);
+    },
+    styleColor() {
+      return {
+        position: this.ispreview ? "relative" : "static",
+        color: this.transStyle.color || "#00FF00",
+        text: this.transStyle.text || "按钮",
+        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",
+        borderRadius: this.transStyle.isBorderRadius === "true"? this.transStyle.borderRadius +"px" :"0px",
+        "text-align": this.transStyle.textAlign,
+        "font-weight": this.transStyle.fontWeight || "600",
+        "font-size": this.transStyle.fontSize + "px" || "12px",
+        "letter-spacing": this.transStyle.letterSpacing + "em",
+        "background-color": this.transStyle.backgroundColor || "#0000ff",
+        display:
+          this.transStyle.hideLayer === undefined
+            ? "block"
+            : this.transStyle.hideLayer ? "none" : "block",
+      };
+    },
+    allComponentLinkage() {
+      return this.$store.state.designer.allComponentLinkage;
+    },
+  },
+  watch: {
+    value: {
+      handler(val) {
+        this.options = val;
+      },
+      deep: true,
+    },
+  },
+  mounted() {
+    this.options = this.value;
+    this.optionsSetup = this.value.setup;
+    this.optionsData = this.value.data;
+    this.optionsStyle = this.value.position;
+    targetWidgetLinkageLogic(this); // 联动-目标组件逻辑
+  },
+  methods: {
+    //设置表单Key
+    setFormData(paramConfig){
+      console.log("paramConfig:" + paramConfig);
+      paramConfig.forEach(item =>{
+        this.formData[item.targetKey] = "";
+      });
+    },
+    click() {
+      console.log(this.formData);
+      let formDataKey = Object.keys(this.formData);
+      let needInputAll = false;
+      for(let index in formDataKey){
+        if(this.formData[formDataKey[index]] === null || this.formData[formDataKey[index]] === "" ){
+          needInputAll = true;
+          break;
+        }
+      }
+      if(needInputAll){
+        this.$message.error("请填写所有必填字段");
+        return;
+      }
+      originWidgetLinkageLogic(this, true, {
+        currentData: this.formData,
+      }); // 联动-源组件逻辑
+    },
+
+    setOptionsData(e, paramsConfig) {
+      let _this = this;
+      console.log("ces", e);
+      console.log("ces", paramsConfig);
+      const optionsData = this.optionsData; // 数据类型 静态 or 动态
+      // 联动接收者逻辑开始
+      optionsData.dynamicData = optionsData.dynamicData || {}; // 兼容 dynamicData undefined
+      const myDynamicData = optionsData.dynamicData;
+      clearInterval(this.flagInter); // 不管咋,先干掉上一次的定时任务,避免多跑
+      if (
+        e &&
+        optionsData.dataType !== "staticData" &&
+        Object.keys(myDynamicData.contextData).length
+      ) {
+        const keyArr = Object.keys(myDynamicData.contextData);
+        paramsConfig.forEach((conf) => {
+          if (keyArr.includes(conf.targetKey)) {
+            myDynamicData.contextData[conf.targetKey] = e[conf.originKey];
+            _this.formData[conf.targetKey] = e[conf.originKey];    //把参数设置到FormData
+          }
+        });
+      }
+      //这里,button按钮是要通过【按钮】联动触发其他组件,本身没有要展示的动态数据,所以无需执行 dynamicDataFn
+      // // 联动接收者逻辑结束
+      // optionsData.dataType == "staticData"
+      //   ? this.staticDataFn(optionsData.staticData)
+      //   : this.dynamicDataFn(optionsData.dynamicData, optionsData.refreshTime);
+    }
+  },
+};
+</script>
+
+<style scoped lang="scss">
+
+</style>

+ 2 - 0
report-ui/src/views/bigscreenDesigner/designer/widget/widget.vue

@@ -19,6 +19,7 @@
 <script>
 import widgetHref from "./texts/widgetHref.vue";
 import widgetText from "./texts/widgetText.vue";
+import widgetButton from './texts/widgetButton.vue';
 import WidgetMarquee from "./texts/widgetMarquee.vue";
 import widgetTime from "./texts/widgetTime.vue";
 import widgetImage from "./texts/widgetImage.vue";
@@ -68,6 +69,7 @@ export default {
   components: {
     widgetHref,
     widgetText,
+    widgetButton,
     widgetBorder,
     widgetDecorateFlowLine,
     widgetDecoration,