組件化頁面:封裝el-table

項目作的愈來愈多,重複的東西不斷的封裝成了組件,慢慢的,頁面就組件化了,只須要定義組件配置數據,使用就行了,這是一件很是舒服的事情,這篇文章主要和你們講講如何對element-ui中的el-table進行二次封裝。vue

分析需求

公有組件,能夠被任何頁面調用,首先要保證組件不和外部業務發生耦合,其次就是要設計合理的字段,使用時經過不一樣的配置便可使用。
那先大體來分析如下可能有的需求:git

  • 動態表頭
  • 嵌套表頭
  • 表格顯示內容類型自定義(文字,圖片,超連接等)
  • 動態接口加載數據
  • 表格和分頁聯動
  • 分頁和查詢數據聯動
  • 表格事件的處理
  • className, width, height...
  • 更多需求...

目前封裝的組件並不算完美,不可能知足因此需求,這裏的話主要仍是和你們分享思路github

動態表頭和嵌套表頭的實現

實現動態表頭,這個應該是許多使用table的朋友們的痛點,明明是同樣的東西,卻要寫多個表格,實在不能忍,讓咱們今天來一舉殲滅它。element-ui

分析表頭結構

el-table表頭有兩個必須的屬性,prop值和label名字,其餘非必須的有fixed,align,width或者min-width等,那由此可設計出一個這樣的數據結構:api

{
    prop: 'name',
    label: '名稱',
    fixed: true/false,
    align: 'center',
    minWidth: 160
}

進階->嵌套表格

上面咱們得出了普通表頭列的設計,那咱們繼續分析,看看嵌套表格配置多了哪些字段。
根據element-ui官網文檔,能夠看到前面字段基本同樣,嵌套表格多了children字段,用來循環子級表頭,那由此咱們能夠設計出這樣的數據結構:數據結構

{
    prop: 'name',
    label: '名稱',
    fixed: true/false,
    align: 'center',
    minWidth: 160,
    children: [
        {
            prop: 'oldName',
            label: '舊名稱',
            fixed: true/false,
            align: 'center',
            minWidth: 160,
        },
        {
            prop: 'newName',
            label: '新名稱',
            fixed: true/false,
            align: 'center',
            minWidth: 160,
        }
    ]
}

表頭設計總結

表頭設計思路大概是這樣,並不複雜,根據業務需求,你們均可以設計適合本身使用的字段。

完整的表頭設計字段應該大概會是這個樣子這個是我的字段配置的例子,其中將prop字段改爲了value, 下面代碼統一會使用value代替propdom

fieldList: [
          { label: '帳號', value: 'account' },
          { label: '用戶名', value: 'name' },
          { label: '所屬角色', value: 'role_name', minWidth: 120 },
          { label: '性別', value: 'sex', width: 80, list: 'sexList' },
          { label: '帳號類型', value: 'type', width: 100, list: 'accountTypeList' },
          { label: '狀態', value: 'status', width: 90, type: 'slot', list: 'statusList' },
          { label: '建立人', value: 'create_user_name' },
          { label: '建立時間', value: 'create_time', minWidth: 180 },
          { label: '更新人', value: 'update_user_name' },
          { label: '更新時間', value: 'update_time', minWidth: 180 }
        ]

表格顯示內容類型自定義

表頭設計只是將一些基本的需求實現了,可是實際業務每每更爲複雜,好比當前列要顯示的是圖片,tag,超連接,或者列的數據是一個id要顯示對應的label。組件化

字段列表擴展

以前定義的字段列表都是簡單的文字顯示,當有了不一樣的類型顯示需求,則意味着須要一個類型字段,type,根據業務需求,能夠設計知足image,tag,href等。
字段設計爲type爲image時,同時能夠考慮設計width和height字段。
字段設計爲href時,能夠同時設計顏色,跳轉方式字段。
好比:ui

{label: '設備信息', prop: 'deviceInfo', type: 'href', herf: 'https://www.baidu.com', target: '_blank'},
{label: '設備圖標', prop: 'deviceImage', type: 'image', src: 'https://www.baidu.com', height: '60px', width: 'auto'}

當列的數據是一個id的時候須要顯示對應的label,狀況又稍微複雜了一點,多種實現方法:this

  • 獲取到表格數據後對數據作處理,這個比較簡單,但須要在組件外部操做(不推薦)
  • 將對應的列表傳入組件中,在組件內部進行轉換(推薦)
  • 設置爲slot(好用,但建議使用在複雜的自定義場景,這個在下面會細講)

講講第二種方式,將對應的列表傳入組件中,在組件內部進行轉換,須要設置當前字段的類型爲id轉換爲label的類型,我在字段上定義的是type: select,而後要定義相關的list,字段設計大概長這樣:

{ label: '菜單組件', value: 'component', type: 'select', list: 'componentList1' }

個人實現方式是定義了一個listType對象,而後把頁面上用到的list都掛在了這個對象上面,將listType傳入到table組件中,經過listType[item.list]能夠獲取到字段對應列表而後獲取對應的label顯示。

slot

很是很是很是重要的slot,特別提醒你們,若是想寫複雜的組件,考慮到自定義類型,請必定去了解slot不瞭解的請戳
vue2.6+已經廢棄slot-scope官網api描述

插槽

  • 父級能夠向組件內部傳入dom,組件內部經過插槽接收
  • 渲染方式1: dom使用父級數據渲染,傳入組件
  • 渲染方式2: dom使用組件內部插槽穿出的數據渲染,再傳入組件

匿名插槽

父級在使用組件的時候,在組件標籤內編寫內容,將會組件內部<solt><slot/>接收到

具名插槽

父級設置傳入的插槽的名字,組件內部匹配到名字相同的插槽進行渲染。
組件內部具名插槽傳輸數據到父級(dom接收方,數據傳出方):

<!-- solt 自定義列-->
          <template v-if="item.type === 'slot'">
            <slot
              :name="'col-' + item.value"
              :row="scope.row"
            />
          </template>

父級獲取插槽數據渲染dom(dom傳出方,數據接收方):

<!-- 自定義插槽顯示狀態 -->
      <template v-slot:col-status="scope">
        <i
          :class="scope.row.status === 1 ? 'el-icon-check' : 'el-icon-close'"
          :style="{color: scope.row.status === 1 ? '#67c23a' : '#f56c6c', fontSize: '20px'}"
        />
      </template>

總結

slot是自定義組件的神器。
回到table組件,咱們須要自定義顯示內容,設計的字段應該以下:

{ label: '菜單圖標', value: 'icon', type: 'slot' }

動態接口加載數據

上面說的都是顯示字段設計的東西,如今開始分析表格的數據,從哪裏來,到哪裏去。

若是要偷懶,那麼必定是要把懶偷到底的,有一丁點多餘的工做要作,都是偷懶不成功的。

組件內部加載數據

須要什麼:

  • 接口
  • 數據響應成功後在response的哪一個字段上面
  • 怎麼刷新接口
  • 是否分頁,分頁初始化

接口

定義一個api字段,將須要請求的接口傳入到組件中,若是有相關參數,須要同時將參數傳入到組件中

數據所在字段

定義一個resFieldList,好比數據在res.content.data上,則傳入數據:

resFieldList: ['content', ‘data’] // 數據所在字段

組件內部則須要在接口請求成功以後作這樣一步操做:

let resData = res
          const resFieldList = tableInfo.resFieldList
          // 獲得定義的響應成功的數據字段
          for (let i = 0; i < resFieldList.length; i++) {
            resData = resData[resFieldList[i]]
          }

數據獲取成功以後,建議使用父子組件雙向通訊,.sync或者自定義model均可以實現,將數據派發到父組件,而後由父組件傳入子組件渲染組件。
直接由組件內部獲取數據而且渲染可能會須要擴展等問題限制組件的使用範圍。

刷新接口

定義一個refresh字段,刷新頁面只須要設置爲:

// 刷新表格
tableInfo.refresh = Math.random()

而組件內部watch字段change,從新調獲取數據的接口,便可實現刷新功能

分頁相關設置

  • 是否分頁,設置字段好比 pager: true/false
  • 是否初始化分頁,設置字段好比 initCurpage = Math.random() // 刷新則重置

組件事件處理

分析有哪幾種類型的事件:

  • 表頭點擊事件
  • 列點擊事件
  • 表格操做欄點擊事件
  • 多選
  • ....

事件中間件的設計

不一樣的業務可能涉及到各類類型的事件,若是封裝成爲了組件,怎麼處理???
換一個思路,咱們把事件看做是一個類型操做,好比點擊是click,刪除是delete,那咱們只須要一個事件轉發器,好比:

// 數據渲染事件的派發
this.$emit('handleEvent', 'list', arr)
// 表格選擇事件的派發
this.$emit('handleEvent', 'tableCheck', rows)
// 點擊事件的派發
this.$emit('handleClick', event, data)

咱們定義事件中間件,組件內部發生事件時將事件的類型還有相關的數據派發,父級接收而且處理。

組件完整字段和使用

字段

  • refresh 刷新數據
  • api 數據接口
  • resFieldList 數據成功的響應字段
  • pager 是否分頁
  • initCurpage 初始化分頁
  • data 表格數據
  • fieldList 字段列表
  • handle 操做欄配置
// 表格相關
      tableInfo: {
        refresh: 1,
        initCurpage: 1,
        data: [],
        fieldList: [
          { label: '帳號', value: 'account' },
          { label: '用戶名', value: 'name' },
          { label: '所屬角色', value: 'role_name', minWidth: 120 },
          { label: '性別', value: 'sex', width: 80, list: 'sexList' },
          { label: '帳號類型', value: 'type', width: 100, list: 'accountTypeList' },
          { label: '狀態', value: 'status', width: 90, type: 'slot', list: 'statusList' },
          { label: '建立人', value: 'create_user_name' },
          { label: '建立時間', value: 'create_time', minWidth: 180 },
          { label: '更新人', value: 'update_user_name' },
          { label: '更新時間', value: 'update_time', minWidth: 180 }
        ],
        handle: {
          fixed: 'right',
          label: '操做',
          width: '280',
          btList: [
            { label: '啓用', type: 'success', icon: 'el-icon-albb-process', event: 'status', loading: 'statusLoading', show: false, slot: true },
            { label: '編輯', type: '', icon: 'el-icon-edit', event: 'update', show: false },
            { label: '刪除', type: 'danger', icon: 'el-icon-delete', event: 'delete', show: false }
          ]
        }
      }

使用

<!-- 表格 -->
    <page-table
      :refresh="tableInfo.refresh"
      :init-curpage="tableInfo.initCurpage"
      :data.sync="tableInfo.data"
      :api="getListApi"
      :query="filterInfo.query"
      :field-list="tableInfo.fieldList"
      :list-type-info="listTypeInfo"
      :handle="tableInfo.handle"
      @handleClick="handleClick"
      @handleEvent="handleEvent"
    >
      <!-- 自定義插槽顯示狀態 -->
      <template v-slot:col-status="scope">
        <i
          :class="scope.row.status === 1 ? 'el-icon-check' : 'el-icon-close'"
          :style="{color: scope.row.status === 1 ? '#67c23a' : '#f56c6c', fontSize: '20px'}"
        />
      </template>
      <!-- 自定義插槽狀態按鈕 -->
      <template v-slot:bt-status="scope">
        <el-button
          v-if="scope.data.item.show && (!scope.data.item.ifRender || scope.data.item.ifRender(scope.data.row))"
          v-waves
          size="mini"
          :type="scope.data.row.status - 1 >= 0 ? 'danger' : 'success'"
          :icon="scope.data.item.icon"
          :disabled="scope.data.item.disabled"
          :loading="scope.data.row[scope.data.item.loading]"
          @click="handleClick(scope.data.item.event, scope.data.row)"
        >
          {{ scope.data.row.status - 1 >= 0 ? '停用' : '啓用' }}
        </el-button>
      </template>
    </page-table>

最後

演示地址

github

相關文章
相關標籤/搜索