如何在 vue 中封裝一個好用不上火的列表條件過濾組件

前言

平時工做中,想必你必定會遇到無數次列表需求,而且是帶有搜索功能的列表需求,但無論怎麼怎麼樣,請相信這一切都是萬變不離其宗。css

不信?請看:html

  1. 可輸入關鍵字進行搜索
  2. 可在下拉框中選擇搜索條件
  3. 能夠選擇日期時間
  4. 能夠勾選 的複選框

基本就是上面這些了吧?咱們不妨看看下面這張截圖: vue

本文所說的組件封裝是指業務上的封裝,而不是組件底層的封裝,像這種底層封裝的組件庫不少,好比:antd、element。

正常套路

上面的截圖也算是簡單的了,只有一個兩種類型,一個是輸入框,一個是下拉框,但也足以說明問題了,回想一下,當你遇到這樣的需求你會如何實現?通常地咱們是否是會像下面這樣寫書:數組

<div class="container">
    <a-row>
      <a-col :span="20">
        <a-form class="search-form" layout="inline" @submit="search">
          <a-form-item label="維修項目">
            <a-input placeholder="請輸入" />
          </a-form-item>
          <a-form-item label="維修師傅">
            <a-select placeholder="請選擇">
              <a-select-option
                :value="item.id"
                :key="item.id"
                v-for="item of userOptions"
              >{{item.name}}</a-select-option>
            </a-select>
          </a-form-item>
          <a-form-item label="樓號">
            <a-select placeholder="請選擇">
              <a-select-option
                :value="item.id"
                :key="item.id"
                v-for="item of floorOptions"
              >{{item.name}}</a-select-option>
            </a-select>
          </a-form-item>
          <a-form-item label="宿舍">
            <a-select placeholder="請選擇">
              <a-select-option
                :value="item.id"
                :key="item.id"
                v-for="item of roomOptions"
              >{{item.name}}</a-select-option>
            </a-select>
          </a-form-item>
          <a-form-item class="button">
            <a-button class="offset-right-12" type="primary" html-type="submit">搜索</a-button>
            <a-button @click="reset">重置</a-button>
          </a-form-item>
        </a-form>
      </a-col>
      <a-col :span="4" class="text-right">
        <a-button type="primary" @click="showModal()">新增</a-button>
      </a-col>
    </a-row>
    <a-table
      class="offset-top-12"
      rowKey="id"
      :pagination="pagination"
      :loading="loading"
      bordered
      :columns="columns"
      :dataSource="dataList"
    >
      <span slot="visitDate" slot-scope="scope">{{scope.visitDate | date}}</span>
      <span slot="leaveDate" slot-scope="scope">{{scope.leaveDate | date}}</span>
      <span slot="createDate" slot-scope="scope">{{scope.createDate | date}}</span>
      <div slot="action" slot-scope="scope" class="text-right no-wrap">
        <a-button type="link" @click="showModal(scope)">修改</a-button>
        <a-divider type="vertical" />
        <a-button type="link" @click="deleteModal(scope.id)">刪除</a-button>
      </div>
    </a-table>
  </div>
</template>
複製代碼

示例基於 antd vueantd

若是過濾條件更多(常有的事),那麼頁面中的代碼量更加驚人,整個頁面瀰漫着反客爲主的味道,僅僅是過濾條件就已經佔了整個頁面的一半。ide

這會致使什麼問題?函數

  1. 頁面代碼量過多
  2. 代碼很差維護
    • 沒法對多個列表頁面中的這些查詢條件樣式統一的修改(好比:樣式)
    • 即便不是修改這些查詢條件,也會讓你在維護時花更多的時間去定位代碼

若是以爲頁面代碼時過多,不會對你在開發以及維護上產生過多的負面影響,但至少有一個理由我以爲你應該選擇去把這些過濾條件進行封裝,那就是若是不去對列表頁過濾條件進行封裝,若遇到統一修改這些過濾條件(不論是業務性的,仍是樣式及結構的)你就會變得很被動,瞬間從高大尚的腦力活變成了出力不討好的苦力活。那麼如何去封裝呢?this

  • 咱們能夠把過濾條件裏的全部可能都放到一個組件中,而後經過一個字段來判斷到底要顯示哪一個
  • 自定義鍵名(對於下拉框等場景須要,而且咱們能夠爲其設置默認的 key,分別爲:idname
  • 如何實現過濾條件的數據收集(用於接口請求的參數)

封裝

組件有至少有兩個參數以及兩個事件。其中一個參數是用於遍歷出過濾條件的一個數組,另外一個是保存了全部查詢參數的一個對象。spa

爲何分紅兩個參數,而不一個參數搞定。緣由以下:雙向綁定

  1. 若是都放到一個參數裏就會讓組件思路不夠清晰明,雖說一個參數比兩個參數更加方便,但對於開發者而言,須要兼顧效率的同時也要考慮到組件自己的封裝及代碼實現。
  2. 對於這個過濾組件來講,顯示 UI 的數據及參數分開,可讓使用者更容易理解,這也是封裝組件的一個必須要考慮細節之一。

兩個事件分別是 searchrest,這兩個應該不用多說了。下面咱們就先來看看封裝好的條件過濾組件。

<template>
  <a-row>
    <a-col :span="20">
      <a-form class="search-form" :form="form" layout="inline" @submit="search">
        <a-form-item :label="item.name" v-for="(item, index) of items" :key="index">
          <a-input v-model="searchParams[item.key]" v-if="!item.type || item.type === 'input'" :placeholder="item.placeholder || '請輸入'"/>
          <a-select allowClear v-model="searchParams[item.key]" v-else-if="item.type === 'select'" :placeholder="item.placeholder || '請選擇'">
            <a-select-option
              :key="index"
              v-for="(option, index) of item.options"
              :value="option[item.optionValuekey || 'id']"
            >{{option[item.optionNamekey || 'name']}}</a-select-option>
          </a-select>
        </a-form-item>
        <a-form-item class="button">
          <a-button type="primary" html-type="submit">搜索</a-button>
          <a-button @click="reset">重置</a-button>
        </a-form-item>
      </a-form>
    </a-col>
    <a-col :span="4" class="text-right">
      <slot name="right-content"></slot>
    </a-col>
  </a-row>
</template>
<script>
import deepClone from "@/utils/deepClone";
/**
 * items 爲過濾條件,能夠在對應的項中自定義下拉選項的名稱(optionNamekey)和值(optionValuekey)的鍵,默認分別取 name 和 id
 * items 如何要下拉框顯示 placeholder,對應的下拉框的初始值能夠設置爲 undefined 或者 不定義查詢參數,若是設置爲空, placeholder 不會顯示。
 * searchParams 爲接口查詢參數對象
 */
export default {
  model: {
    prop: 'searchParams',
    event: "change"
  },
  props: {
    searchParams: {
      type: Object,
      default: () => {
        return {};
      }
    },
    items: {
      type: Array,
      default: () => {
        return [];
      }
    }
  },
  data() {
    return {
      // 保存初始化時的項
      initSearchParams:{},
      form: this.$form.createForm(this, { name: "dynamic_search" })
    };
  },
  created() {
    this.init()
  },
  methods: {
    init(){
      // 保存初始值的
      this.initSearchParams = deepClone(this.searchParams)
    },
    search() {
      const cpValue = deepClone(this.searchParams)
      this.$emit("change", cpValue);
      this.$emit("search", cpValue);
    },
    reset() {
      const cpValue = deepClone(this.initSearchParams)
      this.$emit("change", cpValue);
      this.$emit("reset", cpValue);
    }
  }
};
</script>
<style lang="scss" scoped>
.search-form {
  @extend .offset-bottom-24;
  .ant-form-item {
    &.button {
      margin-right: 0;
      white-space: nowrap;
    }
    .ant-input {
      width: 120px;
    }
    .ant-select {
      min-width: 120px;
    }
    .ant-btn + .ant-btn {
      @extend .offset-left-12;
    }
  }
}
</style>
複製代碼

上面的這個組件只是包涵了兩各狀況(輸入框,下拉框),其它的話能夠本身按需添加。deepClone() 方法爲深度拷貝。爲會把須要深度拷貝?緣由以下:

  • 備份一個用於重置的查詢參數。
  • 防止使用者在監聽事件中對函數參數進行修改後影響到組件中的 searchParams 數據。

組件接收兩個 prop 屬性

  • items 是用以保存用於渲染過濾條件的一個數據。
  • searchParams 初始化及收集請求接口參數,這個參數實現了雙向綁定,配合 v-model 使用。

如何使用

用法就很簡單了,引入,調用,傳參:

<template>
  <div class="container">
    <search-form :items="searchForm" v-model="searchParams" @search="findRepair">
      <a-button slot="right-content" type="primary" @click="showModal()">新增</a-button>
    </search-form>
    <a-table
      rowKey="id"
      :pagination="pagination"
      :loading="loading"
      bordered
      :columns="columns"
      :dataSource="dataList"
    >
      <template slot="createDate" slot-scope="createDate">{{createDate | date }}</template>
      <a-tag :color="scheduleMap[scope.schedule].color" slot="schedule" slot-scope="scope">{{ scheduleMap[scope.schedule].name }}</a-tag>
      <div slot="action" slot-scope="scope" class="text-right no-wrap">
        <a-button type="link" @click="showModal(scope)">修改</a-button>
        <a-divider type="vertical" />
        <a-button type="link" @click="deleteModal(scope.id)">刪除</a-button>
      </div>
    </a-table>
    <EditModal ref="EditModal"></EditModal>
  </div>
</template>
複製代碼

而後在 data 中定義兩個 searchForm 和 searchParams

data() {
    return {
      searchForm: [
        { name: "發起人", key: "name" },
        { name: "維修師傅", key: "repairerId", type: "select", options: [] },
        { name: "樓號", key: "floorId", type: "select", options: [] },
        { name: "宿舍", key: "roomId", type: "select", options: [] }
      ],
      searchParams: {
        pageNo: 1,
        pageSize: 15,
        name: "",
        repairerId: undefined,
        floorId: undefined,
        roomId: undefined
      }
    };
  },
複製代碼

searchParams 對象中有三個字段的值設置爲 undefined,這三個屬性是用於收集下拉框的選中值,因此不能夠給空字符串,而若是你不想這麼寫,你也可能直接寫成:

searchParams: {
  pageNo: 1,
  pageSize: 15,
  name: ""
}
複製代碼

兩種方式均可能,不過第一種方式相對來講會比較好一些,就是一看就知道這接口的請求參數都有哪些,有時寫代碼真的不能過於含蓄,要直白一點,畢竟公司裏都是團隊模式開發,要讓改你代碼的年輕小夥也能享受到如魚得水感受,而且可讓相似:「前人種樹,後人乘涼」 這樣的佳句一直傳承下去,而不是讓後來者接連 ***,何樂而不爲呢。

相關文章
相關標籤/搜索