首页 > 技术文章 > 基于 elementui 的时间组件,分页组件的二次封装

ymdi 2020-11-02 13:45 原文

在后台管理系统中,开发人员面临最多的开发任务,table 数据管理应有一席之地。而随之而来的,就是数不清的筛选,排序操作。

而且每个table,都会配置一个分页用来更好的显示数据。

本文就从这个需求触发,来聊一聊时间组件和分页组件的二次封装。

为什么要二次封装

elementui 的各种组件做的很友好,但偶尔神经的产品就是不喜欢大众风格,非得独树一帜

这时候,在每个有 table 的位置都去做一个繁琐的分页,可以想像一下发生的问题:

  1. 代码量增加不少,阅读就会很繁琐;
  2. 工作量也增加不少;
  3. 更重要的是,不利于维护,比方 ui 姐姐改了一个显示效果,就会让人很头痛。

如何友好的二次封装一个组件

以分页效果为例。

假如我希望在每个需要分页组件的地方,只需要敲一行代码就可以,比如<pagi-nation @pageEvent="handleCurrentChange" :count="count"/>

那么,就需要基于 elementui 现有的分页进行再次封装。

首先,确定一下思路:

  • 子组件需要总页数、当前页数、每页数据量、总页数等信息
  • 总页数是父组件传递过来(prop),其他则可以直接在子组件中定义或者计算出来
  • 父组件需要当前页数、总页数信息
  • 总页数需要传递给子组件,当前页是子组件传递过来(emit)
  • 考虑到每个需要分页的父组件,都需要总页数信息、当前页和表格数据,我们可以定义一个mixin,来专门提供这几个属性。

实现

1、实现分页子组件

<template>
    <el-pagination
      background
      layout="slot, prev, pager, next"
      @current-change="handleCurrentChange"
      :current-page.sync="page"
      :page-size="pageSize"
      :prev-text="$tt('pagination.上一页')"
      :next-text="$tt('pagination.下一页')"
      :total="count"
    >
      <span
        v-html="$tt('pagination.page', { totalCount: count, pageCount: pages })"
      ></span>
    </el-pagination>
</template>

<script lang='ts'>
// $tt 是全局挂载的国际化,可以参考 i18n
import { Component, Vue, Prop, Emit } from 'vue-property-decorator'
@Component({})
export default class extends Vue {
  @Prop({ default: 0 }) private count!: number;
  // data属性
  private pageSize = 15  // 每页15条数据
  private page: number = 0 // 当前页码,默认0,即获取数据时的偏移量是0

  // 向父组件通信,告诉父组件当前页码,用于计算获取数据的偏移量
  @Emit('pageEvent')
  private handleCurrentChange (val: number) {
    this.page = val || this.page
    return this.page
  }
  // computed 计算总页数
  get pages () {
    return Math.ceil(this.count / this.pageSize)
  }
}
</script>

2、 实现提供共有属性的mixin

import { Vue, Component } from 'vue-property-decorator'

@Component
export default class TableMixin extends Vue {
  page = 0
  count = 0
  tableData = []
}

3、在父组件中引入和使用

<template>
<div>
	<el-table :data="tableData>
	</el-table>
	<pagi-nation @pageEvent="handleCurrentChange" :count="count"/>
</div>
</template>

<script lang="ts">
import pagiNation from '@/views/components/pagination.vue'
import tableMiXin from '@/mixins/table'
import { Component, Vue, Watch, Prop, Emit } from 'vue-property-decorator'
@Component({
  components: { pagiNation },
  mixins: [ tableMiXin ] //提供tableData、page、count属性
})
export default class extends Vue {
  private handleCurrentChange (page: number) {
    this.page = page || this.page
    this.getData()  // 获取数据
  }
  async getData(){
	let {list: tableData, count} = await getList({offset: 15 * this.page})
	this.tableData = tableData
	this.count = count
  }
}
</script>

这样,一个基于 elementui 的分页组件的二次封装就完成了。

总结

这里需要注意的是,利用 mixin 来对多个组件的共有属性进行定义和注入,这样做

  1. 可以极大的减少在组件内部定义属性和方法的数量,提高编程效率
  2. 便于代码的维护与更改,
  3. 优化了代码结构,提高可读性

思考

假如现在要对日期组件进行封装,我们知道,表格的日期筛选一般都是有开始时间和结束时间两个选项,这就要求我们需要对起止时间有严格的判断,那么,这怎么来做呢?

首先,依旧要做一个日期子组件

<template>
  <div>
    <div>
      <span>开始时间:</span>
      <el-date-picker
        v-model="startDate"
        type="datetime"
        @change="checkTime"
        value-format="yyyy-MM-dd HH:mm:ss"
        :placeholder="$tt('placeholder.开始时间')"
        :picker-options="sOption"
        >></el-date-picker
      >
    </div>
    <div>
      <span>结束时间:</span>
      <el-date-picker
        v-model="endDate"
        type="datetime"
        @change="checkTime"
        value-format="yyyy-MM-dd HH:mm:ss"
        :placeholder="$tt('placeholder.结束时间')"
        :picker-options="eOption"
      ></el-date-picker>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      startDate: '',
      endDate: '',
      sOption: {
        disabledDate: (time) =>
          this.endDate
            ? time.getTime() > Date.now() ||
              time.getTime() > new Date(this.endDate).getTime()
            : time.getTime() > Date.now()
      },
      eOption: {
        disabledDate: (time) =>
          this.startDate
            ? time.getTime() > Date.now() ||
              time.getTime() < new Date(this.startDate).getTime()
            : time.getTime() > Date.now()
      }
    }
  },
  methods: {
    checkTime () {
      let { startDate, endDate } = this
      let sDate = new Date(startDate)
      let eDate = new Date(endDate)
      if (e && sDate > eDate) {
        this.$message.info('开始时间不能大于结束时间')
        this.endDate = ''
      } else {
        this.$emit('getTime', { startDate, endDate })
      }
    }
  }
}
</script>

其次,要把父组件的共有属性方法抽取到一个 mixin 中

import { Vue, Component } from 'vue-property-decorator'

@Component
export default class DateMixin extends Vue {
  startDate = ''
  endDate = ''
  getTime (timeObj) {
    this.startDate = timeObj.startDate
    this.endDate = timeObj.endDate
  }
}

写完以后,就可以在父组件中使用了。

推荐阅读