二次封装 table 组件

125 阅读1分钟

1. 二次封装table, 新建一个 OnlyTable.vue

其中的 v-scroll-bar-fixed 指令是横向滚动条固定

<template>
  <div class="table">
    <el-table
      ref="table"
      :data="data"
      v-bind="tableProps"
      v-on="tableEvent"
      :border="border"
      :toolbar="toolbar"
      :toolbar-grey="true"
      v-el-loading="loading"
      :header-fixed-props="headerFix && headerFixProps"
      cell-empty-text="-"
      :cell-style="cellStyle"
      :cell-class-name="cellClassName"
      :summary-method="summaryMethod"
      :show-summary="showSummary"
      :summaryGrey="showSummary"
      v-scroll-bar-fixed>
      <template #tools>
        <div>
          <slot name="tools" />
        </div>
      </template>
      <el-table-column type="selection" v-if="isSelection"></el-table-column>
      <el-table-column type="radio" v-if="isRadioSelectable"></el-table-column>
      <el-table-column
        v-if="isIndex"
        type="index"
        :index="indexMethod"
        label="序号"
        width="60"
        fixed />
      <template v-for="(item, index) in column">
        <el-table-column
          v-if="$scopedSlots[item.value]"
          :key="index"
          :label="item.label"
          :prop="item.value"
          v-bind="item.props">
          <template #header="{ column }" v-if="item.props?.required">
            <span class="el-form-item__required">*</span>
            {{ column.label }}
          </template>

          <template #default="{ row, $index }">
            <slot :name="item.value" :row="row" :index="$index"></slot>
          </template>
        </el-table-column>
        <el-table-column
          v-else
          :key="`key-${index}`"
          :label="item.label"
          :prop="item.value"
          v-bind="item.props">
          <template #header="{ column }" v-if="item.props?.required">
            <span class="el-form-item__required">*</span>
            {{ column.label }}
          </template>
        </el-table-column>
      </template>
    </el-table>
    <div slot="tableFooter">
      <slot name="tableFooter" />
    </div>
    <div v-if="needPage" class="page">
      <div class="page-total">
        <span>Total  {{ total }} items </span>
      </div>
      <el-pagination
        :current.sync="page.pageNum"
        :pageSize.sync="page.pageSize"
        :page-size-options="[10, 20, 50, 100, 300, 500]"
        :total="total"
        showTotalPages
        showSizeChanger
        showQuickJumper
        @current-change="pageChange"
        @size-change="sizeChange">
      </el-pagination>
    </div>
  </div>
</template>

<script>
export default {
  name: 'OnlyTable',
  components: {},
  props: {
    // 表格表头信息
    column: {
      type: Array,
      default: () => [],
    },
    data: {
      type: Array,
      default: () => [],
    },
    tableProps: {
      type: Object,
      default: () => ({}),
    },
    tableEvent: {
      type: Object,
      default: () => ({}),
    },
    cellStyle: {
      default:() => {},
    },
    cellClassName: {
      default: () => {},
    },
    loading: {
      type: Boolean,
      default: false,
    },
    // 是否需要分页
    needPage: {
      type: Boolean,
      default: true,
    },
    total: {
      type: Number,
      default: 0,
    },
    size: {
      type: Number,
      default: 10,
    },
    isIndex: {
      type: Boolean,
      default: false,
    },
    // 是否多选
    isSelection: {
      type: Boolean,
      default: false,
    },
    // 是否支持单选
    isRadioSelectable: {
      type: Boolean,
      default: false,
    },
    headerFix: {
      type: Boolean,
      default: true,
    },
    border: {
      type: Boolean,
      default: false,
    },
    // 是否显示工具栏
    toolbar: {
      type: Boolean,
      default: false,
    },
    page: {
      type: Object,
      default: () => ({}),
    },
    // 统计方法
    summaryMethod: {
      default:() => {},
    },
    // 是否显示统计
    showSummary: {
      type: Boolean,
      default: false,
    },
    fixContainerEl: {
      type: String,
      default: '.main-container-wrap'
    }
  },
  data() {
    return {
      // current: 1,
      // pageSize: this.size || 50,
      // 表头信息是否固定
      headerFixProps: {
        container: () => document.querySelector(this.fixContainerEl),
        zIndex: 10,
      },

    };
  },
  mounted() {},
  methods: {
    indexMethod(index) {
      return index;
    },
    getTableRef() {
      return this.$refs.table;
    },
    reset() {
      this.current = 1;
    },
    pageChange(current) {
      this.$emit('pageChange', {
        page: current,
        size: this.pageSize,
      });
    },
    sizeChange(pageSize) {
      this.$emit('pageChange', {
        page: 1,
        size: pageSize,
      });
    },
  },
};
</script>

<style lang="less" scoped>
:deep(.el-table .cell) {
  height: auto;
}
.page {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  overflow: hidden;
  margin-top: 20px;
  &-total {
    font-size: 14px;
    color: #8b8b8b;
  }
}
</style>

2. 引用 组件

<template>
  <OnlyTable
    ref="tableRef"
    v-bind="tableProps"
    @pageChange="onPageChange"
    :page="page"
    :cellStyle="cellStyle"
    :cellClassName="cellClassName"
  >
    <template #count="{ row }">
      {{row.count }}
    </template>
    <template #opts="{ row }">
      <el-button type="link"  @click="goDetail(row)" > 详情 </el-button>
    </template>
  </OnlyTable>

</template>

<script>
import OnlyTable from '@/components/OnlyTable/index.vue';
import lodash from 'lodash';
export default {
  components: {
    OnlyTable
  },
  data() {
    const tableProps = {
      column: [
        {
          value: '',
          label: '多选',
          props: {
            width: 60,
            fixed: 'left',
            type: 'selection',
            reserveSelection: true,
            selectable: (row) => {
              // 根据条件判断是否可以
              return row.status === '1';
            }
          },
        },
        {value: 'count',label: '数量',props: {width: 130,fixed: 'left',}},
        {value: 'opts',label: '操作',props: {width: 60,fixed: 'right', }}
      ],
      data: [],
      total: 0,
      loading: false,
      toolbar: true,
      tableEvent: {
        select: this.tableSelect,
        'select-all': this.tableSelect,
        // 统计显示
        summaryMethod: this.getSummaries,
      },
    };
    return {
      tableProps,
      page: {
        pageNum: 1,
        pageSize: 50,
      },
      tableSelectedData: []
    };
  },
  methods: {
    // 列表分页
    onPageChange(data) {
      this.page.pageNum = data.page || this.page.pageNum;
      this.page.pageSize = data.size || this.page.pageSize;
      this.query();
    },
    tableSelect(selection) {
      if (selection.length <= 500) {
        this.tableSelectedData = lodash.cloneDeep(selection);
      } else {
        const tableRef = this.$refs.tableRef.getTableRef();
        tableRef.clearSelectionCache();
        tableRef.toggleBatchSelectionCache(this.tableSelectedData, true, true);
        this.$message.warning('最多选择500条')
      }
    },
    // 行样式
    cellStyle({ row }) {
      if (['合计'].includes(row.seriesName) ) {
        return {
          background: 'rgb(245 246 247)',
          color: '#f76400'
        };
      }
      if (['小计'].includes(row.seriesName) ) {
        return {
          background: 'rgb(245 246 247)',
        };
      }
    },
    // 行样式类名
    cellClassName({row}) {
      if (['合计'].includes(row.seriesName)) {
        return 'special-cell';
      }
    },
    // 统计
    getSummaries({ column, data }) {
      let res = [];
      column.map((col, i) => {
        let addRes = 0;
        data.map(item => {
          addRes += Number(item[col.prop]);
        });
        if (i) {
          let content = '';
          if (!isNaN(addRes)) {
            content = addRes;
            if (col.prop === 'price') content = addRes.toFixed(2);
          }
          res.push(content);
        } else {
          res.push('总计');
        }
      });
      return res;
    }
  },
};
</script>