【Vue3+Vite+Ts+element-plus】 使用tsx 动态表格封装

系列文章目录

【Vue3+Vite+Ts+element-plus】
超级详细 最新 vite4+vue3+ts+element-plus+eslint-prettier 项目搭建流程
【Vue3+Vite+Ts+element-plus】使用tsx实现左侧栏菜单无限层级封装
【Ts 系列】
TypeScript 从入门到进阶之基础篇(一) ts类型篇


文章目录

  • 系列文章目录
  • 前言
  • 一、必要插件安装
    • 1.安装
    • 2.配置
  • 二、table 封装实现步骤
    • 1.创建
    • 2.封装
    • 3、使用


前言

在我们日常开发中 会经常用到表格 特别是后台管理系统 几乎大部分页面都涉及到表格,使用好的表格封装能为我们节省很多开发时间,也能让代码清晰易懂,接下来我们将从零使用tsx 二次封装一下element-plus的表格。

一、必要插件安装

我们需要创建一个vue3项目 我用的是vite去创建vue3+ts 项目的 ,这里就不多讲项目的创建了 ,如果要详细的项目搭建流程可参考:超级详细 最新 vite4+vue3+ts+element-plus+eslint-prettier 项目搭建流程
在使用tsx之前 我们要安装一些插件 使我们的项目支持tsx

1.安装

//下面3种安装方式选择一种 推荐pnpm
yarn add @vitejs/plugin-vue-jsx
//or
npm install @vitejs/plugin-vue-jsx -D
//or
pnpm install @vitejs/plugin-vue-jsx -D

2.配置

在 vite.config.ts 文件中挂载

import vueJsx from '@vitejs/plugin-vue-jsx'
export default defineConfig({
 plugins: [ vueJsx()]
})

tsconfig.json 文件中

{
  // include 需要包含tsx
"include": ["src/*", "src/**/*.vue", "src/**/*.tsx", "src/**/*.jsx", "src/**/*.ts", "src/**/*.js"],
 "compilerOptions": {
    // 在.tsx文件里支持JSX
    "jsx": "preserve",
 }
}

二、table 封装实现步骤

1.创建

首先我们在项目的 components 文件夹下 新建 DynamicTable 文件夹, 并在DynamicTable* 文件夹下新建 DynamicTable.vue文件

2.封装

在封装表格之前 我们要了解 在通常的表格中 有哪些情况会出现 例如: 表格可能涉及到分页 、可能涉及到接口的请求、也可能表格中的每一行 可内容自定义 等情况 。首先我们先将表格和分页写出来先 代码如下

<script lang="tsx">
import { ElTable } from 'element-plus'
export default {
  props: {
    //表格数据
    tableData: {
      type: Array,
      default: () => [],
    },
    //表格列数据
    tableColumns: {
      type: Array,
      default: () => [],
    },
    //表格配置项
    configuration: {
      type: Object,
      default: () => ({}),
    },
    //分页下拉选择
    pageSizes: {
      type: Array,
      default: () => [10, 20, 50, 100],
    },
    //分页组件的样式
    paginationLayout: {
      type: String,
      default: () => 'total,sizes,prev, pager, next,jumper',
    },
    //是否为分页按钮添加背景色
    background: {
      type: Boolean,
      default: true,
    },
    //请求函数
    request: {
      type: Function,
      default: () => {
        return Function
      },
    },
    //是否开启单选
    highlightCurrentRow: {
      type: Boolean,
      default: () => {
        return false
      },
    },
    //是否在打开页面时默认加载表格请求 默认为 true
    isLoadRequest: {
      type: Boolean,
      default: () => {
        return true
      },
    },
    //表格勾选的数据
    selectedList: {
      type: Array,
      default: () => {
        return []
      },
    },
    //获取表格勾选的行数据
    getAllSelectedList: {
      type: Array,
      default: () => {
        return []
      },
    },
    //表格行禁用
    tableDisable: {
      type: Function,
      default: () => {
        return Function
      },
    },
    //分页是否只有一页时隐藏
    hideOnSinglePage: {
      type: Boolean,
      default: () => {
        return false
      },
    },
    //是否自定义转换
    isCustomConversion: {
      type: Boolean,
      default: () => {
        return false
      },
    },
    //返回转换好的数据
    customConversion: {
      type: Function,
      default: () => {
        return Function
      },
    },
    //是否自定义分页
    isCustomPage: {
      type: Boolean,
      default: () => {
        return false
      },
    },
    // 接收自定义分页返回的参数
    customPage: {
      type: Function,
      default: () => {
        return Function
      },
    },
  },
  emits: [
    'handleSelectionChange',
    'update:selectedList',
    'update:getAllSelectedList',
    'update:tableData',
    'getResultData',
    'selectable',
  ],
  setup(props, { slots, expose, emit }) {
    const singleTableRef = ref<InstanceType<typeof ElTable>>()

    //表格加载
    const loading = ref(false)

    const clientWidth = ref(document.documentElement.clientWidth)

    onMounted(() => {
      window.onresize = () => {
        return (() => {
          clientWidth.value = document.documentElement.clientWidth
        })()
      }
    })

    const getWidth = (width: string) => {
      return `${clientWidth.value * (parseInt(width) / 1440.0)}px`
    }

    //设置表格 Column/
    const setTableColumn = () => {
      return props.tableColumns.map((item: any) => {
        if (!item.slot) {
          return (
            <el-table-column
              {...item}
              width={
                item.type === 'selection' ? getWidth(item.width) : item.width
              }
              selectable={selectable}
            ></el-table-column>
          )
        } else {
          return (
            <el-table-column
              {...item}
              selectable={selectable}
              width={
                item.slotName === 'operation'
                  ? getWidth(item.width)
                  : item.width
              }
            >
              {{
                default: (scope: any) => slots[item.slotName]?.(scope),
              }}
            </el-table-column>
          )
        }
      })
    }

    /**
     * 分页相关配置
     */
    //表格数据
    const tableData = ref(props.tableData as any[])
    //第几页
    const currentPage = ref(1)
    //每页的数量
    const pageSize = ref(10)
    //总数量
    const total = ref(props.tableData.length)

    const getData = ref({})

    watch(
      props.tableData,
      (nel) => {
        tableData.value = nel
      },
      { deep: true },
    )

    //数据请求
    const refreshTable = async (datas: any = {}, reset = true) => {
      if (reset) {
        currentPage.value = 1
      }
      loading.value = true
      getData.value = datas
      try {
        const { result }: any = await props.request({
          ...datas,
          pageNo: props.isCustomPage ? 1 : currentPage.value,
          pageSize: pageSize.value,
        })

        const { totalPage }: any = result.pageInfo
        //判断要跳转的页是否没有了 是的话回到接口返回的最后一页
        if (!reset && totalPage < currentPage.value && currentPage.value > 1) {
          currentPage.value = totalPage
          refreshTable(datas, reset)
        }

        //表格数据转换赋值
        tableData.value = result
        emit('getResultData', result, tableData.value)
        //总条数赋值
        total.value = props.isCustomPage
          ? props.customPage()
          : result.pageInfo && result.pageInfo.totalSize
          ? Number(result.pageInfo.totalSize)
          : 0
        loading.value = false
      } catch (err) {
        tableData.value = []
        loading.value = false
      }
    }

    //是否默认开启请求
    props.isLoadRequest ? refreshTable(getData.value) : ''

    onMounted(() => {
      getData.value = {}
    })

    //page-size 改变时触发
    const handleSizeChange = (val: number) => {
      console.log(`${val} items per page`)
      refreshTable(getData.value, false)
    }

    //current-page 改变时触发
    const handleCurrentChange = (val: number) => {
      console.log(`current page: ${val}`)
      refreshTable(getData.value, false)
    }

    //勾选事件
    const selectedList = ref<string[]>([])
    const handleSelectionChange = (val: any) => {
      selectedList.value = val.map((item: any) => item.id)
      emit('update:getAllSelectedList', val)
      emit('update:selectedList', selectedList.value)
    }

    const selectable = (row: any) => {
      return props.tableDisable(row)
    }

    //选中项高亮
    const tableRowClassName = ({ row }: { row: any }) => {
      return selectedList.value.indexOf(row.id) ? 'warning-row' : 'success-row'
    }

    /**获取页数 和每页数量 */
    const getPageSizeAndCurrentPage = () => {
      return {
        currentPage: currentPage.value,
        pageSize: pageSize.value,
      }
    }

    /**设置页数 和每页数量 */
    const setCurrentPage = (current: any) => {
      currentPage.value = current
    }

    const tableRowStype = () => {
      return {
        borderRadius: '15px',
        overflow: 'hidden',
      }
    }

    // 使用 expose 暴露组件内部的方法
    expose({
      refreshTable,
      getPageSizeAndCurrentPage,
      setCurrentPage,
    })

    return () => (
      <div class="table-style table">
        <el-table
          max-height="800px"
          ref="singleTableRef"
          class="custom-table"
          row-key="id"
          v-loading={loading.value}
          data={tableData.value}
          row-class-name={tableRowClassName}
          row-style={tableRowStype}
          cell-style={{ textAlign: 'center' }}
          onSelectionChange={handleSelectionChange}
          {...props.configuration}
        >
          {setTableColumn()}
        </el-table>
        <el-pagination
          small
          v-model:current-page={currentPage.value}
          v-model:page-size={pageSize.value}
          total={total.value}
          highlightCurrentRow={props.highlightCurrentRow}
          background={props.background}
          layout={props.paginationLayout}
          onCurrentChange={handleCurrentChange}
          onSizeChange={handleSizeChange}
          popper-class="popperClass"
          hideOnSinglePage={props.hideOnSinglePage}
        ></el-pagination>
      </div>
    )
  },
}
</script>


3、使用

在需要使用的引入使用 并 创建一个ts文件用于存放列的配置即可

    <dynamic-table
      ref="dynamicTableRef"
      class="dynamic-table"
      :request="request.listPagesBrand"
      :tableColumns="tableColumns"
      :isLoadRequest="false"
      :tableDisable="selectable"
    >
      <template #internal="scope">
        {{ scope.row.internal === 1 ? '是' : '否' }}
      </template>
      <template #operation="scope">
        <el-button
          type="primary"
          link
          @click="editbrand(scope.row.id)"
          :disabled="!!Number(scope.row.internal)"
        >
          编辑
        </el-button>
        <el-divider direction="vertical" />
        <delete-button
          :data="[scope.row.id]"
          :request="request.delBatchBrand"
          @requestCallback="() => submitSearch(false)"
          :disabled="!!Number(scope.row.internal)"
          type="primary"
          link
        ></delete-button>
      </template>
    </dynamic-table>
//Table 表头定义
const tableColumns: any = [
  {
    type: 'selection',
    width: '70px',
  },
  {
    prop: 'brandName',
    label: '品牌',
    showOverflowTooltip: true,
  },
  {
    prop: 'englishLogo',
    label: '英文标识',
    showOverflowTooltip: true,
  },
  {
    slot: true,
    slotName: 'internal',
    label: '内置',
    showOverflowTooltip: true,
  },
  {
    slot: true,
    slotName: 'operation',
    width: '200px',
    label: '操作',
  },
]

export default tableColumns


你可能感兴趣的:(vue3+vite项目实战,vue,elementui,vue)