需求:可以動態增減組合條件來進行數據查詢。
界面運行效果如下圖所示:
界面第一次加載時,默認會顯示一個空的查詢條件,如下圖所示:
點擊“加”圖標,可以無限增加查詢條件,也可以點擊“減”圖標刪除新增的查詢條件,如下圖所示:
說明:第一個下拉框的數據變化時,第三個下拉框的數據要進行聯動,第三個組件可以是下拉框也可以是文本框,它是根據第一個下拉框的數據來決定的。第二個下拉框是固定的四個選項>、<、=、!=。如下圖所示:
后端接口返回的數據結構:

{ "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 }
把接口數據賦值給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操作當中。
在實際工作中,關於接口的數據結構,可能是后端開發人員自定義的,也可能是前端開發人員自定義好后給后端你的,也可能是前后端一起協商的。當后端接口提供的數據若是無法直接滿足前端的需求時,前端人員將不得不對數據進行二次構造,這是很痛苦的一件事情,作為前端開發者而言,必然希望接口返回的數據是不需要二次構造和清洗的,拿來賦值就可以使用。