项目中使用了Element UI的表格组件el-table,按照官方提供的写法,展示的列越多,代码量也就越大。如果是开发后台管理系统,需要用到统计的地方有很多。不把时间花在重复的工作上,封装el-table,作为组件引用,只需要少量的配置,就能极大的提高开发效率。
el-table需要面对的场景多种多样,业务不同,呈现出的样式也不同,根据使用的难易程度,把它分为:
- 1、显示数据从父视图传递,集成只需要一行代码的简易表格——
<simpleTable>
- 2、集成需要配置请求接口、查询参数,有分页,组件内请求数据的表格——
<myTable>
一、simpleTable
<template>
<div ref="table" >
<el-table :show-summary="isShowSummary" v-if="!fixHeight" :header-cell-style="{background:headerBgColor}" :data="tableData" ref="multipleTable" border style="width: 100%;" @selection-change="handleSelectionChange">
<el-table-column v-if="isShowSelection" type="selection" width="55" align="center"></el-table-column>
<el-table-column type="index" align="center" label="序号" min-width="50" v-if="isShowSerialNumber"></el-table-column>
<template v-for="k in props">
<el-table-column v-if="k.fixw" :min-width="k.fixw" align="center" :prop="k.prop" :label="k.label" :sortable="k.sort ? true : false">
<slot :name="k.prop" :data="scope.row" slot-scope="scope">{{scope.row[k.prop]}}</slot>
</el-table-column>
<el-table-column v-else align="center" :prop="k.prop" :label="k.label" :sortable="k.sort ? true : false">
<slot :name="k.prop" :data="scope.row" slot-scope="scope">{{scope.row[k.prop]}}</slot>
</el-table-column>
</template>
<el-table-column align="center" :fixed="fixedType?'right':fixedType" label="操作" v-if="isShowOperation">
<slot name="operation" :data="scope.row" slot-scope="scope"></slot>
</el-table-column>
</el-table>
<el-table v-else :height="tableH" :show-summary="isShowSummary" :header-cell-style="{background:headerBgColor}" :data="tableData" ref="multipleTable" border style="width: 100%;" @selection-change="handleSelectionChange">
<el-table-column v-if="isShowSelection" type="selection" width="55" align="center"></el-table-column>
<el-table-column type="index" align="center" label="序号" min-width="50" v-if="isShowSerialNumber"></el-table-column>
<template v-for="k in props">
<el-table-column v-if="k.fixw" :min-width="k.fixw" align="center" :prop="k.prop" :label="k.label" :sortable="k.sort ? true : false">
<slot :name="k.prop" :data="scope.row" slot-scope="scope">{{scope.row[k.prop]}}</slot>
</el-table-column>
<el-table-column v-else align="center" :prop="k.prop" :label="k.label" :sortable="k.sort ? true : false">
<slot :name="k.prop" :data="scope.row" slot-scope="scope">{{scope.row[k.prop]}}</slot>
</el-table-column>
</template>
<el-table-column align="center" :fixed="fixedType?'right':fixedType" label="操作" v-if="isShowOperation">
<slot name="operation" :data="scope.row" slot-scope="scope"></slot>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
props: {
headerBgColor: {
type: String,
default: 'white'
},
fixHeight:{
type: Boolean,
default: false
},
tableH:{
type: Number,
default: 0
},
isShowSelection: {
type: Boolean,
default: false
},
isShowSerialNumber: {
type: Boolean,
default: true
},
isShowOperation: {
type: Boolean,
default: true
},
isShowSummary: {
type: Boolean,
default: false
},
fixedType:{
type: Boolean,
default: false
},
sort: {
type: Array,
default: () => []
},
props: Array,
tableData: {
type: Array,
default: () => []
},
},
data () {
return {
}
},
created () {},
mounted(){},
methods: {
handleSelectionChange (val) {
this.$emit('selectionChange', val)
}
}
}
</script>
SimpleTable
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
headerBgColor | 表头背景色 | String | white |
fixHeight | 是否固定表格高度 | boolean | false |
tableH | 表格高度(fixHeight为true, 则tableH必须设定) | number | 0 |
isShowSelection | 是否开启选择功能 | boolean | false |
isShowSerialNumber | 是否显示序号 | boolean | true |
isShowOperation | 是否显示操作列 | boolean | true |
isShowSummary | 是否显示总计 | boolean | true |
fixedType | 是否固定右侧操作栏 | boolean | false |
props | 列标题以及对应的参数名数组 | Array | [] |
tableData | 后台返回的数据 | Array | [] |
Table Events
事件名 | 说明 | 参数 |
---|---|---|
selectionChange | 当选择项发生变化时会触发该事件 | row |
使用方式:
<template>
<div class="h-table">
<simpleTable
:tableData="chartData2.rows"
:props="callProps">
<template #operation="{ data }">
<a href="javascript:;">查看详情</a>
</template>
</simpleTable>
</div>
</template>
<script>
import simpleTable from "./../components/simpleTable";
export default {
name: "home",
components: {
simpleTable
},
data() {
return {
callProps: [
{
prop: "date",
label: "日期",
fixw: "150" // 自定义某列宽度
},
{
prop: "callNum",
label: "外呼数",
sort: true // 是否可以筛选
},
{
prop: "callSuccess",
label: "外呼成功数",
},
{
prop: "connectNum",
label: "接通率",
},
{
prop: "connectDuring",
label: "外呼平均通话时长",
},
],
chartData2: {
rows: [
{ date: "2020-03-04", callNum: 1393, callSuccess: 1093 },
{ date: "2020-03-05", callNum: 3530, callSuccess: 3230 },
{ date: "2020-03-06", callNum: 2923, callSuccess: 2623 },
{ date: "2020-03-07", callNum: 1723, callSuccess: 1423 },
{ date: "2020-03-08", callNum: 3792, callSuccess: 3492 },
{ date: "2020-03-09", callNum: 4593, callSuccess: 4293 },
],
},
};
}
};
</script>
<style lang="scss" scoped>
.h-table{
width: 90%;
margin-top: 20px;
margin-left: 20px;
}
</style>
二、myTable
<template>
<div ref="table" v-loading="loading" >
<el-table v-if="!fixHeigth" :data="tableData" :header-cell-style="{background:headerBgColor}" ref="multipleTable" border style="width: 100%;" @selection-change="handleSelectionChange" @sort-change="handleSortChange">
<el-table-column v-if="isShowSelection" type="selection" width="55" align="center" :selectable="isSelectable"></el-table-column>
<el-table-column align="center" label="序号" min-width="70" v-if="isShowSerialNumber">
<template slot-scope="scope">
<span>{{scope.$index+(currentPage-1)*pageSize+1}}</span>
</template>
</el-table-column>
<template v-for="k in props">
<el-table-column v-if="k.fixw" :min-width="k.fixw" align="center" :prop="k.prop" :label="k.label" :sortable="k.sort ? true : false">
<slot :name="k.prop" :data="scope.row" slot-scope="scope">{{scope.row[k.prop]}}</slot>
</el-table-column>
<el-table-column v-else align="center" :prop="k.prop" :label="k.label" :sortable="k.sort ? true : false">
<slot :name="k.prop" :data="scope.row" slot-scope="scope">{{scope.row[k.prop]}}</slot>
</el-table-column>
</template>
<el-table-column align="center" :fixed="fixedType?'right':fixedType" label="操作" v-if="isShowOperation">
<slot name="operation" :data="scope.row" slot-scope="scope"></slot>
</el-table-column>
</el-table>
<el-table v-else :height="tableH" :data="tableData" :header-cell-style="{background:headerBgColor}" ref="multipleTable" border style="width: 100%;" @selection-change="handleSelectionChange" @sort-change="handleSortChange">
<el-table-column v-if="isShowSelection" type="selection" width="55" align="center" :selectable="isSelectable"></el-table-column>
<el-table-column align="center" label="序号" min-width="70" v-if="isShowSerialNumber">
<template slot-scope="scope">
<span>{{scope.$index+(currentPage-1)*pageSize+1}}</span>
</template>
</el-table-column>
<template v-for="k in props">
<el-table-column v-if="k.fixw" :min-width="k.fixw" align="center" :prop="k.prop" :label="k.label" :sortable="k.sort ? true : false">
<slot :name="k.prop" :data="scope.row" slot-scope="scope">{{scope.row[k.prop]}}</slot>
</el-table-column>
<el-table-column v-else align="center" :prop="k.prop" :label="k.label" :sortable="k.sort ? true : false">
<slot :name="k.prop" :data="scope.row" slot-scope="scope">{{scope.row[k.prop]}}</slot>
</el-table-column>
</template>
<el-table-column align="center" :fixed="fixedType?'right':fixedType" label="操作" v-if="isShowOperation">
<slot name="operation" :data="scope.row" slot-scope="scope"></slot>
</el-table-column>
</el-table>
<div class="bottom-row" v-if="isShowPagination">
<el-pagination
@current-change="handleCurrentChange"
:page-size="pageSize"
:current-page.sync="currentPage"
layout="prev, pager, next, jumper, total"
:total="total">
</el-pagination>
</div>
</div>
</template>
<script>
export default {
props: {
headerBgColor: {
type: String,
default: 'white'
},
isShowSelection: {
type: Boolean,
default: false
},
immediate: {
type: Boolean,
default: true
},
fixHeigth:{ //固定高度
type: Boolean,
default: false
},
isShowSerialNumber: {
type: Boolean,
default: true
},
isShowOperation: {
type: Boolean,
default: true
},
isShowPagination: {
type: Boolean,
default: true
},
isSecondLayer: {
type: Boolean,
default: true
},
isPost: {
type: Boolean,
default: true
},
isCustomTime:{ // 起始时间划分为两个参数的情况
type: Boolean,
default: false
},
tableH:{ // 表格高度,横向内容超过可视区域时适配小屏幕电脑
type: Number,
default: 0
},
fixedType:{ // 是否固定右侧操作栏
type: Boolean,
default: false
},
isArray: {
type: Boolean,
default: true
},
callback: Function,
sort: {
type: Array,
default: () => []
},
query: Object,
props: Array,
action: String,
isSelectable: Function,
dataList: String,
selected: String
},
data () {
return {
loading: false,
tableData: [],
total: 0,
pageSize: 10,
currentPage: 1
}
},
async created () {
this.immediate && await this.search()
},
methods: {
async search (i, isScroll) {
this.currentPage = i > 0 ? i : this.currentPage
let query = JSON.parse(JSON.stringify(this.query))
for (let key in query) {
if (Array.isArray(query[key])) {
query[key] = query[key].toString()
}
}
let req = {
pageNum: this.currentPage,
pageSize: this.pageSize,
...query
}
if(this.isCustomTime){
let paytime=this.query.payTime || [];
req.payTimeStart=paytime[0]
req.payTimeEnd=paytime[1]
}
this.loading = true
if (this.isPost) {
this.$api.post(this.action, req, r => {
this.loading = false
this.handleOperation(r, isScroll)
})
} else {
this.$api.get4(this.action, req, r => {
this.loading = false
this.handleOperation(r, isScroll)
})
}
},
handleOperation (r, isScroll) {
if (r.flag) {
this.callback && this.callback(r) // 如果要对返回值做额外操作
if (this.isSecondLayer) { // 有分页
if(r.data.allNum){
this.total = +r.data.allNum
}else{
this.total = +r.data.total
}
if (!this.dataList) { // 如果接口返回的data数据为dataList,根据实际情况而定
this.tableData = r.data.dataList
} else { // 其他参数名时取值
this.tableData = r.data[this.dataList]
}
} else { // 没有分页
if (this.isArray) { // 如果数据类型为数组
this.tableData = r.data
} else { // 如果数据类型为非数组
this.tableData = []
this.tableData.push(r.data)
}
}
this.selected && this.handleTest()
isScroll && this.$refs.table.scrollIntoView({behavior: 'smooth', block: 'start'})
}
},
handleCurrentChange (index) {
this.search(index, true)
},
handleSortChange (val) {
this.$emit('sortChange', val)
},
handleSelectionChange (val) {
this.$emit('selectionChange', val)
},
handleSizeChange (val) {
this.pageSize = val
this.search(1, true)
},
handleTest () {
const rows = this.selected.split(',')
this.tableData.forEach((row, i) => {
rows.forEach(_ => {
if (row.id === parseInt(_)) {
this.$nextTick(() => {
this.$refs.test.toggleRowSelection(row, true)
})
}
})
})
},
clearSelected () {
this.$refs.multipleTable.clearSelection()
}
}
}
</script>
<style lang="scss" scoped>
</style>
myTable
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
headerBgColor | 表头背景色 | String | white |
fixHeight | 是否固定表格高度 | boolean | false |
tableH | 表格高度(fixHeight为true, 则tableH必须设定) | number | 0 |
isShowSelection | 是否开启选择功能 | boolean | false |
isShowSerialNumber | 是否显示序号 | boolean | true |
isShowOperation | 是否显示操作列 | boolean | true |
isShowSummary | 是否显示总计 | boolean | true |
fixedType | 是否固定右侧操作栏 | boolean | false |
props | 列标题以及对应的参数名数组 | Array | [] |
tableData | 后台返回的数据 | Array | [] |
immediate | 是否创建即请求数据 | boolean | true |
isShowPagination | 是否显示底部的分页器 | boolean | true |
isSecondLayer | 是否有分页 | boolean | true |
isPost | 是否为post请求 | boolean | true |
isCustomTime | 起始时间划分为两个参数的情况 | boolean | false |
isArray | 返回数据是否为数组形式 | boolean | true |
query | 查询参数 | Object | { } |
action | 请求接口 | String | 必传 |
dataList | 获取data数据的key值 | String | r.data[dataList] |
使用方式:
- 1、数据结构为非数组,只显示一行的统计类表格
data:{
redAmount: "632.00"
people: 3
gift: 11
}
<template>
<div>
<el-date-picker
style="width:220px;"
size="small"
clearable
value-format="yyyy-MM-dd"
v-model="searchInfo.time"
type="daterange"
:picker-options="$utils.pickerOptions"
range-separator="至"
start-placeholder="时间选择"
end-placeholder="时间选择">
</el-date-picker>
<el-button type="primary" size="small" @click="$refs.table.search(1)">查询</el-button>
<mytable style="padding: 10px;" :isArray="false" :action="action" :query="searchInfo" :props="props" ref="table" :isShowSerialNumber="false" :isShowPagination="false" :isSecondLayer="false" :isShowOperation="false"></mytable>
</div>
</template>
<script>
export default {
data() {
return {
action: this.$api.baseUrl + 'xxx/xxx/xxxx',
searchInfo: {
time: ''
},
props: [
{
prop: 'uvNum',
label: '落地页uv'
},
{
prop: 'people',
label: '红包领取人数'
},
{
prop: 'gift',
label: '坚果礼包领取人数'
}
],
};
},
};
</script>
- 2、常用统计表
<mytable dataList="records" :fixedType="true" style="padding: 10px;" :action="action1" :query="searchInfo" :props="props1" ref="table1">
<template #status="{data}">
<p class="status-reject" v-if="data.status === 0">审核不通过</p>
<p class="status-pass" v-if="data.status === 1">审核通过</p>
<p class="status-wait" v-else>未审核</p>
</template>
<template #operation="{data}">
<p class="detail" v-if="!data.status" @click="statusAction(data,1)">审核通过</p>
<p></p>
</template>
</mytable>
<script>
export default {
data() {
return {
action1: this.$api.baseUrl + 'xxx/xxx/xxxx',
searchInfo: {
time: ''
},
props1: [
{
prop: 'uvNum',
label: '落地页uv'
},
{
prop: 'people',
label: '红包领取人数'
},
{
prop: 'gift',
label: '坚果礼包领取人数'
},
{
prop: 'status' ,
label: '审核状态'
}
],
};
},
};
</script>