封装的vue表格组件,整理记录下

1,分为两个组件去封装,一个是基本的表格的框架组件,一个是行内容组件

基本的表格框架组件:

增加了移动调整列大小功能

<template>
  <div class="data-table">
    <!-- 表格标题 -->
    <table>
      <thead>
        <tr ref="dataTableHead">
          <th v-if="isCheck">
            <input type="checkbox" class="text-input" v-model="isAll" />
          </th>
          <th v-if="isNum">{{ $t("common.num") }}</th>
          <slot name="data-table-head"></slot>
          <th></th>
        </tr>
      </thead>
    </table>
    <div class="table-content">
      <!-- 表格数据内容 -->
      <table v-show="tableData.length > 0 && !isLoading">
        <tbody ref="dataTableContent">
          <slot name="data-table-content"></slot>
          <!-- <data-table-row :idx="idx" :item="item" v-for="(item, idx) in tableData" :key="idx"></data-table-row> -->
        </tbody>
      </table>
      <!-- 暂无数据 -->
      <div class="table-nodata" v-show="tableData.length == 0 && !isLoading">{{ $t("common.nodata") }}</div>
      <div class="table-loading" v-show="isLoading">{{ $t("common.loading") }}</div>
    </div>
  </div>
</template>
<script>
export default {
  name: "DataTableComp",
  props: {
    tableData: {
      //表格数据
      type: Array,
      default: () => [],
    },
    isLoading: {
      //是否正在加载数据
      type: Boolean,
      default: false,
    },
    isCheck: {
      type: Boolean,
      default: false,
    },
    isNum: {
      type: Boolean,
      default: false,
    },
    selects: {
      type: Array,
      default: () => [],
    },
  },
  provide() {
    return { dataTable: this };
  },
  data: () => ({
    isAll: false,
    selectList: [],
    isLoad: false,
  }),
  watch: {
    selectList: {
      //多选表格,选中的数据项索引数组
      deep: true,
      handler() {
        if (!this.isLoad) {
          this.$emit("update:selects", this.selectList);
          this.$emit("table-select", this.selectList);
        }
      },
    },

    selects() {
      //多选表格,选中的数据项
      this.isLoad = true;
      this.selectList = this.selects;
      var that = this;
      this.$nextTick(function() {
        that.isLoad = false;
      });
    },
    isAll() {
      //是否全选多选表格
      if (this.isAll) {
        var list = [];
        for (var i = 0; i < this.tableData.length; i++) {
          list.push(i);
        }
        this.selectList = list;
      } else {
        this.selectList = [];
      }
    },
    tableData() {
      //表格数据更新,重新渲染
      this.isLoad = true;
      var that = this;
      this.$nextTick(function() {
        that.initColumWidth();
        that.isLoad = false;
      });
    },
  },
  methods: {
    clearSelect() {
      //多选表格,清空选中
      this.isAll = false;
    },
    listenEvent() {
      this.app.$on("table-select-clear", this.clearSelect);
    },
    offEvent() {
      this.app.$off("table-select-clear", this.clearSelect);
    },

    initColumWidth() {
      //初始每列的宽度,保留每次调整后的列宽
      if (this.$refs.dataTableContent && this.$refs.dataTableHead) {
        if (this.tableData.length > 0 && !this.isLoading) {
          var head = this.$refs.dataTableHead;
          var body = this.$refs.dataTableContent;
          if (body.children) {
            for (var i = 0; i < body.children.length; i++) {
              for (var j = 0; j < head.children.length - 1; j++) {
                var td = body.children[i].children[j];
                td.style.width = head.children[j].style.width;
              }
            }
          }
        }
      }
    },
    //移动调整列大小
    onTableColum() {
      var movepage = document.getElementById("moving_page");
      if (this.$refs.dataTableContent && this.$refs.dataTableHead) {
        var dataTableContent = this.$refs.dataTableContent;
        this.$refs.dataTableHead.onmousedown = function(e) {
          if (e.target.tagName == "TH" && e.target != this.lastElementChild) {
            var elmt = e.target;
            var startX = e.pageX;
            var distance = e.pageX - elmt.offsetLeft;
            var leftRight = distance < elmt.offsetWidth * 0.5 ? "left" : "right";
            var elmt1 = leftRight == "left" ? elmt.previousElementSibling : elmt.nextElementSibling;

            if (elmt1 != this.lastElementChild) {
              var width = elmt.offsetWidth;
              var width1 = elmt1.offsetWidth;

              movepage.style.display = "flex";
              movepage.style.cursor = "ew-resize";
              movepage.onmousemove = function(e) {
                var w = e.pageX - startX + width;
                var w1 = width + width1 - w;
                elmt.style.width = w + "px";
                elmt1.style.width = w1 + "px";
                for (var i = 0; i < dataTableContent.children.length; i++) {
                  var td = dataTableContent.children[i].children[elmt.cellIndex];
                  var td1 = leftRight == "left" ? td.previousElementSibling : td.nextElementSibling;
                  td.style.width = w + "px";
                  td1.style.width = w1 + "px";
                }
              };
              movepage.onmouseup = function() {
                movepage.onmousemove = null;
                movepage.style.display = "none";
              };
            }
          }
          e.preventDefault();
        };
      }
    },
  },
  mounted() {
    this.listenEvent();
    this.onTableColum();
  },
  beforeDestroy() {
    this.offEvent();
  },
};
</script>

<style lang='scss'>
.data-table {
  display: block;
  overflow-x: hidden;
  overflow-y: hidden;
  padding-bottom: 0.1rem;
  height: 100%;
  width: 100%;

  table {
    background: white;
    width: 100%;
    border-collapse: collapse;
    border-spacing: 0;

    thead > tr {
      background: #cedeee;
      border-radius: 0.4rem;
      line-height: 3rem;
      white-space: nowrap;
      height: 3rem;
      display: flex;
      flex-direction: row;
      cursor: ew-resize;
    }

    thead > tr > th {
      text-align: center;
      font-size: 1.4rem;
      font-weight: normal;
    }

    thead > tr > th:not(:last-child) {
      // border-right: solid 1px  #0095ec;
      min-width: 5rem;
    }

    thead > tr > th:last-child {
      width: 0.8rem;
    }

    tbody > tr {
      display: flex;
      flex-direction: row;
      border-radius: 0.4rem;
      align-items: center;
      line-height: 1.6rem;
      min-height: 3rem;
    }

    tbody > tr:nth-child(even) {
      background: #f3f3f3;
    }

    tbody > tr:hover,
    tbody > tr.active {
      color: #388dea;
      background-color: #d9edf6;
    }

    tbody > tr.active {
      font-weight: bold;
    }

    tbody > tr > td {
      text-align: center;
      font-size: 1.2rem;
      min-width: 4rem;
      word-break: break-word;
      white-space: normal;

      > span {
        max-width: 100%;
        white-space: normal;
        word-break: break-word;
      }

      a {
        color: #0095ec;
        text-decoration: underline;
        font-size: 1.2rem;
        cursor: pointer;
      }

      button:nth-child(odd),
      button.btn-white {
        @extend .btn-white;
        height: 2.4rem !important;
        line-height: 2.4rem !important;
      }

      button:nth-child(even),
      button.btn-orange {
        @extend .btn-orange;
        height: 2.4rem !important;
        line-height: 2.4rem !important;
      }

      a:not(:last-child),
      button:not(:last-child) {
        margin-right: 0.8rem;
      }
    }
  }

  .table-content {
    height: calc(100% - 3.1rem);
    overflow-y: auto;
    overflow-x: hidden;
    border: dashed 0.1rem gainsboro;
    border-top: none;

    .table-nodata {
      font-weight: bold;
      margin: 0.8rem;
      font-size: 1.2rem;
      border: #e2a814 0.1rem dashed;
      background-color: #f6f0ca;
      display: flex;
      align-items: center;
      justify-content: center;
      height: calc(100% - 1.8rem);
      color: black;
    }

    .table-loading {
      @extend .table-nodata;
      color: red;
      font-size: 1.4rem;
    }
  }
}

</style>

行内容组件:

<template>
  <tr>
    <td v-if="dataTable.isCheck">
      <input type="checkbox" class="text-input" v-model="dataTable.selectList" :value="idx" />
    </td>
    <td v-if="dataTable.isNum">{{idx+1}}</td>
    <slot />
  </tr>
</template>

<script>
export default {
  name: "DataTableRow",
  props: {
    idx: {
      type: Number,
      default: 0
    },
    item: {
      type: Object,
      default: () => { }
    }
  },
  inject: ["dataTable"],
  provide () {
    return { dataTableRow: this };
  }
};
</script>

<style>
</style>

2,main.js中全局定义使用:

import DataTableComp from "@/components/comp/table/DataTableComp";
import DataTableRow from "@/components/comp/table/DataTableRow";

Vue.use({
  install: function() {
    Vue.component("data-table", DataTableComp); //数据表格组件
    Vue.component("data-table-row", DataTableRow); //数据表格行组件
  },
});

3,在组件中使用

<data-table :is-num="false" :table-data="dataList" :is-check="true" :is-custom="true" :selects.sync="selectList">
          <template slot="data-table-head">
            <th style="width:15rem">{{ $t("common.name") }}</th>
            <th style="width:12rem">{{ $t("common.age") }}</th>
            <th style="width:12rem">{{ $t("common.company") }}</th>
          </template>
          <template slot="data-table-content">
            <data-table-row v-for="(item, idx) in dataList" :key="'abc' + idx" :idx="idx" :item="item">
              <td style="width:15rem">{{ item.name}}</td>
              <td style="width:12rem">{{ item.age}}</td>
              <td style="width:12rem">{{ item.company}}</td>
            </data-table-row>
          </template>
        </data-table>

is-num是否显示行的序号,is-check是否可以选中,select.sync是选中的行数的序号

Logo

前往低代码交流专区

更多推荐