@kne/react-form-antd
Version:
把 @kne/react-form 表单校验逻辑应用到antd
1,241 lines (1,105 loc) • 65.5 kB
Markdown
# react-form-antd
### 描述
把 @kne/react-form 表单校验逻辑应用到antd
### 安装
```shell
npm i --save @kne/react-form-antd
```
### 概述
基于 @kne/react-form 封装的 Ant Design 表单组件库,提供完整的表单校验功能和丰富的表单字段组件。
#### 核心特性
- **UI分离设计** - UI与校验逻辑分离,可适配不同UI框架
- **分级校验规则** - 支持同步/异步校验,可配置校验规则优先级
- **事件驱动架构** - 通过事件机制实现灵活扩展,支持调试模式
- **复杂表单支持** - 支持 GroupList 动态表单、嵌套表单、子表单
- **完整字段组件** - 覆盖所有常用 Ant Design 表单组件
- **国际化支持** - 内置中英文提示信息
#### 支持的组件
**表单核心**
- Form - 表单容器组件
**基础字段**
- Input / Input.Password - 文本输入框
- InputNumber - 数字输入框
- TextArea - 多行文本
**选择器**
- Select - 下拉选择器
- TreeSelect - 树形选择器
- Cascader - 级联选择器
**日期时间**
- DatePicker - 日期选择器
- TimePicker - 时间选择器
- DatePickerToday - 快捷日期选择
**多选/单选**
- Checkbox - 复选框
- CheckboxGroup - 复选框组
- RadioGroup - 单选框组
**其他**
- Rate - 评分
- Slider - 滑动条
- Switch - 开关
**按钮**
- SubmitButton - 提交按钮
- ResetButton - 重置按钮
- CancelButton - 取消按钮
**高级功能**
- GroupList - 动态表单列表
- preset - 全局配置
#### 快速开始
```bash
npm install @kne/react-form-antd antd
```
```jsx
import Form, { Input, SubmitButton } from '@kne/react-form-antd';
function MyForm() {
return (
<Form onSubmit={(data) => console.log(data)}>
<Input name="username" label="用户名" rule="REQ" />
<SubmitButton>提交</SubmitButton>
</Form>
);
}
```
#### 校验规则
```jsx
<Input name="field" rule="REQ" /> // 必填
<Input name="field" rule="LEN-2-10" /> // 长度2-10
<Input name="field" rule="REQ LEN-2-10" /> // 组合规则
```
### 示例
#### 示例代码
- 基础表单
- 展示表单的基本使用方法,包含各种表单字段和校验规则
- _ReactFormAntd(@kne/current-lib_react-form-antd),(@kne/current-lib_react-form-antd/dist/index.css),antd(antd)
```jsx
const {default: Form, Input, InputNumber, Select, TextArea, Switch, Checkbox, CheckboxGroup, RadioGroup, DatePicker, TimePicker, Rate, Slider, SubmitButton} = _ReactFormAntd;
const {Flex, Space, Divider} = antd;
const {useState} = React;
const BaseExample = () => {
const [formData, setFormData] = useState(null);
return (
<Flex vertical gap={24}>
<Form
onSubmit={(data) => {
console.log('表单提交数据:', data);
setFormData(data);
}}
>
<Flex vertical gap={16}>
<Input name="username" label="用户名" rule="REQ" placeholder="请输入用户名" />
<Input.Password name="password" label="密码" rule="REQ LEN-6-20" placeholder="请输入6-20位密码" />
<InputNumber name="age" label="年龄" rule="REQ NUM" min={0} max={150} placeholder="请输入年龄" />
<Select
name="gender"
label="性别"
rule="REQ"
placeholder="请选择性别"
options={[
{label: '男', value: 'male'},
{label: '女', value: 'female'}
]}
/>
<TextArea name="description" label="个人简介" placeholder="请输入个人简介" rows={3} />
<RadioGroup name="status" label="状态" rule="REQ" options={[{label: '启用', value: 1}, {label: '禁用', value: 0}]} />
<CheckboxGroup
name="hobbies"
label="兴趣爱好"
options={[
{label: '阅读', value: 'reading'},
{label: '运动', value: 'sports'},
{label: '音乐', value: 'music'},
{label: '旅行', value: 'travel'}
]}
/>
<Switch name="subscribe" label="订阅通知" />
<Rate name="rating" label="评分" allowHalf />
<Slider name="progress" label="进度" />
<DatePicker name="birthday" label="出生日期" rule="REQ" />
<TimePicker name="appointmentTime" label="预约时间" />
<SubmitButton>提交表单</SubmitButton>
</Flex>
</Form>
{formData && (
<>
<Divider>提交数据</Divider>
<pre style={{background: '#f5f5f5', padding: 12, borderRadius: 4}}>{JSON.stringify(formData, null, 2)}</pre>
</>
)}
</Flex>
);
};
render(<BaseExample />);
```
- 表单类型和尺寸
- 展示表单的不同类型(default/inline/inner)和尺寸(small/middle/large)
- _ReactFormAntd(@kne/current-lib_react-form-antd),(@kne/current-lib_react-form-antd/dist/index.css),antd(antd)
```jsx
const {default: Form, Input, SubmitButton} = _ReactFormAntd;
const {Flex, Card, Space} = antd;
const FormTypeExample = () => {
return (<Flex vertical gap={24}>
<Card title="Default 类型(默认)" size="small">
<Form type="default">
<Flex gap={16} vertical>
<Input name="name" label="姓名" rule="REQ"/>
<Input name="email" label="邮箱" rule="REQ"/>
</Flex>
<SubmitButton>提交</SubmitButton>
</Form>
</Card>
<Card title="Inline 类型(行内表单)" size="small">
<Form type="inline">
<Space wrap>
<Input name="keyword" label="关键词"/>
<Input name="author" label="作者"/>
</Space>
<SubmitButton style={{marginLeft: 8}}>搜索</SubmitButton>
</Form>
</Card>
<Card title="Inner 类型(内嵌表单)" size="small">
<Form type="inner">
<Flex gap={16} vertical>
<Input name="title" label="标题" rule="REQ"/>
<Input name="content" label="内容"/>
<SubmitButton>提交</SubmitButton>
</Flex>
</Form>
</Card>
<Card title="不同尺寸" size="small">
<Flex vertical gap={16}>
<Form size="small">
<Input name="smallField" label="Small 尺寸"/>
</Form>
<Form size="middle">
<Input name="middleField" label="Middle 尺寸(默认)"/>
</Form>
<Form size="large">
<Input name="largeField" label="Large 尺寸"/>
</Form>
</Flex>
</Card>
</Flex>);
};
render(<FormTypeExample/>);
```
- 输入框组件
- 展示Input和InputNumber组件的各种用法
- _ReactFormAntd(@kne/current-lib_react-form-antd),(@kne/current-lib_react-form-antd/dist/index.css),antd(antd)
```jsx
const {Input, InputNumber, TextArea} = _ReactFormAntd;
const {default: Form, SubmitButton} = _ReactFormAntd;
const {Flex, Card, Space, Divider} = antd;
const {useState} = React;
const InputExample = () => {
const [formData, setFormData] = useState(null);
return (
<Flex vertical gap={24}>
<Card title="Input 文本输入框" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<Input name="basicInput" label="基础输入" placeholder="请输入文本" />
<Input name="requiredInput" label="必填输入" rule="REQ" placeholder="此项为必填" />
<Input name="lengthInput" label="长度限制" rule="LEN-2-10" placeholder="长度需在2-10个字符之间" />
<Input name="realtimeInput" label="实时校验" realtime rule="REQ" placeholder="输入时实时校验" />
<Input.Password name="password" label="密码输入" rule="REQ LEN-6-20" placeholder="请输入6-20位密码" />
<Input.Password
name="confirmPassword"
label="确认密码"
rule="REQ LEN-6-20"
placeholder="请再次输入密码"
/>
<Divider>带前缀/后缀</Divider>
<Input name="price" label="价格" addonBefore="¥" placeholder="请输入金额" />
<Input name="website" label="网址" addonAfter=".com" placeholder="请输入网站名称" />
</Flex>
</Form>
</Card>
<Card title="InputNumber 数字输入框" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<InputNumber name="basicNumber" label="基础数字" placeholder="请输入数字" />
<InputNumber name="ageNumber" label="年龄" min={0} max={150} placeholder="0-150" />
<InputNumber name="priceNumber" label="价格" min={0} step={0.01} precision={2} placeholder="请输入价格" />
<InputNumber name="quantityNumber" label="数量" min={1} max={999} step={10} placeholder="请输入数量" />
<InputNumber name="formatterNumber" label="格式化数字" formatter={(value) => `¥ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')} parser={(value) => value.replace(/¥\s?|(,*)/g, '')} />
</Flex>
</Form>
</Card>
<Card title="TextArea 多行文本" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<TextArea name="basicTextArea" label="基础文本域" placeholder="请输入内容" rows={4} />
<TextArea name="limitedTextArea" label="字数限制" rule="LEN-0-200" placeholder="最多200字" rows={4} showCount maxLength={200} />
<TextArea name="autoTextArea" label="自适应高度" placeholder="自动调整高度" autoSize={{minRows: 2, maxRows: 6}} />
</Flex>
</Form>
</Card>
{formData && (
<Card title="表单数据" size="small">
<pre style={{background: '#f5f5f5', padding: 12, borderRadius: 4}}>{JSON.stringify(formData, null, 2)}</pre>
</Card>
)}
</Flex>
);
};
render(<InputExample />);
```
- 选择器组件
- 展示Select、TreeSelect、Cascader组件的使用方法
- _ReactFormAntd(@kne/current-lib_react-form-antd),(@kne/current-lib_react-form-antd/dist/index.css),antd(antd)
```jsx
const {Select, TreeSelect, Cascader} = _ReactFormAntd;
const {default: Form, SubmitButton} = _ReactFormAntd;
const {Flex, Card, Divider} = antd;
const {useState} = React;
const SelectExample = () => {
const [formData, setFormData] = useState(null);
const treeData = [
{
title: 'Node1',
value: '0-0',
children: [
{title: 'Child Node1', value: '0-0-1'},
{title: 'Child Node2', value: '0-0-2'}
]
},
{
title: 'Node2',
value: '0-1',
children: [
{title: 'Child Node3', value: '0-1-1'},
{title: 'Child Node4', value: '0-1-2'}
]
}
];
const cascaderOptions = [
{
label: '浙江省',
value: 'zhejiang',
children: [
{
label: '杭州市',
value: 'hangzhou',
children: [
{label: '西湖区', value: 'xihu'},
{label: '余杭区', value: 'yuhang'}
]
},
{
label: '宁波市',
value: 'ningbo',
children: [
{label: '海曙区', value: 'haishu'},
{label: '江北区', value: 'jiangbei'}
]
}
]
},
{
label: '江苏省',
value: 'jiangsu',
children: [
{
label: '南京市',
value: 'nanjing',
children: [
{label: '玄武区', value: 'xuanwu'},
{label: '鼓楼区', value: 'gulou'}
]
}
]
}
];
return (
<Flex vertical gap={24}>
<Card title="Select 选择器" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<Select
name="basicSelect"
label="单选"
placeholder="请选择"
options={[
{label: '选项一', value: 1},
{label: '选项二', value: 2},
{label: '选项三', value: 3}
]}
/>
<Select
name="requiredSelect"
label="必选"
rule="REQ"
placeholder="此项为必选"
options={[
{label: '选项A', value: 'a'},
{label: '选项B', value: 'b'},
{label: '选项C', value: 'c'}
]}
/>
<Select
name="multiSelect"
label="多选"
mode="multiple"
placeholder="请选择多个选项"
options={[
{label: '苹果', value: 'apple'},
{label: '香蕉', value: 'banana'},
{label: '橙子', value: 'orange'},
{label: '葡萄', value: 'grape'}
]}
/>
<Select
name="searchSelect"
label="可搜索"
showSearch
placeholder="输入关键词搜索"
filterOption={(input, option) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase())}
options={[
{label: '北京市', value: 'beijing'},
{label: '上海市', value: 'shanghai'},
{label: '广州市', value: 'guangzhou'},
{label: '深圳市', value: 'shenzhen'}
]}
/>
<Select
name="groupSelect"
label="分组选择"
placeholder="请选择"
>
<Select.OptGroup label="热门城市">
<Select.Option value="hot1">北京</Select.Option>
<Select.Option value="hot2">上海</Select.Option>
</Select.OptGroup>
<Select.OptGroup label="其他城市">
<Select.Option value="other1">杭州</Select.Option>
<Select.Option value="other2">成都</Select.Option>
</Select.OptGroup>
</Select>
</Flex>
</Form>
</Card>
<Card title="TreeSelect 树选择" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<TreeSelect
name="basicTreeSelect"
label="单选树"
placeholder="请选择"
treeData={treeData}
/>
<TreeSelect
name="requiredTreeSelect"
label="必选树"
rule="REQ"
placeholder="此项为必选"
treeData={treeData}
/>
<TreeSelect
name="multiTreeSelect"
label="多选树"
treeCheckable
placeholder="请选择多个"
treeData={treeData}
/>
<TreeSelect
name="searchTreeSelect"
label="可搜索树"
showSearch
placeholder="输入关键词搜索"
treeData={treeData}
filterTreeNode={(node, value) => node.title.toLowerCase().indexOf(value.toLowerCase()) > -1}
/>
</Flex>
</Form>
</Card>
<Card title="Cascader 级联选择" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<Cascader
name="basicCascader"
label="地区选择"
placeholder="请选择地区"
options={cascaderOptions}
/>
<Cascader
name="requiredCascader"
label="必选地区"
rule="REQ"
placeholder="此项为必选"
options={cascaderOptions}
/>
<Cascader
name="searchCascader"
label="可搜索"
showSearch
placeholder="支持搜索"
options={cascaderOptions}
/>
</Flex>
</Form>
</Card>
{formData && (
<Card title="表单数据" size="small">
<pre style={{background: '#f5f5f5', padding: 12, borderRadius: 4}}>{JSON.stringify(formData, null, 2)}</pre>
</Card>
)}
</Flex>
);
};
render(<SelectExample />);
```
- 日期时间组件
- 展示DatePicker、TimePicker、DatePickerToday组件的使用方法
- _ReactFormAntd(@kne/current-lib_react-form-antd),(@kne/current-lib_react-form-antd/dist/index.css),antd(antd)
```jsx
const {DatePicker, TimePicker, DatePickerToday} = _ReactFormAntd;
const {default: Form, SubmitButton} = _ReactFormAntd;
const {Flex, Card, Divider} = antd;
const {useState} = React;
const DatePickerExample = () => {
const [formData, setFormData] = useState(null);
return (
<Flex vertical gap={24}>
<Card title="DatePicker 日期选择" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<DatePicker name="basicDate" label="基础日期" placeholder="请选择日期" />
<DatePicker name="requiredDate" label="必选日期" rule="REQ" placeholder="此项为必选" />
<DatePicker name="formatDate" label="自定义格式" format="YYYY年MM月DD日" placeholder="请选择日期" />
<DatePicker name="disabledDate" label="禁用日期" placeholder="请选择日期" disabledDate={(current) => current && current < Date.now()} />
<Divider>日期范围选择</Divider>
<DatePicker.RangePicker name="dateRange" label="日期范围" placeholder={['开始日期', '结束日期']} />
<DatePicker.RangePicker name="requiredDateRange" label="必选范围" rule="REQ" placeholder={['开始日期', '结束日期']} />
<Divider>其他类型</Divider>
<DatePicker.MonthPicker name="monthPicker" label="月份选择" placeholder="请选择月份" />
<DatePicker.WeekPicker name="weekPicker" label="周选择" placeholder="请选择周" />
</Flex>
</Form>
</Card>
<Card title="TimePicker 时间选择" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<TimePicker name="basicTime" label="基础时间" placeholder="请选择时间" />
<TimePicker name="requiredTime" label="必选时间" rule="REQ" placeholder="此项为必选" />
<TimePicker name="formatTime" label="自定义格式" format="HH时mm分ss秒" placeholder="请选择时间" />
<TimePicker name="stepTime" label="步长设置" hourStep={1} minuteStep={15} secondStep={30} placeholder="请选择时间" />
<TimePicker.RangePicker name="timeRange" label="时间范围" placeholder={['开始时间', '结束时间']} />
</Flex>
</Form>
</Card>
<Card title="DatePickerToday 快捷日期" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<DatePickerToday name="basicToday" label="基础日期范围" />
<DatePickerToday name="requiredToday" label="必选范围" rule="REQ" />
<DatePickerToday name="weekPicker" label="周选择" picker="week" />
<DatePickerToday name="monthPicker" label="月份选择" picker="month" />
<DatePickerToday name="yearPicker" label="年份选择" picker="year" />
<DatePickerToday
name="customSoFarText"
label="自定义至今文本"
soFarText="现在"
/>
<DatePickerToday
name="customSoFarValue"
label="自定义至今值"
soFarText="至今"
soFarValue="PRESENT"
onChange={([start, end]) => {
if (end === 'PRESENT') {
console.log('用户选择了至今');
}
}}
/>
</Flex>
</Form>
</Card>
{formData && (
<Card title="表单数据" size="small">
<pre style={{background: '#f5f5f5', padding: 12, borderRadius: 4}}>{JSON.stringify(formData, null, 2)}</pre>
</Card>
)}
</Flex>
);
};
render(<DatePickerExample />);
```
- 多选和单选组件
- 展示Checkbox、CheckboxGroup、RadioGroup组件的使用方法
- _ReactFormAntd(@kne/current-lib_react-form-antd),(@kne/current-lib_react-form-antd/dist/index.css),antd(antd)
```jsx
const {Checkbox, CheckboxGroup, RadioGroup} = _ReactFormAntd;
const {default: Form, SubmitButton} = _ReactFormAntd;
const {Flex, Card, Divider, Space} = antd;
const {useState} = React;
const CheckboxRadioExample = () => {
const [formData, setFormData] = useState(null);
return (
<Flex vertical gap={24}>
<Card title="Checkbox 复选框" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<Checkbox name="agree" label="同意协议">我已阅读并同意《用户协议》</Checkbox>
<Checkbox name="subscribe" label="订阅">订阅新闻资讯</Checkbox>
<Checkbox name="requiredCheckbox" rule="REQ">此项为必选</Checkbox>
<Checkbox name="disabledCheckbox" disabled>禁用状态</Checkbox>
</Flex>
</Form>
</Card>
<Card title="CheckboxGroup 复选框组" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<CheckboxGroup
name="fruits"
label="喜欢的水果"
options={[
{label: '苹果', value: 'apple'},
{label: '香蕉', value: 'banana'},
{label: '橙子', value: 'orange'},
{label: '葡萄', value: 'grape'}
]}
/>
<CheckboxGroup
name="requiredFruits"
label="必选水果"
rule="REQ"
options={[
{label: '苹果', value: 'apple'},
{label: '香蕉', value: 'banana'}
]}
/>
<CheckboxGroup
name="interests"
label="兴趣爱好"
options={[
{label: '阅读', value: 'reading'},
{label: '运动', value: 'sports'},
{label: '音乐', value: 'music'},
{label: '旅行', value: 'travel'},
{label: '摄影', value: 'photography'},
{label: '游戏', value: 'gaming'}
]}
/>
</Flex>
</Form>
</Card>
<Card title="RadioGroup 单选框组" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<RadioGroup
name="gender"
label="性别"
options={[
{label: '男', value: 'male'},
{label: '女', value: 'female'}
]}
/>
<RadioGroup
name="requiredGender"
label="必选性别"
rule="REQ"
options={[
{label: '男', value: 'male'},
{label: '女', value: 'female'}
]}
/>
<RadioGroup
name="size"
label="尺寸"
optionType="button"
buttonStyle="solid"
options={[
{label: '小', value: 'small'},
{label: '中', value: 'middle'},
{label: '大', value: 'large'}
]}
/>
<RadioGroup
name="status"
label="状态"
optionType="button"
options={[
{label: '启用', value: 1},
{label: '禁用', value: 0}
]}
/>
</Flex>
</Form>
</Card>
{formData && (
<Card title="表单数据" size="small">
<pre style={{background: '#f5f5f5', padding: 12, borderRadius: 4}}>{JSON.stringify(formData, null, 2)}</pre>
</Card>
)}
</Flex>
);
};
render(<CheckboxRadioExample />);
```
- 评分和滑块组件
- 展示Rate、Slider、Switch组件的使用方法
- _ReactFormAntd(@kne/current-lib_react-form-antd),(@kne/current-lib_react-form-antd/dist/index.css),antd(antd)
```jsx
const {Rate, Slider, Switch} = _ReactFormAntd;
const {default: Form, SubmitButton} = _ReactFormAntd;
const {Flex, Card, Divider} = antd;
const {useState} = React;
const RateSliderExample = () => {
const [formData, setFormData] = useState(null);
return (
<Flex vertical gap={24}>
<Card title="Rate 评分" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<Rate name="basicRate" label="基础评分" />
<Rate name="requiredRate" label="必选评分" rule="REQ" />
<Rate name="halfRate" label="半星评分" allowHalf />
<Rate name="customRate" label="自定义数量" count={10} />
<Rate name="disabledRate" label="禁用状态" disabled defaultValue={3} />
<Rate name="clearRate" label="可清除" allowClear />
<Rate name="charRate" label="自定义字符" character="A" allowHalf />
<Rate name="heartRate" label="心形评分" character="♥" allowHalf style={{color: 'red'}} />
</Flex>
</Form>
</Card>
<Card title="Slider 滑动条" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<Slider name="basicSlider" label="基础滑动条" />
<Slider name="rangeSlider" label="范围滑动条" range defaultValue={[20, 50]} />
<Slider name="stepSlider" label="步长设置" step={10} defaultValue={30} />
<Slider name="marksSlider" label="带标记" marks={{0: '0°C', 26: '26°C', 37: '37°C', 100: '100°C'}} defaultValue={37} />
<Slider name="minMaxSlider" label="范围限制" min={0} max={1000} defaultValue={100} />
<Slider name="disabledSlider" label="禁用状态" disabled defaultValue={30} />
<Slider name="verticalSlider" label="垂直方向" vertical style={{height: 200}} />
</Flex>
</Form>
</Card>
<Card title="Switch 开关" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<Switch name="basicSwitch" label="基础开关" />
<Switch name="requiredSwitch" label="必选开关" rule="REQ" />
<Switch name="textSwitch" label="带文字" checkedChildren="开" unCheckedChildren="关" />
<Switch name="disabledSwitch" label="禁用状态" disabled defaultChecked />
<Switch name="smallSwitch" label="小尺寸" size="small" />
<Switch name="loadingSwitch" label="加载中" loading />
</Flex>
</Form>
</Card>
{formData && (
<Card title="表单数据" size="small">
<pre style={{background: '#f5f5f5', padding: 12, borderRadius: 4}}>{JSON.stringify(formData, null, 2)}</pre>
</Card>
)}
</Flex>
);
};
render(<RateSliderExample />);
```
- 表单校验
- 展示表单的各种校验规则和自定义校验
- _ReactFormAntd(@kne/current-lib_react-form-antd),(@kne/current-lib_react-form-antd/dist/index.css),antd(antd)
```jsx
const {default: Form, Input, InputNumber, SubmitButton, preset, RULES} = _ReactFormAntd;
const {Flex, Card, Divider, Alert, Space, Button, Typography} = antd;
const {Text} = Typography;
const {useState, useEffect} = React;
const ValidationExample = () => {
const [formData, setFormData] = useState(null);
const formRef = React.useRef();
// 配置自定义校验规则
useEffect(() => {
preset({
rules: Object.assign({}, RULES, {
// 自定义密码强度规则(带参数)
PASSWORD_STRENGTH: (value, level, {data}) => {
if (!value) return {result: true, errMsg: ''};
const strength = value.length >= 6 ? (value.length >= 10 ? 3 : 2) : 1;
const hasNumber = /\d/.test(value);
const hasLetter = /[a-zA-Z]/.test(value);
const hasSpecial = /[!@#$%^&*]/.test(value);
const score = (hasNumber ? 1 : 0) + (hasLetter ? 1 : 0) + (hasSpecial ? 1 : 0);
const finalStrength = strength + score;
return {
result: finalStrength >= (level || 2),
errMsg: finalStrength < (level || 2) ? `密码强度不足,当前强度: ${finalStrength}` : '',
data: {strength: finalStrength}
};
}, // 确认密码规则(使用数据联动)
CONFIRM_PASSWORD: (value, {data}) => {
return {
result: value === data.password, errMsg: '两次输入的密码不一致'
};
}, // 异步验证规则:用户名唯一性检查
CHECK_USERNAME: async (value, {field}) => {
if (!value) return {result: true, errMsg: ''};
// 模拟已存在的用户名
const existingUsernames = ['admin', 'test', 'user', 'root'];
console.log(`开始验证用户名: ${value}`);
await new Promise(resolve => setTimeout(resolve, 1000));
console.log(`验证完成用户名: ${value}`);
const exists = existingUsernames.includes(value.toLowerCase());
return {
result: !exists, errMsg: exists ? '该用户名已被占用' : ''
};
}
})
});
}, []);
return (<Flex vertical gap={24}>
<Card title="内置校验规则" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<Alert
message="内置规则: REQ(必填), TEL(手机号), EMAIL(邮箱), LEN-最小-最大(长度限制)"
type="info"
showIcon
/>
<Input name="required" label="必填字段" rule="REQ" placeholder="此项为必填"/>
<Input name="tel" label="手机号" rule="REQ TEL" placeholder="请输入手机号"/>
<Input name="email" label="邮箱" rule="REQ EMAIL" placeholder="请输入邮箱"/>
<Input name="length" label="长度限制" rule="REQ LEN-2-10" placeholder="长度需在2-10个字符之间"/>
<Input name="combined" label="组合规则" rule="REQ LEN-4-20 EMAIL"
placeholder="必填 + 长度4-20 + 邮箱格式"/>
</Flex>
</Form>
</Card>
<Card title="自定义校验规则" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<Alert
message="通过 preset 配置自定义规则,支持正则、函数、异步函数三种形式"
type="info"
showIcon
/>
<Input
name="password"
label="密码"
rule="REQ LEN-6-20 PASSWORD_STRENGTH-3"
placeholder="请输入密码(需要高强度)"
/>
<Input
name="confirmPassword"
label="确认密码"
rule="REQ CONFIRM_PASSWORD"
placeholder="请再次输入密码"
/>
</Flex>
</Form>
</Card>
<Card title="异步校验规则" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<Alert
message="支持异步验证,如远程接口校验用户名唯一性"
type="info"
showIcon
/>
<Text type="secondary">已占用的用户名: admin, test, user, root</Text>
<Input
name="username"
label="用户名"
rule="REQ LEN-3-20 CHECK_USERNAME"
placeholder="请输入用户名(会异步校验唯一性)"
/>
</Flex>
</Form>
</Card>
<Card title="实时校验" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<Alert
message="realtime 属性会在输入时实时校验,不需要等待失焦"
type="info"
showIcon
/>
<Input name="realtimeInput" label="实时校验" realtime rule="REQ LEN-2-10"
placeholder="输入时实时校验"/>
<Input name="normalInput" label="普通校验" rule="REQ LEN-2-10" placeholder="失焦时校验"/>
</Flex>
</Form>
</Card>
<Card title="表单操作 API" size="small">
<Form ref={formRef} onSubmit={setFormData}>
<Flex vertical gap={16}>
<Input name="field1" label="字段1" rule="REQ" placeholder="测试字段1"/>
<Input name="field2" label="字段2" rule="LEN-0-20" placeholder="测试字段2"/>
</Flex>
</Form>
<Divider/>
<Space wrap>
<Button onClick={() => formRef.current?.setFormData({field1: '预设值', field2: '测试数据'})}>
设置表单值
</Button>
<Button onClick={() => console.log('表单数据:', formRef.current?.getFormData())}>
获取表单值
</Button>
<Button onClick={() => formRef.current?.reset()}>重置表单</Button>
<Button onClick={() => formRef.current?.validateAll()}>手动校验</Button>
</Space>
</Card>
{formData && (<Card title="提交数据" size="small">
<pre style={{
background: '#f5f5f5', padding: 12, borderRadius: 4
}}>{JSON.stringify(formData, null, 2)}</pre>
</Card>)}
</Flex>);
};
render(<ValidationExample/>);
```
- 动态表单
- 展示GroupList动态表单的使用方法
- _ReactFormAntd(@kne/current-lib_react-form-antd),(@kne/current-lib_react-form-antd/dist/index.css),antd(antd)
```jsx
const {default: Form, Input, Select, GroupList, SubmitButton} = _ReactFormAntd;
const {Flex, Card, Space, Button, Divider} = antd;
const {useState, useRef} = React;
const GroupListExample = () => {
const [formData, setFormData] = useState(null);
const addButtonRef = useRef();
return (
<Flex vertical gap={24}>
<Card title="基础动态表单" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<GroupList name="members">
{({index, onRemove}) => (
<Space key={index} style={{display: 'flex', marginBottom: 8}} align="baseline">
<span>成员 {index + 1}:</span>
<Input name="name" placeholder="姓名" rule="REQ" />
<Input name="email" placeholder="邮箱" rule="REQ" />
<Button danger onClick={onRemove}>删除</Button>
</Space>
)}
</GroupList>
<SubmitButton>提交</SubmitButton>
</Flex>
</Form>
</Card>
<Card title="复杂动态表单" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<Space>
<Button type="primary" onClick={() => addButtonRef.current?.onAdd()}>
添加联系人
</Button>
</Space>
<GroupList name="contacts" ref={addButtonRef} defaultLength={1}>
{({index, onRemove, onAdd}) => (
<Card key={index} title={`联系人 ${index + 1}`} size="small" extra={<Button danger size="small" onClick={onRemove}>删除</Button>}>
<Flex vertical gap={12}>
<Input name="name" label="姓名" rule="REQ" placeholder="请输入姓名" />
<Input name="phone" label="电话" rule="REQ" placeholder="请输入电话" />
<Input name="email" label="邮箱" placeholder="请输入邮箱" />
<Select
name="type"
label="类型"
rule="REQ"
placeholder="请选择类型"
options={[
{label: '主要联系人', value: 'primary'},
{label: '次要联系人', value: 'secondary'},
{label: '紧急联系人', value: 'emergency'}
]}
/>
<Input name="address" label="地址" placeholder="请输入地址" />
</Flex>
</Card>
)}
</GroupList>
<SubmitButton>提交</SubmitButton>
</Flex>
</Form>
</Card>
<Card title="嵌套表单" size="small">
<Form onSubmit={setFormData}>
<Flex vertical gap={16}>
<Input name="projectName" label="项目名称" rule="REQ" placeholder="请输入项目名称" />
<Divider>项目成员</Divider>
<GroupList name="team">
{({index, onRemove}) => (
<Card key={index} size="small" title={`成员 ${index + 1}`} extra={<Button danger size="small" onClick={onRemove}>删除</Button>}>
<Flex vertical gap={12}>
<Input name="memberName" label="成员姓名" rule="REQ" />
<Select
name="role"
label="角色"
rule="REQ"
options={[
{label: '负责人', value: 'leader'},
{label: '开发', value: 'developer'},
{label: '测试', value: 'tester'}
]}
/>
<GroupList name="tasks">
{({index: taskIndex, onRemove: taskRemove}) => (
<Space key={taskIndex} style={{display: 'flex', alignItems: 'center'}}>
<span>任务 {taskIndex + 1}:</span>
<Input name="taskName" placeholder="任务名称" rule="REQ" />
<Button danger size="small" onClick={taskRemove}>删除</Button>
</Space>
)}
</GroupList>
</Flex>
</Card>
)}
</GroupList>
<SubmitButton>提交</SubmitButton>
</Flex>
</Form>
</Card>
{formData && (
<Card title="表单数据" size="small">
<pre style={{background: '#f5f5f5', padding: 12, borderRadius: 4, maxHeight: 300, overflow: 'auto'}}>{JSON.stringify(formData, null, 2)}</pre>
</Card>
)}
</Flex>
);
};
render(<GroupListExample />);
```
- 表单按钮
- 展示SubmitButton、ResetButton、CancelButton的使用方法
- _ReactFormAntd(@kne/current-lib_react-form-antd),(@kne/current-lib_react-form-antd/dist/index.css),antd(antd)
```jsx
const {default: Form, Input, SubmitButton, ResetButton, CancelButton} = _ReactFormAntd;
const {Flex, Card, Space, Divider, message, Button, Alert} = antd;
const {useState} = React;
const ButtonsExample = () => {
const [formData, setFormData] = useState(null);
const [loading, setLoading] = useState(false);
const handleSubmit = (data) => {
console.log('提交数据:', data);
setFormData(data);
setLoading(true);
// 模拟异步提交
setTimeout(() => {
setLoading(false);
message.success('提交成功!');
}, 1500);
};
return (
<Flex vertical gap={24}>
<Card title="SubmitButton 提交按钮" size="small">
<Form onSubmit={handleSubmit}>
<Flex vertical gap={16}>
<Input name="username" label="用户名" rule="REQ" placeholder="请输入用户名" />
<Input.Password name="password" label="密码" rule="REQ" placeholder="请输入密码" />
<Divider>提交按钮类型</Divider>
<Space wrap>
<SubmitButton>默认提交</SubmitButton>
<SubmitButton type="default">Default类型</SubmitButton>
<SubmitButton type="dashed">Dashed类型</SubmitButton>
<SubmitButton type="primary" loading={loading}>带加载状态</SubmitButton>
</Space>
<Divider>实时校验模式</Divider>
<Alert message="realtime属性为true时,表单未通过校验时按钮禁用" type="info" showIcon style={{marginBottom: 16}} />
<SubmitButton realtime>实时校验提交</SubmitButton>
</Flex>
</Form>
</Card>
<Card title="ResetButton 重置按钮" size="small">
<Form>
<Flex vertical gap={16}>
<Input name="field1" label="字段1" placeholder="输入后点击重置" />
<Input name="field2" label="字段2" placeholder="输入后点击重置" />
<Space>
<ResetButton>重置表单</ResetButton>
<ResetButton type="primary">Primary重置</ResetButton>
<ResetButton danger>Danger重置</ResetButton>
</Space>
</Flex>
</Form>
</Card>
<Card title="CancelButton 取消按钮" size="small">
<Form cache="form-cache-key">
<Flex vertical gap={16}>
<Alert message="CancelButton用于清除表单缓存,配合Form的cache属性使用" type="info" showIcon />
<Input name="cachedField1" label="缓存字段1" placeholder="输入后会缓存" />
<Input name="cachedField2" label="缓存字段2" placeholder="输入后会缓存" />
<Space>
<CancelButton onClick={() => message.info('取消并清除缓存')}>取消</CancelButton>
<CancelButton type="default">Default取消</CancelButton>
</Space>
</Flex>
</Form>
</Card>
<Card title="完整表单示例" size="small">
<Form
cache="complete-form"
onSubmit={handleSubmit}
>
<Flex vertical gap={16}>
<Input name="name" label="姓名" rule="REQ" placeholder="请输入姓名" />
<Input name="email" label="邮箱" rule="REQ" placeholder="请输入邮箱" />
<Input name="phone" label="电话" placeholder="请输入电话" />
<Divider>表单操作</Divider>
<Space>
<SubmitButton type="primary">提交</SubmitButton>
<ResetButton>重置</ResetButton>
<CancelButton>取消</CancelButton>
</Space>
</Flex>
</Form>
</Card>
{formData && (
<Card title="提交数据" size="small">
<pre style={{background: '#f5f5f5', padding: 12, borderRadius: 4}}>{JSON.stringify(formData, null, 2)}</pre>
</Card>
)}
</Flex>
);
};
render(<ButtonsExample />);
```
### API
#### 属性
| 属性 | 类型 | 默认值 | 描述 |
|---------------|------------------------------------|---------------------|--------------------------------|
| type | `'default' \| 'inline' \| 'inner'` | `'default'` | 表单类型 |
| size | `'small' \| 'middle' \| 'large'` | `'middle'` | 表单尺寸 |
| className | `string` | - | 自定义类名 |
| cache | `string` | - | 表单缓存标识,用于表单数据缓存 |
| enterSubmit | `boolean` | `false` | 是否支持回车提交 |
| scrollToError | `boolean` | `true` | 校验失败时是否滚动到错误字段 |
| scrollProps | `object` | `{block: 'center'}` | 滚动配置,参考 Element.scrollIntoView |
| onSubmit | `function` | - | 表单提交回调函数 |
---
### Input 输入框
文本输入框组件,支持密码输入。
#### 属性
| 属性 | 类型 | 默认值 | 描述 |
|-------------|-----------|---------|----------|
| name | `string` | - | 字段名称(必填) |
| label | `string` | - | 字段标签 |
| rule | `string` | - | 校验规则 |
| realtime | `boolean` | `false` | 是否实时校验 |
| placeholder | `string` | - | 占位文本 |
| disabled | `boolean` | `false` | 是否禁用 |
#### Input.Password 密码输入框
与 Input 属性相同,显示为密码输入框。
---
### InputNumber 数字输入框
#### 属性
| 属性 | 类型 | 默认值 | 描述 |
|-----------|------------|-----|----------|
| name | `string` | - | 字段名称(必填) |
| label | `string` | - | 字段标签 |
| rule | `string` | - | 校验规则 |
| min | `number` | - | 最小值 |
| max | `number` | - | 最大值 |
| step | `number` | `1` | 步长 |
| precision | `number` | - | 小数精度 |
| formatter | `function` | - | 格式化显示值 |
| parser | `function` | - | 解析输入值 |
---
### TextArea 多行文本
#### 属性
| 属性 | 类型 | 默认值 | 描述 |
|-----------|---------------------|---------|----------|
| name | `string` | - | 字段名称(必填) |
| label | `string` | - | 字段标签 |
| rule | `string` | - | 校验规则 |
| rows | `number` | `4` | 行数 |
| maxLength | `number` | - | 最大字符数 |
| showCount | `b