antd-vue + vue3 实现a-table动态增减行,通过a-from实现a-table行内输入验证, form嵌套两个table实现检验。 个人博客vue3 nodejs mysql http://36.151.145.67:30000/注意table的dataSource与form的mode必须是同一个数组一、效果图图一校验效果二、主要代码注意1、form 与 table 绑定的是同一个数据 tableSource 并且是一个数据ElementUI 需要 对象包数组2、form用的是 name 绑定 - :name[index, vlan_id]3、form-item 总要需要加上 rules - :rulesrules.blura-form reftableFormRef :modeltableSource :label-col{ style: { width: 10px } } :wrapper-col{ span: 0 } :rulesrules a-table classant-table-striped bordered :dataSourcetableSource :columnstableColumns :paginationfalse :scroll{ x: 1260 } :row-class-name(_record, index) (index % 2 1 ? table-striped : null) template #bodyCell{ column, text, record, index } template v-ifcolumn.dataIndex vlan_id a-form-item classcustom-form-item label :name[index, vlan_id] :rulesrules.blur a-input stylewidth: 100% v-model:valuerecord[column.dataIndex]/a-input /a-form-item /template /template /a-table /a-form1、templatediv classbottom-box div classtitle-box p classorder-title工单操作/p a-button stylemargin: 0 0 10px 0px typeprimary sizesmall clickhandleRowAdd增加行/a-button /div div classtable-box a-form reftableFormRef :modeltableSource :label-col{ style: { width: 10px } } :wrapper-col{ span: 0 } :rulesrules a-table classant-table-striped bordered :dataSourcetableSource :columnstableColumns :paginationfalse :scroll{ x: 1260 } :row-class-name(_record, index) (index % 2 1 ? table-striped : null) template #bodyCell{ column, text, record, index } template v-ifcolumn.dataIndex vlan_id a-form-item classcustom-form-item label :name[index, vlan_id] :rulesrules.blur a-input stylewidth: 100% v-model:valuerecord[column.dataIndex]/a-input /a-form-item /template template v-else-ifcolumn.dataIndex cloud a-form-item classcustom-form-item label :name[index, cloud] :rulesrules.cloud a-select stylewidth: 100% v-model:valuerecord[column.dataIndex] changehanlePlatformChange(index) a-select-option v-foritem in platforms :valueitem.value :keyitem.value{{ item.label }}/a-select-option /a-select /a-form-item /template template v-else-ifcolumn.dataIndex related_pool a-form-item classcustom-form-item label :name[index, related_pool] :rulesrules.relatedPool a-select stylewidth: 100% v-model:valuerecord[column.dataIndex] a-select-option v-foritem in platform[index].children :valueitem.value :keyitem.value{{ item.label }}/a-select-option /a-select /a-form-item /template template v-else-ifcolumn.dataIndex allocated || column.dataIndex purpose || column.dataIndex vlan_domain a-form-item classcustom-form-item label :name[index, column.dataIndex] :rulesrules.change a-select stylewidth: 100% v-model:valuerecord[column.dataIndex] a-select-option v-foritem in column.list :valueitem.value :keyitem.value{{ item.label }}/a-select-option /a-select /a-form-item /template template v-else-ifcolumn.dataIndex operation a-button stylemargin: 0 5px typeprimary sizesmall clickhandleRowDel(index) danger删除/a-button /template template v-else a-input stylewidth: 100% v-model:valuerecord[column.dataIndex]/a-input /template /template /a-table /a-form /div div classbtn-box a-button v-ifsendFail stylemargin: 0 5px clickhandleCancleApply取消申请/a-button a-button stylemargin: 0 5px typeprimary clickhandleSubmit提交/a-button /div /div2、scriptscript setup import { h, reactive, ref } from vue; // 路由跳转 import { useRouter } from vue-router; const { currentRoute } useRouter(); const router useRouter(); const tableFormRef ref(); // form标签 /** * * 表格数据 */ let tableSource ref([]); const tableColumns [ { title: 序号, dataIndex: vlan_id, key: vlan_id, align: center, width: 65 }, { title: 所属云平台, dataIndex: cloud, key: cloud, align: center, width: 65 }, { title: 所属硬件资源池, dataIndex: related_pool, key: related_pool, align: center, width: 65 }, { title: 类型, dataIndex: type, key: type, align: center, width: 65 }, { title: 用途, dataIndex: purpose, key: purpose, align: center, width: 65 }, { title: 规划范围, dataIndex: plan, key: plan, align: center, width: 65 }, { title: 是否已分配, dataIndex: allocated, key: allocated, align: center, width: 65 }, { title: 备注, dataIndex: remark, key: remark, align: center, width: 65 }, ] // 校验规则 const rules { blur: [{ required: true, message: 请输入, trigger: blur }], change: [{ required: true, message: 请选择, trigger: change }], cloud: [{ required: true, message: 请选择所属平台, trigger: change }], relatedPool: [{ required: true, message: 请选择硬件资源池, trigger: change }] }; // 增加行 const handleRowAdd () { tableSource.value.push({ vlan_id: null, cloud: null, related_pool: null, type: null, purpose: null, plan: , allocated: null, remark: }); }, // 删除行 const handleRowDel (index) { tableSource.value.splice(index, 1) } // 提交申请 const handleSubmit () { let params {}; if (tableSource.value.length 0) { return message.error(工单不能为空); } // form的校验方法 tableFormRef.value.validate().then(() { const tableSourceParams JSON.parse(JSON.stringify(tableSource.value)); params { ...formState, // 其他的参数 status: 1, deviceLists: tableSourceParams }; // 接口 submitOrder(params).then(res { if (res.code 8200) { cancelId.value res.data.id; if (res.data.status 3) { message.success(二级VLAN地址入网添加成功); router.push({ path: /network-access/vlan }); } else if (res.data.status 2) { message.error(failTip(res.data.errorMessage)); sendFail.value true; // if (route.query.id) { // echoDate(); // } } } }); }); }; /script二、form嵌套两个table的情况因为form与table绑定的值必须是同一个才能就行验证。所以直接form嵌套form就好了如下图这个一样只需要form里面再嵌套form就行了 不会相互影响的注意两个form绑定的值不是同的 所以才需要另一个form来关联里面的table form与table绑定的值一一对应