羊奶 發表於 2025-6-23 00:01:00

在elementui,antDesign,antDesignVue中表格嵌套多个表单项校验

<h3 id="前言">前言</h3>
<p>在此记录下使用 <code>elementui</code>,<code>antDesign</code>,<code>antDesignVue</code> 的表格嵌套表单校验的一些经验。</p>
<p>要达到的目的是:有个多行表格,每一行有多个表单项(比如输入框),表单项填完值后,点击提交,校验表格中所有表单项,校验通过则将整个表格数据送到后台。还可以重新给表格赋值(比如进入详情页面)。效果如下:</p>
<p><img src="https://img2024.cnblogs.com/blog/895887/202506/895887-20250622235028385-1040437839.gif" alt="image" loading="lazy"></p>
<h3 id="正文">正文</h3>
<p><code>elementui</code> 和 <code>elementPlus</code> 使用方式一样,这里就只用 <code>elementui</code> 来举例,版本是 <code>2.x</code>。</p>
<p><code>antDesignVue 1.x</code> 版本有两种表单校验方式,<code>v-decorator</code> 和 <code>FormModel</code> ,要达到上面的效果需要用 <code>FormModel</code> 的方式,而后面 <code>2.x</code> 及以后的版本将两种方式合并,统一用 <code>FormModel</code> 的方式。</p>
<p><code>antDesign</code> 我这里用的 <code>3.x</code> 版本,用的是 <code>getFieldDecorator</code> 方式,并且是函数式组件的写法。</p>
<blockquote>
<p>需要注意的是如果使用的是&nbsp;<code>react@&lt;15.3.0</code>,则&nbsp;<code>getFieldDecorator</code>&nbsp;调用不能位于纯函数组件中。</p>
</blockquote>
<h4 id="elementui-用法">elementui 用法</h4>
<p>只需要将 <code>prop</code> 里面的属性和<code>v-model</code>表单数据引用保持一致即可,例如:</p>
<pre><code class="language-js">:prop="'rows.'+scope.$index+'.age'"
v-model.trim="asdfform.rows.age"
</code></pre>
<p>页面结构</p>
<pre><code class="language-html">&lt;el-form :model="asdfform" ref="asdfform" label-width="20px"&gt;
    &lt;el-table :data="asdfform.rows"&gt;
      &lt;el-table-column prop="name" label="name"&gt;&lt;/el-table-column&gt;
      &lt;el-table-column prop="age" label="age"&gt;
            &lt;template slot-scope="scope"&gt;
                &lt;el-form-item label=" " :rules="[{required: true, message:'请输入'}]" :prop="'rows.'+scope.$index+'.age'"&gt;
                  &lt;el-input v-model.trim="asdfform.rows.age"&gt;&lt;/el-input&gt;
                &lt;/el-form-item&gt;
            &lt;/template&gt;
      &lt;/el-table-column&gt;
      &lt;el-table-column prop="address" label="address"&gt;
            &lt;template slot-scope="scope"&gt;
                &lt;el-form-item label=" " :rules="[{required:true,message:'请输入'}]" :prop="'rows.'+ scope.$index + '.address'"&gt;
                  &lt;el-input v-model.trim="asdfform.rows.address"&gt;&lt;/el-input&gt;
                &lt;/el-form-item&gt;
            &lt;/template&gt;
      &lt;/el-table-column&gt;
    &lt;/el-table&gt;
&lt;/el-form&gt;
&lt;el-button type="primary" size="small" @click="handleSubmit"&gt;表单提交&lt;/el-button&gt;
&lt;el-button type="danger" size="small" @click="resetTableData"&gt;重新给表格赋值&lt;/el-button&gt;
</code></pre>
<p>数据和方法定义</p>
<pre><code class="language-js">data() {
    return {
      asdfform: {
      rows: [
          { key: 1, name: `Edrward ${1}`, age: "1234", address: "1234" },
          { key: 2, name: `Edrward ${2}`, age: "1234", address: "1234" },
          { key: 3, name: `Edrward ${3}`, age: "", address: "" },
      ],
      },
    };
},
methods: {
    handleSubmit() {
      this.$refs.asdfform.validate((valid) =&gt; {
      console.log('表格数据',JSON.parse(JSON.stringify(this.asdfform)));
      if (valid) {
      }
      });
    },
    resetTableData() {
      this.asdfform.rows = [
      { key: 1, name: `Edrward ${1}`, age: "1234", address: "1234" },
      { key: 2, name: `Edrward ${2}`, age: "1234", address: "1234" },
      { key: 3, name: `Edrward ${3}`, age: "", address: "" },
      { key: 4, name: `Edrward ${4}`, age: "4", address: "44" },
      ];
    },
},
</code></pre>
<p>完整示例</p>
<h4 id="antdesignvue-1x-用法">antDesignVue 1.x 用法</h4>
<p>如果用的是<code>FormModel</code> 方式,那么写法和 elementui 中一样,不过需要先引入</p>
<pre><code class="language-js">import { FormModel } from 'ant-design-vue'
Vue.use(FormModel)
</code></pre>
<p>页面中使用</p>
<pre><code class="language-html">&lt;a-form-model :model="asdfform" ref="asdfform"&gt;
    &lt;a-table
      :columns="asdfcolumns"
      :dataSource="asdfform.rows"
      bordered
      :rowKey="(record,index)=&gt;index"
    &gt;
      &lt;template slot="age" slot-scope="text,record, index"&gt;
            &lt;a-form-model-item
                label=" "
                :prop="'rows.'+index+'.age'"
                :rules=" [{required: true, message:'请输入'}]"
            &gt;
                &lt;a-input v-model="asdfform.rows.age" /&gt;
            &lt;/a-form-model-item&gt;
      &lt;/template&gt;
      &lt;template slot="address" slot-scope="text,record,index"&gt;
            &lt;a-form-model-item
                label=" "
                :prop="'rows.'+index+'.address'"
                :rules="[{required: true, message:'请输入' }]"
            &gt;
                &lt;a-input v-model="asdfform.rows.address" /&gt;
            &lt;/a-form-model-item&gt;
      &lt;/template&gt;
    &lt;/a-table&gt;
    &lt;a-form-item&gt;
      &lt;a-button type="primary" @click="handleSubmit"&gt;Submit&lt;/a-button&gt;
    &lt;/a-form-item&gt;
&lt;/a-form-model&gt;
</code></pre>
<pre><code class="language-js">data: function () {
    return {
      asdfform: {
            rows: [
                { key: 1, name: `Edrward ${1}`, age: '1234', address: '1234' },
                { key: 2, name: `Edrward ${2}`, age: '1234', address: '1234' },
                { key: 3, name: `Edrward ${3}`, age: '', address: '' },
            ]
      },
      asdfcolumns: [
            { title: 'name', dataIndex: 'name', width: '25%' },
            {
                title: 'age',
                dataIndex: 'age',
                width: '15% ',
                scopedSlots: { customRender: 'age' }
            },
            {
                title: 'address',
                dataIndex: 'address',
                width: '40%',
                scopedSlots: { customRender: 'address' }
            },
            { title: 'operation', dataIndex: 'operation' },
      ],
    }
},
methods: {
    handleSubmit() {
      this.$refs.asdfform.validate(valid =&gt; {
            console.log(this.asdfform)
            if (valid) { }
      });
    }
}
</code></pre>
<h4 id="antdesign-3x-用法">antDesign 3.x 用法</h4>
<p><code>react</code> 中没有 <code>v-model</code> 的用法,这里通过 <code>setState + useEffect</code> 来实现。</p>
<p><code>vue</code> 中使用 <code>asdfform</code> 定义表单数据,<code>asdfform.rows</code> 定义表格数据,也就是表格数据是嵌套在表单数据里面的,所以表单项改变,表格数据也会同步改变。</p>
<p><code>react</code> 中表格数据和表单数据的分开维护的,所以当表格数据改变时,需要在 <code>useEffect</code> 里面同步表单数据,同样表单数据变化后,提交函数中也会创建一份新的表格数据。</p>
<p>1、定义基本结构</p>
<p>这里使用了 <code>Form.create</code>&nbsp;和&nbsp;<code>getFieldDecorator</code> 的方式校验表单。</p>
<p>经&nbsp;<code>Form.create()</code>&nbsp;包装过的组件会自带&nbsp;<code>this.props.form</code>&nbsp;属性。</p>
<blockquote>
<p>使用&nbsp;<code>Form.create</code>&nbsp;处理后的表单具有自动收集数据并校验的功能,但如果您不需要这个功能,或者默认的行为无法满足业务需求,可以选择不使用&nbsp;<code>Form.create</code>&nbsp;并自行处理数据。</p>
</blockquote>
<pre><code class="language-js">const App = ({ form }) =&gt; {

    const { getFieldDecorator, setFieldsValue, validateFields } = form;
   
    // 定义表格数据
    const = useState([
      { key: 1, name: `Edrward ${1}`, age: "1234", address: "1234" },
      { key: 2, name: `Edrward ${2}`, age: "1234", address: "1234" },
      { key: 3, name: `Edrward ${3}`, age: "", address: "" },
    ]);

    return (
      &lt;&gt;
          &lt;Form&gt;
            &lt;Table
            columns={asdfcolumns}
            dataSource={data}
            pagination={false}
            rowKey="key"
            &gt;&lt;/Table&gt;
            &lt;Form.Item&gt;
            &lt;Button type="primary" onClick={handleSubmit}&gt;
                Submit
            &lt;/Button&gt;
            &lt;Button onClick={handleReset}&gt;reset&lt;/Button&gt;
            &lt;/Form.Item&gt;
          &lt;/Form&gt;
      &lt;/&gt;
    );
}

const WrappedMyFormComponent = Form.create()(App);
export default WrappedMyFormComponent;
</code></pre>
<p>2、添加表格列,根据需要自定义列内容</p>
<pre><code class="language-js">const asdfcolumns = [
    { title: "name", dataIndex: "name", width: "25%" },
    {
      title: "age",
      dataIndex: "age",
      width: "15%",
      render: (text, record, index) =&gt; (
      &lt;Form.Item&gt;
          {getFieldDecorator(`data.${index}.age`, {
            rules: [{ required: true, message: "请输入" }],
            initialValue: record["age"],
          })(&lt;Input /&gt;)}
      &lt;/Form.Item&gt;
      ),
    },
    {
      title: "address",
      dataIndex: "address",
      width: "40%",
      render: (text, record, index) =&gt; (
      &lt;Form.Item&gt;
          {getFieldDecorator(`data.${index}.address`, {
            rules: [
            {
                required: true,
                message: "请输入!",
            },
            ],
            initialValue: record["address"],
          })(&lt;Input /&gt;)}
      &lt;/Form.Item&gt;
      ),
    },
    { title: "operation", dataIndex: "operation" },
];
</code></pre>
<p>3、添加提交函数和重置数据函数(给表格重新赋值)</p>
<pre><code class="language-js">const handleSubmit = () =&gt; {
    validateFields((err, values) =&gt; {
      console.log("Received values of form: ", values);
      if (!err) {
      const updatedData = data.map((item, index) =&gt; ({
          ...item,
          age: values.data.age,
          address: values.data.address,
      }));
      console.log(updatedData); //更新后的数据
      }
    });
};
</code></pre>
<pre><code class="language-js">const handleReset = () =&gt; {
    let newData = [
      { key: 1, name: `Edrward ${1}`, age: "1", address: "2" },
      { key: 2, name: `Edrward ${2}`, age: "3", address: "4" },
      { key: 3, name: `Edrward_${3}`, age: "", address: "" },
      { key: 4, name: `Edrward ${3}`, age: "5", address: "6" },
      setData(newData),
    ];
};
</code></pre>
<p>4、表格数据改变后同步更新表单</p>
<pre><code class="language-js">useEffect(() =&gt; {
    //当 data 状态更新时,手动设置表单字段的值
    const fields = data.reduce((acc, record, index) =&gt; {
      acc[`data.${index}.age`] = record.age;
      acc[`data.${index}.address`] = record.address;
      return acc;
    }, {});
    setFieldsValue(fields);
}, );
</code></pre>
<p>antDesign 的完整代码</p>
<pre><code class="language-js">import { Button, Form, Input, Table } from "antd";
import { useEffect, useState } from "react";
const App = ({ form }) =&gt; {
const { getFieldDecorator, setFieldsValue, validateFields } =
    form;
const asdfcolumns = [
    { title: "name", dataIndex: "name", width: "25%" },
    {
      title: "age",
      dataIndex: "age",
      width: "15%",
      render: (text, record, index) =&gt; (
      &lt;Form.Item&gt;
          {getFieldDecorator(`data.${index}.age`, {
            rules: [{ required: true, message: "请输入" }],
            initialValue: record["age"],
          })(&lt;Input /&gt;)}
      &lt;/Form.Item&gt;
      ),
    },
    {
      title: "address",
      dataIndex: "address",
      width: "40%",
      render: (text, record, index) =&gt; (
      &lt;Form.Item&gt;
          {getFieldDecorator(`data.${index}.address`, {
            rules: [
            {
                required: true,
                message: "请输入!",
            },
            ],
            initialValue: record["address"],
          })(&lt;Input /&gt;)}
      &lt;/Form.Item&gt;
      ),
    },
    { title: "operation", dataIndex: "operation" },
];
const = useState([
    { key: 1, name: `Edrward ${1}`, age: "1234", address: "1234" },
    { key: 2, name: `Edrward ${2}`, age: "1234", address: "1234" },
    { key: 3, name: `Edrward ${3}`, age: "", address: "" },
]);
const handleSubmit = () =&gt; {
    validateFields((err, values) =&gt; {
      if (!err) {
      console.log("Received values of form: ", values);
      const updatedData = data.map((item, index) =&gt; ({
                ...item,
                age: values.data.age,
                address: values.data.address,
      }));
      console.log(updatedData);
      }
    });
};

const handleReset = () =&gt; {
    let newData = [
      { key: 1, name: `Edrward ${1}`, age: "1", address: "2" },
      { key: 2, name: `Edrward ${2}`, age: "3", address: "4" },
      { key: 3, name: `Edrward_${3}`, age: "", address: "" },
      { key: 4, name: `Edrward ${3}`, age: "5", address: "6" },
      setData(newData),
    ];
};

useEffect(() =&gt; {
    //当 data 状态更新时,手动设置表单字段的值
    const fields = data.reduce((acc, record, index) =&gt; {
      acc[`data.${index}.age`] = record.age;
      acc[`data.${index}.address`] = record.address;
      return acc;
    }, {});
    setFieldsValue(fields);
}, );
return (
    &lt;&gt;
      &lt;Form&gt;
      &lt;Table
          columns={asdfcolumns}
          dataSource={data}
          pagination={false}
          rowKey="key"
      &gt;&lt;/Table&gt;
      &lt;Form.Item&gt;
          &lt;Button type="primary" onClick={handleSubmit}&gt;
            Submit
          &lt;/Button&gt;
          &lt;Button onClick={handleReset}&gt;reset&lt;/Button&gt;
      &lt;/Form.Item&gt;
      &lt;/Form&gt;
    &lt;/&gt;
);
};
const WrappedMyFormComponent = Form.create()(App);
export default WrappedMyFormComponent;
</code></pre><br><br>
来源:https://www.cnblogs.com/zsxblog/p/18943522
頁: [1]
查看完整版本: 在elementui,antDesign,antDesignVue中表格嵌套多个表单项校验