什么才是完美的表格二次封装elementPlus表格?-从零开始vue3+vite+ts+pinia+router4后台管理(7)

什么才是完美的表格二次封装elementPlus表格?-从零开始vue3+vite+ts+pinia+router4后台管理(7)

前言

ElementPlus 是一个优秀的组件库,后台管理表格页面多的话大家都想到表格的二次封装,封装的时候大家都想到el-table-column 每一列写成 "JSON 数组" 写法。然后用vue3的tsx 语法、h 函数 Render函数去写一些自定义的东西,如下面的例子

html 复制代码
<template>
  <my-table :columns="columns" :data="tableData">
    <el-table-column
        v-for="(item, index) in columns"
        :key="index">
      </el-table-column>
  </my-table>
</template>
<script setup>
const columns = [
  { prop: 'date', label: 'Date', width: '180'},
  { prop: 'name', label: 'Name'},
  {width: '140', label: '操作',
    render: ({ row, index }) =>
        h('div', null, [
          h(
              ElButton,
              {
                type: 'primary',
                size: 'small',
                onClick: () => handleRenderEdit(row, index)
              },
              { default: () => '编辑' }
          ),
          h(
              ElButton,
              {
                type: 'danger',
                size: 'small',
                onClick: () => handleRenderDelete(row, index)
              },
              { default: () => '删除' }
          )
        ])
  }
]
// ...其他略
</script>

"JSON 数组" 封装的缺点

有上面的代码可以看出 el-table-column虽然少写了,但是json数组的代码并没有少写,上面的自定义列其实在ElementPlus上面直接写的话也就几行代码

html 复制代码
<el-button size="small" @click="handleRenderEdit(scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleRenderDelete(scope.row)">删除</el-button>

我们尤其是复杂的管理页面 自定义的列也会越来越复杂,后面我们的代码也会越来越难维护,那么应该怎么办呢?

什么才是完美的二次表格封装

我的想法

  1. 有一个必须的前提:我们在二次封装elementPlus表格组件的时候不能影响原有组件的事件 ,方法 ,属性
  2. 在同一个项目里,render/jsx/template/tsx /h函数混用让人感到吃力。它的语法写法是否简单易学大部分就和 elementPlus 一模一样 ?
  3. 它是否确确实实减少了你的工作量?
  4. 它是否扩展性强,易维护?

下面是我封装二次封装elementPlus表格的一些做法,当然他肯定不是完美的二次表格封装,只是自己抛砖引玉的一些想法

1.table封装的目录结构

代码gitee地址

bash 复制代码
├── src
│   ├── api                 
│   │   ├── table/index.ts  # 表格列表数据获取接口请求
│   ├── components          
│   │   └── table           # 表格组件
│	│			└── tableType/index.ts # 表格配置和表格每一行的配置
│   │       	└── spursTable/index.vue 	# 表格二次封装的主要业务代码
│   ├── views               # 页面
│   │   ├── table
│   │   	└── tableConfig/index.ts # 表格配置json
│   │   │   └── index.vue # 入口

2. 表格配置项

首先对表格进行一些配置 如 是否显示索引列,是否显示全选列, 是否显示分页,是否有子数据,等等 有其他需要的配置都可以自己去扩展添加

components/table/tableType/index.ts

typescript 复制代码
//表格行el-table-column配置项
export interface ColumnOption {
    prop?: string
    label: string
    minWidth?: string
    slotName?: string
    align?: string
}
//表格配置项
export interface TableOption {
    propList: ColumnOption[]
    showIndexColumn?: boolean
    showSelectColumn?: boolean
    showPagination?: boolean
    childrenProps?: object
}

3. 表格配置json

views/tablet/tableConfig/index.ts 这个是表格每一行配置的数据prop,label,等等 无论你怎么样封装表格 这个每一页的数据都是不同的这个是必不可少的 注意的是我们给自定义列配置了一个slotName, 怎么用 继续看下面

typescript 复制代码
//表格配置json
import {TableOption} from "@/components/table/tableType";
export const contentTableConfig: TableOption = {
    // 表格配置
    propList: [
        { prop: 'nickName', label: '姓名', minWidth: '100', align: 'left' },
        { prop: 'roleName', label: '权限名称', minWidth: '100', align: 'left' },
        { prop: 'userMoney.balanceMoney', label: '用户余额', minWidth: '100', align: 'left' },//获取表格list下一级的数据userMoney.balanceMoney
        { prop: 'title', label: '介绍', minWidth: '100', align: 'left' },
        { prop: 'phone', label: '联系方式', minWidth: '100', align: 'left' },
        { prop: 'address', label: '地址', minWidth: '100', align: 'left' },
        { prop: 'createTime', label: '日期', minWidth: '100', align: 'left' },
        {
            prop: 'state',
            label: '状态',
            minWidth: '100',
            slotName: 'state',
            align: 'left'
        },
        {
            label: '操作',
            minWidth: '120',
            slotName: 'handler',
            align: 'left'
        }
    ],
    // 表格具有序号列
    showIndexColumn: true,
    // 表格具有可选列
    showSelectColumn: true,
    //是否显示分页
    showPagination:true
}

4.使用表格配置json-tableConfig

import {contentTableConfig} from './tableConfig/index'// 引入配置 然后通过v-bind="contentTableConfig"去绑定,不是特自定义的不用管, 有自定义列的通过slotName去在template写插槽比如操作列<template #handler="scope">,就和elementPlus一样

html 复制代码
<template>
  <div class="app-container">
    <div class="filter-container">
      <el-input  class="w-100" v-model="queryForm.keyword" placeholder="关键字搜索" />
      <el-button type="primary" :icon="Search" @click="handleSearch">搜索</el-button>
      <el-button class="green-button" @click="refreshTableInfo"  :icon="Plus">刷新</el-button>
    </div>
    <div class="table-con">
      <spurs-table
          ref="spursTable"
          v-bind="contentTableConfig"
          :queryForm="queryForm"
          :requestApi="getTableList"
          @selection-change="handleSelectionChange"
      >
        <template #state="scope">
          <el-tag type="warning" v-if="scope.row.state === 0">禁用</el-tag>
          <el-tag v-else>启用</el-tag>
        </template>
        <template #handler="scope">
         <el-button
           size="small"
           :icon="Edit"
           link
           @click="handleEditClick(scope.row)"
           >编辑</el-button
           >
         <el-button
           size="small"
           :icon="Delete"
           type="warning"
           link
           @click="deleteBtnClick(scope.row)"
           >删除</el-button>
         </template>
      </spurs-table>
    </div>

  </div>
</template>
<script setup  lang="ts">
import {reactive,ref} from 'vue'
import { Search,Plus,Delete,Edit } from '@element-plus/icons-vue'
import {contentTableConfig} from './tableConfig/index'// 引入配置
import SpursTable from '@/components/table/spursTable/index.vue'
import tableApi from '@/api/table'
const queryForm = reactive({
  keyword: ''
})
const getTableList = (params: any) => {
  return tableApi.packTableList(params);
};
const spursTable = ref <any> ()
const multipleSelection = ref<any>([])
const handleSelectionChange = (val:[]) => {
  multipleSelection.value = val
  console.log(multipleSelection.value);
}
const handleSearch =  () => {
  // 搜索
  spursTable.value.handleSearch();
  // console.log(spursTable.value.tableData);
  // spursTable.value.tableData[0].nickName = "测试"
}
const refreshTableInfo = () => {
  // 刷新把queryForm置空
  queryForm.keyword="";
  spursTable.value.refreshTableInfo();
}


const handleEditClick = (row: any) => {
  console.log('点击了编辑按钮,数据为:', row)
  // getTableData();
}
// 点击删除按钮触发事件
const deleteBtnClick = (row: any) => {
  console.log('点击了删除按钮,数据为:', row)
}
</script>

5.SpursTable封装

直接贴代码 下面是一些说明

html 复制代码
<template>
  <el-table
      ref="tableRef"
      style="width: 100%"
      v-loading="loading"
      :data="tableData"
      border
      v-bind="$attrs"
  >
   <!-- 1.传入showSelectColumn时展示的全选列 -->
   <template v-if="showSelectColumn">
    <el-table-column type="selection" />
   </template>
   <!-- 2.传入showIndexColumn时展示的序号列 -->
   <template v-if="showIndexColumn">
    <el-table-column type="index" label="#" />
   </template>
   <!-- 3.propList里面的所有列 -->
   <template v-for="item in propList" :key="item.prop">
    <el-table-column v-bind="item" show-overflow-tooltip>
     <!-- 传有slotName时展示的插槽列 -->
     <template #default="scope" v-if="item.slotName">
      <slot :name="item.slotName" :row="scope.row"></slot>
     </template>
    </el-table-column>
   </template>
  </el-table>
  <Pagination
      v-if="showPagination"
      v-model:page="pagination.pageNum"
      v-model:size="pagination.pageSize"
      :total="total"
      @pagination="getTableData"
  />
</template>
<script setup lang="ts">
import {withDefaults, defineExpose, ref} from 'vue'
import {ColumnOption} from "@/components/table/tableType";
import {useTable} from '@/hooks/useTable'

interface Props {
  requestApi: Function // 请求表格数据的 api的axios方法 ==> 必传
  queryForm?:any
  propList: ColumnOption[] //表格行el-table-column配置项
  showIndexColumn?: boolean //是否显示索引列
  showSelectColumn?: boolean //是否显示全选列
  showPagination?: boolean //是否显示分页
  childrenProps?: object // 是否有子数据,树形数据才用得到
}
const props = withDefaults(defineProps<Props>(), {
  showIndexColumn: false,
  showSelectColumn: false,
  showPagination: false,
  childrenProps: () => ({})
})
const {
  tableData,
  pagination,
  total,
  loading,
  getTableData,
  handleSearch,//搜索
  refreshTableInfo,//刷新
} = useTable(props.requestApi,props.queryForm)
const tableRef = ref <any> ()
defineExpose({
  elementP: tableRef,
  tableData,
  handleSearch,
  refreshTableInfo
})

// console.log(props);
</script>
<style scoped></style>

hooks/useTable 这里面的hook函数是获取表格数据和分页的 这里不做多的说明可以看

从零开始vue3+vite+ts+pinia+router4后台管理(5)-二次封装表格1.0

使用原生elementPlus表格的所有属性,方法,事件等,可以用属性透传**v-bind="$attrs"**来实现 vue官方文档透传 Attributes,也可以阅读文章vue3中的$attrs的使用来了解和使用它

比如我们要使用@selection-change="handleSelectionChange"elementPlue表格获取勾选数据的方法

使用方法:

使用SpursTable的事件方法和数据怎么办?使用d**efineExpose** 把SpursTable需要的东西暴露出去

html 复制代码
defineExpose({
  tableData,
  handleSearch,
  refreshTableInfo
})

使用的时候

html 复制代码
ref="spursTable"//模板上使用
const spursTable = ref <any> ()
spursTable.value.handleSearch();
spursTable.value.tableData

6.后话

关于二次表格的封装,什么才是完美的二次表格封装 如果你有更好的想法欢迎评论区交流

相关推荐
代码小鑫几秒前
A034-基于Spring Boot的供应商管理系统的设计与实现
java·开发语言·spring boot·后端·spring·毕业设计
熊的猫3 分钟前
DOM 规范 — MutationObserver 接口
前端·javascript·chrome·webpack·前端框架·node.js·ecmascript
天农学子4 分钟前
Easyui ComboBox 数据加载完成之后过滤数据
前端·javascript·easyui
mez_Blog4 分钟前
Vue之插槽(slot)
前端·javascript·vue.js·前端框架·插槽
爱睡D小猪7 分钟前
vue文本高亮处理
前端·javascript·vue.js
paopaokaka_luck8 分钟前
基于Spring Boot+Vue的多媒体素材管理系统的设计与实现
java·数据库·vue.js·spring boot·后端·算法
开心工作室_kaic10 分钟前
ssm102“魅力”繁峙宣传网站的设计与实现+vue(论文+源码)_kaic
前端·javascript·vue.js
放逐者-保持本心,方可放逐11 分钟前
vue3 中那些常用 靠copy 的内置函数
前端·javascript·vue.js·前端框架
IT古董11 分钟前
【前端】vue 如何完全销毁一个组件
前端·javascript·vue.js
Henry_Wu00113 分钟前
从swagger直接转 vue的api
前端·javascript·vue.js