el-table封装 vue2基于elementui进行的二次封装

文章介绍了如何在Vue项目中封装公共表格组件,以解决重复代码问题。通过提供父组件配置表格样式、功能,以及处理跨页选择的bug,实现了包括多选、排序、分页等功能。此外,还强调了组件化带来的代码复用和维护便利性。
摘要由CSDN通过智能技术生成

前言:项目中涉及到N个表格,并且功能样式类似,一个一个的写,代码堆积如山,尤其是字段多的时候,封装之路势在必行。

维护内容1-1:跨页选择bug修复

 

⚠️⚠️⚠️⚠️ 此版本为1.0 ⚠️⚠️⚠️⚠️

后续会持续更新功能

看看图:

一、父组件使用

1. html部分

<!-- 
    如果涉及到 跨页选择 需要绑定表格数据的唯一值,如果没有就不需要写了
    :rowKey="row => row.唯一值"
-->
    <public-table
        ref="myTable"
        :rowKey="row => row.id"
        :tableData="tableData"
        :columns="columns"
        :configFlag="tableConfig"
        :pageValue="pageValue"
        @sizeChange="sizeChange"
        @currentChange="currentChange"
        @handleSelectionChange="selectChange"
    >
        <!-- 操作列 -->
        <template slot="operation" slot-scope="{ scope }">
            <el-button type="text" size="small" @click="edit(scope.row)">修改</el-button>
            <el-button type="text" size="small" @click="remove(scope.row)">删除</el-button>
        </template>
      </public-table>

2. js部分

这里注意,columns的配置可以单独拆分出去写成一个js文件,这样代码会更清晰简洁,如下代码注释注意

import PublicTable from '../publicTable.vue';
export default {
  components: { PublicTable },
  watch: {
// 因为没涉及后台,这是自己写的监听表格数组长度给total赋值,项目里就通过请求给total赋值即可
    'tableData.length': {
      handler(total) {
        this.pageValue.total = total;
      },
      immediate: true,
    },
  },
  data() {
    return {
      getTableData: [
        {
          id: '001',
          name: 'LiMing',
          gender: '1',
          born: '1995-12-20',
          height: '180',
          personalIntro:
            '曾毕业于北京科技大学,14年涂鸦实习,16年任职阿里,担任leader,对标P6+',
          property: '32000000',
        },
        {
          id: '002',
          name: 'Jenny',
          gender: '0',
          born: '1997-09-03',
          height: '163.256',
          personalIntro:
            '曾毕业于哈弗大学,20年加州伊布丹公司实习,22年来中国深造,担任河北工业大学信息技术系副主任',
          property: '35000000',
        },
        {
          id: '003',
          name: 'Danny',
          gender: '1',
          born: '未知',
          height: '230.299',
          personalIntro:
            '我是一只恐龙🦕,我来自5000万年前,我的家人都没有了,我的尾巴有三米长,皮肤绿色,我喜欢戴帽子',
          property: '0',
        },
      ],
      tableData: [],
      // 这里total必须放到pageValue里,如果想放在其他地方,需要在publicTable中更改el-pagination中传参
      pageValue: {
        pageNum: 1,
        pageSize: 10,
        total: 0,
      },
// 表格配置项和表头信息都可以拆出去,创建一个js文件,这样你的代码会更清晰明了
// export const tableConfig = { ... }
// export const tableColumns = [ ... ]
// 父组件import {tableColumns , tableConfig} from './XXX.js'
// data(){
//   return {
//      tableConfig:tableConfig,
//      columns:tableColumns
//      }
//  }
      // 表格配置项,看个人需求添加
      tableConfig: {
        needPage: true, // 是否需要分页
        index: true, // 是否需要序号
        selection: true, // 是否需要多选
        reserveSelection: false, // 是否需要支持跨页选择
        indexFixed: 'left', // 序号列固定
      },
      // 表格表头信息
      columns: [
        {
          label: '姓名',
          fieldIndex: 'name',
          width: '100px',
          // fixed: 'left', // 固定列
        },
        {
          label: '性别', // 后台返回的数据是0 和 1,0 代表女生
          fieldIndex: 'gender',
          type: 'statMap',
          options: [
            { value: '1', label: '男' },
            { value: '0', label: '女' },
          ],
        },
        {
          label: '出生日期',
          fieldIndex: 'born',
          sortable: true, // 此属性可以设置排序
        },
        {
          label: '身高(cm)',
          fieldIndex: 'height',
          type: 'tofix2', // 会把身高四舍五入170.235 => 170.24 170 => 170.00
          sortable: true,
        },
        {
          label: '个人经历介绍',
          fieldIndex: 'personalIntro',
          showOverFlowTooltip: true, // 是否需要内容过长隐藏并且,鼠标移入有提示信息
        },
        {
          label: '个人资产',
          fieldIndex: 'property',
          type: 'money', // 会把金额搞成 1,234,567,890
        },
        // ⚠️⚠️⚠️⚠️⚠️⚠️ 操作列 ⚠️⚠️⚠️⚠️⚠️⚠️
        {
          label: '操作',
          slotname: 'operation',
          width: '100',
          fixed: 'right',
        },
      ],
    };
  },

  mounted() {
    this.getData();
  },

3. methods方法。如下都是示例,如果el-table组件库中的方法封装里没有,可以自行在publictable.vue文件中添加,或者评论留言

methods: {
    getData() {
      let t = setTimeout(() => {
        this.tableData = this.getTableData;
      }, 1000);
    },
    // this.$refs.myTable.clearSelected() 方法中调用可以清空选中
    // 使用场景:比如表格既支持批量删除又支持表格内删除,当你勾选☑️了2条数据,
    // 但是没通过批量删除按钮删除,而是点击里表格内的操作列删除了一条数据,
    // 此时你会发现,再勾选一条数据,明明已经删除一条了,勾选的数组却是三个,
    // 这个时候在操作列删除后就需要调用一下此方法了

    // 复选框选择的时候触发
    selectChange(selectedArr) {
      console.log('选中了哪些呢?', selectedArr);
    },
    // 编辑
    edit(row) {
      console.log('我要编辑了', row);
    },
    // 删除
    remove(row) {
      console.log('我要删除了', row);
    },
    // 每页多少条
    sizeChange(size) {
      this.pageValue.pageSize = size;
      console.log('现在是一页多少条?', size);
      // this.getData();
    },
    // 翻页
    currentChange(page) {
      this.pageValue.pageNum = page;
      console.log('现在是第几页?', page);
      // this.getData();
    },
  },

二、子组件代码 publicTable.vue

1. html

<div class="table-common">
    <el-table
      ref="'publicTable'"
      border
      style="width: 100%"
      :data="tableData"
      :row-key="rowKey"
      @selection-change="handleSelectionChange"
      :height="tableHeight"
      :header-cell-style="headerCellStyle"
      :row-style="rowStyle"
    >
      <!-- 当数据为空时,如果想添加一个表达时空数据的图片,可以在这里设置,如果没有就不用管了 -->
      <template slot="empty">
        <span style="display: block; margin: 60px 0"
          ><img style="width: 60px" src="" />
          <!-- 也可以自定义 没有数据时 页面展示的文字 -->
          <p style="line-height: 25px; font-size: 13px">暂无数据(自定义哈)</p>
        </span>
      </template>
      <!-- 全选单选 -->
      <el-table-column
        v-if="configFlag.selection"
        align="center"
        width="55"
        type="selection"
        :reserve-selection="config.reserveSelection"
      />
      <!-- 序号列 -->
      <el-table-column
        type="index"
        v-if="configFlag.index"
        :label="configFlag.indexName || '序号'"
        :width="configFlag.indexWidth || 50"
        align="center"
        :fixed="configFlag.indexFixed || 'left'"
      >
        <template slot-scope="scope">
          <!-- 每页都是从 1 开始 -->
          {{ scope.$index + 1 }}
          <!-- 第二页从 11 开始 -->
          <!-- {{ (pageValue.pageNum - 1) * 10 + (scope.$index + 1) }} -->
        </template>
      </el-table-column>

      <!-- 循环遍历表头展示数据 -->
      <el-table-column
        v-for="item in columns"
        :key="item.fieldIndex"
        :width="item.width || ''"
        :prop="item.fieldIndex"
        :label="item.label"
        :align="item.align || 'center'"
        :sortable="item.sortable"
        :fixed="item.fixed || false"
        header-align="center"
        :show-overflow-tooltip="item.showOverFlowTooltip"
      >
        <template slot="header" slot-scope="{ column }">
          {{ column.label }}
          <el-tooltip
            class="item"
            effect="dark"
            v-if="item.headertip"
            :content="item.headertip"
            placement="top-start"
          >
            <i class="el-icon-warning"></i>
          </el-tooltip>
        </template>
        <template slot-scope="scope">
          <!-- 枚举值 -->
          <div v-if="item.type == 'statMap'">
            {{ statMaps(item.options)[scope.row[item.fieldIndex]] || '--' }}
          </div>
          <!-- 需提示过长信息 -->
          <div v-else-if="item.showOverFlowTooltip">
            <div class="show-tooltip">{{ scope.row[item.fieldIndex] || '--' }}</div>
          </div>
          <!-- 保留两位小数 -->
          <div v-else-if="item.type == 'tofix2'">
            {{
              scope.row[item.fieldIndex]
                ? Number(scope.row[item.fieldIndex]).toFixed(2)
                : '--'
            }}
          </div>
          <!-- 金额千分位展示,项目需求,所以暂时就加上了,主要是做个示例,大家可以参考怎么自定义列的一些方法 -->
          <div v-else-if="item.type == 'money'">
            <span>{{ getMoneyK(scope.row[item.fieldIndex]) || '--' }}</span>
          </div>
          <!-- 根据需求添加效果 返回的slot可以优化.自己来吧.可以实现操作列等 -->
          <slot
            v-else-if="item.type == undefined && item.slotname"
            :scope="scope"
            :name="item.slotname"
          />
          <!-- 最普通的情况 -->
          <div v-else>
            <span>{{ scope.row[item.fieldIndex] || '--' }}</span>
          </div>
        </template>
      </el-table-column>
    </el-table>
    <el-pagination
      v-if="configFlag.needPage"
      :total="pageValue.total"
      :page-count="pageValue.pageNum"
      :page-size="pageValue.pageSize"
      :page-sizes="pageSizes"
      :current-page.sync="pageValue.pageNum"
      layout="prev, pager, next, jumper,total, sizes"
      class="fr"
      style="margin-top: 12px"
      @size-change="sizeChange"
      @current-change="currentChange"
      @prev-click="prevClick"
      @next-click="nextClick"
    />
  </div>

2. js部分传参和methods --- (里面所有的默认值根据自己需求更改,或者父组件传参)

export default {
  name: 'Table',
  data() {
    return {};
  },
  props: {
    // 多选保存选中数据
    rowKey: {
      default: () => row => row.index,
    },
    // 为表头设计样式
    headerCellStyle: {
      default: () => {
        return { background: '#666', height: '70px', color: 'pink' };
      },
    },
    // 为每一行设计样式,比如设置每行的高度等等
    rowStyle: {
      default: () => {
        return { height: '50px' };
      },
    },
    columns: {
      // 表头数据  文案和绑定值,以及需要特殊处理的slot
      type: Array,
      default: () => [],
    },
    tableData: {
      type: Array, // 后台数据
      default: () => [],
    },
    // 分页参数
    pageValue: {
      // 分页数据
      type: Object,
      default: () => {
        return {
          pageNum: 1,
          pageSize: 10,
          total: 0,
          currentPage: 1, //当前页
        };
      },
    },
    // 每页多少条的选项
    pageSizes: {
      type: Array,
      default: () => {
        return [10, 20, 50, 100];
      },
    },
    // 表格配置项
    configFlag: {
      // 配置  其他table配置依次添加
      type: Object,
      default: () => {
        return {
          needPage: false, // 是否需要分页
          selection: false, // 是否需要多选
          index: false, // 是否需要序号
          indexWidth: 70, //序号列宽
          btn: false, //序号添加自定义html
          // 这里不全面,可根据实际情况添加
        };
      },
    },
    tableHeight: {
      // 可以监听屏幕高度获取。
      // 高度
      type: Number,
      default: () => null,
    },
    maxHeight: {
      // 可以监听屏幕高度获取。
      // 高度
      type: Number,
      default: () => 900,
    },
  },
  computed: {},
  methods: {
    // 用于表格中字段是枚举值的列 比如性别 0 代表女 1代表男,就不需要对后台数据单独处理了
    /**
      ```
        tableconfig:
          {
            label:‘多选’,
            fieldIndex:'selections',
            type:'statMap',
            options:[{value:'1',label:'男'},{value:'0',label:'女'}]
          }
      ```
    */
    statMaps(list) {
      if (!list) return;
      let obj = {};
      list.forEach(item => {
        obj[item.value || item.id] = item.label || item.value;
      });
      return obj;
    },
    // 金额千位展示:1,234,567,890
    getMoneyK(money) {
      if (typeof money === 'number') {
        money = money.toString();
      }
      var pattern = /(-?\d+)(\d{3})/;
      while (pattern.test(money)) {
        money = money.replace(pattern, '$1,$2');
      }
      return money;
    },
    // 清空选中
    clearSelected() {
      // 父组件通过ref调用clearSelected方法,例如 this.$refs.clearTable.clearSelected()
      this.$refs.publicTable.clearSelection();
    },
    /*
     默认选中
     需要默认选中的在组件中通过this.$refs.publicTable.selected(默认选中的数据:Array)
    */
    selected(data) {
      if (data.length > 0) {
        data.forEach(item => {
          this.$refs.publicTable.toggleRowSelection(item, true);
        });
      }
    },
    // 设置条数
    sizeChange(size) {
      this.$emit('sizeChange', size);
    },
    // 翻页,直接跳转
    currentChange(page) {
      this.$emit('handleChange', page);
    },
    //上一页
    prevClick(val) {
      this.$emit('prevClick', val);
    },
    //下一页
    nextClick(val) {
      this.$emit('nextClick', val);
    },
    // 多选
    handleSelectionChange(val) {
      this.$emit('handleSelectionChange', val);
    },
    // 多选
    handleSelection(val, row) {
      this.$emit('handleSelection', { val, row });
    },
    handleCellEnter(row, column, cell, event) {
      this.$emit('handleCellEnter', { row, column, cell, event });
    },
    //编辑
    handleEdit(index, row, colIndex, field) {
      this.$emit('handleEdit', { index, row, colIndex, field });
    },
    //下拉框事件
    onSelected(index, row, field) {
      this.$emit('onSelected', { index, row, field });
    },
    //按钮点击事件
    onClickBtn(index, row) {
      this.$emit('onClickBtn', { index, row });
    },
  },
};

3. CSS部分

<style lang="scss" scoped>
.show-tooltip {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  cursor: pointer;
}
</style>

代码部分结束。

总结

        可能刚开始上手会不习惯,但用上三四次,尤其是像后台管理系统,表格超多,你会发现,编码会节省很多时间,枚举值也不需要每次对后台返回数据单独处理,封装组件中都有体现。

        此版本为V1.0,后续还会更新简化~欢迎持续关注,同时我还封装了一个form表单,但是是基于项目业务封装的,耦合性较差,正在优化中,后续也会给大家分享出来。

        封装不易,转载请标明出处...

可以直接复制粘贴(拿来吧你~),有不清晰的地方也请多指教,使用中的问题也可以咨询我哦😊

        一键三连还不搞一波?欢迎批评指正

  • 8
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Vue2 针对 el-table 进行封装可以提高代码的复用性,同时也能简化开发流程,让开发者能够更加专注于业务逻辑的实现。 以下是一个简单的 el-table 封装示例: ```vue <template> <el-table :data="tableData" :columns="columns" v-loading="loading"></el-table> </template> <script> export default { name: "MyTable", props: { data: { type: Array, required: true }, columns: { type: Array, required: true }, loading: { type: Boolean, default: false } }, data() { return { tableData: [] }; }, methods: { loadData() { this.tableData = this.data; } }, mounted() { this.loadData(); }, watch: { data() { this.loadData(); } } }; </script> ``` 在这个示例中,我们将 el-table 组件的 data 和 columns 属性分别作为 MyTable 组件的 props,通过 loadData 方法将 props 中的数据赋值给 tableData,并绑定到 el-table 的 data 属性上,实现了对 el-table封装。 使用这个封装后的组件,只需要传入相应的 data 和 columns 数据即可渲染出一个 el-table,如下所示: ```vue <template> <my-table :data="tableData" :columns="tableColumns"></my-table> </template> <script> import MyTable from "@/components/MyTable"; export default { components: { MyTable }, data() { return { tableData: [ { name: "John", age: 18, gender: "Male" }, { name: "Jane", age: 22, gender: "Female" } ], tableColumns: [ { prop: "name", label: "Name" }, { prop: "age", label: "Age" }, { prop: "gender", label: "Gender" } ] }; } }; </script> ``` 通过这种方式封装 el-table,我们可以大大提高代码的复用性,同时也能减少重复的代码编写,提高开发效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值