elementUI?el-table二次封裝的詳細實例
前言
很多中后臺業(yè)務的系統(tǒng)中,表格是最高頻的組件之一,其中一般包括搜索條件、表格展示、表格操作列、分頁等。那么我們二次封裝的這個表格組件就需要包含以下幾個功能點:
1、數(shù)據(jù)自動獲取和刷新
2、自定義列配置
3、分頁功能
4、根據(jù)搜索條件進行搜索功能
5、加載中狀態(tài)和空數(shù)據(jù)狀態(tài)
一、先上頁面最終效果

二、創(chuàng)建目錄yxt-table如下圖
index.vue為父級頁面
yxt-table.vue為表格組件

二、數(shù)據(jù)自動獲取和刷新
因為表格的數(shù)據(jù)一般都比較簡單,就是根據(jù)搜索條件,調(diào)用接口請求一個到列表,然后將列表數(shù)據(jù)一一展示到表格上,再對特定的列進行一些過濾轉(zhuǎn)化等個性化操作。但是萬變不離其宗,這個步驟基本每個表格都要進行一遍,所以考慮到通用性(其實是為了偷懶),將請求接口獲取數(shù)據(jù)這一步放在組件里面實現(xiàn)。
created () {
this.getData()
},
methods: {
getData () {
const fun = this.apiUrl
fun().then(res => {
this.tableData = res[this.otherConfig.list] || []
this.tableTotal = res.pageInfo?.total || 0
})
}
}三、自定義列配置
組件接收一個數(shù)組作為自定義列
tableColumn: [
{ prop: 'name', label: '名稱' },
{ prop: 'code', label: '編碼' },
{ prop: 'status', label: '狀態(tài)' }
]index.vue
<!-- index.vue -->
<template>
<div>
<yxt-table :apiUrl="yxtTableList"
:tableColumn="tableColumn"></yxt-table>
</div>
</template>
<!-- index.vue -->
<script>
import yxtTable from './yxt-table.vue'
import { yxtTableList } from 'https/yxtDemo.js'
export default {
name: 'yxtDemoTable',
components: {
yxtTable
},
data () {
return {
yxtTableList,
tableColumn: [
{ prop: 'name', label: '名稱' },
{ prop: 'code', label: '編碼' },
{ prop: 'status', label: '狀態(tài)' }
]
}
}
}
</script>yxt-table.vue
<!-- yxt-table.vue -->
<template>
<div>
<el-table :data="tableData">
<el-table-column v-for="item in tableColumn"
:key="item.prop"
:prop="item.prop"
:label="item.label"></el-table-column>
</el-table>
</div>
</template>
<!-- yxt-table.vue -->
<script>
export default {
name: 'yxtTable',
props: {
apiUrl: { // 列表接口(必填)
type: Function,
required: true
},
tableColumn: { // 自定義列配置
type: Array,
default: () => []
},
otherConfig: { //
type: Object,
default: () => {
return {
list: 'list'
}
}
}
},
data () {
return {
tableData: []
}
},
created () {
this.getData()
},
methods: {
getData () {
const fun = this.apiUrl
fun().then(res => {
this.tableData = res[this.otherConfig.list] || []
this.tableTotal = res.pageInfo?.total || 0
})
}
}
}
</script>至此,一個表格可以實現(xiàn)了

1、otherConfig說明
由于我們的接口請求放在組件里面了,但是我們對接的接口可能由于業(yè)務的不同項目組的不同開發(fā)人員的不同,而導致接口返回的列表的字段名不同,這里通過傳參的形式做一下兼容
2、上面這樣只能實現(xiàn)簡單的展示功能,還有些數(shù)據(jù)比如 狀態(tài)1 需要轉(zhuǎn)化為打卡成功,狀態(tài)0 需要轉(zhuǎn)化為打卡失敗進行顯示,這類需求可以通過filter進行轉(zhuǎn)化
<!-- yxt-table.vue -->
<el-table-column v-for="item in tableColumn"
:key="item.prop"
:prop="item.prop"
:label="item.label">
<template v-slot:default="scope">
<div v-if="item.dictCode">
{{ scope.row[item.prop] | filterStatus(dict[item.dictCode]) }}
</div>
<div v-else>
{{ scope.row[item.prop] }}
</div>
</template>
</el-table-column>
props: {
dict: { // 全部字典
type: Object,
default: () => {}
}
},
filters: {
filterStatus (value, array, code = 'code', name = 'name') {
if (!value && value !== 0) { // 要把0摘出來,一般0都是正常的數(shù)據(jù),所以不能只用 !value
return ''
}
const find = array.find(e => (e[code] === value.toString()) || (e[code] === +value)) // 字符型數(shù)值型都得匹配
if (find) {
return find[name]
} else { // 沒有匹配的就原樣返回
return value
}
}
},<!-- index.vue -->
<yxt-table :apiUrl="yxtTableList"
:tableColumn="tableColumn"
:dict="dict"></yxt-table>
data () {
return {
tableColumn: [
{ prop: 'name', label: '名稱' },
{ prop: 'code', label: '編碼' },
{ prop: 'status', label: '狀態(tài)', dictCode: 'status' }
],
dict: {
status: [
{ code: 0, name: '打卡失敗' },
{ code: 1, name: '打卡成功' }
]
}
}
}
這里dict設置為對象的原因是為了裝進更多字典
3、思考一下,如果要在表格中展示這樣的自定義圖標怎么辦?

使用插槽slot,在tableColumn里面設置某行屬性的slot為true,改造el-table-column如下:
<!-- yxt-table.vue -->
<el-table-column v-for="(item, index) in tableColumn"
:key="index"
:prop="item.prop"
:label="item.label">
<template v-if="item.slot"
v-slot:default="scope">
<slot :name="item.prop"
:row="scope.row"
:index="scope.$index"></slot>
</template>
<template v-else v-slot:default="scope">
<div v-if="item.dictCode">
{{ scope.row[item.prop] | filterStatus(dict[item.dictCode]) }}
</div>
<div v-else>
{{ scope.row[item.prop] }}
</div>
</template>
</el-table-column><!-- index.vue -->
<yxt-table :apiUrl="yxtTableList"
:tableColumn="tableColumn"
:otherConfig="otherConfig"
:dict="dict">
<template v-slot:icon="{row, index}">
<i :class="row.status ? 'el-icon-circle-check' : 'el-icon-circle-close'"></i>
</template>
</yxt-table>
data () {
return {
tableColumn: [
{ prop: 'name', label: '名稱' },
{ prop: 'code', label: '編碼' },
{ prop: 'status', label: '狀態(tài)', dictCode: 'status' },
{ prop: 'icon', label: '圖標', slot: true }
]
}
}4、在實際項目中,除了字典轉(zhuǎn)化,還有一些比較定制化的展示需求,這個可以通過傳入一個函數(shù)format進行計算,然后在這個方法里面將最后的計算結(jié)果return
<!-- yxt-table.vue -->
<el-table-column v-for="(item, index) in tableColumn"
:key="index"
:prop="item.prop"
:label="item.label">
<template v-if="item.slot"
v-slot:default="scope">
<slot :name="item.prop"
:row="scope.row"
:index="scope.$index"></slot>
</template>
<template v-else v-slot:default="scope">
<div v-if="item.dictCode">
{{ scope.row[item.prop] | filterStatus(dict[item.dictCode]) }}
</div>
<div v-else-if="item.format">
{{ item.format(scope.row) }}
</div>
<div v-else>
{{ scope.row[item.prop] }}
</div>
</template>
</el-table-column><!-- index.vue -->
data () {
return {
tableColumn: [
{ prop: 'name', label: '名稱' },
{ prop: 'code', label: '編碼' },
{ prop: 'status', label: '狀態(tài)', dictCode: 'status' },
{ prop: 'icon', label: '圖標', slot: true },
{ prop: 'phone',
label: '電話號碼',
format: (row) => {
return `${row.name}-${row.code}(${row.phone})`
} }
]
}
}
5、表格一般還有批量操作,所以需要多選和單選以及針對特定場景設置禁選

yxt-table.vue
<!-- yxt-table.vue -->
<template>
<div class="yxt-table">
<!-- 批量操作按鈕,因為每個需求不同,批量操作的功能也不同,所以這里只放一個插槽,不設置默認內(nèi)容,所有按鈕均在父級設置 -->
<div class="multiple-operation">
<slot name="multiple-operation"
:selectionData="selectionData"></slot>
</div>
<!-- 頁面主表格 -->
<el-table :data="tableData"
:row-key="rowKey"
@selection-change="selectionChange">
<!-- 可選框(多選) -->
<el-table-column v-if="selection === 'multiple'"
type="selection"
align="center"
width="55"
:reserve-selection="rowKey ? true : false"
:selectable="selectable"/>
<!-- 可選框(單選) -->
<el-table-column v-else-if="selection === 'single'"
align="center"
width="30">
<template v-slot:default="scope">
<el-radio v-model="selectionRadio"
:label="scope.$index"
:disabled="selectable ? !selectable(scope.row) : false"
@change="selectionChangeSingle(scope.row)">
{{ '' }}
</el-radio>
</template>
</el-table-column>
<el-table-column v-for="(item, index) in tableColumn"
:key="index"
:prop="item.prop"
:label="item.label">
<template v-if="item.slot"
v-slot:default="scope">
<slot :name="item.prop"
:row="scope.row"
:index="scope.$index"></slot>
</template>
<template v-else v-slot:default="scope">
<div v-if="item.dictCode">
{{ scope.row[item.prop] | filterStatus(dict[item.dictCode]) }}
</div>
<div v-else-if="item.format">
{{ item.format(scope.row) }}
</div>
<div v-else>
{{ scope.row[item.prop] }}
</div>
</template>
</el-table-column>
</el-table>
</div>
</template>
<!-- yxt-table.vue -->
<script>
export default {
name: 'yxtTable',
props: {
apiUrl: { // 列表接口(必填)
type: Function,
required: true
},
tableColumn: { // 自定義列配置
type: Array,
default: () => []
},
otherConfig: { // 其他配置
type: Object,
default: () => {
return {
list: 'list' // 接口返回數(shù)據(jù)的列表字段的字段名(因為在組件里面調(diào)接口,可能不同業(yè)務不同項目組不同一個開發(fā)者返回給前端的參數(shù)名不一致,這里進行兼容)
}
}
},
dict: { // 全部字典
type: [Array, Object],
default: () => []
},
selection: { // 是否顯示可選框(多選-multiple 、單選-single )
type: String
},
selectable: { // 當前行是否可選擇
type: Function
},
rowKey: { // 表格唯一key(適用于分頁多選表格,保留之前的選擇,不傳則為單頁選擇)
type: [Number, String, Function],
default: ''
}
},
filters: {
filterStatus (value, array, code = 'code', name = 'name') {
if (!value && value !== 0) { // 要把0摘出來,一般0都是正常的數(shù)據(jù),所以不能只用 !value
return ''
}
const find = array.find(e => (e[code] === value.toString()) || (e[code] === +value)) // 字符型數(shù)值型都得匹配
if (find) {
return find[name]
} else { // 沒有匹配的就原樣返回
return value
}
}
},
data () {
return {
tableData: [],
tableTotal: 0,
selectionRadio: '',
selectionData: []
}
},
created () {
this.getData()
},
methods: {
getData () {
const fun = this.apiUrl
fun().then(res => {
this.tableData = res[this.otherConfig.list] || []
this.tableTotal = res.pageInfo?.total || 0
})
},
// 多選,選擇行數(shù)據(jù)change
selectionChange (selection) {
this.selectionData = selection
},
// 單選,選擇行數(shù)據(jù)change
selectionChangeSingle (selection) {
this.selectionData = [selection]
}
}
}
</script>
<style scoped lang="scss">
.yxt-table {
margin: 30px;
.multiple-operation {
margin-bottom: 10px;
}
}
</style>index.vue
<!-- index.vue -->
<template>
<div>
<yxt-table :apiUrl="yxtTableList"
:tableColumn="tableColumn"
:otherConfig="otherConfig"
:dict="dict"
selection="multiple"
:selectable="isSelectable">
<!-- 圖標插槽 -->
<template v-slot:icon="{row, index}">
<i :class="row.status ? 'el-icon-circle-check' : 'el-icon-circle-close'"></i>
</template>
<!-- 批量操作按鈕插槽 -->
<template v-slot:multiple-operation="{selectionData}">
<el-button type="primary"
size="small"
@click="handleClick1(selectionData)">批量操作1</el-button>
<el-button type="success"
size="small"
@click="handleClick2(selectionData)">批量操作2</el-button>
</template>
</yxt-table>
<yxt-table :apiUrl="yxtTableList"
:tableColumn="tableColumn"
:otherConfig="otherConfig"
:dict="dict"
selection="single"
:selectable="isSelectable">
<!-- 圖標插槽 -->
<template v-slot:icon="{row, index}">
<i :class="row.status ? 'el-icon-circle-check' : 'el-icon-circle-close'"></i>
</template>
<!-- 批量操作按鈕插槽 -->
<template v-slot:multiple-operation="{selectionData}">
<el-button type="primary"
size="small"
@click="handleClick1(selectionData)">單選操作</el-button>
</template>
</yxt-table>
</div>
</template>
<!-- index.vue -->
<script>
import yxtTable from './yxt-table.vue'
import { yxtTableList } from 'https/yxtDemo.js'
export default {
name: 'yxtDemoTable',
components: {
yxtTable
},
data () {
return {
yxtTableList,
tableColumn: [
{ prop: 'name', label: '名稱' },
{ prop: 'code', label: '編碼' },
{ prop: 'status', label: '狀態(tài)', dictCode: 'status' },
{ prop: 'icon', label: '圖標', slot: true },
{ prop: 'phone',
label: '電話號碼',
format: (row) => {
return `${row.name}-${row.code}(${row.phone})`
} }
],
tableConfig: {
stripe: 'stripe',
border: 'border',
height: '200',
maxHeight: '200',
showHeader: true
},
otherConfig: {
list: 'tasks'
},
dict: {
status: [
{ code: 0, name: '打卡失敗' },
{ code: 1, name: '打卡成功' }
]
}
}
},
methods: {
handleClick1 (selectionData) {
console.log('1', selectionData)
},
handleClick2 (selectionData) {
console.log('2', selectionData)
},
isSelectable (row) {
return row.selectable !== 0
}
}
}
</script>
<style scoped lang="scss">
.el-icon-circle-check {
font-size: 28px;
color: #67C23A;
}
.el-icon-circle-close {
font-size: 28px;
color: #F00;
}
</style>6、操作列
根據(jù)業(yè)務需求,可以在操作列設置幾個默認按鈕,通過setupConfig設置開關(guān),如果有除了默認按鈕之外的操作需求,再通過插槽slot進行插入
<!-- yxt-table.vue -->
<!-- 操作列 -->
<el-table-column v-if="setupConfig.width !== 0"
:fixed="setupConfig.fixed"
:width="setupConfig.width"
label="操作">
<template v-slot:default="scope">
<slot name="setup"
:row="scope.row"
:index="scope.$index"></slot>
<!-- 查看 -->
<el-button v-if="setupConfig.view"
type="text"
@click="setupEvents('view', scope.row)">查看</el-button>
<!-- 編輯 -->
<el-button v-if="setupConfig.edit"
type="text"
@click="setupEvents('edit', scope.row)">編輯</el-button>
<!-- 刪除 -->
<el-button v-if="setupConfig.del"
type="text"
@click="setupEvents('del', scope.row)">刪除</el-button>
<!-- 操作日志 -->
<el-button v-if="setupConfig.log"
type="text"
@click="setupEvents('log', scope.row)">操作日志</el-button>
</template>
</el-table-column>
props: {
setupConfig: {
type: Object,
default: () => {
return {
width: 'auto'
}
}
}
},
methods: {
setupEvents (setupType, row) { // 操作列方法 查看/編輯/刪除/操作日志
this.$emit(setupType, row)
}
}
index.vue做相應的處理,這里不貼代碼了
7、分頁
pagination控制是否需要分頁組件,如果不需要分頁則設置為false。根據(jù)業(yè)務需求,可傳入pageSizes控制條數(shù)下拉框的條數(shù)選項
<!-- yxt-table.vue -->
<!-- 分頁 -->
<el-pagination v-if="pagination"
class="pagination tablePage"
:pager-count="5"
:page-sizes="pageSizes || [10, 20, 50, 100]"
:total="tableTotal || 0"
:page-size="pageInfo.pageSize || 10"
:current-page="pageInfo.startPage || 1"
layout="total, sizes, prev, pager, next, jumper"
@size-change="sizeChange"
@current-change="pageChange"></el-pagination>
props: {
pagination: { // 是否需要分頁,默認需要
type: Boolean,
default: true
},
pageSizes: {
type: Array
}
},
methods: {
getData () {
const fun = this.apiUrl
const pageInfo = { // 分頁信息
pageSize: this.pageInfo.pageSize,
startPage: this.pageInfo.startPage
}
let param = { // 其他的搜素條件
}
if (this.pagination) { // 如果需要分頁,則傳分頁信息
param = { ...param, ...pageInfo }
}
fun(param).then(res => {
this.tableData = res[this.otherConfig.list] || []
this.tableTotal = res.pageInfo?.total || 0
})
},
// 條數(shù)變化
sizeChange (size) {
this.pageInfo.startPage = 1
this.pageInfo.pageSize = size
this.getData()
},
// 頁碼變化
pageChange (page) {
this.pageInfo.startPage = page
this.getData()
}
}
8、el-table還有一個展開行功能expand,根據(jù)業(yè)務需求,也可以加進組件里
<!-- yxt-table.vue -->
<!-- 展開行 -->
<el-table-column v-if="expand"
type="expand">
<template v-slot:default="scope">
<slot name="expand"
:row="scope.row"
:index="scope.$index"></slot>
</template>
</el-table-column>
props: {
expand: { // 是否展開行
type: Boolean,
default: false
}
}<!-- index.vue -->
<yxt-table :apiUrl="yxtTableList"
:tableColumn="tableColumn"
:expand="true">
<template v-slot:expand="{row, index}">
<div>
<p>序號:{{index}}</p>
<p>內(nèi)容:{{row}}</p>
</div>
</template>
</yxt-table>
四、根據(jù)搜索條件進行搜索更新表格數(shù)據(jù)
新增一個yxt-search.vue

<!-- yxt-search.vue -->
<template>
<div class="yxt-search">
<div v-for="(item,index) in searchConfig"
:key="index"
class="yxt-search-item">
<el-input v-if="item.type==='input'"
v-model="searchModel[item.key]"
size="medium"
:clearable="item.clearable || true"
:placeholder="item.placeholder || '請輸入'"
:maxlength="item.maxlength"></el-input>
<el-select v-if="item.type==='select'"
v-model="searchModel[item.key]"
size="medium"
style="width: 100%"
:clearable="item.clearable || true"
:filterable="item.filterable || true"
:disabled="item.disabled || false"
:multiple="item.multiple || false"
:allow-create="item.allowCreate"
:placeholder="item.placeholder || '請選擇'">
<el-option v-for="(selectItem, selectIndex) in item.selectList"
:key="selectIndex"
:label="selectItem[item.listLabel]"
:value="selectItem[item.listValue]"></el-option>
</el-select>
</div>
<div v-if="searchConfig.length" class="yxt-search-button">
<el-button size="medium" type="primary" @click="search">搜索</el-button>
<el-button size="medium" type="primary" plain @click="reset">重置</el-button>
<!-- 其他的按鈕需求通過插槽傳入 -->
<slot name="searchBtn" :searchData="searchModel"></slot>
</div>
</div>
</template>
<!-- yxt-search.vue -->
<script>
export default {
name: 'yxtSearch',
props: {
searchConfig: { // 搜索條件配置項
type: Array,
required: true,
default () {
return []
}
},
searchModel: { // 搜索條件綁定值
type: Object,
required: true,
default () {
return {}
}
},
searchReset: { // 搜索條件默認值重置值
type: Object
}
},
data () {
return {
}
},
methods: {
search () {
this.$emit('search', this.searchModel)
},
reset () {
if (this.searchReset) { // 如果傳入有默認值,則重置后為默認值
Object.keys(this.searchModel).forEach((item) => {
this.searchModel[item] = this.searchReset[item]
})
} else {
Object.keys(this.searchModel).forEach((item) => {
this.searchModel[item] = ''
})
}
}
}
}
</script>
<style scoped lang="scss">
.yxt-search {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
.yxt-search-item {
flex: 1;
margin: 0 10px 10px 0;
width: calc((100% - 30px) / 4); // 這里的30px = (分布個數(shù)4-1)*間隙1px, 可以根據(jù)實際的分布個數(shù)和間隙區(qū)調(diào)整
min-width: calc((100% - 30px) / 4);
max-width: calc((100% - 30px) / 4);
&:nth-child(4n) { // 去除每行最后一個(第4n個)的margin-right
margin-right: 0;
}
}
.yxt-search-button {
margin: 0 0 10px 0;
width: 100%;
text-align: right;
}
}
</style><!-- yxt-table.vue -->
<yxt-search :searchConfig="searchConfig"
:searchModel="searchModel"
:searchReset="searchReset"
@search="getData(1)">
<template v-slot:searchBtn="{searchData}">
<!-- 其他的按鈕需求通過插槽傳入 -->
<slot name="searchBtn" :searchData="searchData"></slot>
</template>
</yxt-search>
props: {
searchConfig: { // 搜索條件配置項
type: Array,
default () {
return []
}
},
searchReset: { // 搜索條件默認值重置值
type: Object
}
},
data () {
return {
searchModel: this.searchReset ? JSON.parse(JSON.stringify(this.searchReset)) : {}
}
},
methods: {
getData (startPage) {
if (startPage) { // 如果傳入值,則從改值的頁碼數(shù)開始
this.pageInfo.startPage = startPage
}
let param = { // 其他的搜素條件
...this.searchModel
}
...
}
}<!-- index.vue -->
<yxt-table :apiUrl="yxtTableList"
:tableColumn="tableColumn"
:searchConfig="searchConfig"
:searchReset="searchReset">
<template v-slot:searchBtn="{searchData}">
<el-button size="medium" type="success" @click="handleClickExport(searchData)">導出</el-button>
</template>
</yxt-table>
data () {
return {
searchConfig: [
{ type: 'input', key: 'name' },
{ type: 'input', key: 'code' },
{ type: 'select',
key: 'status',
selectList: [
{ code: 0, name: '打卡失敗' },
{ code: 1, name: '打卡成功' }
],
listLabel: 'name',
listValue: 'code' }
],
searchReset: {
name: '張三',
code: '',
status: 1
}
}
},
methods: {
handleClickExport (data) {
console.log(data)
}
}五、加載中狀態(tài)和空數(shù)據(jù)狀態(tài)
加載中:el-table 添加 v-loading="loading",getData里面,發(fā)送請求之前設置為true,獲得數(shù)據(jù)后設置為false

空數(shù)據(jù):通過插槽empty設置

六、完整代碼:
index.vue
<!-- index.vue -->
<template>
<div>
<yxt-table :apiUrl="yxtTableList"
:tableColumn="tableColumn"
:otherConfig="otherConfig"
:dict="dict"
selection="multiple"
:selectable="isSelectable"
:setupConfig="setupConfig"
:searchConfig="searchConfig"
:searchReset="searchReset"
@view="view"
@log="log">
<!-- 圖標插槽 -->
<template v-slot:icon="{row, index}">
<i :class="row.status ? 'el-icon-circle-check' : 'el-icon-circle-close'"></i>
</template>
<!-- 批量操作按鈕插槽 -->
<template v-slot:multiple-operation="{selectionData}">
<el-button type="primary"
size="small"
@click="handleClick1(selectionData)">批量操作1</el-button>
<el-button type="success"
size="small"
@click="handleClick2(selectionData)">批量操作2</el-button>
</template>
<template v-slot:searchBtn="{searchData}">
<el-button size="medium" type="success" @click="handleClickExport(searchData)">導出</el-button>
</template>
</yxt-table>
<yxt-table :apiUrl="yxtTableList"
:tableColumn="tableColumn"
:otherConfig="otherConfig"
:dict="dict"
selection="single"
:selectable="isSelectable"
:setupConfig="setupConfig2"
:pagination="false"
:expand="true"
:emptyText="'沒有數(shù)據(jù)的展示文字'">
<!-- 圖標插槽 -->
<template v-slot:icon="{row, index}">
<i :class="row.status ? 'el-icon-circle-check' : 'el-icon-circle-close'"></i>
</template>
<!-- 批量操作按鈕插槽 -->
<template v-slot:multiple-operation="{selectionData}">
<el-button type="primary"
size="small"
@click="handleClick1(selectionData)">單選操作</el-button>
</template>
<template v-slot:expand="{row, index}">
<div>
<p>序號:{{index}}</p>
<p>內(nèi)容:{{row}}</p>
</div>
</template>
</yxt-table>
</div>
</template>
<!-- index.vue -->
<script>
import yxtTable from './yxt-table.vue'
import { yxtTableList } from 'https/yxtDemo.js'
export default {
name: 'yxtDemoTable',
components: {
yxtTable
},
data () {
return {
yxtTableList,
tableColumn: [
{ prop: 'name', label: '名稱' },
{ prop: 'code', label: '編碼' },
{ prop: 'status', label: '狀態(tài)', dictCode: 'status' },
{ prop: 'icon', label: '圖標', slot: true },
{ prop: 'phone',
label: '電話號碼',
format: (row) => {
return `${row.name}-${row.code}(${row.phone})`
} }
],
tableConfig: {
stripe: 'stripe',
border: 'border',
height: '200',
maxHeight: '200',
showHeader: true
},
otherConfig: {
list: 'tasks'
},
setupConfig: {
width: 100,
view: true,
log: true
},
setupConfig2: {
edit: true,
del: true,
log: true
},
dict: {
status: [
{ code: 0, name: '打卡失敗' },
{ code: 1, name: '打卡成功' }
]
},
searchConfig: [
{ type: 'input', key: 'name' },
{ type: 'input', key: 'code' },
{ type: 'select',
key: 'status',
selectList: [
{ code: 0, name: '打卡失敗' },
{ code: 1, name: '打卡成功' }
],
listLabel: 'name',
listValue: 'code' }
],
searchReset: {
name: '張三',
code: '',
status: 1
}
}
},
methods: {
handleClick1 (selectionData) {
console.log('1', selectionData)
},
handleClick2 (selectionData) {
console.log('2', selectionData)
},
handleClickExport (data) {
console.log(data)
},
isSelectable (row) {
return row.selectable !== 0
},
view (row) {
console.log('view', row)
},
log (row) {
console.log('log', row)
}
}
}
</script>
<style scoped lang="scss">
.el-icon-circle-check {
font-size: 28px;
color: #67C23A;
}
.el-icon-circle-close {
font-size: 28px;
color: #F00;
}
</style>yxt-table.vue
<!-- yxt-table.vue -->
<template>
<div class="yxt-table">
<yxt-search :searchConfig="searchConfig"
:searchModel="searchModel"
:searchReset="searchReset"
@search="getData(1)">
<template v-slot:searchBtn="{searchData}">
<!-- 其他的按鈕需求通過插槽傳入 -->
<slot name="searchBtn" :searchData="searchData"></slot>
</template>
</yxt-search>
<!-- 批量操作按鈕,因為每個需求不同,批量操作的功能也不同,所以這里只放一個插槽,不設置默認內(nèi)容,所有按鈕均在父級設置 -->
<div class="multiple-operation">
<slot name="multiple-operation"
:selectionData="selectionData"></slot>
</div>
<!-- 頁面主表格 -->
<el-table :data="tableData"
:row-key="rowKey"
v-loading="loading"
@selection-change="selectionChange">
<!-- 可選框(多選) -->
<el-table-column v-if="selection === 'multiple'"
type="selection"
align="center"
width="55"
:reserve-selection="rowKey ? true : false"
:selectable="selectable"/>
<!-- 可選框(單選) -->
<el-table-column v-else-if="selection === 'single'"
align="center"
width="30">
<template v-slot:default="scope">
<el-radio v-model="selectionRadio"
:label="scope.$index"
:disabled="selectable ? !selectable(scope.row) : false"
@change="selectionChangeSingle(scope.row)">
{{ '' }}
</el-radio>
</template>
</el-table-column>
<!-- 展開行 -->
<el-table-column v-if="expand"
type="expand">
<template v-slot:default="scope">
<slot name="expand"
:row="scope.row"
:index="scope.$index"></slot>
</template>
</el-table-column>
<el-table-column v-for="(item, index) in tableColumn"
:key="index"
:prop="item.prop"
:label="item.label">
<template v-if="item.slot"
v-slot:default="scope">
<slot :name="item.prop"
:row="scope.row"
:index="scope.$index"></slot>
</template>
<template v-else v-slot:default="scope">
<div v-if="item.dictCode">
{{ scope.row[item.prop] | filterStatus(dict[item.dictCode]) }}
</div>
<div v-else-if="item.format">
{{ item.format(scope.row) }}
</div>
<div v-else>
{{ scope.row[item.prop] }}
</div>
</template>
</el-table-column>
<!-- 操作列 -->
<el-table-column v-if="setupConfig.width !== 0"
:fixed="setupConfig.fixed"
:width="setupConfig.width"
label="操作">
<template v-slot:default="scope">
<slot name="setup"
:row="scope.row"
:index="scope.$index"></slot>
<!-- 查看 -->
<el-button v-if="setupConfig.view"
type="text"
@click="setupEvents('view', scope.row)">查看</el-button>
<!-- 編輯 -->
<el-button v-if="setupConfig.edit"
type="text"
@click="setupEvents('edit', scope.row)">編輯</el-button>
<!-- 刪除 -->
<el-button v-if="setupConfig.del"
type="text"
@click="setupEvents('del', scope.row)">刪除</el-button>
<!-- 操作日志 -->
<el-button v-if="setupConfig.log"
type="text"
@click="setupEvents('log', scope.row)">操作日志</el-button>
</template>
</el-table-column>
<!-- 空狀態(tài) -->
<template slot="empty">
<p>{{ emptyText }}</p>
</template>
</el-table>
<!-- 分頁 -->
<el-pagination v-if="pagination"
class="pagination tablePage"
:pager-count="5"
:page-sizes="pageSizes || [10, 20, 50, 100]"
:total="tableTotal || 0"
:page-size="pageInfo.pageSize || 10"
:current-page="pageInfo.startPage || 1"
layout="total, sizes, prev, pager, next, jumper"
@size-change="sizeChange"
@current-change="pageChange"></el-pagination>
</div>
</template>
<!-- yxt-table.vue -->
<script>
import yxtSearch from './yxt-search'
export default {
name: 'yxtTable',
components: {
yxtSearch
},
props: {
apiUrl: { // 列表接口(必填)
type: Function,
required: true
},
tableColumn: { // 自定義列配置
type: Array,
default: () => []
},
otherConfig: { // 其他配置
type: Object,
default: () => {
return {
list: 'list' // 接口返回數(shù)據(jù)的列表字段的字段名(因為在組件里面調(diào)接口,可能不同業(yè)務不同項目組不同一個開發(fā)者返回給前端的參數(shù)名不一致,這里進行兼容)
}
}
},
dict: { // 全部字典
type: [Array, Object],
default: () => []
},
selection: { // 是否顯示可選框(多選-multiple 、單選-single )
type: String
},
selectable: { // 當前行是否可選擇
type: Function
},
rowKey: { // 表格唯一key(適用于分頁多選表格,保留之前的選擇,不傳則為單頁選擇)
type: [Number, String, Function],
default: ''
},
setupConfig: {
type: Object,
default: () => {
return {
width: 'auto'
}
}
},
pagination: { // 是否需要分頁,默認需要
type: Boolean,
default: true
},
pageSizes: { // 分頁的下拉框選項
type: Array
},
expand: { // 是否展開行
type: Boolean,
default: false
},
searchConfig: { // 搜索條件配置項
type: Array,
default () {
return []
}
},
searchReset: { // 搜索條件默認值重置值
type: Object
},
emptyText: {
type: String
}
},
filters: {
filterStatus (value, array, code = 'code', name = 'name') {
if (!value && value !== 0) { // 要把0摘出來,一般0都是正常的數(shù)據(jù),所以不能只用 !value
return ''
}
const find = array.find(e => (e[code] === value.toString()) || (e[code] === +value)) // 字符型數(shù)值型都得匹配
if (find) {
return find[name]
} else { // 沒有匹配的就原樣返回
return value
}
}
},
data () {
return {
loading: true,
tableData: [],
tableTotal: 0,
pageInfo: {
pageSize: 10,
startPage: 1
},
selectionRadio: '',
selectionData: [],
searchModel: this.searchReset ? JSON.parse(JSON.stringify(this.searchReset)) : {}
}
},
created () {
this.getData()
},
methods: {
getData (startPage) {
if (startPage) { // 如果傳入值,則從改值的頁碼數(shù)開始
this.pageInfo.startPage = startPage
}
this.loading = true
const fun = this.apiUrl
const pageInfo = { // 分頁信息
pageSize: this.pageInfo.pageSize,
startPage: this.pageInfo.startPage
}
let param = { // 其他的搜素條件
...this.searchModel
}
if (this.pagination) { // 如果需要分頁,則傳分頁信息
param = { ...param, ...pageInfo }
}
fun(param).then(res => {
setTimeout(() => {
this.tableData = res[this.otherConfig.list] || []
this.tableTotal = res.pageInfo?.total || 0
this.loading = false
}, 2000)
})
},
// 多選,選擇行數(shù)據(jù)change
selectionChange (selection) {
this.selectionData = selection
},
// 單選,選擇行數(shù)據(jù)change
selectionChangeSingle (selection) {
this.selectionData = [selection]
},
// 操作列方法 查看/編輯/刪除/操作日志
setupEvents (setupType, row) {
this.$emit(setupType, row)
},
// 條數(shù)變化
sizeChange (size) {
this.pageInfo.startPage = 1
this.pageInfo.pageSize = size
this.getData()
},
// 頁碼變化
pageChange (page) {
this.pageInfo.startPage = page
this.getData()
}
}
}
</script>
<style scoped lang="scss">
.yxt-table {
margin: 30px;
.multiple-operation {
margin-bottom: 10px;
}
}
</style>yxt-search.vue
<!-- yxt-search.vue -->
<template>
<div class="yxt-search">
<div v-for="(item,index) in searchConfig"
:key="index"
class="yxt-search-item">
<el-input v-if="item.type==='input'"
v-model="searchModel[item.key]"
size="medium"
:clearable="item.clearable || true"
:placeholder="item.placeholder || '請輸入'"
:maxlength="item.maxlength"></el-input>
<el-select v-if="item.type==='select'"
v-model="searchModel[item.key]"
size="medium"
style="width: 100%"
:clearable="item.clearable || true"
:filterable="item.filterable || true"
:disabled="item.disabled || false"
:multiple="item.multiple || false"
:allow-create="item.allowCreate"
:placeholder="item.placeholder || '請選擇'">
<el-option v-for="(selectItem, selectIndex) in item.selectList"
:key="selectIndex"
:label="selectItem[item.listLabel]"
:value="selectItem[item.listValue]"></el-option>
</el-select>
</div>
<div v-if="searchConfig.length" class="yxt-search-button">
<el-button size="medium" type="primary" @click="search">搜索</el-button>
<el-button size="medium" type="primary" plain @click="reset">重置</el-button>
<!-- 其他的按鈕需求通過插槽傳入 -->
<slot name="searchBtn" :searchData="searchModel"></slot>
</div>
</div>
</template>
<!-- yxt-search.vue -->
<script>
export default {
name: 'yxtSearch',
props: {
searchConfig: { // 搜索條件配置項
type: Array,
required: true,
default () {
return []
}
},
searchModel: { // 搜索條件綁定值
type: Object,
required: true,
default () {
return {}
}
},
searchReset: { // 搜索條件默認值重置值
type: Object
}
},
data () {
return {
}
},
methods: {
search () {
this.$emit('search', this.searchModel)
},
reset () {
if (this.searchReset) { // 如果傳入有默認值,則重置后為默認值
Object.keys(this.searchModel).forEach((item) => {
this.searchModel[item] = this.searchReset[item]
})
} else {
Object.keys(this.searchModel).forEach((item) => {
this.searchModel[item] = ''
})
}
}
}
}
</script>
<style scoped lang="scss">
.yxt-search {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
.yxt-search-item {
flex: 1;
margin: 0 10px 10px 0;
width: calc((100% - 30px) / 4); // 這里的30px = (分布個數(shù)4-1)*間隙1px, 可以根據(jù)實際的分布個數(shù)和間隙區(qū)調(diào)整
min-width: calc((100% - 30px) / 4);
max-width: calc((100% - 30px) / 4);
&:nth-child(4n) { // 去除每行最后一個(第4n個)的margin-right
margin-right: 0;
}
}
.yxt-search-button {
margin: 0 0 10px 0;
width: 100%;
text-align: right;
}
}
</style>yxtTable.json
{
"retCode": "0",
"retMsg": "success",
"pageInfo": {
"total": 300
},
"tasks": [
{ "name": "張三",
"code": "zhangSan",
"status": 1,
"icon": true,
"phone": "17801010101",
"selectable": 1
},
{ "name": "李四",
"code": "liSi",
"status": 0,
"icon": false,
"phone": "17802020202",
"selectable": 2
},
{ "name": "王五",
"code": "wangWu",
"status": 2,
"icon": true,
"phone": "17803030303",
"selectable": 0
},
{ "name": "馬六",
"code": "maLiu",
"status": 1,
"icon": false,
"phone": "17804040404",
"selectable": 2
}
]
}最后效果

總結(jié)
到此這篇關(guān)于elementUI el-table二次封裝的文章就介紹到這了,更多相關(guān)el-table二次封裝內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
前端XSS攻擊場景詳解與Vue.js處理XSS的方法(vue-xss)
這篇文章主要給大家介紹了關(guān)于前端XSS攻擊場景與Vue.js使用vue-xss處理XSS的方法,介紹了實際工作中渲染數(shù)據(jù)時遇到XSS攻擊時的防范措施,以及解決方案,需要的朋友可以參考下2024-02-02
解決Vue3?echarts?v-show無法重新渲染的問題
這篇文章主要介紹了Vue3?echarts?v-show無法重新渲染的問題,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-09-09
Vue使用vue-area-linkage實現(xiàn)地址三級聯(lián)動效果的示例
很多時候我們需要使用地址三級聯(lián)動,即省市區(qū)三級聯(lián)動,這篇文章主要介紹了Vue使用vue-area-linkage實現(xiàn)地址三級聯(lián)動效果的示例,感興趣的小伙伴們可以參考一下2018-06-06

