为了提高开发效率对el-table进行二次封装,其中包含分页功能和搜索功能,代码如下:
* Props:
* id:组件唯一标识
* columns: 列属性数组参数
* slotName: 自定义渲染列的插槽名称
* ${prop}_header: 表格自带header插槽命名方法
* indexFn(index, row): 索引自定义方法名称
* 其他属性同element一致(https://element.eleme.cn/#/zh-CN/component/table)
* pagination: 分页配置参数
* size-change:size改变时查询数据,如果组件需要做查询之外的操作,传size-change方法
* current-change:同size-change
* 其他参数同element一致
* onFetchData: 请求数据
-->
<template>
<div :id="id" class="tvt-table">
<!-- 组件属性透传 v-bind, v-on -->
<el-table v-bind="$attrs" v-on="$listeners" :border="border" :class="[borderBottom ? '' : 'noBorder', customClass]">
<template v-for="(column, index) in columns">
<!--表格索引-->
<el-table-column v-if="column.type === 'index'" :key="`${index}_${level}_index`" type="index" v-bind="column">
<template slot-scope="scope">
<span v-if="column.indexFn">{{ column.indexFn(scope.$index, scope) }}</span>
<span v-else>{{ scope.$index + 1 }}</span>
</template>
</el-table-column>
<el-table-column v-else-if="column.type === 'selection'" type="selection" :key="`${column.prop}_selection`" v-bind="column"></el-table-column>
<el-table-column
v-else
:key="column.prop || column.key"
v-bind="column"
>
<!--element-ui内置自定义表头-->
<template v-slot:header="scope">
<slot v-if="$scopedSlots[`${column.prop}_header`]" :name="`${column.prop}_header`" :column="scope.column"></slot>
<span v-else>{{ column.label }}</span>
</template>
<!--自定义列-->
<template v-slot="scope">
<slot v-if="column.slotName" :name="column.slotName" :row="scope.row" :column="column" :$index="scope.$index" />
<span v-else-if="column.valueEnum" :class="['tvt-dot-status', column.valueEnum[scope.row[column.prop]] ? `tvt-dot-status-${column.valueEnum[scope.row[column.prop]].status}` : '']">{{
column.valueEnum[scope.row[column.prop]] ? column.valueEnum[scope.row[column.prop]].text ||
column.valueEnum[scope.row[column.prop]] :
''
}}</span>
<span v-else>{{ scope.row[column.prop] }}</span>
</template>
</el-table-column>
</template>
<template v-slot:append>
<slot v-if="$scopedSlots.append" name="append"></slot>
</template>
</el-table>
<div class="tvt-pagination" v-if="paginationConf">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:page-size.sync="paginationConf.size"
:current-page.sync="paginationConf.current"
v-bind="paginationConf"
>
</el-pagination>
</div>
</div>
</template>
<script>
export default {
name: 'TvtTable',
props: {
id: {
type: String,
default: ''
},
columns: {
type: Array,
default: () => []
},
pagination: {
type: Object
},
border: {
type: Boolean,
default: false
},
borderBottom: { // 表格水平border
type: Boolean,
default: false
},
level: { // 1 - 一级表格 2 - 二级表格
type: String,
default: ''
},
customClass: {
type: String,
default: ''
},
backToFirstPage: { // 切换size,是否需要回到第一页
type: Boolean,
default: true
}
},
computed: {
// 分页参数
paginationConf: {
// 合并props和内置默认配置
set (val) {
return val
},
get() {
return this.pagination ? {
...this.defaultPagination,
...this.pagination
} : false
}
}
},
mounted(){
},
data () {
return {
defaultPagination: {
layout: "total, sizes, prev, pager, next, jumper",
'page-sizes': [10, 20, 30, 40],
size: 10,
current: 1
}
}
},
methods: {
handleSizeChange (size, page) {
const onSizeChange = this.paginationConf['size-change']
this.handleFetchData({
size,
current: this.backToFirstPage ? 1 : this.paginationConf.current
})
if (onSizeChange) onSizeChange(size)
},
handleCurrentChange (current) {
const onPageChange = this.paginationConf['current-change']
this.handleFetchData({
current,
size: this.paginationConf.size
})
if (onPageChange) onPageChange(current)
},
handleFetchData (cond) {
const { total, size, current } = { ...this.paginationConf, ...cond }
if (total && (current - 1) * size > total) {
return false
}
this.$emit('onFetchData', cond)
}
}
}
</script>
<style lang="scss" scoped>
.tvt-table {
.noBorder >>> td.el-table__cell {
border-bottom: 0
}
.noBorder.el-table::before {
height: 0;
}
.tvt-pagination {
padding: 10px 0;
text-align: right;
}
>>> .header-class {
background: #e4e4e4;
th.el-table__cell {
background-color: #e4e4e4
}
}
.tvt-dot-status {
position: relative;
display: inline-block;
&::before {
content: '';
position: absolute;
width: 6px;
height: 6px;
border-radius: 50%;
top: 50%;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
left: 0;
}
}
.tvt-dot-status-success,
.tvt-dot-status-error,
.tvt-dot-status-default {
padding-left: 10px;
}
.tvt-dot-status-default::before {
background: #909399;
}
.tvt-dot-status-success::before {
background: #67c23a;
}
.tvt-dot-status-error::before {
background: #f56c6c;
}
}
</style>
表格组件封装好后,具体的使用如下:
<template>
<tvt-table
v-loading="tableLoading"
ref="tvtTableRef"
:data="list"
:columns="columns"
height="350"
header-row-class-name="header-class"
:filterCond="filterCond"
:pagination="{
total: total,
current: filterCond.current,
size: filterCond.size,
background: true
}"
@onFetchData="handleQueryData"
@selection-change="handleSelectionChange"
style="width: 100%"
>
<!--可操作表头-->
<template #number_header>
<el-select v-model="value" placeholder="请选择">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
<!--自定义列插槽-->
<!--可编辑列-->
<template #bodyCell="{row, column }">
<template v-if="column.key === "operation"">
<el-button type="primary" size="mini" @click="handleTest(row)">编辑</el-button>
<el-button type="danger" size="mini" @click="handleDelete(row)">删除</el-button>
</template>
</template>
</tvt-table>
</template>
<script>
export default {
data () {
list: [],
tableLoading: false,
columns: [{ label: "序号", type: "index" },
{ label: "站点名称", prop: "name", sortable: true },
{ label: "类型", prop: "type", valueEnum: {1: "IPC", 2: "NVR"} },
{ label: "位置", prop: "position" },
{ label: "联系方式", prop: "mobile" },
{ label: "input", prop: "number", slotName: "bodyCell" },
{ label: "operation", key: "operation", slotName: "bodyCell" }],
filterCond: {
search: "",
current: 1,
size: 10
},
value: "",
options: [
{ value: "选项1", label: "黄金糕" },
{ value: "选项2", label: "双皮奶" }
],
},
methods: {
handleQueryData () {},
handleDelete() {},
handleTest() {},
handleSelectionChange() {}
}
}
</script>
最后一起来看一下页面效果吧