Element-plus table 二次封裝

前言

Element 已經(jīng)提供簡潔轨帜、樣式優(yōu)雅魄咕、易用的table組件,可快速上手蚌父。項目中可通過<el-table>哮兰、<el-table-column>標(biāo)簽及其屬性快速生成一個個性化的獨立table毛萌,項目中有較多標(biāo)格展示的情況下,會出現(xiàn)一些問題:
1喝滞、每個頁面都有一整套風(fēng)格相似的table標(biāo)簽
2阁将、對同類型數(shù)據(jù)做處理時,每個頁面都需要引入公用的處理函數(shù)
3右遭、例如align這種屬性有默認值(左對齊)做盅,如果UI需要居中或居右,需要對每個<el-table-column>標(biāo)簽中的align屬性設(shè)置窘哈。

針對上面的問題言蛇,嘗試將el-table二次封裝成全局公共組件

需要保證:
1、組件輸入宵距、輸出數(shù)據(jù)格式清晰
2、可復(fù)用吨拗,貼合絕大部分應(yīng)用場景
3满哪、可擴展,1)可根據(jù)Element官方升級做對應(yīng)調(diào)整 2)可添加對數(shù)據(jù)的處理能力

創(chuàng)建table/index.vue劝篷,并注冊為全局組件

table/index.vue

<template>
  <el-table :data="tableData">
    <el-table-column prop="name" label="Name" width="180" />
    <el-table-column prop="address" label="Address" />
  </el-table>
</template>

<script lang="ts" setup>
  const tableData = [
  {
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  }
  ]
</script>

main.ts

import Table from '@/components/table/index.vue'

import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)

app.component('Table', Table)

app.mount('#app')

頁面中引用

<template>
  <Table></Table>
</template>
上面Table組件中哨鸭,展示了table的基礎(chǔ)結(jié)構(gòu),引用時會展示固定的列表娇妓,需要對Table進行改造
  1. 接收不同的數(shù)據(jù)源tableData
<template>
  <el-table :data="tableData">
    <el-table-column prop="name" label="Name" width="180" />
    <el-table-column prop="address" label="Address" />
  </el-table>
</template>

<script lang="ts" setup>
defineProps({
  tableData: {
    type: Array,
    default: function () {
      return []
    }
  }
})
</script>
  1. tableData只是全量源數(shù)據(jù)像鸡,我們需要一個可以描述el-table-column的集合,描述展示列的順序哈恰、名稱只估、字段、列寬等着绷。
  • 例如下面的格式
[
    // 根據(jù)el-table提供的列屬性進行擴展
    {
      prop: 'name', // 列數(shù)據(jù)參數(shù)
      label: '姓名', // 列名稱
      width: 180, // 列寬度蛔钙,空或不填寫表示自適應(yīng)
    },
]
  • 使用tableColumn接收該數(shù)據(jù),并通過v-for渲染
<template>
  <el-table :data="tableData">
    <el-table-column v-for="item in tableColumn" :key="item.prop" :prop="item.prop" :label="item.label" :width="item.width" />
  </el-table>
</template>

<script lang="ts" setup>
defineProps({
  tableData: {
    type: Array,
    default: function () {
      return []
    }
  },
  tableColumn: {
    type: Array,
    default: function () {
      return []
    }
  },
})
</script>
  • 列表中數(shù)據(jù)往往需要一些格式處理后再展示荠医,我們通過改造el-table-column吁脱,提供一些方案
    1)單純對字段數(shù)據(jù)進行處理
<template>
  <el-table :data="tableData">
    <el-table-column
      v-for="item in tableColumn"
      :key="item.prop"
      :prop="item.prop"
      :label="item.label"
      :width="item.width"
    >
      <template v-slot="scope">
        {{ dealField(scope.row[item.prop], item.format) }}
      </template>
    </el-table-column>
  </el-table>
</template>

<script lang="ts" setup>
import timeFormat from '@/utils/timeFormat'

function dealField(value, format) {
  if (!value && value !== 0) {
    return '-'
  }
  if (!format) {
    return value
  }
  const { type, rules } = format
  // 自定義字段處理邏輯
  // 時間格式
  if (type === 'time') {
    // 時間處理的公共函數(shù)
    return timeFormat(value, rules)
  }
  // 數(shù)據(jù)字典
  if (type === 'dictionaries') {
    let find = rules.find(item => item.value == value)
    if (find) {
      return find.label
    }
    return ''
  }
}
</script>

對應(yīng)fomart格式

  [
    {
      prop: 'name', // 列數(shù)據(jù)參數(shù)
      label: '姓名', // 列名稱
      width: '', // 列寬度,空或不填寫表示自適應(yīng)
      format: {
        type: 'time|dictionaries', // 數(shù)據(jù)處理類型
        rules: [{ label: '', value: '' }], // 定義數(shù)據(jù)字典
        ...
      }
    },
  ]

2)展示數(shù)據(jù)需要根據(jù)多個字段計算彬向,提供filter函數(shù)

<el-table-column
      v-for="item in tableColumn"
      :key="item.prop"
    >
      <template v-slot="scope">
        <div v-if="item.filter">{{ item.filter(scope.row) }}</div>
        <div v-else>
          {{ dealField(scope.row[item.prop], item.format) }}
        </div>
      </template>
    </el-table-column>

filte格式

[
   {
      filter: (row) => {
        let nickName = row.nickName ? `(曾用名:${row.nickName})`  : ''
        return row.name + nickName
      }
    },
]

3)單元格數(shù)據(jù)可點擊

<el-table-column
      v-for="item in tableColumn"
      :key="item.prop"
    >
      <template v-slot="scope">
        <div v-if="item.event">
          <el-button link type="primary" @click="item.event(scope.row)">{{
            dealField(scope.row[item.prop], item.format)
          }}</el-button>
        </div>
        <div v-else>
          {{ dealField(scope.row[item.prop], item.format) }}
        </div>
      </template>
    </el-table-column>

event格式

// tableColumn
 [
    {
      event: (row) => { fn(row) }, // 事件,數(shù)據(jù)可點擊;fn為觸發(fā)的函數(shù);row是當(dāng)前列
    },
  ]

function fn(row) {
}

4)較復(fù)雜情況兼贡,如同時需要特定樣式、觸發(fā)事件
4-1)具名作用域插槽

<el-table-column
      v-for="item in tableColumn"
      :key="item.prop"
    >
      <template v-slot="scope">
        <div v-if="item.slotName">
          <slot :name="item.slotName" :row="scope.row"></slot>
        </div>
      </template>
</el-table-column>

父組件

<template>
  <Table :tableAttributes="tableAttributes" :tableColumn="tableColumn" :tableData="list" @handleSelectionChange="personSelect1">
      <template #age="props">
        <el-button type="primary" @click="getAge(props.row.age)">{{ props.row.age }}</el-button>
      </template>
  </Table>
<template>

<script setup lang="ts">
  import { ref } from 'vue'
  const tableColumn = ref({
    {
      prop: 'age',
      label: '年齡',
      slotName: 'age'
    },
  })
</script>

4-2)渲染函數(shù) & JSX
創(chuàng)建render/index.ts

import { h } from 'vue'

export default {
  props: {
    render: Function,
    scope: Object,
  },
  setup(props) {
    // 返回渲染函數(shù)
    return () => h('div', {}, props.render(props.scope))
  }
}

table/index.vue引入

<template>
  <el-table
    :data="tableData"
  >
    <el-table-column
      v-for="item in tableColumn"
      :key="item.prop"
    >
      <template v-slot="scope">
        <div v-if="item.render">
          <HRender :render="item.render" :scope="scope"></HRender>
        </div>
      </template>
    </el-table-column>
  </el-table>
</template>

<script setup>
import HRender from '@/components/render/index.ts'
</script>

創(chuàng)建role.jsx

export function role(scope, event) {
  if (scope.row.perType == 1 || scope.row.perType == 0) {
    return (
      <el-tag type={ scope.row.perType == 1 ? 'info' : 'success' } onClick={ () => { event(scope.row) } }>{scope.row.perType == 1 ? '訪客' : '員工'}</el-tag>
    )
  } else {
    return (
      <span>-</span>
    )
  }
}

父組件中引入role.jsx

<script setup lang="ts">
  import { role } from './jsx/role.jsx'
  import { ref } from 'vue'
  const tableColumn = ref({
    {
      prop: 'age',
      label: '年齡',
      render: (scope) => {
        // 傳入數(shù)據(jù)和方法
        return role(scope, getPerType)
      }
    },
  }) 

 function getPerType(row) {
    console.log('獲取角色', row)
  }
</script>

如果不想額外創(chuàng)建jsx文件娃胆,可以使用tsx,將jsx寫在render函數(shù)內(nèi)部

// lang="tsx" 滿足同時使用ts和jsx
<script setup lang="tsx">
  import { ref } from 'vue'
  const tableColumn = ref({
    {
      prop: 'age',
      label: '年齡',
      render: (scope) => {
        if (scope.row.perType == 1 || scope.row.perType == 0) {
          return (
            <el-tag type={ scope.row.perType == 1 ? 'info' : 'success' } onClick={ () => { getPerType(scope.row) } }>{scope.row.perType == 1 ? '訪客' : '員工'}</el-tag>
          )
        } else {
          return (
            <span>-</span>
          )
        }
      }
    },
  }) 

 function getPerType(row) {
    console.log('獲取角色', row)
  }
</script>

5)針對操作列遍希,使用默認插槽
Table組件

<template>
  <el-table
    v-loading="tableAttributes.loading"
    :data="tableData"
    :height="tableAttributes.height"
    :max-height="tableAttributes.maxHeight"
    :stripe="tableAttributes.stripe || false"
    :border="tableAttributes.border || false"
    :fit="tableAttributes.fit || true"
  >

    <!-- 內(nèi)容 -->
    <el-table-column
      v-for="item in tableColumn"
      :key="item.prop"
      :fixed="item.fixed"
      :prop="item.prop"
      :label="item.label"
      :width="item.width"
      min-width="120"
      :align="item.align || 'center'"
    >
      <template v-slot="scope">
        <div v-if="item.event">
          <el-button link type="primary" @click="item.event(scope.row)">{{
            dealField(scope.row[item.prop], item.format)
          }}</el-button>
        </div>
        <div v-else-if="item.render">
          <HRender :render="item.render" :scope="scope"></HRender>
        </div>
        <div v-else-if="item.slotName">
          <slot :name="item.slotName" :row="scope.row"></slot>
        </div>
        <div v-else-if="item.filter">{{ item.filter(scope.row) }}</div>
        <div v-else>
          {{ dealField(scope.row[item.prop], item.format) }}
        </div>
      </template>
    </el-table-column>

    <!-- 插槽,用來自定義操作事件 -->
    <slot></slot>
  </el-table>
</template>

父組件

<template>
   <Table :tableAttributes="tableAttributes" :tableColumn="tableColumn" :tableData="list">
      <el-table-column
        label="操作"
      >
        <template v-slot="scope">
          <el-button @click="handleClick(scope.row)" link type="primary" size="small">查看</el-button>
          <el-button @click="edit(scope.row)" link type="primary" size="small">編輯</el-button>
        </template>
      </el-table-column>
    </Table>
</template>

<script setup lang="ts">
  function handleClick(row) {
    console.log('row', row.name)
  }
  function edit(row) {
    console.log('row', row)
  }
</script>

6)多選列
Table組件

<template>
  <el-table
    :data="tableData"
    @selection-change="handleSelectionChange"
  >
    <!-- 多選 -->
    <el-table-column
      v-if="tableAttributes.needSelection"
      type="selection"
      width="55"
      :align="tableAttributes.align || 'left'"
    >
    </el-table-column>
  </el-table>
</template>

<script setup>
defineProps({
  tableData: {
    type: Array,
    default: function () {
      return []
    }
  }
})

const emit = defineEmits(['handleSelectionChange'])

function handleSelectionChange(row) {
  emit('handleSelectionChange', row)
}
</script>

父組件

<template>
  <Table :tableAttributes="tableAttributes" :tableColumn="tableColumn" :tableData="list" @handleSelectionChange="personSelectChange">
  </Table>
</template>

<script setup lang="ts">
  import { ref } from 'vue'
  const tableAttributes = ref({
    needSelection: true, // 是否啟用多選
    align: 'center' // 多選列對其方式
  })

  // 處理表格選中更改
  function personSelectChange(row) {

  }
</script>
  1. 我們使用一個對象集合描述table的一些屬性
{
    height: '500',
    maxHeight: '500',
    stripe: true,
    border: false,
    fit: true,
    needSelection: false, // 是否啟用多選
    align: '', // 多選列對其方式里烦,left-左對齊孵班,center-居中涉兽,right-右對齊
    loading: false, // 表格加載時loading
  }
  • 使用tableAttributes接收
<template>
  <el-table 
    :data="tableData"
    v-loading="tableAttributes.loading"
    :height="tableAttributes.height"
    :max-height="tableAttributes.maxHeight"
    :stripe="tableAttributes.stripe || false"
    :border="tableAttributes.border || false"
    :fit="tableAttributes.fit || true"
  >
    <el-table-column v-for="item in tableColumn" :key="item.prop" :prop="item.prop" :label="item.label" :width="item.width" />
  </el-table>
</template>

<script lang="ts" setup>
defineProps({
  tableData: {
    type: Array,
    default: function () {
      return []
    }
  },
  tableColumn: {
    type: Array,
    default: function () {
      return []
    }
  },
  tableAttributes: {
    type: Object,
    default: function () {
      return {}
    }
  },
})
</script>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市篙程,隨后出現(xiàn)的幾起案子枷畏,更是在濱河造成了極大的恐慌,老刑警劉巖虱饿,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拥诡,死亡現(xiàn)場離奇詭異,居然都是意外死亡氮发,警方通過查閱死者的電腦和手機渴肉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來爽冕,“玉大人仇祭,你說我怎么就攤上這事【被” “怎么了乌奇?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長眯娱。 經(jīng)常有香客問我礁苗,道長,這世上最難降的妖魔是什么徙缴? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任试伙,我火速辦了婚禮,結(jié)果婚禮上于样,老公的妹妹穿的比我還像新娘疏叨。我一直安慰自己,他們只是感情好穿剖,可當(dāng)我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布考廉。 她就那樣靜靜地躺著,像睡著了一般携御。 火紅的嫁衣襯著肌膚如雪昌粤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天啄刹,我揣著相機與錄音涮坐,去河邊找鬼。 笑死誓军,一個胖子當(dāng)著我的面吹牛袱讹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼捷雕,長吁一口氣:“原來是場噩夢啊……” “哼椒丧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起救巷,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤壶熏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后浦译,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體棒假,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年精盅,在試婚紗的時候發(fā)現(xiàn)自己被綠了帽哑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡叹俏,死狀恐怖妻枕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情粘驰,我是刑警寧澤屡谐,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站晴氨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏碉输。R本人自食惡果不足惜籽前,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望敷钾。 院中可真熱鬧枝哄,春花似錦、人聲如沸阻荒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽侨赡。三九已至蓖租,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間羊壹,已是汗流浹背蓖宦。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留油猫,地道東北人稠茂。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像情妖,于是被迫代替她去往敵國和親睬关。 傳聞我的和親對象是個殘疾皇子诱担,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內(nèi)容