diff --git a/README.md b/README.md
index cb5fbad..22e9eb2 100644
--- a/README.md
+++ b/README.md
@@ -1,80 +1,1524 @@
-
# 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 - 全局配置
-* UI分离,支持自定义UI框架。提供了antd的组件封装 @kne/react-form-antd 和 taro的组件封装 @kne/react-form-taro
-* 分级校验规则配置,校验规则支持异步校验
-* 事件驱动,方便灵活扩展。可以通过debug选项配置,通过触发事件顺序和参数轻松调试
-* 支持包含Group的复杂表单,子表单
+#### 快速开始
+
+```bash
+npm install @kne/react-form-antd antd
+```
+
+```jsx
+import Form, { Input, SubmitButton } from '@kne/react-form-antd';
+
+function MyForm() {
+ return (
+
+ );
+}
+```
+
+#### 校验规则
+
+```jsx
+ // 必填
+ // 长度2-10
+ // 组合规则
+```
### 示例
#### 示例代码
-- 这里填写示例标题
-- 这里填写示例说明
-- reactFormAntd(@kne/current-lib_react-form-antd),(@kne/current-lib_react-form-antd/dist/index.css),antd(antd)
+- 基础表单
+- 展示表单的基本使用方法,包含各种表单字段和校验规则
+- _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 (
+
+
+ {formData && (
+ <>
+ 提交数据
+ {JSON.stringify(formData, null, 2)}
+ >
+ )}
+
+ );
+};
+
+render();
+
+```
+
+- 表单类型和尺寸
+- 展示表单的不同类型(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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+render();
+
+```
+
+- 输入框组件
+- 展示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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {formData && (
+
+ {JSON.stringify(formData, null, 2)}
+
+ )}
+
+ );
+};
+
+render();
+
+```
+
+- 选择器组件
+- 展示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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {formData && (
+
+ {JSON.stringify(formData, null, 2)}
+
+ )}
+
+ );
+};
+
+render();
+
+```
+
+- 日期时间组件
+- 展示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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {formData && (
+
+ {JSON.stringify(formData, null, 2)}
+
+ )}
+
+ );
+};
+
+render();
+
+```
+
+- 多选和单选组件
+- 展示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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {formData && (
+
+ {JSON.stringify(formData, null, 2)}
+
+ )}
+
+ );
+};
+
+render();
+
+```
+
+- 评分和滑块组件
+- 展示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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {formData && (
+
+ {JSON.stringify(formData, null, 2)}
+
+ )}
+
+ );
+};
+
+render();
+
+```
+
+- 表单校验
+- 展示表单的各种校验规则和自定义校验
+- _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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {formData && (
+ {JSON.stringify(formData, null, 2)}
+ )}
+ );
+};
+
+render();
+
+```
+
+- 动态表单
+- 展示GroupList动态表单的使用方法
+- _ReactFormAntd(@kne/current-lib_react-form-antd),(@kne/current-lib_react-form-antd/dist/index.css),antd(antd)
```jsx
-const {Button} = antd;
-const {default: Form, Select, Input, Group, GroupList, SubmitButton, DatePickerToday, Rate, Slider} = reactFormAntd;
-const {useRef} = React;
-
-const Example = () => {
- const addButton = useRef();
- return
+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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {formData && (
+
+ {JSON.stringify(formData, null, 2)}
+
+ )}
+
+ );
};
-render();
+render();
```
+- 表单按钮
+- 展示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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {formData && (
+
+ {JSON.stringify(formData, null, 2)}
+
+ )}
+
+ );
+};
+
+render();
+
+```
### 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 | `boolean` | `false` | 是否显示字符数 |
+| autoSize | `boolean \| object` | `false` | 自适应高度 |
+
+---
+
+### Select 选择器
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|--------------|------------------------|---------|-------------------------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| options | `array` | - | 选项数据 `[{label, value}]` |
+| mode | `'multiple' \| 'tags'` | - | 模式 |
+| showSearch | `boolean` | `false` | 是否可搜索 |
+| filterOption | `function` | - | 搜索过滤函数 |
+
+---
+
+### TreeSelect 树选择
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|---------------|-----------|---------|----------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| treeData | `array` | - | 树形数据 |
+| treeCheckable | `boolean` | `false` | 是否可多选 |
+| showSearch | `boolean` | `false` | 是否可搜索 |
+
+---
+
+### Cascader 级联选择
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|------------|-----------|---------|----------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| options | `array` | - | 级联数据 |
+| showSearch | `boolean` | `false` | 是否可搜索 |
+
+---
+
+### DatePicker 日期选择
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|--------------|---------------------|----------------|----------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| format | `string` | `'YYYY-MM-DD'` | 日期格式 |
+| disabledDate | `function` | - | 禁用日期函数 |
+| showTime | `boolean \| object` | `false` | 是否显示时间选择 |
+
+#### DatePicker.RangePicker 日期范围
+
+#### DatePicker.MonthPicker 月份选择
+
+#### DatePicker.WeekPicker 周选择
+
+---
+
+### TimePicker 时间选择
+
+#### 属性
+| 属性 | 类型 | 默认值 | 描述 |
+|------------|----------|--------------|----------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| format | `string` | `'HH:mm:ss'` | 时间格式 |
+| hourStep | `number` | - | 小时步长 |
+| minuteStep | `number` | - | 分钟步长 |
+| secondStep | `number` | - | 秒步长 |
+
+#### TimePicker.RangePicker 时间范围
+
+---
+
+### DatePickerToday 快捷日期
+
+带有快捷选项的日期范围选择组件,支持"至今"选项。返回值为数组 `[startDate, endDate]`,当选择"至今"时,endDate 值为
+`soFarValue`。
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|------------|------------|-----------|--------------------------------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| soFarText | `string` | `'至今'` | "至今"按钮显示的文本 |
+| soFarValue | `string` | `'soFar'` | 选择"至今"时的结束日期值,可通过此值判断是否选择了至今 |
+| onChange | `function` | - | 值变化回调 `([start, end]) => void` |
+
+#### 返回值说明
+
+组件返回一个数组 `[startDate, endDate]`:
+
+- `startDate`: 开始日期,ISO 格式字符串(如 `'2024-01-01T00:00:00.000Z'`)
+- `endDate`: 结束日期,ISO 格式字符串,或 `soFarValue`(默认 `'soFar'`)
+
+---
+
+### Checkbox 复选框
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|----------|-----------|---------|----------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| disabled | `boolean` | `false` | 是否禁用 |
+
+---
+
+### CheckboxGroup 复选框组
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|----------|-----------|---------|-------------------------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| options | `array` | - | 选项数据 `[{label, value}]` |
+| disabled | `boolean` | `false` | 是否禁用 |
+
+---
+
+### RadioGroup 单选框组
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|-------------|-------------------------|-------------|-------------------------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| options | `array` | - | 选项数据 `[{label, value}]` |
+| optionType | `'default' \| 'button'` | `'default'` | 选项类型 |
+| buttonStyle | `'outline' \| 'solid'` | `'outline'` | 按钮样式 |
+
+---
+
+### Rate 评分
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|------------|-------------|---------|----------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| count | `number` | `5` | 星星数量 |
+| allowHalf | `boolean` | `false` | 是否允许半星 |
+| allowClear | `boolean` | `true` | 是否允许清除 |
+| character | `ReactNode` | - | 自定义字符 |
+| disabled | `boolean` | `false` | 是否禁用 |
+
+---
+
+### Slider 滑动条
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|----------|-----------|---------|----------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| min | `number` | `0` | 最小值 |
+| max | `number` | `100` | 最大值 |
+| step | `number` | `1` | 步长 |
+| range | `boolean` | `false` | 是否为范围选择 |
+| marks | `object` | - | 刻度标记 |
+| vertical | `boolean` | `false` | 是否垂直 |
+| disabled | `boolean` | `false` | 是否禁用 |
+
+---
+
+### Switch 开关
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|-------------------|------------------------|-------------|----------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| checkedChildren | `ReactNode` | - | 选中时的内容 |
+| unCheckedChildren | `ReactNode` | - | 未选中时的内容 |
+| disabled | `boolean` | `false` | 是否禁用 |
+| size | `'default' \| 'small'` | `'default'` | 开关尺寸 |
+
+---
+
+### SubmitButton 提交按钮
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|----------|-----------|-------------|------------------|
+| type | `string` | `'primary'` | 按钮类型 |
+| realtime | `boolean` | `false` | 是否实时校验(未通过时禁用按钮) |
+| disabled | `boolean` | `false` | 是否禁用 |
+| loading | `boolean` | `false` | 是否加载中 |
+
+---
+
+### ResetButton 重置按钮
+
+#### 属性
+
+继承 Ant Design Button 的所有属性。
+
+---
+
+### CancelButton 取消按钮
+
+用于清除表单缓存,需配合 Form 的 cache 属性使用。
+
+#### 属性
+
+继承 Ant Design Button 的所有属性。
+
+| 属性 | 类型 | 默认值 | 描述 |
+|---------|------------|-----|------|
+| onClick | `function` | - | 点击回调 |
+
+---
+
+### GroupList 动态表单列表
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|---------------|----------|-----|----------|
+| name | `string` | - | 字段名称(必填) |
+| defaultLength | `number` | `0` | 默认项数 |
+
+#### 子组件参数
+
+| 参数 | 类型 | 描述 |
+|----------|------------|-------|
+| index | `number` | 当前项索引 |
+| onRemove | `function` | 删除当前项 |
+| onAdd | `function` | 添加新项 |
+
+---
+
+### preset 预设配置
+
+用于全局配置表单规则和默认属性。
+
+```javascript
+import {preset} from '@kne/react-form-antd';
+
+preset({
+ type: 'default', // 默认表单类型
+ size: 'middle', // 默认表单尺寸
+ rules: { // 自定义校验规则
+ PHONE: {
+ reg: /^1[3-9]\d{9}$/,
+ message: '请输入正确的手机号码'
+ }
+ },
+ field: { // 字段默认属性
+ Input: {
+ defaultProps: {
+ autoComplete: 'off'
+ }
+ }
+ }
+});
+```
diff --git a/doc/api.md b/doc/api.md
index e69de29..dda4412 100644
--- a/doc/api.md
+++ b/doc/api.md
@@ -0,0 +1,352 @@
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|---------------|------------------------------------|---------------------|--------------------------------|
+| 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 | `boolean` | `false` | 是否显示字符数 |
+| autoSize | `boolean \| object` | `false` | 自适应高度 |
+
+---
+
+### Select 选择器
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|--------------|------------------------|---------|-------------------------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| options | `array` | - | 选项数据 `[{label, value}]` |
+| mode | `'multiple' \| 'tags'` | - | 模式 |
+| showSearch | `boolean` | `false` | 是否可搜索 |
+| filterOption | `function` | - | 搜索过滤函数 |
+
+---
+
+### TreeSelect 树选择
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|---------------|-----------|---------|----------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| treeData | `array` | - | 树形数据 |
+| treeCheckable | `boolean` | `false` | 是否可多选 |
+| showSearch | `boolean` | `false` | 是否可搜索 |
+
+---
+
+### Cascader 级联选择
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|------------|-----------|---------|----------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| options | `array` | - | 级联数据 |
+| showSearch | `boolean` | `false` | 是否可搜索 |
+
+---
+
+### DatePicker 日期选择
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|--------------|---------------------|----------------|----------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| format | `string` | `'YYYY-MM-DD'` | 日期格式 |
+| disabledDate | `function` | - | 禁用日期函数 |
+| showTime | `boolean \| object` | `false` | 是否显示时间选择 |
+
+#### DatePicker.RangePicker 日期范围
+
+#### DatePicker.MonthPicker 月份选择
+
+#### DatePicker.WeekPicker 周选择
+
+---
+
+### TimePicker 时间选择
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|------------|----------|--------------|----------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| format | `string` | `'HH:mm:ss'` | 时间格式 |
+| hourStep | `number` | - | 小时步长 |
+| minuteStep | `number` | - | 分钟步长 |
+| secondStep | `number` | - | 秒步长 |
+
+#### TimePicker.RangePicker 时间范围
+
+---
+
+### DatePickerToday 快捷日期
+
+带有快捷选项的日期范围选择组件,支持"至今"选项。返回值为数组 `[startDate, endDate]`,当选择"至今"时,endDate 值为
+`soFarValue`。
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|------------|------------|-----------|--------------------------------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| soFarText | `string` | `'至今'` | "至今"按钮显示的文本 |
+| soFarValue | `string` | `'soFar'` | 选择"至今"时的结束日期值,可通过此值判断是否选择了至今 |
+| onChange | `function` | - | 值变化回调 `([start, end]) => void` |
+
+#### 返回值说明
+
+组件返回一个数组 `[startDate, endDate]`:
+
+- `startDate`: 开始日期,ISO 格式字符串(如 `'2024-01-01T00:00:00.000Z'`)
+- `endDate`: 结束日期,ISO 格式字符串,或 `soFarValue`(默认 `'soFar'`)
+
+---
+
+### Checkbox 复选框
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|----------|-----------|---------|----------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| disabled | `boolean` | `false` | 是否禁用 |
+
+---
+
+### CheckboxGroup 复选框组
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|----------|-----------|---------|-------------------------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| options | `array` | - | 选项数据 `[{label, value}]` |
+| disabled | `boolean` | `false` | 是否禁用 |
+
+---
+
+### RadioGroup 单选框组
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|-------------|-------------------------|-------------|-------------------------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| options | `array` | - | 选项数据 `[{label, value}]` |
+| optionType | `'default' \| 'button'` | `'default'` | 选项类型 |
+| buttonStyle | `'outline' \| 'solid'` | `'outline'` | 按钮样式 |
+
+---
+
+### Rate 评分
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|------------|-------------|---------|----------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| count | `number` | `5` | 星星数量 |
+| allowHalf | `boolean` | `false` | 是否允许半星 |
+| allowClear | `boolean` | `true` | 是否允许清除 |
+| character | `ReactNode` | - | 自定义字符 |
+| disabled | `boolean` | `false` | 是否禁用 |
+
+---
+
+### Slider 滑动条
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|----------|-----------|---------|----------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| min | `number` | `0` | 最小值 |
+| max | `number` | `100` | 最大值 |
+| step | `number` | `1` | 步长 |
+| range | `boolean` | `false` | 是否为范围选择 |
+| marks | `object` | - | 刻度标记 |
+| vertical | `boolean` | `false` | 是否垂直 |
+| disabled | `boolean` | `false` | 是否禁用 |
+
+---
+
+### Switch 开关
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|-------------------|------------------------|-------------|----------|
+| name | `string` | - | 字段名称(必填) |
+| label | `string` | - | 字段标签 |
+| rule | `string` | - | 校验规则 |
+| checkedChildren | `ReactNode` | - | 选中时的内容 |
+| unCheckedChildren | `ReactNode` | - | 未选中时的内容 |
+| disabled | `boolean` | `false` | 是否禁用 |
+| size | `'default' \| 'small'` | `'default'` | 开关尺寸 |
+
+---
+
+### SubmitButton 提交按钮
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|----------|-----------|-------------|------------------|
+| type | `string` | `'primary'` | 按钮类型 |
+| realtime | `boolean` | `false` | 是否实时校验(未通过时禁用按钮) |
+| disabled | `boolean` | `false` | 是否禁用 |
+| loading | `boolean` | `false` | 是否加载中 |
+
+---
+
+### ResetButton 重置按钮
+
+#### 属性
+
+继承 Ant Design Button 的所有属性。
+
+---
+
+### CancelButton 取消按钮
+
+用于清除表单缓存,需配合 Form 的 cache 属性使用。
+
+#### 属性
+
+继承 Ant Design Button 的所有属性。
+
+| 属性 | 类型 | 默认值 | 描述 |
+|---------|------------|-----|------|
+| onClick | `function` | - | 点击回调 |
+
+---
+
+### GroupList 动态表单列表
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|---------------|----------|-----|----------|
+| name | `string` | - | 字段名称(必填) |
+| defaultLength | `number` | `0` | 默认项数 |
+
+#### 子组件参数
+
+| 参数 | 类型 | 描述 |
+|----------|------------|-------|
+| index | `number` | 当前项索引 |
+| onRemove | `function` | 删除当前项 |
+| onAdd | `function` | 添加新项 |
+
+---
+
+### preset 预设配置
+
+用于全局配置表单规则和默认属性。
+
+```javascript
+import {preset} from '@kne/react-form-antd';
+
+preset({
+ type: 'default', // 默认表单类型
+ size: 'middle', // 默认表单尺寸
+ rules: { // 自定义校验规则
+ PHONE: {
+ reg: /^1[3-9]\d{9}$/,
+ message: '请输入正确的手机号码'
+ }
+ },
+ field: { // 字段默认属性
+ Input: {
+ defaultProps: {
+ autoComplete: 'off'
+ }
+ }
+ }
+});
+```
diff --git a/doc/base.js b/doc/base.js
index e9a172e..1a2f6d5 100644
--- a/doc/base.js
+++ b/doc/base.js
@@ -1,38 +1,59 @@
-const {Button} = antd;
-const {default: Form, Select, Input, Group, GroupList, SubmitButton, DatePickerToday, Rate, Slider} = reactFormAntd;
-const {useRef} = React;
+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 Example = () => {
- const addButton = useRef();
- return
+const BaseExample = () => {
+ const [formData, setFormData] = useState(null);
+ return (
+
+
+ {formData && (
+ <>
+ 提交数据
+ {JSON.stringify(formData, null, 2)}
+ >
+ )}
+
+ );
};
-render();
+render();
diff --git a/doc/buttons.js b/doc/buttons.js
new file mode 100644
index 0000000..881bb44
--- /dev/null
+++ b/doc/buttons.js
@@ -0,0 +1,97 @@
+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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {formData && (
+
+ {JSON.stringify(formData, null, 2)}
+
+ )}
+
+ );
+};
+
+render();
diff --git a/doc/checkbox-radio.js b/doc/checkbox-radio.js
new file mode 100644
index 0000000..d4f6b01
--- /dev/null
+++ b/doc/checkbox-radio.js
@@ -0,0 +1,113 @@
+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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {formData && (
+
+ {JSON.stringify(formData, null, 2)}
+
+ )}
+
+ );
+};
+
+render();
diff --git a/doc/date-picker.js b/doc/date-picker.js
new file mode 100644
index 0000000..917183e
--- /dev/null
+++ b/doc/date-picker.js
@@ -0,0 +1,74 @@
+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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {formData && (
+
+ {JSON.stringify(formData, null, 2)}
+
+ )}
+
+ );
+};
+
+render();
diff --git a/doc/example.json b/doc/example.json
index 0d0b059..20aab7d 100644
--- a/doc/example.json
+++ b/doc/example.json
@@ -2,12 +2,174 @@
"isFull": false,
"list": [
{
- "title": "这里填写示例标题",
- "description": "这里填写示例说明",
+ "title": "基础表单",
+ "description": "展示表单的基本使用方法,包含各种表单字段和校验规则",
"code": "./base.js",
"scope": [
{
- "name": "reactFormAntd",
+ "name": "_ReactFormAntd",
+ "packageName": "@kne/current-lib_react-form-antd"
+ },
+ {
+ "packageName": "@kne/current-lib_react-form-antd/dist/index.css"
+ },
+ {
+ "name": "antd",
+ "packageName": "antd"
+ }
+ ]
+ },
+ {
+ "title": "表单类型和尺寸",
+ "description": "展示表单的不同类型(default/inline/inner)和尺寸(small/middle/large)",
+ "code": "./form-type.js",
+ "scope": [
+ {
+ "name": "_ReactFormAntd",
+ "packageName": "@kne/current-lib_react-form-antd"
+ },
+ {
+ "packageName": "@kne/current-lib_react-form-antd/dist/index.css"
+ },
+ {
+ "name": "antd",
+ "packageName": "antd"
+ }
+ ]
+ },
+ {
+ "title": "输入框组件",
+ "description": "展示Input和InputNumber组件的各种用法",
+ "code": "./input.js",
+ "scope": [
+ {
+ "name": "_ReactFormAntd",
+ "packageName": "@kne/current-lib_react-form-antd"
+ },
+ {
+ "packageName": "@kne/current-lib_react-form-antd/dist/index.css"
+ },
+ {
+ "name": "antd",
+ "packageName": "antd"
+ }
+ ]
+ },
+ {
+ "title": "选择器组件",
+ "description": "展示Select、TreeSelect、Cascader组件的使用方法",
+ "code": "./select.js",
+ "scope": [
+ {
+ "name": "_ReactFormAntd",
+ "packageName": "@kne/current-lib_react-form-antd"
+ },
+ {
+ "packageName": "@kne/current-lib_react-form-antd/dist/index.css"
+ },
+ {
+ "name": "antd",
+ "packageName": "antd"
+ }
+ ]
+ },
+ {
+ "title": "日期时间组件",
+ "description": "展示DatePicker、TimePicker、DatePickerToday组件的使用方法",
+ "code": "./date-picker.js",
+ "scope": [
+ {
+ "name": "_ReactFormAntd",
+ "packageName": "@kne/current-lib_react-form-antd"
+ },
+ {
+ "packageName": "@kne/current-lib_react-form-antd/dist/index.css"
+ },
+ {
+ "name": "antd",
+ "packageName": "antd"
+ }
+ ]
+ },
+ {
+ "title": "多选和单选组件",
+ "description": "展示Checkbox、CheckboxGroup、RadioGroup组件的使用方法",
+ "code": "./checkbox-radio.js",
+ "scope": [
+ {
+ "name": "_ReactFormAntd",
+ "packageName": "@kne/current-lib_react-form-antd"
+ },
+ {
+ "packageName": "@kne/current-lib_react-form-antd/dist/index.css"
+ },
+ {
+ "name": "antd",
+ "packageName": "antd"
+ }
+ ]
+ },
+ {
+ "title": "评分和滑块组件",
+ "description": "展示Rate、Slider、Switch组件的使用方法",
+ "code": "./rate-slider.js",
+ "scope": [
+ {
+ "name": "_ReactFormAntd",
+ "packageName": "@kne/current-lib_react-form-antd"
+ },
+ {
+ "packageName": "@kne/current-lib_react-form-antd/dist/index.css"
+ },
+ {
+ "name": "antd",
+ "packageName": "antd"
+ }
+ ]
+ },
+ {
+ "title": "表单校验",
+ "description": "展示表单的各种校验规则和自定义校验",
+ "code": "./validation.js",
+ "scope": [
+ {
+ "name": "_ReactFormAntd",
+ "packageName": "@kne/current-lib_react-form-antd"
+ },
+ {
+ "packageName": "@kne/current-lib_react-form-antd/dist/index.css"
+ },
+ {
+ "name": "antd",
+ "packageName": "antd"
+ }
+ ]
+ },
+ {
+ "title": "动态表单",
+ "description": "展示GroupList动态表单的使用方法",
+ "code": "./group-list.js",
+ "scope": [
+ {
+ "name": "_ReactFormAntd",
+ "packageName": "@kne/current-lib_react-form-antd"
+ },
+ {
+ "packageName": "@kne/current-lib_react-form-antd/dist/index.css"
+ },
+ {
+ "name": "antd",
+ "packageName": "antd"
+ }
+ ]
+ },
+ {
+ "title": "表单按钮",
+ "description": "展示SubmitButton、ResetButton、CancelButton的使用方法",
+ "code": "./buttons.js",
+ "scope": [
+ {
+ "name": "_ReactFormAntd",
"packageName": "@kne/current-lib_react-form-antd"
},
{
diff --git a/doc/form-type.js b/doc/form-type.js
new file mode 100644
index 0000000..adfb60e
--- /dev/null
+++ b/doc/form-type.js
@@ -0,0 +1,52 @@
+const {default: Form, Input, SubmitButton} = _ReactFormAntd;
+const {Flex, Card, Space} = antd;
+
+const FormTypeExample = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+render();
diff --git a/doc/group-list.js b/doc/group-list.js
new file mode 100644
index 0000000..c414470
--- /dev/null
+++ b/doc/group-list.js
@@ -0,0 +1,112 @@
+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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {formData && (
+
+ {JSON.stringify(formData, null, 2)}
+
+ )}
+
+ );
+};
+
+render();
diff --git a/doc/input.js b/doc/input.js
new file mode 100644
index 0000000..55b60f9
--- /dev/null
+++ b/doc/input.js
@@ -0,0 +1,63 @@
+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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {formData && (
+
+ {JSON.stringify(formData, null, 2)}
+
+ )}
+
+ );
+};
+
+render();
diff --git a/doc/rate-slider.js b/doc/rate-slider.js
new file mode 100644
index 0000000..cefbf79
--- /dev/null
+++ b/doc/rate-slider.js
@@ -0,0 +1,62 @@
+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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {formData && (
+
+ {JSON.stringify(formData, null, 2)}
+
+ )}
+
+ );
+};
+
+render();
diff --git a/doc/select.js b/doc/select.js
new file mode 100644
index 0000000..d6dc243
--- /dev/null
+++ b/doc/select.js
@@ -0,0 +1,207 @@
+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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {formData && (
+
+ {JSON.stringify(formData, null, 2)}
+
+ )}
+
+ );
+};
+
+render();
diff --git a/doc/summary.md b/doc/summary.md
index 3a720cc..9d5cda6 100644
--- a/doc/summary.md
+++ b/doc/summary.md
@@ -1,6 +1,76 @@
-#### 特点
+基于 @kne/react-form 封装的 Ant Design 表单组件库,提供完整的表单校验功能和丰富的表单字段组件。
-* UI分离,支持自定义UI框架。提供了antd的组件封装 @kne/react-form-antd 和 taro的组件封装 @kne/react-form-taro
-* 分级校验规则配置,校验规则支持异步校验
-* 事件驱动,方便灵活扩展。可以通过debug选项配置,通过触发事件顺序和参数轻松调试
-* 支持包含Group的复杂表单,子表单
+#### 核心特性
+
+- **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 (
+
+ );
+}
+```
+
+#### 校验规则
+
+```jsx
+ // 必填
+ // 长度2-10
+ // 组合规则
+```
diff --git a/doc/validation.js b/doc/validation.js
new file mode 100644
index 0000000..44e18ee
--- /dev/null
+++ b/doc/validation.js
@@ -0,0 +1,155 @@
+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 (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {formData && (
+ {JSON.stringify(formData, null, 2)}
+ )}
+ );
+};
+
+render();
diff --git a/package.json b/package.json
index bad4318..79a3dc8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@kne/react-form-antd",
- "version": "4.0.3",
+ "version": "4.1.0",
"syntax": {
"esmodules": true
},
@@ -60,8 +60,9 @@
"react": ">=18.x"
},
"dependencies": {
- "@kne/react-form": "^3.1.2",
+ "@kne/react-form": "^3.1.6",
"@kne/react-form-helper": "^3.0.2",
+ "@kne/react-intl": "^0.1.12",
"@kne/use-control-value": "^0.1.1",
"classnames": "^2.2.6",
"dayjs": "^1.11.7",
@@ -70,7 +71,6 @@
"devDependencies": {
"@kne/microbundle": "^0.15.5",
"@kne/modules-dev": "^2.1.2",
- "@kne/react-form": "^3.1.0",
"cross-env": "^7.0.3",
"husky": "^9.0.11",
"npm-run-all": "^4.1.5",
diff --git a/prompts/prompts-frontend-libs/README.md b/prompts/prompts-frontend-libs/README.md
new file mode 100644
index 0000000..33fe5be
--- /dev/null
+++ b/prompts/prompts-frontend-libs/README.md
@@ -0,0 +1,132 @@
+# Prompts Frontend Libs 文档索引
+
+本项目包含多个前端开发辅助提示词文档,用于指导 AI 完成常见的前端组件库开发任务。
+
+## 文档列表
+
+### 1. 组件国际化
+
+**功能**: 指导将 React 组件完成国际化,支持多语言切换
+
+**适用场景**:
+- 组件需要支持多语言显示
+- 项目需要面向国际用户
+- 现有组件需要添加国际化支持
+
+**核心内容**:
+- useIntl Hook 使用方法
+- withLocale HOC 包裹模式
+- 语言包配置规范
+- 不同组件类型的国际化处理方式
+
+**使用方式**: 阅读 [国际化](prompts/国际化.md) 了解详情
+
+---
+
+### 2. 添加 TypeScript 类型声明
+
+**功能**: 为现有的 React 组件库添加完整的 TypeScript 类型定义
+
+**适用场景**:
+- JavaScript 组件库需要添加 TypeScript 支持
+- 提升代码可维护性和 IDE 智能提示
+- 便于团队协作和代码审查
+
+**核心内容**:
+- Props 接口定义规范
+- 类型声明文件结构
+- 复杂场景处理(嵌套对象、函数属性、联合类型)
+- 组件导出声明模板
+
+**使用方式**: 阅读 [添加ts类型声明](prompts/添加ts类型声明.md) 了解详情
+
+---
+
+### 3. 完善 package.json 描述和关键词
+
+**功能**: 为项目完善 package.json 的 description 和 keywords 字段
+
+**适用场景**:
+- 新建项目需要完善 package.json 信息
+- 项目功能更新后需要调整描述
+- 提升 npm 包的搜索可发现性
+
+**核心内容**:
+- 描述撰写原则和模板
+- 关键词分类和提取来源
+- 信息收集和分析流程
+- 输出格式和检查清单
+
+**使用方式**: 阅读 [完善package.json描述和关键词](prompts/完善package.json描述和关键词.md) 了解详情
+
+---
+
+### 4. 生成包功能描述文件
+
+**功能**: 为 JavaScript/TypeScript 包生成 package-manifest.json 描述文件
+
+**适用场景**:
+- 自动化 API 文档生成
+- IDE 智能提示和自动补全
+- 包管理和分发平台的组件展示
+- 第三方工具的集成和分析
+
+**核心内容**:
+- 项目结构分析流程
+- 导出模块识别方法
+- JSON 结构构建规范
+- 类型定义标准和描述编写原则
+
+**使用方式**: 阅读 [生成包功能描述文件](prompts/生成包功能描述文件.md) 了解详情
+
+---
+
+### 5. 生成文档
+
+**功能**: 为项目生成标准化的 API 文档和概述文档
+
+**适用场景**:
+- 需要生成项目概述文档 (summary.md)
+- 需要生成 API 文档 (api.md)
+- 规范化项目文档结构
+
+**核心内容**:
+- 文档结构规范
+- 标题级别使用规范
+- 表格格式要求
+- 内容基于实际代码分析的原则
+
+**使用方式**: 阅读 [生成文档](prompts/生成文档.md) 了解详情
+
+---
+
+### 6. 组件示例编写
+
+**功能**: 编写遵循特定目录结构规范的 React 组件示例
+
+**适用场景**:
+- 为组件创建可运行的示例代码
+- 编写组件演示和文档
+- 构建组件库的示例展示系统
+
+**核心内容**:
+- 项目目录布局规范
+- example.json 配置结构
+- 示例代码编写模板
+- scope 依赖声明规则
+- 数据模拟方法
+
+**使用方式**: 阅读 [组件示例编写](prompts/组件示例编写.md) 了解详情
+
+---
+
+## 如何选择
+
+| 需求 | 使用文档 |
+|------|----------|
+| 组件需要支持多语言 | 组件国际化 |
+| 为 JS 组件库添加 TS 类型支持 | 添加 TypeScript 类型声明 |
+| 完善 package.json 描述和关键词 | 完善 package.json 描述和关键词 |
+| 生成包的 API 描述文件 | 生成包功能描述文件 |
+| 生成项目概述和 API 文档 | 生成文档 |
+| 编写组件演示示例代码 | 组件示例编写 |
diff --git "a/prompts/prompts-frontend-libs/\345\233\275\351\231\205\345\214\226.md" "b/prompts/prompts-frontend-libs/\345\233\275\351\231\205\345\214\226.md"
new file mode 100644
index 0000000..9ec750e
--- /dev/null
+++ "b/prompts/prompts-frontend-libs/\345\233\275\351\231\205\345\214\226.md"
@@ -0,0 +1,217 @@
+# 组件国际化指南
+
+## 概述
+
+本指南用于将组件完成国际化。
+
+## 一、创建的文件
+
+1. **`src/withLocale.js`** - 统一的国际化 HOC,所有组件共用
+2. **`src/locale/zh-CN.js`** - 中文语言包,所有组件共用
+3. **`src/locale/en-US.js`** - 英文语言包,所有组件共用
+
+## 二、需要修改的文件类型
+
+### 主组件文件
+- 添加 `useIntl` Hook
+- 用 `withLocale` 包裹导出
+
+### FormInner 表单组件
+- 添加 `useIntl` Hook
+- 用 `withLocale` 包裹导出
+- 表单 label 国际化
+
+### getColumns 等工具函数
+- 通过参数接收 `formatMessage`
+- 移除内部的 `useIntl` 和 `withLocale` 引入
+
+### Action 操作组件
+- 添加 `useIntl` Hook
+- 用 `withLocale` 包裹导出
+
+## 三、国际化的关键模式
+
+### 1. useIntl Hook 使用
+```javascript
+import { useIntl } from '@kne/react-intl';
+
+const Component = () => {
+ const { formatMessage } = useIntl();
+ return {formatMessage({ id: 'ComponentName.Key' })}
;
+};
+```
+
+### 2. withLocale 包裹普通组件
+```javascript
+import withLocale from '../withLocale';
+import { useIntl } from '@kne/react-intl';
+
+const ComponentInner = ({ ...props }) => {
+ const { formatMessage } = useIntl();
+ // 将所有中文替换为 formatMessage({ id: 'ComponentName.Key' })
+ return {formatMessage({ id: 'ComponentName.Key' })}
;
+};
+
+const Component = withLocale(ComponentInner);
+
+export default Component;
+```
+
+### 3. createWithRemoteLoader 组件(推荐格式)
+```javascript
+import withLocale from '../withLocale';
+import { useIntl } from '@kne/react-intl';
+
+const ComponentInner = createWithRemoteLoader({...})(({ remoteModules, ...props }) => {
+ const { formatMessage } = useIntl();
+ // ...
+});
+
+const Component = withLocale(ComponentInner);
+
+export default Component;
+```
+
+**注意:** 对于 `createWithRemoteLoader` 创建的组件,可以先用 `ComponentInner` 存储,再用 `withLocale` 包裹。
+
+### 4. getColumns 等工具函数(formatMessage 从父组件传入)
+```javascript
+const getColumns = ({formatMessage}) => {
+ return [
+ {
+ name: 'name',
+ title: formatMessage({ id: 'ComponentName.Key' })
+ }
+ ];
+};
+
+// 父组件中使用
+const columns = getColumns({formatMessage});
+```
+
+### 5. 带参数的翻译
+```javascript
+formatMessage({ id: 'ComponentName.KeyWithParam' }, { name: value })
+```
+
+## 四、注意事项
+
+1. **所有使用 `useIntl` 的组件必须用 `withLocale` 包裹**
+2. **`getColumns` 等工具函数通过参数接收 `formatMessage`,不使用 `useIntl`**
+3. **语言包中避免重复的 key**,命名规则:`组件名 + 功能名`,如 `PersonalCard.status.online`
+4. **`createWithRemoteLoader` 创建的组件内部使用 useIntl 时,外层需要重命名并用 withLocale 包裹**
+5. 注意检查 `withLocale`文件的引用地址,根目录组件使用 `../withLocale`
+
+---
+
+# 组件国际化操作步骤
+
+## 步骤
+
+### 1. 更新语言包文件
+在 `src/locale/zh-CN.js` 和 `src/locale/en-US.js` 中添加对应的翻译文本:
+```javascript
+// zh-CN.js
+const locale = {
+ ComponentName: {
+ status: {
+ online: '在线',
+ offline: '离线'
+ },
+ summary: '简介'
+ }
+};
+
+// en-US.js
+const locale = {
+ ComponentName: {
+ status: {
+ online: 'Online',
+ offline: 'Offline'
+ },
+ summary: 'Summary'
+ }
+};
+```
+
+### 2. 修改组件文件
+
+#### 主组件修改模式:
+```javascript
+import withLocale from '../withLocale';
+import { useIntl } from '@kne/react-intl';
+
+const ComponentInner = ({ ...props }) => {
+ const { formatMessage } = useIntl();
+ // 将所有中文替换为 formatMessage({ id: 'ComponentName.Key' })
+ return (
+ // ...
+ );
+};
+
+const Component = withLocale(ComponentInner);
+
+export default Component;
+```
+
+#### FormInner 修改模式:
+```javascript
+import withLocale from '../withLocale';
+import { useIntl } from '@kne/react-intl';
+
+const FormInnerInner = createWithRemoteLoader({...})(({ remoteModules, ...props }) => {
+ const { formatMessage } = useIntl();
+ // label={formatMessage({ id: 'ComponentName.Key' })}
+ // ...
+});
+
+const FormInner = withLocale(FormInnerInner);
+
+export default FormInner;
+```
+
+#### Action 操作组件修改模式:
+```javascript
+import withLocale from '../withLocale';
+import { useIntl } from '@kne/react-intl';
+
+const ActionInner = createWithRemoteLoader({...})(({ remoteModules, ...props }) => {
+ const { formatMessage } = useIntl();
+ // ...
+});
+
+const ActionComponent = withLocale(ActionInner);
+
+export default ActionComponent;
+```
+
+#### getColumns 修改模式:
+```javascript
+// 移除 useIntl 和 withLocale 引入
+const getColumns = ({formatMessage}) => {
+ return [
+ {
+ name: 'xxx',
+ title: formatMessage({ id: 'ComponentName.Key' })
+ }
+ ];
+};
+```
+
+父组件中调用:`getColumns({formatMessage})`
+
+### 3. 语言包 key 命名规范
+- 避免重复,使用 `组件名+功能名` 格式,如 `PersonalCard.status.online`、`UserName`、`UserRole`、`SettingType`
+- 中文和英文语言包保持完全一致的 key 结构
+
+### 4. 检查要点
+- [ ] 所有使用 `useIntl` 的组件都用 `withLocale` 包裹
+- [ ] `getColumns` 等工具函数通过参数接收 `formatMessage`
+- [ ] 语言包中无重复 key
+- [ ] `withLocale` 引用路径正确(组件在子目录使用 `../withLocale`)
+
+### 5. 最后检查
+运行命令找到所有使用 useIntl 的文件,确保都已正确包裹:
+```bash
+grep -r "useIntl" src/ --include="*.js" -l
+```
diff --git "a/prompts/prompts-frontend-libs/\345\256\214\345\226\204package.json\346\217\217\350\277\260\345\222\214\345\205\263\351\224\256\350\257\215.md" "b/prompts/prompts-frontend-libs/\345\256\214\345\226\204package.json\346\217\217\350\277\260\345\222\214\345\205\263\351\224\256\350\257\215.md"
new file mode 100644
index 0000000..b6438cc
--- /dev/null
+++ "b/prompts/prompts-frontend-libs/\345\256\214\345\226\204package.json\346\217\217\350\277\260\345\222\214\345\205\263\351\224\256\350\257\215.md"
@@ -0,0 +1,142 @@
+# 完善 package.json 描述和关键词
+
+## 任务描述
+
+为 JavaScript/TypeScript 包项目的 `package.json` 文件完善 `description` 和 `keywords` 字段,使其准确反映项目的功能定位和核心特性。
+
+## 实施步骤
+
+### 1. 收集信息
+
+#### 1.1 分析文档目录
+- 阅读 `doc/api.md` 了解组件 API 和功能列表
+- 阅读 `README.md` 了解项目概述和主要特性
+- 查看 `doc/example.json` 了解示例配置
+
+#### 1.2 分析源码结构
+- 查看 `src/` 目录结构,识别导出的组件和模块
+- 分析主入口文件 `src/index.js` 的导出内容
+- 检查组件的 props 和功能特性
+
+#### 1.3 分析依赖关系
+- 查看 `package.json` 中的 `peerDependencies` 了解技术栈
+- 查看 `dependencies` 了解核心依赖库
+
+### 2. 撰写 description
+
+#### 2.1 描述结构
+```
+[核心定位] + [主要功能] + [适用场景]
+```
+
+#### 2.2 撰写原则
+- **简洁明了**:控制在 1-2 句话,不超过 100 个字符
+- **突出特色**:强调项目的独特价值
+- **用户视角**:从使用者角度描述功能
+- **避免技术细节**:不包含实现细节
+
+#### 2.3 描述模板
+
+**React 组件库模板**:
+```
+一个用于 [场景] 的 React 组件库,提供 [核心组件/功能],支持 [特性列表]。
+```
+
+**工具库模板**:
+```
+用于 [场景] 的 [类型] 工具库,提供 [核心功能],支持 [特性列表]。
+```
+
+### 3. 生成 keywords
+
+#### 3.1 关键词分类
+
+| 分类 | 示例 | 说明 |
+|------|------|------|
+| 核心功能 | `select`, `picker`, `dropdown` | 描述主要功能 |
+| 技术栈 | `react`, `antd`, `typescript` | 依赖的技术框架 |
+| 组件类型 | `component`, `ui`, `form` | 组件分类 |
+| 数据结构 | `tree`, `table`, `list` | 支持的数据展示形式 |
+| 特性 | `multiselect`, `searchable`, `i18n` | 核心特性标签 |
+| 场景 | `enterprise`, `admin`, `dashboard` | 适用场景 |
+
+#### 3.2 关键词数量
+- 建议数量:5-15 个
+- 按重要性排序
+- 包含英文和常见缩写
+
+#### 3.3 关键词提取来源
+
+1. **组件名称**:从导出的组件名称提取核心词汇
+2. **功能特性**:从支持的功能特性提取标签
+3. **技术依赖**:从 peerDependencies 和 dependencies 提取技术栈
+4. **使用场景**:从项目定位提取适用场景
+
+### 4. 输出格式
+
+#### 4.1 JSON 格式
+```json
+{
+ "description": "完善的描述内容",
+ "keywords": [
+ "keyword1",
+ "keyword2",
+ "keyword3"
+ ]
+}
+```
+
+#### 4.2 直接修改 package.json
+将生成的内容更新到 `package.json` 的对应字段。
+
+## 示例
+
+### 示例分析流程
+
+**步骤一:源码分析**
+- 识别 `src/index.js` 导出的所有模块和组件
+- 分析组件的 props、功能特性
+- 确定技术栈(如 React、Vue、TypeScript 等)
+
+**步骤二:文档分析**
+- 阅读 `README.md` 获取项目概述
+- 分析 `doc/api.md` 了解 API 功能列表
+- 查看示例代码理解使用场景
+
+**步骤三:生成结果**
+- 根据 分析结果撰写 description
+- 从组件功能、技术栈、特性等维度提取 keywords
+- 输出 JSON 格式的 description 和 keywords
+
+**示例输出格式**:
+```json
+{
+ "description": "一个用于 [场景] 的 React 组件库,提供 [核心功能],支持 [特性列表]",
+ "keywords": [
+ "react",
+ "component",
+ "功能关键词",
+ "技术栈关键词",
+ "特性关键词"
+ ]
+}
+```
+
+## 检查清单
+
+- [ ] description 长度适中(不超过 100 字符)
+- [ ] description 包含核心功能和特色
+- [ ] keywords 包含技术栈标签
+- [ ] keywords 包含功能标签
+- [ ] keywords 按重要性排序
+- [ ] 没有重复或冗余的关键词
+- [ ] 关键词使用小写字母
+- [ ] 关键词使用连字符分隔单词(如 `tree-select`)
+
+## 注意事项
+
+1. **避免过度宣传**:描述应客观准确,不使用夸张词汇
+2. **保持一致性**:关键词应与项目实际功能一致
+3. **考虑搜索优化**:使用 npm 搜索中常见的关键词
+4. **参考同类项目**:可参考 npm 上类似项目的关键词设置
+5. **定期更新**:当项目功能变化时,同步更新描述和关键词
diff --git "a/prompts/prompts-frontend-libs/\346\267\273\345\212\240ts\347\261\273\345\236\213\345\243\260\346\230\216.md" "b/prompts/prompts-frontend-libs/\346\267\273\345\212\240ts\347\261\273\345\236\213\345\243\260\346\230\216.md"
new file mode 100644
index 0000000..f6117f6
--- /dev/null
+++ "b/prompts/prompts-frontend-libs/\346\267\273\345\212\240ts\347\261\273\345\236\213\345\243\260\346\230\216.md"
@@ -0,0 +1,103 @@
+# 为React组件库添加TypeScript类型定义的通用提示词
+
+## 任务描述
+
+为一个已有的React组件库添加完整的TypeScript类型定义,包括所有组件的Props接口、类型声明和导出。
+
+## 实施步骤
+
+### 1. 创建类型定义文件
+
+在源代码目录创建`index.d.ts`文件,位置与主入口文件`index.js`同级。
+
+### 2. 分析组件结构
+
+- 查看所有组件文件的Props参数
+- 确定每个组件的输入属性类型
+- 识别回调函数的参数和返回值类型
+
+### 3. 定义基础类型
+
+```typescript
+import { ReactNode, ComponentType, FC } from 'react';
+```
+
+### 4. 为每个组件创建Props接口
+
+- 组件名 + Props 作为接口名(如:FormInfoProps)
+- 所有可选属性使用`?`标记
+- ReactNode类型用于支持字符串和JSX元素
+- 函数类型明确定义参数和返回值
+- 使用`[key: string]: any`支持动态属性
+
+### 5. 处理复杂场景
+
+- **嵌套对象属性**:定义内联对象类型
+- **函数属性**:明确参数类型和返回值类型
+- **联合类型**:使用`|`支持多种类型
+- **泛型组件**:使用ComponentType
+
+### 6. 导出组件类型声明
+
+```typescript
+export declare const ComponentName: FC;
+```
+
+## 类型定义模板
+
+### 基础组件Props模板
+
+```typescript
+export interface ComponentNameProps {
+ className?: string;
+ children?: ReactNode;
+
+ // 其他属性...
+ [key: string]: any; // 支持动态属性
+}
+```
+
+### 函数回调模板
+
+```typescript
+onEvent ? : (data: any, context: any, ...args: any[]) => Promise | any;
+```
+
+### 组件声明模板
+
+```typescript
+export declare const ComponentName: FC;
+```
+
+## 最佳实践
+
+1. **类型完整性**:覆盖所有组件的公开API
+2. **向后兼容**:保持现有JavaScript代码的正常运行
+3. **可选属性**:合理使用可选属性,提供良好的开发体验
+4. **文档注释**:为复杂类型添加JSDoc注释
+5. **版本控制**:在package.json中指定types字段指向类型文件
+
+## 使用场景
+
+- 为现有组件库添加TypeScript支持
+- 提升代码可维护性和开发体验
+- 支持IDE智能提示和类型检查
+- 便于团队协作和代码审查
+
+## 示例项目结构
+
+```
+src/
+├── index.js # 主入口文件
+├── index.d.ts # 类型定义文件(新增)
+├── ComponentA.js # 组件A
+├── ComponentB.js # 组件B
+└── ...
+```
+
+## 注意事项
+
+- 类型定义文件应与实际组件实现保持同步
+- 考虑使用更具体的类型替代`any`类型
+- 定期检查和更新类型定义以匹配组件API的变化
+- 对于复杂的组件,考虑拆分类型定义到多个文件中
\ No newline at end of file
diff --git "a/prompts/prompts-frontend-libs/\347\224\237\346\210\220\345\214\205\345\212\237\350\203\275\346\217\217\350\277\260\346\226\207\344\273\266.md" "b/prompts/prompts-frontend-libs/\347\224\237\346\210\220\345\214\205\345\212\237\350\203\275\346\217\217\350\277\260\346\226\207\344\273\266.md"
new file mode 100644
index 0000000..39dfcd3
--- /dev/null
+++ "b/prompts/prompts-frontend-libs/\347\224\237\346\210\220\345\214\205\345\212\237\350\203\275\346\217\217\350\277\260\346\226\207\344\273\266.md"
@@ -0,0 +1,179 @@
+# 生成包功能描述文件(package-manifest.json)的通用提示词
+
+## 任务描述
+
+为一个JavaScript/TypeScript包项目生成`package-manifest.json`文件,该文件用于描述包的主要功能、导出模块、组件API等详细信息。
+
+## 实施步骤
+
+### 1. 分析项目结构
+
+- 查看`package.json`了解包的基本信息
+- 分析主入口文件(`index.js`/`index.ts`)的导出内容
+- 检查源代码目录结构,识别所有可导出的模块/组件
+
+### 2. 识别导出模块
+
+- 默认导出:通常标记为`default`
+- 命名导出:所有具名导出的组件、函数、类等
+
+### 3. 分析每个模块的功能
+
+对于每个导出的模块,需要收集以下信息:
+
+#### React组件
+- **description**: 组件的主要功能描述
+- **type**: 通常为`ReactNode`
+- **props**: 组件的所有属性,包括:
+ - 属性名
+ - 属性描述
+ - 属性类型(支持联合类型,如`string|number`,不能使用下面`类型定义规范`定义之外的类型)
+
+#### 函数/类
+- **description**: 功能描述
+- **type**: 返回值类型
+- **parameters**: 参数列表(如果有)
+- **properties**: 类属性(如果是类)
+
+#### 常量/对象
+- **description**: 用途描述
+- **type**: 数据类型
+- **properties**: 对象属性结构
+
+### 4. 构建JSON结构
+
+```json
+{
+ "description": "包的主要导出模块入口",
+ "modules": {
+ "模块名": {
+ "description": "模块功能描述",
+ "type": "类型声明",
+ // 根据模块类型可能包含以下字段
+ "props": {}, // React组件属性
+ "parameters": [], // 函数参数
+ "properties": {}, // 对象/类属性
+ "returns": "返回值类型" // 函数返回值
+ }
+ }
+}
+```
+
+## 类型定义规范
+
+### 基础类型
+- `string`: 字符串
+- `number`: 数字
+- `boolean`: 布尔值
+- `object`: 对象
+- `array`: 数组
+- `function`: 函数
+- `Date`: 日期对象
+- `Promise`: Promise对象
+- `RegExp`: 正则表达式
+- `Error`: 错误对象
+- `undefined`: 未定义
+- `null`: 空值
+- `any`: 任意类型
+- `void`: 无返回值
+- `unknown`: 未知类型
+- `ReactNode`: React节点(组件专用)
+
+### 联合类型
+使用`|`连接多种类型:
+- `string|ReactNode`: 字符串或React节点
+- `number|object`: 数字或对象
+- `function|null`: 函数或null
+
+### 复杂类型
+- `object[]`: 对象数组
+- `ReactNode[]`: React节点数组
+- `string[]`: 字符串数组
+
+## 优先级排序
+
+1. **主要组件**: 包中最核心、最常用的组件
+2. **辅助组件**: 支持性组件
+3. **工具函数**: 纯函数、工具方法
+4. **类型定义**: TypeScript类型、接口
+5. **常量配置**: 配置对象、常量
+
+## 属性命名约定
+
+### 通用属性
+- `className`: CSS类名
+- `children`: 子元素
+- `style`: 样式对象
+- `title`: 标题
+- `description`: 描述
+
+## 描述编写原则
+
+1. **简洁明确**: 一句话说明核心功能
+2. **用户视角**: 从使用者的角度描述
+3. **突出价值**: 说明解决了什么问题
+4. **避免实现细节**: 不描述内部实现逻辑
+
+## 示例格式
+
+### React组件示例
+```json
+{
+ "FormModal": {
+ "description": "模态框表单组件,在弹窗中展示表单",
+ "type": "ReactNode",
+ "props": {
+ "open": {
+ "description": "是否打开模态框",
+ "type": "boolean"
+ },
+ "title": {
+ "description": "模态框标题",
+ "type": "ReactNode"
+ },
+ "onSubmit": {
+ "description": "提交回调函数",
+ "type": "function"
+ }
+ }
+ }
+}
+```
+
+### 工具函数示例
+```json
+{
+ "formatDate": {
+ "description": "格式化日期字符串",
+ "type": "string",
+ "parameters": [
+ {
+ "name": "format",
+ "description": "格式化模板",
+ "type": "string"
+ }
+ ]
+ }
+}
+```
+
+## 文件位置
+
+将生成的`package-manifest.json`文件放置在:
+- `src/package-manifest.json`
+
+## 使用场景
+
+- 自动化API文档生成
+- IDE智能提示和自动补全
+- 包管理和分发平台的组件展示
+- 团队协作时的API参考
+- 第三方工具的集成和分析
+
+## 注意事项
+
+- 避免包含内部实现细节
+- 不要包含私有方法和属性
+- 确保向后兼容性
+- 定期更新以匹配代码变更
+- 考虑国际化需求(如需要)
\ No newline at end of file
diff --git "a/prompts/prompts-frontend-libs/\347\224\237\346\210\220\346\226\207\346\241\243.md" "b/prompts/prompts-frontend-libs/\347\224\237\346\210\220\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..3476ca7
--- /dev/null
+++ "b/prompts/prompts-frontend-libs/\347\224\237\346\210\220\346\226\207\346\241\243.md"
@@ -0,0 +1,20 @@
+### 文档生成说明
+
+本次文档生成遵循以下规范:
+
+#### 文档结构
+
+1. **doc/summary.md** - 项目概述文档
+ - 使用 h3 及以下标题级别
+ - 包含:项目概述、主要特性、使用场景
+
+2. **doc/api.md** - API 文档
+ - 使用 h3 及以下标题级别
+ - 使用表格格式展示命令和参数
+ - 包含:命令行接口、程序化 API、输出结构说明
+
+#### 文档要求
+
+- 标题级别:仅使用 h3(###)及以下
+- 表格格式:属性名、类型/参数、默认值/返回值、说明
+- 内容基于实际代码分析,确保准确性
diff --git "a/prompts/prompts-frontend-libs/\347\273\204\344\273\266\347\244\272\344\276\213\347\274\226\345\206\231.md" "b/prompts/prompts-frontend-libs/\347\273\204\344\273\266\347\244\272\344\276\213\347\274\226\345\206\231.md"
new file mode 100644
index 0000000..df906c5
--- /dev/null
+++ "b/prompts/prompts-frontend-libs/\347\273\204\344\273\266\347\244\272\344\276\213\347\274\226\345\206\231.md"
@@ -0,0 +1,316 @@
+# 组件示例编写提示词
+
+本提示词用于编写遵循特定目录结构规范的 React 项目组件示例。
+
+## 1. 目录结构规范
+
+### 项目目录布局
+
+```
+{PROJECT_ROOT}/
+├── src/ # 源代码目录
+│ └── components/ # 组件源代码
+│ ├── ComponentA/ # 单个组件目录
+│ ├── ComponentB/ # 带子组件的目录
+│ │ ├── Sub1/
+│ │ ├── Sub2/
+│ │ └── index.js
+│ └── ...
+├── doc/ # 文档及示例目录(所有组件共用)
+│ ├── example.json # 示例配置文件(核心)
+│ ├── api.md # 组件API文档
+│ ├── summary.md # 组件简要描述
+│ ├── base.js # 基础示例代码
+│ ├── component-a.js # ComponentA示例
+│ ├── component-b.js # ComponentB主组件示例
+│ ├── component-b-sub1.js # ComponentB子组件Sub1示例
+│ ├── component-b-sub2.js # ComponentB子组件Sub2示例
+│ └── style.scss # 示例样式(可选)
+└── package.json # 项目配置文件
+```
+
+### 示例文件规则
+
+**重要**:所有组件的示例文件都统一放在项目根目录的 `doc/` 中,而不是在每个组件目录下创建单独的 `doc/` 目录。
+
+- 单个组件:使用组件名的小写短横线形式命名示例文件,如 `component-a.js`
+- 带子组件的组件:主组件示例使用组件名,子组件示例使用组件名加子组件名,如 `component-b.js`、`component-b-sub1.js`
+
+### 项目配置变量
+
+在编写示例时需要替换以下变量:
+
+| 变量名 | 说明 | 示例值 |
+|--------|------|--------|
+| `{PACKAGE_NAME}` | 从 package.json 的 name 字段获取 | `@kne/react-file` |
+| `{PROJECT_NAME}` | 项目名称,用于 getPublicPath | `react-file` |
+| `{LIB_NAME}` | 组件库名称,通常是驼峰形式 | `ReactFile` |
+
+## 2. example.json 配置结构
+
+所有组件的示例配置统一在 `doc/example.json` 中管理:
+
+```json
+{
+ "isFull": true,
+ "list": [
+ {
+ "title": "ComponentA",
+ "description": "组件A的描述",
+ "code": "./component-a.js",
+ "scope": [
+ {
+ "name": "_{LIB_NAME}",
+ "packageName": "{PACKAGE_NAME}",
+ "importStatement": "import * as _{LIB_NAME} from \"{PACKAGE_NAME}\""
+ },
+ {
+ "packageName": "{PACKAGE_NAME}/dist/index.css"
+ },
+ {
+ "name": "antd",
+ "packageName": "antd"
+ },
+ {
+ "name": "remoteLoader",
+ "packageName": "@kne/remote-loader"
+ }
+ ]
+ },
+ {
+ "title": "ComponentB",
+ "description": "组件B主组件",
+ "code": "./component-b.js",
+ "scope": []
+ },
+ {
+ "title": "ComponentB-Sub1",
+ "description": "组件B子组件Sub1",
+ "code": "./component-b-sub1.js",
+ "scope": []
+ }
+ ]
+}
+```
+
+**重要说明**:
+- `packageName` 可以使用当前组件包名的特殊格式:`@kne/current-lib_{PACKAGE_NAME_SUFFIX}`
+- 例如:`@kne/react-file` 项目可以使用 `@kne/current-lib_react-file` 来引用当前正在开发的组件包
+- 这种特殊写法用于文档示例系统自动解析当前包名
+
+## 3. 示例代码规范
+
+### 示例编写原则
+
+**重要原则**:
+
+1. **充分展示API功能**:示例代码要尽可能完整地展示组件的所有API功能和配置选项,包括:
+ - 组件的所有核心属性(props)及其不同取值
+ - 事件回调函数的完整实现
+ - 不同配置模式下的使用场景
+ - 组件间的组合使用方式
+ - 各种边界情况和特殊场景
+
+2. **贴近业务场景的示例数据**:示例数据应尽量模拟真实的业务场景,避免过于简化的假数据:
+ - 使用真实的字段名称和数据结构
+ - 包含合理的业务上下文信息
+ - 体现实际应用中的复杂度和多样性
+ - 模拟真实接口返回的数据格式
+
+### 导入方式
+
+- 使用 `scope` 中声明的变量名:`const { FilePreview } = _{LIB_NAME};`
+- 工具包引用:`const { createWithRemoteLoader, getPublicPath } = remoteLoader;`
+
+### 标准示例模板(需要 PureGlobal 模拟数据)
+
+```javascript
+const { YourComponent } = _{LIB_NAME};
+const { createWithRemoteLoader, getPublicPath } = remoteLoader;
+
+const BaseExample = createWithRemoteLoader({
+ modules: ['components-core:Global@PureGlobal', 'components-core:InfoPage']
+})(({ remoteModules }) => {
+ const [PureGlobal, InfoPage] = remoteModules;
+ return (
+ {
+ return { data: { code: 0, data: api.loader() } };
+ },
+ apis: {
+ file: {
+ staticUrl: getPublicPath('{PROJECT_NAME}') || window.PUBLIC_URL,
+ getUrl: {
+ loader: async ({ params }) => {
+ // 模拟数据
+ return 'mock-url';
+ }
+ }
+ }
+ }
+ }}>
+
+
+
+
+
+
+ );
+});
+
+render();
+```
+
+### 简化示例模板(无需远程加载)
+
+```javascript
+const { YourComponent } = _{LIB_NAME};
+const { Flex, Switch } = antd;
+const { useState } = React;
+
+const BaseExample = () => {
+ const [state, setState] = useState(false);
+ return (
+
+
+
+ );
+};
+
+render();
+```
+
+## 4. scope 依赖声明规则
+
+| 场景 | name | packageName | importStatement(可选) |
+|------|------|-------------|------------------------|
+| 项目组件 | `_{LIB_NAME}` | `{PACKAGE_NAME}` 或 `@kne/current-lib_{PACKAGE_NAME_SUFFIX}` | `import * as _{LIB_NAME} from "{PACKAGE_NAME}"` |
+| 样式文件 | - | `{PACKAGE_NAME}/dist/index.css` 或 `@kne/current-lib_{PACKAGE_NAME_SUFFIX}/dist/index.css` | - |
+| 远程加载器 | `remoteLoader` | `@kne/remote-loader` | - |
+| Antd组件 | `antd` | `antd` | - |
+| React原生 | `React` | - | -(无需声明) |
+
+**当前包引用规则**:
+- 在 `example.json` 的 `scope` 中引用当前正在开发的组件包时,使用 `@kne/current-lib_{PACKAGE_NAME_SUFFIX}`
+- 例如:`@kne/info-page` 项目应使用 `@kne/current-lib_info-page`
+- 这样可以让文档系统自动识别并加载当前开发中的组件版本
+
+## 5. API文档编写
+
+`doc/api.md` 中包含所有组件的API文档,使用表格格式:
+
+```markdown
+### 组件名称
+
+组件描述
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|------|------|-------|------|
+| propName | string | - | 属性描述 |
+```
+
+## 6. 数据模拟
+
+在示例中使用 PureGlobal 的 preset 模拟数据:
+
+```javascript
+apis: {
+ file: {
+ staticUrl: getPublicPath('{PROJECT_NAME}') || window.PUBLIC_URL,
+ getUrl: {
+ loader: async ({ params }) => {
+ // 根据 params 返回对应的 mock 数据
+ const urlMap = {
+ 1: '/mock/example.png',
+ 2: '/mock/example.pdf'
+ };
+ return new Promise(resolve => {
+ setTimeout(() => {
+ resolve(urlMap[params.id]);
+ }, 500);
+ });
+ }
+ }
+ }
+}
+```
+
+## 7. 组件导出规范
+
+在组件的主入口 `index.js` 中,需要导出所有子组件:
+
+```javascript
+export { default } from './MainComponent';
+export { default as SubComponent1 } from './SubComponent1';
+export { default as SubComponent2 } from './SubComponent2';
+```
+
+在示例中通过解构引用:
+
+```javascript
+const { MainComponent, SubComponent1, SubComponent2 } = _{LIB_NAME};
+```
+
+## 8. 使用说明
+
+### 创建新组件示例步骤
+
+1. 在 `src/components/` 下创建或编辑组件
+2. 在 `doc/example.json` 中添加示例配置
+3. 在 `doc/` 目录下创建对应的 `.js` 示例文件
+4. 在 `doc/api.md` 中添加组件API文档(如果是新组件)
+5. 在组件的 `index.js` 中确保正确导出
+
+### 变量替换示例
+
+假设项目 `package.json` 中配置为:
+
+```json
+{
+ "name": "@your-org/your-project"
+}
+```
+
+则变量值为:
+
+- `{PACKAGE_NAME}` → `@your-org/your-project`
+- `{PROJECT_NAME}` → `your-project`
+- `{LIB_NAME}` → `YourProject`
+- `{PACKAGE_NAME_SUFFIX}` → `your-project`(用于当前包引用)
+
+实际使用时需要替换为:
+
+```json
+{
+ "name": "_YourProject",
+ "packageName": "@your-org/your-project",
+ "importStatement": "import * as _YourProject from \"@your-org/your-project\""
+}
+```
+
+**引用当前开发包时的特殊写法**:
+```json
+{
+ "name": "_YourProject",
+ "packageName": "@kne/current-lib_your-project",
+ "importStatement": "import * as _YourProject from \"@your-org/your-project\""
+}
+```
+
+```javascript
+const { YourComponent } = _YourProject;
+const { createWithRemoteLoader, getPublicPath } = remoteLoader;
+
+// ...
+staticUrl: getPublicPath('your-project') || window.PUBLIC_URL,
+```
+
+## 9. 项目参考
+
+本项目的示例文件位于 `doc/` 目录:
+
+- `doc/example.json` - 所有组件的示例配置
+- `doc/api.md` - 所有组件的API文档
+- `doc/summary.md` - 项目概述和核心特性说明
\ No newline at end of file
diff --git a/prompts/prompts.json b/prompts/prompts.json
new file mode 100644
index 0000000..3d07349
--- /dev/null
+++ b/prompts/prompts.json
@@ -0,0 +1,3 @@
+{
+ "@kne/prompts-frontend-libs": "1.0.1"
+}
diff --git "a/prompts/\347\224\237\346\210\220README\347\264\242\345\274\225.md" "b/prompts/\347\224\237\346\210\220README\347\264\242\345\274\225.md"
new file mode 100644
index 0000000..b7f99c7
--- /dev/null
+++ "b/prompts/\347\224\237\346\210\220README\347\264\242\345\274\225.md"
@@ -0,0 +1,65 @@
+### 文档索引生成指南
+
+#### 概述
+
+本文档描述如何从包含多个子目录的文档目录生成结构清晰、便于查阅的索引文档。
+
+#### 生成流程
+
+##### 第一步:扫描源目录
+
+识别当前文件所在目录中的所有子目录和文档文件中的所有README.md文件。
+
+##### 第二步:分析子目录结构
+
+对每个README.md进行内容分析:
+
+- 提取文档集合的核心用途和主要特性
+- 记录已安装的版本信息(如存在 prompts.json)
+
+##### 第三步:设计索引结构
+
+采用统一的模板组织信息:
+
+```markdown
+### 序号. 文档名称
+
+**功能**: 简要描述
+
+**适用场景**:
+- 场景一
+- 场景二
+
+**核心内容**:
+- 内容点一
+- 内容点二
+```
+
+##### 第四步:生成索引文档
+
+按以下结构组织最终文档:
+
+1. **项目标题与简介**:说明文档集合的用途
+2. **文档集合列表**:按子目录分组展示各文档摘要
+3. **快速选择指南**:帮助用户根据需求快速定位
+
+##### 第五步:添加导航辅助
+
+在文末添加决策表格,帮助用户根据需求快速定位:
+
+| 需求 | 推荐文档 | 所属集合 |
+|------|----------|----------|
+| 具体需求描述 | 对应文档名称 | 所属子目录 |
+
+#### 设计原则
+
+1. **信息层次清晰**:使用标题层级区分结构,使用加粗、列表等格式突出重点
+2. **内容适度抽象**:不直接复制原文内容,提炼核心要点
+3. **便于快速检索**:提供功能定位、明确适用场景、添加决策辅助表格
+4. **保持可维护性**:结构化模板便于更新,格式统一减少维护成本
+
+#### 扩展建议
+
+- **版本管理**:可在文档列表中添加版本号和更新日期
+- **标签系统**:为文档添加标签便于分类检索
+- **依赖关系**:说明文档之间的关联或前置依赖
diff --git a/src/assets/index.scss b/src/assets/index.scss
index 309c237..baa57c1 100644
--- a/src/assets/index.scss
+++ b/src/assets/index.scss
@@ -242,3 +242,125 @@
}
}
}
+
+.svg_box {
+ display: flex;
+ align-items: center;
+ padding: 0 8px;
+ color: rgba(0, 0, 0, 0.25);
+}
+
+.react-form__field-input {
+ .data_range_picker {
+ flex: 1;
+ }
+}
+
+// 垂直布局下的特殊处理
+.react-form--inner {
+ .svg_box {
+ padding: 0 4px;
+ }
+
+ .ant-picker {
+ flex: 1;
+ width: 100%;
+ }
+
+ .date-picker-today-container {
+ width: 100%;
+ }
+}
+
+// DatePickerToday 样式
+.date-picker-today-container {
+ position: relative;
+ display: inline-block;
+ vertical-align: top;
+
+ .date-picker-today-inputs {
+ display: inline-flex;
+ align-items: center;
+ border: 1px solid #d9d9d9;
+ border-radius: 6px;
+ padding: 0 11px;
+ background: #fff;
+ transition: all 0.2s;
+ cursor: pointer;
+
+ &:hover {
+ border-color: #4096ff;
+ }
+
+ &:focus-within {
+ border-color: #4096ff;
+ box-shadow: 0 0 0 2px rgba(5, 145, 255, 0.1);
+ }
+
+ .date-picker-today-start,
+ .date-picker-today-end {
+ display: flex;
+ align-items: center;
+
+ .ant-input {
+ border: none;
+ padding: 0;
+ height: 30px;
+ cursor: pointer;
+ width: 110px;
+ outline: none;
+ background: transparent;
+ color: rgba(0, 0, 0, 0.85);
+ font-size: 14px;
+
+ &::placeholder {
+ color: rgba(0, 0, 0, 0.25);
+ }
+
+ &:focus {
+ box-shadow: none;
+ }
+ }
+
+ .ant-input.so-far-active {
+ color: rgba(0, 0, 0, 0.85);
+ font-weight: 500;
+ }
+ }
+
+ .date-picker-today-separator {
+ padding: 0 8px;
+ color: rgba(0, 0, 0, 0.25);
+ line-height: 30px;
+ }
+
+ .date-picker-today-suffix {
+ display: flex;
+ align-items: center;
+ padding-left: 4px;
+ color: rgba(0, 0, 0, 0.25);
+
+ .date-picker-today-clear {
+ color: rgba(0, 0, 0, 0.25);
+ font-size: 12px;
+ transition: color 0.2s;
+ cursor: pointer;
+
+ &:hover {
+ color: rgba(0, 0, 0, 0.45);
+ }
+ }
+ }
+ }
+
+ // 隐藏的 DatePicker 触发器
+ .ant-picker {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 0 !important;
+ height: 0 !important;
+ visibility: hidden;
+ pointer-events: none;
+ }
+}
diff --git a/src/fields/DatePicker.js b/src/fields/DatePicker.js
index 83b24d2..a72aa64 100644
--- a/src/fields/DatePicker.js
+++ b/src/fields/DatePicker.js
@@ -1,53 +1,58 @@
import {DatePicker} from 'antd';
import {hooks} from '@kne/react-form-helper';
+import withLocale, {useIntl} from '../withLocale';
const {useOnChange} = hooks;
const {MonthPicker, RangePicker, WeekPicker} = DatePicker;
-const _DatePicker = (props) => {
- props = Object.assign({}, {
+const DatePickerInner = (props) => {
+ const {formatMessage} = useIntl();
+ const mergedProps = Object.assign({}, {
fieldName: 'datePicker'
}, props);
- const render = useOnChange(Object.assign({placeholder: `请选择${props.label || ''}`}, props));
+ const render = useOnChange(Object.assign({placeholder: formatMessage({id: 'PleaseSelect'}, {label: mergedProps.label || ''})}, mergedProps));
return render(DatePicker);
};
-_DatePicker.Field = DatePicker;
+DatePickerInner.Field = DatePicker;
-const _MonthPicker = (props) => {
- props = Object.assign({}, {
+const MonthPickerInner = (props) => {
+ const {formatMessage} = useIntl();
+ const mergedProps = Object.assign({}, {
fieldName: 'monthDatePicker'
}, props);
- const render = useOnChange(Object.assign({placeholder: ['开始时间', '结束时间']}, props));
+ const render = useOnChange(Object.assign({placeholder: [formatMessage({id: 'StartTime'}), formatMessage({id: 'EndTime'})]}, mergedProps));
return render(MonthPicker);
};
-_MonthPicker.Field = MonthPicker;
+MonthPickerInner.Field = MonthPicker;
-const _RangePicker = (props) => {
- props = Object.assign({}, {
+const RangePickerInner = (props) => {
+ const {formatMessage} = useIntl();
+ const mergedProps = Object.assign({}, {
fieldName: 'rangeDatePicker'
}, props);
- const render = useOnChange(Object.assign({placeholder: ['开始时间', '结束时间']}, props));
+ const render = useOnChange(Object.assign({placeholder: [formatMessage({id: 'StartTime'}), formatMessage({id: 'EndTime'})]}, mergedProps));
return render(RangePicker);
};
-_RangePicker.Field = RangePicker;
+RangePickerInner.Field = RangePicker;
-const _WeekPicker = (props) => {
- props = Object.assign({}, {
+const WeekPickerInner = (props) => {
+ const {formatMessage} = useIntl();
+ const mergedProps = Object.assign({}, {
fieldName: 'weekDatePicker'
}, props);
- const render = useOnChange(Object.assign({placeholder: ['开始时间', '结束时间']}, props));
+ const render = useOnChange(Object.assign({placeholder: [formatMessage({id: 'StartTime'}), formatMessage({id: 'EndTime'})]}, mergedProps));
return render(WeekPicker);
};
-_WeekPicker.Field = WeekPicker;
-
-_DatePicker.MonthPicker = _MonthPicker;
-_DatePicker.RangePicker = _RangePicker;
-_DatePicker.WeekPicker = _WeekPicker;
+WeekPickerInner.Field = WeekPicker;
+const _DatePicker = withLocale(DatePickerInner);
+_DatePicker.MonthPicker = withLocale(MonthPickerInner);
+_DatePicker.RangePicker = withLocale(RangePickerInner);
+_DatePicker.WeekPicker = withLocale(WeekPickerInner);
export default _DatePicker;
diff --git a/src/fields/DatePickerToday.js b/src/fields/DatePickerToday.js
index a4ce66b..e4eaf9b 100644
--- a/src/fields/DatePickerToday.js
+++ b/src/fields/DatePickerToday.js
@@ -1,77 +1,223 @@
-import {Button, DatePicker} from 'antd';
-import React, {useRef, useMemo} from 'react'
+import {Button, DatePicker, Input} from 'antd';
+import {CalendarOutlined, CloseCircleFilled} from '@ant-design/icons';
+import React, {useRef, useMemo, useCallback, useState} from 'react'
import dayjs from 'dayjs'
-import {get} from 'lodash'
import useControlValue from '@kne/use-control-value'
import {hooks} from '@kne/react-form-helper';
+import withLocale, {useIntl} from '../withLocale';
const {useOnChange} = hooks;
-const PickerToday = ({soFarText, startProps, endProps, ...props}) => {
+const PickerTodayInner = ({soFarText, soFarValue = 'soFar', ...props}) => {
+ const {formatMessage} = useIntl();
const [data, onChange] = useControlValue(props);
- const ref_d = useRef();
- const newData = useMemo(() => {
- // console.log('......', data);
- const s = get(data, 0, '');
- const d = get(data, 1, '');
- const p = get(props, ['placeholder'], ['开始日期', '结束日期']);
+ const [openStart, setOpenStart] = useState(false);
+ const [openEnd, setOpenEnd] = useState(false);
+ const [tempStart, setTempStart] = useState(null); // 临时存储开始时间
+ const [tempEnd, setTempEnd] = useState(null); // 临时存储结束时间
+ const [hovering, setHovering] = useState(false); // hover 状态
+ const containerRef = useRef(null);
+ const isSwitchingRef = useRef(false); // 标记是否正在切换到结束时间选择
+
+ const soFarLabel = soFarText || formatMessage({id: 'SoFar'});
+
+ // 判断是否有值
+ const hasValue = useMemo(() => {
+ const [start, end] = data || [];
+ return !!(start || end);
+ }, [data]);
+
+ // 判断是否为"至今"
+ const isSoFar = useMemo(() => {
+ const [, end] = data || [];
+ return end === soFarValue;
+ }, [data, soFarValue]);
+
+ // 解析数据
+ const parsedValue = useMemo(() => {
+ const [start, end] = data || [];
return {
- start: s ? dayjs(s) : '',
- end: d === '至今' ? null : (d ? dayjs(d) : ''),
- showZj: d === '至今',
- placeholder: p
- }
+ start: start ? dayjs(start) : null, end: isSoFar ? null : (end ? dayjs(end) : null)
+ };
+ }, [data, isSoFar]);
+
+ // 显示文本 - 弹窗打开时显示临时值,关闭后显示实际值
+ const displayText = useMemo(() => {
+ // 开始时间:有临时值时显示临时值
+ const startText = tempStart
+ ? tempStart.format('YYYY-MM-DD')
+ : (parsedValue.start ? parsedValue.start.format('YYYY-MM-DD') : '');
+
+ // 结束时间:有临时值时显示临时值
+ const endText = tempEnd
+ ? tempEnd.format('YYYY-MM-DD')
+ : (isSoFar ? soFarLabel : (parsedValue.end ? parsedValue.end.format('YYYY-MM-DD') : ''));
+
+ return {start: startText, end: endText};
+ }, [parsedValue, isSoFar, soFarLabel, tempStart, tempEnd]);
+
+ // 点击整个Input框时,先弹出开始时间选择
+ const handleInputClick = useCallback(() => {
+ const [start] = data || [];
+ setTempStart(start ? dayjs(start) : null);
+ setOpenStart(true);
+ setOpenEnd(false);
}, [data]);
- const startChange = (v) => {
- // 比较日期大小
- if (!newData.showZj && newData.end && v && newData.end.isBefore(v)) {
- onChange([newData.end, v]);
- return
+ // 清除值
+ const handleClear = useCallback((e) => {
+ e.stopPropagation();
+ onChange([]);
+ setTempStart(null);
+ setTempEnd(null);
+ setOpenStart(false);
+ setOpenEnd(false);
+ }, [onChange]);
+
+ // 处理开始时间变化
+ const handleStartChange = useCallback((date) => {
+ setTempStart(date);
+ isSwitchingRef.current = true; // 标记正在切换
+ setOpenStart(false);
+ setTimeout(() => {
+ setOpenEnd(true);
+ isSwitchingRef.current = false;
+ }, 100);
+ }, []);
+
+ // 处理结束时间变化(选择日期时实时更新显示,选择后确认值)
+ const handleEndChange = useCallback((date) => {
+ if (date) {
+ // 选择了有效日期,确认值
+ if (!tempStart) return;
+ onChange([tempStart.toISOString(), date.toISOString()]);
+ setOpenEnd(false);
+ setTempStart(null);
+ setTempEnd(null);
+ } else {
+ // 清空选择
+ setTempEnd(null);
}
- onChange([v || '', newData.showZj ? '至今' : newData.end]);
- };
+ }, [tempStart, onChange]);
- const endChange = (v) => {
- if (newData.start && v && v.isBefore(newData.start)) {
- onChange([v, newData.start]);
- return
+ // 点击"至今"按钮
+ const handleSoFarClick = useCallback(() => {
+ if (!tempStart) return;
+ onChange([tempStart.toISOString(), soFarValue]);
+ setOpenEnd(false);
+ setTempStart(null);
+ setTempEnd(null);
+ }, [tempStart, onChange, soFarValue]);
+
+ // 处理开始时间弹窗关闭(未完成选择)
+ const handleStartOpenChange = useCallback((open) => {
+ if (!open) {
+ setOpenStart(false);
+ // 如果不是正在切换到结束时间,则清空临时状态
+ if (!isSwitchingRef.current) {
+ setTempStart(null);
+ }
}
- onChange([newData.start, v || '']);
- };
-
- const foot = () => {
- return ()
- }
- return (
-
-
-
-
{soFarText || '至今'}
-
+ }, []);
+
+ // 处理结束时间弹窗关闭(未完成选择)
+ const handleEndOpenChange = useCallback((open) => {
+ if (!open) {
+ setOpenEnd(false);
+ // 关闭结束时间弹窗时,不修改field值
+ setTempStart(null);
+ setTempEnd(null);
+ }
+ }, []);
+
+ // 结束时间面板变化时更新临时值(用于实时显示)
+ const handleEndPanelChange = useCallback((date) => {
+ setTempEnd(date);
+ }, []);
+
+ // 空的 footer(用于开始时间,保持高度一致)
+ const renderEmptyFooter = useCallback(() => (), []);
+
+ // 带至今按钮的 footer
+ const renderExtraFooter = useCallback(() => {
+ const isCurrentSoFar = !tempStart && isSoFar;
+ return (
+
+
);
+ }, [isSoFar, tempStart, handleSoFarClick, soFarLabel]);
+
+ // 结束时间的可选日期(不能早于开始时间)
+ const endDisabledDate = useCallback((current) => {
+ if (!tempStart) return false;
+ return current && current < tempStart.startOf('day');
+ }, [tempStart]);
+
+ return (
+
setHovering(true)}
+ onMouseLeave={() => setHovering(false)}
+ >
+
+
+
+
~
+
+
+
+
+ {hasValue && hovering ? (
+
+ ) : (
+
+ )}
+
+
+ {/* 开始时间 DatePicker */}
+
containerRef.current || document.body}
+ style={{width: 0, height: 0, visibility: 'hidden', position: 'absolute'}}
+ placement="bottomLeft"
+ />
+
+ {/* 结束时间 DatePicker */}
+ containerRef.current || document.body}
+ style={{width: 0, height: 0, visibility: 'hidden', position: 'absolute'}}
+ placement="bottomLeft"
+ />
);
-}
+};
+const PickerToday = withLocale(PickerTodayInner);
const RangePickerToday = (props) => {
props = Object.assign({}, {
diff --git a/src/fields/Input.js b/src/fields/Input.js
index b746df4..3d32726 100644
--- a/src/fields/Input.js
+++ b/src/fields/Input.js
@@ -1,26 +1,32 @@
import {Input} from 'antd';
import {hooks} from '@kne/react-form-helper';
+import withLocale, {useIntl} from '../withLocale';
const {useDecorator} = hooks;
-const InputField = (props) => {
- props = Object.assign({}, {
+const InputFieldInner = (props) => {
+ const {formatMessage} = useIntl();
+ const mergedProps = Object.assign({}, {
fieldName: 'input', autoComplete: 'off'
}, props);
- const render = useDecorator(Object.assign({placeholder: `请输入${props.label}`}, props));
+ const render = useDecorator(Object.assign({placeholder: formatMessage({id: 'PleaseInput'}, {label: mergedProps.label})}, mergedProps));
return render(Input);
};
-InputField.Field = Input;
+InputFieldInner.Field = Input;
-InputField.Password = (props) => {
- props = Object.assign({}, {
+const PasswordInner = (props) => {
+ const {formatMessage} = useIntl();
+ const mergedProps = Object.assign({}, {
fieldName: 'password', autoComplete: 'off'
}, props);
- const render = useDecorator(Object.assign({placeholder: `请输入${props.label}`}, props));
+ const render = useDecorator(Object.assign({placeholder: formatMessage({id: 'PleaseInput'}, {label: mergedProps.label})}, mergedProps));
return render(Input.Password);
};
-InputField.Password.Field = Input.Password;
+PasswordInner.Field = Input.Password;
+
+const InputField = withLocale(InputFieldInner);
+InputField.Password = withLocale(PasswordInner);
export default InputField;
diff --git a/src/fields/InputNumber.js b/src/fields/InputNumber.js
index c07c184..49d288e 100644
--- a/src/fields/InputNumber.js
+++ b/src/fields/InputNumber.js
@@ -1,16 +1,20 @@
import {InputNumber} from 'antd';
import {hooks} from '@kne/react-form-helper';
+import withLocale, {useIntl} from '../withLocale';
const {useDecorator} = hooks;
-const InputNumberField = (props) => {
- props = Object.assign({}, {
+const InputNumberFieldInner = (props) => {
+ const {formatMessage} = useIntl();
+ const mergedProps = Object.assign({}, {
fieldName: 'inputNumber', autoComplete: 'off'
}, props);
- const render = useDecorator(Object.assign({placeholder: `请输入${props.label}`}, props));
+ const render = useDecorator(Object.assign({placeholder: formatMessage({id: 'PleaseInput'}, {label: mergedProps.label})}, mergedProps));
return render(InputNumber);
};
-InputNumberField.Field = InputNumber;
+InputNumberFieldInner.Field = InputNumber;
+
+const InputNumberField = withLocale(InputNumberFieldInner);
export default InputNumberField;
diff --git a/src/fields/Select.js b/src/fields/Select.js
index c7d697d..48bb225 100644
--- a/src/fields/Select.js
+++ b/src/fields/Select.js
@@ -1,17 +1,22 @@
import {Select} from 'antd';
import {hooks} from '@kne/react-form-helper';
+import withLocale, {useIntl} from '../withLocale';
const {useOnChange} = hooks;
-const _Select = (props) => {
- props = Object.assign({}, {
+const SelectInner = (props) => {
+ const {formatMessage} = useIntl();
+ const mergedProps = Object.assign({}, {
fieldName: 'select'
}, props);
- const render = useOnChange(Object.assign({placeholder: `请选择${props.label || ''}`}, props));
+ const render = useOnChange(Object.assign({placeholder: formatMessage({id: 'PleaseSelect'}, {label: mergedProps.label || ''})}, mergedProps));
return render(Select);
};
-_Select.Field = Select;
-_Select.Option = Select.Option;
-_Select.OptGroup = Select.OptGroup;
+
+SelectInner.Field = Select;
+SelectInner.Option = Select.Option;
+SelectInner.OptGroup = Select.OptGroup;
+
+const _Select = withLocale(SelectInner);
export default _Select;
diff --git a/src/fields/TextArea.js b/src/fields/TextArea.js
index 1c7c9c0..f216ca4 100644
--- a/src/fields/TextArea.js
+++ b/src/fields/TextArea.js
@@ -1,16 +1,20 @@
import {Input} from 'antd';
import {hooks} from '@kne/react-form-helper';
+import withLocale, {useIntl} from '../withLocale';
const {useDecorator} = hooks;
-const TextArea = (props) => {
- props = Object.assign({}, {
+const TextAreaInner = (props) => {
+ const {formatMessage} = useIntl();
+ const mergedProps = Object.assign({}, {
fieldName: 'textArea'
}, props);
- const render = useDecorator(Object.assign({placeholder: `请输入${props.label}`}, props));
+ const render = useDecorator(Object.assign({placeholder: formatMessage({id: 'PleaseInput'}, {label: mergedProps.label})}, mergedProps));
return render(Input.TextArea);
};
-TextArea.Field = Input.TextArea;
+TextAreaInner.Field = Input.TextArea;
+
+const TextArea = withLocale(TextAreaInner);
export default TextArea;
diff --git a/src/fields/TimePicker.js b/src/fields/TimePicker.js
index 163b497..0a06d08 100644
--- a/src/fields/TimePicker.js
+++ b/src/fields/TimePicker.js
@@ -1,30 +1,34 @@
import {TimePicker} from 'antd';
import {hooks} from '@kne/react-form-helper';
+import withLocale, {useIntl} from '../withLocale';
const {useOnChange} = hooks;
const {RangePicker} = TimePicker;
-const _TimePicker = (props) => {
- props = Object.assign({}, {
+const TimePickerInner = (props) => {
+ const {formatMessage} = useIntl();
+ const mergedProps = Object.assign({}, {
fieldName: 'timePicker'
}, props);
- const render = useOnChange(Object.assign({placeholder: `请选择${props.label || ''}`}, props));
+ const render = useOnChange(Object.assign({placeholder: formatMessage({id: 'PleaseSelect'}, {label: mergedProps.label || ''})}, mergedProps));
return render(TimePicker);
};
-_TimePicker.Field = TimePicker;
+TimePickerInner.Field = TimePicker;
-const _RangePicker = (props) => {
- props = Object.assign({}, {
+const RangePickerInner = (props) => {
+ const {formatMessage} = useIntl();
+ const mergedProps = Object.assign({}, {
fieldName: 'rangeTimePicker'
}, props);
- const render = useOnChange(Object.assign({placeholder: `请选择${props.label || ''}`}, props));
+ const render = useOnChange(Object.assign({placeholder: formatMessage({id: 'PleaseSelect'}, {label: mergedProps.label || ''})}, mergedProps));
return render(RangePicker);
};
-_RangePicker.Field = RangePicker;
+RangePickerInner.Field = RangePicker;
-_TimePicker.RangePicker = _RangePicker;
+const _TimePicker = withLocale(TimePickerInner);
+_TimePicker.RangePicker = withLocale(RangePickerInner);
export default _TimePicker;
diff --git a/src/fields/TreeSelect.js b/src/fields/TreeSelect.js
index 0f6d8ff..b958670 100644
--- a/src/fields/TreeSelect.js
+++ b/src/fields/TreeSelect.js
@@ -1,18 +1,21 @@
import {TreeSelect} from 'antd';
import {hooks} from '@kne/react-form-helper';
+import withLocale, {useIntl} from '../withLocale';
const {useOnChange} = hooks;
-const _TreeSelect = (props) => {
- props = Object.assign({}, {
+const TreeSelectInner = (props) => {
+ const {formatMessage} = useIntl();
+ const mergedProps = Object.assign({}, {
fieldName: 'treeSelect'
}, props);
- const render = useOnChange(Object.assign({placeholder: `请选择${props.label || ''}`}, props));
+ const render = useOnChange(Object.assign({placeholder: formatMessage({id: 'PleaseSelect'}, {label: mergedProps.label || ''})}, mergedProps));
return render(TreeSelect);
};
-_TreeSelect.Field = TreeSelect;
+TreeSelectInner.Field = TreeSelect;
+TreeSelectInner.TreeNode = TreeSelect.TreeNode;
-_TreeSelect.TreeNode = TreeSelect.TreeNode;
+const _TreeSelect = withLocale(TreeSelectInner);
export default _TreeSelect;
diff --git a/src/locale/en-US.js b/src/locale/en-US.js
new file mode 100644
index 0000000..cc27d74
--- /dev/null
+++ b/src/locale/en-US.js
@@ -0,0 +1,11 @@
+const locale = {
+ PleaseSelect: 'Please select {label}',
+ PleaseInput: 'Please enter {label}',
+ StartTime: 'Start Time',
+ EndTime: 'End Time',
+ StartDate: 'Start Date',
+ EndDate: 'End Date',
+ SoFar: 'Present'
+};
+
+export default locale;
diff --git a/src/locale/zh-CN.js b/src/locale/zh-CN.js
new file mode 100644
index 0000000..e48946a
--- /dev/null
+++ b/src/locale/zh-CN.js
@@ -0,0 +1,11 @@
+const locale = {
+ PleaseSelect: '请选择{label}',
+ PleaseInput: '请输入{label}',
+ StartTime: '开始时间',
+ EndTime: '结束时间',
+ StartDate: '开始日期',
+ EndDate: '结束日期',
+ SoFar: '至今'
+};
+
+export default locale;
diff --git a/src/withLocale.js b/src/withLocale.js
new file mode 100644
index 0000000..4f51545
--- /dev/null
+++ b/src/withLocale.js
@@ -0,0 +1,25 @@
+import React from 'react';
+import { IntlProvider, useIntl } from '@kne/react-intl';
+import zhCN from './locale/zh-CN';
+import enUS from './locale/en-US';
+
+const localeMap = {
+ 'zh-CN': zhCN,
+ 'en-US': enUS
+};
+
+const withLocale = (WrappedComponent) => {
+ const WithLocaleComponent = ({ locale = 'zh-CN', ...props }) => {
+ const messages = localeMap[locale] || zhCN;
+ return (
+
+
+
+ );
+ };
+ WithLocaleComponent.displayName = `withLocale(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`;
+ return WithLocaleComponent;
+};
+
+export { useIntl };
+export default withLocale;