使用过antd表格组件的,应该会被他那种风格吸引,一个表格只需要传入一个column就可以了,我在使用element表格组件的时候就有点不习惯,所以自己封装了一个类似与antd表格组件。
封装这个table表格的思路
这个组件是用vue3.0+ts+jsx语法实现的,因为vue3.0官网上实现了支持JSX语法。
下一步开始写每一种数组类型的事件处理
// 文本类型的处理事件
const textHandler = (item: tableItemBaseType) => {
const cb = (scope: { row: { [x: string]: any } }) => (
<span>{scope.row[item.key]}</span>
return elTableColumnTpl(item, cb);
// 时间类型的处理方法
const dateHandler = (item: tableItemBaseType) => {
const cb = (scope: { row: { [x: string]: any } }) => (
{scope.row[item.key]
? moment(scope.row[item.key]).format(item.format || "YYYY-MM-DD")
: ""}
</span>
return elTableColumnTpl(item, cb);
// switch开关的处理方法
const switchHandler = (item: tableItemBaseType) => {
const cb = (scope: { row: { [x: string]: any } }) => (
<el-switch
v-model={scope.row[item.key]}
{...(item.switchOptions && item.switchOptions)}
return elTableColumnTpl(item, cb);
// 操作按钮的处理方法 尽量不要用any 我这里忘记定义类型了,所以直接使用了any
const operationHandler = (item: any) => {
const cb = (scope: { row: any; $index: any }) => {
return (
<div class="table-button-box">
{item.operationList.map((buttonItem: operationListType) => (
<el-button
type={buttonItem.type || null}
onClick={buttonItem.handler.bind(
null,
scope.row,
scope.$index,
buttonItem?.key
{buttonItem.title}
</el-button>
</div>
return elTableColumnTpl(item, cb);
// 数字类型的处理方法 尽量不要用any 我这里忘记定义类型了,所以直接使用了any
const numberHandler = (item: any) => {
const cb = (scope: { row: any; $index: any }) => {
return (
{scope.row[item.key]
scope.row[item.key] / (item.numberOptions?.divide || 100)
).toFixed(item.numberOptions?.keepPoint || 2)
: "0.00"}
</span>
return elTableColumnTpl(item, cb);
// 表格渲染模板的处理方法 通过回调函数的方式将vue插槽中的数据传入
const elTableColumnTpl = (item: any, cb: Function) => {
return (
<el-table-column
{...item}
prop={item.key}
label={item.title}
width={item.width || null}
key={item.key}
fixed={item.fixed || (item.type === "operation" ? "right" : false)}
default: (scope: { row: any }) => {
if (scope && scope.row) {
return cb(scope);
</el-table-column>
// 模拟antd中render方法,这里我使用的是组件进行的替换,可以通过传入组件的形式进行个性化的处理
const renderHandler = (item: any) => {
return (
<el-table-column
{...item}
prop={item.key}
label={item.title}
width={item.width || null}
key={item.key}
fixed={item.fixed || (item.type === "operation" ? "right" : false)}
default: (scope: { row: any }) => {
if (scope && scope.row) {
return (
<item.component
scope={scope.row}
componentPorps={item.componentPorps}
</el-table-column>
下边是全部的代码,希望大佬们多多提出意见。
<script lang="tsx">
import { defineComponent, ref, nextTick } from "vue";
import moment from "moment";
import { ElTable } from "element-plus";
export default defineComponent({
props: {
tableItemOptions: {
type: Array,
required: true,
tableData: {
type: Array,
required: true,
tableOptions: {
type: Object,
setup(props) {
const tableItemOptions: tableItemBaseType[] =
props.tableItemOptions as unknown as tableItemBaseType[];
// const tableOptions: tableOptionsType =
// props.tableOptions as tableOptionsType;
const textHandler = (item: tableItemBaseType) => {
const cb = (scope: { row: { [x: string]: any } }) => (
<span>{scope.row[item.key]}</span>
return elTableColumnTpl(item, cb);
const dateHandler = (item: tableItemBaseType) => {
const cb = (scope: { row: { [x: string]: any } }) => (
{scope.row[item.key]
? moment(scope.row[item.key]).format(item.format || "YYYY-MM-DD")
: ""}
</span>
return elTableColumnTpl(item, cb);
const switchHandler = (item: tableItemBaseType) => {
const cb = (scope: { row: { [x: string]: any } }) => (
<el-switch
v-model={scope.row[item.key]}
{...(item.switchOptions && item.switchOptions)}
return elTableColumnTpl(item, cb);
const operationHandler = (item: any) => {
const cb = (scope: { row: any; $index: any }) => {
return (
<div class="table-button-box">
{item.operationList.map((buttonItem: operationListType) => (
<el-button
type={buttonItem.type || null}
onClick={buttonItem.handler.bind(
null,
scope.row,
scope.$index,
buttonItem?.key
{buttonItem.title}
</el-button>
</div>
return elTableColumnTpl(item, cb);
const numberHandler = (item: any) => {
const cb = (scope: { row: any; $index: any }) => {
return (
{scope.row[item.key]
scope.row[item.key] / (item.numberOptions?.divide || 100)
).toFixed(item.numberOptions?.keepPoint || 2)
: "0.00"}
</span>
return elTableColumnTpl(item, cb);
const elTableColumnTpl = (item: any, cb: Function) => {
return (
<el-table-column
{...item}
prop={item.key}
label={item.title}
width={item.width || null}
key={item.key}
fixed={item.fixed || (item.type === "operation" ? "right" : false)}
default: (scope: { row: any }) => {
if (scope && scope.row) {
return cb(scope);
</el-table-column>
const renderHandler = (item: any) => {
return (
<el-table-column
{...item}
prop={item.key}
label={item.title}
width={item.width || null}
key={item.key}
fixed={item.fixed || (item.type === "operation" ? "right" : false)}
default: (scope: { row: any }) => {
if (scope && scope.row) {
return (
<item.component
scope={scope.row}
componentPorps={item.componentPorps}
</el-table-column>
// 类型处理事件Map
const mapHandler = new Map([
["render", renderHandler],
["text", textHandler],
["operation", operationHandler],
["date", dateHandler],
["switch", switchHandler],
["number", numberHandler],
// 判断类型调用方法
const typeRenderHandler = (item: tableItemBaseType) => {
let type = item.type as string;
if (mapHandler.has(type)) {
return mapHandler.get(type)?.call(null, item);
} else {
return mapHandler.get("text")?.call(null, item);
const multipleTableRef = ref<InstanceType<typeof ElTable>>();
const toggleSelection = (rows?: any[]) => {
if (rows) {
rows.forEach((row) => {
// TODO: improvement typing when refactor table
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
nextTick(() => {
multipleTableRef.value!.toggleRowSelection(row, true);
} else {
multipleTableRef.value!.clearSelection();
const filterTableData = () => {
console.log(props.tableData, ">>>>>>>>>>>>>");
const arr = props.tableData.filter((item: any) => item.checked);
toggleSelection(arr);
if (props.tableOptions?.ref === "multipleTableRef") {
filterTableData();
console.log(props.tableOptions);
return () => (
<el-table
data={props.tableData}
border={false}
style={{ width: "100%", height: "100%" }}
{...props.tableOptions}
{props.tableOptions?.ref === "multipleTableRef" ? (
<el-table-column type="selection" width="55" />
) : null}
{tableItemOptions.map((item) => typeRenderHandler(item))}
</el-table>
</script>
<style lang="scss" scoped>
.table-button-box {
display: flex;
</style>
下边是ts类型定义文件
// 会导致全局声明文件失效
// import { DefineComponent } from "vue";
declare interface operationListType {
key?: string;
type: string;
handler: (a: any, b: number, c?: string) => void;
title: string;
declare interface tableOptionsType {
height?: number;
"max-height"?: number;
border?: boolean;
stripe?: boolean;
size?: string;
fit?: boolean;
declare interface switchOptionsType {
"active-text"?: string;
"inactive-text"?: string;
"active-color"?: string;
"inactive-color"?: string;
disabled?: boolean;
loading?: boolean;
size?: string;
"inline-prompt"?: boolean;
change?: () => {};
class: string;
declare interface numberOptionsType {
keepPoint: number;
divide: number;
declare interface tableItemBaseType {
key: string;
title: string;
type?: string;
width?: number;
"min-width"?: number;
fixed?: string | boolean;
sortable?: boolean | string;
resizable?: boolean;
format?: string;
componentPorps?: any;
component?: import("vue").DefineComponent<any, any, any>;
switchOptions?: switchOptionsType;
operationList?: operationListType[];
numberOptions?: numberOptionsType;
复制代码