需求:可以動態增減組合條件來進行資料查詢。
介面執行效果如下圖所示:
介面第一次載入時,預設會顯示一個空的查詢條件,如下圖所示:
點選“加”圖示,可以無限增加查詢條件,也可以點選“減”圖示刪除新增的查詢條件,如下圖所示:
說明:第一個下拉框的資料變化時,第三個下拉框的資料要進行聯動,第三個元件可以是下拉框也可以是文字框,它是根據第一個下拉框的資料來決定的。第二個下拉框是固定的四個選項>、<、=、!=。如下圖所示:
後端介面返回的資料結構:
{ "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操作當中。
在實際工作中,關於介面的資料結構,可能是後端開發人員自定義的,也可能是前端開發人員自定義好後給後端你的,也可能是前後端一起協商的。當後端介面提供的資料若是無法直接滿足前端的需求時,前端人員將不得不對資料進行二次構造,這是很痛苦的一件事情,作為前端開發者而言,必然希望介面返回的資料是不需要二次構造和清洗的,拿來賦值就可以使用。