<template>
<div :ref="mainRef" :key="mainRef" class="table-main">
<div v-if="isShow('query') || isShow('button')" :ref="searchRef">
<el-row
v-if="isShow('button')"
:gutter="10"
style="margin: 0 0 5px 0"
class="header-buttons"
>
<slot name="button" :single="single" :multiple="multiple" />
</el-row>
<el-form
v-if="showSearch && isShow('query')"
ref="queryForm"
:model="queryParams"
:label-width="labelWidth"
class="table-query-form"
>
<el-row>
<slot
name="query"
:handleQuery="handleQuery"
:isExpandAll="isExpandAll"
/>
<el-col :xs="24" :sm="12" :md="8" :lg="6" style="padding-left: 10px">
<el-button icon="el-icon-search" @click="handleQuery"> 搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
<el-tag v-if="showExpandQuery" type="success" plain style="cursor: pointer; margin-left: 10px" @click="toggleExpandAll">
<i :class="isExpandAll ? 'el-icon-sort-up' : 'el-icon-sort-down'" />
{{ isExpandAll ? "折叠" : "展开" }}
</el-tag>
</el-col>
</el-row>
</el-form>
</div>
<el-table
v-if="isShow('table')"
:ref="tableRef"
:key="tableRef"
v-loading="loading"
:height="height === 'auto' ? null : height || tableHeight"
:row-key="rowKey"
:data="listData"
v-bind="$attrs"
:highlight-current-row="!hasCheckbox"
@selection-change="handleSelectionChange"
@current-change="handleCurrentChange"
v-on="$listeners"
>
<slot name="table" :page="{pageNum,pageSize}" />
</el-table>
<el-pagination
v-show="total > 0"
class="pagination-container"
:current-page.sync="currentPageNum"
:page-size.sync="currentPageSize"
:layout="layout"
:page-sizes="pageSizes"
:pager-count="pagerCount"
:total="total"
v-bind="$attrs"
@size-change="handlePageSizeChange"
@current-change="handlePageCurrentChange"
/>
</div>
</template>
<script>
export default {
name: 'ListTable',
props: {
queryParams: {
type: Object,
default: () => {}
},
tableData: {
type: Array,
default: () => null
},
// 列表整体高度
height: {
type: [Number, String],
default: null
},
// 其它高度(用于自动计算高度时,除查询条件高度后的其它高度)
otherHeight: {
type: Number,
default: 0
},
// 本地分页
localPage: {
type: Boolean,
default: false
},
// 默认是否加载数据
defaultLoadData: {
type: Boolean,
default: true
},
// 默认是否加载数据
labelWidth: {
type: String,
default: '120px'
},
showExpandQuery: {
type: Boolean,
default: true
},
// 行数据的 Key,用来优化 Table 的渲染;在使用 reserve-selection 功能与显示树形数据时,该属性是必填的。类型为 String 时,支持多层访问:user.info.id,但不支持 user.info[0].id,此种情况请使用 Function
rowKey: {
type: [Function, String],
default: 'id'
},
pageNum: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 20
},
pageSizes: {
type: Array,
default() {
return [10, 20, 30, 50]
}
},
// 移动端页码按钮的数量端默认值5
pagerCount: {
type: Number,
default: document.body.clientWidth < 992 ? 5 : 7
},
layout: {
type: String,
default: 'total, prev, pager, next, jumper'
}
},
data () {
return {
mainRef: `main${Math.random()}`,
// 表名
tableRef: `list${Math.random()}`,
// 查询
searchRef: `search${Math.random()}`,
// 遮罩层
loading: false,
// 选中数组
selectData: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总数
total: 0,
// 表格数据
listData: [],
// 本地分页所有数据
localPageData: [],
// 高度
tableHeight: null,
// 展开折叠
isExpandAll: false,
// 新增数据集
addData: [],
// 删除数据集
delData: [],
// 已选数据rowKey集
selectRowKeyDatas: [],
// 是否有复选框
hasCheckbox: false,
// 页码
currentPageNum: this.pageNum,
// 每页多少
currentPageSize: this.pageSize
}
},
watch: {
pageNum(newVal) {
this.currentPageNum = newVal
},
pageSize(newVal) {
this.currentPageSize = newVal
},
showSearch: {
handler (route) {
this.resizeTableHeight()
},
immediate: true
},
otherHeight: {
handler (route) {
this.resizeTableHeight()
},
immediate: true
},
tableData: {
deep: true,
handler (val) {
if (val) {
if (this.localPage) {
this.localPageData = val
this.total = this.localPageData.length
this.getLocalPageList()
} else {
this.listData = val
}
}
},
immediate: true
}
},
created () {
const _this = this
if (_this.defaultLoadData) {
_this.getList()
}
_this.addData = []
_this.delData = []
window.addEventListener('resize', function () {
_this.$nextTick(() => {
_this.resizeTableHeight()
})
})
this.$nextTick(() => {
_this.checkForCheckbox()
})
},
methods: {
// 判断slot存在否
isShow (name) {
const flag =
this.$slots[name] !== undefined ||
this.$scopedSlots[name] !== undefined
return flag
},
/* 计算table高度 */
resizeTableHeight () {
if (this.height === 'auto') {return}
this.$nextTick(() => {
// 浏览器呈现高度-搜索框居顶部位置-搜索框高度=列表高度
let height = window.innerHeight
if (this.$refs[this.tableRef]) {
const rect = this.$refs[this.tableRef].$el.getBoundingClientRect()
height = height - rect.y
if (this.total === 0) {
height = height - 18
} else {
height = height - 60
}
this.tableHeight = height - this.otherHeight
}
})
},
/** 查询列表 */
getList (type) {
if (type === 1 && this.localPage) {
this.getLocalPageList()
}
// 本地分页-点击搜索调用搜索方法
if (this.$listeners['query']) {
this.$emit('query', this.queryParams)
} else {
if (!this.$listeners['loadData']) {return}
this.loading = true
const params = Object.assign({ pageNum: this.currentPageNum, pageSize: this.currentPageSize }, this.queryParams)
this.$emit('loadData', params, (callback) => {
if (callback && callback.rows) {
this.listData = callback.rows
this.total = callback.total
} else {
this.listData = []
this.total = 0
}
this.loading = false
this.selectRowSelection()
this.resizeTableHeight()
})
}
},
/** 本地分页 */
getLocalPageList() {
this.listData = this.localPageData.slice(
(this.currentPageNum - 1) * this.currentPageSize,
this.currentPageNum * this.currentPageSize
)
this.selectRowSelection()
this.resizeTableHeight()
},
/** 搜索按钮操作 */
handleQuery () {
this.currentPageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery () {
if (this.$listeners['resetQuery']) {
this.$emit('resetQuery', {}, (callback) => {
this.handleQuery()
})
} else {
this.$refs.queryForm.resetFields()
this.handleQuery()
}
},
/** 多选框选中数据 */
handleSelectionChange(selection) {
const _this = this
if (!_this.addData) _this.addData = []
if (!_this.delData) _this.delData = []
_this.listData.forEach((row) => {
const selected = selection.indexOf(row) !== -1
const rowKey = _this.selectRowKeyDatas.find(t => t === row[_this.rowKey])
const rowAdd = _this.addData.find(t => {return t[_this.rowKey] === row[_this.rowKey] })
if (selected && !rowKey) {
_this.addData.push(row)
_this.selectRowKeyDatas.push(row[_this.rowKey])
const dd = _this.delData.find(t => t[_this.rowKey] === row[_this.rowKey])
if (dd) {
const index = _this.delData.indexOf(dd)
if (index > -1) {
_this.delData.splice(index, 1)
}
}
}
if (!selected && rowAdd) {
const index = _this.addData.indexOf(rowAdd)
if (index > -1) {
_this.addData.splice(index, 1)
}
}
if (!selected && rowKey) {
_this.delData.push(row)
const index = _this.selectRowKeyDatas.indexOf(rowKey)
if (index > -1) {
_this.selectRowKeyDatas.splice(index, 1)
}
}
if (selected && rowKey) {
const dd = _this.delData.find(t => t[this.rowKey] === rowKey)
if (dd) {
const index = _this.delData.indexOf(dd)
if (index > -1) {
_this.delData.splice(index, 1)
}
}
}
})
_this.setSelectData()
_this.$emit('selectionChange', selection)
},
/** 单选选中数据 */
handleCurrentChange(selection) {
if (!this.hasCheckbox && selection) {
this.addData = []
this.addData.push(selection)
this.setSelectData()
}
this.$emit('currentChange', selection)
},
/** 设置已选集合 */
setSelectDatas(val) {
this.checkForCheckbox()
if (val) {
if (!Array.isArray(val)) {
val = [val]
}
if (val.length === 0) {
this.clearSelectDatas()
} else {
if (this.$refs[this.tableRef]) {
this.$refs[this.tableRef].clearSelection()
this.$refs[this.tableRef].setCurrentRow()
}
this.selectRowKeyDatas = this.$vzcCopyObj(val)
this.selectRowSelection()
}
}
},
/** 清除选中集合*/
clearSelectDatas() {
this.selectRowKeyDatas = []
this.delData = []
this.addData = []
if (this.$refs[this.tableRef]) {
this.$refs[this.tableRef].clearSelection()
this.$refs[this.tableRef].setCurrentRow()
}
},
/** 设置选中行*/
selectRowSelection() {
if (this.hasCheckbox) {
this.selectRowKeyDatas.forEach(t => {
const data = this.listData.find(m => {
return m[this.rowKey] === t
})
if (data) {
this.$nextTick(() => {
this.$refs[this.tableRef].toggleRowSelection(data, true)
})
}
})
} else {
this.selectRowKeyDatas.forEach(t => {
const data = this.listData.find(m => {
return m[this.rowKey] === t
})
if (data) {
this.$refs[this.tableRef].setCurrentRow(data)
}
})
}
if (!this.addData) this.addData = []
this.addData.forEach(t => {
if (t) {
const data = this.listData.find(m => {
return m[this.rowKey] === t[this.rowKey]
})
if (data) {
if (this.hasCheckbox) {
this.$refs[this.tableRef].toggleRowSelection(data, true)
} else {
this.$refs[this.tableRef].setCurrentRow(data)
}
}
}
})
},
/** 获取选中行值 */
getSelectData() {
return { addList: this.addData, delList: this.delData }
},
/** 设置选中对象是单还是集合 */
setSelectData() {
this.single = this.addData.length !== 1
this.multiple = !this.addData.length
},
/** 是否有复选框 */
checkForCheckbox() {
if (this.$refs[this.tableRef] && this.$refs[this.tableRef].$children) {
this.hasCheckbox = this.$refs[this.tableRef].$children.some(column => {
return column.type === 'selection'
})
}
},
// 展开折叠
toggleExpandAll () {
this.isExpandAll = !this.isExpandAll
this.resizeTableHeight()
},
setCurrentRow (row) {
this.$nextTick(() => {
this.$refs[this.tableRef].setCurrentRow(row)
})
},
handlePageSizeChange(val) {
if (this.currentPageNum * val > this.total) {
this.currentPageNum = 1
}
this.getList(1)
this.$emit('pagination', { pageNum: this.currentPageNum, pageSize: val })
},
handlePageCurrentChange(val) {
this.currentPageNum = val
this.getList(1)
this.$emit('pagination', { pageNum: val, pageSize: this.currentPageSize })
}
}
}
</script>
<style lang="scss" scoped>
//主色
$main-color:
//背景
$bg-color1:
$bg-color2:
$bg-color3:
$bg-color4:
$bg-color5:
$bg-color6:
$bg-color7:
$bg-color8:
//线条色
$link-color1:
$link-color2:
$link-color3:
$link-color4:
//字体色
$font-color1:
$font-color2:
$font-color3:
$font-color4:
$font-color5:
$font-color6:
//辅色
$fu-color1:
$fu-color2:
$fu-color3:
$fu-color4:
$fu-color5:
$fu-color6:
.table-main {
.table-query-form {
background: linear-gradient($bg-color5, $bg-color3);
padding: 5px;
margin-bottom: 5px;
}
.pagination-container {
float: right;
background: transparent;
padding: 5px 10px;
margin-top: 10px;
white-space:nowrap;
color:$font-color1 !important;
font-weight:700 ;
::v-deep {
& button:hover {
background: radial-gradient(ellipse at center, $bg-color3, $bg-color4);
border-color: $link-color3;
color: $fu-color1;
}
& button:disabled {
color:
background-color: $link-color1;
cursor: default
}
& .btn-next, &n .btn-prev {
background: radial-gradient(ellipse at center, $bg-color2, $bg-color3);
border: 1px solid $main-color;
color: $font-color1;
}
& .el-pager li.disabled {
color:
cursor: default
}
.el-pagination__sizes {
margin: 0 10px 0 0;
font-weight: 400;
color: $font-color1
}
.el-pagination__sizes .el-input .el-input__inner {
font-size: 13px;
padding-left: 8px
}
.el-pagination__sizes .el-input .el-input__inner:hover {
border-color: $main-color
}
.el-pagination__total {
margin-right: 10px;
font-weight: 400;
color: $font-color1
}
.el-pagination__jump {
margin-left: 24px;
font-weight: 400;
color: $font-color1
}
&.is-background .btn-next, &.is-background .btn-prev, &.is-background .el-pager li {
margin: 0 5px;
background-color:
color: $font-color1;
min-width: 30px;
border-radius: 2px
}
&.is-background .btn-next.disabled, &.is-background .btn-prev.disabled, &.is-background .el-pager li.disabled {
color:
}
&.is-background .btn-next:disabled, &.is-background .btn-prev:disabled {
background: radial-gradient(ellipse at center, $bg-color2, $bg-color3);
border: 1px solid $main-color;
color: $font-color1;
}
&.is-background .el-pager li:not(.disabled):hover {
background: radial-gradient(ellipse at center, $bg-color3, $bg-color4);
border-color: $link-color3;
color: $fu-color1;
}
&.is-background .el-pager li:not(.disabled).active {
background: radial-gradient(ellipse at center, $bg-color3, $bg-color4);
border-color: $link-color3;
color: $fu-color1;
}
.el-pager li {
background:radial-gradient(ellipse at center, $bg-color2, $bg-color3);
border: 1px solid $main-color;
color: $font-color1;
}
.el-pager li.btn-quicknext, .el-pager li.btn-quickprev {
line-height: 28px;
color: $font-color1
}
.el-pager li.btn-quicknext.disabled, .el-pager li.btn-quickprev.disabled {
color:
}
.el-pager li.active + li {
border-left: 1px solid $main-color;
}
.el-pager li:hover {
color: $fu-color1;
}
.el-pager li.active {
background: radial-gradient(ellipse at center, $bg-color3, $bg-color4);
border-color: $link-color3;
color: $fu-color1;
cursor: default
}
}
}
}
</style>