cjd-parkball
Version:
> 中后台业务组件库,中后台就像公园,进入需要买门票(登录),所以以 Parkball(公园球) 命名,公园内必定捕获!作为一个组件库,提供使用方法文档,方便开发者的调用
495 lines (464 loc) • 14.9 kB
JavaScript
import React, { PureComponent } from 'react'
import moment from 'moment'
import get from 'lodash/get'
import PropTypes from 'prop-types'
import template from 'lodash/template'
import { Input, InputNumber, Button, DatePicker, Select, Checkbox, Radio, Cascader, Upload, Form, Icon, SelectTable } from '../..'
import { request, errorTip } from '../utilities'
const { Item } = Form
const { Option } = Select
const { TextArea } = Input
// eslint-disable-next-line no-eval
export const urlFunc = (url, option = {}) => template(url)({ option })
const componentName = 'FormBuilder Field'
export default class extends PureComponent {
static defaultProps = {
dataTransfer: options => (options.result ? options.result : options),
}
static propTypes = {
dataTransfer: PropTypes.func,
}
state = {
options: [],
}
componentWillReceiveProps (nextProps) {
if (this.props.field.resetOptions !== nextProps.field.resetOptions) {
this.props.form.resetFields([nextProps.field.name])
this.setState({
options: [],
})
}
}
queryDynamic (url, query) {
const { dataTransfer = this.props.dataTransfer } = this.props.field
request(urlFunc(url, query)).then((options) => {
let newOptions
if (dataTransfer instanceof Function) {
newOptions = dataTransfer(options)
} else {
newOptions = options
}
this.setState(() => ({ options: newOptions }))
})
}
renderHideItem (props) {
const { getFieldProps } = this.props.form
return props.hide.map((field, index) => {
return (<Item key={`${field.name}-${index}`} className="form-hide-item">
<Input {...getFieldProps(field.name)} />
</Item>)
})
}
verifyParams = (fields) => {
const { name } = fields
if (!name) {
errorTip(componentName, 'field.name 不能为空')
return false
}
return true
}
handleChange = (value, selectedOptions) => {
const { triggerConfig = {}, onChange } = this.props.field
onChange && onChange(value, selectedOptions)
if (triggerConfig.onChange) {
this.batchHandleFun(triggerConfig.onChangeFunc, value, selectedOptions)
}
return true
}
batchHandleFun = (list, value, selectedOptions) => {
list.forEach((item) => {
item && item(value, selectedOptions)
})
}
handleSelect = (value, option, onSelectHandler = () => null) => {
const { onSelect, triggerConfig = {} } = this.props.field
if (onSelect) {
onSelect(value, option)
} else {
this.setState({ selectOption: option.props.option }, () => {
const { selectOption } = this.state
const { setFieldsValue } = this.props.form
const hideFields = this.props.field.hide || []
const fields = {}
hideFields.forEach((item) => {
fields[item.name] = item.get ? item.get(selectOption) : get(selectOption, item.valueKey || item.name)
})
setFieldsValue(fields)
})
onSelectHandler(value, option)
}
if (triggerConfig.onSelect) {
this.batchHandleFun(triggerConfig.onSelectFunc, value, option)
}
}
handleResult = (value) => {
const { onChange } = this.props.field
onChange && onChange(value.id)
const { setFieldsValue } = this.props.form
const hideFields = this.props.field.hide || []
const fields = { [this.props.field.name]: value.id }
hideFields.forEach((item) => {
fields[item.name] = value[item.get]
})
setFieldsValue(fields)
}
render () {
const { getFieldDecorator } = this.props.form
const {
// support: select, cascader
// todo support: radio, checkbox
hide, // 隐藏字段
valueKey = 'value', // 每一个选项的值 key
labelKey = 'label', // 每一个选项的展示名 key
initialValue, // 初始值
defaultData = [], // 级联选择器初始化数据
...attrs
} = this.props.field
const {
// 通用
label, // 字段名成
type, // 表单元素类型
placeholder, // 占位提示
name, // 表单元素字段key
required = true, // 是否必填,默认必填
emptyMessage, // 为空时的提示信息
options, // 可选值
url, // 数据动态来源
validator = {}, // 自定义校验规则
setter = null, // 数据序列化输出
getter = null, // 输入数据序列化
// 下拉
optionChild, // 自定义下拉选项
// datepicker
// format, // 格式化
// upload
uploadText,
// custom
customNode,
dataTransfer = this.props.dataTransfer, // 处理返回数据
} = this.props.field
const onChange = this.handleChange // 值改变时的回调
const onSelect = this.handleSelect // 值选择的回调
const onResult = this.handleResult // 值选择的回调
// 对传入参数进行校验
if (!this.verifyParams(this.props.field)) {
return null
}
switch (type) {
case 'text': {
return (
<Item label={label}>
{
getFieldDecorator(name, {
initialValue,
rules: [{
required,
message: emptyMessage || `${label}不能为空`,
}, ...validator],
onChange,
})(<Input {...attrs} placeholder={placeholder || '请输入'} />)
}
</Item>
)
}
case 'number': {
return (
<Item label={label}>
{
getFieldDecorator(name, {
initialValue,
rules: [{
required,
message: emptyMessage || `${label}不能为空`,
}, ...validator],
onChange,
})(<InputNumber {...attrs} placeholder={placeholder || '请输入'} />)
}
</Item>
)
}
case 'radio': {
return (
<Item label={label}>
{
getFieldDecorator(name, {
initialValue,
rules: [{
required,
message: emptyMessage || `${label}不能为空`,
}, ...validator],
onChange,
})(<Radio.Group>
{
options.map(option => <Radio key={option[valueKey]} value={option[valueKey]} disabled={option.disabled || false}>{option[labelKey]}</Radio>)
}
</Radio.Group>)
}
{
hide && hide.length && this.renderHideItem({ hide })
}
</Item>
)
}
case 'checkbox': {
return (
<Item label={label}>
{
getFieldDecorator(name, {
initialValue,
rules: [{
required,
message: emptyMessage || `${label}不能为空`,
}, ...validator],
onChange,
})(<Checkbox.Group>
{
options.map(option => <Checkbox key={option[valueKey]} value={option[valueKey]} disabled={option.disabled || false}>{option[labelKey]}</Checkbox>)
}
</Checkbox.Group>)
}
{
hide && hide.length && this.renderHideItem({ hide })
}
</Item>
)
}
case 'selectTable': {
return (
<Item label={label}>
{
getFieldDecorator(name, {
initialValue,
rules: [{
required,
message: emptyMessage || `${label}不能为空`,
}, ...validator],
})(<SelectTable
label={label}
name={name}
onResult={onResult}
placeholder={placeholder || '请选择'}
>
</SelectTable>)
}
{
hide && hide.length && this.renderHideItem({ hide })
}
</Item>
)
}
case 'select': {
if (options) {
return (
<Item label={label}>
{
getFieldDecorator(name, {
initialValue,
rules: [{
required,
message: `${label}不能为空`,
}],
})(<Select
onSelect={onSelect}
placeholder={placeholder || '请选择'}
{...attrs}
>
{
options.map((option) => {
return optionChild ? React.cloneElement(optionChild, {
...this.props.field,
option,
}) :
<Option key={option[valueKey]} value={option[valueKey]} option={option}>{option[labelKey]}</Option>
})
}
</Select>)
}
{
hide && hide.length && this.renderHideItem({ hide })
}
</Item>
)
}
return (
<Item label={label}>
{
getFieldDecorator(name, {
initialValue,
rules: [{
required,
message: `${label}不能为空`,
}],
})(<Select
showSearch
onFocus={() => (this.state.options.length ? null : this.queryDynamic(url))}
onSearch={value => this.queryDynamic(url, { [name]: value })}
onSelect={onSelect}
placeholder={placeholder || '请选择'}
{...attrs}
>
{
this.state.options.map((option) => {
return optionChild ? React.cloneElement(optionChild, {
...this.props.field,
key: option[valueKey],
option,
}) :
<Option key={option[valueKey]} value={option[valueKey]} option={option}>{option[labelKey]}</Option>
})
}
</Select>)
}
{
hide && hide.length && this.renderHideItem({ hide })
}
</Item>
)
}
case 'cascader': {
const loadData = (selectedOptions) => {
const targetOption = selectedOptions[selectedOptions.length - 1] || { id: 0 }
targetOption.loading = true
request(urlFunc(url, targetOption)).then((optionItem) => {
const optionChildren = dataTransfer(optionItem)
targetOption.loading = false
targetOption.children = optionChildren
this.setState({
options: this.state.options.length ? [...this.state.options] : [...optionChildren],
})
})
}
if (options) {
return (
<Item label={label}>
{
getFieldDecorator(name, {
initialValue,
rules: [{
required,
message: `${label}不能为空`,
}],
})(<Cascader
options={options}
placeholder={placeholder || '请选择'}
{...attrs}
onChange={(value, selectedOptions) => onChange(value, selectedOptions)}
/>)
}
{
hide && hide.length && this.renderHideItem({ hide })
}
</Item>
)
}
return (
<Item label={label}>
{
getFieldDecorator(name, {
initialValue,
rules: [{
required,
message: `${label}不能为空`,
}],
})(<Cascader
options={this.state.options.length ? this.state.options : defaultData}
onFocus={this.state.options.length ? null : loadData}
loadData={loadData}
fieldNames={{ label: labelKey, value: valueKey }}
placeholder={placeholder || '请选择'}
onChange={(value, selectedOptions) => onChange(value, selectedOptions)}
{...attrs}
/>)
}
{
hide && hide.length && this.renderHideItem({ hide })
}
</Item>
)
}
case 'upload': {
return (
<Item label={label}>
{
getFieldDecorator(name, {
initialValue,
rules: [{
required,
message: `${label}不能为空`,
}],
})(<Upload {...attrs} action={url} defaultFileList={initialValue}>
<Button>
<Icon type="upload" /> {uploadText || '上传'}
</Button>
</Upload>)
}
{
hide && hide.length && this.renderHideItem({ hide })
}
</Item>
)
}
case 'date': {
return (<Item label={label}>
{
getFieldDecorator(name, {
initialValue: initialValue && moment(initialValue),
rules: [{
required,
message: `${label}不能为空`,
}],
})(<DatePicker placeholder={placeholder || '请选择'} {...attrs} />)
}
{
hide && hide.length && this.renderHideItem({ hide })
}
</Item>)
}
case 'textarea': {
return (<Item label={label}>
{
getFieldDecorator(name, {
initialValue,
rules: [{
required,
message: `${label}不能为空`,
}],
})(<TextArea placeholder={placeholder || '请选择'} {...attrs} />)
}
</Item>)
}
case 'custom': {
const { field, form } = this.props
return customNode({ form, field })
}
case 'hidden': {
return (
<Item className="form-hide-item">
{
getFieldDecorator(name, {
initialValue,
})(<Input {...attrs} />)
}
</Item>
)
}
default: {
return (<Item label={label}>
{
getFieldDecorator(name, {
initialValue,
rules: [{
required,
message: emptyMessage || `${label}不能为空`,
}],
onChange,
})(<Input placeholder={placeholder || '请输入'} {...attrs} />)
}
{
hide && hide.length && this.renderHideItem({ hide })
}
</Item>)
}
}
}
}