UNPKG

@kne/react-form-antd

Version:

把 @kne/react-form 表单校验逻辑应用到antd

1,241 lines (1,105 loc) 65.5 kB
# 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) => &#96;¥ ${value}&#96;.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) ? &#96;密码强度不足,当前强度: ${finalStrength}&#96; : '', 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(&#96;开始验证用户名: ${value}&#96;); await new Promise(resolve => setTimeout(resolve, 1000)); console.log(&#96;验证完成用户名: ${value}&#96;); 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={&#96;联系人 ${index + 1}&#96;} 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={&#96;成员 ${index + 1}&#96;} 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