dynamicformdjx-react
v0.3.1
Published
Dynamic form for React
Downloads
241
Readme
dynamicformdjx-react
基于 React 的动态表单及录入。
概述
DynamicForm 一个灵活且动态的表单组件,使用数组,简化模版操作,提供多种hook快速操作表单等。
- 简化节点代码,快速处理表单
- 提供render2函数渲染表单项,使用函数渲染值或自定义Component函数
- 提供多种hooks函数,快速处理数据值
DynamicInput 组件是一个灵活且动态的表单输入组件,允许用户添加、修改和删除键值对。它提供了多种自定义选项,如按钮文本、表单布局和输入过滤
- 支持通过
v-model双向绑定任意对象,(包含受控和非受控) 可动态增删字段 - 支持将值解析为:字符串 / 数字 / 数组(字符串数组、数字数组)
- 文案、样式、数组分隔符等均可配置
安装
# 任意一种
npm install dynamicformdjx-react
# or
yarn add dynamicformdjx-react
# or
pnpm add dynamicformdjx-react动态表单
与Antd配合
需配合antd v5+ 版本以上
简单表单
import {useRef, useState} from "react";
import {Button, Input, Radio} from "antd";
import {AdDynamicForm, type adDynamicFormRef, renderInput} from "dynamicformdjx-react/antd";
import {omitFormCommonKey, OmitValue, useDyForm, useReactiveForm} from "dynamicformdjx-react";
import type {PresetType} from "dynamicformdjx-react/types";
import type {Rule} from "antd/es/form";
type RowProps = {
username: string
password: string
desc: string
preset: string
}
const SimpleForm = () => {
const [presetType, setPresetType] = useState<PresetType>('fullRow')
const [formItems, setFormItems] = useReactiveForm<RowProps, Rule | Rule[]>([
{
key: "username",
label: "用户名",
value: "",
allowClear: true,
rule: [{required: true, message: 'Please input your username!', validateTrigger: 'onBlur'}],
render2: f => renderInput({}, f),
span: 12
},
{
key: "password",
label: "密码",
required: true,
value: "",
render2: (f) => <Input.Password placeholder="请输入密码" {...OmitValue(f, omitFormCommonKey)}/>,
span: 8,
offset: 2,
sort: 0
},
{
key: "preset",
label: "表格预设",
value: "fullRow",
render2: (f) => <Radio.Group
value={f.value}
options={[
{value: 'fullRow', label: 'row'},
{value: 'grid', label: 'grid'},
]}
onChange={(v) => {
setPresetType(v.target.value)
}}
/>,
}
])
const useForm = useDyForm([formItems, setFormItems])
const antdFormRef = useRef<adDynamicFormRef>(null)
const rules: Partial<Record<keyof RowProps, Rule | Rule[]>> = {
desc: [{required: true, message: '请输入详情'}]
}
return (
<div className='dynamicFormTest'>
<AdDynamicForm ref={antdFormRef} rules={rules} validateTrigger={null} items={formItems}
preset={presetType}/>
<div className="footer" style={{
display: 'flex',
gap: '5px'
}}>
<Button color={'green'} variant={'outlined'} onClick={() => {
// const res=antdFormRef.current?.getResult?.()
const res = useForm.getValues()
console.log(res)
}}>getData</Button>
<Button color={'orange'} variant={'outlined'} onClick={() => {
useForm.setValues({
username: 'antd',
password: 'I love you'
})
}}>setData</Button>
<Button color={'blue'} variant={'outlined'} onClick={() => {
antdFormRef.current?.validator().then(v => {
console.log(v)
}).catch(r => {
console.log(r)
})
}}>validator</Button>
<Button color={'red'} variant={'outlined'} onClick={() => {
useForm.onReset()
}}>reset</Button>
<Button variant={'outlined'} onClick={() => {
useForm.setDisabled(true)
}}>setDisabled</Button>
</div>
</div>
);
};
export default SimpleForm;
自定义表单
import {useRef} from "react";
import {Button, Input, Select} from "antd";
import {
DynamicInput,
type dynamicInputRef,
omitFormCommonKey,
OmitValue,
useDyForm,
useReactiveForm
} from "dynamicformdjx-react";
import {AdDynamicForm, type adDynamicFormRef} from "dynamicformdjx-react/antd";
import type {Rule} from "antd/es/form";
type RowProps = {
username: string
job: string
json: object
}
const CustomForm = () => {
const [formItems, setFormItems] = useReactiveForm<RowProps, Rule | Rule[]>([
{
key: "username",
label: "用户名",
value: "",
allowClear: true,
render2: (f) => <Input placeholder="请输入姓名" {...OmitValue(f, omitFormCommonKey)}/>,
rule: [
{
required: true,
message: 'Please confirm your username!',
},
{
validator: async (_, value) => {
if (!value) return; // 交给 required 处理
if (value.length < 3) {
throw new Error('至少 3 个字符');
}
},
}
],
},
{
key: "job",
label: "职位",
value: "",
required: true,
render2: (f) => <Select
style={{
width: '100%'
}}
options={[
{value: 'jack', label: 'Jack'},
{value: 'lucy', label: 'Lucy'},
{value: 'Yiminghe', label: 'yiminghe'},
{value: 'disabled', label: 'Disabled', disabled: true},
]}
/>,
},
{
key: "json",
label: "Json",
value: {},
isCustom: true,
rule: [
{
required: true,
message: 'json 不能为空'
},
{
validator: async (_, value) => {
if (!value || Object.keys(value).length === 0) {
throw new Error('json 不能为空');
}
},
}
],
render2: f => {
return <DynamicInput ref={dynamicInputRef} value={f.value} onChange={(v: object) => {
f.value = v
}} isController/>
},
},
])
const useForm = useDyForm([formItems, setFormItems])
const antdFormRef = useRef<adDynamicFormRef>(null)
const dynamicInputRef = useRef<dynamicInputRef>(null)
return (
<div className='dynamicFormTest'>
<AdDynamicForm ref={antdFormRef} items={formItems}/>
<div className="footer" style={{
display: 'flex',
gap: '5px'
}}>
<Button color={'green'} variant={'outlined'} onClick={() => {
// const res=antdFormRef.current?.getResult?.()
const res = useForm.getValues()
console.log(res)
}}>getData</Button>
<Button color={'orange'} variant={'outlined'} onClick={() => {
useForm.setValues({
username: 'antd',
job: 'jack'
})
dynamicInputRef.current?.onSet?.({
a: 'Hello world',
b: 1314,
c: [5, 2, 0]
})
}}>setData</Button>
<Button color={'blue'} variant={'outlined'} onClick={() => {
antdFormRef.current?.validator().then(v => {
console.log(v)
}).catch(r => {
console.error(r)
})
}}>validator</Button>
<Button color={'red'} variant={'outlined'} onClick={() => {
useForm.onReset()
dynamicInputRef.current?.onSet?.({})
}}>reset</Button>
</div>
</div>
);
};
export default CustomForm;
装饰表单
import {DATETIME_FORMAT, TIME_FORMAT, useDyForm} from "dynamicformdjx-react";
import type {Rule} from "antd/es/form";
import {
AdDynamicForm,
type adDynamicFormRef, useDecorateForm,
datePickerFormat, renderDatePicker
} from "dynamicformdjx-react/antd";
import {useRef} from "react";
import {Button} from "antd";
type FormRow = {
password: string
job: number
birthday: string
time: string
}
const DecorateForm = () => {
const [formItems, setFormItems] = useDecorateForm<FormRow, Rule | Rule[]>([
{
key: "password",
label: "密码",
value: null,
allowClear: true,
placeholder: '请输入密码',
required: true,
type: 'password',
renderType: 'renderInput'
},
{
key: "job",
label: "职位",
value: null,
allowClear: true,
options: ['前端', '后端'].map((label, value) => ({label, value})),
renderType: 'renderSelect',
},
{
key: "birthday",
label: "生日",
value: null,
// render2: f => renderDatePicker({type: 'datetime', showTime: true}, f),
renderType: 'renderDatePicker',
renderProps: {
type: 'datetime', showTime: true, isRange: true
},
formItemProps: {
...datePickerFormat({formatStr: DATETIME_FORMAT})
}
},
{
key: "time",
label: "时间",
value: ['00:00:00', '23:59:00'],
renderType: 'renderDatePicker',
renderProps: {
isRange: true,
},
formItemProps: {
...datePickerFormat({formatStr: TIME_FORMAT})
}
}
])
const useForm = useDyForm([formItems, setFormItems])
const antdFormRef = useRef<adDynamicFormRef>(null)
return (
<div className='dynamicFormTest'>
<AdDynamicForm ref={antdFormRef} items={formItems}/>
<div className="footer" style={{
display: 'flex',
gap: '5px'
}}>
<Button color={'green'} variant={'outlined'} onClick={() => {
// const res=antdFormRef.current?.getResult?.()
const res = useForm.getValues()
console.log(res)
}}>getData</Button>
<Button color={'orange'} variant={'outlined'} onClick={() => {
useForm.setValues({
password: 'Antd',
job: 0,
birthday: '2026-02-11'
})
}}>setData</Button>
<Button color={'blue'} variant={'outlined'} onClick={() => {
antdFormRef.current?.validator().then(v => {
console.log(v)
}).catch(r => {
console.log(r)
})
}}>validator</Button>
<Button color={'red'} variant={'outlined'} onClick={() => {
useForm.onReset()
}}>reset</Button>
<Button variant={'outlined'} onClick={() => {
useForm.setDisabled(true)
}}>setDisabled</Button>
</div>
</div>
);
}
export default DecorateForm;总表单
import {useRef, useState} from "react";
import {Button, Input, Radio} from "antd";
import {
AdDynamicForm,
type adDynamicFormRef, renderCheckbox, renderCheckboxGroup, renderDatePicker, renderDynamicTags,
renderInput, renderInputNumber,
renderPopSelect, renderRadioButtonGroup, renderRadioGroup,
renderSelect, renderSlider, renderSwitch, renderTimePicker,
renderTreeSelect
} from "dynamicformdjx-react/antd";
import {useDyForm, useReactiveForm} from "dynamicformdjx-react";
import type {Rule} from "antd/es/form";
type RowProps = {
username: string
password: string
gender: number
description: string
email: string
birthday: string
desc: string
sex: number
birthdayT: number
admin: number
favorite: number[]
job: number
job2: number
job3: number
checkbox: boolean
future: string[]
slider: number
inputNumber: number
}
const AllForm = () => {
const [formItems, setFormItems] = useReactiveForm<RowProps, Rule | Rule[]>([
{
key: "username",
label: "用户名",
value: "",
allowClear: true,
render2: f => renderInput({}, f),
},
{
key: "password",
label: "密码",
required: true,
value: "",
render2: (f) => renderInput({}, {...f, type: 'password'}),
},
{
key: "gender",
label: "性别",
value: null,
placeholder: '请选择性别',
labelField: 'f',
valueField: 'v',
showSearch: true,
allowClear: true,
searchOnLabel: true,
options: [
{f: <b>男</b>, v: 0},
{f: '女', v: 1}
],
render2: (f) => renderSelect([], {}, f)
},
{
key: "job",
label: "职业",
value: null,
placeholder: '请选择职业',
labelField: 'f',
valueField: 'v',
showSearch: true,
allowClear: true,
searchOnLabel: true,
childField: 'childOptions',
options: [
{
f: '前端', v: '1', childOptions: [
{f: '网页开发', v: '1-1'},
{f: '小程序开发', v: '1-2'},
]
},
{
f: '后端', v: '2', childOptions: [
{f: '后台开发', v: '2-1'},
{f: '运维', v: '2-2'},
]
}
],
render2: (f) => renderTreeSelect([], {
treeDefaultExpandAll: true
}, f),
},
{
key: "job2",
label: "职位2",
value: null,
labelField: 'l',
valueField: 'v',
options: ['Drive My Car', 'Norwegian Wood'].map((label, index) => ({
l: label,
v: label,
children: [
{l: 'aaa' + index, v: 'aaa' + index},
{l: 'bbb' + index, v: 'bbb' + index},
]
})),
// mode: 'multiple',
render2: f => renderPopSelect([], {}, f),
},
{
key: "sex",
label: "性别",
labelField: 'label1',
valueField: 'value1',
value: null,
options: [
{label1: '男', value1: 0}, {label1: '女', value1: 1},
],
render2: f => renderRadioGroup([], {}, f),
},
{
key: "favorite",
label: "爱好",
labelField: 'fl',
valueField: 'fv',
sort: 1,
options: [
{fl: '吃饭', fv: 0},
{fl: '睡觉', fv: 1},
{fl: '打豆豆', fv: 2},
],
value: [],
render2: f => renderCheckboxGroup([], {}, f),
},
{
key: "admin",
label: "管理员?",
value: null,
render2: f => renderSwitch({}, f),
},
{
key: "birthday",
label: "生日",
value: null,
render2: f => renderDatePicker({showTime: true}, f),
},
{
key: "birthdayT",
label: "时间",
value: null,
render2: f => renderTimePicker({}, f),
},
{
key: "future",
label: "未来",
value: [
{label: '你没见过不等于没有', value: 'hello world 1'},
{
label: '不要给自己设限',
value: 'hello world 2'
},
{
label: '不要说连升两级',
value: 'hello world 3'
},
{
label: '直接升到 CEO 都是有可能的',
value: 'hello world 4'
}
],
render2: f => renderDynamicTags(f.value, {}, f),
},
{
key: "checkbox",
label: "复选",
value: true,
render2: f => renderCheckbox({}, f),
formItemProps: {
valuePropName: 'checked',
}
},
{
key: "slider",
label: "滑块",
value: 0,
render2: f => renderSlider({}, f),
},
{
key: "inputNumber",
label: "数字输入",
value: 20,
render2: f => renderInputNumber({}, f),
},
])
const useForm = useDyForm([formItems, setFormItems])
const antdFormRef = useRef<adDynamicFormRef>(null)
const rules: Partial<Record<keyof RowProps, Rule | Rule[]>> = {
desc: [{required: true, message: '请输入详情'}]
}
return (
<div className='dynamicFormTest'>
<AdDynamicForm ref={antdFormRef} rules={rules} items={formItems}/>
<div className="footer" style={{
display: 'flex',
gap: '5px'
}}>
<Button color={'green'} variant={'outlined'} onClick={() => {
// const res=antdFormRef.current?.getResult?.()
const res = useForm.getValues()
console.log(res)
}}>getData</Button>
<Button color={'orange'} variant={'outlined'} onClick={() => {
useForm.setValues({
username: 'antd',
password: 'I love you'
})
}}>setData</Button>
<Button color={'blue'} variant={'outlined'} onClick={() => {
antdFormRef.current?.validator().then(v => {
console.log(v)
}).catch(r => {
console.log(r)
})
}}>validator</Button>
<Button color={'red'} variant={'outlined'} onClick={() => {
useForm.onReset()
}}>reset</Button>
<Button variant={'outlined'} onClick={() => {
useForm.setDisabled(true)
}}>setDisabled</Button>
</div>
</div>
);
};
export default AllForm;
动态录入
此录入无需组件库依赖
1.单组件
import {useState} from "react";
import {DynamicInput, dynamicFormRef} from "dynamicformdjx-react";
function App() {
const [obj, setObj] = useState<Record<string, any>>({
a: 'Hello world',
b: 1314,
c: [5, 2, 0]
});
const dynamicInputRef = useRef<dynamicFormRef>(null)
return (<div>
<DynamicInput ref={dynamicInputRef} isController value={obj} onChange={(e) => setObj(e)}/>
<pre>
{JSON.stringify(obj, null, 2)}
</pre>
<div>
<button onClick={() => {
dynamicInputRef.current?.onSet?.({
test: 'hello World'
})
}}>setData
</button>
</div>
</div>)
}
export default App2.级联基本使用
import {useState} from "react";
import {DynamicCascadeInput, dynamicCascadeInputRef} from "dynamicformdjx-react";
const App = () => {
const [obj, setObj] = useState<Record<string, any>>({
a: {
b: {
c: {
d: {
e: "hello world"
}
}
}
},
aa: [5, 2, 0],
aaa: 1314
});
const dynamicInputRef = useRef<dynamicCascadeInputRef>(null)
return (<div>
<DynamicCascadeInput ref={dynamicInputRef} isController value={obj} onChange={(e) => setObj(e)}/>
<pre>
{JSON.stringify(obj, null, 2)}
</pre>
<div>
<button onClick={() => {
dynamicInputRef.current?.onSet?.({
test: 'hello world'
})
}}>setData
</button>
</div>
</div>)
}
export default App;