基於ElementUI封裝一個表格組件 tableList

背景

咱們項目中使用的表格通常都比較相似,若是不進行封裝的話,那麼每一個頁面均可能有一些相似的代碼。不只浪費時間,並且因爲開發人員不一樣的開發習慣。後期維護人員須要花費一點時間去看每一個人的代碼。因此我直接將表格作一個二次封裝,只要一我的去維護這份代碼便可。下面是我封裝的內容vue

內容:

一、支持直接傳入後臺請求地址渲染列表,且參數修改以後自動刷新
二、支持自定義每一列的顯示
三、支持根據內容自動撐開列寬
四、支持動態篩選表頭 
五、支持分頁
六、防抖
七、列權限
... 
更多請自行嘗試
複製代碼

如下是tableList 組件的代碼

<template>
<!--刷新按鈕 和 篩選列的多選框 layout中要有 control 才顯示,默認顯示-->
  <div class="table">
    <div class="table-control" v-if="layoutKey.control">
      <div class="table-control-title" @click="reload">
        <i class="el-icon-refresh"></i></div>
      <el-dropdown class="table-control-title">
        <span class="el-dropdown-link"><i class="el-icon-s-operation table-control-icon"></i></span>
        <el-dropdown-menu slot="dropdown" class="table-control-checkbox">
          <el-checkbox-group v-model="headItems" @change="changeChecked">
            <el-checkbox class="table-control-checkbox-item"
                         v-for="(item,index) in allColumn"
                         :label="item"
                         :key="index">{{item}}
            </el-checkbox>
          </el-checkbox-group>
        </el-dropdown-menu>
      </el-dropdown>
    </div>
    <!--列表主體-->
    <el-table class="table" style="width: 100%"
              ref="tableList"
              :data="tableData"
              :defaultSort.async="defaultSort"
              v-bind="$attrs"
              v-on="$listeners"
              @selectionChange="selectionChange"
              @sort-change="sortChange">
      <ex-table-column v-if="layoutKey.expand" type="expand" fixed>
        <slot name="expand" :data="props" slot-scope="props"/>
      </ex-table-column>
      <ex-table-column v-if="layoutKey.checkBox" fixed type="selection" :width="62"
                       :selectable="selectable"/>
      <ex-table-column v-if="layoutKey.index" type="index" label="序號" width="60"/>
      <ex-table-column v-for="(column,index) in activeColumn" :key="index"
                       :prop="column.field"
                       :column-key="column.field" :label="column.title" :fixed="column.fixed"
                       :sortable="column.sort" :selectable="column.selectable"
                       :show-overflow-tooltip="column.tooltip"
                       :autoFit='true' :width="column.width"
                       :fitByClass="autoWidth(column.width)"
                       :minWidth="column.minWidth || defaultWidth">
        <slot :name="column.field" :data="scope.row" :field="column.field" :content="column.field"
              :index="index" slot-scope="scope">
          <div>{{$utils.nvl(scope.row[column.field],'--')}}</div>
        </slot>
      </ex-table-column>
    </el-table>
  <!--分頁控件,layout中要有 control 才顯示,默認顯示-->
    <el-pagination background small class="table-pagination"
                   :current-page.sync="page.pageNo"
                   :page-sizes="page.list"
                   :page-size="page.pageSize"
                   :layout="page.layout" :total="page.total"
                   @current-change="handleCurrentChange"
                   @size-change="handleSizeChange" v-if="layoutKey.page"/>
  </div>
</template>

<script>
  import debounce from 'lodash/debounce';
  import ExTableColumn from './ExTableColumn';

  export default {
    components: { ExTableColumn },
    // 提供出來給其餘組件調用,具體用法參考 vue 官方用法
    provide() {
      return {
        tableList: this,
      };
    },

    props: {
    // 默認的表格大小
      defaultWidth: {
        type: Number,
        required: false,
        default: 100,
      },
    // 顯示的控件,目前就page,control 兩個可選,可根據需求自行擴展
      layout: {
        default: 'page,control',
      },
    // 多選時 返回的key,默認id
      checkKey: {
        type: [Number, String],
        default: () => 'id',
      },
    // 請求參數,必填
      req: {
        type: Object,
        required: true,
        default: () => ({
          url: undefined,
          type: 'post',
          params: {
            query: {},
          },
          pageNo: 1,
          pageSize: 1,
        }),
      },
    // 默認排序,參考 elementUI table 用法
      defaultSort: {
        type: [String, Object],
        required: false,
      },
      // 列表顯示的列
      //  {
            title :    必填  String,      顯示的列名
            field :    必填  String ,     列中的key
            width :    選填,String        列寬,單位像素,
            fixed :    選填,String        是否固定的列,可選 right, left
            sort  :    選填,Boolean       是否可排序
            expend:    選填,Boolean       是否可展開,配置slot:expand 顯示展開內容
            limit :     選填,Boolean       權限控制,false 則不顯示
        }
      columns: {
        type: Array,
        required: true,
        default: () => [{ title: '操做', field: 'ctrl', width: '60', fixed: 'right' }],
      },

    // 這一行的 CheckBox 是否能夠勾選,用法參考elementUI table用法
      selectable: {
        type: Function,
        default: () => true,
      },
      
      // 其餘table參數,都會傳給table
    },
    data() {
      return {
        layoutKey: {},

        page: {
          list: [5, 10, 20, 50, 100],
          total: null,
          pageSize: 10,
          pageNo: 1,
          layout: 'total,sizes,prev,pager,next,jumper',
        },

        tableData: [],
        sort: '',
        checkRows: [],
        checkKeys: [],

        headItems: [],
        allColumn: [],
        activeColumn: [],
      };
    },

    methods: {
      sortChange({ prop, order }) {
        this.sort = `${prop} ${order === 'descending' ? 'desc' : 'asc'}`;
        this.refresh();
      },

      selectionChange(selection) {
        this.checkRows = selection;
        this.checkKeys = [];
        selection.map((row) => {
          this.checkKeys.push(row[this.checkKey]);
          return true;
        });
        this.$emit('selectionChange', selection);
      },

      /** **************************method*********************************** */

      // 分頁
      async handleCurrentChange(currentPage) {
        this.page.pageNo = currentPage;
        this.$emit('handleCurrentChange', currentPage);
        await this.initTable({ vm: this });
      },

      handleSizeChange(size) {
        this.page.pageSize = size;
        this.page.pageNo = 1;
        this.$emit('handleSizeChange', size);
        this.initTable({ vm: this });
      },

      /** *****************************動態渲染頭部************************************* */
      // 取消選中菜單
      changeChecked() {
        this.changeColumn({ vm: this });
      },
      changeColumn: debounce(async ({ vm }) => {
        const that = vm;
        const keys = new Set(vm.headItems);
        const activeColumn = vm.columns.filter((item) => {
          if (keys.has(item.title)) {
            return true;
          }
          return false;
        });
        that.activeColumn = activeColumn;
        that.activeColumn.splice(1, 0);
      }, 200),

      /** *****************************刷新************************************* */
      // 刷新表格數據(使用現有參數)
      refresh(type) {
        if (type === 'delete' && this.page.pageNo > 1 && this.tableData.length === 1) {
          this.page.pageNo = this.page.pageNo - 1;
        }
        this.initTable({ vm: this });
      },

      // 從新加載數據(重置到第一頁)
      reload() {
        if (this.page.pageNo !== 1) {
          this.page.pageNo = 1;
        }
        this.initTable({ vm: this });
      },

      initTable: debounce(async ({ vm }) => {
        const that = vm;
        that.tableData = [];
        const params = that._.assign({
          pageNo: that.page.pageNo,
          pageSize: that.page.pageSize,
          sortStr: that.sort
        }, that.req.params); // 右值覆蓋左值,返回左值
        // 發起請求,根據實際項目中,接口來作
        const { data } = await window.axios[that.req.type || 'post'](that.req.url, params);
        if (data && that.$utils.Type.isArray(data.result)) {
          that.tableData = data.result;
          that.page.total = data.total * 1;
        }
        that.$nextTick(() => {
          that.$emit('loadDone', that.tableData, params);
        });
      }, 300),

      getCheckRows() {
        return this.checkRows;
      },

      getCheckKeys() {
        return this.checkKeys;
      },

      handleHead(columns) {
        const allColumn = [];
        columns.map((item) => {
          if (!item.limit) {
            allColumn.push(item.title);
          }
          return true;
        });
        this.headItems = allColumn;
        this.allColumn = allColumn;
        this.changeChecked();
      },

      handleLayout() {
        const layout = this.layout;
        if (!layout) return null;
        layout.split(',')
        .map(
          (item) => {
            const key = item.trim();
            this.layoutKey[key] = true;
            return true;
          },
        );
        return this.layoutKey;
      },

      autoWidth(width) {
        if (this.$utils.isEmpty(width)) {
          return 'cell';
        }
        return width;
      },

      init() {
        this.handleLayout();
        this.handleHead(this.columns);
        if (this.defaultSort) {
          const { prop, order } = this.defaultSort;
          this.sort = `${prop} ${order === 'descending' ? 'desc' : 'asc'}`;
        }
        this.initTable({ vm: this });
      }
      ,
    },
    async created() {
      this.init();
    },

    watch: {
      queryParams: {
        handler() {
          this.reload({ vm: this });
        },
      },
      columns: {
        handler() {
          this.handleHead(this.columns);
        },
      },
    },
    computed: {
      queryParams() {
        if (this.$utils.isNotEmpty(this.req.params)) {
          return this.req.params;
        }
        return {};
      },
    },
  };
</script>

<style lang="less" scoped>
  @import './style/index';
</style>
複製代碼

使用方法

<template>
  <div>
    <table-list
      :req="tableReq"
      ref="tableList"
      :stripe="true"  // table 原來的參數也是支持使用的,方法也支持使用。
      :columns="[ {title:'用戶名',field:'name',sort:'true',fixed:true}, {title:'英文名',field:'aliasName',sort:'true',fixed:true}, {title:'年齡',field:'age', sort:true}, {title:'職業',field:'job', sort:true}, {title:'郵箱',field:'email'}, {title:'出生日期',field:'birthday'}, {title:'家庭住址',field:'address'}, {title:'戶籍',field:'domicile'}, ]">
      <!--格式化時間列,全部的列均可以這麼使用,slot 名爲列field-->
      <template slot="birthday" slot-scope="{data}">
        <span>{{format(data.birthday)}}</span>
      </template>
    </table-list>
  </div>
</template>

<script>
  import TableList from './table/components/TableList';

  export default {
    name: 'HelloWorld',
    components: { TableList },
    data() {
      return {
        tableReq: {
          url: '/user/list', //必填
          type: 'post', //可選, 默認 post
          params: {} // 可選
        }
      };
    },

    methods: {
      format(time) {
        if (this.$utils.isEmpty(time)) return '/';
        return this.$utils.format(new Date(time), 'yyyy-MM-dd HH:mm');
      },
    },

    mounted() {
    }
  };
</script>
複製代碼

如下是顯示效果

相關文章
相關標籤/搜索