目录
- 示例场景
- 技术栈
- 示例代码
- 功能点总结
- 详情场景 - 依赖联动初始化
- 示例说明:详情页场景(含回显、联动)
- 修改点说明
- 示例代码(详情页)
- 总结一下关键点
下面是一个基于 React + TypeScript + Ant Design (antd) 的表单联动示例,适用于实际业务中常见的 字段依赖联动 场景,比如:
- 选择国家后,城市选项联动更新;
- 选择某项开关后,展示额外字段;
- 某些字段值变化后,自动填充其他字段。
示例场景
- 选择「国家」后,动态加载「城市」;
- 如果选择了「是否需要发票」,则显示发票信息输入框;
- 输入公司名称后,自动填充公司域名(模拟联动逻辑)。
技术栈
React 18+
TypeScript
antd 5+
react-hook-form
可选(这里示例使用的是 antd 原生表单)
示例代码
import React, { useEffect, useState } from 'react';
import { Form, Select, Input, Switch, Button } from 'antd';const { Option } = Select;const countryCityMap: Record<string, string[]> = {China: ['Beijing', 'Shanghai', 'Guangzhou'],USA: ['New York', 'Los Angeles', 'San Francisco'],Japan: ['Tokyo', 'Osaka', 'Kyoto'],
};const AppForm: React.FC = () => {const [form] = Form.useForm();const [cities, setCities] = useState<string[]>([]);const [showInvoiceInfo, setShowInvoiceInfo] = useState(false);// 联动:国家改变 -> 更新城市选项const handleCountryChange = (value: string) => {const newCities = countryCityMap[value] || [];setCities(newCities);form.setFieldsValue({ city: undefined }); // 重置城市};// 联动:公司名改变 -> 自动填充域名const handleCompanyChange = (e: React.ChangeEvent<HTMLInputElement>) => {const company = e.target.value;if (company) {form.setFieldsValue({ domain: `${company.toLowerCase().replace(/\s/g, '')}.com` });}};return (<Form form={form} layout="vertical" onFinish={(values) => console.log('Submitted:', values)}><Form.Item label="国家" name="country" rules={[{ required: true, message: '请选择国家' }]}><Select placeholder="请选择国家" onChange={handleCountryChange}>{Object.keys(countryCityMap).map((country) => (<Option key={country} value={country}>{country}</Option>))}</Select></Form.Item><Form.Item label="城市" name="city" rules={[{ required: true, message: '请选择城市' }]}><Select placeholder="请选择城市">{cities.map((city) => (<Option key={city} value={city}>{city}</Option>))}</Select></Form.Item><Form.Item label="公司名称" name="company"><Input placeholder="请输入公司名称" onChange={handleCompanyChange} /></Form.Item><Form.Item label="公司域名" name="domain"><Input disabled /></Form.Item><Form.Item label="是否需要发票" name="needInvoice" valuePropName="checked"><Switch onChange={setShowInvoiceInfo} /></Form.Item>{showInvoiceInfo && (<Form.Item label="发票抬头" name="invoiceTitle" rules={[{ required: true, message: '请输入发票抬头' }]}><Input placeholder="请输入发票抬头" /></Form.Item>)}<Form.Item><Button type="primary" htmlType="submit">提交</Button></Form.Item></Form>);
};export default AppForm;
功能点总结
功能 | 实现方式 |
---|---|
国家-城市联动 | onChange 更新 state 并 reset 城市字段 |
Switch 控制字段显隐 | useState 控制组件渲染 |
字段值变化影响其他字段 | onChange + form.setFieldsValue 实现同步填充 |
详情场景 - 依赖联动初始化
如果是「进入详情页」的场景,通常是要将已有数据 回显到表单中,并且保证联动逻辑依然生效,比如:
- 国家和城市依然需要联动;
- 控件的显示状态(比如是否显示发票字段)需要根据数据初始化;
- 有时还需要根据某些字段从后台再次获取数据(例如异步加载某些字段的选项)。
示例说明:详情页场景(含回显、联动)
继续基于前面的例子,我们假设你进入详情页时拿到这段数据:
const detailData = {country: 'USA',city: 'Los Angeles',company: 'Example Inc',domain: 'exampleinc.com',needInvoice: true,invoiceTitle: 'Example Inc 发票抬头',
};
修改点说明
useEffect
中监听detailData
并执行form.setFieldsValue
;- 设置
城市选项
与发票信息展示状态
; - 表单初始化时同步渲染依赖字段。
示例代码(详情页)
import React, { useEffect, useState } from 'react';
import { Form, Select, Input, Switch, Button } from 'antd';const { Option } = Select;const countryCityMap: Record<string, string[]> = {China: ['Beijing', 'Shanghai', 'Guangzhou'],USA: ['New York', 'Los Angeles', 'San Francisco'],Japan: ['Tokyo', 'Osaka', 'Kyoto'],
};// 模拟从后端获取的详情数据
const detailData = {country: 'USA',city: 'Los Angeles',company: 'Example Inc',domain: 'exampleinc.com',needInvoice: true,invoiceTitle: 'Example Inc 发票抬头',
};const DetailForm: React.FC = () => {const [form] = Form.useForm();const [cities, setCities] = useState<string[]>([]);const [showInvoiceInfo, setShowInvoiceInfo] = useState(false);// 设置初始值 & 联动逻辑useEffect(() => {if (detailData.country) {const initialCities = countryCityMap[detailData.country] || [];setCities(initialCities);}if (detailData.needInvoice) {setShowInvoiceInfo(true);}form.setFieldsValue(detailData);}, [form]);const handleCountryChange = (value: string) => {const newCities = countryCityMap[value] || [];setCities(newCities);form.setFieldsValue({ city: undefined }); // 重置城市};const handleCompanyChange = (e: React.ChangeEvent<HTMLInputElement>) => {const company = e.target.value;if (company) {form.setFieldsValue({ domain: `${company.toLowerCase().replace(/\s/g, '')}.com` });}};return (<Form form={form} layout="vertical" onFinish={(values) => console.log('提交内容:', values)}><Form.Item label="国家" name="country" rules={[{ required: true }]}><Select placeholder="请选择国家" onChange={handleCountryChange}>{Object.keys(countryCityMap).map((country) => (<Option key={country} value={country}>{country}</Option>))}</Select></Form.Item><Form.Item label="城市" name="city" rules={[{ required: true }]}><Select placeholder="请选择城市">{cities.map((city) => (<Option key={city} value={city}>{city}</Option>))}</Select></Form.Item><Form.Item label="公司名称" name="company"><Input onChange={handleCompanyChange} /></Form.Item><Form.Item label="公司域名" name="domain"><Input disabled /></Form.Item><Form.Item label="是否需要发票" name="needInvoice" valuePropName="checked"><Switch onChange={setShowInvoiceInfo} /></Form.Item>{showInvoiceInfo && (<Form.Item label="发票抬头" name="invoiceTitle" rules={[{ required: true }]}><Input /></Form.Item>)}<Form.Item><Button type="primary" htmlType="submit">保存</Button></Form.Item></Form>);
};export default DetailForm;
总结一下关键点
关键点 | 实现方式 |
---|---|
表单回显 | form.setFieldsValue(detailData) |
城市选项联动回显 | 初始化时同步设置 cities 状态 |
控件展示逻辑回显 | 例如发票开关,通过设置 setShowInvoiceInfo |
编辑时仍保留联动能力 | 保持 onChange 回调,逻辑不变 |