使用element-ui中的el-form和el-table嵌套实现表格内容编辑并提交表格表单数据校验
vue3+element-plus: el-table表格动态添加或删除行,无校验,支持下拉选择
https://blog.csdn.net/m0_58953167/article/details/134895241
点击空白处保存的能力
https://www.yii666.com/blog/214446.html
判断点击区域是否为表格区域
该功能点通过 contains 接口实现。判断条件为 tableDom.contains(target) ;该接口可以判断target是否为tableDom的子节点。
// 首先通过addEventListener 传入点击的dom区域
document.addEventListener(
"click",
e => {
this.judgeClickDom(e);
},
false
);
// this.bindClick是为了取消监听,下文会进行描述
// 判断点击是否为table区域
judgeClickDom(e) {
const { target } = e;
let tableDom = document.getElementsByClassName("table");
// getElementsByClassName获取到的是数组,一定要有下标不然会报错
// 如果我们点击的区域在表格外保存数据
if (!tableDom[0].contains(target)) {
this.saveTableData();
}
},
校验
Vue+element实现el-table行内编辑并校验
https://blog.csdn.net/qq_43145310/article/details/129048397
动态表单的校验和提交
使用element-ui中的el-form和el-table嵌套实现表格内容编辑并提交表格表单数据校验(可以对勾选到的表格内容必填校验+勾选框)
https://blog.csdn.net/weixin_48612307/article/details/132445304
最终的组件布局如下
<el-form ref="formRef" :model="ruleForm" label-width="120px" class="demo-dynamic" :rules="rules">
<el-table :data="ruleForm.tableData" border style="width: 100%;margin: 0 auto;" @cell-click="editRow">
<el-table-column label="序号" align="center" width="100">
<template #default="scope">
<span>{{ scope.$index + 1 }}</span>
</template>
</el-table-column>
<el-table-column label="字段英文名称" align="center" width="200">
<template #default="scope">
<el-form-item :prop="`tableData.${scope.$index}.enField`" :rules="rules.enField">
//<el-form-item :prop="`ruleForm.tableData.${scope.$index}.enField`" :rules="rules.enField">
<el-input v-show="scope.row.edit" v-model="scope.row.enField" size="small"></el-input>
<span v-show="!scope.row.edit" >{{ scope.row.enField }}</span>
</el-form-item>
</template>
</el-table-column>
</el-table>
</el-form>
部分代码如下
<template>
<div style="margin-bottom: 50px;">
<el-form ref="formRef" :model="ruleForm" label-width="120px" class="demo-dynamic" :rules="rules">
<el-card>
<template #header>
<div class="card-header flex justify-between">
<span>表信息</span>
</div>
</template>
<el-form-item label="表中文名" prop="cnname">
<el-input v-model="ruleForm.cnname" />
</el-form-item>
<el-form-item label="表英文名" prop="enname">
<el-input v-model="ruleForm.enname" />
</el-form-item>
</el-card>
<el-card>
<template #header>
<div class="card-header flex justify-between">
<span>字段信息</span>
<el-button @click="addTableData" type="primary">添加字段</el-button>
</div>
</template>
<div class="jiegou">
<el-table :data="ruleForm.tableData" border style="width: 100%;margin: 0 auto;" @cell-click="editRow" :class="editClass?'':'noEditClass'">
<el-table-column label="序号" align="center" width="100">
<template #default="scope">
<span>{{ scope.$index + 1 }}</span>
</template>
</el-table-column>
<el-table-column label="字段英文名称" align="center" width="200">
<template #default="scope">
<el-form-item label-width="0" :prop="`tableData.${scope.$index}.enField`" :rules="rules.enField" >
<el-input v-show="scope.row.edit" v-model="scope.row.enField"></el-input>
<span v-show="!scope.row.edit" class="flex-1">{{ scope.row.enField }}</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="字段中文名称" align="center" width="200">
<template #default="scope">
<el-form-item label-width="0" :prop="`tableData.${scope.$index}.enField`" :rules="rules.enField" >
<el-input v-show="scope.row.edit" v-model="scope.row.enField"></el-input>
<span v-show="!scope.row.edit" class="flex-1">{{ scope.row.enField }}</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="字段类型" align="center" width="200">
<template #default="scope">
<el-form-item label-width="0" :prop="`tableData.${scope.$index}.enField`" :rules="rules.enField" >
<el-select v-show="scope.row.edit" v-model="scope.row.enField" clearable>
<el-option v-for="item in fieldType" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<span v-show="!scope.row.edit" class="flex-1">{{ scope.row.enField }}</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="主键" align="center" width="200">
<template #default="scope">
<el-form-item label-width="0" :prop="`tableData.${scope.$index}.enField`" :rules="rules.enField" >
<el-select v-show="scope.row.edit" v-model="scope.row.enField" clearable>
<el-option v-for="item in boolSelect" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<span v-show="!scope.row.edit" class="flex-1">{{ scope.row.enField }}</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="允许为空" align="center" width="200">
<template #default="scope">
<el-form-item label-width="0" :prop="`tableData.${scope.$index}.enField`" :rules="rules.enField" >
<el-select v-show="scope.row.edit" v-model="scope.row.enField" clearable>
<el-option v-for="item in boolSelect" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<span v-show="!scope.row.edit" class="flex-1">{{ scope.row.enField }}</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="字段注释" align="center" width="200">
<template #default="scope">
<el-form-item label-width="0" :prop="`tableData.${scope.$index}.enField`" :rules="rules.enField" >
<el-input v-show="scope.row.edit" v-model="scope.row.enField"></el-input>
<span v-show="!scope.row.edit" class="flex-1">{{ scope.row.enField }}</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" min-width="200">
<template #default="scope">
<el-button @click="deleteTableData(scope.row)" link icon="Delete" type="primary">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="shuju">
</div>
</el-card>
</el-form>
<div class="footer flex justify-center">
<el-button @click="onSubmit" type="primary">保存</el-button>
<el-button>取消</el-button>
</div>
</div>
</template>
<script setup>
import { reactive, ref, onMounted, onUnmounted } from 'vue';
import { addData, fieldType, boolSelect } from './mock';
const tableData = ref(addData)
const editClass=ref(false)
// 新增一行
const addTableData = () => {
const newRow = {
// index:tableData.value?.length,
type: null,
days: null,
amount: null,
edit: true
}
tableData.value.push(newRow)
}
// 删除
const deleteTableData = (row) => {
console.log('删除', row)
const index = tableData.value.indexOf(row);
if (index !== -1) {
tableData.value.splice(index, 1);
}
}
const ruleForm = reactive({
cnname: '中文名',
enname: '英文名',
tableData: tableData
})
const rules = reactive({
cnname: [
{ required: true, message: '请输入', trigger: 'blur' },
{ min: 3, max: 5, message: '长度是3到5个字符', trigger: 'blur' },
],
enname: [
{ required: true, message: '请输入', trigger: 'blur' },
{ min: 3, max: 5, message: '长度是3到5个字符', trigger: 'blur' },
],
enField: [
{ required: true, message: '请输入', trigger: 'blur' },
{ min: 3, max: 5, message: '长度是3到5个字符', trigger: 'blur' },
],
})
const editRow = (row) => {
editClass.value=true
const index = tableData.value.indexOf(row);
if (index !== -1) {
tableData.value[index].edit = true;
}
}
// const cellBlur=(index)=>{
// // console.log('index',index)
// // tableData.value[index].edit=false
// }
onMounted(() => {
window.addEventListener('click', judgeClickDom)
})
onUnmounted(() => {
// 移除事件监听
window.removeEventListener('click', judgeClickDom)
})
// 判断是否点击了table
const judgeClickDom = (e) => {
const { target } = e;
let tableDom = document.getElementsByClassName("el-table");
// getElementsByClassName获取到的是数组,一定要有下标不然会报错
console.log('tableDom[0].contains(target)', tableDom[0].contains(target))
// 如果我们点击的区域在表格外保存数据
if (!tableDom[0].contains(target)) {
// this.saveTableData();
formRef.value.validate((valid) => {
if (valid) {
editClass.value=false
tableData.value = tableData.value.map(item => {
item.edit = false
return item
})
} else {
console.log('check false!')
return false
}
})
}
}
const formRef = ref(null)
const onSubmit = () => {
// console.log('submit!')
formRef.value.validate((valid) => {
if (valid) {
console.log('submit!')
} else {
console.log('error submit!')
return false
}
})
}
</script>
<style lang="less" scoped>
.box {
position: relative;
}
.box .icon {
position: absolute;
bottom: 10px;
right: 19px;
}
.el-table.noEditClass .el-form-item {
margin-bottom: 0px;
}
.footer {
padding: 10px;
position: fixed;
bottom: 0;
box-sizing: border-box;
width: 100%;
background: #fff;
border-top: 1px solid #e2e2e2;
z-index: 100;
}
</style>