封装element的table组件,让其实现动态表头
由于后端传递过来的数据是表头跟数据分开的,而element的table组件是固定死表头的,跟ant-design不一样。这怎么搞?只能自己二次封装了。
封装思路
- 1、参照ant-design,分别传入表头header和表数据tableData这两个重要数据(数组类型)
- 2、table组件在接收到这两个数据之后进行展示
- 4、适用作用域插槽,让表格的内容展示由父组件来定义,增加灵活性
- 5、根据实际情况决定是否把分页跟table封装在一起(我这里是封装在一起的)
封装table(部分代码)
<template>
<div class="container-box">
<div class="table-box">
<el-dropdown
:hide-on-click="false"
trigger="click"
v-if="isSelect"
>
<el-button
size="mini"
icon="el-icon-menu"
circle
style="height: 40px; width: 40px"
v-if="isSelect"
>
</el-button>
<el-dropdown-menu slot="dropdown" class="dropdown">
<el-dropdown-item>
<el-checkbox
:indeterminate="isIndeterminate"
v-model="checkAll"
@change="handleCheckAllChange"
>全选</el-checkbox
>
<el-checkbox-group
v-model="checkedHeader"
@change="handleCheckedCitiesChange"
>
<el-checkbox
v-for="(item, index) in columns"
:disabled="!item.edit"
:label="item"
:key="item.name + index"
>{{ item.name }}</el-checkbox
>
</el-checkbox-group>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-table
:data="dataList"
v-loading="loading"
border
:header-cell-style="{
flex: 1,
color: '#464959',
backgroundColor: '#e7edff',
borderColor: '#ffffff',
padding: 0,
height: '40px',
}"
ref="tableWidth"
:fit="true"
style="width: 100%"
@sort-change="sortChange"
:height="height"
>
<el-table-column
v-for="item in headers"
:key="item.prop"
:label="item.label"
:prop="item.prop"
:align="item.align || 'center'"
:min-width="item.width || ''"
:sortable="item.sortable || false"
>
<template slot-scope="{ row }">
<slot :info="{label:item.prop, data:row}">
{{row[item.prop]}}
</slot>
</template>
</el-table-column>
</el-table>
</div>
<div class="pageSample" v-if="showPagination">
<el-pagination
background
:disabled="loading"
layout="total,prev, pager, next, sizes"
:total="total"
:page-size="pageSize"
:current-page="pageNum"
@size-change="changeSize"
@current-change="changePage"
>
</el-pagination>
</div>
</div>
</template>
<script>
export default {
props: {
// 表头
columns: {
type: Array,
default: () => [],
},
// 表数据
dataList: {
type: Array,
default: () => [],
},
// 总页数
total: {
type: Number,
default: 0,
},
// 每页显示条数
pageSize: {
type: Number,
default: 10,
},
// 当前页
pageNum: {
type: Number,
default: 1,
},
// 是否表头可选
isSelect: {
type: Boolean,
default: false,
},
// 表格高度
height: {
type: Number | String,
default: 500
},
// 加载动画
loading: {
type: Boolean,
default: false
},
// 是否显示分页
showPagination: {
type: Boolean,
default: true
}
},
data() {
return {
checkAll: false,
checkedHeader: [],
isIndeterminate: true,
headers: []
};
},
watch: {
dataList: {
deep: true,
handler() {
this.initData();
},
},
},
methods: {
handleCheckAllChange(val) {
// val 为true 表示全选,为false表示全部不选
this.checkedHeader = val
? this.columns
: this.columns.filter((v) => !v.edit);
this.isIndeterminate = this.checkedHeader.length < 0;
this.headers = this.checkedHeader
this.resetTableLayout();
},
handleCheckedCitiesChange(list) {
if(list.length > this.headers.length) {
let newVal = list[list.length - 1]
let index = this.columns.findIndex(item => item.name == newVal.name),
i = index,
btnIndex = this.headers.findIndex(item => item.name == '操作')
btnIndex != -1 && index > this.headers.length ? i = btnIndex : null
this.headers.splice(i, 0, this.columns[index])
} else {
this.headers = this.checkedHeader
}
},
// 重排表格布局
resetTableLayout() {
this.$nextTick(() => {
if(this.$refs.tableWidth) {
this.$refs.tableWidth.doLayout();
this.$refs.tableWidth.bodyWrapper.scrollTop = 0
}
});
},
changeSize(size) {
// console.log(size);
this.$emit("changeSize", size);
},
changePage(page) {
// console.log(page);
this.$emit("changePage", page);
},
initData() {
this.checkedHeader = this.headers = this.columns
.filter((v) => v.show)
},
sortChange(val) {
console.log('排序', val)
this.$emit("on-sort", val);
}
},
created() {
this.initData();
},
mounted() {
},
destroyed() {
},
};
</script>
在父组件中使用封装的table
<tables
:loading="loading"
:total="total"
:columns="column"
:dataList="dataList"
:pageSize="pageSize"
:pageNum="pageNum"
@changeSize="pageSizeChange"
@changePage="pageNumChange"
@on-sort="sortData"
height="calc(100vh - 312px)">
<template slot-scope="{ info }">
<div v-if="info.label == '试听'" v-show="!loading">
<img :src="playIcon" alt="" v-show="!info.data.play" @click.stop="playAudio(info.data, true)"/>
<img :src="playIcon_pause" alt="" v-show="info.data.play" @click.stop="playAudio(info.data, false)"/>
</div>
<el-button v-show="!loading" v-else-if="info.label == '操作'" type="text" @click="lookDetail(info.data)">查看详情</el-button>
<span v-else>
{{ info.data[info.label] }}
</span>
</template>
</tables>
// 部分方法
methods: {
// 处理表格数据:就是将tableData和header进行“拼装”,拼成el-table接收的data格式
handleDataList(dataList) {
let header = this.column.map(item =>item.name)
header = header.slice(0, header.length-1)
dataList = dataList.map(item => {
let res = {}
header.forEach((val,i) => {
res[val] = item[i]
res.play = false
})
return res
})
return dataList
},
// 处理表头数据
handleHeader(header) {
let resHeader = []
header.forEach((item, i) => {
resHeader.push({
...item,
index: i,
label: item.name,
width: this.width(item.name),
prop: item.name,
sortable: item.name == '入库时间'? 'sortable' : false
})
})
if(resHeader.length > 0) {
resHeader.push({
index: resHeader.length,
name: '操作',
label: '操作',
prop: '操作',
width: 100,
show: true,
edit: true
})
}
return resHeader
},
}
实现效果
以上封装只是我根据自己业务需求来进行的二次封装,方法不一定是最好的,大致只是提供了一个思路。如果大家有更好的想法,欢迎一起来讨论