基于element-ui进行二次封装的表格组件

<!--
* @description 表格组件
* @fileName TableList.vue
* @authorQ
* @date 2021/05/15 15:13:45
-->
<template>
  <div class="table-container">
    <el-table
      v-if="showTable"
      ref="filterTable"
      :span-method="spanName?objectSpanMethod:null"
      :data="data"
      :show-summary="showSummary.show ? showSummary.show : null"
      :summary-method="showSummary.show ? summaryMethod : null"
      :sum-text="showSummary.sumText ? showSummary.sumText : null"
      @filter-change="handleFilterChange"
      :border="border"
      :stripe="stripe"
      empty-text="暂无数据"
      style="width: 100%"
      :show-header="showHeader"
      :tooltip-effect="tooltipTheme"
      @selection-change="handleSelectionChange"
    >
      <!-- 选择框列 -->
      <el-table-column
        v-if="selection"
        type="selection"
        :align="'center'"
      ></el-table-column>
      <!-- 排序列 -->
      <el-table-column
        v-if="indexShow"
        width="100"
        label="序号"
        :align="'center'"
      >
        <template slot-scope="scope">
          <div>
            <span>{{ scope.$index + 1 }}</span>
          </div>
        </template>
      </el-table-column>

      <template v-for="(item, index) in columns">
        <!-- 特殊列处理 -->
        <template v-if="item.render">
          <!-- visible 是否显示该列 -->
          <el-table-column
            v-if="item.visible"
            :filters="item.filters ? item.filters : null"
            :column-key="item.prop"
            :key="index"
            :prop="item.prop ? item.prop : null"
            :align="item.align ? item.align : null"
            :fixed="item.fixed ? item.fixed : null"
            :label="item.label ? item.label : null"
            :show-overflow-tooltip="item.tooltip"
            :class-name="className"
            :sortable="item.sortable ? item.sortable : false"
            :width="item.width ? item.width : null"
          >
            <!-- 多级表头 -->
            <template v-if="item.children && item.children.length > 0">
              <div v-for="(item1, index1) in item.children" :key="index1">
                <el-table-column
                  :key="index1"
                  :filters="item.filters ? item.filters : null"
                  :column-key="item.prop"
                  :prop="item1.prop ? item1.prop : null"
                  :align="item1.align ? item1.align : null"
                  :fixed="item1.fixed ? item1.fixed : null"
                  :label="item1.label ? item1.label : null"
                  :show-overflow-tooltip="item1.tooltip"
                  :class-name="className"
                  :sortable="item1.sortable ? item1.sortable : false"
                  :width="item1.width ? item1.width : null"
                >
                  <exSlot
                    :render="item1.render"
                    :row="scope.row"
                    :index1="scope.$index"
                    :column="item1"
                  />
                </el-table-column>
              </div>
            </template>
            <!-- 不是多级表头 -->
            <template slot-scope="scope">
              <exSlot
                :render="item.render"
                :row="scope.row"
                :index="scope.$index"
                :column="item"
              />
            </template>
          </el-table-column>
        </template>

        <!-- 正常列 -->
        <template v-else>
          <!-- visible 是否显示该列 -->
          <el-table-column
            v-if="item.visible"
            :key="index"
            :column-key="item.prop"
            :filters="item.filters ? item.filters : null"
            :prop="item.prop ? item.prop : null"
            :align="item.align ? item.align : null"
            :fixed="item.fixed ? item.fixed : null"
            :label="item.label ? item.label : null"
            :class-name="className"
            :show-overflow-tooltip="item.tooltip"
            :sortable="item.sortable ? item.sortable : false"
            :width="item.width ? item.width : null"
          >
            <!-- 多级表头 -->
            <template v-if="item.children && item.children.length > 0">
              <template v-for="(item1, index1) in item.children">
                <el-table-column
                  :prop="item1.prop ? item1.prop : null"
                  :column-key="item.prop"
                  :filters="item.filters ? item.filters : null"
                  :align="item1.align ? item1.align : null"
                  :fixed="item1.fixed ? item1.fixed : null"
                  :label="item1.label ? item1.label : null"
                  :show-overflow-tooltip="item1.tooltip"
                  :class-name="className"
                  :sortable="item1.sortable ? item1.sortable : false"
                  :width="item1.width ? item1.width : null"
                >
                  <template slot-scope="scope">
                    <span v-html="formatter(scope.row[item1.prop])"></span>
                  </template>
                </el-table-column>
              </template>
            </template>
            <template slot-scope="scope">
              <!-- 字典处理 -->
              <template v-if="item.dict">
                <!-- 判断原始数据是否有效,有效转为字典数据,无效则转为--占位符 -->
                <span
                  v-if="!scope.row[item.prop]"
                  v-html="formatter(scope.row[item.prop])"
                ></span>
                <dict-tag
                  v-else
                  :options="dict.type[item.prop]"
                  :value="scope.row[item.prop]"
                />
              </template>
              <!-- 时间格式化 -->
              <span v-else-if="item.time">{{
                parseTime(scope.row[item.prop], "{y}-{m}-{d}")
              }}</span>
              <!-- 不做处理 -->
              <span v-else v-html="formatter(scope.row[item.prop])"></span>
            </template>
          </el-table-column>
        </template>
      </template>

      <!-- 操作列 -->
      <el-table-column
        v-if="isEdit === true"
        label="操作"
        :align="'center'"
        width="200"
        :fixed="fixed"
      >
        <template slot-scope="scope">
          <slot name="editSlot">
            <template>
              <el-button type="primary" @click="editClick(scope.row)"
                >编辑</el-button
              >
              <el-button type="danger" @click="deleteClick(scope.row)"
                >删除</el-button
              >
            </template>
          </slot>
        </template>
      </el-table-column>
    </el-table>
    <el-pagination
      :style="{ float: `${paginationPosition}` }"
      v-if="pagination"
      :background="background"
      :current-page.sync="currentPage"
      :page-size.sync="currentSize"
      :layout="layout"
      :page-sizes="pageSizes"
      :total="total"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
    />
  </div>
</template>

<script>
// 自定义内容的组件
var exSlot = {
  functional: true,
  props: {
    row: Object,
    render: Function,
    index: Number,
    column: {
      type: Object,
      default: null,
    },
  },
  // render 函数
  render: (h, context) => {
    const params = {
      row: context.props.row,
      index: context.props.index,
    };
    if (context.props.columns) params.columns = context.props.columns;
    return context.props.render(h, params);
  },
};
export default {
  name: "TableList",
  components: { exSlot },
  props: {
    data: {
      type: Array,
      default: () => [],
    },
    update: {
      type: Number,
      default: () => null,
    },

    // 是否显示合计列
    showSummary: {
      type: Object,
      default: () => {
        return {
          show: false,
          sumText: "总计",
        };
      },
    },
    // 需要通过那个字段属性进行比较合并
    spanName: {
      type: String,
      default: () => {
        return null;
      },
    },
    // 需要合并数组
    spanColumnArr:{
      type:Array,
      default:()=>{
        return [0]
      }
    },
    tooltipTheme: {
      type: String,
      default: "dark",
    },
    // 是否显示表头
    showHeader: {
      type: Boolean,
      default: true,
    },
    // 是否添加排序列
    indexShow: {
      type: Boolean,
      default: true,
    },
    showTable: {
      type: Boolean,
      default: true,
    },
    // 是否显示选择框列
    selection: {
      type: Boolean,
      default: false,
    },
    // 字段名
    columns: {
      type: Array,
      default: () => [],
    },
    // 是否含有边框
    border: {
      type: Boolean,
      default: false,
    },

    // 是否显示斑马条纹
    stripe: {
      type: Boolean,
      default: false,
    },
    // 是否是可以编辑的表格
    isEdit: {
      type: Boolean,
      default: false,
    },
    // 是否固定右侧一列,只对右侧操作栏起作用
    fixed: {
      type: String,
      default: "right",
    },

    // 是否显示分页
    pagination: {
      type: Boolean,
      default: false,
    },
    // 分页的位置
    paginationPosition: {
      type: String,
      default: "center",
    },
    total: {
      required: false,
      type: Number,
    },
    page: {
      type: Number,
      default: 1, // 默认第一页
    },
    limit: {
      type: Number,
      default: 10, // 默认每页20条
    },
    pageSizes: {
      type: Array,
      // default: [10, 20, 30, 50]
      default: function () {
        return [1, 2, 3, 5]; // 默认显示可选的每页多少条数据
      },
    },
    layout: {
      type: String,
      default: "total, sizes, prev, pager, next, jumper",
    },
    background: {
      type: Boolean,
      default: true,
    },
    autoScroll: {
      type: Boolean,
      default: true,
    },
    hidden: {
      type: Boolean,
      default: false,
    },
    className: {
      type: String,
      default: "",
    },
    render: {
      type: Function,
      default: function () {},
    },
  },
  data() {
    return {
      cloneColumns: [],
      spanArr: [], // 跨行数组
      pos: null, // 跨行数组
    };
  },
  computed: {
    // 当前页多少条数据并且赋值给父组件
    currentPage: {
      get() {
        return this.page;
      },
      set(val) {
        this.$emit("update:page", val);
      },
    },
    // 改变当前页几条数据得值赋值给父组件
    currentSize: {
      get() {
        return this.limit;
      },
      set(val) {
        this.$emit("update:limit", val);
      },
    },
  },
  created() {
    this.cloneColumns = JSON.parse(JSON.stringify(this.columns));
  },
  watch: {
    columns(value) {},
  },
  mounted() {
    if (this.spanName) {
      this.getSpanArr(this.data);
    }
  },

  methods: {
     // 合同单元格直接传入:spanName="id或者其他字段属性名"
    getSpanArr(data) {
      for (let i = 0; i < data.length; i++) {
        if (i === 0) {
          this.spanArr.push(1);
          this.pos = 0;
        } else {
          // 判断当前的元素和上一个元素是否相同
          // name 需要通过哪个字段来进行比较合并
          if (data[i][this.spanName] === data[i - 1][this.spanName]) {
            this.spanArr[this.pos] += 1;
            this.spanArr.push(0);
          } else {
            this.spanArr.push(1);
            this.pos = i;
          }
        }
      }
    },

  // 合并
    objectSpanMethod({ row, column, rowIndex, columnIndex }) {
      if (this.spanColumnArr.includes(columnIndex)) {
        const _row = this.spanArr[rowIndex];
        const _col = _row > 0 ? 1 : 0;
        return {
          rowspan: _row,
          colspan: _col,
        };
      }
    },

    // 自定义合计行
    summaryMethod(params) {
      const { columns, data } = params;
      const sums = [];
      columns.forEach((column, index) => {
        if (index === 0) {
          sums[index] = "总计";
          return;
        }
        const values = data.map((item) => item[column.property]);
        let num = 0;
        for (let i = 0; i < values.length; i++) {
          if (values[i]) {
            num += Number(values[i]);
            sums[index] = num;
          } else {
            if (isNaN(values[i])) {
            } else {
              num += Number(values[i]);
              sums[index] = num;
            }
          }
        }
        if (column.property === "lv1Time" || column.property === "lv2Time") {
          if (values.filter(Boolean).length === 0) {
            sums[index] = "";
          } else {
            let num = 0;
            for (let i = 0; i < values.length; i++) {
              if (values[i]) {
                num += Number(values[i]);
                sums[index] = num;
              } else {
                if (isNaN(values[i])) {
                } else {
                  num += Number(values[i]);
                  sums[index] = num;
                }
              }
            }
          }
        }
      });
      let arr = [];
      sums.map((i, index) => {
        if (index === 0) {
          arr.push(i);
        } else {
          if (!isNaN(i)) {
            arr.push(i);
          } else {
            arr.push("");
          }
        }
      });
      return arr;
    },
    handleFilterChange(filters) {
      if (filters.length > 0) {
        this.$refs.filterTable.clearSelection();
        this.$refs.filterTable.toggleRowSelection(filters.pop());
      } else {
        this.columns.map((item) => {
          // 判断当前是那一列进行了筛选
          if (item.prop === Object.keys(filters)[0]) {
            item.filterValues = filters;
          }
        });

        this.$emit("filtersParams", this.columns);
      }
    },
    // 当前行当前列数据是否有效,无效的话,返回--占位符
    formatter(row) {
      if (row) {
        return isNaN(parseFloat(row)) && isFinite(row)
          ? '<span class="isNaN">--</span>'
          : row;
      } else {
        return '<span class="isNaN">--</span>';
      }
    },
    /**
     * @param {*}
     * @return {*}
     * @author: Q
     * @Date: 2021-09-01 11:56:37
     * @description: 已选的数据项
     */
    handleSelectionChange(val) {
      this.$emit("selectVal", val);
    },
    handleSizeChange(val) {
      this.pageSize = val;
      this.$emit("pagination", { pageIndex: this.page, pageSize: val });
    },
    handleCurrentChange(val) {
      this.$emit("pagination", { pageIndex: val, pageSize: this.limit });
    },
    // 修改按钮的点击事件
    editClick(val) {
      this.$emit("edit", val);
    },
    // 删除按钮点击事件
    deleteClick(val) {
      this.$confirm("此操作将永久删除该文件, 是否继续?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          this.$emit("del", val);
        })
        .catch(() => {
          this.$message({
            type: "info",
            message: "已取消删除",
          });
        });
    },
  },
};
</script>

<style lang="scss">
.table-container {
  text-align: center;
  .el-pagination {
    margin-left: 0 !important;
    margin-top: 0 !important;
    background: #fff !important;
    height: 112px;
    line-height: 112px;
    display: flex;
    justify-content: center;
    align-items: center;
    ul {
      display: flex;
      justify-content: space-around;
      align-items: center;
      li {
        border-radius: 6px !important;
        color: #666666 !important;
      }
      .active {
        color: #fff !important;
        background: #1677ffff !important;
      }
    }
    button {
      border-radius: 6px !important;
      color: #666666 !important;
    }
  }
}
.table-container.el-table {
  margin-top: 20px;
}

.table-container .el-pagination {
  margin-top: 20px;
  margin-left: 20px;
}
.isNaN {
  color: red;
}
</style>

热门相关:斗神战帝   无法隐瞒的本能:偷拍   大神你人设崩了   豪门闪婚:帝少的神秘冷妻   薄先生,情不由己