Vue封装通用table组件的完整步骤记录

目录

前言
为什么需要封装table组件?
第一步:定义通用组件
第二步:父组件与子组件进行render通信
第三步:使用组件
总结

前言

随着业务的发展和功能的增多,我们发现不少页面都具备相似的功能,这里举几个比较俗的例子:可以多选的下拉菜单,带输入的对话框,日期选择器等等,于是我们会想办法将这些共有的功能抽取成一个个公共组件,以便能够在不同的页面或业务中使用。

为什么需要封装table组件?

后台管理系统表格使用频率高,减少关于table的业务代码,且便于后期统一修改、便于后期维护。如给table内容展示,超出单元格以省略号展示等。

对于大部分的后台管理系统,数据表格的展示大同小异,由于不想写重复的代码,所以我选择封装通用table组件,解放双手。如果你的表格有一列并不是简单dom元素,比如switch按钮,完全可以传入一个render函数,来达到目的。

第一步:定义通用组件

<!-- pro-table.vue -->
<template>
  <div>
    <el-table
      :data=\"tableData\"
      style=\"width: 100%\"
      :stripe=\"tableTitle.stripe\"
      :border=\"tableTitle.border\"
      :fit=\"tableTitle.fit\"
      :highlight-current-row=\"tableTitle.highlightCurrentRow\"
      @selection-change=\"handleSelectionChange\">
      <!--表格第一列-->
      <el-table-column
        :type=\"firstTableCol.type\"
        :width=\"firstTableCol.width\"
        v-if=\"firstTableCol.select\"
      >
      </el-table-column>
      <!--表格其它列-->
      <el-table-column v-for=\"(value,index) in tableCol\" :key=\"index\"
                       :prop=\"value.prop\"
                       :label=\"value.label\"
                       :width=\"value.width || 180\">
        <template slot-scope=\"scope\">
          <template v-if=\"!value.render\">
            <template v-if=\"value.formatter\">
              {{ value.formatter(scope.row, value) }}
            </template>
            <template v-else-if=\"value.getImgurl\">
              <el-image :src=\"value.getImgurl(scope.row, value)\" style=\"width: 70px; height: 70px\"
                        :preview-src-list=\"value.previewSrcList ? value.previewSrcList(scope.row, value) : value.getImgurl(scope.row, value).split(\',\')\"/>
            </template>
            <template v-else>
              {{ scope.row[value.prop] }}
            </template>
          </template>
          <!--扩展dom-->
          <template v-else>
            <Table :key=\"`cus${index}`\" :render=\"value.render\" :param=\"scope.row\"></Table>
          </template>
        </template>
      </el-table-column>
      <!--基础操作-->
      <el-table-column label=\"操作\">
        <template slot-scope=\"scope\">
          <el-button type=\"text\" v-for=\"(value,index) in operator\" @click=\"value.click(scope.row, value)\" :key=\"index\">
            {{ value.text }}
          </el-button>
        </template>
      </el-table-column>
    </el-table>
    <!--分页插件-->
    <el-pagination
      v-show=\"total>0\"
      :total=\"total\"
      :page-size.sync=\"pageSize\"
      :current-page.sync=\"currentPage\"
      :page-sizes=\"[10, 20, 30, 50]\"
      layout=\"total, sizes, prev, pager, next, jumper\"
      @current-change=\"handleCurrentChange\"
      @size-change=\"handleSizeChange\"
      v-bind=\"$attrs\">
    </el-pagination>
  </div>
</template>

<script>
// render函数
import Table from \'./table\'
export default {
  components: {Table},
  props: {
    tableTitle: {
      type: Object,
      default: {
        stripe: false,
        border: false,
        fit: true,
        highlightCurrentRow: false
      }
    },
    firstTableCol: {
      type: Object,
      default: {
        select: false,
        width: 55,
        type: \'selection\'
      }
    },
    tableCol: {
      type: Array,
      default: []
    },
    tableData: {
      type: Array,
      default: []
    },
    operator: {
      type: Array,
      default: []
    },
    total: {
      type: Number,
      default: 0
    },
    page: {
      type: Number,
      default: 1
    },
    limit: {
      type: Number,
      default: 10
    },
    autoScroll: {
      type: Boolean,
      default: true
    }
  },
  computed: {
    currentPage: {
      get () {
        return this.page
      },
      set (val) {
        this.$emit(\'update:page\', val)
      }
    },
    pageSize: {
      get () {
        return this.limit
      },
      set (val) {
        this.$emit(\'update:limit\', val)
      }
    }
  },
  data () {
    return {
    }
  },
  methods: {
    // 监听table选择框
    handleSelectionChange (selection) {
      // 调用父组件对应的方法 handleSelectionChange
      this.$emit(\'handleSelectionChange\', selection)
    },
    // 监听每页多少条数据(limit)
    handleSizeChange (limit) {
      this.$emit(\'pagination\', {page: this.currentPage, limit: limit})
      if (this.autoScroll) {
        scrollTo(0, 800)
      }
    },
    // 监听当前是第几页(page)
    handleCurrentChange (page) {
      this.$emit(\'pagination\', {page: page, limit: this.pageSize})
      if (this.autoScroll) {
        scrollTo(0, 800)
      }
    }
  }
}
</script>
<style scoped>
</style>

第二步:父组件与子组件进行render通信

为了实现父组件render函数在子组件中生效,我们需要定义一个render函数,在子组件中引用。

// table.js
export default {
  props: {
    render: {
      type: Function
    },
    param: {
      type: Object
    }
  },
  render(h) {
    return this.render(h, this.param)
  }
}

第三步:使用组件

<template>
  <div>
    <!--
        @自定义事件=\"父组件方法\", 子组件中,this.$emit(\'自定义事件名称\') 触发父组件事件。
        ref=\"proTable\",标记在子组件上,指向子组件实例
    -->
    <proTable ref=\"proTable\" :tableTitle=\"tableTitle\" :tableCol=\"tableCol\" :tableData=\"tableData\" :operator=\"operator\"
        :firstTableCol=\"firstTableCol\"
        @handleSelectionChange=\"handleSelectionChange\"
        :total=\"total\" :page.sync=\"queryParams.page\" :limit.sync=\"queryParams.limit\" @pagination=\"getList\"/>

  </div>
</template>

<script>
import proTable from \'./pro-table\'

export default {
  components: {
    proTable
  },
  data() {
    return {
      queryParams: {
        page: 1,
        limit: 10,
      },
      type: \'success\',
      total: 50,
      // element-ui中对table属性的设置
      tableTitle: {
        \'stripe\': true,
        \"highlightCurrentRow\": true
      },
      // 设置table的列
      tableCol: [
        { prop:\'date\',label:\'日期\'},
        { prop:\'name\',label:\'姓名\'},
        { prop:\'address\',label:\'地址\',width: 300},
        { prop:\'src\',label:\'图片\',  
        getImgurl: (row, col, cellValue) => { return this.getImgurl(row)}, 
        previewSrcList: (row, col, cellValue) => {return this.listImgUrl(row)}},
        { prop:\'sex\',label:\'性别\',  
        formatter: (row, col, cellVaule) => {return this.sexFormatter(row)}},
        { prop:\'src\',label:\'图片\',  
        getImgurl: (row, col, cellValue) => { return this.getImgurl(row)}},
        { prop:\'text\',label:\'函数\', render: (h, params) => {return  this.render(h, params)}}
      ],
      // table的基本操作
      operator: [
        {\'text\':\'详情\', click: (row, col, cellValue) => {return this.getInfo(row)}},
        {\'text\':\'删除\', click: (row, col, cellValue) => {return this.delInfo(row)}},
        {\'text\':\'编辑\', click: (row, col, cellValue) => {return this.editInfo(row)}},
      ],
      // 模拟数据
      tableData: [
        {
          date: \'2016-05-02\',
          name: \'王小虎\',
          address: \'上海市普陀区金沙江路 1518 弄\',
          sex: 0,
          img:\'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic2.zhimg.com%2F50%2Fv2-193cbb243dc14d3a016caaa54ba02837_hd.jpg&refer=http%3A%2F%2Fpic2.zhimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1628435704&t=deb5584cb9ff53fe6977f14a5e0755bb\'
        }, {
          date: \'2016-05-04\',
          name: \'王小虎\',
          address: \'上海市普陀区金沙江路 1517 弄\',
          sex: 1,
          img:\'https://pic1.zhimg.com/80/v2-894ab624807fd4cfa33dd4e42cc90ac8_720w.jpg?source=1940ef5c\'
        }, {
          date: \'2016-05-01\',
          name: \'王小虎\',
          address: \'上海市普陀区金沙江路 1519 弄\',
          sex: 0,
          img:\'xx.jpg\'
        }, {
          date: \'2016-05-03\',
          name: \'王小虎\',
          address: \'上海市普陀区金沙江路 1516 弄\',
          sex: 1,
          img:\'xx.jpg\'
        }],
      firstTableCol: {
        \'select\': true,
        \'type\': \'selection\'
      }
    }
  },
  methods: {
    getInfo(val) {
      // 触发父方法
      console.log(\"获取详情\",val)
    },
    delInfo(val) {
      // 触发父方法
      console.log(\"删除信息\",val)
    },
    editInfo(val) {
      // 触发父方法
      console.log(\"编辑信息\",val)
    },
    getImgurl(val) {
      console.log(val.img)
      return val.img
    },
    sexFormatter(val) {
      return val.sex === 0 ? \'男\' : \'女\'
    },
    handleSelectionChange(val) {
      console.log(\"监听选择框\",val)
    },
    getList(queryParams) {
      console.log(\"父级方法\",queryParams)
    },
    listImgUrl() {
      let array = [];
      array.push(\"https://pic1.zhimg.com/80/v2-894ab624807fd4cfa33dd4e42cc90ac8_720w.jpg?source=1940ef5c\");
      array.push(\"https://cdn.pixabay.com/photo/2021/07/01/21/20/girl-6380331_960_720.jpg\");
      return array;
    },
    render(h, params) {
      return h(\'span\', null , \'我是一个render组件\')
    }
  }
}
</script>

总结

在引用组件的页面中,我们可以给每一个table列加方法,也可以给编辑、删除、详情添加自定义的方法,完全实现定制化。也可以自定义render函数。效果图如下:

Vue封装通用table组件的完整步骤记录

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容