likes
comments
collection
share

element plus table封装

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

前言

在table数据请求的过程 过于频繁,从而导致页面代码多,封装的目的就是减少相同代码,提高在开发中的效率

封装的目的及好处

  • 隐藏实现细节,逻辑统一处理,只需传递参数内容
  • 低耦合,利于全局扩展,继承所有elemenet plus table的属性及方法
  • 只需配置一个参数及可控制列的显示和隐藏

源码地址

源码

  • table-props.ts
import { ExtractPropTypes, PropType } from 'vue'

export const tableProps = {
    /** 是否显示分页 */
    pagination: {
        type: Boolean,
        default: true
    },
    /** 请求地址 */
    url: {
        type: String,
        default: ''
    },
    /** 默认数据源 */
    data: {
        type: Array,
        default: () => []
    },
    /** 请求参数 */
    params: {
        type: Object,
        default: () => ({})
    },
    /** 是否立即请求 */
    init: {
        type: Boolean,
        default: true
    },
    /** 是否重新加载 */
    reload: {
        type: Boolean,
        default: false
    },
    /** 默认显示列 */
    defaultColumns: {
        type: Array as PropType<string[]>,
        default: () => []
    }
}
export type TableProps = ExtractPropTypes<typeof tableProps>

  • table.tsx
import { SetupContext } from 'vue'
import { ElTable, ElPagination, vLoading } from 'element-plus'
import { TableProps, tableProps } from './table-props'

import './table.scss'
import { useDataSource } from './composable/userDataSource'
import tableColumn from './TableColumn'
import initMethods from './tableMethods'

export default defineComponent({
    directives: { loading: vLoading },
    props: tableProps,
    emits: ['load', 'update:reload'],
    setup(props: TableProps, context: SetupContext) {
        const { slots, attrs, expose } = context

        const tableHeader = slots['table-header']
        const tableFooter = slots['table-footer']

        const { paginationState, table, onSortChange } = useDataSource(props, context)
        const { renderVNode } = tableColumn(props, context)

        // 初始化 el-table 方法
        const refTable = ref()
        const methodObj = initMethods(refTable)
        expose(methodObj)

        return () => {
            // 分页器
            const paginationEl = (
                <ElPagination
                    v-model:current-page={paginationState.currentPage}
                    v-model:page-size={paginationState.pageSize}
                    page-sizes={[10, 20, 30, 50]}
                    total={paginationState.total}
                    layout="total, sizes, prev, pager, next, jumper"
                    class="mt-10px justify-end"
                />
            )

            return (
                <div class="c-table h-full flex-1 flex flex-col">
                    {/* 表格头区域 */}
                    <div class="c-table-header mb-10px">{tableHeader && tableHeader()}</div>
                    {/* 表格区域 */}
                    <ElTable
                        ref={refTable}
                        v-loading={table.loading}
                        data={table.dataSource}
                        {...attrs}
                        onSort-change={onSortChange}>
                        {renderVNode.value}
                    </ElTable>

                    {/* 表格尾区域 */}
                    <div class="c-table-footer">
                        {tableFooter && tableFooter()}
                        {props.pagination && paginationEl}
                    </div>
                </div>
            )
        }
    }
})

  • tableColumns.tsx
import { SetupContext, VNode } from 'vue'
import { ElPopover, ElCheckbox, ElCheckboxGroup, ElButton } from 'element-plus'

import { Icon } from '@/components'
import { TableProps } from './table-props'

// 默认固定列
const fixedColumn = ['selection', 'index', 'operation']
export default function useColumn(props: TableProps, context: SetupContext) {
    // 默认列
    const defaultVNode = ref<VNode[]>([])
    // 需要渲染的列
    const renderVNode = ref<VNode[]>([])
    // 复选框选中的列名
    const checkboxGroupValue = ref<string[]>([])

    const getProps = (node: VNode) => node.props as any

    /** 数据列 */
    const dataColumn = computed(() =>
        defaultVNode.value.filter((node) => !fixedColumn.includes(getProps(node).type))
    )

    /** 表格列 用作 作为 多选框数据源 */
    const checkboxData = computed(() => {
        return dataColumn.value.map((item) => {
            const props = getProps(item)
            return {
                field: props.prop,
                label: props.label
            }
        })
    })

    /** 设置渲染列 */
    const setColumn = () => {
        const result = defaultVNode.value.filter((node) => {
            const props = getProps(node)
            if (fixedColumn.includes(props.type)) return node
            return checkboxGroupValue.value.includes(getProps(node).prop)
        })
        renderVNode.value = result
    }

    /** 重置渲染列 */
    const resetColumn = () => {
        // 如果设置默认显示列
        if (props.defaultColumns.length > 0) {
            checkboxGroupValue.value = props.defaultColumns
        } else {
            checkboxGroupValue.value = dataColumn.value.map((node) => getProps(node).prop)
        }
        setColumn()
    }

    const init = () => {
        if (context.slots.default) {
            defaultVNode.value = context.slots.default()
            resetColumn()
        }
    }

    init()

    // 获取操作列
    const operation = defaultVNode.value.filter((node) => getProps(node).type === 'operation')
    if (operation.length > 0) {
        // 获取操作列 所有插槽
        const operationSorts = operation[0].children as any

        // 通过操作 header插槽 自定义内容
        operationSorts.header = () => {
            const popoverSlots = {
                reference: () => <Icon name="ic:round-more-vert" class="ml-5px cursor-pointer" />
            }
            const popoverContent = () => {
                return (
                    <>
                        <ElCheckboxGroup v-model={checkboxGroupValue.value}>
                            {checkboxData.value.map((item) => {
                                return <ElCheckbox label={item.field}>{item.label}</ElCheckbox>
                            })}
                        </ElCheckboxGroup>
                        <div class="mt-5px">
                            <ElButton type="primary" size="small" onClick={setColumn}>
                                确定
                            </ElButton>
                            <ElButton class="ml-5px" size="small" onClick={resetColumn}>
                                重置
                            </ElButton>
                        </div>
                    </>
                )
            }

            return (
                <div class="flex flex-y-center">
                    {(operation[0].props as any).label}
                    <ElPopover v-slots={popoverSlots}>{popoverContent()}</ElPopover>
                </div>
            )
        }
    } else {
        renderVNode.value = defaultVNode.value
    }

    return {
        renderVNode
    }
}

  • tableMethods.ts
import { Ref } from 'vue'
export const methods = [
    'clearSelection',
    'getSelectionRows',
    'toggleRowSelection',
    'toggleAllSelection',
    'toggleRowExpansion',
    'setCurrentRow',
    'clearSort',
    'clearFilter',
    'doLayout',
    'sort',
    'scrollTo',
    'setScrollTop',
    'setScrollLeft'
]
const initMethods = (refTable: Ref) => {
    const methodObj: Record<string, (...args: any) => void> = {}
    methods.forEach((method) => {
        methodObj[method] = (...args: any) => {
            if (refTable.value && refTable.value[method]) {
                refTable.value[method](...args)
            }
        }
    })

    return methodObj
}

export default initMethods

文档(继承eltable所有属性)

参数说明类型可选值默认值
pagination是否分页booleantrue/falsetrue
url后台请求地址string--
data静态数据源array--
params后台请求参数object--
init是否已经发送请求booleantrue/falsetrue
v-model:reload是否重新加载数据booleantrue/falsetrue
v-model:firstPage是否回到第一页booleantrue/falsefalse
defaultColumns(用于控制列的显示)默认显示列array--

事件(继承eltable所有事件)

事件名说明事件参数
load数据加载完成事件(data:any)=>void

插槽

插槽名说名子标签
table-headertable 顶部插槽-
table-footertable 底部插槽-

基本用法

<template>
    <PageContainer>
        <c-table :data="tableData" :pagination="false" style="width: 100%">
            <template #table-header>
                <el-button type="primary">
                    <icon name="ep:plus">新增</icon>
                </el-button>
            </template>
            <el-table-column prop="date" label="Date" width="180" />
            <el-table-column prop="name" label="Name" width="180" />
            <el-table-column prop="address" label="Address" />
        </c-table>
    </PageContainer>
</template>
<script lang="ts" setup>
import { PageContainer, CTable, Icon } from '@/components'
const tableData = [
    {
        date: '2016-05-03',
        name: 'Tom',
        address: 'No. 189, Grove St, Los Angeles'
    },
    {
        date: '2016-05-02',
        name: 'Tom',
        address: 'No. 189, Grove St, Los Angeles'
    },
    {
        date: '2016-05-04',
        name: 'Tom',
        address: 'No. 189, Grove St, Los Angeles'
    },
    {
        date: '2016-05-01',
        name: 'Tom',
        address: 'No. 189, Grove St, Los Angeles'
    }
]
</script>

element plus table封装

进阶用法

<template>
    <PageContainer>
        <template #header>
            <el-form inline @submit.prevent>
                <el-form-item label="姓名:">
                    <el-input v-model="queryParams.name" class="w-250px" @change="onQuery" />
                </el-form-item>
                <el-form-item label="性别:">
                    <el-select v-model="queryParams.sex" class="w-250px">
                        <el-option label="男" value="男" />
                        <el-option label="女" value="女" />
                    </el-select>
                </el-form-item>
                <el-form-item label="是否已婚:">
                    <el-select v-model="queryParams.married" class="w-250px">
                        <el-option label="是" value="是" />
                        <el-option label="否" value="否" />
                    </el-select>
                </el-form-item>
                <el-form-item label="生日:">
                    <el-date-picker v-model="queryParams.birth" type="date" />
                </el-form-item>
                <el-form-item label="地址:">
                    <el-input v-model="queryParams.addr" class="w-250px" @change="onQuery" />
                </el-form-item>
                <el-form-item label="邮箱:">
                    <el-input v-model="queryParams.email" class="w-250px" @change="onQuery" />
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" @click.stop="onQuery">
                        <icon name="ep:search"> 查询 </icon>
                    </el-button>
                    <el-button>
                        <icon name="ep:brush">重置</icon>
                    </el-button>
                    <el-button @click="onClick">全选/反选</el-button>
                </el-form-item>
            </el-form>
        </template>
        <c-table ref="refTable" v-model:reload="reload" url="/user" :params="queryParams">
            <template #table-header>
                <el-button type="primary">
                    <icon name="ep:plus">新增用户</icon>
                </el-button>
            </template>
            <el-table-column type="selection" width="55" />
            <el-table-column type="index" width="50" />
            <el-table-column label="id" prop="id" sortable show-overflow-tooltip></el-table-column>
            <el-table-column label="姓名" prop="name" sortable></el-table-column>
            <el-table-column label="年龄" prop="age"></el-table-column>
            <el-table-column label="财产" prop="asset"></el-table-column>
            <el-table-column label="是否已婚" prop="married"></el-table-column>
            <el-table-column label="生日" prop="birth" show-overflow-tooltip></el-table-column>
            <el-table-column label="地址" prop="addr" show-overflow-tooltip></el-table-column>
            <el-table-column label="邮箱" prop="email" show-overflow-tooltip></el-table-column>
            <el-table-column label="操作" type="operation">
                <el-button link type="primary" size="small">修改</el-button>
                <el-button link type="danger" size="small">删除</el-button>
            </el-table-column>
        </c-table>
    </PageContainer>
</template>
<script lang="ts" setup>
import { CTable, PageContainer, Icon } from '@/components'
defineOptions({ name: 'Workbench' })
const queryParams = reactive({
    name: undefined,
    birth: undefined,
    sex: undefined,
    married: undefined,
    addr: '',
    email: ''
})
const reload = ref(false)
const onQuery = () => {
    reload.value = true
}

const refTable = ref()
const onClick = () => {
    refTable.value.toggleAllSelection()
}
</script>

element plus table封装

转载自:https://juejin.cn/post/7202417229148651581
评论
请登录