Skip to content

Instantly share code, notes, and snippets.

@SuneBear
Created February 21, 2019 05:41
Show Gist options
  • Save SuneBear/f9dffc8430c8ce7ee52ecb9bdf80b89e to your computer and use it in GitHub Desktop.
Save SuneBear/f9dffc8430c8ce7ee52ecb9bdf80b89e to your computer and use it in GitHub Desktop.
Form Generator Component
import React, { PropTypes } from 'react'
import { Form, Select, Input, Switch, DatePicker, InputNumber } from 'antd'
import cx from 'classnames'
import { getFormField } from './Field'
import styles from './form.styl'
const empty = data => data
const noop = () => {}
export default function createObjectForm (schema, { onValuesChange = noop, transformValue = empty, formatData = empty } = {}) {
const wrapOnValuesChange = (props, values) => {
const { onChange, content } = props
values = transformValue({ ...content, ...values }, schema)
onValuesChange(props, values)
}
class TheForm extends React.Component {
static propTypes = {
form: PropTypes.object,
content: PropTypes.object,
layout: PropTypes.string,
className: PropTypes.string,
children: PropTypes.node
}
static defaultProps = {
layout: 'horizontal',
content: {}
}
render () {
const { getFieldDecorator } = this.props.form
const { content, layout, className, children } = this.props
return (
<Form
layout={ layout }
className={ cx(styles.wrap, className) }
>
{
schema.properties.map(property => getFormField(property, this.props.form, formatData(content, schema), layout))
}
{ children }
</Form>
)
}
}
return Form.create({ onValuesChange: wrapOnValuesChange })(TheForm)
}
import React from 'react'
import cx from 'classnames'
import { Form, Select, Input, Switch, DatePicker, InputNumber, Radio } from 'antd'
import { transformValue, getValidateRules, getValueFromEvent, getTrigger, getValuePropName } from './utils'
import { Upload } from 'mainComponents/Upload'
import { TopicSearchBox } from 'mainComponents/SearchBox'
import styles from './form.styl'
const Option = Select.Option
const FormItem = Form.Item
const RadioButton = Radio.Button
const RadioGroup = Radio.Group
const RangePicker = DatePicker.RangePicker
export function getFormField (property, form, content, layout) {
const { getFieldDecorator, getFieldsValue, setFieldsValue } = form
property = {
visible: () => true,
disable: () => false,
valid: () => true,
...property
}
const formItemLayout = layout === 'horizontal' ? {
labelCol: {
xs: { span: 24 },
sm: { span: 6 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 14 }
}
} : null
const values = getFieldsValue()
const isValid = property.valid(values)
const InputComponent = getFieldInput(property)
const InputComponentProps = {
disabled: property.disable(values),
setFieldsValue
}
let initialValue = getContentValue(content, property.field) === undefined ? property.defaultValue : transformValue(getContentValue(content, property.field), property)
return (
isValid ? (
<FormItem
{ ...formItemLayout }
className={ cx({ [styles.hidden]: !(property.visible(values)) }) }
label={ property.title }
key={ property.field }
>
{
getFieldDecorator(property.field, {
rules: getValidateRules(property.rules),
initialValue,
valuePropName: getValuePropName(property.format),
trigger: getTrigger(property.format),
getValueFromEvent: getValueFromEvent(property.format)
})(
React.cloneElement(InputComponent, InputComponentProps)
)
}
</FormItem>
) : null
)
}
export function getFieldInput (property) {
return property.component ? property.component : getCommonFiledInput(property)
}
export function getContentValue (content, field) {
const paths = field.split('.')
let value = content
paths.forEach(path => { value = value[path] })
return value
}
function getCommonFiledInput (property) {
let source = property.source
if (property.source && typeof property.source === 'function') {
source = property.source()
}
// 常用的类型组件
switch (property.format) {
case 'Select':
return (
<Select>
{
source.map(option => <Option key={ option.key } value={ option.value }>{ option.title }</Option>)
}
</Select>
)
case 'multiple':
return (
<Select
mode='multiple'
placeholder={ property.placeholder }
>
{
source.map(option => <Option key={ option.key } value={ option.value }>{ option.title }</Option>)
}
</Select>
)
case 'tags':
return (
<Select
mode='tags'
placeholder={ property.placeholder }
style={ { minWidth: 150 } }
>
{
source.map(option => <Option key={ option.key } value={ option.value }>{ option.title }</Option>)
}
</Select>
)
case 'Input':
return (
<Input placeholder={ property.placeholder } />
)
case 'TextArea':
// 该type 2.12 后废弃,请直接使用 Input.TextArea
return (
<Input type='textarea' placeholder={ property.placeholder } />
)
case 'InputNumber':
return (
<InputNumber />
)
case 'Switch':
return (
<Switch />
)
case 'TopicSearchBox':
return (
<TopicSearchBox />
)
case 'Upload':
return (
<Upload />
)
case 'DatePicker':
return (
<DatePicker format='YYYY-MM-DD HH:mm' showTime disabledDate={ property.disabledDate } />
)
case 'RangePicker':
return <RangePicker format='YYYY-MM-DD HH:mm' />
case 'RadioGroup':
return (
<RadioGroup>
{
source.map(option => <RadioButton key={ option.key } value={ option.value }>{ option.title }</RadioButton>)
}
</RadioGroup>
)
default:
return <span>没有对应input 组件</span>
}
}

createForm

参数 说明 类型 默认值
schema schema.md中已有定义及详细介绍 object -
options 一些关于创建通用表单的配置项 object -

options的数据结构

属性 说明 类型 默认值
transformValue 将表单的value进行转换,不推荐使用在新表单中。应该将这部分逻辑抽离在Input组件内部 (values, schema) => values -
formatData 将输入表单的Data进行转换,不推荐使用在新表单中。应该将这部分逻辑抽离在Input组件内部 (data, schema) => data -

Form

props 详解

属性 说明 类型 默认值
content 表单field的数据 object -
layout 表单布局方式 'horizontal' 'vertical'

form schema

数据结构

属性 说明 类型 默认值
type 什么类型的表单 'object' , 'array' 'object'
title 表单标题 string -
properties 表单 fields array []
groups 表单field分组 array []

property中的结构说明

属性 说明 类型 默认值
field 表单该数据项的字段名 string -
title 该field的lable提示,可为空 string -
type 该字段的数据类型 'images', 'image', 'urls', 'url', 'string', 'longString' ,'boolean', 'array' string
format 应该使用的'input'组件类型 antd 中对应的组件 和 编辑工具中自定义的 string
placeholder format 为 TextArea或Input 时可用 string -
defaultValue 该field的默认值 any -
source fromat 为 Select, tags, multiple时的数据源 () => datas 或者datas -
valid 控制该field 是否展现 (values) => boolean -
visible 控制该field是否隐藏, (values 中依然存在该字段) (values) => boolean -
disable 控制该field是否可编辑 (values) => boolean -
rules 同 antd 的form 中的定义,用于校验表单 array -

type只是对 field的一个输入输出值的弱限定。之后有可能会使用ts 重写加强类型检查。之后会对 image 等的结构做一个 规定。希望后端也能按照规定去实现表单的提交。最好的结果是将之前的image 的字段也能够重写。

group的结构说明 尚未实现

属性 说明 类型 默认值
fields 属于该group的field, 按照数组中顺序渲染 ['a','b'] -
title 该分组名 string -
collapsible 是否可以折叠 boolean -
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment