大家好!我叫戴向天
QQ群:602504799
如若有不理解的,可加QQ群进行咨询了解
<template>
<div ref="table" :class="getClassName(full ? 'full' : '')">
<el-table
style="width:100%"
:stripe="true"
:class="getClassName('table')"
:data="data"
:border="border"
:header-cell-class-name="center ? getClassName('center') : ''"
:cell-style="{ 'text-align': 'left' }"
cell-class-name="content-text"
:header-cell-style="{
background: 'rgba(245, 245, 247, 1)',
color: '#49445f',
'text-align': 'left',
}"
v-loading="pictLoading"
element-loading-text="数据正在加载中"
element-loading-spinner="el-icon-loading"
@selection-change="selectionChange"
:height="height || null"
>
<template slot="empty">
<span>暂无数据</span>
</template>
<el-table-column type="selection" width="60" v-if="showSelection">
</el-table-column>
<el-table-column
v-for="(item, key) in columns"
:key="key"
:label="item.label"
:prop="item.prop"
:width="item.width"
:min-width="item.minWidth"
:showOverflowTooltip="item.showOverflowTooltip"
>
<template slot-scope="scope">
<div class="operation" v-if="item.is === 'operation'">
<el-tooltip
:content="button.name"
v-for="(button, b) in scope.row.buttons"
:key="b"
placement="top"
>
<el-link
:type="button.type"
:class="getClassName('button')"
size="small"
@click="tableOperationHadnler(button.emit, scope.row)"
>{
{ button.name }}</el-link
>
</el-tooltip>
</div>
<div v-else>
{
{ scope.row[item.prop] }}
</div>
</template>
</el-table-column>
</el-table>
<div v-if="showPagination" :class="getClassName('pagination')">
<el-pagination
background
layout="total, sizes, prev, pager, next, jumper"
:total="pagination.total"
:page-size="pagination.size"
:page-sizes="pagination.sizes"
:current-page="pagination.currentPage"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
></el-pagination>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop, Watch } from 'vue-property-decorator';
@Component({
name: 'TableList',
components: {},
})
export default class TableList extends Vue {
// 开启这个的话,将会实现实时请求,比如在关键词查询的时候,将会一边输入一边请求
// @Watch("params", {
// deep: true,
// })
// private paramsWatch() {
// this.lastMethod = "";
// this.autoHandler();
// }
/**
* 判读是否可以进行自动处理
* 如果可以的话,将会返回请求的配置信息
* 如果不可以的话,将会返回false
*/
private get canHandler() {
const dataType: string = typeof this.autoConfig;
const all = dataType === 'function' ? this.autoConfig() : this.autoConfig;
const { auto, method, rule, server } = all;
// 是否具备自动处理的条件
const conditional = 'auto' in all && 'method' in all;
// 方法是不是字符串参数
const isRightVoid = typeof method === 'string';
// 是否进行自动处理
const isAuto = typeof auto === 'boolean' && auto;
// 是否有规则处理
const hasRule = typeof rule === 'function';
// 是否有服务
const hasServer = typeof server === 'object';
if (conditional && isRightVoid && isAuto && hasRule && hasServer) {
return all;
} else {
return false;
}
}
// 获取请求参数
private get getParams() {
const params = {
...(this.params || {}), // this.params只是作为一个辅助性的参数
};
const props = this.props; // 字段信息
const pagination = this.pagination; // 分页信息
// 字段信息转化
return Object.keys(this.props).reduce((frist: any, str: string) => {
const key = props[str];
if (str === 'number') {
// 页码数据
frist[key] = pagination.currentPage;
} else if (str === 'size') {
frist[key] = pagination.size;
}
return frist;
}, params);
}
@Prop({
default: () => [],
})
private tableData?: any[];
@Prop({
default: true,
})
private full?: boolean;
// 是否居中
@Prop({
default: false,
})
private center?: boolean;
// 表格高度
@Prop({
default: false,
})
private border?: boolean;
// 表格字段
@Prop({
default: () => [],
})
private columns?: any[];
// 是否显示选择框
@Prop({
default: false,
})
private showSelection?: boolean;
// 是否显示分页
@Prop({
default: true,
})
private showPagination?: boolean;
// 是保存内容为一行,并且当鼠标移入的时候进行展示全文
@Prop({
default: true,
})
private showOverflowTooltip?: boolean;
/**
* Author: 戴向天
* 自行处理的配置参数 - 主要是处理请求方面的事情
* @param method {void} 请求函数 - 必传
* @param params {Object} 请求参数 - 可选
* @param auto {Boolean} 是否自行处理 - 必传
* @param rule {void} 请求结果的处理规则 - 当请求到数据之后,将会以回调的形式调用 rule - 必传
* rule 将会接收到一个请求的结果参数,
* rule 所需要做的处理就是将最终的结果进行返回出来
* @param server {Vuex Server}
* */
@Prop({
default: () => ({
method: null,
params: {},
auto: false,
rule: null,
server: null,
}),
})
private autoConfig?: any;
/**
* 请求的附属参数
* 当前的tableList只是做关于分页的处理请求
* 但是有些接口所需要的参数不仅仅是页码和页码数
* 这个时候就可以通过params来进行告知tableList组件,在请求的时候还有一些附属性的参数
* */
@Prop({
default: () => ({}),
})
private params?: any;
/**
* 分页的字段名称转化
* 由于每个接口它的页码和页码数的字段名可能不是一致的,
* 这个时候可以通过props来进行解决,目前的话,主要就是页码和页码数的字段可以进行更改
*
* {
* '//number': '取值就是接口所需要的页码字段名称'
* number: 'page', //页码
* '/size/': '取值就是接口所需要的页码数字段名称'
* size: 'pageSize' //页码数
* }
*/
@Prop({
default: () => ({ number: 'number', size: 'size' }),
})
private props?: any;
private height: string | number = '';
// 分页参数
private pagination: any = {
size: 10,
sizes: [10, 20, 30, 40, 50],
total: 0,
currentPage: 1,
};
// 自动处理的时候,将方法名进行保存下来
private lastMethod: string = '';
// 渲染数据
private data: any = [];
// 加载动画
private pictLoading = false;
// 已选中的数据
private selections: any[] = [];
@Watch('tableData', {
deep: true,
})
private tableDataWatch() {
this.data = JSON.parse(JSON.stringify(this.tableData || []));
}
// 表格按钮操作
private tableOperationHadnler(emit: string, row: any) {
this.$emit('tableOperationHadnler', emit, row);
}
// 表格选择
private selectionChange(selections: any) {
this.$emit('selectionChange', selections);
this.selections = selections;
}
// 返回表格选中的数据
private getSelections() {
return this.selections;
}
private setPagination(data?: {
size: number;
sizes: number[];
total: number;
currentPage: number;
}) {
this.pagination = {
...this.pagination,
...data,
};
}
// page size 有变化的时候进行触发
private handleSizeChange(data: any) {
this.pagination.size = data;
this.autoHandler();
}
// 当前分页的页码有变化的时候进行触发
private async handleCurrentChange(data: any) {
this.pagination.currentPage = data;
this.autoHandler();
}
// 请求处理
private async requestHandler() {
this.pictLoading = true;
let params: any = this.getParams;
if (
this.canHandler.before &&
typeof this.canHandler.before === 'function'
) {
params = this.canHandler.before(params);
}
const res = await this.canHandler.server[this.canHandler.method](
this.getParams,
);
const { total, data } = this.canHandler.rule(res);
if (Array.isArray(data)) {
if (
data.length === 0 &&
this.pagination.currentPage > 1 &&
this.pagination.currentPage > 0
) {
this.pagination.currentPage--;
this.autoHandler();
} else {
this.data = data;
}
}
this.pagination.total = total || 0;
this.pictLoading = false;
}
/** 执行自动处理 */
private autoHandler(reload?: boolean, num?: number) {
if (this.canHandler) {
if (this.lastMethod !== this.canHandler.method || reload) {
this.lastMethod = this.canHandler.method;
this.handleCurrentChange(num || 1);
} else {
// 为了防止数据未能及时更新,进行强行等待vue处理完成之后在进行执行
this.$nextTick(this.requestHandler);
}
}
}
// 获取数据类型的方法
private getType = (o: any, s?: string) => {
let t = Object.prototype.toString.call(o).split(' ')[1];
t = t
.substring(0, t.length - 1)
.trim()
.toLowerCase();
if (
t === 'object' &&
Object.prototype.toString.call(o).toLowerCase() === '[object object]' &&
!o.length
) {
t = 'json';
}
return s ? t === s : t;
}
private setHeight() {
if (this.full) {
const pH = (this.$el.querySelector('.el-table') as HTMLDivElement)
.offsetHeight;
const cH = ((this.$el.querySelector(
'.el-table',
) as HTMLDivElement).querySelector(
'.el-table__header-wrapper',
) as HTMLDivElement).offsetHeight;
this.height = pH - cH;
}
}
private mounted() {
this.$nextTick(() => this.setHeight());
}
private created() {
if (this.params[this.props.number]) {
this.pagination.currentPage = this.params[this.props.number];
}
if (this.tableData) {
this.data = JSON.parse(JSON.stringify(this.tableData || []));
}
this.autoHandler(true, this.pagination.currentPage);
}
}
</script>
<style lang="less" scoped>
.tableList {
&-full {
display: flex;
flex-direction: column;
flex-grow: 1;
padding: 0 0 unit(30 / @fontSize, rem);
overflow: hidden;
.tableList-table {
flex-grow: 1;
}
}
&-button {
/deep/ span {
font-size: unit(20 / @fontSize, rem);
}
}
&-pagination {
display: flex;
margin-top: unit(10 / @fontSize, rem);
justify-content: flex-end;
}
/deep/ .tableList-center > div {
text-align: center;
}
}
/deep/ .el-table thead {
.el-checkbox {
padding-left: unit(13 / @fontSize, rem);
}
}
/deep/ table thead {
.cell {
font-size: unit(20 / @fontSize, rem);
font-weight: bolder;
color: #534d6a;
padding: unit(5 / @fontSize, rem);
}
}
.operation {
/deep/ .el-link + .el-link {
margin-left: unit(10 / @fontSize, rem);
}
}
/deep/ .content-text {
padding: unit(18 / @fontSize, rem) 0;
/deep/.cell > div {
font-size: unit(20 / @fontSize, rem);
}
}
</style>