• 根据组件类型与条件动态添加下拉框及选项


    前言

    本次在做低代码平台时,遇见了一个稍微有些复杂的业务场景,需求描述:

    1. 根据用户拖拽表单生成的JSON数据(本次采用了FormGenerator去生成JSON数据),生成对应的所有组件的下拉列表,并在流程图的边(节点连接线)上添加条件判断。
    2. 当用户点击流程图的边时,生成该边上对应的所有组件的下拉列表,在点击某个组件的选项后,生成对应组件的下拉列表组件,在用户取消选择后,对应组件的下拉列表组件随之消失。
    3. 针对不同组件对应的下拉列表,要求有不同的条件判断,例如:数字类型的需要有范围选择、大小判断、为空不为空,等条件;文本组件有包含不包含,等条件。
    4. 当用户选择后,自动保存到当前边对应的JSON数据,若用户的选择无效,则不保存。
    5. 要求能够回显用户选择的数据,即点击不同边时,回显该边上JSON对应的所有下拉列表组件。

    经过梳理后,其实逻辑看起来也不是特别复杂,不过当时确实是有点被难倒了,主要原因就是一些细节性的问题没有考虑到,然后流程图也需要去学习如何操作,时间上有些紧,同时也在思考如何写能够更便于维护。其次就是没有一个清晰的该处业务的流程思考,不过后来捋了捋也就差不多啦,所以说三思而后行啊。

    三思而后行:多思考几遍,自己又觉得自己行了哈哈

    Logicflow和FromGenerator就不多说了,虽然也有点坑,但都是轮子,主要记录下这块业务场景如何实现的。

    组件展示

    1. 包含所有组件的下拉列表
      在这里插入图片描述
    2. 对应组件的下拉列表组件
      在这里插入图片描述
    3. 根据条件改变输入形式
      在这里插入图片描述

    在这里插入图片描述

    具体实现

    JSON数据处理

    处理JSON,不得不提一嘴ES6结构赋值,属实是属性提取的利器。
    原JSON大概长这个样子,为了区别不同类型的组件,和表单那边的小伙伴约定了两个字段,tag用于区分组件输入框的类型,typeNumber用于区分下拉列表内容有哪些

    {
      "fields": [
        {
          "__config__": {
            "label": "单行文本",
            "labelWidth": null,
            "showLabel": true,
            "changeTag": true,
            "tag": "el-input",
            "tagIcon": "input",
            "required": true,
            "layout": "colFormItem",
            "span": 24,
            "document": "https://element.eleme.cn/#/zh-CN/component/input",
            "regList": [],
            "formId": 101,
            "renderKey": 1649927471934,
            "typeNumber": 0
          },
          "__slot__": {
            "prepend": "",
            "append": ""
          },
          "placeholder": "请输入单行文本",
          "style": {
            "width": "100%"
          },
          "clearable": true,
          "prefix-icon": "",
          "suffix-icon": "",
          "maxlength": null,
          "show-word-limit": false,
          "readonly": false,
          "disabled": false,
          "__vModel__": "field101"
        },
        {
          "__config__": {
            "label": "多行文本",
            "labelWidth": null,
            "showLabel": true,
            "tag": "el-input",
            "tagIcon": "textarea",
            "required": true,
            "layout": "colFormItem",
            "span": 24,
            "regList": [],
            "changeTag": true,
            "document": "https://element.eleme.cn/#/zh-CN/component/input",
            "formId": 102,
            "typeNumber": 1,
            "renderKey": 1649927472786
          },
          "type": "textarea",
          "placeholder": "请输入多行文本",
          "autosize": {
            "minRows": 4,
            "maxRows": 4
          },
          "style": {
            "width": "100%"
          },
          "maxlength": null,
          "show-word-limit": false,
          "readonly": false,
          "disabled": false,
          "__vModel__": "field102"
        },
        {
          "__config__": {
            "label": "级联选择",
            "showLabel": true,
            "labelWidth": null,
            "tag": "el-cascader",
            "tagIcon": "cascader",
            "layout": "colFormItem",
            "defaultValue": [
              1,
              2
            ],
            "dataType": "dynamic",
            "span": 24,
            "required": true,
            "regList": [],
            "changeTag": true,
            "document": "https://element.eleme.cn/#/zh-CN/component/cascader",
            "formId": 111,
            "typeNumber": 9,
            "renderKey": 1649942215947
          },
          "options": [
            {
              "id": 1,
              "value": 1,
              "label": "选项1",
              "children": [
                {
                  "id": 2,
                  "value": 2,
                  "label": "选项1-1"
                }
              ]
            }
          ],
          "placeholder": "请选择级联选择",
          "style": {
            "width": "100%"
          },
          "props": {
            "props": {
              "multiple": false,
              "label": "label",
              "value": "value",
              "children": "children"
            }
          },
          "show-all-levels": true,
          "disabled": false,
          "clearable": true,
          "filterable": false,
          "separator": "/",
          "__vModel__": "field111"
        },
        {
          "__config__": {
            "label": "计数器",
            "showLabel": true,
            "changeTag": true,
            "labelWidth": null,
            "tag": "el-input-number",
            "tagIcon": "number",
            "span": 24,
            "layout": "colFormItem",
            "required": true,
            "regList": [],
            "document": "https://element.eleme.cn/#/zh-CN/component/input-number",
            "formId": 105,
            "typeNumber": 2,
            "renderKey": 1649927477883
          },
          "placeholder": "计数器",
          "step": 1,
          "step-strictly": false,
          "controls-position": "",
          "disabled": false,
          "__vModel__": "field105"
        },
        {
          "__config__": {
            "label": "下拉选择",
            "showLabel": true,
            "labelWidth": null,
            "tag": "el-select",
            "tagIcon": "select",
            "layout": "colFormItem",
            "span": 24,
            "required": true,
            "regList": [],
            "changeTag": true,
            "document": "https://element.eleme.cn/#/zh-CN/component/select",
            "formId": 106,
            "typeNumber": 6,
            "renderKey": 1649927484505
          },
          "__slot__": {
            "options": [
              {
                "label": "选项一",
                "value": 1
              },
              {
                "label": "选项二",
                "value": 2
              }
            ]
          },
          "placeholder": "请选择下拉选择",
          "style": {
            "width": "100%"
          },
          "clearable": true,
          "disabled": false,
          "filterable": false,
          "multiple": false,
          "__vModel__": "field106"
        },
        {
          "__config__": {
            "label": "单选框组",
            "labelWidth": null,
            "showLabel": true,
            "tag": "el-radio-group",
            "tagIcon": "radio",
            "changeTag": true,
            "layout": "colFormItem",
            "span": 24,
            "optionType": "default",
            "regList": [],
            "required": true,
            "border": false,
            "document": "https://element.eleme.cn/#/zh-CN/component/radio",
            "formId": 107,
            "typeNumber": 4,
            "renderKey": 1649927491036
          },
          "__slot__": {
            "options": [
              {
                "label": "选项一",
                "value": 1
              },
              {
                "label": "选项二",
                "value": 2
              }
            ]
          },
          "style": {},
          "size": "medium",
          "disabled": false,
          "__vModel__": "field107"
        },
        {
          "__config__": {
            "label": "多选框组",
            "tag": "el-checkbox-group",
            "tagIcon": "checkbox",
            "defaultValue": [],
            "span": 24,
            "showLabel": true,
            "labelWidth": null,
            "layout": "colFormItem",
            "optionType": "default",
            "required": true,
            "regList": [],
            "changeTag": true,
            "border": false,
            "document": "https://element.eleme.cn/#/zh-CN/component/checkbox",
            "formId": 108,
            "typeNumber": 5,
            "renderKey": 1649927491380
          },
          "__slot__": {
            "options": [
              {
                "label": "选项一",
                "value": 1
              },
              {
                "label": "选项二",
                "value": 2
              }
            ]
          },
          "style": {},
          "size": "medium",
          "disabled": false,
          "__vModel__": "field108"
        },
        {
          "__config__": {
            "label": "日期选择",
            "tag": "el-date-picker",
            "tagIcon": "date",
            "defaultValue": null,
            "showLabel": true,
            "labelWidth": null,
            "span": 24,
            "layout": "colFormItem",
            "required": true,
            "regList": [],
            "changeTag": true,
            "document": "https://element.eleme.cn/#/zh-CN/component/date-picker",
            "formId": 110,
            "typeNumber": 3,
            "renderKey": 1649927506700
          },
          "placeholder": "请选择日期选择",
          "type": "date",
          "style": {
            "width": "100%"
          },
          "disabled": false,
          "clearable": true,
          "format": "yyyy-MM-dd",
          "value-format": "yyyy-MM-dd",
          "readonly": false,
          "__vModel__": "field110"
        }
      ],
      "formRef": "elForm",
      "formModel": "formData",
      "size": "medium",
      "labelPosition": "right",
      "labelWidth": 100,
      "formRules": "rules",
      "gutter": 15,
      "disabled": false,
      "span": 24,
      "formBtns": true,
      "unFocusedComponentBorder": false
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302

    解构工具类

    解构赋值 过滤数据:

    // utils/adpterForForm.js
    
    // 依照tag判断组件输入框的类型(FormGenerator采用的elementui,所以会有el-xxx的tag)
    const contentTypeMap = {
      'el-input': 'input',
      'el-textarea': 'input',
      'el-input-number': 'input',
      'el-date-picker': 'date',
      'el-radio-group': 'select',
      'el-checkbox-group': 'select',
      'el-select': 'select',
      'el-cascader': 'tree',
      'el-upload': 'none'
    };
    
    // { label: '等于', value: 0 },
    // { label: '不等于', value: 1 },
    // { label: '包含', value: 2 },
    // { label: '不包含', value: 3 },
    // { label: '为空', value: 4 },
    // { label: '不为空', value: 5 }
    // { label: '大于', value: 6 },
    // { label: '大于等于', value: 7 },
    // { label: '小于', value: 8 },
    // { label: '小于等于', value: 9 },
    // { label: '选择范围', value: 10 },
    // { label: '等于任意一个', value: 11 },
    // { label: '不等于任意一个', value: 12 },
    // { label: '包含任意一个', value: 13 },
    // { label: '同时包含', value: 14 },
    // { label: '属于', value: 15 },
    // { label: '不属于', value: 16 },
    // { label: '已验证', value: 17 },
    // { label: '未验证', value: 18 },
    // 依照type判断组件所对应的下拉列表
    const judgeListMap = {
      // 单行文本
      '0': [
        { label: '等于', value: 0 },
        { label: '不等于', value: 1 },
        { label: '包含', value: 2 },
        { label: '不包含', value: 3 },
        { label: '为空', value: 4 },
        { label: '不为空', value: 5 }
      ],
      // 多行文本
      '1': [
        { label: '包含', value: 2 },
        { label: '不包含', value: 3 },
        { label: '为空', value: 4 },
        { label: '不为空', value: 5 }
      ],
      // 数字
      '2': [
        { label: '等于', value: 0 },
        { label: '不等于', value: 1 },
        { label: '大于', value: 6 },
        { label: '大于等于', value: 7 },
        { label: '小于', value: 8 },
        { label: '小于等于', value: 9 },
        { label: '选择范围', value: 10 },
        { label: '为空', value: 4 },
        { label: '不为空', value: 5 }
      ],
      // 日期时间
      '3': [
        { label: '等于', value: 0 },
        { label: '不等于', value: 1 },
        { label: '大于等于', value: 7 },
        { label: '小于等于', value: 9 },
        { label: '选择范围', value: 10 },
        { label: '为空', value: 4 },
        { label: '不为空', value: 5 }
      ],
      // 单选按钮组
      '4': [
        { label: '等于', value: 0 },
        { label: '不等于', value: 1 },
        { label: '等于任意一个', value: 11 },
        { label: '不等于任意一个', value: 12 },
        { label: '包含', value: 2 },
        { label: '不包含', value: 3 },
        { label: '为空', value: 4 },
        { label: '不为空', value: 5 }
      ],
      // 复选框组
      '5': [
        { label: '等于', value: 0 },
        { label: '包含任意一个', value: 13 },
        { label: '同时包含', value: 14 },
        { label: '为空', value: 4 },
        { label: '不为空', value: 5 }
      ],
      // 单选列表
      '6': [
        { label: '等于', value: 0 },
        { label: '不等于', value: 1 },
        { label: '等于任意一个', value: 11 },
        { label: '不等于任意一个', value: 12 },
        { label: '包含', value: 2 },
        { label: '不包含', value: 3 },
        { label: '为空', value: 4 },
        { label: '不为空', value: 5 }
      ],
      // 复选列表
      '7': [
        { label: '等于', value: 0 },
        { label: '包含任意一个', value: 13 },
        { label: '同时包含', value: 14 },
        { label: '为空', value: 4 },
        { label: '不为空', value: 5 }
      ],
      // 地址
      '9': [
        { label: '属于', value: 15 },
        { label: '不属于', value: 16 },
        { label: '为空', value: 4 },
        { label: '不为空', value: 5 }
      ],
      // 图片
      '10': [
        { label: '为空', value: 4 },
        { label: '不为空', value: 5 }
      ],
      // 附件
      '11': [
        { label: '为空', value: 4 },
        { label: '不为空', value: 5 }
      ],
      // 手机
      '12': [
        { label: '包含', value: 2 },
        { label: '已验证', value: 17 },
        { label: '未验证', value: 18 },
        { label: '为空', value: 4 },
        { label: '不为空', value: 5 }
      ],
      // 成员单选
      '13': [
        { label: '等于', value: 0 },
        { label: '不等于', value: 1 },
        { label: '等于任意一个', value: 11 },
        { label: '不等于任意一个', value: 12 },
        { label: '为空', value: 4 },
        { label: '不为空', value: 5 }
      ],
      // 成员多选
      '14': [
        { label: '等于', value: 0 },
        { label: '为空', value: 4 },
        { label: '不为空', value: 5 },
        { label: '包含任意一个', value: 13 },
        { label: '同时包含', value: 14 }
      ],
      // 成员单选
      '15': [
        { label: '等于', value: 0 },
        { label: '不等于', value: 1 },
        { label: '等于任意一个', value: 11 },
        { label: '不等于任意一个', value: 12 },
        { label: '为空', value: 4 },
        { label: '不为空', value: 5 }
      ],
      // 部门多选
      '16': [
        { label: '等于', value: 0 },
        { label: '为空', value: 4 },
        { label: '不为空', value: 5 },
        { label: '包含任意一个', value: 13 },
        { label: '同时包含', value: 14 }
      ]
    };
    
    // 获取表单JSON数据中所有组件的必要信息,并存放在map中去,便于查询和管理
    export function getComponentMap (data) {
      // 声明一个map集合
      const map = new Map();
      // 遍历解构赋值每一项
      for (const item of data) {
        const {
          '__config__': {
            'label': label = '',
            'renderKey': renderKey = '',
            'tag': tag = '',
            'typeNumber': type = ''
          },
          '__slot__': {
            'options': options = []
          } = {}
        } = item;
        // 构造所需要的数据对象
        const obj = {
          label: label,
          renderKey: renderKey, // 唯一标识
          tag: tag, // 决定条件组件卡片的内容选择形式,因为文本输入框和数字、日期的展示形式不同
          type: type, // 决定条件卡片的条件选项有哪些
          options: options // 如果内容选项为下拉列表时,通过options展示
        };
        obj.judgeList = judgeListMap[obj.type]; // 依照type添加条件列表
        obj.componentFormat = contentTypeMap[obj.tag]; // 依照tag添加内容展示形式
        map.set(obj.renderKey, obj); // 依据组件的唯一标识去存放进map
      }
      return map;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204

    解构结果

    [{
    	"key": "1011655792483713",
    	"value": {
    		"label": "单行文本",
    		"renderKey": "1011655792483713",
    		"tag": "el-input",
    		"type": 0,
    		"options": [],
    		"judgeList": [{
    			"label": "等于",
    			"value": 0
    		}, {
    			"label": "不等于",
    			"value": 1
    		}, {
    			"label": "包含",
    			"value": 2
    		}, {
    			"label": "不包含",
    			"value": 3
    		}, {
    			"label": "为空",
    			"value": 4
    		}, {
    			"label": "不为空",
    			"value": 5
    		}],
    		"componentFormat": "input"
    	}
    }, {
    	"key": "1021655792484018",
    	"value": {
    		"label": "多行文本",
    		"renderKey": "1021655792484018",
    		"tag": "el-input",
    		"type": 1,
    		"options": [],
    		"judgeList": [{
    			"label": "包含",
    			"value": 2
    		}, {
    			"label": "不包含",
    			"value": 3
    		}, {
    			"label": "为空",
    			"value": 4
    		}, {
    			"label": "不为空",
    			"value": 5
    		}],
    		"componentFormat": "input"
    	}
    }, {
    	"key": "1031655792484335",
    	"value": {
    		"label": "数字",
    		"renderKey": "1031655792484335",
    		"tag": "el-input",
    		"type": 2,
    		"options": [],
    		"judgeList": [{
    			"label": "等于",
    			"value": 0
    		}, {
    			"label": "不等于",
    			"value": 1
    		}, {
    			"label": "大于",
    			"value": 6
    		}, {
    			"label": "大于等于",
    			"value": 7
    		}, {
    			"label": "小于",
    			"value": 8
    		}, {
    			"label": "小于等于",
    			"value": 9
    		}, {
    			"label": "选择范围",
    			"value": 10
    		}, {
    			"label": "为空",
    			"value": 4
    		}, {
    			"label": "不为空",
    			"value": 5
    		}],
    		"componentFormat": "input"
    	}
    }, {
    	"key": "1041655792484586",
    	"value": {
    		"label": "日期时间",
    		"renderKey": "1041655792484586",
    		"tag": "el-date-picker",
    		"type": 3,
    		"options": [],
    		"judgeList": [{
    			"label": "等于",
    			"value": 0
    		}, {
    			"label": "不等于",
    			"value": 1
    		}, {
    			"label": "大于等于",
    			"value": 7
    		}, {
    			"label": "小于等于",
    			"value": 9
    		}, {
    			"label": "选择范围",
    			"value": 10
    		}, {
    			"label": "为空",
    			"value": 4
    		}, {
    			"label": "不为空",
    			"value": 5
    		}],
    		"componentFormat": "date"
    	}
    }, {
    	"key": "1051655792484934",
    	"value": {
    		"label": "单选按钮组",
    		"renderKey": "1051655792484934",
    		"tag": "el-radio-group",
    		"type": 4,
    		"options": [{
    			"label": "选项一",
    			"value": 1
    		}, {
    			"label": "选项二",
    			"value": 2
    		}],
    		"judgeList": [{
    			"label": "等于",
    			"value": 0
    		}, {
    			"label": "不等于",
    			"value": 1
    		}, {
    			"label": "等于任意一个",
    			"value": 11
    		}, {
    			"label": "不等于任意一个",
    			"value": 12
    		}, {
    			"label": "包含",
    			"value": 2
    		}, {
    			"label": "不包含",
    			"value": 3
    		}, {
    			"label": "为空",
    			"value": 4
    		}, {
    			"label": "不为空",
    			"value": 5
    		}],
    		"componentFormat": "select"
    	}
    }, {
    	"key": "1061655792485183",
    	"value": {
    		"label": "复选框组",
    		"renderKey": "1061655792485183",
    		"tag": "el-checkbox-group",
    		"type": 5,
    		"options": [{
    			"label": "选项一",
    			"value": 1
    		}, {
    			"label": "选项二",
    			"value": 2
    		}],
    		"judgeList": [{
    			"label": "等于",
    			"value": 0
    		}, {
    			"label": "包含任意一个",
    			"value": 13
    		}, {
    			"label": "同时包含",
    			"value": 14
    		}, {
    			"label": "为空",
    			"value": 4
    		}, {
    			"label": "不为空",
    			"value": 5
    		}],
    		"componentFormat": "select"
    	}
    }, {
    	"key": "1071655792485471",
    	"value": {
    		"label": "下拉框",
    		"renderKey": "1071655792485471",
    		"tag": "el-select",
    		"type": 6,
    		"options": [{
    			"label": "选项一",
    			"value": 1
    		}, {
    			"label": "选项二",
    			"value": 2
    		}],
    		"judgeList": [{
    			"label": "等于",
    			"value": 0
    		}, {
    			"label": "不等于",
    			"value": 1
    		}, {
    			"label": "等于任意一个",
    			"value": 11
    		}, {
    			"label": "不等于任意一个",
    			"value": 12
    		}, {
    			"label": "包含",
    			"value": 2
    		}, {
    			"label": "不包含",
    			"value": 3
    		}, {
    			"label": "为空",
    			"value": 4
    		}, {
    			"label": "不为空",
    			"value": 5
    		}],
    		"componentFormat": "select"
    	}
    }, {
    	"key": "1081655792485723",
    	"value": {
    		"label": "下拉复选框",
    		"renderKey": "1081655792485723",
    		"tag": "el-select",
    		"type": 7,
    		"options": [{
    			"label": "选项一",
    			"value": 1
    		}, {
    			"label": "选项二",
    			"value": 2
    		}],
    		"judgeList": [{
    			"label": "等于",
    			"value": 0
    		}, {
    			"label": "包含任意一个",
    			"value": 13
    		}, {
    			"label": "同时包含",
    			"value": 14
    		}, {
    			"label": "为空",
    			"value": 4
    		}, {
    			"label": "不为空",
    			"value": 5
    		}],
    		"componentFormat": "select"
    	}
    }, {
    	"key": "1091655792486011",
    	"value": {
    		"label": "分割线",
    		"renderKey": "1091655792486011",
    		"tag": "el-divider",
    		"type": 8,
    		"options": [],
    		"judgeList": "__vue_devtool_undefined__",
    		"componentFormat": "__vue_devtool_undefined__"
    	}
    }, {
    	"key": "1101655792486384",
    	"value": {
    		"label": "地址",
    		"renderKey": "1101655792486384",
    		"tag": "el-cascader",
    		"type": 9,
    		"options": [],
    		"judgeList": [{
    			"label": "属于",
    			"value": 15
    		}, {
    			"label": "不属于",
    			"value": 16
    		}, {
    			"label": "为空",
    			"value": 4
    		}, {
    			"label": "不为空",
    			"value": 5
    		}],
    		"componentFormat": "tree"
    	}
    }, {
    	"key": "1111655792487022",
    	"value": {
    		"label": "附件",
    		"renderKey": "1111655792487022",
    		"tag": "el-upload",
    		"type": 11,
    		"options": [],
    		"judgeList": [{
    			"label": "为空",
    			"value": 4
    		}, {
    			"label": "不为空",
    			"value": 5
    		}],
    		"componentFormat": "none"
    	}
    }, {
    	"key": "1121655792487317",
    	"value": {
    		"label": "图片",
    		"renderKey": "1121655792487317",
    		"tag": "el-upload",
    		"type": 10,
    		"options": [],
    		"judgeList": [{
    			"label": "为空",
    			"value": 4
    		}, {
    			"label": "不为空",
    			"value": 5
    		}],
    		"componentFormat": "none"
    	}
    }, {
    	"key": "1131655792487550",
    	"value": {
    		"label": "手机",
    		"renderKey": "1131655792487550",
    		"tag": "el-input",
    		"type": 12,
    		"options": [],
    		"judgeList": [{
    			"label": "包含",
    			"value": 2
    		}, {
    			"label": "已验证",
    			"value": 17
    		}, {
    			"label": "未验证",
    			"value": 18
    		}, {
    			"label": "为空",
    			"value": 4
    		}, {
    			"label": "不为空",
    			"value": 5
    		}],
    		"componentFormat": "input"
    	}
    }, {
    	"key": "1141655792487891",
    	"value": {
    		"label": "成员单选",
    		"renderKey": "1141655792487891",
    		"tag": "el-cascader",
    		"type": 13,
    		"options": [],
    		"judgeList": [{
    			"label": "等于",
    			"value": 0
    		}, {
    			"label": "不等于",
    			"value": 1
    		}, {
    			"label": "等于任意一个",
    			"value": 11
    		}, {
    			"label": "不等于任意一个",
    			"value": 12
    		}, {
    			"label": "为空",
    			"value": 4
    		}, {
    			"label": "不为空",
    			"value": 5
    		}],
    		"componentFormat": "tree"
    	}
    }, {
    	"key": "1151655792488133",
    	"value": {
    		"label": "部门单选",
    		"renderKey": "1151655792488133",
    		"tag": "el-cascader",
    		"type": 15,
    		"options": [],
    		"judgeList": [{
    			"label": "等于",
    			"value": 0
    		}, {
    			"label": "不等于",
    			"value": 1
    		}, {
    			"label": "等于任意一个",
    			"value": 11
    		}, {
    			"label": "不等于任意一个",
    			"value": 12
    		}, {
    			"label": "为空",
    			"value": 4
    		}, {
    			"label": "不为空",
    			"value": 5
    		}],
    		"componentFormat": "tree"
    	}
    }, {
    	"key": "1161655792488389",
    	"value": {
    		"label": "部门多选",
    		"renderKey": "1161655792488389",
    		"tag": "el-cascader",
    		"type": 16,
    		"options": [],
    		"judgeList": [{
    			"label": "等于",
    			"value": 0
    		}, {
    			"label": "为空",
    			"value": 4
    		}, {
    			"label": "不为空",
    			"value": 5
    		}, {
    			"label": "包含任意一个",
    			"value": 13
    		}, {
    			"label": "同时包含",
    			"value": 14
    		}],
    		"componentFormat": "tree"
    	}
    }, {
    	"key": "1171655792488689",
    	"value": {
    		"label": "成员多选",
    		"renderKey": "1171655792488689",
    		"tag": "el-cascader",
    		"type": 14,
    		"options": [],
    		"judgeList": [{
    			"label": "等于",
    			"value": 0
    		}, {
    			"label": "为空",
    			"value": 4
    		}, {
    			"label": "不为空",
    			"value": 5
    		}, {
    			"label": "包含任意一个",
    			"value": 13
    		}, {
    			"label": "同时包含",
    			"value": 14
    		}],
    		"componentFormat": "tree"
    	}
    }]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473

    至此,我们就满足了业务流程中的第 1 点和第 3 点:

    根据用户拖拽表单生成的JSON数据(本次采用了FormGenerator去生成JSON数据),生成对应的所有组件的下拉列表,并在流程图的连接线上添加条件判断。

    针对不同组件对应的下拉列表,要求有不同的条件判断,例如:数字类型的需要有范围选择、大小判断、为空不为空,等条件;文本组件有包含不包含,等条件

    我们只需提取map中每个组件的renderKeylabel作为选项的valuelabel即可生成所有组件的下拉列表。当然,不要忘了用JSON.parse解析JSON对象后再提取

    生成/删除单个下拉列表组件

    来让我们看看第2个流程:

    当用户点击流程图的边时,生成该边上对应的所有组件的下拉列表,在点击某个组件的选项后,生成对应组件的下拉列表组件,在用户取消选择后,对应组件的下拉列表组件随之消失

    我们借鉴Vue的思想,给这个增增删删的组件一个生命周期,那么他大概分为如下几个生命周期:

    1. init:点击流程图时,属于init初始化的阶段,判断是否有回显的数据,并组织各类数据去展示。
      • 首先初始化该边的数据,由父组件传递过来,然后判断是否有数据需要回显(initEdgeData
      • 当边的数据初始化结束后,假如没有数据需要回显,则结束init。否则,添加回显的数据item进已选项中(initSelectedItems
      • 当选中项存在时,需要初始化选中项对应的组件(initSelectedComponents
    2. update:当已选组件列表发生变化时,更新下拉列表组件
      • 如果是在全部组件列表里添加了新选项,那么就需要将其由renderKeylabel的item添加进已选项中
      • 如果是取消了某个选项,删除该选项renderKey对应的数据
      • 如果是更新了已选项的数据,判断是否应该被保存
    3. distory:当全部组件列表的某个已选项取消选择后,删除对应的下拉列表组件。触发update。所以这个阶段可以和update合二为一
    //所有组件下拉列表的vue结构
     	<!-- 添加流转条件多选列表 -->
                <el-select
                  v-model="selectedComponentIds"
                  size="small"
                  :popper-append-to-body="false"
                  collapse-tags
                  filterable
                  multiple
                  placeholder="请选择"
                  @change="updateSelectedComponents"
                >
                  <el-option
                    v-for="item in formComponents"
                    :key="item.renderKey"
                    :label="item.label"
                    :value="item.renderKey"
                  />
                </el-select>
    // 遍历展示下拉组件的vue结构
    		<div class="conditions-items-box">
              <!-- key值不同保证更新子组件的值 -->
              <componentsCard
                v-for="item in selectedComponentsAry"
                :key="JSON.stringify(item.condition)"
                :custom-component="item"
                :cur-judge="curJudge"
                @changeCurJudge="showJudge"
                @saveComponent="saveComponent"
              />
            </div>
    <script>
    import componentsCard from '@/components/workflow/PropertySetting/componentsCard';
    import { getComponentMap } from '@/utils/adpterForForm';
    components: { componentsCard },
    data() {
        return {
          conditionOptions: [{
            value: 0,
            label: '使用自定义流转条件'
          }, {
            value: 1,
            label: '使用Else条件'
          }],
          choseType: false,
          isAll: false,
          selectedCondition: 0,
          formComponents: [], // 用于遍历展示所有组件的选项列表
          componentsMap: null, // 存放所有组件的基础信息
          selectedComponentIds: [], // 存放所有已选组件的renderKey(唯一id)
          selectedComponentsAry: null, // 遍历展示每个组件下拉列表组件
          curJudge: -1,
          form: null, // 拷贝当前边的自定义数据
          saveMap: new Map() // 仅保存conditions,这是后端要求的,即renderKey、Judge、content
        };
      },
    // 通过watch监听边的变化并获取数据
    watch: {
       // 边更新后初始化数据
       nodeData: function(val) {
         if (val.type === 'customEdge') {
           this.initEdgeData(val);
         }
       }
     },
    // Vue组件初始化时
    created() {
       // 构建组件map,key为renderKey,value为组件所有的基本信息
       // 把表单数据存在了localStorage里面防止刷新页面后表单数据丢失
       this.componentsMap = getComponentMap(JSON.parse(window.localStorage.drawingItems));
       // 获取表单数据并展示选择列表。这里就是v-for展示下拉选项用的
       this.formComponents = [...this.componentsMap.values()];
       // 此处可以优化,只用到了label和renderKey,存在一个数据冗余的问题 可以遍历解构label和value。
     },
     methods:{
     	// 初始化边的条件数据
        initEdgeData(data) {
          // 提取边的自定义属性properties,并拷贝一份到当前实例(不需要深拷贝)
          // 便于在保存数据时直接拿来save替换掉老数据
          // 保存的数据都存在properties.conditions里面
          const { properties } = data;
          if (properties) {
            this.$data.form = Object.assign({}, this.$data.form, properties);
          }
          // 更新当前选中项
          this.initSelectedItems(data);
          // 更新当前选中项对应的自定义组件
          this.initSelectedComponents(this.$data.form.conditions);
        },
        
        // 初始化选中项
        initSelectedItems(data) {
          // 初始化选中项数组。
          this.selectedComponentIds = [];
          // 将边上之前添加过的数据回显出来
          data.properties.conditions.forEach(item => {
            this.selectedComponentIds.push(item.renderKey);
          });
        },
        
        // 初始化选中项对应的组件,此时除了初始化组件的基本信息外,还会添加已选组件的用户输入信息,即conditionList
        initSelectedComponents(conditionList) {
          const ary = [];
          // 遍历当前选中项,更新条件map和卡片数组
          for (const item of conditionList) {
            // 此处需要深拷贝,componentsMap获取的是一份地址,直接操作会污染componentsMap
            const componentObj = Object.create(this.componentsMap.get(item.renderKey));
            componentObj.condition = item;
            ary.push(componentObj);
            // 初始化要保存的map,map保存的仅有conditions,这是后端要求的
            /* conditions形如
            *{ 
            * renderKey:唯一id
            * Judge:条件
            * content:内容
            *}
            */
            this.saveMap.set(item.renderKey, item);
          }
          // 更新当前选中项对应的组件卡片数组
          this.selectedComponentsAry = ary;
        },
        
        // 更新当前选中项对应的组件
        // 此方法在触发下拉列表事@change件时传入已选项的renderKey的list
        // 使用renderKey,控制条件组件的展示
        updateSelectedComponents(idList) {
          const ary = []; // 用于遍历展示用的已选项数组
          const newSaveMap = new Map(); // 用于替换saveMap的新map,数据格式和saveMap一样,是update后的saveMap
          let saveMapItem = null; // 判断是否已存在该数据,是延用数据还是初始化保存项
          
          // 遍历当前选中项,更新newSaveMap和卡片数组
          // item为renderKey(唯一id)
          for (const item of idList) {
            // 此处需要浅拷贝,直接操作会污染componentsMap(组件的基本信息)
            const componentObj = Object.create(this.componentsMap.get(item)); // 目标组件的基本信息
            saveMapItem = this.saveMap.get(item);
            // 向组件添加要入库的condition信息,即用户输入的信息
            // 同时判断该数据是否已存在,是延用数据还是初始化数据
            // 这么做的原因是因为无法获取到最新添加/删除的id,只能获得所有的id,然后遍历替换数据,所以已存在的老数据需要保留
            componentObj.condition = saveMapItem  ? saveMapItem  : {
              Judge: -1, // 条件默认为 请选择
              content: null, // 选择的内容默认为 null
              renderKey: item // 唯一id
            };
            ary.push(componentObj); // 用于遍历展示用的已选项数组
            newSaveMap.set(item, this.saveMap.get(item)); // 向准备替换的map内添加数据
          }
          // 更新要存入节点的map
          this.saveMap = newSaveMap;
          this.form.conditions = this.filterSaveData();
          // 更新当前选中项对应的组件卡片数组
          this.selectedComponentsAry = ary;
          this.saveData();
        },
        
        // 新添加的下拉列表组件值被修改后触发
        saveComponent(conditions) {
          const { Judge: judge, renderKey } = conditions;
          if (judge !== -1) {
            this.saveMap.set(renderKey, conditions);
          }
          this.form.conditions = this.filterSaveData();
          this.saveData();
        }
        
        // 过滤可保存数据
        filterSaveData() {
          this.form.conditions = [...this.saveMap.values()].filter(item => {
            if (item && item.judge !== -1) {
              // 选择了条件
              if (item.judge === 4 || item.judge === 5) {
                // 条件是 为空 和 不为空 不需要content有值
                return item;
              } else if (item.content !== '' && item.content !== null && item.content !== undefined) {
                // 其他条件content必须有值才通过过滤
                return item;
              }
            }
          });
        },
    
    	// 保存数据到当前选中元素
        saveData() {
          const { id } = this.$props.nodeData;
          // logicFlow的保存节点信息的Api
          this.$props.lf.setProperties(id, this.$data.form);
        },
     }
     </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190

    至此,我们已经完成了流程2的所有逻辑。

    当用户点击流程图的边时,生成该边上对应的所有组件的下拉列表,在点击某个组件的选项后,生成对应组件的下拉列表组件,在用户取消选择后,对应组件的下拉列表组件随之消失。

    因为流程3的逻辑在我们编写工具类时就已经搞定了,所以进入流程4

    当用户选择后,自动保存到当前边对应的JSON数据,若用户的选择无效,则不保存

    子组件的编写+数据保存

    子组件采用工厂模式写了一个工厂组件,这里起得名字叫componentsCard,通过组件内部的componentFormat属性决定该组件该如何展示。该属性在解构的时候通过tag属性添加。

    当用户在全部组件的下拉列表选择后,该组件的renderKey就会被添加进已选项中,假如用户并没有进行任何操作,则该条数据会被filterSaveData过滤掉,被认为是无效数据。只有满足filterSaveData过滤条件的数据才会被存入当前边的JSON数据中

    // 只展示了部分关键代码
    
    	....
    	
    <div class="container-title">
          <div class="title-name">
            {{ customComponent.label }}
          </div>
          <div class="title-conditions" @click="showJudgeList(customComponent)">
            {{ selectedItem.label }}
            <i class="icon-wf-caretdown iconfont" />
            <div v-if="+curJudge === +customComponent.renderKey" class="list-box">
              <div
                v-for="(item,index) in customComponent.judgeList "
                :key="index"
                class="list-item"
                @click.capture.stop="selectItem(item)"
              >
                {{ item.label }}
              </div>
            </div>
          </div>
        </div>
        
        ....
    
    <!-- 为空或不为空作为条件时,该选项不显示 -->
        <div v-show="showContent">
          <div
            v-if="customComponent.componentFormat === 'input'"
            class="content"
          >
            <el-input
              v-show="selectedItem.value !== 4 && selectedItem.value !== 5"
              v-model="content"
              @change="saveData"
            />
          </div>
          <div
            v-if="customComponent.componentFormat === 'select'"
            class="content"
          >
            <!-- 单选列表 -->
            <el-select
              v-show="!isMultipleSelect"
              v-model="content"
              @change="saveData"
            >
              <el-option
                v-for="item in customComponent.options"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
            <!-- 多选列表 -->
            <el-select
              v-show="isMultipleSelect"
              v-model="contentAry"
              multiple
              placeholder="请选择"
              @change="saveDataAry"
            >
              <el-option
                v-for="item in customComponent.options"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
          </div>
          <div
            v-if="customComponent.componentFormat === 'date'"
            class="content"
          >
            <el-date-picker
              v-show="!isRange"
              v-model="content"
              type="date"
              placeholder="选择日期"
              value-format="timestamp"
              @change="saveData"
            />
            <el-date-picker
              v-show="isRange"
              v-model="contentAry"
              type="daterange"
              range-separator="~"
              value-format="timestamp"
              @change="saveDataAry"
            />
          </div>
          <div
            v-if="customComponent.componentFormat === 'tree'"
            class="content"
          >
            <el-cascader
              v-model="content"
              :options="customComponent.options"
              @change="saveData"
            />
          </div>
          <div
            v-if="customComponent.componentFormat === 'none'"
            class="content"
          />
        </div>
        
    <script>
    export default {
      props: {
        curJudge: {
          type: Number,
          default: -1
        },
        customComponent: {
          type: Object,
          default: () => {}
        }
      },
      data() {
        return {
          selectedItem: { label: '请选择', value: -1 }, // 条件字段选择的item
          content: '', // 内容
          contentAry: [], // 内容也可能是数组(范围选择时)
          multipleSelectVal: [2, 3, 11, 12, 13, 14], // 这些值表示可以进行多选
          rangeVal: [10] // 这些值表示是范围选择,
        };
      },
      computed: {
        // 要保存的数据
        sendData: function() {
          return {
            content: this.content,
            renderKey: this.customComponent.renderKey,
            Judge: this.selectedItem.value
          };
        },
        sendDataAry: function() {
          return {
            content: this.contentAry,
            renderKey: this.customComponent.renderKey,
            Judge: this.selectedItem.value
          };
        },
        // 是否展示输入框
        showContent: function() {
          return this.selectedItem.value !== 4 && this.selectedItem.value !== 5;
        },
        // 是否多选
        isMultipleSelect: function() {
          return this.findVal(this.multipleSelectVal, this.selectedItem.value);
        },
        // 是否范围选择
        isRange: function() {
          return this.findVal(this.rangeVal, this.selectedItem.value);
        }
      },
      mounted() {
        // 判断显示的内容是 数组 还是 字符串/数字
        Array.isArray(this.customComponent.condition.content) ? this.contentAry = this.customComponent.condition.content : this.content = this.customComponent.condition.content;
        // 已选值传入时需要回显,找到对应的选项
        const initSelectJudge = this.customComponent.judgeList.find(options => {
          return options.value === this.customComponent.condition.Judge;
        });
        // 初始化当前组件条件选项的值
        this.selectedItem = initSelectJudge || this.selectedItem;
      },
      methods: {
        selectItem(item) {
          this.selectedItem = item;
          // 选择完关闭条件列表
          this.$emit('changeCurJudge', -1);
          this.content = null;
          this.contentAry = null;
     
          // 这里将数据是否保存的判断全部交由父组件去处理了,这里本来是只有选择 为空/不为空 才会发送
          // 原因1
          // 数据保存的条件是blur,所以当更改数据后就需要确认数据是否能够被保存(这里和流程图的设计有关)
          // 原因2
          // 但即使父组件接收到了数据,也需要额外的判断流程,不如就将所有的判断逻辑都交由了父组件处理
          // 这样子组件只管选择就行了,后期即使需要更改也只需要更改父组件的判断逻辑
          // 原因3
          // 这样在用户切换条件时,能够重置输入框
          this.$emit('saveComponent', this.sendData);
        },
        showJudgeList(item) {
          this.$emit('changeCurJudge', item.renderKey);
        },
        // 保存数据,通过计算属性包装的data直接发送
        saveData(val) {
          this.content = val;
          this.$emit('saveComponent', this.sendData);
        },
        // 保存数据,通过计算属性包装的data直接发送
        saveDataAry(val) {
          this.contentAry = val;
          this.$emit('saveComponent', this.sendDataAry);
        },
        // 工具方法,找数用的
        findVal(ary, val) {
          return ary.find(item => {
            return item === val;
          });
        }
      }
    };
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208

    这样子组件就可以在数据更改后向父组件通知,并将所有的判断逻辑交由父组件处理,将子组件的功能尽量简化。避免后期在维护时还需要关注子组件的判断逻辑。

    而且通过工厂组件,当需要新加组件时也只需要去维护一个componentCard组件就行了,逻辑完全可以复用。

    到这,业务流程4就能跑通了。

    当用户选择后,自动保存到当前边对应的JSON数据,若用户的选择无效,则不保存。

    至于业务流程5

    要求能够回显用户选择的数据,即点击不同边时,回显该边上JSON对应的所有下拉列表组件

    因为在父组件使用了watch,当节点发生变化时,会重新执行init阶段的各种方法,从而做到数据回显。

    总结

    做这种组件还是需要理清楚逻辑,多多关注细节问题。同时尽量让逻辑简单起来。当然肯定存在不少更优的写法,不过目前还考虑不到,欢迎各位大佬评论指正。

  • 相关阅读:
    登录获取token字符串
    2022年国家高新企业认定申报最全问答-财务数据篇
    【毕业设计】基于Stm32的便携体测仪(心率 体温) - 单片机 嵌入式 物联网
    Python 基础面试第三弹
    市政行业乙级资质改革对公共交通工程的影响
    含文档+PPT+源码等]精品微信小程序计算机维修服务+后台管理系统|前后分离VUE[包运行成功]计算机毕业设计项目源码Java毕设项目
    6-1 列表推导生成随机数矩阵(高教社,《Python编程基础及应用》习题4-10 Python
    C++中STL库
    【行业动态】福建服装品牌如何完成差异化战略?
    十六、模型构建器(ModelBuilder)快速提取城市建成区——理论介绍
  • 原文地址:https://blog.csdn.net/qq_45670042/article/details/125387467