el-table公共表格组件

452 阅读1分钟

前提

写这个目的是纯粹记录一下,自己项目中使用到的关于el-table组件二次封装的一些细节。

common-table-list公共组件使用方法

<common-table-list :data="tableData" :pagination="pagination" :total="total" :tableColumnProps="tableColumnProps"
@handlePageChange="handlePageChange" ref="commonTableList">
  <template slot="code" slot-scope="scope">
      xxxx
  </template>
</common-table-list>

// data
tableColumnProps: {
    showSelection: false,
    selectionConfig: {
        width: 0,
        reserveSelection: false
    },
    showIndex: false,
    data: [
        {label: 'xxxx',prop: 'code',showSlot: true},
    ],
},
total: 0,
pagination: {
  pageSize: 10,
  pageNum: 1,
},

// methods 
handlePageChange(obj){
  this.pagination.pageNum = obj.pageNum;
  this.pagination.pageSize = obj.pageSize;
  // 接下来分页获取数据
},

源码分析

el-table部分

  1. template 使用$attrs和$listeners保证该组件能够具备el-table的所有功能
<el-table
    class="common-el-table"
    ref="tableList"
    v-loading="loading"
    v-bind="{...$attrs,...bindProps}"
    :data="tableData"
    tooltip-effect="light"
    height="100%"
    v-on="onTableListeners"
    @selection-change="handleSelectionChange"
    @sort-change="tableSortChange">
</el-table>  
props: {
    data: Array,
    rowKey: String,
    loading: {
        type: Boolean,
        default: false
    },
},
data(){
    return {
        selfEmit: Object.freeze({ // common-table-list组件自身定义的事件,防止重复传递给el-table
            'handlePageChange': 'handlePageChange',
            'filter-change': 'filter-change',
            'clickIcon': 'clickIcon',
            'sort-change': 'sort-change',
            'selection-change': 'selection-change'
        }),
    }
},
computed: {
    // table v-bind
    bindProps() {
        const target = {};
        if(this.rowKey){
            target['row-key'] = this.getRowKeys;
        }
        return target;
    },
    // listeners common-table-list组件自身定义的事件,防止重复传递给el-table
    onTableListeners(){
        const listeners = deepClone(this.$listeners);
        const selfEmit = this.selfEmit;
        Object.keys(selfEmit).forEach((key) => {
            if(listeners.hasOwnProperty(key)) delete listeners[key];
        });
        return listeners;
    },
    tableData:{
        get () {
            return deepClone(this.data || [])
        },
        set (value) {
            return value;
        }
    }
},
methods: {
    // 获取row的key值
    getRowKeys(row) {
      return row[this.rowKey];
    },
    handleSelectionChange(val){
        this.multipleSelection = val;
        this.$emit('selection-change',val);
    },
    /**
     * @description: 监听order变化时触发
     * custom时默认排序方法失效,需自定义
     * @param {*}
     * @return {*}
     */        
    tableSortChange({ prop, order, column }) {
        this.pagination.pageNum = 1;
        // 自定义过滤
        this.$emit('sort-change',{ prop, order, column })
    },
}

el-table-column部分

/**
 * @description: 处理columnData数据v-bind
 * @param {*} source
 * @param {*} filterKeys 需要过滤的数据
 * @param {*} target
 * @return {*}
 */
const handleColumnData = (source,filterKeys,target) => {
    Object.keys(source).forEach((key) => {
        if(source.hasOwnProperty(key) && !filterKeys.includes(key)) target[key] = source[key];
    })
    return target;
}

// tableColumnProps是props传入的值;defaultColumnTableProps 是默认值
// merge 之后传递的值可以不用写,直接取默认值
tableColumnPropsComputed(){
    return merge(deepClone(defaultColumnTableProps),this.tableColumnProps);
},
// props
tableColumnProps: {
    type: Object,
    default(){
        return deepClone(defaultColumnTableProps)
    }
},

实现selection部分

<el-table-column
type="selection"
v-bind="bindSelectionProps"
:width="tableColumnPropsComputed.selectionConfig.width"
v-if="tableColumnPropsComputed.showSelection"></el-table-column>

// computed
// table-column selection v-bind
bindSelectionProps(){
    return this.tableColumnPropsComputed.selectionConfig
},

实现index部分

<el-table-column label="序号" type="index" show-overflow-tooltip v-if="tableColumnPropsComputed.showIndex"></el-table-column>

实现其它column部分

<template v-for="(columnData,columnIndex) in tableColumnPropsComputed.data">
    <el-table-column v-if="columnData.show && columnData.showColumn" :key="columnIndex" :label-class-name="getCoumnLabelClassName(columnData)"
    :show-overflow-tooltip="columnData.showTooltip === void 0 || columnData.showTooltip" v-bind="bindColumnProps(columnData)" :column-key="getKey()">
        <template slot-scope="scope">
            <slot :name="columnData.prop" :row="scope.row" :column="scope.column" :columnData="columnData" :$index="scope.$index" v-if="columnData.showSlot"></slot>
            <span v-else>{{formateTableData(scope,columnData)}}</span>
        </template>
    </el-table-column>
</template>

// computed
// table-column v-bind
bindColumnProps(){
    return function(columnData){
        const target = {};
        const filterKeys = ['show','showColumn','showColumnHidden','sortable','filterable','filterConfig','showTooltip'];
        return handleColumnData(columnData,filterKeys,target);
    }
},

// methods
/**
 * @description: 判断是否是函数
 * @param {*}
 * @return {*}
 */        
isFunction(obj){
    return typeof obj === 'function' && typeof obj.nodeType !== 'number';
},
/**
 * @description: 格式化数据
 * @param {*}
 * @return {*}
 */        
formateTableData(scope,columnData){
    if(columnData.formate && this.isFunction(columnData.formate)) return columnData.formate(scope,columnData);
    if(columnData.formatter && this.isFunction(columnData.formatter)) return columnData.formatter(scope.row, scope.column, scope.row[scope.column.property], scope.$index);
    return scope.row[columnData.prop];
},

el-pagination部分

<!-- 分页 -->
<div class="el-pagination-wrapper" v-show="tableData.length && total">
    <el-pagination background @current-change="handleCurrentChange" @size-change="handleSizeChange"
      :current-page="pagination.pageNum" :page-size="pagination.pageSize" :page-sizes="pageSizes"
      layout="sizes,prev, pager, next,total" :total="total">
    </el-pagination>
</div>

// props
pagination: {
    type: Object,
    default(){
        return {
            pageNum: 1,
            pageSize: 10
        }
    }
},
total: Number,
pageChangeTrigger: String,
pageSizes: {
    type: Array,
    default(){
        return [10, 20, 50, 100, 500]
    }
},

// methods 
 /**
 * @description: 分页
 * @param {*}
 * @return {*}
 */
handlePageChange(){
    const obj = {
        pageNum: this.pagination.pageNum,
        pageSize: this.pagination.pageSize
    }
    this.$emit('handlePageChange',obj);
    if(this.pageChangeTrigger) this.$parent[this.pageChangeTrigger](obj);
},
handleCurrentChange(val) {
    this.pagination.pageNum = val;
    this.handlePageChange();
},
handleSizeChange(val) {
    this.pagination.pageNum = 1;
    this.pagination.pageSize = val;
    this.handlePageChange();
},
/**
 * @description: 将页码前置一页(用于数据删除时,数据已经是分页最后一条时)
 * @param {*}
 * @return {*}
 */        
handleDeletePageNum(){
    const isNeedcPrev = this.tableData.length === 1 || this.multipleSelection.length === this.tableData.length; 
    if(isNeedcPrev) this.pagination.pageNum = this.pagination.pageNum === 1 ? 1 : this.pagination.pageNum - 1;
    this.handlePageChange();
},

其它功能---基于项目自己实现的功能

该组件还实现了以下功能

  1. 自定义排序功能
  2. 操作区el-table-column部分
  3. empty部分
<slot name="empty" slot="empty"></slot>
  1. footer部分
<slot name="footer"></slot>

仓库地址

放在这里,有问题欢迎提出改进