diff --git a/src/components/detail/importSubform/index.tsx b/src/components/detail/importSubform/index.tsx index 5e59af54ef5d066d09fd42bfa310611a8165d512..1a0656a47d6a40c78e92d73f7ca17793dacd6435 100644 --- a/src/components/detail/importSubform/index.tsx +++ b/src/components/detail/importSubform/index.tsx @@ -155,16 +155,11 @@ export default class DetailImportSubformField extends DetailField } - render = () => { + getConfigData=() => { const { config, - formLayout, - value, - record, - data, - step + value } = this.props - if (config.configFrom && config.configFrom.type === 'interface' && config.configFrom.interface) { this.interfaceHelper.request( config.configFrom.interface, @@ -191,7 +186,6 @@ export default class DetailImportSubformField extends DetailField { + const { + config, + formLayout, + value, + record, + data, + step + } = this.props + + const fields = this.state.fields if (!fields || !Array.isArray(fields) || fields.length === 0) { return } else { diff --git a/src/components/formFields/form/display.tsx b/src/components/formFields/form/display.tsx new file mode 100644 index 0000000000000000000000000000000000000000..0999d8081cb4a9d40d21485d7d36c6c524ba69c6 --- /dev/null +++ b/src/components/formFields/form/display.tsx @@ -0,0 +1,282 @@ +import React from 'react' +import { display as getALLComponents } from '../' +import { FormFieldConfig } from '.' +import { Display, FieldConfigs, DisplayProps } from '../common' +import { getValue, setValue } from '../../../util/value' +import { cloneDeep } from 'lodash' +import ConditionHelper from '../../../util/condition' + +export interface IFormField { + canCollapse?: boolean + children: React.ReactNode[] +} + +export interface IFormFieldItem { + index: number + title: string + canCollapse?: boolean + children: React.ReactNode[] +} + +export interface IFormFieldItemField { + index: number + label: string + fieldType: string + children: React.ReactNode +} + +interface FormState { + didMount: boolean + showItem: boolean + showIndex: number +} + +export default class FormField extends Display, FormState> { + getALLComponents = (type: any): typeof Display => getALLComponents[type] + + formFieldsList: Array | null>> = [] + formFieldsMountedList: Array> = [] + + constructor (props: DisplayProps) { + super(props) + + this.state = { + didMount: false, + showItem: false, + showIndex: 0 + } + } + + didMount = async () => { + await this.setState({ + didMount: true + }) + } + + set = async (value: any) => { + if (this.props.config.unstringify && this.props.config.unstringify.length > 0 && Array.isArray(value)) { + for (let index = 0; index < value.length; index++) { + if (value[index]) { + for (const field of this.props.config.unstringify) { + const info = getValue(value[index], field) + try { + value[index] = setValue(value[index], field, JSON.parse(info)) + } catch (e) {} + } + } + } + } + + return value + } + + get = async () => { + const data: any[] = [] + + for (let index = 0; index < this.formFieldsList.length; index++) { + if (this.formFieldsList[index]) { + let item: any = {} + + if (Array.isArray(this.props.config.fields)) { + for (const formFieldIndex in this.props.config.fields) { + const formFieldConfig = this.props.config.fields[formFieldIndex] + if (!ConditionHelper(formFieldConfig.condition, { record: this.props.value[index], data: this.props.data, step: this.props.step })) { + continue + } + const formField = this.formFieldsList[index] && this.formFieldsList[index][formFieldIndex] + if (formField) { + const value = await formField.get() + item = setValue(item, formFieldConfig.field, value) + } + } + } + + if (this.props.config.stringify) { + for (const field of this.props.config.stringify) { + const info = getValue(item, field) + item = setValue(item, field, JSON.stringify(info)) + } + } + + data[index] = item + } + } + + return data + } + + handleMount = async (index: number, formFieldIndex: number) => { + if (!this.formFieldsMountedList[index]) { + this.formFieldsMountedList[index] = [] + } + if (this.formFieldsMountedList[index][formFieldIndex]) { + return true + } + this.formFieldsMountedList[index][formFieldIndex] = true + + if (this.formFieldsList[index] && this.formFieldsList[index][formFieldIndex]) { + const formField = this.formFieldsList[index][formFieldIndex] + if (formField) { + const formFieldConfig = (this.props.config.fields || [])[formFieldIndex] + + let value = getValue(this.props.value[index] === undefined ? {} : this.props.value[index], formFieldConfig.field) + const source = value + if ((formFieldConfig.defaultValue) && value === undefined) { + value = await formField.reset() + } + value = await formField.set(value) + if (source !== value) { + this.props.onValueSet(`[${index}]${formFieldConfig.field}`, value) + } + + await formField.didMount() + } + } + } + + handleValueSet = async (index: number, formFieldIndex: number, path: string, value: any, options?: { noPathCombination?: boolean }) => { + const formFieldConfig = (this.props.config.fields || [])[formFieldIndex] + if (formFieldConfig) { + const fullPath = options && options.noPathCombination ? path : (formFieldConfig.field === '' || path === '' ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}`) + await this.props.onValueSet(`[${index}]${fullPath}`, value) + } + } + + handleValueUnset = async (index: number, formFieldIndex: number, path: string, options?: { noPathCombination?: boolean }) => { + const formFieldConfig = (this.props.config.fields || [])[formFieldIndex] + if (formFieldConfig) { + const fullPath = options && options.noPathCombination ? path : (formFieldConfig.field === '' || path === '' ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}`) + await this.props.onValueUnset(`[${index}]${fullPath}`) + } + } + + handleValueListAppend = async (index: number, formFieldIndex: number, path: string, value: any, options?: { noPathCombination?: boolean }) => { + const formFieldConfig = (this.props.config.fields || [])[formFieldIndex] + if (formFieldConfig) { + const fullPath = options && options.noPathCombination ? path : (formFieldConfig.field === '' || path === '' ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}`) + await this.props.onValueListAppend(`[${index}]${fullPath}`, value) + } + } + + handleValueListSplice = async (index: number, formFieldIndex: number, path: string, _index: number, count: number, options?: { noPathCombination?: boolean }) => { + const formFieldConfig = (this.props.config.fields || [])[formFieldIndex] + if (formFieldConfig) { + const fullPath = options && options.noPathCombination ? path : (formFieldConfig.field === '' || path === '' ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}`) + await this.props.onValueListSplice(`[${index}]${fullPath}`, _index, count) + } + } + + /** + * 用于展示子表单组件中的每一子项中的每一个子表单项组件 + * @param props + * @returns + */ + renderItemFieldComponent = (props: IFormFieldItemField) => { + return + 您当前使用的UI版本没有实现FormField组件的renderItemFieldComponent方法。 + + } + + /** + * 用于展示子表单组件中的每一个子项 + * @param props + * @returns + */ + renderItemComponent = (props: IFormFieldItem) => { + return + 您当前使用的UI版本没有实现FormField组件的renderItemComponent方法。 + + } + + /** + * 用于展示子表单组件 + * @param _props + * @returns + */ + renderComponent = (_props: IFormField) => { + return + 您当前使用的UI版本没有实现FormField组件。 + + } + + render = () => { + const { + value = [], + data, + step, + config: { + fields, + primaryField, + canCollapse + } + } = this.props + + return ( + + { + this.renderComponent({ + canCollapse, + children: ( + this.state.didMount + ? (Array.isArray(value) ? value : []).map((itemValue: any, index: number) => { + return + {this.renderItemComponent({ + index, + title: primaryField !== undefined ? getValue(itemValue, primaryField, '').toString() : index.toString(), + canCollapse, + children: (fields || []).map((formFieldConfig, fieldIndex) => { + if (!ConditionHelper(formFieldConfig.condition, { record: itemValue, data: this.props.data, step: this.props.step })) { + if (!this.formFieldsMountedList[index]) this.formFieldsMountedList[index] = [] + this.formFieldsMountedList[index][fieldIndex] = false + return null + } + const FormField = this.getALLComponents(formFieldConfig.type) || Display + + // 渲染表单项容器 + return ( +
+ { + this.renderItemFieldComponent({ + index: fieldIndex, + label: formFieldConfig.label, + fieldType: formFieldConfig.type, + children: ( + | null) => { + if (fieldRef) { + if (!this.formFieldsList[index]) this.formFieldsList[index] = [] + this.formFieldsList[index][fieldIndex] = fieldRef + this.handleMount(index, fieldIndex) + } + }} + value={getValue(value[index], formFieldConfig.field)} + record={value[index]} + data={cloneDeep(data)} + step={step} + config={formFieldConfig} + onValueSet={async (path, value, options) => this.handleValueSet(index, fieldIndex, path, value, options)} + onValueUnset={async (path, options) => this.handleValueUnset(index, fieldIndex, path, options)} + onValueListAppend={async (path, value, options) => this.handleValueListAppend(index, fieldIndex, path, value, options)} + onValueListSplice={async (path, _index, count, options) => this.handleValueListSplice(index, fieldIndex, path, _index, count, options)} + baseRoute={this.props.baseRoute} + loadDomain={async (domain: string) => await this.props.loadDomain(domain)} + /> + ) + }) + } +
+ ) + }) + }) + } +
+ } + ) + : [] + ) + }) + } +
+ ) + } +} diff --git a/src/components/formFields/group/display.tsx b/src/components/formFields/group/display.tsx new file mode 100644 index 0000000000000000000000000000000000000000..48f71a32a903c6b8bcb67d354aa3d7143f3f112d --- /dev/null +++ b/src/components/formFields/group/display.tsx @@ -0,0 +1,207 @@ +import React from 'react' +import { display as getALLComponents, FieldConfigs } from '../' +import { GroupFieldConfig, IGroupField } from '.' +import { setValue, getValue, getBoolean } from '../../../util/value' +import { Display, DisplayProps } from '../common' +import { IFormItem } from '../../../steps/form' +import { cloneDeep } from 'lodash' +import ConditionHelper from '../../../util/condition' +import StatementHelper from '../../../util/statement' + +interface IGroupFieldState { + didMount: boolean +} + +export default class GroupField extends Display { + // 各表单项对应的类型所使用的UI组件的类 + getALLComponents = (type: any): typeof Display => getALLComponents[type] + + formFields: Array | null> = [] + formFieldsMounted: Array = [] + + constructor(props: DisplayProps) { + super(props) + + this.state = { + didMount: false + } + } + + didMount = async () => { + await this.setState({ + didMount: true + }) + } + + get = async () => { + let data: any = {} + + if (Array.isArray(this.props.config.fields)) { + for (let formFieldIndex = 0; formFieldIndex < this.props.config.fields.length; formFieldIndex++) { + const formFieldConfig = this.props.config.fields[formFieldIndex] + if (!ConditionHelper(formFieldConfig.condition, { record: this.props.value, data: this.props.data, step: this.props.step })) { + continue + } + const formField = this.formFields[formFieldIndex] + if (formField) { + const value = await formField.get() + data = setValue(data, formFieldConfig.field, value) + } + } + } + return data + } + + handleMount = async (formFieldIndex: number) => { + if (this.formFieldsMounted[formFieldIndex]) { + return true + } + this.formFieldsMounted[formFieldIndex] = true + + if (this.formFields[formFieldIndex]) { + const formField = this.formFields[formFieldIndex] + if (formField) { + const formFieldConfig = this.props.config.fields[formFieldIndex] + + let value = getValue(this.props.value, formFieldConfig.field) + const source = value + if ((formFieldConfig.defaultValue) && value === undefined) { + value = await formField.reset() + } + value = await formField.set(value) + if (source !== value) { + this.props.onValueSet(formFieldConfig.field, value) + } + + await formField.didMount() + } + } + } + + handleValueSet = async (formFieldIndex: number, path: string, value: any, options?: { noPathCombination?: boolean }) => { + const formFieldConfig = (this.props.config.fields || [])[formFieldIndex] + if (formFieldConfig) { + const fullPath = options && options.noPathCombination ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` + await this.props.onValueSet(fullPath, value) + } + } + + handleValueUnset = async (formFieldIndex: number, path: string, options?: { noPathCombination?: boolean }) => { + const formFieldConfig = (this.props.config.fields || [])[formFieldIndex] + if (formFieldConfig) { + const fullPath = options && options.noPathCombination ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` + await this.props.onValueUnset(fullPath) + } + } + + handleValueListAppend = async (formFieldIndex: number, path: string, value: any, options?: { noPathCombination?: boolean }) => { + const formFieldConfig = (this.props.config.fields || [])[formFieldIndex] + if (formFieldConfig) { + const fullPath = options && options.noPathCombination ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` + await this.props.onValueListAppend(fullPath, value) + } + } + + handleValueListSplice = async (formFieldIndex: number, path: string, index: number, count: number, options?: { noPathCombination?: boolean }) => { + const formFieldConfig = (this.props.config.fields || [])[formFieldIndex] + if (formFieldConfig) { + const fullPath = options && options.noPathCombination ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` + await this.props.onValueListSplice(fullPath, index, count) + } + } + + renderComponent = (props: IGroupField) => { + return + 您当前使用的UI版本没有实现GroupField组件。 + + } + + /** + * 表单项组件 - UI渲染方法 + * 各UI库需重写该方法 + * @param props + */ + renderItemComponent = (props: IFormItem) => { + return + 您当前使用的UI版本没有实现FormItem组件。 + + } + + render = () => { + const { + value, + record, + data, + step + } = this.props + + return ( + + {this.renderComponent({ + children: this.state.didMount + ? (this.props.config.fields || []).map((formFieldConfig, formFieldIndex) => { + if (!ConditionHelper(formFieldConfig.condition, { record: value, data: this.props.data, step: this.props.step })) { + this.formFieldsMounted[formFieldIndex] = false + return null + } + let hidden: boolean = true + let display: boolean = true + + if (formFieldConfig.type === 'hidden') { + hidden = true + display = false + } + + if (formFieldConfig.display === 'none') { + hidden = true + display = false + } + + const FormField = this.getALLComponents(formFieldConfig.type) || Display + + let status: 'normal' = 'normal' + const renderData = { + key: formFieldIndex, + label: formFieldConfig.label, + status, + layout: 'horizontal' as 'horizontal', + extra: StatementHelper(formFieldConfig.extra, { record: this.props.record, data: this.props.data, step: this.props.step }), + required: getBoolean(formFieldConfig.required), + visitable: display, + fieldType: formFieldConfig.type, + children: ( + | null) => { + if (formField) { + this.formFields[formFieldIndex] = formField + this.handleMount(formFieldIndex) + } + }} + value={getValue(value, formFieldConfig.field)} + record={record} + data={cloneDeep(data)} + step={step} + config={formFieldConfig} + onValueSet={async (path, value, options) => this.handleValueSet(formFieldIndex, path, value, options)} + onValueUnset={async (path, options) => this.handleValueUnset(formFieldIndex, path, options)} + onValueListAppend={async (path, value, options) => this.handleValueListAppend(formFieldIndex, path, value, options)} + onValueListSplice={async (path, index, count, options) => this.handleValueListSplice(formFieldIndex, path, index, count, options)} + baseRoute={this.props.baseRoute} + loadDomain={async (domain: string) => await this.props.loadDomain(domain)} + /> + ) + } + // 渲染表单项容器 + return ( + hidden + ? this.renderItemComponent(renderData) + : + ) + }) + : [] + })} + + ) + } +} diff --git a/src/components/formFields/hidden/display.tsx b/src/components/formFields/hidden/display.tsx new file mode 100644 index 0000000000000000000000000000000000000000..0ff145f9294fcdf6cba8167ebba7dd81cb43abf5 --- /dev/null +++ b/src/components/formFields/hidden/display.tsx @@ -0,0 +1,5 @@ +import { HiddenFieldConfig } from '.' +import { Display } from '../common' + +export default class HiddenField extends Display { +} diff --git a/src/components/formFields/importSubform/display.tsx b/src/components/formFields/importSubform/display.tsx new file mode 100644 index 0000000000000000000000000000000000000000..52b90e552a87015eff275a333b21469fec99cd54 --- /dev/null +++ b/src/components/formFields/importSubform/display.tsx @@ -0,0 +1,272 @@ +import React from 'react' +import { getValue } from '../../../util/value' + +import { DetailField, DetailFieldConfig, DetailFieldProps, IDetailField } from '../../detail/common' +import { Display } from '../../formFields/common' +import { display as getALLComponents, FieldConfigs } from '../../formFields' +import { IDetailItem } from '../../../steps/detail' +import { cloneDeep, isEqual } from 'lodash' +import ConditionHelper from '../../../util/condition' +import InterfaceHelper, { InterfaceConfig } from '../../../util/interface' +/** + * 子表单配置项 + * - withConfig: 拓展配置 + * - * - enable: 是否开启 + * - * - dataField: (序列化)数据 + * - * - configField: (序列化)配置 + */ +export interface ImportSubformFieldConfig extends DetailFieldConfig { + type: 'import_subform', + configFrom?: ImportSubformConfigFromData | ImportSubformConfigFromInterface +} + +interface ImportSubformConfigFromData { + type: 'data' + dataField?: string + configField?: string +} + +interface ImportSubformConfigFromInterface { + type: 'interface' + interface?: InterfaceConfig +} + +export interface IImportSubformField { + children: React.ReactNode[] +} + +interface IImportSubformFieldState { + didMount: boolean + fields: FieldConfigs[] + formData: { status: 'normal' | 'error' | 'loading', message?: string }[] +} + +export default class ImportSubformFieldDisplay extends DetailField implements IDetailField { + // 各表单项对应的类型所使用的UI组件的类 + getALLComponents = (type: any): typeof Display => getALLComponents[type] + + // 用于请求防频的判断条件 + requestConfig: string = '' + value: string = '' + + formFields: Array | null> = [] + formFieldsMounted: Array = [] + + interfaceHelper = new InterfaceHelper() + + constructor(props: DetailFieldProps) { + super(props) + + this.state = { + didMount: false, + fields: [], + formData: [] + } + } + + getFullpath + (field: string, path: string = '') { + const withConfigPath = this.props.config.configFrom?.type === 'data' && this.props.config.configFrom.dataField ? `${this.props.config.configFrom.dataField}` : '' + const _fullPath = `${withConfigPath}.${field}.${path}.` + const fullPath = _fullPath.replace(/(^\.*)|(\.*$)|(\.){2,}/g, '$3') + return fullPath + } + + didMount = async () => { + await this.setState({ + didMount: true + }) + } + + set: (value: any) => Promise = async (value) => { + return value + }; + + handleMount = async (formFieldIndex: number) => { + if (this.formFieldsMounted[formFieldIndex]) { + return true + } + this.formFieldsMounted[formFieldIndex] = true + if (this.formFields[formFieldIndex]) { + const formField = this.formFields[formFieldIndex] + if (formField) { + const formFieldConfig = this.state.fields[formFieldIndex] + + let value = getValue(this.props.value, this.getFullpath(formFieldConfig.field)) + const source = value + if ((formFieldConfig.defaultValue) && value === undefined) { + value = await formField.reset() + } + value = await formField.set(value) + if (source !== value) { + this.props.onValueSet(this.getFullpath(formFieldConfig.field), value) + } + await formField.didMount() + } + } + } + + getConfigData = () => { + const { + config, + value + } = this.props + if (config.configFrom && config.configFrom.type === 'interface' && config.configFrom.interface) { + this.interfaceHelper.request( + config.configFrom.interface, + {}, + { record: this.props.record, data: this.props.data, step: this.props.step }, + { loadDomain: this.props.loadDomain } + ).then((data: any) => { + let dataToUnstringfy = data + let dataToStringfy = JSON.stringify(data) + if (Object.prototype.toString.call(data) === '[object String]') { + try { + dataToStringfy = data + dataToUnstringfy = JSON.parse(data) + } catch (e) { + console.error('当前动态子表单接口响应数据格式不是合格的json字符串') + dataToUnstringfy = [] + dataToStringfy = '[]' + } + } + if (dataToStringfy !== JSON.stringify(this.state.fields)) { + this.setState({ + fields: dataToUnstringfy + }) + } + }) + } + let fields = this.state.fields + if (config.configFrom && config.configFrom.type === 'data') { + fields = config.configFrom.configField ? getValue(value, config.configFrom.configField) : [] + if (!isEqual(fields, this.state.fields)) { + this.setState({ + fields + }) + } + } + } + + componentDidMount() { + this.getConfigData() + } + + handleValueSet = async (formFieldIndex: number, path: string, value: any) => { + const formFieldConfig = (this.state.fields || [])[formFieldIndex] + if (formFieldConfig) { + const fullPath = this.getFullpath(formFieldConfig.field, path) + await this.props.onValueSet(fullPath, value) + } + } + + handleValueUnset = async (formFieldIndex: number, path: string) => { + const formFieldConfig = (this.state.fields || [])[formFieldIndex] + if (formFieldConfig) { + const fullPath = this.getFullpath(formFieldConfig.field, path) + await this.props.onValueUnset(fullPath) + } + } + + handleValueListAppend = async (formFieldIndex: number, path: string, value: any) => { + const formFieldConfig = (this.state.fields || [])[formFieldIndex] + if (formFieldConfig) { + const fullPath = this.getFullpath(formFieldConfig.field, path) + await this.props.onValueListAppend(fullPath, value) + } + } + + handleValueListSplice = async (formFieldIndex: number, path: string, index: number, count: number) => { + const formFieldConfig = (this.state.fields || [])[formFieldIndex] + if (formFieldConfig) { + const fullPath = this.getFullpath(formFieldConfig.field, path) + await this.props.onValueListSplice(fullPath, index, count) + } + } + + renderComponent = (props: IImportSubformField) => { + return + 您当前使用的UI版本没有实现ImportSubformField组件。 + + } + + /** + * 表单项组件 - UI渲染方法 + * 各UI库需重写该方法 + * @param props + */ + renderItemComponent = (props: IDetailItem) => { + return + 您当前使用的UI版本没有实现FormItem组件。 + + } + + render = () => { + const { + config, + value, + record, + data, + step + } = this.props + const fields = this.state.fields + + if (!fields || !Array.isArray(fields) || fields.length === 0) { + return + } else { + return ( + + {this.renderComponent({ + children: this.state.didMount + ? fields.map((formFieldConfig, formFieldIndex) => { + if (!ConditionHelper(formFieldConfig.condition, { record: value, data, step })) { + this.formFieldsMounted[formFieldIndex] = false + return null + } + let display: boolean = true + + if (formFieldConfig.type === 'hidden' || formFieldConfig.display === 'none') { + display = false + } + + const FormField = this.getALLComponents(formFieldConfig.type) || Display + + const renderData: IDetailItem = { + key: formFieldIndex, + label: formFieldConfig.label, + visitable: display, + fieldType: formFieldConfig.type, + layout: 'horizontal' as 'horizontal', + children: ( + | null) => { + if (formField) { + this.formFields[formFieldIndex] = formField + this.handleMount(formFieldIndex) + } + }} + value={getValue(value, this.getFullpath(formFieldConfig.field))} + record={record} + data={cloneDeep(data)} + step={step} + config={formFieldConfig} + onValueSet={async (path, value) => this.handleValueSet(formFieldIndex, path, value)} + onValueUnset={async (path) => this.handleValueUnset(formFieldIndex, path)} + onValueListAppend={async (path, value) => this.handleValueListAppend(formFieldIndex, path, value)} + onValueListSplice={async (path, index, count) => this.handleValueListSplice(formFieldIndex, path, index, count)} + baseRoute={this.props.baseRoute} + loadDomain={async (domain: string) => await this.props.loadDomain(domain)} + /> + ) + } + // 渲染表单项容器 + return this.renderItemComponent(renderData) + }) + : [] + })} + + ) + } + } +} diff --git a/src/components/formFields/index.tsx b/src/components/formFields/index.tsx index d7e901304b2416ed2dbb5be77ad3caae618d6696..4035f59068db9674aff6fc6481de36d256075a3a 100644 --- a/src/components/formFields/index.tsx +++ b/src/components/formFields/index.tsx @@ -27,6 +27,7 @@ import CodeField, { CodeFieldConfig } from './code' import DiffCodeField, { DiffCodeFieldConfig } from './diffCode' import TextDisplay from './text/display' +import FormDisplay from './form/display' import RadioDisplay from './radio/display' import ColorDisplay from './color/display' import UploadDisplay from './upload/display' @@ -36,8 +37,12 @@ import DatetimeDisplay from './datetime/display' import DatetimeRangeDisplay from './datetimeRange/display' import SelectSingleDisplay from './select/single/display' import SelectMultipleDisplay from './select/multiple/display' +import ImportSubformDisplay from './importSubform/display' +import GroupDisplay from './group/display' import SwitchDisplay from './switch/display' +import TabsDisplay from './tabs/display' import MultipleTextDisplay from './multipleText/display' +import HiddenDisplay from './hidden/display' export interface HiddenFieldConfig extends FieldConfig { type: 'hidden' | 'none' @@ -132,14 +137,19 @@ export default { export const display = { text: TextDisplay, longtext: LongtextDisplay, + form: FormDisplay, radio: RadioDisplay, color: ColorDisplay, upload: UploadDisplay, + import_subform: ImportSubformDisplay, + group: GroupDisplay, number: NumberDisplay, datetime: DatetimeDisplay, datetimeRange: DatetimeRangeDisplay, select_single: SelectSingleDisplay, select_multiple: SelectMultipleDisplay, switch: SwitchDisplay, - multiple_text: MultipleTextDisplay + tabs: TabsDisplay, + multiple_text: MultipleTextDisplay, + hidden: HiddenDisplay } diff --git a/src/components/formFields/tabs/display.tsx b/src/components/formFields/tabs/display.tsx new file mode 100644 index 0000000000000000000000000000000000000000..0673de1acfe29f474b846265a30214722a1f1bcd --- /dev/null +++ b/src/components/formFields/tabs/display.tsx @@ -0,0 +1,287 @@ +import React from 'react' +import { display as getALLComponents } from '../' +import { TabsFieldConfig } from '.' +import { Display, FieldConfigs, DisplayProps } from '../common' +import ConditionHelper from '../../../util/condition' +import { getValue, setValue, getBoolean } from '../../../util/value' +import { cloneDeep } from 'lodash' + +export interface ITabsField { + children: React.ReactNode[] +} + +export interface ITabsFieldItem { + key: string + label: string + children: React.ReactNode[] +} + +export interface ITabsFieldItemField { + index: number + label: string + required: boolean + status: 'normal' | 'error' | 'loading' + description?: string + message?: string + extra?: string + fieldType: string + children: React.ReactNode +} +export interface TabsFieldState { + didMount: boolean + extra?: S +} + +export default class TabsField extends Display> { + // 各表单项对应的类型所使用的UI组件的类 + getALLComponents = (type: any): typeof Display => getALLComponents[type] + + formFieldsList: Array | null>> = [] + formFieldsMountedList: Array> = [] + + constructor(props: DisplayProps) { + super(props) + + this.state = { + didMount: false + } + } + + didMount = async () => { + await this.setState({ + didMount: true + }) + } + + get = async () => { + let data: any = {} + + for (let index = 0; index < (this.props.config.tabs || []).length; index++) { + const tab = (this.props.config.tabs || [])[index] + const fields = this.props.config.mode === 'same' ? (this.props.config.fields || []) : (((this.props.config.tabs || [])[index] || {}).fields || []) + + for (let formFieldIndex = 0; formFieldIndex < fields.length; formFieldIndex++) { + const formFieldConfig = fields[formFieldIndex] + if (!ConditionHelper(formFieldConfig.condition, { record: getValue(this.props.value, tab.field), data: this.props.data, step: this.props.step })) { + continue + } + const formField = this.formFieldsList[index] && this.formFieldsList[index][formFieldIndex] + if (formField) { + const value = await formField.get() + const fullPath = tab.field === '' || formFieldConfig.field === '' ? `${tab.field}${formFieldConfig.field}` : `${tab.field}.${formFieldConfig.field}` + data = setValue(data, fullPath, value) + } + } + } + + return data + } + + handleMount = async (index: number, formFieldIndex: number) => { + if (!this.formFieldsMountedList[index]) { + this.formFieldsMountedList[index] = [] + } + if (this.formFieldsMountedList[index][formFieldIndex]) { + return true + } + this.formFieldsMountedList[index][formFieldIndex] = true + + const tab = (this.props.config.tabs || [])[index] + + if (this.formFieldsList[index] && this.formFieldsList[index][formFieldIndex]) { + const formField = this.formFieldsList[index][formFieldIndex] + if (formField) { + const fields = this.props.config.mode === 'same' ? (this.props.config.fields || []) : (((this.props.config.tabs || [])[index] || {}).fields || []) + const formFieldConfig = fields[formFieldIndex] + + const fullPath = tab.field === '' || formFieldConfig.field === '' ? `${tab.field}${formFieldConfig.field}` : `${tab.field}.${formFieldConfig.field}` + + let value = getValue(this.props.value, fullPath) + const source = value + if ((formFieldConfig.defaultValue) && value === undefined) { + value = await formField.reset() + } + value = await formField.set(value) + if (source !== value) { + this.props.onValueSet(fullPath, value) + } + + await formField.didMount() + } + } + } + + handleChange = async (index: number, formFieldIndex: number, value: any) => { + } + + handleValueSet = async (index: number, formFieldIndex: number, path: string, value: any, options?: { noPathCombination?: boolean }) => { + const tab = (this.props.config.tabs || [])[index] + + const fields = this.props.config.mode === 'same' ? (this.props.config.fields || []) : (((this.props.config.tabs || [])[index] || {}).fields || []) + const formFieldConfig = fields[formFieldIndex] + if (formFieldConfig) { + const fieldPath = options && options.noPathCombination ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` + const fullPath = tab.field === '' || fieldPath === '' ? `${tab.field}${fieldPath}` : `${tab.field}.${fieldPath}` + await this.props.onValueSet(fullPath, value) + } + } + + handleValueUnset = async (index: number, formFieldIndex: number, path: string, options?: { noPathCombination?: boolean }) => { + const tab = (this.props.config.tabs || [])[index] + + const fields = this.props.config.mode === 'same' ? (this.props.config.fields || []) : (((this.props.config.tabs || [])[index] || {}).fields || []) + const formFieldConfig = fields[formFieldIndex] + if (formFieldConfig) { + const fieldPath = options && options.noPathCombination ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` + const fullPath = tab.field === '' || fieldPath === '' ? `${tab.field}${fieldPath}` : `${tab.field}.${fieldPath}` + await this.props.onValueUnset(fullPath) + } + } + + handleValueListAppend = async (index: number, formFieldIndex: number, path: string, value: any, options?: { noPathCombination?: boolean }) => { + const tab = (this.props.config.tabs || [])[index] + + const fields = this.props.config.mode === 'same' ? (this.props.config.fields || []) : (((this.props.config.tabs || [])[index] || {}).fields || []) + const formFieldConfig = fields[formFieldIndex] + if (formFieldConfig) { + const fieldPath = options && options.noPathCombination ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` + const fullPath = tab.field === '' || fieldPath === '' ? `${tab.field}${fieldPath}` : `${tab.field}.${fieldPath}` + await this.props.onValueListAppend(fullPath, value) + } + } + + handleValueListSplice = async (index: number, formFieldIndex: number, path: string, _index: number, count: number, options?: { noPathCombination?: boolean }) => { + const tab = (this.props.config.tabs || [])[index] + + const fields = this.props.config.mode === 'same' ? (this.props.config.fields || []) : (((this.props.config.tabs || [])[index] || {}).fields || []) + const formFieldConfig = fields[formFieldIndex] + if (formFieldConfig) { + const fieldPath = options && options.noPathCombination ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` + const fullPath = tab.field === '' || fieldPath === '' ? `${tab.field}${fieldPath}` : `${tab.field}.${fieldPath}` + await this.props.onValueListSplice(fullPath, _index, count) + } + } + + /** + * 用于展示子表单组件 + * @param _props + * @returns + */ + renderComponent = (_props: ITabsField) => { + return + 您当前使用的UI版本没有实现FormField组件。 + + } + + /** + * 用于展示子表单组件中的每一个子项 + * @param props + * @returns + */ + renderItemComponent = (props: ITabsFieldItem) => { + return + 您当前使用的UI版本没有实现FormField组件的renderItemComponent方法。 + + } + + /** + * 用于展示子表单组件中的每一子项中的每一个子表单项组件 + * @param props + * @returns + */ + renderItemFieldComponent = (props: ITabsFieldItemField) => { + return + 您当前使用的UI版本没有实现FormField组件的renderItemFieldComponent方法。 + + } + + render = () => { + const { + value = {} + } = this.props + + return ( + + { + this.renderComponent({ + children: ( + this.state.didMount + ? (this.props.config.tabs || []).map((tab: any, index: number) => { + const fields = this.props.config.mode === 'same' ? (this.props.config.fields || []) : (((this.props.config.tabs || [])[index] || {}).fields || []) + return ( + + {this.renderItemComponent({ + key: index.toString(), + label: tab.label, + children: fields.map((formFieldConfig, formFieldIndex) => { + if (!ConditionHelper(formFieldConfig.condition, { record: this.props.record, data: this.props.data, step: this.props.step })) { + if (!this.formFieldsMountedList[index]) this.formFieldsMountedList[index] = [] + this.formFieldsMountedList[index][formFieldIndex] = false + return null + } + let hidden: boolean = true + let display: boolean = true + + if (formFieldConfig.type === 'hidden') { + hidden = true + display = false + } + + if (formFieldConfig.display === 'none') { + hidden = true + display = false + } + + const FormField = this.getALLComponents(formFieldConfig.type) || Display + + let status: 'normal' = 'normal' + // 渲染表单项容器 + if (hidden) { + return ( +
+ {this.renderItemFieldComponent({ + index: formFieldIndex, + label: formFieldConfig.label, + status, + required: getBoolean(formFieldConfig.required), + fieldType: formFieldConfig.type, + children: ( + | null) => { + if (!this.formFieldsList[index]) this.formFieldsList[index] = [] + this.formFieldsList[index][formFieldIndex] = formField + this.handleMount(index, formFieldIndex) + }} + value={getValue(getValue(value, tab.field), formFieldConfig.field)} + record={getValue(value, tab.field)} + data={cloneDeep(this.props.data)} + step={this.props.step} + config={formFieldConfig} + onValueSet={async (path, value, validation) => this.handleValueSet(index, formFieldIndex, path, value, validation)} + onValueUnset={async (path, validation) => this.handleValueUnset(index, formFieldIndex, path, validation)} + onValueListAppend={async (path, value, validation) => this.handleValueListAppend(index, formFieldIndex, path, value, validation)} + onValueListSplice={async (path, _index, count, validation) => this.handleValueListSplice(index, formFieldIndex, path, _index, count, validation)} + baseRoute={this.props.baseRoute} + loadDomain={async (domain: string) => await this.props.loadDomain(domain)} + /> + ) + })} +
+ ) + } else { + return + } + }) + })} + + ) + }) + : [] + ) + }) + } +
+ ) + } +} diff --git a/src/index.tsx b/src/index.tsx index e66ae3d778d7051a2016ed4b2affad561deb6bc9..c25627c0a2d3701babd80ffb2cf5c4a04f2806d8 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -30,6 +30,7 @@ export { default as CodeField } from './components/formFields/code' export { default as DiffCodeField } from './components/formFields/diffCode' export { default as TextDisplay } from './components/formFields/text/display' +export { default as FormDisplay } from './components/formFields/form/display' export { default as LongTextDisplay } from './components/formFields/longtext/display' export { default as RadioDisplay } from './components/formFields/radio/display' export { default as ColorDisplay } from './components/formFields/color/display' @@ -40,7 +41,11 @@ export { default as DatetimeDisplay } from './components/formFields/datetime/dis export { default as DatetimeRangeDisplay } from './components/formFields/datetimeRange/display' export { default as SelectSingleDisplay } from './components/formFields/select/single/display' export { default as SelectMultipleDisplay } from './components/formFields/select/multiple/display' +export { default as ImportSubformDisplay } from './components/formFields/importSubform/display' +export { default as GroupDisplay } from './components/formFields/group/display' +export { default as TabsDisplay } from './components/formFields/tabs/display' export { default as MultipleTextDisplay } from './components/formFields/multipleText/display' +export { default as HiddenDisplay } from './components/formFields/hidden/display' export { default as TableStep } from './steps/table' export { default as TextColumn } from './components/tableColumns/text'