likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 22

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

代码gitee地址

在线预览

系列文章

# 克隆项目 gitee地址
git clone https://gitee.com/3439/Spurs-Admin.git

# 进入项目目录
cd Spurs-Admin

# 安装依赖
npm install

# 本地开发 启动项目
npm run dev

前言

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

<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上面直接写的话也就几行代码

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

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

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

我的想法

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

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

1.table封装的目录结构

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

2. 表格配置项

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

components/SpursTable/tableType.ts

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

//表格配置json
import {TableOption} from "@/components/SpursTable/tableType.ts";
export const tableConfig: 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.ts'// 引入配置 然后通过v-bind="tableConfig"去绑定,不是特自定义的不用管, 有自定义列的通过slotName去在template写插槽比如操作列<template #handler="scope">,就和elementPlus一样

<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="spursTableRef"
          v-bind="tableConfig"
          :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 { tableConfig } from './tableConfig.ts'// 引入配置
import SpursTable from '@/components/SpursTable/index.vue'
import tableApi from '@/api/table'
const queryForm = reactive({
  keyword: ''
})
const getTableList = (params: any) => {
  return tableApi.packTableList(params);
};
const spursTableRef = ref <any> ()
const multipleSelection = ref<any>([])
const handleSelectionChange = (val:[]) => {
  multipleSelection.value = val
  console.log(multipleSelection.value);
}
const handleSearch =  () => {
  // 搜索
  spursTableRef.value.handleSearch();
  console.log(spursTableRef.value.tableData);
  // spursTable.value.tableData[0].nickName = "测试"
}
const refreshTableInfo = () => {
  // 刷新把queryForm置空
  queryForm.keyword="";
  spursTableRef.value.refreshTableInfo();
}


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

5.SpursTable封装

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

<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/SpursTable/tableType.ts";
import {useTable} from '@/hooks/useTable.ts'

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)
defineExpose({
  tableData,
  handleSearch,
  refreshTableInfo
})
</script>

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

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

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

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

使用方法:

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

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

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

defineExpose({
  tableData,
  handleSearch,
  refreshTableInfo
})

使用的时候

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

6.后话

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