首页 前端知识 超详细~vue3 ts封装element plus表格组件教程(附源码)

超详细~vue3 ts封装element plus表格组件教程(附源码)

2025-02-27 11:02:11 前端知识 前端哥 130 618 我要收藏

封装这个表格

复制一份基础表格稍加修改

从element plus官网复制一份基础表格,稍加修改,将表格数据改为由父组件传递给子组。子组件通过v-for动态渲染表格每一列

//子组件
<template>
	<div class="container">
		<el-table ref="TableData" :data="props.tableData" style="width: 100%">
			<el-table-column
				v-for="column in columns"
				:key="column.prop"
				:prop="column.prop"
				:label="column.label"
				:sortable="column.sortable ? 'custom' : false"
				:width="column.width"
			>
			</el-table-column>
		</el-table>
	</div>
</template>

<script lang="ts" setup>
	import { defineProps, ref } from "vue";
	interface TableRow {
		Date: string;
		Name: string;
		Organization: string;
        _edit?: boolean;
        Tag:string
	}
	const props = defineProps<{
		tableData: Array<TableRow>;
		columns: {
			prop: string;
			label: string;
			sortable?: boolean;
			width?: string;
			style?: string;
		}[];
	}>();
	const TableData = ref<TableRow[]>();
</script>
//父组件
<template>
    //父组件将表格数据传入子组件
	<TestTable :tableData="tableData" :columns="columns"></TestTable>
</template>

<script setup lang="ts">	
	import TestTable from "./TestTable.vue";
	const tableData = [
		{
			Date: "2024/12/1",
			Name: "Dec",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "公司"
		},
		{
			Date: "2024/12/1",
			Name: "Tom",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "合作伙伴"
		},
		{
			Date: "2024/12/1",
			Name: "Tom",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "甲方"
		},
		{
			Date: "2024/12/4",
			Name: "Dec",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "公司"
		},
		{
			Date: "2024/12/4",
			Name: "Tom",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "合作伙伴"
		},
		{
			Date: "2024/12/4",
			Name: "Tom",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "甲方"
		},
		{
			Date: "2024/12/4",
			Name: "Dec",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "甲方"
		},
		{
			Date: "2024/12/8",
			Name: "Tom",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "公司"
		},
		{
			Date: "2024/12/11",
			Name: "Tom",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "合作伙伴"
		},
		{
			Date: "2024/12/15",
			Name: "Dec",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "公司"
		},
		{
			Date: "2024/12/8",
			Name: "Tom",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "合作伙伴"
		}
    ];
	const columns = [
		{ prop: "Date", label: "Date", width: "180" },
		{ prop: "Name", label: "Name", width: "180" },
		{ prop: "Organization", label: "Organization", width: "280" },
		{ prop: "Tag", label: "Tag", width: "180" }
	];	
</script>
<style lang="scss" scoped></style>

添加序号列和展开行

在子组件中为表格添加序号列和展开行,同时在onmounted中对接收到的表格数据进行处理,为表格数据设置_edit默认值,添加counter属性

//子组件		
<el-table ref="TableData" :data="props.tableData" style="width: 100%">
    <!-- 添加以下内容(start) -->
    //表格可选框
    <el-table-column type="selection" :selectable="selectable" width="55" />
    //表格展开行
	<el-table-column type="expand">
        <template #default="{ row }">
            <div m="4" class="table_item_container">
                <p m="t-0 b-2" class="table_item">日期:{{ row.Date }}</p>
                <p m="t-0 b-2" class="table_item">姓名:{{ row.Name }}</p>
                <p m="t-0 b-2" class="table_item">单位:{{ row.Organization }}</p>
				<p m="t-0 b-2" class="table_item">标签:{{ row.Tag }}</p>
			</div>
		</template>
	</el-table-column>
    //表格序号(自增)
	<el-table-column type="index" label="序号" width="55" />
	<!-- 添加以上内容(end) -->
		<el-table-column
			v-for="column in columns"
			:key="column.prop"
			:prop="column.prop"
			:label="column.label"
			:sortable="column.sortable ? 'custom' : false"
			:width="column.width"
		>
       //...其余内容
<script lang="ts" setup>
	//...其余内容
    onMounted(() => {
		let counter = 1;
        // counter用于标识表格每一行数据的唯一性,
        //这样在勾选某一行表格时候不会因为某几行数据相同而误选到其他行  
		TableData.value = props.tableData.map((row) => ({
			...row,
			_edit: false, // 设置默认非编辑状态
			counter: counter++
		}));
	});
	const selectable = () => true;
</script>

通过插槽添加编辑按钮

//子组件
<el-table-column
	v-for="column in columns"
	:key="column.prop"
	:prop="column.prop"
	:label="column.label"
	:sortable="column.sortable ? 'custom' : false"
	:width="column.width"
>   
</el-table-column>
//添加操作和编辑按钮
<el-table-column label="操作">
	<template #default="scope">
	    <div v-if="scope.row._edit">
			<el-button type="primary" @click="handleSave(scope)"> 保存 </el-button>
			<el-button @click="handleCancle(scope)"> 取消 </el-button>
		</div>
			<el-button v-else type="primary" @click="handleEdit(scope)"> 编辑 </el-button>
	</template>
</el-table-column>

<script lang="ts" setup>
	const handleEdit = (scope: { row: TableRow }) => {
		scope.row._edit = true;
	};
	const handleSave = (scope: { row: TableRow }) => {
		scope.row._edit = false;
	};
	const handleCancle = (scope: { row: TableRow }) => {
		scope.row._edit = false;
	};
</script>

设置在编辑状态下可以修改表格内容,同时为表格添加自定义表头

//子组件
<el-table-column
	v-for="column in columns"
	:key="column.prop"
	:prop="column.prop"
	:label="column.label"
	:sortable="column.sortable ? 'custom' : false"
	:width="column.width"
>
    //自定义表头
    <template #header>
		<slot :name="column.prop">{{ column.label }}</slot>
	</template>
	<template #default="{ row }">
        //在编辑状态下可输入的表格单元格输入形式
		<template v-if="row._edit">
			<el-input
				v-if="column.prop === 'Name' || column.prop === 'Organization'"
				v-model="row[column.prop]"
			/>
			<el-select v-if="column.prop === 'Tag'" v-model="Tagvalue">
				<el-option
					v-for="(type, label) in tagTypes"
					:key="label"
					:label="label"
					:value="label"
				/>
			</el-select>
				</template>
		<template v-else>
			<span v-if="column.prop !== 'Tag'">{{ row[column.prop] }}</span>
			<el-tag v-if="column.prop === 'Tag'" :type="getTagType(row[column.prop])">
                {{row[column.prop]}}
            </el-tag>
		</template>
	</template>
</el-table-column>
<script lang="ts" setup>
	const Tagvalue = ref("");
	const tagTypes = {
		公司: "primary",
		甲方: "success",
		合作伙伴: "primary"
	};
	const getTagType = (tag: string) => {
		return tagTypes[tag] || tagTypes["公司"];
	};
	const handleEdit = (scope: { row: TableRow }) => {
		Tagvalue.value = scope.row.Tag || "";
		scope.row._edit = true;
	};
	const handleSave = (scope: { row: TableRow }) => {
		scope.row.Tag = Tagvalue.value;
		scope.row._edit = false;
	};
	const handleCancle = (scope: { row: TableRow }) => {
		scope.row._edit = false;
	};
</script>

添加了自定义表头之后父组件可以自定义表头显示内容

//父组件
<TestTable :tableData="tableData" :columns="columns">
	<template #Date>
		<span>日期</span>
	</template>
	<template #Name>
		<span>姓名</span>
	</template>
	<template #Organization>
		<span>单位</span>
	</template>
	<template #Tag>
		<span>标签</span>
	</template>
</TestTable>

添加表格分页器

<div class="container">
    <el-table>
    //...
    </el-table>
</div>
<div class="page_box">
	<el-pagination layout="prev, pager, next" :total="1000" />
</div>

给表格添加样式

<style lang="scss" scoped>
	.container {
		width: 100%;
		height: calc(100vh - 120px);
		margin: 5px;
		display: flex;
		flex-direction: column;		
	}
	.table_box {
		flex: 1;
		margin-bottom: 10px;
		overflow-y: auto;
		.table_item_container {
			margin-left: 20px;
			padding: 0;
			.table_item {
				margin: 5px 0;
			}
		}
	}
</style>

源码

//父组件
<template>
	<TestTable :tableData="tableData" :columns="columns">
		<template #Date>
			<span>日期</span>
		</template>
		<template #Name>
			<span>姓名</span>
		</template>
		<template #Organization>
			<span>单位</span>
		</template>
		<template #Tag>
			<span>标签</span>
		</template>
	</TestTable>
</template>

<script setup lang="ts">
	import Select from "@/components/select/selectMain.vue";
	import TestTable from "./TestTable.vue";
	const tableData = [
		{
			Date: "2024/12/1",
			Name: "Dec",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "公司"
		},
		{
			Date: "2024/12/1",
			Name: "Tom",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "合作伙伴"
		},
		{			
			Date: "2024/12/1",
			Name: "Tom",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "甲方"
		},
		{
			Date: "2024/12/4",
			Name: "Dec",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "公司"
		},
		{
			Date: "2024/12/4",
			Name: "Tom",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "合作伙伴"
		},
		{			
			Date: "2024/12/4",
			Name: "Tom",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "甲方"
		},
		{
			Date: "2024/12/4",
			Name: "Dec",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "甲方"
		},
		{
			Date: "2024/12/8",
			Name: "Tom",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "公司"
		},
		{
			Date: "2024/12/11",
			Name: "Tom",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "合作伙伴"
		},
		{		
			Date: "2024/12/15",
			Name: "Dec",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "公司"
		},
		{		
			Date: "2024/12/8",
			Name: "Tom",
			Organization: "No. 189, Grove St, Los Angeles",
			Tag: "合作伙伴"
		}
	];
	const columns = [	
		{ prop: "Date", label: "Date", width: "180" },
		{ prop: "Name", label: "Name", width: "180" },
		{ prop: "Organization", label: "Organization", width: "280" },
		{ prop: "Tag", label: "Tag", width: "180" }
	];

</script>
<style lang="scss" scoped></style>
//子组件
<template>
	<div class="container">
		<div class="table_box">
			<el-table ref="multipleTableRef" :data="props.tableData" style="width: 100%">
				<el-table-column type="selection" :selectable="selectable" width="55" />
				<el-table-column type="expand">
					<template #default="{ row }">
						<div m="4" class="table_item_container">
							<p m="t-0 b-2" class="table_item">日期:{{ row.Date }}</p>
							<p m="t-0 b-2" class="table_item">姓名:{{ row.Name }}</p>
							<p m="t-0 b-2" class="table_item">单位:{{ row.Organization }}</p>
							<p m="t-0 b-2" class="table_item">标签:{{ row.Tag }}</p>
						</div>
					</template>
				</el-table-column>
				<el-table-column type="index" label="序号" width="55" />
				<el-table-column
					v-for="column in columns"
					:key="column.prop"
					:prop="column.prop"
					:label="column.label"
					:sortable="column.sortable ? 'custom' : false"
					:width="column.width"
				>
					<template #header>
						<slot :name="column.prop">{{ column.label }}</slot>
					</template>
					<template #default="{ row }">
						<template v-if="row._edit">
							<el-input
								v-if="
									column.prop === 'Date' || column.prop === 'Name' || column.prop === 'Organization'
								"
								v-model="row[column.prop]"
							/>
							<el-select v-if="column.prop === 'Tag'" v-model="Tagvalue">
								<el-option
									v-for="(type, label) in tagTypes"
									:key="label"
									:label="label"
									:value="label"
								/>
							</el-select>
						</template>
						<template v-else>
							<span v-if="column.prop !== 'Tag'">{{ row[column.prop] }}</span>
							<el-tag v-if="column.prop === 'Tag'" :type="getTagType(row[column.prop])">{{
								row[column.prop]
							}}</el-tag>
						</template>
					</template>
				</el-table-column>
				<el-table-column label="操作">
					<template #default="scope">
						<div v-if="scope.row._edit">
							<el-button type="primary" @click="handleSave(scope)"> 保存 </el-button>
							<el-button @click="handleCancle(scope)"> 取消 </el-button>
						</div>
						<el-button v-else type="primary" @click="handleEdit(scope)"> 编辑 </el-button>
					</template>
				</el-table-column>
			</el-table>
		</div>
		<div class="page_box">
			<el-pagination layout="prev, pager, next" :total="1000" />
		</div>
	</div>
</template>

<script lang="ts" setup>
	import { defineProps, ref, onMounted } from "vue";
	import type { TableInstance } from "element-plus";

	interface TableRow {
		Date: string;
		Name: string;
		Organization: string;
		_edit?: boolean;
		Tag: string;
	}
	const props = defineProps<{
		tableData: Array<TableRow>;
		columns: {
			prop: string;
			label: string;
			sortable?: boolean;
			width?: string;
			style?: string;
		}[];
	}>();
	const multipleTableRef = ref<TableInstance>();
	const TableData = ref<TableRow[]>();
	onMounted(() => {
		let counter = 1;
		TableData.value = props.tableData.map((row) => ({
			...row,
			_edit: false, // 设置默认非编辑状态
			counter: counter++
		}));
	});
	const selectable = () => true;
	const Tagvalue = ref("");
	const tagTypes = {
		公司: "primary",
		甲方: "success",
		合作伙伴: "primary"
	};
	const getTagType = (tag: string) => {
		return tagTypes[tag] || tagTypes["公司"];
	};
	const handleEdit = (scope: { row: TableRow }) => {
		Tagvalue.value = scope.row.Tag || "";
		scope.row._edit = true;
	};
	const handleSave = (scope: { row: TableRow }) => {
		scope.row.Tag = Tagvalue.value;
		scope.row._edit = false;
	};
	const handleCancle = (scope: { row: TableRow }) => {
		scope.row._edit = false;
	};
</script>
<style lang="scss" scoped>
	.container {
		width: 100%;
		height: calc(100vh - 120px);
		margin: 5px;
		display: flex;
		flex-direction: column;
	}
	.table_box {
		flex: 1;
		margin-bottom: 10px;
		overflow-y: auto;
		.table_item_container {
			margin-left: 20px;
			padding: 0;
			.table_item {
				margin: 5px 0;
			}
		}
	}
</style>

总结

因为我也是一边学一边写,写完之后理清大概思路写了这篇文章,代码也是根据写好了的再复制粘贴复现一遍,所以可能会有点bug或者有的功能没有实现= =,大概的流程就是这样子的:父组件传值给子组件,子组件接收值然后通过表格渲染。表格除了通过v-for动态渲染之外,还可以通过插槽自定义表格头和单元格内容。后面可能还会再修改修改再写一篇吧。。。。

转载请注明出处或者链接地址:https://www.qianduange.cn//article/21638.html
标签
评论
会员中心 联系我 留言建议 回顶部
复制成功!