很多中后台业务的系统中,表格是最高频的组件之一,其中一般包括搜索条件、表格展示、表格操作列、分页等。那么我们二次封装的这个表格组件就需要包含以下几个功能点:
1、数据自动获取和刷新
2、自定义列配置
3、分页功能
4、根据搜索条件进行搜索功能
5、加载中状态和空数据状态
一、先上页面最终效果
二、创建目录yxt-table如下图
index.vue为父级页面
yxt-table.vue为表格组件
二、数据自动获取和刷新
因为表格的数据一般都比较简单,就是根据搜索条件,调用接口请求一个到列表,然后将列表数据一一展示到表格上,再对特定的列进行一些过滤转化等个性化操作。但是万变不离其宗,这个步骤基本每个表格都要进行一遍,所以考虑到通用性(其实是为了偷懒),将请求接口获取数据这一步放在组件里面实现。
created () {
this.getData()
},
methods: {
getData () {
const fun = this.apiUrl
fun().then(res => {
this.tableData = res[this.otherConfig.list] || []
this.tableTotal = res.pageInfo?.total || 0
})
}
}
三、自定义列配置
组件接收一个数组作为自定义列
tableColumn: [
{ prop: 'name', label: '名称' },
{ prop: 'code', label: '编码' },
{ prop: 'status', label: '状态' }
]
index.vue
yxt-table.vue
至此,一个表格可以实现了
1、otherConfig说明
由于我们的接口请求放在组件里面了,但是我们对接的接口可能由于业务的不同项目组的不同开发人员的不同,而导致接口返回的列表的字段名不同,这里通过传参的形式做一下兼容
2、上面这样只能实现简单的展示功能,还有些数据比如 状态1 需要转化为打卡成功,状态0 需要转化为打卡失败进行显示,这类需求可以通过filter进行转化
{{ scope.row[item.prop] | filterStatus(dict[item.dictCode]) }}
{{ scope.row[item.prop] }}
props: {
dict: { // 全部字典
type: Object,
default: () => {}
}
},
filters: {
filterStatus (value, array, code = 'code', name = 'name') {
if (!value && value !== 0) { // 要把0摘出来,一般0都是正常的数据,所以不能只用 !value
return ''
}
const find = array.find(e => (e[code] === value.toString()) || (e[code] === +value)) // 字符型数值型都得匹配
if (find) {
return find[name]
} else { // 没有匹配的就原样返回
return value
}
}
},
data () {
return {
tableColumn: [
{ prop: 'name', label: '名称' },
{ prop: 'code', label: '编码' },
{ prop: 'status', label: '状态', dictCode: 'status' }
],
dict: {
status: [
{ code: 0, name: '打卡失败' },
{ code: 1, name: '打卡成功' }
]
}
}
}
这里dict设置为对象的原因是为了装进更多字典
3、思考一下,如果要在表格中展示这样的自定义图标怎么办?
使用插槽slot,在tableColumn里面设置某行属性的slot为true,改造el-table-column如下:
{{ scope.row[item.prop] | filterStatus(dict[item.dictCode]) }}
{{ scope.row[item.prop] }}
data () {
return {
tableColumn: [
{ prop: 'name', label: '名称' },
{ prop: 'code', label: '编码' },
{ prop: 'status', label: '状态', dictCode: 'status' },
{ prop: 'icon', label: '图标', slot: true }
]
}
}
4、在实际项目中,除了字典转化,还有一些比较定制化的展示需求,这个可以通过传入一个函数format进行计算,然后在这个方法里面将最后的计算结果return
{{ scope.row[item.prop] | filterStatus(dict[item.dictCode]) }}
{{ item.format(scope.row) }}
{{ scope.row[item.prop] }}
data () {
return {
tableColumn: [
{ prop: 'name', label: '名称' },
{ prop: 'code', label: '编码' },
{ prop: 'status', label: '状态', dictCode: 'status' },
{ prop: 'icon', label: '图标', slot: true },
{ prop: 'phone',
label: '电话号码',
format: (row) => {
return `${row.name}-${row.code}(${row.phone})`
} }
]
}
}
5、表格一般还有批量操作,所以需要多选和单选以及针对特定场景设置禁选
yxt-table.vue
{{ '' }}
{{ scope.row[item.prop] | filterStatus(dict[item.dictCode]) }}
{{ item.format(scope.row) }}
{{ scope.row[item.prop] }}
index.vue
批量操作1
批量操作2
单选操作
6、操作列
根据业务需求,可以在操作列设置几个默认按钮,通过setupConfig设置开关,如果有除了默认按钮之外的操作需求,再通过插槽slot进行插入
查看
编辑
删除
操作日志
props: {
setupConfig: {
type: Object,
default: () => {
return {
width: 'auto'
}
}
}
},
methods: {
setupEvents (setupType, row) { // 操作列方法 查看/编辑/删除/操作日志
this.$emit(setupType, row)
}
}
7、分页
pagination控制是否需要分页组件,如果不需要分页则设置为false。根据业务需求,可传入pageSizes控制条数下拉框的条数选项
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
})
},
// 条数变化
sizeChange (size) {
this.pageInfo.startPage = 1
this.pageInfo.pageSize = size
this.getData()
},
// 页码变化
pageChange (page) {
this.pageInfo.startPage = page
this.getData()
}
}
8、el-table还有一个展开行功能expand,根据业务需求,也可以加进组件里
props: {
expand: { // 是否展开行
type: Boolean,
default: false
}
}
序号:{{index}}
内容:{{row}}
四、根据搜索条件进行搜索更新表格数据
新增一个yxt-search.vue
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) { // 如果传入值,则从改值的页码数开始
this.pageInfo.startPage = startPage
}
let param = { // 其他的搜素条件
...this.searchModel
}
...
}
}
导出
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)
}
}
五、加载中状态和空数据状态
加载中:el-table 添加 v-loading="loading",getData里面,发送请求之前设置为true,获得数据后设置为false
空数据:通过插槽empty设置
六、完整代码:
index.vue
批量操作1
批量操作2
导出
单选操作
序号:{{index}}
内容:{{row}}
yxt-table.vue
{{ '' }}
{{ scope.row[item.prop] | filterStatus(dict[item.dictCode]) }}
{{ item.format(scope.row) }}
{{ scope.row[item.prop] }}
查看
编辑
删除
操作日志
{{ emptyText }}
yxt-search.vue
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
}
]
}
最后效果