通過一個很常用的場景來展示vue資料驅動的應用

鄒瓊俊發表於2020-11-04

  需求:可以動態增減組合條件來進行資料查詢。

  介面執行效果如下圖所示:

  介面第一次載入時,預設會顯示一個空的查詢條件,如下圖所示:

  

  點選“加”圖示,可以無限增加查詢條件,也可以點選“減”圖示刪除新增的查詢條件,如下圖所示:

  

  說明:第一個下拉框的資料變化時,第三個下拉框的資料要進行聯動,第三個元件可以是下拉框也可以是文字框,它是根據第一個下拉框的資料來決定的。第二個下拉框是固定的四個選項>、<、=、!=。如下圖所示:

  

  後端介面返回的資料結構:

通過一個很常用的場景來展示vue資料驅動的應用
{
    "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操作當中。

  在實際工作中,關於介面的資料結構,可能是後端開發人員自定義的,也可能是前端開發人員自定義好後給後端你的,也可能是前後端一起協商的。當後端介面提供的資料若是無法直接滿足前端的需求時,前端人員將不得不對資料進行二次構造,這是很痛苦的一件事情,作為前端開發者而言,必然希望介面返回的資料是不需要二次構造和清洗的,拿來賦值就可以使用。

相關文章