基于el-table的多级表头表格组件封装

基于el-table的多级表头表格组件封装

StandardTable.vue 表格组件

<template>
  <div class="tableWrap">
    <el-table
      ref="table"
      element-loading-text="Loading"
      v-loading="loading"
      :data="tableData"
      border
      :highlight-current-row="radioSelect"
      tooltip-effect="dark"
      :stripe="stripe"
      @selection-change="handleSelectChange"
      @sort-change="handleSortChange"
      @row-click="rowClick"
      :span-method="spanMethod"
      :header-cell-style="headerCellStyle"
      :row-key="rowKey"
      :tree-props="treeProps"
      style="width: 100%"
      :height="height"
      :summary-method="getSummaries"
      :show-summary="showSummary"
    >
      <template slot="empty">
        <span>{{ emptyTxt }}</span>
      </template>
      <el-table-column
        v-if="selectionShow"
        key="selection"
        type="selection"
        align="center"
        :selectable="selectableFn"
        :resizable="resizable"
      ></el-table-column>
      <el-table-column
        v-if="radioSelect"
        align="center"
        width="50px"
        label=""
        fixed="left"
      >
        <template slot-scope="scope">
          <el-radio
            v-model="radio"
            :label="scope.row.id"
            :disabled="scope.row.disabled"
            @change="handleRadioChange(scope.row)"
            >{{ "" }}</el-radio
          >
        </template>
      </el-table-column>
      <el-table-column
        v-if="indexShow"
        key="index"
        type="index"
        align="center"
        :label="indexShowStr || '序号'"
        fixed="left"
        width="50px"
        :resizable="resizable"
      >
        <template slot-scope="scope">
          <span>{{ (current - 1) * pageSize + scope.$index + 1 }}</span>
        </template>
      </el-table-column>
      <table-column
        v-for="(item, index) in tableLabel || []"
        :column="item"
        :min-width="minWidth"
        :resizable="resizable"
        :key="index"
      ></table-column>
      <el-table-column
        v-if="tableOption.label"
        :width="tableOption.width"
        :label="tableOption.label"
        :fixed="tableOption.fixed ? tableOption.fixed : false"
        align="center"
        class-name="small-padding fixed-width"
        :resizable="resizable"
      >
        <template slot-scope="scope">
          <el-button
            v-for="(item, index) in tableOption.options"
            :key="index"
            :type="item.type"
            :icon="item.icon"
            @click="item.clickFun(scope.row, scope.$index)"
            size="mini"
            v-if="item.showFn ? item.showFn(scope.row) : true"
            :disabled="item.disabledFn ? item.disabledFn(scope.row) : false"
          >
            <span v-if="item.render">
              {{ renderToHtml(item, scope.row) }}
              <slot :name="item.renderName"></slot>
            </span>
            <span v-else>{{ item.label }}</span>
          </el-button>
        </template>
      </el-table-column>
    </el-table>
    <el-row type="flex" justify="end">
      <el-pagination
        v-if="!noPage"
        @size-change="handlePageSizeChange"
        @current-change="handleCurrentChange"
        :current-page="current"
        :page-sizes="pageSizes"
        :page-size="pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total"
      ></el-pagination>
    </el-row>
  </div>
</template>

<script>
import { accAdd } from "@/utils/commonUtils.js";
import TableColumn from "@/components/table/table-column.vue";
export default {
  name: "StandardTable",
  props: {
    pageSize: {
      type: Number,
      default: 50
    },
    current: {
      type: Number,
      default: 1
    },
    total: {
      type: Number,
      default: 0
    },
    pageSizes: {
      type: Array,
      default: () => {
        return [10, 20, 50, 100, 200, 500];
      }
    },
    stripe: {
      type: Boolean,
      default: false
    },
    noPage: {
      type: Boolean,
      default: false
    },
    isRowDrop: {
      type: Boolean,
      default: false
    },
    indexShow: {
      type: Boolean,
      default: false
    },
    selectionShow: {
      type: Boolean,
      default: false
    },
    radioSelect: {
      type: Boolean,
      default: false
    },
    showSummary: {
      type: Boolean,
      default: false
    },
    tableData: {
      type: Array,
      default: () => {
        return [];
      }
    },
    tableLabel: {
      type: Array,
      default: () => {
        return [];
      }
    },
    totalParams: {
      type: Array,
      default: () => {
        return [];
      }
    },
    tableOption: {
      type: Object,
      default: () => {
        return {};
      }
    },
    loading: {
      type: Boolean,
      default: false
    },
    height: "",
    spanMethod: {},
    headerCellStyle: {},
    dropDataType: {
      type: Number,
      default: 0
    },
    indexShowStr: {
      type: String,
      default: "序号"
    },
    emptyTxt: {
      type: String,
      default: "暂无数据"
    },
    //禁止拖动表头
    resizable: {
      type: Boolean,
      default: false
    },
    treeProps: {
      //树状表格子级参数
      type: Object,
      default: () => {
        return {};
      }
    },
    rowKey: {
      type: String,
      default: "id"
    },
    // 需要传的其他参数
    params: {
      type: Object,
      default: () => {}
    }
  },
  data() {
    return {
      radio: "",
      minWidth: "100px" //不生效,elementUI当前版本不支持
    };
  },
  components: {
    TableColumn
  },
  mounted() {
    if (this.isRowDrop) {
      this.$emit("rowDrop");
    }
  },
  methods: {
    rowClick(row, column, event) {
      this.$emit("rowClick", row, column, event);
    },
    toggleRowSelection(row, selectedStatus) {
      if (row) {
        this.$refs.table.toggleRowSelection(row, selectedStatus);
      } else {
        this.$refs.table.clearSelection();
      }
    },

    renderToHtml(col, row) {
      if (typeof col.render === "function") {
        this.$slots[col.renderName] = [col.render(row)];
        return;
      }
      return;
    },

    //行拖拽
    // rowDrop() {
    //   const tbody = document.querySelectorAll('.el-table__body-wrapper tbody')
    //   const _this = this
    //   Sortable.create(tbody, {
    //     onEnd({newIndex, oldIndex}) {
    //       const currRow = _this.tableData.splice(oldIndex, 1)[0]
    //       _this.tableData.splice(newIndex, 0, currRow)
    //     },
    //   })
    // },
    handleCurrentChange(current) {
      this.$emit("handleCurrentChange", current);
    },
    handlePageSizeChange(pageSize) {
      this.$emit("handlePageSizeChange", pageSize);
    },
    handleSelectChange(value) {
      this.$emit("handleSelectChange", value);
    },
    handleRadioChange(value) {
      this.$emit("handleRadioChange", value);
    },
    handleRadioChange2(value) {
      console.log(value);
    },
    handleButton(method, value) {
      this.$emit(method, value);
    },
    handleSortChange(column, prop, order) {
      this.$emit("handleSortChange", column, prop, order);
    },
    getSummaries(params) {
      const { columns, data } = params;
      let sums = [];
      columns.forEach((column, index) => {
        if (index === 0) {
          sums[index] = "合计";
          return;
        }
        if (this.totalParams.indexOf(column.property) != -1) {
          const values = data.map(item => Number(item[column.property]));
          if (!values.every(value => isNaN(value))) {
            sums[index] = values.reduce((prev, curr) => {
              const value = Number(curr);
              let res = 0;
              if (!isNaN(value)) {
                res = accAdd(prev, curr);
              } else {
                res = prev;
              }
              //    if(res!=NaN&&res!='undefined'){
              return parseFloat(res.toFixed(2));
              //    }
            }, 0);
          } else {
            sums[index] = "-";
          }
        }
      });
      return sums;
    },
    // 该行是否禁用
    selectableFn(row, index) {
      if (row.disabled) {
        return false;
      } else {
        return true;
      }
    },
    doLayout() {
      if (this.$refs.table) {
        this.$refs.table.doLayout();
      }
    },
    handleBlurChange(e, type) {
      this.$emit("handleBlurChange");
    }
  },
  watch: {
    tableData: {
      immediate: true,
      deep: true,
      handler() {
        setTimeout(() => {
          this.$nextTick(() => {
            // 重新渲染表格,解决固定列错位的问题
            if (this.$refs.table) {
              this.$refs.table.doLayout();
            }
          });
        }, 50);
      }
    }
  }
};
</script>
<style lang="scss" scoped>
a {
  text-decoration: underline;
}
/deep/ .el-table__header th,
/deep/ .el-table thead.is-group th {
  background-color: #fff8f2;
}
.el-table .cell > span,
.el-table .cell a {
  // max-width: 100px;
  overflow: hidden;
  text-overflow: ellipsis;
  /*禁止换行显示*/
  white-space: nowrap;
  color: #4d4d4d;
}
.el-table /deep/ .cell {
  padding-left: 6px;
  padding-right: 6px;
  color: #4d4d4d;
}
// 表格折叠样式兼容 start
/deep/ .el-table__expand-icon {
  vertical-align: middle;
}
/deep/ .el-table__expand-icon + span {
  width: calc(100% - 23px);
  display: inline-block;
  vertical-align: middle;
}
/deep/ .el-table__placeholder + span {
  width: calc(100% - 36px);
  display: inline-block;
  vertical-align: middle;
}
// 表格折叠样式兼容 end
.el-pagination {
  margin-top: 12px;
  padding-bottom: 12px;
}
.el-table /deep/ .el-button--primary {
  padding: 5px 8px;
  height: 30px;
  background: linear-gradient(180deg, #f79999 0%, #c70019 60%);
  border-radius: 6px;
  border: 1px solid #c70019;
  color: #fff !important;
  font-size: 14px;
  &:hover {
    color: #ffe300 !important;
    background: linear-gradient(180deg, #f79999 0%, #c70019 60%);
    border: 1px solid #c70019;
  }
  &:focus {
    color: #fff;
    background: linear-gradient(180deg, #c50018 0%, #960000 60%);
    border: 1px solid #c8001a;
  }
}

.el-button.is-disabled,
.el-button.is-disabled:hover,
.el-button.is-disabled:focus {
  color: #979797 !important;
  cursor: not-allowed;
  background-image: none;
  background: linear-gradient(180deg, #dddddd 0%, #ffffff 100%) !important;
  border: 1px solid #e5e5e5;
}
.input-wrap {
  display: flex;
  align-items: center;
  label {
    width: auto;
  }
  div {
    min-width: 50%;
    flex: 1;
  }
}
.editLable {
  label {
    float: left;
    width: 55%;
    text-align: left;
  }
  .el-input {
    float: right;
    width: 44%;
  }
}
/deep/ .el-table__footer-wrapper .cell {
  font-weight: bold;
}
.fontW {
  font-weight: bold;
}
</style>

列组件

<template>
  <el-table-column
    :width="column.width ? column.width : ''"
    :min-width="minWidth"
    :align="column.align"
    :label="column.label"
    :fixed="column.fixed"
    :prop="column.param"
    :sortable="column.sortable ? 'custom' : false"
    :resizable="resizable"
  >
    <!-- 多级表头 -->
    <template v-if="column.children">
      <table-column
        v-for="(sunitem, i) in column.children"
        :column="sunitem"
        :min-width="minWidth"
        :resizable="resizable"
        :key="i"
      ></table-column>
    </template>
    <template slot="header" slot-scope="scope">
      <!-- 自定义表头 -->
      <div v-if="column.headerRender">
        {{ renderHeaderToHtml(item, scope.row) }}
        <slot :name="column.headerRenderName"></slot>
      </div>
      <div v-else>{{ column.label }}</div>
    </template>
    <template slot-scope="scope">
      <!-- 枚举值,动态入参 -->
      <span
        v-if="column.dicType && column.dicParam"
        :title="dic[column.dicType][scope.row[column.param]]"
      >
        {{ dic[column.dicType][column.dicParam(scope.row)] || "-" }}
      </span>
      <!-- 枚举值 -->
      <span
        v-else-if="column.dicType"
        :title="dic[column.dicType][scope.row[column.param]]"
      >
        {{ dic[column.dicType][scope.row[column.param]] || "-" }}
      </span>
      <!-- 自定义渲染 -->
      <span v-else-if="column.render">
        {{ renderToHtml(item, scope.row) }}
        <slot :name="column.renderName"></slot>
      </span>
      <!-- 可进行点击操作 -->
      <span v-else-if="column.handler" @click="column.handler(scope.row)">
        {{ scope.row[column.param] }}
      </span>
      <!-- creatTime、createTime 时间格式处理 -->
      <span
        v-else-if="column.param == 'creatTime' || column.param == 'createTime'"
        :title="filterDate(scope.row[column.param])"
      >
        {{ filterDate(scope.row[column.param]) }}
      </span>
      <!-- 可编辑列,label为枚举值 -->
      <div class="input-box editLable" v-else-if="column.edit">
        <label v-if="dic[column.labelType]">{{
          dic[column.labelType][scope.row[column.labelType]]
        }}</label>
        <el-form-item :prop="column.prop" :ref="column.ref">
          <el-input
            size="small"
            v-model="scope.row[column.param]"
            @blur="handleBlurChange($event, scope.row[column.labelType])"
          ></el-input>
        </el-form-item>
      </div>
      <!-- 不可编辑列,label为枚举值 -->
      <div class="input-box editLable" v-else-if="column.showLabel">
        <label v-if="dic[column.labelType]">{{
          dic[column.labelType][scope.row[column.labelType]]
        }}</label>
        <span>{{ scope.row[column.param] }}</span>
      </div>
      <!-- 其他文本类型渲染 -->
      <span
        v-else
        :title="scope.row[column.param]"
        :class="scope.row.fontBold || column.fontBold ? 'fontW' : ''"
        >{{
          scope.row[column.param] ||
          scope.row[column.param] === 0 ||
          scope.row[column.param] == "0"
            ? scope.row[column.param]
            : "-"
        }}</span
      >
    </template>
  </el-table-column>
</template>

<script>
import TableColumn from "@/components/table/table-column.vue";
import dic from "@/utils/dic.js";
import moment from "moment";
export default {
  name: "table-column",
  components: { TableColumn },
  props: {
    // column 列的参数如下:
    // param 取值字段名称
    // dicType  枚举字段名称
    // dicParam  枚举参数 例:let dic={ name:{name1:"名称一",name2:"名称二"}}  name为dicType  ,name1、name2 为 dicParam
    // fontBold 文字是否加粗
    // directShow 暂未使用
    // renderProject 自定义项目名称render方式
    // renderName 插槽名称
    // link 添加跳转链接
    // prop 绑定的prop变量,与el-form-item相关
    // ref 绑定的ref变量,与el-form-item相关
    // showLabel 是否显示el-form-item的label
    // labelType  el-form-item的枚举取值变量
    column: {
      type: Object,
      default: () => {}
    },
    //列的最小宽度
    minWidth: {
      type: String,
      default: "100px"
    },
    //禁止拖动表头
    resizable: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      dic: dic || {}
    };
  },
  methods: {
    renderToHtml(col, row) {
      if (typeof col.render === "function") {
        this.$slots[col.renderName] = [col.render(row)];
        return;
      }
      return;
    },
    renderHeaderToHtml(col, row) {
      if (typeof col.headerRender === "function") {
        this.$slots[col.headerRenderName] = [col.headerRender(row)];
        return;
      }
      return;
    },
    // 格式化时间
    filterDate(date) {
      if (date) {
        return moment(date).format("YYYY-MM-DD");
      } else {
        return "";
      }
    }
  }
};
</script>

<style lang="scss" scoped></style>

表格的使用

<template>
  <div class="about">
    <h1>表格</h1>
    <StandardTable
      ref="table"
      style="margin-top: 15px"
      :table-data="tableData"
      :table-label="tableLabel"
      :table-option="tableOption"
      :total="total"
      :pageSize="size"
      :current="current"
    >
    </StandardTable>
  </div>
</template>
<script>
import StandardTable from "@/components/table/StandardTable2.vue";
export default {
  name: "tableList",
  components: {
    StandardTable
  },
  data() {
    return {
      form: {},
      tableData: [
        {
          projectName: "名称",
          projectName1: "二级名称1",
          projectName2: "二级名称2",
          implementDept: "项目单位名称",
          controlLevelId: "层次1"
        },
        {
          projectName: "名称2",
          projectName1: "二级名称1",
          projectName2: "二级名称2",
          implementDept: "项目单位名称1",
          controlLevelId: "层次1"
        },
        {
          projectName: "名称3",
          projectName1: "二级名称1",
          projectName2: "二级名称2",
          implementDept: "项目单位名称2",
          controlLevelId: "层次1"
        },
        {
          projectName: "名称4",
          projectName1: "二级名称1",
          projectName2: "二级名称2",
          implementDept: "项目单位名称3",
          controlLevelId: "层次1"
        },
        {
          projectName: "名称5",
          projectName1: "二级名称1",
          projectName2: "二级名称2",
          implementDept: "项目单位名称4",
          controlLevelId: "层次1"
        },
        {
          projectName: "名称6",
          projectName1: "二级名称1",
          projectName2: "二级名称2",
          implementDept: "项目单位名称5",
          controlLevelId: "层次1"
        }
      ],
      tableLabel: [
        {
          label: "项目名称",
          param: "projectName",
          align: "center",
          fixed: "left"
        },
        {
          label: "总名称",
          param: "implementDept",
          align: "center",
          children: [
            {
              label: "二级表头1",
              param: "projectName1",
              align: "center",
              children: [
                {
                  label: "三级表头1",
                  param: "projectName2",
                  align: "center"
                },
                {
                  label: "三级表头2",
                  param: "projectName2",
                  align: "center",
                  children: [
                    {
                      label: "三级表头1",
                      param: "projectName2",
                      align: "center"
                    },
                    {
                      label: "三级表头2",
                      param: "projectName2",
                      align: "center"
                    }
                  ]
                }
              ]
            },
            {
              label: "二级表头2",
              param: "projectName2",
              align: "center"
            }
          ]
        },
        {
          label: "项目管控级次",
          param: "controlLevelId",
          align: "center"
        }
      ],
      // 功能列表操作按钮
      tableOption: {
        label: "操作",
        width: "130px",
        options: [
          {
            label: "编辑",
            type: "primary",
            clickFun: this.handleEdit
          },
          {
            label: "删除",
            type: "primary",
            clickFun: this.handleDelete
          }
        ]
      },
      tableHeight: "auto",
      current: 1,
      size: 100000,
      total: 0,
      loading: false,
      selectRows: []
    };
  },
  methods: {
    handlePageSizeChange(size) {
      this.size = size;
      let params = { ...this.form, current: this.current, size: size };
      // this.loadData(params)
    },
    handleCurrentChange(current) {
      this.current = current;
      let params = { ...this.form, current: current, size: this.size };
      // this.loadData(params)
    },
    handleSelectTableChange(rows) {
      this.selectRows = rows;
    },
    handleEdit() {},
    handleDelete() {}
  }
};
</script>

效果如下:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值