在一瞬间有一千万种可能......

在一瞬间有一千万种可能......

  界面运行效果如下图所示:

  界面第一次加载时,默认会显示一个空的查询条件,如下图所示:

  

  点击“加”图标,可以无限增加查询条件,也可以点击“减”图标删除新增的查询条件,如下图所示:

  

  说明:第一个下拉框的数据变化时,第三个下拉框的数据要进行联动,第三个组件可以是下拉框也可以是文本框,它是根据第一个下拉框的数据来决定的。第二个下拉框是固定的四个选项>、<、=、!=。如下图所示:

  通过一个很常用的场景来展示vue数据驱动的应用-LMLPHP

  后端接口返回的数据结构:

通过一个很常用的场景来展示vue数据驱动的应用-LMLPHP通过一个很常用的场景来展示vue数据驱动的应用-LMLPHP
{
    "data": {
        "array": [{
            "opts": [{
                "val": "0",
                "name": "停止"
            }, {
                "val": "1",
                "name": "运行"
            }],
            "paramCode": "runStatus",
            "name": "运行状态"
        }, {
            "opts": [{
                "val": "0",
                "name": "否"
            }, {
                "val": "1",
                "name": "是"
            }],
            "paramCode": "alarmStatus",
            "name": "报警与否"
        }, {
            "opts": [{
                "val": "0",
                "name": "就地"
            }, {
                "val": "1",
                "name": "远程"
            }],
            "paramCode": "remoteLocal",
            "name": "远程就地"
        }, {
            "opts": [{
                "val": 0,
                "name": "禁用"
            }, {
                "val": 1,
                "name": "启用"
            }],
            "paramCode": "startUse",
            "name": "是否启用"
        }, {
            "opts": [{
                "val": "0",
                "name": "变频"
            }, {
                "val": "1",
                "name": "工频"
            }],
            "paramCode": "runMode",
            "name": "工频启停"
        }, {
            "opts": [{
                "val": 0,
                "name": "手动"
            }, {
                "val": 1,
                "name": "自动"
            }],
            "paramCode": "controlMode",
            "name": "控制模式"
        }, {
            "opts": [{
                "val": "0",
                "name": "手动"
            }, {
                "val": "1",
                "name": "自动"
            }],
            "paramCode": "frequencyMode",
            "name": "频率手自动"
        }, {
            "opts": [],
            "paramCode": "frequencySetValue",
            "name": "频率设定"
        }, {
            "opts": [],
            "paramCode": "frequencyReturnValue",
            "name": "频率反馈"
        }, {
            "opts": [],
            "paramCode": "tempSetValue",
            "name": "温度设定"
        }, {
            "opts": [],
            "paramCode": "tempReturnValue",
            "name": "温度反馈"
        }, {
            "opts": [{
                "val": 0,
                "name": "关"
            }, {
                "val": 1,
                "name": "开"
            }],
            "paramCode": "newWindValveOnOff",
            "name": "新风阀启停"
        }, {
            "opts": [],
            "paramCode": "newWindValveOpen",
            "name": "新风阀开度"
        }, {
            "opts": [],
            "paramCode": "newWindTemp",
            "name": "新风阀温度"
        }, {
            "opts": [],
            "paramCode": "newWindHumidity",
            "name": "新风阀湿度"
        }, {
            "opts": [{
                "val": 0,
                "name": "关"
            }, {
                "val": 1,
                "name": "开"
            }],
            "paramCode": "returnWindValveOnOff",
            "name": "回风阀启停"
        }, {
            "opts": [],
            "paramCode": "returnWindValveOpen",
            "name": "回风阀开度"
        }, {
            "opts": [],
            "paramCode": "returnWindHumidity",
            "name": "回风阀湿度"
        }, {
            "opts": [],
            "paramCode": "co2",
            "name": "回风阀co2"
        }, {
            "opts": [{
                "val": "0",
                "name": "手动"
            }, {
                "val": "1",
                "name": "自动"
            }],
            "paramCode": "waterValveHandAuto",
            "name": "水阀手自动"
        }, {
            "opts": [{
                "val": 0,
                "name": "关"
            }, {
                "val": 1,
                "name": "开"
            }],
            "paramCode": "waterValveOnOff",
            "name": "水阀启停"
        }, {
            "opts": [],
            "paramCode": "waterValveOpenSet",
            "name": "水阀开度设定"
        }, {
            "opts": [],
            "paramCode": "waterValveOpenReturn",
            "name": "水阀开度反馈"
        }, {
            "opts": [],
            "paramCode": "supplyAirTemp",
            "name": "送风温度"
        }, {
            "opts": [],
            "paramCode": "supplyAirHumidity",
            "name": "送风湿度"
        }, {
            "opts": [],
            "paramCode": "supplyAirPressure",
            "name": "送风静压"
        }, {
            "opts": [{
                "val": 0,
                "name": "关"
            }, {
                "val": 1,
                "name": "开"
            }],
            "paramCode": "humidityValveOnOff",
            "name": "加湿阀启停"
        }, {
            "opts": [],
            "paramCode": "humidityValveOpen",
            "name": "加湿阀开度"
        }, {
            "opts": [],
            "paramCode": "filterDiffPressure",
            "name": "过滤网压差"
        }, {
            "opts": [{
                "val": 0,
                "name": "关"
            }, {
                "val": 1,
                "name": "开"
            }],
            "paramCode": "elecHeatOnOff",
            "name": "电加热"
        }, {
            "opts": [],
            "paramCode": "power",
            "name": "功率"
        }, {
            "opts": [],
            "paramCode": "runTime",
            "name": "运行时间"
        }, {
            "opts": [],
            "paramCode": "loadPower",
            "name": "供冷负荷"
        }, {
            "opts": [],
            "paramCode": "cold",
            "name": "累计供冷量"
        }, {
            "opts": [{
                "val": 0,
                "name": "正常"
            }, {
                "val": 1,
                "name": "中断"
            }],
            "paramCode": "commuInterrupt",
            "name": "通讯是否正常"
        }]
    },
    "code": 200,
    "msg": "",
    "errors": null
}
View Code

  把接口数据赋值给paramList。

  因为数据量不大,为了提升性能,数据是接口一次性返回的,数据的联动是在内存当中进行数据筛选,从而避免频繁的接口调用。

  在数据结构当中,当opts属性值为空数组时,第三个组件显示为文本框,否则显示为下拉框,并把opts中的数据作为第三个组件的下拉框内容展示出来。当第三个组件是下拉框时,第二个下拉框只能显示=和!=这两项,如果是文本框时,都可以显示。

  后端接口需要的查询参数是:

      dataListParams: [{paramName: "runStatus", operator: 2, value: "0"}, {paramName: "startUse", operator: 2, value: 0}]

  接下来,我们定义vue组件中的内容,Dom部分:

        <div class='search-item'>
          <label>组合条件:</label>
        </div>
        <div class="search-item"
             v-for="(paramObj,index) in dataListParams"
             :key="index">
          <!-- 参数名 -->
          <el-select v-model="paramObj.paramName"
                     filterable
                     clearable
                     @change="(e)=>changeParam(e,index)"
                     placeholder="请选择"
                     style="width:100px">
            <el-option v-for="item in paramList"
                       :key="item.paramCode"
                       :label="item.name"
                       :value="item.paramCode"></el-option>
          </el-select>
          <!-- 操作符列表 -->
          <el-select v-model="paramObj.operator"
                     clearable
                     placeholder=""
                     style="width:60px">
            <el-option v-for="(item,sindex) in operatorOptions"
                       :disabled="getDisabled(sindex,index)"
                       :key="item.id"
                       :label="item.name"
                       :value="item.id"></el-option>
          </el-select>
          <!-- 参数值 -->
          <el-select v-if="listOpts[index].length>0"
                     v-model="paramObj.value"
                     filterable
                     clearable
                     placeholder="请选择"
                     style="width:100px">
            <el-option v-for="item in listOpts[index]"
                       :key="item.val"
                       :label="item.name"
                       :value="item.val"></el-option>
          </el-select>
          <el-input v-else
                    v-model="paramObj.value"
                    :clearable="true"
                    placeholder="请输入"
                    style="width:100px"></el-input>

          <span v-if="index==0"
                class="add-where"><i class="iconfont icon-add"
               @click="addWhere"></i></span>
          <span v-else
                class="remove-where"><i class="iconfont icon-shanchu1"
               @click="removeWhere(index)"></i></span>
        </div>

  js部分:

export default {
  mixins: [indexOptions],
  data () {
    return {
      //操作符列表
      operatorOptions: [
        { id: 0, name: '>' },
        { id: 1, name: '<' },
        { id: 2, name: '=' },
        { id: 3, name: '!=' }
      ],
      //组合条件
      dataListParams: [{ paramName: "", operator: '', value: "" }],
      deviceIds: [],
paramList:[],//参数列表,包含操作名称、操作值列表(JSon数据结构)
listOpts: [[]]//操作值列表 } }, computed: {//筛选参数列表,如果参数列表dataListParams当中有任何一个属性值为空,则不传递 filterDataListParams () { return this.dataListParams.filter(f => { return f.paramName !== "" && f.operator !== "" && f.value !== "" }); } }, methods: {//获取操作选项启用、禁用 getDisabled (sindex, index) { if (this.listOpts.length > 0 && this.listOpts[index].length > 0) { return [0, 1].includes(sindex); } else { return false; } }, //添加组合条件 addWhere () { this.dataListParams.push({ paramName: '', operator: '', value: '' }); this.listOpts.push([]); }, //移除组合条件 removeWhere (index) { this.dataListParams.splice(index, 1); this.listOpts.splice(index, 1); }, //根据参数编码获取操作列表 getOptsByParamCode (code,) { let res = this.paramList.find(f => f.paramCode == code); return res ? res.opts : []; }, //参数选项变化 changeParam (code, index) { //变化之前的类型 let preType = this.listOpts[index].length > 0; //是否下拉框 let arr = this.getOptsByParamCode(code); this.listOpts[index] = arr; this.dataListParams[index].value = ''; //变化之后的类型 let nextType = arr.length > 0;//是否是下拉框 //前后类型不一致时,清空操作符 if (preType != nextType) { this.dataListParams[index].operator = ''; } } } };

  当第一个下拉框选项变化时,如果第三个组件是相同类型(下拉框或文本框),则第二个下拉框的选项不清空,否则清空。因为文本框可以选4个选项,下拉框只能选两个,不能记录上一次的选中状态。

  只有一组查询条件当中三个选项的值都不为空时,才会把参数传递给后端,所以前端通过计算熟悉filterDataListParams进行了数据过滤。

  由于数据结构是动态变化的,所以为了保存查询条件的保存状态,下拉框的数据列表项也应当是动态的(数组存储)。

  这里其实还漏了东西,那就是查询条件去重,我虽然知道,但是我并没有在前端进行去重,因为需求不提、产品不提,待他们发现这个问题并且提时bug时,我会把锅帅给后端,让后端对查询条件的数据进行去重处理...O(∩_∩)O哈哈~(^_^)

  还有查询条件参数值是文本框时的输入值的合法性校验,这个产品不提、测试不提、我也不做,因为做也不知道要做成什么样,到时候,接口报错了,我一并再把锅甩给后端~O(∩_∩)O哈哈~甩锅非我愿,然而,我这样做,都是为了高效的完成开发任务,然后留出时间搞自己的事情,毕竟我一个前端要对接4个后端,每天净干些打杂的事情,太浪费时间了...像我们这样的小企业,每天都讲产出,有时候自己花太多时间和精力去想把东西做好,领导一句:搞个这么简单的玩意怎么要花费那么久。为此,对于所有开发任务,你给一天时间我有一天时间的玩法,你给三天时间我有三天的玩法,你给一周我有一周的玩法,反正最终都是结果导向:按时完成开发和上线。一分价钱一分货,一分时间也是一分货。我相信有许多像我一样的老程序员、老油条,面对一些很不合理的要求,总是有各种办法偷工减料,然后让领导满意。

  最后把filterDataListParams作为参数传递给后端接口就可以了,这是一个很典型的vue动态数据驱动应用。

  数据驱动的核心,在于构造数据结构,因为dom的变化都是根据数据来渲染的,在过去用js或者jquery的方式,会频繁的操作dom,不单要写一大坨代码,而且性能还很低,数据驱动之后,只要定义好了数据结构,程序员可以把更多的精力投入到业务场景当中,而并非dom操作当中。

  在实际工作中,关于接口的数据结构,可能是后端开发人员自定义的,也可能是前端开发人员自定义好后给后端你的,也可能是前后端一起协商的。当后端接口提供的数据若是无法直接满足前端的需求时,前端人员将不得不对数据进行二次构造,这是很痛苦的一件事情,作为前端开发者而言,必然希望接口返回的数据是不需要二次构造和清洗的,拿来赋值就可以使用。

11-04 22:52