平时在开发中,会经常使用到element-ui等类似的UI组件库,而在业务开发中,经常会调用一些组件,如table组件,于是就想,可不可以将其封装起来,方便自己调用呢,于是说干就干。
封装的这个组件要有以下功能:
- 首先想的是封装的这个组件要完全覆盖element-ui组件table原生的功能。
- 不仅支持prop方式去渲染表格列,也支持render方式渲染。
- 支持分页
于是,为了实现自己的这个想法,就利用vue-cli 3.0脚手架,搭建了一个项目工程,取名为vue-table
然后在components目录下新建一个BaseTable的目录,用来存放我们的这个公共table组件。
BaseTable目录下有两个文件index.vue, tableConfig.js
index.vue:处理table组件的相关逻辑。
tableConfig.js: 放置table组件一些默认的配置项。
这里用到vue2.4所提到的$attrs, $listeners两个属性,来简化代码。
index.vue的代码如下:
<template>
<div v-bind="$attrs" class="base-table">
<slot name="title"/>
<slot/>
<el-table
:data="data"
:ref="$attrs.ref"
v-bind="tableAttrs"
style="width: 100%"
v-on="$listeners"
>
<div
v-for="(col, index) in columnAttrs"
:key="index"
>
<el-table-column
v-if="!col.render"
v-bind="col"
/>
<el-table-column
v-else
v-bind="col"
>
<template slot-scope="scope">
<expandDom
:row="scope.row"
:col="col"
:render="col.render"
:col-index="index"
/>
</template>
</el-table-column>
</div>
</el-table>
<el-pagination
v-if="paginationAttrs.isPagination"
v-bind="paginationAttrs"
class="pagination-container"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</template>
<script>
import {
defaultTableAttrs,
defaultColumn,
defaultPagination
} from './tableConfig'
export default {
name: 'BaseTable',
components: {
expandDom: {
functional: true,
props: {
row: Object,
col: Object,
render: Function,
colIndex: [Number, String]
},
render(h, ctx) {
const randomIndex = Math.random().toString(35).replace('.', '')
const params = {
row: { ...ctx.props.row },
colIndex: ctx.props.colIndex || randomIndex
}
if (ctx.props.col) {
params.col = ctx.props.col
}
return ctx.props.render && ctx.props.render(h, params)
}
}
},
props: {
data: {
type: Array,
default() {
return []
}
},
columns: {
type: Array,
default() {
return []
}
},
// eslint-disable-next-line vue/require-default-prop
pagination: {
type: [Object, Boolean]
}
},
data() {
return {
tableAttrs: {},
columnAttrs: [],
paginationAttrs: {}
}
},
watch: {
pagination: {
handler(val) {
this.getPagination()
},
deep: true
}
},
created() {
this.$nextTick(() => {
this.init()
})
},
methods: {
init() {
// 获取element table上的属性
const tableAttrs = { }
Object.keys(defaultTableAttrs).forEach(key => {
if (this.$attrs[key] !== undefined) {
tableAttrs[key] = this.$attrs[key]
}
})
this.tableAttrs = Object.assign({}, defaultTableAttrs, tableAttrs)
// 获取element table col上的属性
this.columnAttrs = this.columns.map(column => {
const obj = Object.assign({}, defaultColumn, column)
return obj
})
},
getPagination() {
// 获取element 分页属性
const pagination = this.pagination
let paginationAttrs = {}
if (pagination) {
if (typeof pagination === 'object') {
paginationAttrs = {
...defaultPagination,
...pagination,
isPagination: true
}
} else {
paginationAttrs = {
...defaultPagination,
isPagination: true
}
}
}
Object.keys(paginationAttrs).forEach(key => {
if (this.$attrs[key] !== undefined && key !== 'pagination') {
paginationAttrs[key] = this.$attrs[key]
}
})
this.paginationAttrs = paginationAttrs
},
handleSizeChange(pageSize) {
this.$emit('size-change', pageSize)
},
handleCurrentChange(page) {
this.$emit('current-change', page)
}
}
}
</script>
<style lang="scss" scoped>
.pagination-container{
margin-top: 10px;
text-align: right;
}
</style>
tableConfig.js代码如下:
export const defaultTableAttrs = {
data: null,
height: null,
"max-height": null,
stripe: false,
border: false,
size: null,
fit: true,
"show-header": true,
"highlight-current-row": false,
"current-row-key": null,
"row-class-name": null,
"row-style": null,
"cell-class-name": null,
"cell-style": null,
"header-row-class-name": null,
"header-row-style": null,
"header-cell-class-name": null,
"header-cell-style": null,
"row-key": null,
"empty-text": "暂无数据",
"default-expand-all": false,
"expand-row-keys": null,
"default-sort": null,
"tooltip-effect": null,
"show-summary": false,
"sum-text": "合计",
"summary-method": null,
"span-method": null,
"select-on-indeterminate": true,
indent: 16,
lazy: null,
load: null,
"tree-props": { hasChildren: "hasChildren", children: "children" }
};
export const defaultColumn = {
type: null,
index: null,
"column-key": null,
label: null,
prop: null,
width: null,
"min-width": null,
fixed: null,
"render-header": null,
sortable: false,
"sort-method": null,
"sort-by": null,
"sort-orders": ["ascending", "descending", null],
resizable: true,
formatter: null,
"show-overflow-tooltip": false,
align: "left",
"header-align": null,
"class-name": null,
"label-class-name": null,
selectable: null,
"reserve-selection": false,
filters: null,
"filter-placement": null,
"filter-multiple": true,
"filter-method": null,
"filtered-value": null
};
export const defaultPagination = {
pageSize: 10,
currentPage: 1,
total: 0,
layout: "total, sizes, prev, pager, next, jumper"
};
这样,就大功告成啦,于是去试试吧
<template>
<div class="home">
<BaseTable :data="tableData" :columns="columns" :height="300"></BaseTable>
</div>
</template>
<script>
// @ is an alias to /src
export default {
name: "home",
components: {
BaseTable: () => import("@/components/BaseTable")
},
data() {
return {
tableData: [
{
date: "2016-05-02",
name: "王小虎",
address: "上海市普陀区金沙江路 1518 弄"
},
{
date: "2016-05-04",
name: "王小虎",
address: "上海市普陀区金沙江路 1517 弄"
},
{
date: "2016-05-01",
name: "王小虎",
address: "上海市普陀区金沙江路 1519 弄"
},
{
date: "2016-05-03",
name: "王小虎",
address: "上海市普陀区金沙江路 1516 弄"
}
],
columns: [
{
prop: "name",
label: "姓名"
},
{
prop: "date",
label: "日期"
},
{
prop: "address",
label: "地址",
sortable: true,
render(h, params) {
console.log(params);
return h("el-tag", {}, "哈哈");
}
}
]
};
}
};
</script>
最终页面显现效果如下:
非常nice,喜欢的朋友记得收藏呀!