vue3和el-table实现表格内新增、保存、取消、修改、删除——不用弹窗(一)
1.各个操作逻辑流程
如图1所示:初始时,表格为空表格或者含有数据的表格。
2.各个操作的方法逻辑
如图2所示:初始时数据的含义如下:
1. beforeEditRow表示点击修改后,将修改前的数据暂存,用于后续取消时恢复原数据;
2. addRow表示点击新增后,新的一行空数据;
3. editingIndex表示当前正在编辑的行,-1表示没有数据行被编辑;
4. data表示表格数据,可以为空,或者含有一定行;
3.vue3代码实现结果
代码实现页面如下,拥有数据校验功能。
4. 源代码
整体代码采用vue3的script setup风格➕element-plus➕tailwindcss,采用element-plus的playground,便于分享和查看。代码实现页面链接。欢迎讨论分享,指出不足之处,共同进步!
<template>
<el-form :model="formData" ref="formRef" label-width="auto" :rules="rules">
<el-form-item label="姓名" prop="name">
<el-input v-model="formData.name" placeholder="请输入姓名" style="width: 800px" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-select
v-model="formData.sex"
placeholder="请选择性别"
filterable
size="large"
style="width: 800px"
clearable
>
<el-option
v-for="item in sexOptions"
:key="item.value"
:label="item.label"
:value="item.value"
>
{{ item.label }}
</el-option>
</el-select>
</el-form-item>
<el-form-item label="年龄" prop="age">
<el-input-number
v-model="formData.age"
placeholder="请输入年龄"
style="width: 800px"
></el-input-number>
</el-form-item>
<el-form-item label="地址" prop="address">
<el-input
v-model="formData.address"
placeholder="请输入地址"
style="width: 800px"
></el-input>
</el-form-item>
</el-form>
<div class="mb-3">
<div class="font-bold text-16px">新增家庭信息</div>
</div>
<!-- 添加家庭信息 -->
<el-form ref="familyInfoRef">
<el-table :data="familyInfo" border>
<template #empty>
<div class="flex flex-row justify-center items-center space-x-2">
<span>点击 + 按钮新增家庭成员</span>
</div>
</template>
<el-table-column prop="relation" label="关系" align="center">
<template #default="{ row, $index }">
<el-input
v-if="$index === editingIndex"
v-model="row.relation"
placeholder="请输入关系"
size="small"
>
</el-input>
<span v-else>{{ row.relation }}</span>
</template>
</el-table-column>
<el-table-column prop="name" label="姓名" min-width="60px" align="center">
<template #default="{ row, $index }">
<el-input
v-if="$index === editingIndex"
v-model="row.name"
placeholder="请输入姓名"
size="small"
>
</el-input>
<span v-else>{{ row.name }}</span>
</template>
</el-table-column>
<el-table-column prop="sex" label="性别" min-width="100px" align="center">
<template #default="{ row, $index }">
<el-select
v-if="$index === editingIndex"
v-model="row.sex"
filterable
clearable
placeholder="请选择性别"
size="small"
style="width: 100%"
>
<el-option
v-for="item in sexOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<span v-else>{{ getSexLabel(row.sex) }}</span>
</template>
</el-table-column>
<el-table-column prop="age" label="年龄" min-width="80px" align="center">
<template #default="{ row, $index }">
<el-input
v-if="$index === editingIndex"
v-model="row.age"
placeholder="请输入年龄"
size="small"
>
</el-input>
<span v-else>{{ row.age }}</span>
</template>
</el-table-column>
<el-table-column prop="address" label="地址" min-width="100px" align="center">
<template #default="{ row, $index }">
<el-input
v-if="$index === editingIndex"
v-model="row.address"
placeholder="请输入地址"
size="small"
>
</el-input>
<span v-else>{{ row.address }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="{ row, $index }">
<div class="flex flex-row justify-center items-center">
<template v-if="$index === editingIndex">
<el-button
type="success"
size="small"
plain
@click="handleSave(row, $index)"
>保存</el-button
>
<el-button
type="info"
size="small"
plain
@click="handleCancel(row, $index)"
>取消</el-button
>
</template>
<template v-else>
<el-button
type="primary"
size="small"
plain
@click="handleEdit(row, $index)"
>
修改
</el-button>
<el-popconfirm
title="是否确认删除?"
@confirm="handleDelete(row, $index)"
style="margin-left: 10px"
>
<template #reference>
<el-button type="danger" size="small" plain>删除</el-button>
</template>
</el-popconfirm>
</template>
</div>
</template>
</el-table-column>
</el-table>
</el-form>
<!-- 添加新行 -->
<div class="flex justify-center mt-[4px]">
<el-icon @click="handleAdd()" class="icon" size="24" color="#fb7a14"
><CirclePlusFilled />
</el-icon>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { ElMessage, ElMessageBox, type FormInstance } from 'element-plus'
import { CirclePlusFilled } from '@element-plus/icons-vue'
/* --------------------------------- 用户数据类型 --------------------------------- */
interface User {
name: string
age: number
sex: string
address: string
email: string
}
interface FamilyInfo {
relation: string
name: string
age: number
sex: string
address: string
}
const sexOptions = [
{ value: 1, label: '男' },
{ value: 2, label: '女' }
]
/* ---------------------------------- 表格数据 ---------------------------------- */
const formData = reactive<User>({
name: '',
age: 0,
sex: '',
address: '',
email: ''
})
const formRef = ref<FormInstance>()
const familyInfo = ref<FamilyInfo[]>([])
const familyInfoRef = ref<FormInstance>()
const rules = {
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
age: [{ required: true, message: '请选择出生日期', trigger: 'change' }],
sex: [{ required: true, message: '请选择性别', trigger: 'change' }]
}
/* ------------------------------ 当前编辑行下标 ------------------------------ */
const editingIndex = ref<number>(-1)
/* ------------------------------- 编辑行修改前的数据 ------------------------------- */
const beforeEditRow = ref<FamilyInfo>({
relation: '',
name: '',
age: 0,
sex: '',
address: ''
})
/* --------------------------------- 新增行的空数据 -------------------------------- */
const addContractRow: FamilyInfo = {
relation: '',
name: '',
age: 0,
sex: '',
address: ''
}
/* ---------------------------------- 重置编辑行 --------------------------------- */
const resetTable = () => {
editingIndex.value = -1
beforeEditRow.value = { ...addContractRow }
}
/* ---------------------------------- 表格操作 ---------------------------------- */
const handleAdd = () => {
if (editingIndex.value > -1) {
ElMessage.warning('请先完成修改中的行')
return
}
editingIndex.value = familyInfo.value.length
familyInfo.value.push({ ...addContractRow })
}
const handleEdit = (row: FamilyInfo, index: number) => {
if (editingIndex.value > -1) {
ElMessage.warning('请先完成修改中的行')
return
}
beforeEditRow.value = { ...row }
editingIndex.value = index
}
const handleSave = (row: FamilyInfo, index: number) => {
if (!row.relation || !row.name || !row.age || !row.sex) {
ElMessage.warning('请填写所有必填字段再保存')
return
}
familyInfo.value[index] = { ...row }
resetTable()
}
const handleDelete = (row: FamilyInfo, index: number) => {
familyInfo.value.splice(index, 1)
}
const handleCancel = (row: FamilyInfo, index: number) => {
if (Object.values(beforeEditRow.value).some((value) => value !== '')) {
familyInfo.value[index] = { ...beforeEditRow.value }
} else {
familyInfo.value.splice(index, 1)
}
resetTable()
}
const getSexLabel = (value: number) => {
return sexOptions.find((option) => option.value === value)?.label || ''
}
</script>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)