UNPKG

ssc-grid

Version:

React grid component for SSC 3.0

1,161 lines (1,076 loc) 39.8 kB
import _typeof from 'babel-runtime/helpers/typeof'; import _JSON$stringify from 'babel-runtime/core-js/json/stringify'; import _extends from 'babel-runtime/helpers/extends'; import _classCallCheck from 'babel-runtime/helpers/classCallCheck'; import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn'; import _inherits from 'babel-runtime/helpers/inherits'; import classNames from 'classnames'; import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Button from 'react-bootstrap/lib/Button'; import FormGroup from 'react-bootstrap/lib/FormGroup'; import ControlLabel from 'react-bootstrap/lib/ControlLabel'; import HelpBlock from 'react-bootstrap/lib/HelpBlock'; import Col from 'react-bootstrap/lib/Col'; import ReactBootstrapForm from 'react-bootstrap/lib/Form'; import Grid from 'react-bootstrap/lib/Grid'; import Row from 'react-bootstrap/lib/Row'; // 表单(form)控件(control/widget) import FormControl from 'react-bootstrap/lib/FormControl'; import Checkbox from 'react-bootstrap/lib/Checkbox'; // 参照控件 import { Refers } from 'ssc-refer'; // YBZSAAS-461 // IE11不支持Array.prototype.find() import 'core-js/fn/array/find'; import { getFieldDefaultValue } from './utils/sscgridUtils'; import * as validationUtils from './utils/validation'; import * as actions from './Form.actions'; /** * 控件(control/widget)分类 * Command input: Button, Drop-down list, ... * Data input-output: Checkbox Color picker Combo box Cycle button Date Picker Grid view List box List builder Radio button Scrollbar Search box Slider Spinner Text box * 来源:https://en.wikipedia.org/wiki/Widget_(GUI) */ // 使用我们自己造的轮子 import TextField from './TextField'; import DatePicker from './DatePicker2'; /** * helper functions */ /** * @param {Array|Null} validators 当不需要校验的时候为null * @return {boolean} 当是必选项的时候,输出true否则为false */ function showRequiredStar(validators) { if (!validators) { return false; } return validators.find(function (_ref) { var type = _ref.type, type2 = _ref.type2; return type === 'required' || type2 === 'required'; }) !== undefined; } var propTypes = { /** * 填充表单值<br> * 时间类型比较特殊,请先转成 * <a href="http://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a>格式的字符串 * 之后,再传进来。 * ``` * defaultData = { * date: new Date('2017-02-14').toISOString() * } * ``` */ defaultData: PropTypes.object, /** * 表单中的数据 * fieldsModel数据举例: * ```js * [ * { * type: 'string', * id: 'formValidationEmail', * label: '邮箱地址', * validators: [ * {type: 'email'} * ] * }, * { * type: 'custom', * component: <CustomComponent> * } * ] * ``` * schema为 * ```js * fieldsModel = [ fieldModel, fieldModel, ... ]; * ``` * ## type字段 * 字段类型type: * - 0 `string` 字符类型 * - 1 `int` 整形类型 * - 2 `double` 数值类型 * - 3 `date` 日期类型 * - 4 `boolean` 布尔类型 * - 5 `ref` 参照类型 * - 6 `enum` 枚举型 * - 9 `textarea` 多行文本类型 * - `custom` 自定义类型 * * ### string字符型 * ```js * { * type: 'string', * id: 'formValidationEmail', * label: '邮箱地址', * validators: [ * { type: 'email' } * ] * } * ``` * * * ### date日期类型 * 字段定义举例: * ```js * { * type: 'date', * dateConfig: { * locale: 'en_US', * todayButton:'Today' * } * ``` * * ### custom 自定义类型 * ```js * { * type: 'custom', * component: <CustomComponent> * } * ``` * 对于自定义类型,需要调用者传入一个组件,表单在回调该组件的时候,传入如下属性: * ```js * propTypes: { * customFieldModel: PropTypes.object, * customFieldValue: PropTypes.string, * onCustomFieldChange: PropTypes.func, * } * ``` * ### enum枚举型 * ```js * { * type: 'enum', * id: 'accountProperty', * label: '账户性质', * data: [ * { key: 'BASE', value: '基本' }, * { key: 'NORMAL', value: '一般' }, * { key: 'TEMPORARY', value: '临时' }, * { key: 'SPECIAL', value: '专用' }, * ], * } * ``` * * * ### ref参照型 * 字段定义举例: * ```js * { * type: 'ref', * referConfig: { * referConditions: { * refCode: 'org', * refType: 'tree', * rootName: '组织' * }, * referDataUrl: 'http://172.20.4.220/ficloud/refbase_ctr/queryRefJSON', * renderMenuItemChildren: (option, props, index) => ([ * <div>{option.code + ' ' + option.name}</div> * ]) * labelKey: 'name', * referExtend: { showDisabledBtnText: 'Display Disabled', showDisabledBtnText_Not: 'Hide Disabled' }, * }, * multiple:false * } * ``` * 所有`referConfig`下的属性直接向下传递给`Refers`组件, * 比如`referConfig = { foo: 'bar' }`,那么就相当于 * ```jsx * <Refers * foo="bar" * /> * ``` * 关于`Refers`组件的属性定义,详见[ssc-refer](https://ssc-refer.github.io/components.html) * ## validators字段 * 校验类型,比如 * ```js * validators: [ * { type: 'required' }, * { type: 'length', min: 3, max: 6, * helpText: '字符串长度应该大于等于3小于等于6' } * ] * ``` * schema为: * ```js * validators = [ validator, validator, ... ]; * ``` * `type`字段支持如下类型: * - `email` 邮件地址 * - `decimal` 数字,比如0.1, .3, 1.1, 1.00003, 4.0 * - `int` 整数 * - `mobilePhone` 手机号 * - `custom` 自定义格式 * * `helpText`字段是错误提示。如果不提供,则使用默认错误提示。 * 如果是自定义类型,则通过`matchFunc`参数传递校验函数 * ```js * { * type: 'custom', * helpText: value => '请输入正确的XX格式', * matchFunc: value => {} * } * ``` * 当`matchFunc`返回值为`true`的时候,认为校验通过 * 对于自定义类型,如果不提供`helpText`,则默认不显示错误提示。 * ## disabled字段 * 当值为`true`的时候禁用该字段,其他值都是不禁用该字段。 */ fieldsModel: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.string.isRequired, type: PropTypes.string.isRequired, label: PropTypes.string.isRequired, validators: PropTypes.arrayOf(PropTypes.shape({ type: PropTypes.string.isRequired })), disabled: PropTypes.boolean, referConfig: PropTypes.object, dateConfig: PropTypes.object, multiple: PropTypes.boolean })), PropTypes.object // 默认类型应该是数组,但是为了支持mobx传入observable object... ]).isRequired, /** * 自定义布局(bootstrap列布局) * 具体参照:https://react-bootstrap.github.io/components.html#grid-props-col * ```js * [ * ['id', 'name', 'code'], * ['src_system'] * ] * ``` */ layout: PropTypes.shape({ xs: PropTypes.number, sm: PropTypes.number, md: PropTypes.number, lg: PropTypes.number }), /** * 当控件的值发生改变的时候触发 * - 参数1 {String} `fieldId` 也就是传入组件中fieldsModel中的id<br> * - 参数2 {String} `value` 改变之后的值<br> * - 参数3 {Object} `opt` 可选参数,当type为string/boolean/enum等简单类型的时候,可以 * 通过opt.event获取Event对象。<br> * 当type为date类型的时候,可以通过opt.formattedValue获取格式化 * 之后的时间值。<br> */ onChange: PropTypes.func, /** * 当点击“重置”按钮的时候 */ onReset: PropTypes.func, /** * 当表单被提交的时候触发<br> * 当用户使用了自定义提交按钮的时候不会调用该回调<br> * 参数1. `formData`, 整个表单中所有控件的值,是一个JSON对象,结构和传入参数 * defaultData保持一致。<br> */ onSubmit: PropTypes.func, /** * 是否显示提交按钮 */ showSubmitButton: PropTypes.bool, /** * 取消按钮文本<br> * 默认值:取消 */ cancelLabel: PropTypes.string, /** * 确定按钮文本<br> * 默认值:完成 */ okLabel: PropTypes.string }; var defaultProps = { showSubmitButton: true }; var Form = function (_Component) { _inherits(Form, _Component); function Form(props) { _classCallCheck(this, Form); /** * 暂时只用于使用ref获取子组件的校验状态 * 结构示例 * ``` * { * id: {TextField}, * name: {TextField} * } * ``` */ var _this = _possibleConstructorReturn(this, _Component.call(this, props)); _this.fieldRefs = {}; _this.state = { /** * 记录当前表单的验证状态,这是一个键值对,其中key表示字段id,value表示 * 验证状态,用户需要自己判断所有字段是否都验证通过了 * 验证状态,验证状态分三种: * - 'success' 验证成功 * - 'error' 验证失败 * - null 未知状态,比如TextField组件刚mount上的时候,还不知道验证状态, * 除非触发了一次onChange事件,或者onSubmit才能知道其验证状态 * ``` * { * email: 'success', * name: 'error', * name2: null * } * 字段email是fieldId, 'success'是验证状态 * 这是react-bootstrap中关于form validation的直接映射 * ``` */ fieldsValidationState: {}, /** * 验证失败时候显示的帮助信息 * ``` * { * email: '请输入正确的邮件地址', * name: '请输入必选项内容' * } * ``` * 这是react-bootstrap中关于form validation的直接映射 */ fieldsHelpText: {}, formData: {}, /** * 提交按钮是否被禁用 * 当值为true的时候,提交按钮的样式为“禁用” */ submitButtonDisabled: false }; /** * 初始化表单的默认值 * 当传入组件的表单的字段的默认值为空(null/undefined)的时候,需要计算一下默认值 */ _this.state.formData = _extends({}, _this.props.defaultData); _this.props.fieldsModel.forEach(function (fieldModel) { _this.state.formData[fieldModel.id] = getFieldDefaultValue(fieldModel, _this.state.formData[fieldModel.id]); }); // Initialize validation state of all form field with null, but not hidden fields _this.props.fieldsModel.forEach(function (fieldModel) { if (fieldModel.hidden === true) { return; } if (fieldModel.validators) { _this.state.fieldsValidationState[fieldModel.id] = null; _this.state.fieldsHelpText[fieldModel.id] = ''; } }); return _this; } Form.prototype.componentWillMount = function componentWillMount() {}; Form.prototype.componentDidMount = function componentDidMount() {}; Form.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) { // 更新表单默认值 if (nextProps.defaultData !== this.props.defaultData) { var formData = _extends({}, nextProps.defaultData); nextProps.fieldsModel.forEach(function (fieldModel) { formData[fieldModel.id] = getFieldDefaultValue(fieldModel, formData[fieldModel.id]); }); this.setState(actions.updateFormData(formData)); } }; /** * Call this method from ref * 1. Show validation state on form controls * 2. call onSubmit callback * @memberof Form */ Form.prototype.submit = function submit() { this.handleSubmit(); }; // 以id来查询对应的字段模型 Form.prototype.getFieldModelById = function getFieldModelById(fieldId) { return this.props.fieldsModel.find(function (_ref2) { var id = _ref2.id; return id === fieldId; }); }; /** * 这里只处理简单类型的控件,比如input, select, checkbox * 不处理复杂类型的空间,比如date-picker */ Form.prototype.handleChange = function handleChange(fieldId, validators, event) { var _this2 = this; var target = event.target; var value = target.type === 'checkbox' ? target.checked : target.value; // const name = target.name; this.setState(actions.updateFieldValue(fieldId, value)); // 如果该字段需要校验,那么设置校验状态 if (validators) { this.setState(actions.updateFormFieldValidationState(fieldId, value, validators), function () /* prevState, props */{ // 现在校验状态来决定提交按钮的状态 _this2.setState(actions.updateSubmitButtonState()); }); } if (this.props.onChange) { this.props.onChange(fieldId, value, { event: event }); } }; // 只处理date-picker控件 Form.prototype.handleDatePickerChange = function handleDatePickerChange(fieldId, validators, value, formattedValue) { var _this3 = this; // console.log(fieldId, value, formattedValue); var newState = _extends({}, this.state); newState.formData[fieldId] = formattedValue; this.setState(newState); // 如果该字段需要校验,那么设置校验状态 if (validators) { this.setState(actions.updateFormFieldValidationState(fieldId, value, validators), function () /* prevState, props */{ // 现在校验状态来决定提交按钮的状态 _this3.setState(actions.updateSubmitButtonState()); }); } if (this.props.onChange) { this.props.onChange(true, fieldId, formattedValue, { value: value }); } }; /** * 参照修改后的回调 * @param {String} fieldId * @param {Array} selected * 目前不清楚为什么selected返回一个数组 * 先不管三七二十一,直接扔到state中,让用户可以获取到 * ``` * selected = [ * { * "id": "0500CC91-4A98-4C1D-A4D6-C6A0ABCC53AD", * "isLeaf": "true", * "name": "服务中心", * "pid": "", * "code": "02" * } * ] * ``` */ Form.prototype.handleReferChange = function handleReferChange(fieldId, validators, multiple, selected) { var _this4 = this; var onChange = this.props.onChange; // 清空或者设置新值 if (!multiple && selected.length > 0) { selected = selected[0]; } this.setState(actions.updateReferFieldValue(fieldId, selected, multiple), function () {}); // 如果该字段需要校验,那么设置校验状态 if (validators) { // 参照是一个复杂类型的值,需要专门处理。 var value = ''; if (multiple) { value = selected.length > 0 ? _JSON$stringify(selected) : ''; } else { // 对参照的API不了解,所以写死获取第一个 if (selected) { // 由于参照的字段是可变的,有时候是name有时候是displayName,所以这里不再进入对象 // 而是直接将对象转成字符串进行判断。 // value = selected[0].name || ''; value = _JSON$stringify(selected); } else { value = ''; } } this.setState(actions.updateFormFieldValidationState(fieldId, value, validators), function () /* prevState, props */{ // 现在校验状态来决定提交按钮的状态 _this4.setState(actions.updateSubmitButtonState()); }); } if (onChange) { onChange(fieldId, selected, {}); } }; /** * 参照回调 * @param {String} fieldId * @param {Array} validators * @param {Event} event */ Form.prototype.handleReferBlur = function handleReferBlur() /* fieldId, validators, event */{}; Form.prototype._renderMenuItemChildren = function _renderMenuItemChildren(option, props, index) { var label = option.code + ' ' + option.name; return React.createElement( 'span', { title: label, key: index }, label, ' ' ); }; /** * 自定义类型字段发生变化的时候 * @param {String} fieldId 字段ID * @param {*} value value为动态类型,具体类型由`CustomComponent.prop.value`的类型决定 */ Form.prototype.handleCustomFieldChange = function handleCustomFieldChange(fieldId, validators, value) { var _this5 = this; this.setState(actions.updateFieldValue(fieldId, value)); this.setState(actions.updateFormFieldValidationState(fieldId, value, validators), function () /* prevState, props */{ // 现在校验状态来决定提交按钮的状态 _this5.setState(actions.updateSubmitButtonState()); }); if (this.props.onChange) { this.props.onChange(fieldId, value, {}); } }; Form.prototype.handleSubmit = function handleSubmit() { var _this6 = this; var formData = this.state.formData; var fieldsModel = this.props.fieldsModel; // 更新所有字段的校验状态,并更新提交按钮的状态,最后再给用户回调 this.setState(actions.updateAllFormFieldsValidationState(fieldsModel, formData), function () /* prevState, props */{ // 现在校验状态来决定提交按钮的状态 _this6.setState(actions.updateSubmitButtonState()); if (validationUtils.isStatesValid(_this6.state.fieldsValidationState)) { if (_this6.props.onSubmit) { _this6.props.onSubmit(formData); } } }); }; Form.prototype.handleReset = function handleReset(event) { if (this.props.onReset) { this.props.onReset(event); } }; /** * [stateful] 由于state只存储了所有字段的验证状态,所以需要专门计算一下总的状态 * 可以用在验证表单是否允许提交 * @return {boolean} 验证状态 * - true 所有字段验证通过 * - false 有一个或者多个字段验证失败 * ({name: null, age: null}) => (true) * ({name: 'success', age: 'success'}) => (true) * ({name: 'success', age: 'error'}) => (false) */ Form.prototype.isAllFieldsValid = function isAllFieldsValid() { var fieldsValidationState = this.state.fieldsValidationState; return validationUtils.isStatesValid(fieldsValidationState); }; /** * 校验状态 */ Form.prototype.getFieldValidationState = function getFieldValidationState(fieldId) { return this.state.fieldsValidationState[fieldId]; }; /** * 校验帮助信息 */ Form.prototype.getFieldHelpText = function getFieldHelpText(fieldId) { return this.state.fieldsHelpText[fieldId]; }; Form.prototype.genLayoutFormGroup = function genLayoutFormGroup(fieldModel, index) { var _this7 = this; var id = fieldModel.id, type = fieldModel.type, label = fieldModel.label, validators = fieldModel.validators; var placeholder = fieldModel.placeholder || ''; var formGroup = void 0, formCtrl = void 0; // 隐藏字段 if (fieldModel.hidden === true) { return null; } function getDefaultFormGroup(key, fieldId, fieldLabel, fieldFormCtrl, fm, validationState, helpText) { return React.createElement( FormGroup, { key: key, controlId: 'formControl-' + fieldId, validationState: validationState }, React.createElement( Col, { componentClass: ControlLabel, sm: 4 }, React.createElement( 'div', null, (typeof validators === 'undefined' ? 'undefined' : _typeof(validators)) === 'object' ? React.createElement( 'span', { className: 'required', style: { color: 'red' } }, showRequiredStar(validators) ? '*' : null ) : null, fieldLabel ) ), React.createElement( Col, { sm: 5 }, fieldFormCtrl, fm.type !== 'custom' && (typeof validators === 'undefined' ? 'undefined' : _typeof(validators)) === 'object' && fm.type !== 'ref' ? React.createElement(FormControl.Feedback, null) : null, (typeof validators === 'undefined' ? 'undefined' : _typeof(validators)) === 'object' ? React.createElement( HelpBlock, null, helpText ) : null ), React.createElement(Col, { sm: 3 }) ); } // 根据字段类型,生成不同的表单控件 // 每个类型后面跟着的数字是后端传过来的datatype,这里提到的后端是 // 用友自己的后端,Form组件并不依赖这些datetype数值,写在这里只是 // 为了用友程序员调试方便。 switch (type) { default: case 'string': // 0 case 'int': // 1 formCtrl = React.createElement(TextField, { label: label, value: this.state.formData[id], disabled: fieldModel.disabled, placeholder: placeholder, onChange: this.handleChange.bind(this, id, validators) }); formGroup = getDefaultFormGroup(index, id, label, formCtrl, fieldModel, this.getFieldValidationState(id), validationUtils.isFieldValid(this.state.fieldsValidationState[id]) ? null : this.getFieldHelpText(id)); break; case 'double': // 2 formCtrl = React.createElement(TextField, { label: label, value: this.state.formData[id], disabled: fieldModel.disabled, placeholder: placeholder, onChange: this.handleChange.bind(this, id, validators) }); formGroup = getDefaultFormGroup(index, id, label, formCtrl, fieldModel, this.getFieldValidationState(id), validationUtils.isFieldValid(this.state.fieldsValidationState[id]) ? null : this.getFieldHelpText(id)); break; case 'date': // 3 // 注意value的格式 // value = new Date('2017-02-14').toISOString() var dateConfig = fieldModel.dateConfig || {}; formCtrl = React.createElement(DatePicker, _extends({}, dateConfig, { value: this.state.formData[id], locale: dateConfig.locale || 'zh_CN', peekNextMonth: true, showYearDropdown: true, showMonthDropdown: true, className: classNames(dateConfig.className), todayButton: dateConfig.todayButton || '今天', onChange: this.handleDatePickerChange.bind(this, id, validators), disabled: fieldModel.disabled === true })); // formGroup = getDefaultFormGroup(index, id, label, formCtrl, fieldModel); formGroup = getDefaultFormGroup(index, id, label, formCtrl, fieldModel, this.getFieldValidationState(id), validationUtils.isFieldValid(this.state.fieldsValidationState[id]) ? null : this.getFieldHelpText(id)); break; case 'boolean': // 4 formCtrl = React.createElement(Checkbox, { checked: this.state.formData[id], onChange: this.handleChange.bind(this, id, validators) }); formGroup = getDefaultFormGroup(index, id, label, formCtrl, fieldModel); break; case 'ref': // 5 var referValue = this.state.formData[id]; var defaultData = []; if (fieldModel.multiple) { defaultData = referValue; } else { if (referValue && referValue.id) { defaultData[0] = _extends({}, referValue); } } if (fieldModel.referConfig) { // 参照的示例数据 // ```js // defaultData = [{ // "id": "02EDD0F9-F384-43BF-9398-5E5781DAC5D0", // "code": "0502", // "name": "二车间", // "pid": "", // "isLeaf": "true" // }]; // fieldModel.referConfig = { // referConditions: {"refCode":"dept","refType":"tree","rootName":"部门"}; // referDataUrl: "http://10.3.14.239/ficloud/refbase_ctr/queryRefJSON"; // } // ``` var referExtend = {}; if (fieldModel.referExtend) { referExtend = fieldModel.referExtend; } formCtrl = React.createElement(Refers, _extends({}, fieldModel.referConfig, { labelKey: fieldModel.referConfig.labelKey || 'name', disabled: fieldModel.disabled === true, minLength: 0, align: 'justify', emptyLabel: '', multiple: fieldModel.multiple || false, onChange: this.handleReferChange.bind(this, id, validators, fieldModel.multiple), onBlur: this.handleReferBlur.bind(this, id, validators), placeholder: placeholder, referType: 'list', selected: defaultData, ref: function ref(_ref3) { return _this7._myrefers = _ref3; }, renderMenuItemChildren: this._renderMenuItemChildren }, referExtend)); } else { // fallback到纯文本框 formCtrl = React.createElement(TextField, { key: index, controlId: 'formControl-' + id, label: label, value: this.state.formData[id], placeholder: placeholder, inForm: true, onChange: this.handleChange.bind(this, id, validators) }); } formGroup = getDefaultFormGroup(index, id, label, formCtrl, fieldModel, this.getFieldValidationState(id), validationUtils.isFieldValid(this.state.fieldsValidationState[id]) ? null : this.getFieldHelpText(id)); break; case 'enum': // 6 formCtrl = React.createElement( FormControl, { componentClass: 'select', placeholder: placeholder, value: this.state.formData[id], onChange: this.handleChange.bind(this, id, validators) }, fieldModel.data.map(function (opt) { return React.createElement( 'option', { key: opt.key, value: opt.key }, opt.value ); }) ); formGroup = getDefaultFormGroup(index, id, label, formCtrl, fieldModel, this.getFieldValidationState(id), validationUtils.isFieldValid(this.state.fieldsValidationState[id]) ? null : this.getFieldHelpText(id)); break; case 'textarea': // 9 formCtrl = React.createElement(TextField, { label: label, type: 'textarea', value: this.state.formData[id], disabled: fieldModel.disabled, placeholder: placeholder, onChange: this.handleChange.bind(this, id, validators) }); formGroup = getDefaultFormGroup(index, id, label, formCtrl, fieldModel, this.getFieldValidationState(id), validationUtils.isFieldValid(this.state.fieldsValidationState[id]) ? null : this.getFieldHelpText(id)); break; case 'custom': // 后端没有该类型,这是前端自己定义的 formCtrl = React.createElement(fieldModel.component, { customFieldModel: fieldModel, customFieldValue: this.state.formData[id], onCustomFieldChange: this.handleCustomFieldChange.bind(this, id, validators) }); formGroup = getDefaultFormGroup(index, id, label, formCtrl, fieldModel, this.getFieldValidationState(id), validationUtils.isFieldValid(this.state.fieldsValidationState[id]) ? null : this.getFieldHelpText(id)); break; } return formGroup; }; Form.prototype.genField = function genField(fieldModel) { var _this8 = this; var id = fieldModel.id, type = fieldModel.type, label = fieldModel.label, validators = fieldModel.validators; var placeholder = fieldModel.placeholder || ''; var formGroup = void 0, formCtrl = void 0; // 隐藏字段 if (fieldModel.hidden === true) { return null; } function getDefaultFormGroup(fieldId, fieldLabel, fieldFormCtrl, fm, validationState, helpText) { return React.createElement( FormGroup, { controlId: 'formControl-' + fieldId, validationState: validationState }, React.createElement( 'span', { className: 'required', style: { color: 'red' } }, showRequiredStar(validators) ? '*' : null ), React.createElement( ControlLabel, null, fieldLabel ), ' ', fieldFormCtrl, fm.type !== 'ref' ? React.createElement(FormControl.Feedback, null) : null, React.createElement( HelpBlock, null, helpText ) ); } // 根据字段类型,生成不同的表单控件 // 每个类型后面跟着的数字是后端传过来的datatype,这里提到的后端是 // 用友自己的后端,Form组件并不依赖这些datetype数值,写在这里只是 // 为了用友程序员调试方便。 switch (type) { default: case 'string': // 0 case 'int': // 1 formCtrl = React.createElement(TextField, { value: this.state.formData[id], placeholder: placeholder, onChange: this.handleChange.bind(this, id, validators) }); formGroup = getDefaultFormGroup(id, label, formCtrl, fieldModel, this.getFieldValidationState(id), validationUtils.isFieldValid(this.state.fieldsValidationState[id]) ? null : this.getFieldHelpText(id)); break; case 'double': // 2 formCtrl = React.createElement(TextField, { value: this.state.formData[id], placeholder: placeholder, onChange: this.handleChange.bind(this, id, validators) }); formGroup = getDefaultFormGroup(id, label, formCtrl, fieldModel, this.getFieldValidationState(id), validationUtils.isFieldValid(this.state.fieldsValidationState[id]) ? null : this.getFieldHelpText(id)); break; case 'date': // 3 // 注意value的格式 // value = new Date('2017-02-14').toISOString() var dateConfig = fieldModel.dateConfig || {}; formCtrl = React.createElement(DatePicker, _extends({}, dateConfig, { value: this.state.formData[id], locale: dateConfig.locale || 'zh_CN', peekNextMonth: true, showYearDropdown: true, showMonthDropdown: true, className: classNames(dateConfig.className), todayButton: dateConfig.todayButton || '今天', onChange: this.handleDatePickerChange.bind(this, id, validators), disabled: fieldModel.disabled === true })); // formGroup = getDefaultFormGroup(id, label, formCtrl, fieldModel); formGroup = getDefaultFormGroup(id, label, formCtrl, fieldModel, this.getFieldValidationState(id), validationUtils.isFieldValid(this.state.fieldsValidationState[id]) ? null : this.getFieldHelpText(id)); break; case 'boolean': // 4 formCtrl = React.createElement(Checkbox, { checked: this.state.formData[id], onChange: this.handleChange.bind(this, id, validators) }); formGroup = getDefaultFormGroup(id, label, formCtrl, fieldModel); break; case 'ref': // 5 var referValue = this.state.formData[id]; var defaultData = []; if (fieldModel.multiple) { defaultData = referValue; } else { if (referValue && referValue.id) { defaultData[0] = _extends({}, referValue); } } if (fieldModel.referConfig) { // 参照的示例数据 // ```js // defaultData = [{ // "id": "02EDD0F9-F384-43BF-9398-5E5781DAC5D0", // "code": "0502", // "name": "二车间", // "pid": "", // "isLeaf": "true" // }]; // fieldModel.referConfig = { // referConditions: {"refCode":"dept","refType":"tree","rootName":"部门"}; // referDataUrl: "http://10.3.14.239/ficloud/refbase_ctr/queryRefJSON"; // } // ``` var referExtend = {}; if (fieldModel.referExtend) { referExtend = fieldModel.referExtend; } formCtrl = React.createElement(Refers, _extends({}, fieldModel.referConfig, { labelKey: fieldModel.referConfig.labelKey || 'name', disabled: fieldModel.disabled === true, minLength: 0, align: 'justify', emptyLabel: '', multiple: fieldModel.multiple || false, onChange: this.handleReferChange.bind(this, id, validators, fieldModel.multiple), onBlur: this.handleReferBlur.bind(this, id, validators), placeholder: placeholder, referType: 'list', selected: defaultData, ref: function ref(_ref4) { return _this8._myrefers = _ref4; }, renderMenuItemChildren: this._renderMenuItemChildren }, referExtend)); } else { // fallback到纯文本框 formCtrl = React.createElement(TextField, { controlId: 'formControl-' + id, label: label, value: this.state.formData[id], placeholder: placeholder, inForm: true, onChange: this.handleChange.bind(this, id, validators) }); } formGroup = getDefaultFormGroup(id, label, formCtrl, fieldModel, this.getFieldValidationState(id), validationUtils.isFieldValid(this.state.fieldsValidationState[id]) ? null : this.getFieldHelpText(id)); break; case 'enum': // 6 formCtrl = React.createElement( FormControl, { componentClass: 'select', placeholder: placeholder, value: this.state.formData[id], onChange: this.handleChange.bind(this, id, validators) }, fieldModel.data.map(function (opt) { return React.createElement( 'option', { key: opt.key, value: opt.key }, opt.value ); }) ); formGroup = getDefaultFormGroup(id, label, formCtrl, fieldModel, this.getFieldValidationState(id), validationUtils.isFieldValid(this.state.fieldsValidationState[id]) ? null : this.getFieldHelpText(id)); break; case 'textarea': // 6 formCtrl = React.createElement(TextField, { type: 'textarea', value: this.state.formData[id], placeholder: placeholder, onChange: this.handleChange.bind(this, id, validators) }); formGroup = getDefaultFormGroup(id, label, formCtrl, fieldModel, this.getFieldValidationState(id), validationUtils.isFieldValid(this.state.fieldsValidationState[id]) ? null : this.getFieldHelpText(id)); break; case 'custom': // 后端没有该类型,这是前端自己定义的 formCtrl = React.createElement(fieldModel.component, { customFieldModel: fieldModel, customFieldValue: this.state.formData[id], onCustomFieldChange: this.handleCustomFieldChange.bind(this, id, validators) }); formGroup = getDefaultFormGroup(id, label, formCtrl, fieldModel, this.getFieldValidationState(id), validationUtils.isFieldValid(this.state.fieldsValidationState[id]) ? null : this.getFieldHelpText(id)); break; } return formGroup; }; Form.prototype.render = function render() { var _this9 = this; var form = void 0; if (this.props.layout) { form = React.createElement( ReactBootstrapForm, { inline: true, className: classNames(this.props.className) }, React.createElement( Grid, { fluid: true }, React.createElement( Row, null, this.props.fieldsModel.map(function (fieldModel) { if (fieldModel.hidden === true) { return null; } return React.createElement( Col, _extends({ key: fieldModel.id }, _this9.props.layout), _this9.genField(fieldModel) ); }) ), this.props.showSubmitButton === false ? null : React.createElement( Row, null, React.createElement( Col, { md: 12, className: 'text-center' }, React.createElement( FormGroup, null, React.createElement( Button, { bsStyle: 'default', type: 'reset', onClick: this.handleReset.bind(this) }, this.props.cancelLabel || '取消' ), ' ', React.createElement( Button, { bsStyle: 'primary', type: 'submit', disabled: this.state.submitButtonDisabled, onClick: function onClick(event) { // Prevent page reloading // https://github.com/yyssc/ssc-grid/issues/85 event.preventDefault(); _this9.handleSubmit(); } }, this.props.okLabel || '完成' ) ) ) ) ) ); } else { form = React.createElement( ReactBootstrapForm, { horizontal: true, className: classNames(this.props.className) }, this.props.fieldsModel.map(this.genLayoutFormGroup.bind(this)), this.props.showSubmitButton === false ? null : React.createElement( FormGroup, null, React.createElement( Col, { sm: 12, className: 'text-center' }, React.createElement( Button, { bsStyle: 'default', onClick: this.handleReset.bind(this), type: 'reset' }, this.props.cancelLabel || '取消' ), ' ', React.createElement( Button, { bsStyle: 'primary', type: 'submit', disabled: this.state.submitButtonDisabled, onClick: function onClick(event) { // Prevent page reloading // https://github.com/yyssc/ssc-grid/issues/85 event.preventDefault(); _this9.handleSubmit(); } }, this.props.okLabel || '完成' ) ) ) ); } return form; }; return Form; }(Component); Form.displayName = 'Form'; export default Form; Form.propTypes = propTypes; Form.defaultProps = defaultProps;