前言
由于项目的原因,几乎所有的页面都需要用到表格,但是表格的样式又有些许的差别。起初,项目启动的时候,选择使用饿了么的UI,便也懒得去设计其中的UI,基于它的组件封装table组件。希望以后有机会自己能封装独自的组件。
原因
直接上代码,你就可以知道为什么要封装组件。
<el-table
:data="userData"
border
tooltip-effect="dark"
style="width: 100%"
@selection-change="handleSelectionChange">
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column
type="index"
width="80">
</el-table-column>
<el-table-column
:sortable=true
header-align="center"
prop="name"
label="姓名"
width="120">
</el-table-column>
<el-table-column
:sortable=true
header-align="center"
prop="pid"
label="证件号">
</el-table-column>
<el-table-column
:sortable=true
header-align="center"
prop="birth"
label="出生日期">
</el-table-column>
<el-table-column
:sortable=true
header-align="center"
prop="dutyname"
label="职务">
</el-table-column>
<el-table-column
:sortable=true
header-align="center"
prop="sex"
label="性别">
</el-table-column>
<el-table-column
:sortable=true
header-align="center"
fixed="right"
label="操作"
width="150">
<template scope="scope">
<el-button @click="detailMore(scope.$index, scope.row)"
type="text"
size="small">
查看详情
</el-button>
<el-button
@click.native.prevent="deleteRow(scope.$index, userData)"
type="text"
size="small">
删除
</el-button>
</template>
</el-table-column>
</el-table>
这样的代码可以说非常的长。在一个页面里怎么说也要有80行的代码量。而且当很多个页面需要复用的时候,你就必须去不断的改它的label
属性还有方法,非常的繁琐。
于是,封装table组件,势在必行。
起始
封装组件从文件夹开始:
├── components
├── index.js
├── vue
├── table.vue
├── utils
├── utils.js
为了方便引用,建立了一个index.js
:
import TmTable from "./vue/table.vue";
export default UTable;
为了方便引用和路径的维护,首先在build
文件夹下的webpack.base.conf.js
对路径进行修改。
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
'domain$': path.resolve(__dirname, '../src/api/domain.js'),
'api': path.resolve(__dirname, '../src/api'),
'utils': path.resolve(__dirname, '../src/utils'),
'components': path.resolve(__dirname, '../src/components'),
}
}
这样就可以在vue文件中对组件进行这样引用,就可以使用了。
import TmTable from 'components/newTable'
封装思路
做好了准备工作,我们就可以进去vue文件中开始封装了。

拿到一个表格,首先思考,这个表格能做什么?
在本项目中,需要有选择,序号,数据,操作。表头上还有可能需要有排序的功能。
<template>
<el-table>
<!--选择-->
<!--序号-->
<!--数据源-->
<!--操作-->
</el-table>
</template>
首先呢,先把选择和排序加上,这两个比较简单。只需要通过props
属性来判断是否需要该列。由于是一次性生成就完事了。所以在这里选择用v-if
。
<template>
<el-table>
<!--选择-->
<el-table-column
v-if="hasSelection"
type="selection"
width="55">
</el-table-column>
<!--序号-->
<el-table-column
v-if="hasIndex"
type="index"
width="55">
</el-table-column>
</el-table>
</template>
export default {
name: 'tm-table',
props: {
//是否可以选择
hasSelection: {
type: Boolean,
default: function () {
return false
}
},
//是否有序列项
hasIndex: {
type: Boolean,
default: function () {
return false
}
}
}
搞定!好了接下来就是将数据传入表格里。这里就涉及到两点。饿了么UI是通过label
来控制表头的,在通过prop
控制数据流的。而且由于项目的需要,有些数据本身就不会被展现出来。
于是我通过参数columns
来控制列的行为,再通过参数dataSource
存储数据源。
以下是数据格式:
columns: [
{
hasSort: false, //<Boolean> 是否排序
isShow: true, //<Boolean> 是否展示
prop: 'name', //<String> 对应属性名
label: '姓名', //<String> 表头标签
},
{
hasSort: false, //<Boolean> 是否排序
isShow: true, //<Boolean> 是否展示
prop: 'date', //<String> 对应属性名
label: '日期', //<String> 表头标签
},
{
hasSort: true, //<Boolean> 是否排序
isShow: true, //<Boolean> 是否展示
prop: 'org', //<String> 对应属性名
label: '单位', //<String> 表头标签
},
{
hasSort: true, //<Boolean> 是否排序
isShow: true, //<Boolean> 是否展示
prop: 'id', //<String> 对应属性名
label: '身份证', //<String> 表头标签
}],
dataSource: [
{
name: '隔壁',
id: '123456',
org: '隔壁济源',
date: '2017-12-12',
sex: '男'
},
{
name: '老王',
id: '456789',
org: '地府',
date: '2017-11-12'
},
]
具体怎么控制,看注释就一目了然。
好了,那么怎么写数据部分呢?
<!--数据源-->
<el-table-column v-for="(column, index) in columns"
v-if="column.isShow"
header-align="center"
:sortable="column.hasSort"
:key="column.prop"
:prop="column.prop"
:label="column.label">
</el-table-column>
是不是很简单。接下来就是一个问题。怎么写操作部分?这就要谢谢前段时间看的一些关于slot的文章,当时是好奇,为什么饿了么UI可以使用<template scope="scope">
这么来写(详情可以查看 Table 表格):
<el-table-column label="操作">
<template scope="scope">
<el-button
size="small"
@click="handleEdit(scope.$index, scope.row)">编辑</el-button>
<el-button
size="small"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
后来才查到可以使用slot来做,于是我就这样构思,如果我需要操作列的时候就直接往上加,如果不需要的话,我就直接忽略。slot不就刚好满足我的需求吗?再来它还有一个好处,也是很重要的一点,那就是可以在父组件上绑定事件到操作的按钮。
于是只要在要加操作列的地方添加一行:
<!--操作-->
<slot name="handle-column">
</slot>
这样就占好位置了。完美!
好了,在看看呢。各个列的功能已经被我们实现了,最后只要把数据绑定到列上面去就可以了。
<el-table :data="dataSource" >
</el-table>
但是仔细想想,貌似漏了一个东西。如果,你有多选的话,那数据在子组件。父组件有没有办法获取到选择的行。这么办?简单啊!用vm.$emit
。
<el-table border
:data="dataSource"
@selection-change="handleSelectionChange">
</el-table>
methods: {
//将选中的行发送到父组件
handleSelectionChange(val) {
const selectionArr = [];
val.forEach(function (el) {
selectionArr.push(el);
});
this.$emit('emitSelection', selectionArr);
},
}
到这里,整个思路就完成了。
完整代码
吸取$川的建议,在此处就不贴源码。直接上GitHub的地址:table组件源码
虽然看起来代码量还是很大,实际上光html的代码行数也就20+,比起原来的70行那是少了大约2/3的量。如果不需要操作列的话,那几乎10行就可以搞定一个列表,而且几乎不需要修改代码的复制黏贴才是我的终极目标。
还请多多指教。
阅读原文请点击:原文链接
网友评论