<template>
<div class="ws-base-table">
<el-table
ref="WsBaseTable"
v-bind="$attrs"
v-on="$listeners"
v-loading="loading"
:element-loading-text="elementLoadingText"
style="width: 100%"
:style="styleText"
:header-cell-style="headerCellStyle"
:row-style="rowStyle"
@select="select"
@select-all="selectAll"
@selection-change="selectionChange"
>
<el-table-column
v-if="selectInfo.total >= 0"
width="40"
type="selection"
reserve-selection
:selectable="selectInfo.selectable"
></el-table-column>
<el-table-column v-for="col of tableColumns" v-bind="col" :key="col.prop" :align="col.align || align">
<template v-slot:header="scope">
<slot v-if="col.header" :name="col.header" :scope="{ header: col.label, ...scope }"></slot>
<span v-else>{{ col.label }}</span>
</template>
<template v-if="!['selection', 'index', 'expand'].includes(col.type)" v-slot:default="scope">
<slot :name="col.prop" :scope="{ ...scope, ...col }">
<span>{{ scope.row[col.prop] }}</span>
</slot>
</template>
</el-table-column>
<!-- 空数据时显示的文本内容 -->
<template slot="empty">
<slot name="empty">
<p>{{ loading ? '' : '暂无数据' }}</p>
</slot>
</template>
<!-- 插入至表格最后一行之后的内容,如果需要对表格的内容进行无限滚动操作,可能需要用到这个 slot。若表格有合计行,该 slot 会位于合计行之上。 -->
<template slot="append">
<slot name="append"></slot>
</template>
</el-table>
<!-- 分页组件插槽 -->
<slot v-if="paginationTotal > paginationPageSize" name="pagination"></slot>
<!-- 自定义全选 -->
<div v-if="selectedNumber > 1 && selectInfo.total >= 0" class="select-all-box">
<span v-show="!isSelectAll" class="selected"
>{{ `已选择${selectSuffixText} ${selectedNumber} 个${selectInfo.selectText || '数据'}` }},</span
>
<span v-show="isSelectAll" class="all-selected"
>{{
`已选择${
selectInfo.total === selectedNumber + (selectInfo.disableSelectTotal || 0) ? '全部' : ''
} ${selectedNumber} 个${selectInfo.selectText || '数据'}`
}},</span
>
<span
v-if="selectedNumber < selectInfo.total - (selectInfo.disableSelectTotal || 0)"
class="select-all"
@click="customSelectAll"
>{{
`选择全部 ${selectInfo.total - (selectInfo.disableSelectTotal || 0)} 个${selectInfo.selectText || '数据'}`
}}</span
>
<span v-else class="cancel-select-all" @click="cancelSelectAll">取消全选</span>
</div>
</div>
</template>
<script>
export default {
name: 'WsBaseTable',
inheritAttrs: false,
props: {
// el-table表格列属性:不够可以加
tableColumns: {
type: Array,
default: () => [],
required: true
},
// 对齐方式
align: {
type: String,
default: 'center'
},
// 加载状态
loading: {
type: Boolean,
default: false
},
elementLoadingText: {
type: String,
default: '加载中...'
},
headerCellStyle: {
type: [Object, Function],
default: () => ({
background: '#F6F6F9',
height: '40px',
color: '#909399',
'font-size': '12px'
})
},
rowStyle: {
type: [Object, Function],
default: () => {
return {
height: '56px',
'white-space': 'nowrap',
'font-size': '14px'
};
}
},
// 可勾选的信息: total:表格总条数; disableSelectTotal: 禁勾选总数; canSelectField: 可勾选字段名, 对应值为boolean类型,true代表可勾选; selectable(和el-table的selectable保持一致)
selectInfo: {
type: Object,
default: () => ({
total: -1, // 全选必须
selectText: '数据',
disableSelectTotal: 0, // 可选 禁选必须
canSelectField: '', // 可选 禁选必须
selectable: () => true // 可选 禁选必须
})
}
},
data() {
return {
selectSuffixText: '',
// 是否全选
isSelectAll: false,
// 已勾选的条数
selectedNumber: 0,
// 非全选时,已勾选数组
selectedRows: [],
// 全选时,未勾选数组
unSelectedRows: []
};
},
computed: {
// 计算总条数,只有一页时隐藏分页
paginationTotal() {
let paginationTotal = 0;
if (this.$slots.pagination && this.$slots.pagination[0].componentOptions.propsData.total) {
paginationTotal = this.$slots.pagination[0].componentOptions.propsData.total;
}
return paginationTotal;
},
// 计算分页条数
paginationPageSize() {
let paginationPageSize = 10;
if (this.$slots.pagination && this.$slots.pagination[0].componentOptions.propsData.pageSize) {
paginationPageSize = this.$slots.pagination[0].componentOptions.propsData.pageSize;
}
return paginationPageSize;
},
// 全选条展示的高度
styleText() {
return {
'--all-select-height': this.selectedNumber > 1 ? '40px' : 0
};
}
},
created() {
this.rowKey = this.$attrs['row-key'] || 'id';
// 判断是否绑定了行点击事件,自动给鼠标添加小手样式
if (this.$listeners['row-click'] && typeof this.rowStyle === 'object') {
this.rowStyle.cursor = 'pointer';
}
},
watch: {
selectedNumber: {
handler(n) {
if (n === this.selectInfo.total - (this.selectInfo.disableSelectTotal || 0)) {
this.isSelectAll = true;
}
}
},
'$attrs.data': {
handler(newData) {
this.tableData = newData;
this.isSelectAll && this.autoCheck();
},
immediate: true
}
},
methods: {
// 向外暴露已选数据的方法
getSelectedInfo() {
let { isSelectAll, selectedNumber } = this;
return {
isSelectAll,
selectedNumber,
list: this.isSelectAll ? this.unSelectedRows : this.selectedRows
};
},
// 封装自动勾选
autoCheck() {
const currentSelectedRows = this.getTwoArrayDiff(this.tableData, this.unSelectedRows, this.rowKey);
let addCurrentSelectedRows = currentSelectedRows.filter(
row => !this.selection.map(item => item[this.rowKey]).includes(row[this.rowKey])
);
if (this.selectInfo.canSelectField) {
addCurrentSelectedRows = addCurrentSelectedRows.filter(item => item[this.selectInfo.canSelectField]);
}
addCurrentSelectedRows.forEach(row => {
this.toggleRowSelection(row, true);
this.selection.push(row);
});
},
// 自定义全选
customSelectAll() {
this.isSelectAll = true;
this.selectedNumber = this.selectInfo.total - (this.selectInfo.disableSelectTotal || 0);
this.unSelectedRows = [];
this.autoCheck();
},
// 取消全选
cancelSelectAll() {
this.clearSelection();
this.clearCustomSelection();
},
// 重置勾选(外部查询条件变更,切记不忘了调用)
resetSelect() {
this.cancelSelectAll();
},
// 当用户手动勾选数据行的 Checkbox 时触发的事件
select(selection, row) {
// 未设置全选所有时
if (this.selectInfo.total < 0) return this.$emit('select', selection, row);
if (this.isSelectAll) {
if (this.itemIsInArray(row, this.unSelectedRows, this.rowKey)) {
this.unSelectedRows = this.getTwoArrayDiff(this.unSelectedRows, [row], this.rowKey);
this.selectedNumber += 1;
} else {
this.unSelectedRows.push(row);
this.selectedNumber -= 1;
}
}
this.selectSuffixText = '';
this.selection = [...selection];
},
// 当用户手动勾选全选 Checkbox 时触发的事件
selectAll(selection) {
// 未设置全选所有时
if (this.selectInfo.total < 0) return this.$emit('select-all', selection);
if (this.isSelectAll) {
if (selection.length >= this.selection.length) {
// 取增量
const newAddRows = this.getTwoArrayIntersection(selection, this.unSelectedRows, this.rowKey);
this.unSelectedRows = this.getTwoArrayDiff(this.unSelectedRows, newAddRows, this.rowKey);
this.selectedNumber += newAddRows.length;
} else {
// 取减量
let newReducedRows = this.tableData;
if (this.selectInfo.canSelectField) {
newReducedRows = newReducedRows.filter(row => row[this.selectInfo.canSelectField]);
}
this.unSelectedRows.push(...newReducedRows);
this.selectedNumber -= newReducedRows.length;
}
} else {
this.selectSuffixText = selection.length === this.paginationPageSize ? '本页' : '';
}
this.selection = [...selection];
},
// 当选择项发生变化时会触发该事件
selectionChange(selection) {
// 未设置全选所有时
if (this.selectInfo.total < 0) return this.$emit('selection-change', selection);
if (!this.isSelectAll) {
this.selectedNumber = selection.length;
this.selectedRows = selection;
}
},
// 清空自定义全选
clearCustomSelection() {
this.isSelectAll = false;
this.selectedNumber = 0;
this.selectedRows = [];
this.unSelectedRows = [];
this.selection = [];
},
// 两个数组对象取差集
getTwoArrayDiff(arr1, arr2, key) {
let diffArray = arr1.filter(i => !arr2.map(j => j[key]).includes(i[key]));
return diffArray;
},
// 两个数组对象取交集
getTwoArrayIntersection(arr1, arr2, key) {
let intersectionArray = arr1.filter(i => arr2.map(j => j[key]).includes(i[key]));
return intersectionArray;
},
// 判断元素是否在数组对象中
itemIsInArray(item, arr, key) {
return arr.map(i => i[key]).includes(item[key]);
},
// el-table自带方法,不够可以加,暂时没有想到如何如何避免重复的写法(不想嵌套$refs,只能一个一个添加)
// 用于多选表格,清空用户的选择
clearSelection() {
this.$refs.WsBaseTable.clearSelection();
},
// 用于多选表格,切换某一行的选中状态,如果使用了第二个参数,则是设置这一行选中与否(selected 为 true 则选中)
toggleRowSelection(row, selected) {
this.$refs.WsBaseTable.toggleRowSelection(row, selected);
},
// 对 Table 进行重新布局。当 Table 或其祖先元素由隐藏切换为显示时,可能需要调用此方法
doLayout() {
this.$refs.WsBaseTable.doLayout();
}
}
};
</script>
<style lang="scss" scoped>
.ws-base-table {
position: relative;
.select-all-box {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 32px;
margin: 4px 0;
left: 0;
top: 40px;
background: #fffae2;
border-radius: 4px;
span {
height: 17px;
font-size: 12px;
line-height: 17px;
}
.selected {
color: #333333;
}
.select-all,
.cancel-select-all {
cursor: pointer;
margin-left: 12px;
color: #294ba3;
}
}
/deep/ .el-table__header {
padding-bottom: var(--all-select-height);
}
/deep/ .el-checkbox__inner {
width: 18px;
height: 18px;
}
/deep/ .el-checkbox__inner::after {
height: 10px;
left: 6px;
}
/deep/ .el-checkbox__input.is-indeterminate .el-checkbox__inner::before {
top: 7px;
}
/deep/ .el-table-column--selection .cell {
text-overflow: initial;
}
}
</style>
el-table二次封装支持全选所有(带禁选,反选功能,全选后不用锁定去勾选)
最新推荐文章于 2024-03-06 17:33:34 发布