当前位置:   article > 正文

Vue3 ElementPlus 二次封装常用表格展示组件_vue3 elementplus 表格

vue3 elementplus 表格

vue3通用表格组件的由来,前面讲过后台管理页面中查询展示表格数据基本上是必备功能。前面我发布过一篇使用vue2+element-ui封装的通用页面组件。原文可访问这篇文章 还在写重复的增删改查的重复代码?还在重复写Ajax请求?Vue2+Element-ui实现常规后台查询展示页面来了

在vue3普及的现在。这篇文章将使用vue3和element-plus。在el-table组件的基础上二次封装表格的常用展示形式。简化我们的代码使用方式。减少重复代码量,提高一定的开发效率。下面我们来一起看看我们的实现思路。

首先我们来初始化一个vue3的项目

初始化vue3的项目我们使用vite+vue3+ts,这里就不多叙述了,大家可以看这篇文章: 初始化

我们先来看看表格的基本效果:

我们来列举下表格的几个常用功能

1.基础文本展示

2.枚举类型展示 如:(状态,分类,标签,等根据status或type等字段展示对应的内容)

3.文本过长超出省略号

4.鼠标悬浮展示

5.操作区域 如:(查看,编辑,删除等操作)

根据以上的功能我们来在el-table的基础上把他封装到一个通用组件中

这里我们使用tsx函数组件的方式来实现,这样避免在template模版中使用大量的if else 哦我

我们先来定义一个table组件 在components文件中定义一个mijiTable.tsx的组件

首先我们在组件中引入element-plus的el-table、el-table-column组件

在这个组件中我们使用defineComponent来定义一个组件,使用组合式api setup来实现内部逻辑

这个组件我们接受两个值,一个是用来渲染el-table-column的数据 columns,一个是表格要渲染的数据 dataSource

我们来看看columns字段都包含了什么:

其实columns的内容字段就是 el-table-column组件的属性我们在这个基础上扩展一个slot字段用来自定义单元格内容

  1. [{
  2. prop: '',
  3. label: '操作',
  4. type: '', //
  5. slot: 'options',
  6. width: 120,
  7. fixed: 'right'
  8. }]

在element-plus中 type属性默认值改成了default我们这里的type不用做el-table-column的type属性。而是用做区分表格列是什么格式。目前我们实现的有以下几种

常规 text、index序号、枚举、和操作栏

我们来看看如何动态渲染el-table-colunm

  1. import {
  2. ElTable,
  3. ElTableColumn
  4. } from 'element-plus'
  5. export default defineComponent({
  6. props: {
  7. columns: {
  8. type: Array,
  9. default: () => []
  10. },
  11. dataSource: {
  12. type: Array,
  13. default: () => []
  14. }
  15. },
  16. setup(props, { slots }: any) {
  17. const { columns, dataSource } = props
  18. console.log('渲染表格列的数据', columns)
  19. console.log('表格数据:', dataSource);
  20. const tableData = ref()
  21. watch(() => props.dataSource, (now) => {
  22. tableData.value = now
  23. })
  24. const initTable = () => {
  25. return <ElTable
  26. data={tableData.value}
  27. style={{ width: "100%" }}>
  28. </ElTable>
  29. }
  30. return () => initTable()
  31. }
  32. })

基础的文本渲染没有什么特殊的还是使用el-table-column组件就好

  1. <ElTableColumn
  2. prop={col.prop}
  3. label={col.label}
  4. className={col.className}
  5. width={colWidth}
  6. min-width={minWidth}
  7. show-overflow-tooltip={showOverflowTooltip}
  8. fixed={fixed}
  9. >
  10. {{ ...tableCellSlot }}
  11. </ElTableColumn>

这里我就不列举官方的这些属性了,大家可以去官方文档查看: el-table-column属性

下面我们看看如何动态渲染不同类型的table-column

我们在el-table中写如下代码:利用数组的map去生成el-table-column组件,在map中我们来判断当前的column是什么类型的 然后动态的返回就好。

  1. {
  2. columns.map((col: any) => {
  3. const colWidth = col.width || '';
  4. const minWidth = col.minWidth || '';
  5. const fixed = col.fixed || false;
  6. const showOverflowTooltip = col.showOverflowTooltip || false
  7. const length = col.options && col.options.length || null
  8. const headerSlot = genHeaderSlot(col); // 自定义表头
  9. const defaultSlot = genDefaultSlot(col); // 自定义表格单元格
  10. const tableCellSlot = {
  11. ...headerSlot,
  12. ...defaultSlot,
  13. }
  14. // 序号
  15. if (col.prop === 'index') {
  16. return (<ElTableColumn
  17. label={col.label}
  18. type="index"
  19. className={col.className}
  20. width={55}
  21. />);
  22. }
  23. switch (col.type) {
  24. // 操作栏
  25. case 'options':
  26. return (
  27. <ElTableColumn
  28. prop={col.prop}
  29. label={col.label}
  30. className={col.className}
  31. width={colWidth || (length > 3 ? '180px' : '160px')}
  32. min-width={minWidth}
  33. show-overflow-tooltip={showOverflowTooltip}
  34. fixed={fixed}
  35. formatter={(row) => {
  36. let btns, moreBtn
  37. if (length > 3) {
  38. btns = col.options.slice(0, 2)
  39. moreBtn = col.options.slice(2, length)
  40. } else {
  41. btns = col.options
  42. }
  43. return length > 3 ? renderOptionsAndMore(btns, moreBtn, row) : renderOptions(btns, row)
  44. }}
  45. >
  46. {{ ...tableCellSlot }}
  47. </ElTableColumn>
  48. );
  49. // 枚举类型的表格单元格
  50. case 'map':
  51. if (col.typeMap) { // 状态枚举
  52. return (<ElTableColumn
  53. prop={col.prop}
  54. label={col.label}
  55. className={col.className}
  56. width={colWidth}
  57. min-width={minWidth}
  58. show-overflow-tooltip={showOverflowTooltip}
  59. fixed={fixed}
  60. formatter={(row): any => {
  61. return renderTableMapCell(row, col);
  62. }}
  63. >
  64. {{ ...tableCellSlot }}
  65. </ElTableColumn>);
  66. } else { // 普通枚举
  67. return (<ElTableColumn
  68. prop={col.prop}
  69. label={col.label}
  70. className={col.className}
  71. width={colWidth}
  72. min-width={minWidth}
  73. show-overflow-tooltip={showOverflowTooltip}
  74. fixed={fixed}
  75. formatter={(row) => {
  76. return col.map[row[col.prop]];
  77. }}
  78. >
  79. {{ ...tableCellSlot }}
  80. </ElTableColumn>);
  81. }
  82. default:
  83. return (
  84. <ElTableColumn
  85. prop={col.prop}
  86. label={col.label}
  87. className={col.className}
  88. width={colWidth}
  89. min-width={minWidth}
  90. show-overflow-tooltip={showOverflowTooltip}
  91. fixed={fixed}
  92. >
  93. {{ ...tableCellSlot }}
  94. </ElTableColumn>
  95. );
  96. }
  97. })
  98. }

index类型就不过多解释了,来看看column中的options类型,也就是表格的操作栏,

操作栏动态计算高度或者通过传入的宽度来设置宽度,这里的按钮我们通过 options 类型中的options数组来渲染

  1. const buttonEvents = (v: any, row: any) => {
  2. if (v.clickEvent) {
  3. v.clickEvent(row)
  4. } else {
  5. alert('请传入clickEvent事件参数')
  6. }
  7. }
  8. const renderButton = (v: any, row: any) => {
  9. return <ElButton
  10. text
  11. type={v.type}
  12. disabled={v.disabled && v.disabled(row)}
  13. class={v.className}
  14. style={{ padding: '5px' }}
  15. onClick={() => buttonEvents(v, row)}
  16. >
  17. {v.text}
  18. </ElButton>
  19. }
  20. // 渲染按钮
  21. const renderOptions = (btns: any, row: any) => {
  22. return btns && btns.map((v: any) => {
  23. return renderButton(v, row)
  24. })
  25. }
  26. // 渲染更多按钮
  27. const renderOptionsAndMore = (btns: any, moreBtn: any, row: any) => {
  28. return (<div>
  29. {renderOptions(btns, row)}
  30. {
  31. <ElDropdown v-slots={{
  32. dropdown: () => <ElDropdownMenu>
  33. {moreBtn.map((v: any) => {
  34. return <ElDropdownItem>
  35. {renderButton(v, row)}
  36. </ElDropdownItem>
  37. })}
  38. </ElDropdownMenu>
  39. }}>
  40. <ElButton style={{ marginLeft: '10px', padding: '5px' }} type="primary" text>
  41. 更多<ElIcon><ArrowDownBold /></ElIcon>
  42. </ElButton>
  43. </ElDropdown>
  44. }
  45. </div>)
  46. }

除了options类型还有枚举和插槽这两种形式,插槽的就相对简单了。

  1. // 根据自定义表头的配置,动态生成需要的scopedSlot对象
  2. const genHeaderSlot = (col: any) => {
  3. if (col.headerSlot) {
  4. return {
  5. header: () => {
  6. return slots.headerSlot && slots.headerSlot();
  7. }
  8. };
  9. }
  10. return {};
  11. }
  12. // 自定义表格单元格的slot显示
  13. const genDefaultSlot = (col: any) => {
  14. if (col.slot) {
  15. return {
  16. // scope 是当前渲染单元格的数据
  17. default: (scope: any) => {
  18. return slots[col.slot] && slots[col.slot](scope);
  19. }
  20. }
  21. }
  22. }
  23. 在table-column 中我们这样使用的
  24. const headerSlot = genHeaderSlot(col); // 自定义表头
  25. const defaultSlot = genDefaultSlot(col); // 自定义表格单元格
  26. const tableCellSlot = {
  27. ...headerSlot,
  28. ...defaultSlot,
  29. }
  30. <ElTableColumn
  31. prop={col.prop}
  32. label={col.label}
  33. >
  34. {{ ...tableCellSlot }} //这里展开插槽的对象默认渲染的 是 default: () => col.slot()
  35. </ElTableColumn>

枚举的形式我们以普通的和带徽标的为例,枚举的我们要额外接收两个参数,map和typeMap,map为当前数据所要映射的内容,typeMap为带徽标组件的类型'primary' | 'success' | 'warning' | 'danger' | 'info' 你们也可以自行拓展其他的组件,如tag等展示型组件

  1. // 渲染 type: map 的单元格
  2. const renderTableMapCell = (row: any, col: any) => {
  3. if (col.map[row[col.prop]]) {
  4. return <ElBadge
  5. is-dot={true}
  6. type={col.typeMap[row[col.prop]]}
  7. dot-position="left-middle"
  8. >
  9. {col.map[row[col.prop]]}
  10. </ElBadge>;
  11. }
  12. return null;
  13. }

到这里我们基础的使用列已经封装好了,下面我们来看看具体的使用方式。

使用方法

可以全局引入注册全局组件,也可以在单独页面中引入。这里我们看下全局组件

  1. import { createApp } from 'vue'
  2. import './style.css'
  3. import App from './App.vue'
  4. import router from './router/index'
  5. import ElementPlus from 'element-plus'
  6. import 'element-plus/dist/index.css'
  7. // 引入表格组件
  8. import MiJiTable from './components/mijiTable'
  9. const app = createApp(App)
  10. // 这里通过app.component注册表格组件为全局组件
  11. app.component(MiJiTable.name, MiJiTable)
  12. app.use(router).use(ElementPlus).mount('#app')

这里MiJiTable.name就是组件中的name属性。miji-table

这样我们在页面中就可以直接使用<miji-table></miji-table>

  1. <template>
  2. <miji-table :columns="columns" :dataSource="dataSource"></miji-table>
  3. </template>
  4. <script setup lang="ts">
  5. import { onMounted, ref } from "vue";
  6. // 我们初始化的时候定义一个columns 定义好要渲染表格的列有哪些。
  7. const columns = ref([
  8. { prop: "index", label: "序号", width: "80px" },
  9. { prop: "date", label: "Date" },
  10. { prop: "name", label: "Name" },
  11. {
  12. prop: "state",
  13. label: "State",
  14. },
  15. { prop: "city", label: "City", minWidth: "120px" },
  16. { prop: "address", label: "Address", minWidth: "120px" },
  17. { prop: "zip", label: "Zip", minWidth: "120px" },
  18. { prop: "tag", label: "Tag", minWidth: "120px" },
  19. {
  20. prop: "option",
  21. label: "操作",
  22. fixed: "right",
  23. type: "options",
  24. options: [
  25. {
  26. type: "primary",
  27. text: "查看",
  28. clickEvent: (row) => optionsFunction(row),
  29. }
  30. ],
  31. },
  32. ]);
  33. // 定义 表格数据的变量
  34. const dataSource = ref([]);
  35. const optionsFunction = (row) => {
  36. console.log(row);
  37. };
  38. onMounted(() => {
  39. setTimeout(() => {
  40. dataSource.value = [] // 这里做请求数据填充
  41. }, 1000);
  42. });
  43. </script>

完整代码:如下

  1. import { watch, defineComponent, ref } from 'vue'
  2. import {
  3. ElTable,
  4. ElTableColumn,
  5. ElButton,
  6. ElDropdown,
  7. ElDropdownMenu,
  8. ElDropdownItem,
  9. ElBadge,
  10. ElIcon
  11. } from 'element-plus'
  12. import { ArrowDownBold } from '@element-plus/icons-vue'
  13. import { tableProps } from './props'
  14. export default defineComponent({
  15. props: tableProps,
  16. setup(props, { slots }: any) {
  17. const { columns, dataSource } = props
  18. console.log('表格数据:', dataSource);
  19. const tableData = ref()
  20. watch(() => props.dataSource, (now) => {
  21. tableData.value = now
  22. })
  23. const buttonEvents = (v: any, row: any) => {
  24. if (v.clickEvent) {
  25. v.clickEvent(row)
  26. } else {
  27. alert('请传入clickEvent事件参数')
  28. }
  29. }
  30. const renderButton = (v: any, row: any) => {
  31. return <ElButton
  32. text
  33. type={v.type}
  34. disabled={v.disabled && v.disabled(row)}
  35. class={v.className}
  36. style={{ padding: '5px' }}
  37. onClick={() => buttonEvents(v, row)}
  38. >
  39. {v.text}
  40. </ElButton>
  41. }
  42. // 渲染按钮
  43. const renderOptions = (btns: any, row: any) => {
  44. return btns && btns.map((v: any) => {
  45. return renderButton(v, row)
  46. })
  47. }
  48. // 渲染更多按钮
  49. const renderOptionsAndMore = (btns: any, moreBtn: any, row: any) => {
  50. return (<div>
  51. {renderOptions(btns, row)}
  52. {
  53. <ElDropdown v-slots={{
  54. dropdown: () => <ElDropdownMenu>
  55. {moreBtn.map((v: any) => {
  56. return <ElDropdownItem>
  57. {renderButton(v, row)}
  58. </ElDropdownItem>
  59. })}
  60. </ElDropdownMenu>
  61. }}>
  62. <ElButton style={{ marginLeft: '10px', padding: '5px' }} type="primary" text>
  63. 更多<ElIcon><ArrowDownBold /></ElIcon>
  64. </ElButton>
  65. </ElDropdown>
  66. }
  67. </div>)
  68. }
  69. // 渲染 type: map 的单元格
  70. const renderTableMapCell = (row: any, col: any) => {
  71. if (col.map[row[col.prop]]) {
  72. return <ElBadge
  73. is-dot={true}
  74. type={col.typeMap[row[col.prop]]}
  75. dot-position="left-middle"
  76. >
  77. {col.map[row[col.prop]]}
  78. </ElBadge>;
  79. }
  80. return null;
  81. }
  82. // 根据自定义表头的配置,动态生成需要的scopedSlot对象
  83. const genHeaderSlot = (col: any) => {
  84. if (col.headerSlot) {
  85. return {
  86. header: () => {
  87. return slots.headerSlot && slots.headerSlot();
  88. }
  89. };
  90. }
  91. return {};
  92. }
  93. // 自定义表格单元格的slot显示
  94. const genDefaultSlot = (col: any) => {
  95. if (col.slot) {
  96. return {
  97. // scope 是当前渲染单元格的数据
  98. default: (scope: any) => {
  99. return slots[col.slot] && slots[col.slot](scope);
  100. }
  101. }
  102. }
  103. }
  104. const initTable = () => {
  105. return <ElTable
  106. data={tableData.value}
  107. style={{ width: "100%" }}>
  108. {
  109. columns.map((col: any) => {
  110. const colWidth = col.width || '';
  111. const minWidth = col.minWidth || '';
  112. const fixed = col.fixed || false;
  113. const showOverflowTooltip = col.showOverflowTooltip || false
  114. const length = col.options && col.options.length || null
  115. const headerSlot = genHeaderSlot(col); // 自定义表头
  116. const defaultSlot = genDefaultSlot(col); // 自定义表格单元格
  117. const tableCellSlot = {
  118. ...headerSlot,
  119. ...defaultSlot,
  120. }
  121. // 序号
  122. if (col.prop === 'index') {
  123. return (<ElTableColumn
  124. label={col.label}
  125. type="index"
  126. className={col.className}
  127. width={55}
  128. />);
  129. }
  130. switch (col.type) {
  131. // 操作栏
  132. case 'options':
  133. return (
  134. <ElTableColumn
  135. prop={col.prop}
  136. label={col.label}
  137. className={col.className}
  138. width={colWidth || (length > 3 ? '180px' : '160px')}
  139. min-width={minWidth}
  140. show-overflow-tooltip={showOverflowTooltip}
  141. fixed={fixed}
  142. formatter={(row) => {
  143. let btns, moreBtn
  144. if (length > 3) {
  145. btns = col.options.slice(0, 2)
  146. moreBtn = col.options.slice(2, length)
  147. } else {
  148. btns = col.options
  149. }
  150. return length > 3 ? renderOptionsAndMore(btns, moreBtn, row) : renderOptions(btns, row)
  151. }}
  152. >
  153. {{ ...tableCellSlot }}
  154. </ElTableColumn>
  155. );
  156. // 枚举类型的表格单元格
  157. case 'map':
  158. if (col.typeMap) { // 状态枚举
  159. return (<ElTableColumn
  160. prop={col.prop}
  161. label={col.label}
  162. className={col.className}
  163. width={colWidth}
  164. min-width={minWidth}
  165. show-overflow-tooltip={showOverflowTooltip}
  166. fixed={fixed}
  167. formatter={(row): any => {
  168. return renderTableMapCell(row, col);
  169. }}
  170. >
  171. {{ ...tableCellSlot }}
  172. </ElTableColumn>);
  173. } else { // 普通枚举
  174. return (<ElTableColumn
  175. prop={col.prop}
  176. label={col.label}
  177. className={col.className}
  178. width={colWidth}
  179. min-width={minWidth}
  180. show-overflow-tooltip={showOverflowTooltip}
  181. fixed={fixed}
  182. formatter={(row) => {
  183. return col.map[row[col.prop]];
  184. }}
  185. >
  186. {{ ...tableCellSlot }}
  187. </ElTableColumn>);
  188. }
  189. default:
  190. return (
  191. <ElTableColumn
  192. prop={col.prop}
  193. label={col.label}
  194. className={col.className}
  195. width={colWidth}
  196. min-width={minWidth}
  197. show-overflow-tooltip={showOverflowTooltip}
  198. fixed={fixed}
  199. >
  200. {{ ...tableCellSlot }}
  201. </ElTableColumn>
  202. );
  203. }
  204. })
  205. }
  206. </ElTable>
  207. }
  208. return () => initTable()
  209. }
  210. })

以上就是常规功能表格的二次封装,下篇文章我会在这个基础上封装一个常规后台页面级别的组件,

欢迎大家私信留言,多多点评

可以扫描下方二维码关注我的公众号或添加我的微信联系、沟通。

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号