深圳幻海软件技术有限公司 欢迎您!

自带分页与loading的表格封装

2023-02-28

首先就是新建一个组件,名字就叫做​​PageTable​​,​​template​​里面很简单,放上表格和分页。既然表格默认带有分页,分页的数据就放在组件内部复制<template><div><el-table></el-table><el-pa

首先就是新建一个组件,名字就叫做​​PageTable​​,​​template​​里面很简单,放上表格和分页。既然表格默认带有分页,分页的数据就放在组件内部

<template>
  <div>
    <el-table></el-table>

    <el-pagination
      layout="total, sizes, prev, pager, next, jumper"
      :current-page.sync="page.current"
      :page-size.sync="page.size"
      :total="page.total"
    />
  </div>
</template>

<script>
export default {
  name: 'PageTable',
  data() {
    return {
      page: {
        current: 1,
        size: 10,
        total: 0
      },
    }
  },
}
</script>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.

之后开始思考一个问题,就是表格的数据是从父组件传进来,还是放在组件自身。如果放在父组件里面,那么分页的变化每次都要​​emit​​事件通知父组件,这样我们把分页和表格合在一起就失去了意义,所以表格的数据是要放在组件自身的。

表格数据放在自身,父组件就需要传进来一个获取数据的方法,就叫做​​fetchData​​吧,在组件内部调用这个方法,就可以获取数据了。这个方法是需要知道当前分页的,并且返回数据和总条数,由此就可以定义​​fetchData​​了

interface ResponseStructure {
  data: any[];
  total: any;
}
interface fetchData {
  (current: number, size: number): Promise<ResponseStructure>
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

现在我们的组件已经变成了这个样子

<template>
  <div>
    <el-table :data="tableData"></el-table>

    <el-pagination
      layout="total, sizes, prev, pager, next, jumper"
      :current-page.sync="page.current"
      :page-size.sync="page.size"
      :total="page.total"
      @size-change="search"
      @current-change="getTableData"
    />
  </div>
</template>

<script>
export default {
  name: 'PageTable',
  props: {
    fetchData: {
      type: Function,
      required: true
    },
  },
  data() {
    return {
      page: {
        current: 1,
        size: 10,
        total: 0
      },
      tableData: []
    }
  },
  methods: {
    search() {
      this.page.current = 1
      this.getTableData()
    },
    async getTableData() {
      try {
        const { current, size } = this.page
        const { data, total } = await this.fetchData(current, size)
        this.tableData = data
        this.page.total = total
      } catch (err) {
        this.tableData = []
        this.page.total = 0
      }
    },
  }
}
</script>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.

表格一般都会带有搜索项,搜索参数等在父组件定义,并且逻辑写在​​fetchData​​里即可,组件内部只处理通用的逻辑。父组件里点击了搜索按钮,是要触发获取事件的,父组件可以通过​​$refs​​调用组件内部方法。目前我们定义了两个方法​​search​​与​​getTableData​​,点击搜索调用​​search​​就可以了,并且会自动重置分页,如果有不想重置分页的情况,可以调用​​getTableData​​。

目前为止,数据我们已经有了,但是并没有展示,因为element-ui的列信息(Table-column)是通过插槽的,我们使用​​slot​​接收一下就可以了

<el-table :data="tableData">
  <slot />
</el-table>
  • 1.
  • 2.
  • 3.

到了这里基本逻辑已经完成了,再进一步,一般我们的表格需要loading,loading应该由父组件自己决定如何使用,但是状态的改变由本组件处理,这里可以使用​​.sync​​修饰符,loading逻辑加在 ​​getTableData​​中即可

<script>
export default {
  props: {
    loading: {
      type: Boolean,
      default: false,
    },
  },
  methods: {
    async getTableData() {
      try {
        const { current, size } = this.page
        this.$emit('update:loading', true)
        const { data, total } = await this.fetchData(current, size)
        this.tableData = data
        this.page.total = total
      } catch (err) {
        this.tableData = []
        this.page.total = 0
      } finally {
        this.$emit('update:loading', false)
      }
    },
  }
}
</script>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.

loading也加完了,功能就已经完成了,但是还存在一些问题,el-table有很多的Attributes、Events和Methods,Attributes和Events我们透传给el-table即可,Methods需要el-table的ref,如果父组件直接使用ref,获取的是组件外层的div,无法获取到el-table的ref,我们暴露一个方法抛出el-table的ref即可

<template>
  <div>
    <el-table v-bind="$attrs" v-on="$listeners" ref="tableRef" :data="tableData">
      <slot />
    </el-table>
  </div>
</template>

<script>
export default {
  methods: {
    getTableRef() {
      return this.$refs.tableRef
    }
  }
}
</script>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

至此,​​PageTable​​就写完了,完整代码如下

<template>
  <div>
    <el-table
      v-bind="$attrs"
      v-on="$listeners"
      ref="tableRef"
      :data="tableData"
      @sort-change="sortChange"
    >
      <slot />
    </el-table>

    <el-pagination
      layout="total, sizes,prev, pager, next, jumper"
      :current-page.sync="page.current"
      :page-sizes="pageSizes"
      :page-size.sync="page.size"
      :total="page.total"
      @size-change="search"
      @current-change="getTableData"
    />
  </div>
</template>

<script>
export default {
  name: 'PageTable',
  props: {
    /**
     * 获取表格数据的请求方法
     * @param {Number} current 当前页数
     * @param {Number} size 每页条数
     * @param {Object} sort 排序
     * @param {String} sort.orderBy 排序的key
     * @param {('ascending'|'descending'|null)} sort.rule 排序的规则
     */
    fetchData: {
      type: Function,
      required: true
    },
    pageSizes: {
      type: Array,
      default: () => [ 10, 20, 50, 100, 150 ]
    },
    loading: {
      type: Boolean,
      default: false
    },
  },
  data() {
    return {
      page: {
        current: 1,
        size: 10,
        total: 0
      },
      tableData: [],
      sort: {
        orderBy: '',
        rule: ''
      }
    }
  },
  created() {
    this.getTableData()
  },
  methods: {
    /** 搜索,可通过 $refs 调用此方法 */
    search() {
      this.page.current = 1
      this.getTableData()
    },
    /** 获取表格数据,可通过 $refs 调用此方法 */
    async getTableData() {
      try {
        const { current, size } = this.page
        this.$emit('beforeFetch')
        this.$emit('update:loading', true)
        const { data, total } = await this.fetchData(current, size, this.sort)

        this.$emit('fetched', data)
        this.tableData = data
        this.page.total = total
      } catch (err) {
        this.$emit('fetched', [])
        this.tableData = []
        this.page.total = 0
      } finally {
        this.$emit('update:loading', false)
      }
    },
    /** 获取el-table的ref,可通过 $refs 调用此方法 */
    getTableRef() {
      return this.$refs.tableRef
    },
    sortChange({ prop, order }) {
      this.sort = {
        orderBy: prop,
        rule: order || ''
      }
      this.getTableData()
    }
  }
}
</script>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.