表格作为后台管理系统的最重要的基础组件之一,功能越强大,后续所做的开发量就越小,这里把常见的提炼出来,分享给大家。基础功能支持loading,分页,按钮操作,数据格式化,数据选择等。
{
"element-ui": "2.11.1",
"vue": "^2.6.10",
"vue-router": "^3.0.1",
"font-awesome": "4.7.0"
}
1.组件
src/components/Table.vue
<template>
<div>
<div v-if="title" class="table-title">{
{ title }}div>
<el-table
:ref="ref"
v-loading="listLoading"
:data="tableData"
:border="isBorder"
:element-loading-text="loadingText"
:header-cell-style="{
backgroundColor: '#e5e9f2', color: '#333' }"
highlight-current-row
stripe
size="small"
empty-text="暂无数据"
@selection-change="handleSelectionChange"
>
<el-table-column v-if="isSelection" type="selection" width="55">el-table-column>
<el-table-column v-if="isShowNumber" fixed="left" type="index" :index="tableIndex">el-table-column>
<el-table-column
v-for="(item, index) in config"
:key="index"
:prop="item.prop"
:label="item.label"
:width="item.width"
:show-overflow-tooltip="item.showOverflowTooltip"
>
<template slot-scope="scope">
<span v-if="item.format === 'img'">
<img title="点击查看大图" alt="图片" class="image-size" :src="scope.row[item.prop]" />
span>
<span v-else-if="item.format === 'timestamp'">{
{ scope.row[item.prop] | parseTime }}span>
<span v-else-if="item.format === 'money'">{
{ '¥'+scope.row[item.prop] }}span>
<span v-else-if="item.format === 'rate'">{
{ scope.row[item.prop]+"%" }}span>
<span v-else-if="item.format === 'number'">{
{ Number(scope.row[item.prop]) }}span>
<span v-else-if="item.format === 'a'">
<u class="link">
<a :href="scope.row[item.prop]" target="_blank">{
{ scope.row[item.prop] }}a>
u>
span>
<span v-else>{
{ scope.row[item.prop] }}span>
template>
el-table-column>
<el-table-column
v-if="isHasButtons"
class="clearfix"
:width="optionColumnWidth+'px'"
fixed="right"
label="操作"
>
<template slot-scope="scope">
<span
v-for="(option, index) in getOptionsName(scope.row.buttonKey).slice(0,buttonCount)"
:key="index"
class="button-margin-left"
>
<el-button
size="small"
:type="index == 0 ? 'primary' : ''"
@click="handleClickOption(scope.$index, scope.row, option,$event)"
>
<span v-html="getButtonHtml(option)">span>
el-button>
span>
<el-dropdown
v-if="getOptionsName(scope.row.buttonKey).length > buttonCount"
trigger="click"
>
<span class="el-dropdown-link">
更多
<i class="el-icon-arrow-down el-icon--right">i>
span>
<el-dropdown-menu slot="dropdown" class="custom-dropdown-menu">
<el-dropdown-item
v-for="(option, index) in getOptionsName(scope.row.buttonKey).slice(buttonCount)"
:key="index"
class="custom-dropdown-menu-item"
>
<el-button
size="small"
@click="handleClickOption(scope.$index, scope.row, option,$event)"
>
<span v-html="getButtonHtml(option)">span>
el-button>
el-dropdown-item>
el-dropdown-menu>
el-dropdown>
template>
el-table-column>
el-table>
<div v-if="isShowPagination && total > 0" class="pagination-container">
<el-pagination
background
layout="total, sizes, prev, pager, next, jumper"
:current-page="page"
:page-sizes="pageSizeList"
:page-size="limit"
:total="total"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
>el-pagination>
div>
div>
template>
<script>
export default {
name: 'AppTable',
props: {
// 是否需要序号
isShowNumber: {
type: Boolean,
default: true
},
// 是否需要翻页组件
isShowPagination: {
type: Boolean,
default: true
},
// 是否需要栅格
isBorder: {
type: Boolean,
default: true
},
// 是否是多选表格,默认非多选 :ref="'multipleTable'"
isSelection: {
type: Boolean,
default: false
},
// 是否有操作按钮
isHasButtons: {
type: Boolean,
default: true
},
// 表头名称
title: {
type: String
},
// 表格数据
tableData: {
type: Array,
required: true
},
// 表格配置信息
config: {
type: Array,
required: true
},
// loading提示框
loadingText: {
type: String,
default: '加载中...'
},
// loading状态
loadingStatus: {
type: Boolean,
default: false
},
// 表格查询列表参数
listQueryParams: {
type: Object
},
// 根据当前行的状态显示按钮的名称
buttonsName: {
type: Object
},
// 操作列表宽度
optionColumnWidth: {
type: Number,
default: 100
},
// 分页数据
pageSizeList: {
type: Array,
default: function() {
return [10, 20, 30, 50, 100]
}
},
// 显示的操作栏按钮个数,多余的显示在dropdown里
buttonCount: {
type: Number,
default: 2
}
},
computed: {
// 看是否是多选表格
ref: function() {
return this.isSelection ? 'multipleTable' : undefined
},
// 第几页
page: function() {
return this.listQueryParams.page || 1
},
// 每页数据数
limit: function() {
return this.listQueryParams.limit || 10
},
// 数据总数
total: function() {
return this.listQueryParams.total || 0
},
// 获取当前loading的状态
listLoading: function() {
return this.loadingStatus
}
},
methods: {
// 根据按钮的名称,获取按钮的html样式,让按钮有个图标,这里使用的是font-awesome,要使用的话先安装
getButtonHtml(name) {
let className
switch (name) {
case '查看':
className = 'eye'
break
case '编辑':
className = 'pencil'
break
case '审核':
className = 'check-circle'
break
case '置顶':
className = 'arrow-circle-o-up'
break
case '取消置顶':
className = 'times-circle'
break
case '推荐':
className = 'thumbs-o-up'
break
case '上线':
className = 'space-shuttle'
break
case '下线':
className = 'space-shuttle'
break
case '删除':
className = 'times-circle-o'
break
case '分析':
className = 'bar-chart'
break
case '排序':
className = 'sort'
break
}
if (className) {
return `${
className}"> ${
name}`
} else {
return name
}
},
// 获取当前操作的按钮组
getOptionsName(key) {
return this.buttonsName[key] || []
},
// 点击按钮传递给父组件
handleClickOption(index, row, option) {
this.$emit('subOpitonButton', index, row, option)
},
// 表格选择分发事件
handleSelectionChange(val) {
this.$emit('subSelected', val)
},
// 改变翻页组件中每页数据总数
handleSizeChange(val) {
this.listQueryParams.limit = val
// 改变翻页数目,将页面=1
this.listQueryParams.page = 1
this.$emit('update:listQueryParams', this.listQueryParams)
this.$emit('subClickPagination', this.listQueryParams)
},
// 跳到当前是第几页
handleCurrentChange(val) {
this.listQueryParams.page = val
this.$emit('update:listQueryParams', this.listQueryParams)
this.$emit('subClickPagination', this.listQueryParams)
},
// 重写索引生成方法
tableIndex(index) {
const i = (this.page - 1) * this.limit + index + 1
return i
}
}
}
script>
<style lang="scss" scoped>
.image-size {
width: 30px;
height: 30px;
cursor: pointer;
}
.table-title {
margin-top: 10px;
font-size: 18px;
}
.el-dropdown {
cursor: pointer;
margin-left: 20px;
.el-dropdown-link {
user-select: none;
cursor: pointer;
}
}
.button-margin-left {
margin-left: 8px;
}
.link {
cursor: pointer;
color: #4876ff;
}
style>
<style lang="scss" >
.el-table {
.el-table__empty-block {
height: unset;
}
}
.custom-dropdown-menu {
cursor: pointer;
.custom-dropdown-menu-item {
margin-top: 10px;
text-align: center;
}
.el-dropdown-menu__item:focus,
.el-dropdown-menu__item:not(.is-disabled):hover {
background-color: #fff;
color: #fff;
}
}
style>
2.使用
<template>
<div>
<el-row class="margin-top-10">
<el-card>
<app-table :list-query-params.sync="listQueryParams" v-bind="tableAttrs" v-on="tableEvent" />
el-card>
el-row>
div>
template>
<script>
import AppTable from '@/components/Table'
// 定义的接口根据自己项目更换
import TalentServe from '@/api/talent'
// 表格查询参数
const DefaultTableQuery = {
page: 1,
limit: 10,
total: 0
}
export default {
name: 'UserList',
components: {
AppTable
},
data() {
return {
// 表格加载loading
loadingStatus: false,
// 操作栏宽度
optionWidth: 280,
// 表头配置 prop字段和服务端数据给的字段一致
tableConfig: [
{
label: '昵称',
width: '120px',
prop: 'nickname'
},
{
label: '头像',
width: '120px',
prop: 'headImgUrl',
// 显示图片
format: 'img'
},
{
label: '手机号',
width: '120px',
prop: 'mobile'
},
{
label: '注册时间',
width: '140px',
prop: 'registrationDate',
// 显示时间
format: 'timestamp'
},
{
label: '邀请人',
width: '80px',
prop: 'inviterUser'
},
{
label: '收益金额',
width: '120px',
prop: 'integrals',
format: 'money'
},
// 最后一个不给宽度让表格自适应
{
label: '类型',
prop: 'customIsExpressive'
}
],
// 参数
listQueryParams: {
...DefaultTableQuery },
// 列表数据
tableData: [],
// url参数
params: {
pageInfo: {
pageSize: 10,
pageNo: 1
}
},
// 操作栏按钮
buttonsName: {
normal: ['查看', '编辑', '设为可提现'],
special: ['查看', '编辑', '取消可提现']
},
// 选择数据
selectData: [],
// 操作数据
operationalData: {
}
}
},
computed: {
// 表格属性
tableAttrs() {
return {
// 表头配置
config: this.tableConfig,
// 表格数据
tableData: this.tableData,
// loading
loadingStatus: this.loadingStatus,
// 按钮名称
buttonsName: this.buttonsName,
// 操作栏宽度
optionColumnWidth: this.optionWidth,
// 是否需要选择
isSelection: true
}
},
// 表格事件
tableEvent() {
return {
// 按钮操作
subOpitonButton: this.handleTableOption,
// 分页
subClickPagination: this.handleRefreshList,
// 表格数据选择
subSelected: this.handleSelectionChange
}
}
},
created() {
this.getList()
},
methods: {
// 获取列表
async getList() {
try {
// 表格加载loading
this.loadingStatus = true
// 分页数据作为参数给服务端
this.params.pageInfo.pageSize = this.listQueryParams.limit
this.params.pageInfo.pageNo = this.listQueryParams.page
// 发送请求,请求到的数据格式见下文,
const {
data, cntData } = await TalentServe.getTalentList(this.params)
const tableData = data || []
tableData.forEach(item => {
if (item.isExpressive === '1') {
// 给每一行设置buttonKey对应于data里定义的buttonsName的key值
// buttonsName: {
// normal: ['查看', '编辑', '设为可提现'],
// special: ['查看', '编辑', '取消可提现']
// },
//
item.buttonKey = 'special'
} else {
item.buttonKey = 'normal'
}
// 修改显示的数据
item.customIsExpressive =
item.isExpressive === '1' ? '可提现' : '不可提现'
})
// 分页组件显示 this.listQueryParams.total 值大于0才会出现
this.listQueryParams.total = cntData
// 数据给表格
this.tableData = data
this.loadingStatus = false
} catch (error) {
console.log(error)
}
},
// 表格操作功能 index:表格索引, row:表格行数据, option:按钮名称
handleTableOption(index, row, option) {
this.operationalData = {
...row }
if (option === '查看') {
console.log(index, row, option)
} else if (option === '编辑') {
console.log(index, row, option)
} else if (option === '设为可提现') {
console.log(index, row, option)
} else if (option === '取消可提现') {
console.log(index, row, option)
}
},
// 选择的数据回调
handleSelectionChange(data) {
console.log(data)
},
// 分页操作
handleRefreshList() {
this.getList()
}
}
}
script>
3.请求到的服务端数据
{
result: true,
message: null,
data: [
{
headImgUrl:
'https://wx.qlogo.cn/mmopen/vi_32/RYIWG1XDAOVbAXHWONRiab639VRyr7cKQcCibXz8s2m4xhuDmDYo8XxGde0NiaAJ5vy8SHznDIoQ7mBtLibg9sicia0w/132',
nickname: '万马奔腾',
mobile: '13892188637',
registrationDate: 1556666683000,
userId: '0172cf8bbda841dca100aa8b30b1d58e',
inviterUser: null,
inviterId: null,
friendsNum: 0,
friendsNumSum: 0,
integrals: 0.0,
isExpressive: ''
},
{
headImgUrl:
'https://wx.qlogo.cn/mmopen/vi_32/DYAIOgq83eptKEXTYEu3Ne3pF7OcUvibUBY0hSibibH7SRTNxN9x7522mYQZ63ZEBF8nbjty1NN60ScPicsuGGrrtQ/132',
nickname: ' 水目菊 ',
mobile: '15991695887',
registrationDate: 1560258459000,
userId: '021c02454ea64fab8640f1fa25e18637',
inviterUser: null,
inviterId: null,
friendsNum: 0,
friendsNumSum: 0,
integrals: 0.0,
isExpressive: ''
},
{
headImgUrl:
'https://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKLvV2y1XiaJJDfibB7zlqaTudCEsiapR8iaShFykZ2SgVD24zxYve5kPLvQooFy4ibePbpcribtZJVXlEQ/132',
nickname: '老要',
mobile: '',
registrationDate: 1563934025000,
userId: '024bc4c4f5db418499d3633621b2b834',
inviterUser: null,
inviterId: null,
friendsNum: 0,
friendsNumSum: 0,
integrals: 0.0,
isExpressive: ''
},
{
headImgUrl:
'https://wx.qlogo.cn/mmhead/2yfPLY1YiaibeyxYwultf0Z7VNea1H1icXrV9pQcldEHTc/132',
nickname: '林群梦',
mobile: '',
registrationDate: 1557438206000,
userId: '035826db900d4b7a912664119189b6f1',
inviterUser: null,
inviterId: null,
friendsNum: 0,
friendsNumSum: 0,
integrals: 0.0,
isExpressive: ''
},
{
headImgUrl:
'https://wx.qlogo.cn/mmopen/vi_32/DYAIOgq83eqF8zPxScdIGo0nK7so6bzFibRF1sEXrT404XUN2gcfFRTB1pcCaZPBUr9lGKGibbiaUWVTOw7HpaNNg/132',
nickname: '贺励',
mobile: '',
registrationDate: 1563490853000,
userId: '035b59b061454336953d47822c7b0406',
inviterUser: null,
inviterId: null,
friendsNum: 0,
friendsNumSum: 0,
integrals: 0.0,
isExpressive: ''
},
{
headImgUrl:
'https://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTLsymhiblxYck4YOUxdtpZOib12KgJj6ciaJxV2ibtia92YODBhC05YL8rCNKQQjc09tZTP6xBxkT0qK5Q/132',
nickname: '乾坤',
mobile: '15091593977',
registrationDate: 1557153760000,
userId: '03a91cb5a60f4f02bf22b1e9e179258d',
inviterUser: null,
inviterId: null,
friendsNum: 0,
friendsNumSum: 0,
integrals: 0.0,
isExpressive: ''
},
{
headImgUrl:
'https://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTJpTy32D4vRETGtricKwfhx3kiaTJcibRyMFg5KvSENibeWKE0Xq4gZ9ZyicZLvkUQ4RSVBpwwiaOhW5lLg/132',
nickname: '鸿涛',
mobile: '13909256676',
registrationDate: 1556680061000,
userId: '03d7811ea5cb4e1e851c5245128674c2',
inviterUser: '三百千.刘震',
inviterId: 'b33b3879531d41e4bc19b629cb7c7cb8',
friendsNum: 0,
friendsNumSum: 0,
integrals: 408.0,
isExpressive: ''
},
{
headImgUrl:
'https://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKLuf2cK1w7sN3qD0cM4XvbYWnJJa5hibyUKGv0oPzG0LT1UjFvTTRuibOCTvxDibQdU9icFxK1TZOSHQ/132',
nickname: '格格',
mobile: '',
registrationDate: 1560569395000,
userId: '04346cf9a9284d058f71a59de4b37e49',
inviterUser: null,
inviterId: null,
friendsNum: 0,
friendsNumSum: 0,
integrals: 0.0,
isExpressive: ''
},
{
headImgUrl:
'https://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTJ9PZJVYXlalZyggdlVpCoPJcvIHrh1eN3SRRoTkdBgGjhQwrBQMibYma0xkdf6BlaKHLdv5YzM1AA/132',
nickname: '水木清华',
mobile: '13359265165',
registrationDate: 1557146507000,
userId: '05f3c11d67044e30a61abd79fc542c36',
inviterUser: null,
inviterId: null,
friendsNum: 0,
friendsNumSum: 0,
integrals: 0.0,
isExpressive: ''
},
{
headImgUrl:
'https://wx.qlogo.cn/mmopen/vi_32/PiajxSqBRaEJiaXZyZ6Tnv4z7YgvVXRvuONlTc9DNQVS0d52HnScISc77XGKawodayVib3fnLwV9trSDlUkRKwHRw/132',
nickname: '王鸣@ODIN',
mobile: '',
registrationDate: 1560861936000,
userId: '07087c0478ae43b18765272fbb4aa5e1',
inviterUser: null,
inviterId: null,
friendsNum: 0,
friendsNumSum: 0,
integrals: 0.0,
isExpressive: ''
}
],
cntPage: 33,
cntData: 323
}
3.补充表格组件用的过滤器(自行注册,步骤略)
export function parseTime(time, cFormat) {
if (arguments.length === 0) {
return null
}
if ((time + '').length === 10) {
time = +time * 1000
}
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
date = new Date(parseInt(time))
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key]
if (key === 'a') {
return ['一', '二', '三', '四', '五', '六', '日'][value - 1]
}
if (result.length > 0 && value < 10) {
value = '0' + value
}
return value || 0
})
return time_str
}