Vue2+ElementUI列表、表格组件的封装

Vue2+ElementUI列表组件的封装:引言

在日常开发中,我们经常会遇到需要展示列表数据的场景。ElementUI 提供的 el-table 组件是一个功能强大的表格组件,可以满足大部分的需求。但是,在实际应用中,我们往往需要根据业务需求对 el-table 组件进行二次封装,以提高开发效率和代码的复用性。

本文将介绍 Vue2+ElementUI 列表组件的封装方法,包括:

  • 封装思路
  • 常见功能的实现
  • 代码示例
  • 注意点

封装思路

封装 el-table 组件的思路是将其作为一个独立的组件,对外暴露必要的属性和方法,并通过插槽机制来定制表格的内容。

具体来说,我们可以将 el-table 组件的封装分为以下几个步骤:

  1. 定义组件的 props,用于接收外部传入的数据和配置。
  2. 在组件内部,使用 el-table 组件来渲染表格。
  3. 通过插槽机制来定制表格的内容,例如表头、表尾、操作列等。
  4. 暴露一些方法,用于控制表格的操作,例如刷新、排序、筛选等。

创建组件模板并测试使用

创建组件,名为:H3yunTableCompV1,这是子组件

html 复制代码
<template>
  <div>
    <el-table
        ref="multipleTable"
        tooltip-effect="dark"
        style="width: 100%"
    >
      <el-table-column
          type="index">
      </el-table-column>

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

<script>
export default {
  name: "H3yunTableCompV1",
  data() {
    return {}
  },
  methods: {}
}
</script>

<style scoped>

</style>

创建测试类并使用上面的组件,方便测试。注意:导入组件时注意路径。这是父组件。

html 复制代码
<template>
  <div class="container" style="min-height: 100%; padding-bottom: 100px;">
    <H3yunTableCompV1></H3yunTableCompV1>
  </div>
</template>

<script>
import H3yunTableCompV1 from "../../components/table/H3yunTableCompV1";

export default {
  props: [],
  components: {
    H3yunTableCompV1
  },
  data() {
    return {}
  },
  watch: {},
  computed: {},
  beforeCreate() {
  },
  created() {
  },
  beforeMount() {
  },
  mounted() {
  },
  beforeUpdate() {
  },
  updated() {
  },
  destroyed() {
  },
  methods: {},
}
</script>

<style scoped>

.container {
}
</style>

父、子组件传值

父组件编写表格对象并传递给子组件。

js 复制代码
<H3yunTableCompV1 :data="tableObj"></H3yunTableCompV1>


data() {
    return {
      // 这个对象可以从后端获取,下面是一些示例数据
      tableObj: {
        // 数据列 - 即el-table-column组件中的属性
        columns: [
          {prop: 'date', label: '日期', width: '180'},
          {prop: 'name', label: '名字', width: '180'},
          {prop: 'address', label: '地址', width: '180'}
        ]
      }
    }
  }

子组件接收值并处理

js 复制代码
export default {
  name: "H3yunTableCompV1",
  data() {
    return {
      // 表格对象   -- 1
      tableObj: {
        columns: []
      }
    }
  },
  // 接收父组件传递过来的值
  props: {
    // 接收父组件传递过来的data数据,类型为Object   -- 2
    data: Object
  },
  watch: {
    // 监控父组件中的data,保证子组件中的tableObj与父组件一致   -- 3
    data: {
      deep: true, // 深度监控
      immediate: true, // 在组件创建时立即触发
      handler(newVal, oldVal) {
        this.init(newVal) // 初始化
      }
    }
  },
  methods: {
    // 初始化方法   -- 4
    init(newVal) {
      for (const key in newVal) {
        // 如果父组件传的值能与tableObj的属性匹配,比如父子组件都有tableObj.columns,则会进入这个if
        if (Object.keys(this.tableObj).includes(key)) {
          // 比如:tableObj.columns = newVal.columns
          this.tableObj[key] = newVal[key]
        }
      }

    }
  }
}

子组件循环 columns

html 复制代码
<el-table
        ref="multipleTable"
        tooltip-effect="dark"
        style="width: 100%"
    >
      <el-table-column
          type="index">
      </el-table-column>
      <el-table-column
          v-for="(column,key) in tableObj.columns"
          :key="key"
          :prop="column.prop"
          :label="column.label"
          :width="column.width"
      >
      </el-table-column>
    </el-table>

效果如下

列隐藏

比如说我想隐藏ID列。我给id这个配置添加一个属性为hide:true

js 复制代码
tableObj: {
        // 数据列 - 即el-table-column组件中的属性
        columns: [
          {prop: 'id', label: 'ID', width: '80', hide: true},
          {prop: 'date', label: '日期', width: '180'},
          {prop: 'name', label: '名字', width: '180'},
          {prop: 'address', label: '地址', width: '180'}
        ]
      }

id列未隐藏之前的效果。

子组件的el-table-column循环时加个属性v-if="!column.hide"。注意:这里使用v-show的话是不起作用的。

html 复制代码
     <el-table-column
          v-for="(column,key) in tableObj.columns"
          :key="key"
          :prop="column.prop"
          :label="column.label"
          :width="column.width"
          v-if="!column.hide"
      >
      </el-table-column>

隐藏后的效果

插槽 - 文字链接

先看效果,标题列是通过插槽的方式插入的。

子组件使用v-if去判断是否是插槽去做分别处理。下面是完整的代码,里面使用了临时数据,后面会通过父组件传递。

html 复制代码
<template>
  <div>
    <el-table
        ref="multipleTable"
        tooltip-effect="dark"
        style="width: 100%"
        :data="tableData"
        @selection-change="handleSelectionChange"
    >
      <!--多选-->
      <el-table-column
          type="selection"
          width="55">
      </el-table-column>
      <!--序号-->
      <el-table-column
          type="index">
      </el-table-column>
      <!--插槽处理-->
      <template v-for="(column,key) in tableObj.columns">
        <el-table-column
            :key="key"
            :prop="column.prop"
            :label="column.label"
            :width="column.width"
            v-if="column.type == 'slot'"
        >
          <template slot-scope="scope">
            <slot :name="column.slot_name" :row="scope.row"></slot>
          </template>
        </el-table-column>
        <el-table-column
            :key="key"
            :prop="column.prop"
            :label="column.label"
            :width="column.width"
            v-else-if="!column.hide"
        >
        </el-table-column>
      </template>


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

<script>
export default {
  name: "H3yunTableCompV1",
  data() {
    return {
      // 表格对象   -- 1
      tableObj: {
        columns: []
      },
      tableData: [{
        id: 1,
        title: '标题1',
        date: '2016-05-02',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1518 弄',
        tag: '家'
      }, {
        id: 2,
        title: '标题2',
        date: '2016-05-04',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1517 弄',
        tag: '公司'
      }, {
        id: 3,
        title: '标题3',
        date: '2016-05-01',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1519 弄',
        tag: '家'
      }]
    }
  },
  // 接收父组件传递过来的值
  props: {
    // 接收父组件传递过来的data数据,类型为Object   -- 2
    data: Object
  },
  watch: {
    // 监控父组件中的data,保证子组件中的tableObj与父组件一致   -- 3
    data: {
      deep: true, // 深度监控
      immediate: true, // 在组件创建时立即触发
      handler(newVal, oldVal) {
        this.init(newVal) // 初始化
      }
    }
  },
  methods: {
    handleSelectionChange(val) {
      this.multipleSelection = val;
    },
    // 初始化方法   -- 4
    init(newVal) {
      for (const key in newVal) {
        // 如果父组件传的值能与tableObj的属性匹配,比如父子组件都有tableObj.columns,则会进入这个if
        if (Object.keys(this.tableObj).includes(key)) {
          // 比如:tableObj.columns = newVal.columns
          this.tableObj[key] = newVal[key]
        }
      }

    }
  }
}
</script>

<style scoped>

</style>

父组件使用templatev-slot:插槽名去处理插槽。这里是拿到了整行数据而不仅仅是title这一列,里面的内容完全是自定义的。

html 复制代码
<H3yunTableCompV1 :data="tableObj">
      // 处理标题插槽
      <template v-slot:title_slot="scope">
        <el-link type="primary">{{ scope.row.title }}</el-link>
      </template>
</H3yunTableCompV1>

export default {
components: {
    H3yunTableCompV1
  },
  data() {
    return {
      // 这个对象可以从后端获取,下面是一些示例数据
      tableObj: {
        // 数据列 - 即el-table-column组件中的属性
        columns: [
          {prop: 'title', label: '标题', width: '180', type: 'slot', slot_name: 'title_slot'}, // 定义标题列是插槽
          {prop: 'date', label: '日期', width: '180'},
          {prop: 'name', label: '名字', width: '180'},
          {prop: 'address', label: '地址', width: '180'}
        ]
      }
    }
  }
}

插槽 - 按钮

注意:插槽 - 按钮这个示例是在上一个示例的基础上的。

效果如下:

增加列,并定义插槽的名称为operate_slot

js 复制代码
{prop: 'operate', label: '操作', width: '180', type: 'slot', slot_name: 'operate_slot'}

插槽处理

html 复制代码
    <H3yunTableCompV1 :data="tableObj">
      // 标题 - 插槽
      <template v-slot:title_slot="scope">
        <el-link type="primary">{{ scope.row.title }}</el-link>
      </template>
      // 操作 - 插槽
      <template v-slot:operate_slot="scope">
        <el-button-group>
          <el-button type="primary" disabled>编辑</el-button>
          <el-button type="primary" disabled>删除</el-button>
        </el-button-group>
      </template>
    </H3yunTableCompV1>

分页

效果如下:

代码改动比较多,下面直接上代码。

父组件如下,主要增加与后端联动。

html 复制代码
<template>
  <div class="container" style="min-height: 100%; padding-bottom: 100px;">
    <H3yunTableCompV1 :data="tableObj">
      // 标题 - 插槽
      <template v-slot:fnsku_slot="scope">
        <el-link type="primary">{{ scope.row.fnsku }}</el-link>
      </template>
      // 操作 - 插槽
      <template v-slot:operate_slot="scope">
        <el-button-group>
          <el-button type="primary" disabled>编辑</el-button>
          <el-button type="primary" disabled>删除</el-button>
        </el-button-group>
      </template>
    </H3yunTableCompV1>
  </div>
</template>

<script>
import H3yunTableCompV1 from "../../components/table/H3yunTableCompV1";

export default {
  props: [],
  components: {
    H3yunTableCompV1
  },
  data() {
    return {
      // 这个对象可以从后端获取,下面是一些示例数据
      tableObj: {
        http: {
          // 这个url你们需要写成完整的url
          url: '/logisticSalesPrediction/list',
          params: {},
          // url返回结果的数据结构,
          result: {
            // 表格内容表示是在result.records中获取
            list: 'records',
            // 数据量总数是在result.total中获取
            total: 'total'
          }
        },
        pageNum: 1,
        pageSize: 10,
        total: 0,
        // 数据列 - 即el-table-column组件中的属性
        columns: [
          {prop: 'fnsku', label: 'fnsku', width: '180', type: 'slot', slot_name: 'fnsku_slot'}, // 定义标题列是插槽
          {prop: 'isSeason', label: '是否是时令性产品', width: '180'},
          {prop: 'activityIncrement', label: '活动增量', width: '180'},
          {prop: 'prediction', label: '销量预测', width: '180'},
        ]
      }
    }
  },
  methods: {},
}
</script>

<style scoped>

.container {
}
</style>

下面是子组件的代码。

首先,在table标签下面增加了分页组件,其次增加了axios的请求,与响应结果的处理。细节部分呢,就是切换页码,页大小和序号。

html 复制代码
<template>
  <div>
    <el-table
        ref="multipleTable"
        tooltip-effect="dark"
        style="width: 100%"
        :data="tableData"
        @selection-change="handleSelectionChange"
    >
      <!--多选-->
      <el-table-column
          type="selection"
          width="55">
      </el-table-column>
      <!--序号-->
      <el-table-column
          type="index"
          :index="calIndex"
      >
      </el-table-column>
      <!--插槽处理-->
      <template v-for="(column,key) in tableObj.columns">
        <el-table-column
            :key="key"
            :prop="column.prop"
            :label="column.label"
            :width="column.width"
            v-if="column.type == 'slot'"
        >
          <template slot-scope="scope">
            <slot :name="column.slot_name" :row="scope.row"></slot>
          </template>
        </el-table-column>
        <el-table-column
            :key="key"
            :prop="column.prop"
            :label="column.label"
            :width="column.width"
            v-else-if="!column.hide"
        >
        </el-table-column>
      </template>
    </el-table>
    <div>
      <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="tableObj.pageNum"
          :page-sizes="[10, 20, 50, 100]"
          :page-size="tableObj.pageSize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="tableObj.total">
      </el-pagination>
    </div>

  </div>
</template>

<script>

import request from "../../utils/request";

export default {
  name: "H3yunTableCompV1",
  data() {
    return {
      // 表格对象   -- 1
      tableObj: {
        http: {
          url: undefined,
          params: {},
          result: {
            list: undefined,
            total: undefined
          }
        },
        pageNum: 1,
        pageSize: 10,
        total: 0,
        columns: []
      },
      tableData: []
    }
  },
  // 接收父组件传递过来的值
  props: {
    // 接收父组件传递过来的data数据,类型为Object   -- 2
    data: Object
  },
  watch: {
    // 监控父组件中的data,保证子组件中的tableObj与父组件一致   -- 3
    data: {
      deep: true, // 深度监控
      immediate: true, // 在组件创建时立即触发
      handler(newVal, oldVal) {
        this.init(newVal) // 初始化
      }
    }
  },
  // 挂载之前加载数据
  beforeMount() {
    this.load()
  },
  methods: {
    // 计算序号
    calIndex(index) {
      return (this.tableObj.pageNum - 1) * (this.tableObj.pageSize) + index + 1
    },
    // 构建查询参数
    buildParams() {
      const params = this.tableObj.http.params
      params.pageNum = this.tableObj.pageNum
      params.pageSize = this.tableObj.pageSize
      return params
    },
    // 解析构建结果 - 为了适配 list: 'data.records',做一下处理
    buildResult(data, key) {
      const keys = key.split('.')
      let res = data
      for (const key of keys) {
        res = res[key]

      }
      return res
    },
    // 加载后端数据
    load() {
      // request对axios进行了一层封装
      request.post(this.tableObj.http.url, this.buildParams()).then(pageObj => {
        // 后端返回结果,需要的字段在哪不写死
        this.tableData = this.buildResult(pageObj, this.tableObj.http.result.list)
        this.tableObj.total = this.buildResult(pageObj, this.tableObj.http.result.total)
      })
    },
    // 更改每页数量
    handleSizeChange(val) {
      this.tableObj.pageSize = val
      this.tableObj.pageNum = 1
      this.load()
    },
    // 页码切换
    handleCurrentChange(val) {
      this.tableObj.pageNum = val
      this.load()
    },
    // 多选
    handleSelectionChange(val) {
      this.multipleSelection = val;
    },
    // 初始化方法   -- 4
    init(newVal) {
      for (const key in newVal) {
        // 如果父组件传的值能与tableObj的属性匹配,比如父子组件都有tableObj.columns,则会进入这个if
        if (Object.keys(this.tableObj).includes(key)) {
          // 比如:tableObj.columns = newVal.columns
          this.tableObj[key] = newVal[key]
        }
      }

    }
  }
}
</script>

<style scoped>

</style>
相关推荐
小满zs4 分钟前
Next.js第一章(入门)
前端
摇滚侠5 分钟前
CSS(层叠样式表)和SCSS(Sassy CSS)的核心区别
前端·css·scss
不爱吃糖的程序媛8 分钟前
Electron 桌面应用开发入门指南:从零开始打造 Hello World
前端·javascript·electron
Dontla15 分钟前
前端状态管理,为什么要状态管理?(React状态管理、zustand)
前端·react.js·前端框架
编程猪猪侠16 分钟前
前端根据文件后缀名智能识别文件类型的实用函数
前端
yinuo23 分钟前
基于 Git Submodule 的代码同步融合方案
前端
伶俜monster35 分钟前
大模型 “万能接口” MCP 横空出世!打破数据孤岛,重塑 AI 交互新规则
前端·mcp
你听得到1135 分钟前
肝了半个月,我用 Flutter 写了个功能强大的图片编辑器,告别image_cropper
android·前端·flutter
宇余1 小时前
前端新玩具Vike:摆脱框架绑架,实现真正的技术自由
vue.js
Macbethad1 小时前
Typora 精通指南:掌握高效 Markdown 写作的艺术
前端·macos·前端框架