CommonTable组件(一款简化开发的表格组件)

目录

概述

实用性

插槽

props

组件源码

right-toolbar组件

组件实现效果

使用示例代码

结语


概述

        CommonTable 组件是一个通用的表格组件,用于展示数据并支持分页、搜索等功能。它可以根据传入的数据和配置灵活地显示表头、分页器、右侧工具栏等元素,支持自定义列的显示与隐藏。

实用性

         在一般的后台管理系统中,出现最多的就是表格,通常由头部搜索栏,中间表格核心以及底部分页组件构成。为了简便开发,减少代码量,避免分页组件的引用,以及点击分页时接口的调用,想到写一款Table组件便于平常开发

插槽

header插槽用于放置头部搜索栏
other插槽用于放置列表操作栏,比如:新增、导入、导出按钮等
table插槽作用域插槽,用于接收搜索或处理之后的表格数据

props

pageSizes[Array]每页显示个数
layout[Array]分页器组件布局
fetchDataList[function,({current,size})=>{}]搜索函数,能获取到分页参数
formatDataFun[function,(list)=>{}]格式化列表函数,能格式化返回的列表数据
immediate[boolean]初次加载是否立即搜索
showPagination[boolean]是否展示分页
columns[Array]只有在showRight_Tool时生效
showColumnsType[String]只有在showRight_Tool时生效(checkBox,transfer)
showRight_Tool[boolean]是否展示右侧显隐列tool

组件源码

<template>
  <div class="table-box">
    <div v-show="showHeader && showSearch" class="table-box__header">
      <slot name="header"></slot>
    </div>

    <div class="table-box__other">
      <div v-if="showOther" class="mb8">
        <slot name="other"></slot>
      </div>
      <div v-if="showRight_Tool" class="mb8">
        <right-toolbar
          :showSearch.sync="showSearch"
          @queryTable="getDataList"
          :columns="columns"
          :showColumnsType="showColumnsType"
        ></right-toolbar>
      </div>
    </div>
    <div class="table-box__body" v-loading="loading">
      <slot name="table" :tableData="tableData" :columnsData="columns"></slot>
    </div>
    <el-pagination
      v-if="showPagination"
      class="table-box__footer"
      :current-page="currentPage"
      :layout="layout"
      :page-size="pageSize"
      :page-sizes="pageSizes"
      :total="total"
      background
      @current-change="handleCurrentChange"
      @size-change="handleSizeChange"
    ></el-pagination>
  </div>
</template>

<script>
export default {
  name: "CommonTable",
  props: {
    pageSizes: {
      type: Array,
      default: () => [10, 20, 30, 40],
    },
    layout: {
      type: String,
      default: "total, sizes, prev, pager, next, jumper",
    },
    fetchDataList: {
      type: Function,
      require: true,
      default: () => {},
    },
    formatDataFun: {
      type: Function,
    },
    immediate: {
      type: Boolean,
      default: true,
    },
    showPagination: {
      type: Boolean,
      default: true,
    },
    showRight_Tool: {
      type: Boolean,
      default: true,
    },
    columns: {
      type: Array,
      default: () => [],
    },
    /* 显隐列类型(transfer穿梭框、checkbox复选框) */
    showColumnsType: {
      type: String,
      default: "checkbox",
    },
  },
  data() {
    return {
      tableData: [],
      total: 0,
      currentPage: 1,
      pageSize: 0,
      loading: false,
      showSearch: true,
    };
  },
  computed: {
    showHeader() {
      return !!this.$slots["header"];
    },
    showOther() {
      return !!this.$slots["other"];
    },
  },
  mounted() {
    this.pageSize = this.pageSizes[1] || this.pageSizes[0];
    if (this.immediate) {
      this.getDataList && this.getDataList();
    }
  },
  methods: {
    // 请求数据
    async getDataList(other = {}, currentPage = 1) {
      this.currentPage = currentPage;
      const params = {
        current: this.currentPage,
        // limit: this.pageSize,
        size: this.pageSize,
        ...other,
      };
      this.loading = true;
      try {
        const result = await this.fetchDataList(params);
        this.loading = false;
        let list = [];
        // 兼容
        if (Array.isArray(result)) {
          list = result;
          this.total = result.length;
        } else if (result?.code === 0) {
          const { records, total } = result.data;
          list = records;
          this.total = parseInt(total);
        } else {
          list = [];
          this.total = 0;
        }
        if (list.length > 0 && typeof this.formatDataFun === "function") {
          list = this.formatDataFun(list);
        }
        list.forEach(
          (item, index) =>
            (item._order = (this.currentPage - 1) * this.pageSize + index + 1)
        );
        // console.log('list', list)
        this.tableData = list;
      } catch (error) {
        console.error(error);
      }
      this.loading = false;
    },
    handleSizeChange(newSize) {
      this.pageSize = newSize;
      this.getDataList({}, this.currentPage);
    },
    handleCurrentChange(currentPage) {
      // this.currentPage = currentPage
      this.getDataList({}, currentPage);
    },
  },
};
</script>

<style lang="scss" scoped>
.table-box {
  height: 100%;
  display: flex;
  flex-direction: column;
  &__header {
    // padding-bottom: 12px;
  }
  &__body {
    flex: 1;
    height: 0px;
    ::v-deep {
      th.el-table__cell {
        background-color: #eef0f2;
      }
      .el-button {
        font-size: 14px;
      }
    }
  }
  &__other {
    display: flex;
    align-items: center;
    justify-content: space-between;
  }
  &__footer {
    margin-top: 12px;
    text-align: right;
  }
}
</style>

right-toolbar组件

<template>
  <div class="top-right-btn" :style="style">
    <el-row>
      <el-tooltip class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top" v-if="search">
        <el-button size="mini" circle icon="el-icon-search" @click="toggleSearch()" />
      </el-tooltip>
      <el-tooltip class="item" effect="dark" content="刷新" placement="top">
        <el-button size="mini" circle icon="el-icon-refresh" @click="refresh()" />
      </el-tooltip>
      <el-tooltip class="item" effect="dark" content="显隐列" placement="top" v-if="columns">
        <el-button size="mini" circle icon="el-icon-menu" @click="showColumn()" v-if="showColumnsType == 'transfer'"/>
        <el-dropdown trigger="click" :hide-on-click="false" style="padding-left: 12px" v-if="showColumnsType == 'checkbox'">
          <el-button size="mini" circle icon="el-icon-menu" />
          <el-dropdown-menu slot="dropdown">
            <template v-for="item in columns">
              <el-dropdown-item :key="item.key">
                <el-checkbox :checked="item.visible" @change="checkboxChange($event, item.label)" :label="item.label" />
              </el-dropdown-item>
            </template>
          </el-dropdown-menu>
        </el-dropdown>
      </el-tooltip>
    </el-row>
    <el-dialog :title="title" :visible.sync="open" append-to-body>
      <el-transfer
        :titles="['显示', '隐藏']"
        v-model="value"
        :data="columns"
        @change="dataChange"
      ></el-transfer>
    </el-dialog>
  </div>
</template>
<script>
export default {
  name: "RightToolbar",
  data() {
    return {
      // 显隐数据
      value: [],
      // 弹出层标题
      title: "显示/隐藏",
      // 是否显示弹出层
      open: false,
    };
  },
  props: {
    /* 是否显示检索条件 */
    showSearch: {
      type: Boolean,
      default: true,
    },
    /* 显隐列信息 */
    columns: {
      type: Array,
    },
    /* 是否显示检索图标 */
    search: {
      type: Boolean,
      default: true,
    },
    /* 显隐列类型(transfer穿梭框、checkbox复选框) */
    showColumnsType: {
      type: String,
      default: "checkbox",
    },
    /* 右外边距 */
    gutter: {
      type: Number,
      default: 10,
    },
  },
  computed: {
    style() {
      const ret = {};
      if (this.gutter) {
        ret.marginRight = `${this.gutter / 2}px`;
      }
      return ret;
    }
  },
  created() {
    if (this.showColumnsType == 'transfer') {
      // 显隐列初始默认隐藏列
      for (let item in this.columns) {
        if (this.columns[item].visible === false) {
          this.value.push(parseInt(item));
        }
      }
    }
  },
  methods: {
    // 搜索
    toggleSearch() {
      this.$emit("update:showSearch", !this.showSearch);
    },
    // 刷新
    refresh() {
      this.$emit("queryTable");
    },
    // 右侧列表元素变化
    dataChange(data) {
      for (let item in this.columns) {
        const key = this.columns[item].key;
        this.columns[item].visible = !data.includes(key);
      }
    },
    // 打开显隐列dialog
    showColumn() {
      this.open = true;
    },
    // 勾选
    checkboxChange(event, label) {
      this.columns.filter(item => item.label == label)[0].visible = event;
    }
  },
};
</script>
<style lang="scss" scoped>
::v-deep .el-transfer__button {
  border-radius: 50%;
  padding: 12px;
  display: block;
  margin-left: 0px;
}
::v-deep .el-transfer__button:first-child {
  margin-bottom: 10px;
}
</style>

组件实现效果

使用示例代码

<template>
  <commonTable
    :fetchDataList="fetchDataList"
    ref="common-table"
    :columns="columns"
  >
    <template #header>
      <el-form
        :model="searchObj"
        label-suffix=":"
        label-width="100px"
        :inline="true"
      >
        <el-form-item label="项目名称" prop="projectName">
          <el-input
            style="width: 100%"
            v-model="searchObj.projectName"
            clearable
            placeholder="请输入项目名称"
          />
        </el-form-item>
        <el-form-item label="合同名称" prop="contractName">
          <el-input
            style="width: 100%"
            v-model="searchObj.contractName"
            clearable
            placeholder="请输入合同名称"
          ></el-input>
        </el-form-item>
        <el-form-item>
          <el-button
            type="primary"
            icon="el-icon-search"
            size="mini"
            @click="handleSearch"
            >搜索</el-button
          >
          <el-button icon="el-icon-refresh" size="mini">重置</el-button>
        </el-form-item>
      </el-form>
    </template>
    <template #table="{ tableData, columnsData }">
      <el-table :data="tableData" stripe height="100%">
        <el-table-column
          v-if="columnsData[0].visible"
          label="项目名称"
          prop="prjName"
        ></el-table-column>
        <el-table-column
          v-if="columnsData[1].visible"
          label="合同名称"
          prop="contractName"
          show-overflow-tooltip
        ></el-table-column>
        <el-table-column
          v-if="columnsData[2].visible"
          label="创建时间"
          prop="createDate"
        ></el-table-column
      ></el-table>
    </template>
    <template #other>
      <div>
        <el-button type="primary" plain icon="el-icon-plus" size="mini"
          >新增</el-button
        >
        <el-button type="info" icon="el-icon-upload" size="mini"
          >导入</el-button
        >
        <el-button type="info" icon="el-icon-upload2" size="mini"
          >导出</el-button
        >
      </div>
    </template>
  </commonTable>
</template>

<script>
import commonTable from "./commonTable.vue";
export default {
  components: { commonTable },
  props: {},
  data() {
    return {
      searchObj: {
        contractName: "",
        code: "",
        projectName: "",
      },
      columns: [
        { key: 0, label: `项目名称`, prop: "prjName", visible: true },
        { key: 1, label: `合同名称`, prop: "contractName", visible: true },
        { key: 2, label: `创建时间`, prop: "createDate", visible: true },
      ],
      showColumnsType: "transfer",
    };
  },
  watch: {},
  computed: {},
  methods: {
    async fetchDataList(params) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log(111); // Just for logging, optional
          const data = {
            code: 0,
            data: {
              records: [
                {
                  id: "96",
                  prjId: "62291648",
                  contractId: "77735285504",
                  createBy: "5895203276800",
                  createDate: "2024-04-01 09:23:49",
                  prjName: "2017配套道路工程",
                  contractName:
                    "配套道路正式外电源监理",
                },
                {
                  id: "97",
                  prjId: "62295021648",
                  contractId: "777340451328",
                  createBy: "5893276800",
                  createDate: "2024-04-09 16:02:26",
                  prjName: "2017配套道路工程",
                  contractName:
                    "道路工程监理(二标段)",
                },
                {
                  id: "98",
                  prjId: "17804657889",
                  contractId: "1298095218560",
                  createBy: "110",
                  createDate: "2024-04-17 16:55:01",
                  prjName: "园林绿化",
                  contractName: null,
                },
                {
                  id: "99",
                  prjId: "679611648",
                  contractId: "77735965617152",
                  createBy: "4",
                  createDate: "2024-04-18 12:26:14",
                  prjName: "配套道路工程",
                  contractName:
                    "2017配套道路工程监理",
                },
                {
                  id: "100",
                  prjId: "178079900609",
                  contractId: "12984063856",
                  createBy: "110",
                  createDate: "2024-04-18 14:54:49",
                  prjName: "项目流程测试1",
                  contractName: null,
                },
                {
                  id: "101",
                  prjId: "17808557553",
                  contractId: "129843960576",
                  createBy: "110",
                  createDate: "2024-04-18 15:23:21",
                  prjName: "流程测试项目",
                  contractName: null,
                },
                {
                  id: "102",
                  prjId: "1780855759153",
                  contractId: "129845430219008",
                  createBy: "4",
                  createDate: "2024-04-18 16:51:17",
                  prjName: "流程测试项目",
                  contractName: " 流程测试项目2",
                },
                {
                  id: "103",
                  prjId: "1780467557889",
                  contractId: "1298463997056",
                  createBy: "4",
                  createDate: "2024-04-18 17:27:09",
                  prjName: "市政基础设施",
                  contractName: null,
                },
                {
                  id: "104",
                  prjId: "178043374242",
                  contractId: "1298464701440",
                  createBy: "4",
                  createDate: "2024-04-18 17:45:24",
                  prjName: "wkai@土地一级开发",
                  contractName: " 土地开发@土地一级开发",
                },
                {
                  id: "105",
                  prjId: "178114366241",
                  contractId: "1298443776",
                  createBy: "110",
                  createDate: "2024-04-19 15:47:34",
                  prjName: "交付策划路@测试",
                  contractName: "交付策划@测试",
                },
                {
                  id: "106",
                  prjId: "1782220218882",
                  contractId: "129977056256",
                  createBy: "4",
                  createDate: "2024-04-22 09:52:23",
                  prjName: "计量支付审批@测试1",
                  contractName: "计量@测试1",
                },
                {
                  id: "107",
                  prjId: "1782247969",
                  contractId: "12991513728",
                  createBy: "4",
                  createDate: "2024-04-22 14:49:29",
                  prjName: "计量@测试2",
                  contractName: null,
                },
                {
                  id: "108",
                  prjId: "17822947969",
                  contractId: "129933351680",
                  createBy: "4",
                  createDate: "2024-04-22 15:05:24",
                  prjName: "计量@测试2",
                  contractName: null,
                },
                {
                  id: "109",
                  prjId: "62295029611648",
                  contractId: "77735091904",
                  createBy: "4",
                  createDate: "2024-04-25 15:20:14",
                  prjName: "道路工程",
                  contractName:
                    "电源工程",
                },
                {
                  id: "110",
                  prjId: "178337358466",
                  contractId: "130129728",
                  createBy: "110",
                  createDate: "2024-04-28 15:22:14",
                  prjName:
                    "数据走向",
                  contractName: "ran承包合同经办单位筛选",
                },
                {
                  id: "111",
                  prjId: "178302273",
                  contractId: "1306566336",
                  createBy: "4",
                  createDate: "2024-05-09 17:02:05",
                  prjName: "房建@学校@测试1",
                  contractName: "承包学校@测试1",
                },
                {
                  id: "112",
                  prjId: "1783026273",
                  contractId: "1306027640576",
                  createBy: "4",
                  createDate: "2024-05-09 17:21:01",
                  prjName: "房建测试1",
                  contractName: "承包合同",
                },
                {
                  id: "113",
                  prjId: "17830257726273",
                  contractId: "13446626816",
                  createBy: "4",
                  createDate: "2024-05-09 18:06:58",
                  prjName: "学校@测试1",
                  contractName: "房学校@测试1",
                },
                {
                  id: "114",
                  prjId: "123104",
                  contractId: "166054912",
                  createBy: "03276800",
                  createDate: "2024-05-13 17:17:29",
                  prjName:
                    "二类居住用地项目",
                  contractName: "咨询合同",
                },
                {
                  id: "115",
                  prjId: "98",
                  contractId: "952",
                  createBy: "4",
                  createDate: "2024-05-16 13:41:52",
                  prjName: "测试",
                  contractName: "测试",
                },
              ],
              total: 57,
            },
          };
          resolve(data);
        }, 1000);
      });
    },
    handleSearch() {
      this.$refs["common-table"] && this.$refs["common-table"].getDataList();
    },
  },
  created() {},
  mounted() {},
};
</script>

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

结语

CommonTable 组件通过封装常见的数据表格功能和灵活的配置选项,为开发者提供了一个强大而易于使用的工具,能够显著提升数据展示和管理的效率,适用于各种复杂的业务场景和需求。

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端摸鱼侠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值