diff --git a/package.json b/package.json index 91472ce2462e729f4f527faa6270e376e4f29fbc..415eaf1c7cd48fc75e7aacb2cb5f160b3e92884c 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "@types/react": "^16.9.46", "@types/react-router-dom": "^5.1.5", "axios": "^0.20.0", + "immer": "^9.0.7", "lodash": "^4.17.21", "marked": "^1.2.5", "moment": "^2.29.0", diff --git a/src/components/detail/common.tsx b/src/components/detail/common.tsx index 57902c18fa9ace6e57473ea7a8066cf43eff1e77..ddc69cb706a842c8149387c8a495cdd8a0cba143 100644 --- a/src/components/detail/common.tsx +++ b/src/components/detail/common.tsx @@ -74,7 +74,8 @@ export interface DetailFieldProps { value: T, record: { [field: string]: any }, data: any[], - step: number, + step: { [field: string]: any } // formValue挂载 + // step: number, config: C // TODO 待删除 onChange: (value: T) => Promise diff --git a/src/components/detail/importSubform/index.tsx b/src/components/detail/importSubform/index.tsx index 9828bbc31b080c71e60f28acbb6840d1224c4d83..a2e1574e5419216e35fe8b4d1a6f3d03afdc5f60 100644 --- a/src/components/detail/importSubform/index.tsx +++ b/src/components/detail/importSubform/index.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { getValue } from '../../../util/value' +import { getValue , getChainPath} from '../../../util/value' import { DetailField, DetailFieldConfig, DetailFieldProps, IDetailField } from '../common' import { Display } from '../../formFields/common' @@ -67,12 +67,7 @@ export default class ImportSubformField extends DetailField { await this.setState({ @@ -89,15 +84,16 @@ export default class ImportSubformField extends DetailField { + const withConfigPath = this.props.config.configFrom?.type === 'data' && this.props.config.configFrom.dataField ? `${this.props.config.configFrom.dataField}` : '' const formFieldConfig = (this.state.fields || [])[formFieldIndex] if (formFieldConfig) { - const fullPath = this.getFullpath(formFieldConfig.field, path) + const fullPath = getChainPath(withConfigPath, formFieldConfig.field, path) await this.props.onValueSet(fullPath, value, true) } } handleValueUnset = async (formFieldIndex: number, path: string) => { + const withConfigPath = this.props.config.configFrom?.type === 'data' && this.props.config.configFrom.dataField ? `${this.props.config.configFrom.dataField}` : '' const formFieldConfig = (this.state.fields || [])[formFieldIndex] if (formFieldConfig) { - const fullPath = this.getFullpath(formFieldConfig.field, path) + const fullPath = getChainPath(withConfigPath, formFieldConfig.field, path) await this.props.onValueUnset(fullPath, true) } } handleValueListAppend = async (formFieldIndex: number, path: string, value: any) => { + const withConfigPath = this.props.config.configFrom?.type === 'data' && this.props.config.configFrom.dataField ? `${this.props.config.configFrom.dataField}` : '' const formFieldConfig = (this.state.fields || [])[formFieldIndex] if (formFieldConfig) { - const fullPath = this.getFullpath(formFieldConfig.field, path) + const fullPath = getChainPath(withConfigPath, formFieldConfig.field, path) await this.props.onValueListAppend(fullPath, value, true) } } handleValueListSplice = async (formFieldIndex: number, path: string, index: number, count: number) => { + const withConfigPath = this.props.config.configFrom?.type === 'data' && this.props.config.configFrom.dataField ? `${this.props.config.configFrom.dataField}` : '' const formFieldConfig = (this.state.fields || [])[formFieldIndex] if (formFieldConfig) { - const fullPath = this.getFullpath(formFieldConfig.field, path) + const fullPath = getChainPath(withConfigPath, formFieldConfig.field, path) await this.props.onValueListSplice(fullPath, index, count, true) } } @@ -202,6 +202,7 @@ export default class ImportSubformField extends DetailField } else { + const withConfigPath = this.props.config.configFrom?.type === 'data' && this.props.config.configFrom.dataField ? `${this.props.config.configFrom.dataField}` : '' return ( {this.renderComponent({ @@ -234,7 +235,7 @@ export default class ImportSubformField extends DetailField { const { - value, - onChange + value } = this.props if (type === 'null') { this.props.onValueSet('', null, true) @@ -80,60 +80,67 @@ export default class AnyField extends Field this.handleChangeType(type) }), - valueContent: type === 'string' ? {}} - form={this.props.form} - formLayout={'horizontal'} - value={typeof value === 'string' ? value : ''} - record={record} - data={_.cloneDeep(data)} - step={step} - config={{ type: 'text', field: '', label: '' }} - onChange={async (value: string) => { await onChange(value) }} - onValueSet={this.props.onValueSet} - onValueUnset={this.props.onValueUnset} - onValueListAppend={this.props.onValueListAppend} - onValueListSplice={this.props.onValueListSplice} - onValueListSort={this.props.onValueListSort} - baseRoute={this.props.baseRoute} - loadDomain={this.props.loadDomain} - /> : ( - type === 'number' ? {}} - form={this.props.form} - formLayout={'horizontal'} - record={record} - value={typeof value === 'number' ? value : ''} - data={_.cloneDeep(data)} - step={step} - config={{ type: 'number', field: '', label: '' }} - onChange={async (value) => { await onChange(Number(value)) }} - onValueSet={async (path, value, validation) => await this.props.onValueSet(path, Number(value), validation)} - onValueUnset={this.props.onValueUnset} - onValueListAppend={this.props.onValueListAppend} - onValueListSplice={this.props.onValueListSplice} - onValueListSort={this.props.onValueListSort} - baseRoute={this.props.baseRoute} - loadDomain={this.props.loadDomain} - /> : {}} - form={this.props.form} - formLayout={'horizontal'} - record={record} - value={typeof value === 'boolean' ? value : false} - data={_.cloneDeep(data)} - step={step} - config={{ type: 'switch', field: '', label: '' }} - onChange={async (value) => { await onChange(Boolean(value)) }} - onValueSet={this.props.onValueSet} - onValueUnset={this.props.onValueUnset} - onValueListAppend={this.props.onValueListAppend} - onValueListSplice={this.props.onValueListSplice} - onValueListSort={this.props.onValueListSort} - baseRoute={this.props.baseRoute} - loadDomain={this.props.loadDomain} - /> - ) + valueContent: + type === 'string' + ? {}} + form={this.props.form} + formLayout={'horizontal'} + value={typeof value === 'string' ? value : ''} + record={record} + data={_.cloneDeep(data)} + step={step} + config={{ type: 'text', field: '', label: '' }} + onChange={async (value: string) => { await onChange(value) }} + onValueSet={this.props.onValueSet} + onValueUnset={this.props.onValueUnset} + onValueListAppend={this.props.onValueListAppend} + onValueListSplice={this.props.onValueListSplice} + onValueListSort={this.props.onValueListSort} + baseRoute={this.props.baseRoute} + loadDomain={this.props.loadDomain} + containerPath={getChainPath(this.props.containerPath, '')} + /> + : ( + type === 'number' + ? {}} + form={this.props.form} + formLayout={'horizontal'} + record={record} + value={typeof value === 'number' ? value : ''} + data={_.cloneDeep(data)} + step={step} + config={{ type: 'number', field: '', label: '' }} + onChange={async (value) => { await onChange(Number(value)) }} + onValueSet={async (path, value, validation) => await this.props.onValueSet(path, Number(value), validation)} + onValueUnset={this.props.onValueUnset} + onValueListAppend={this.props.onValueListAppend} + onValueListSplice={this.props.onValueListSplice} + onValueListSort={this.props.onValueListSort} + baseRoute={this.props.baseRoute} + loadDomain={this.props.loadDomain} + containerPath={getChainPath(this.props.containerPath, '')} + /> + : {}} + form={this.props.form} + formLayout={'horizontal'} + record={record} + value={typeof value === 'boolean' ? value : false} + data={_.cloneDeep(data)} + step={step} + config={{ type: 'switch', field: '', label: '' }} + onChange={async (value) => { await onChange(Boolean(value)) }} + onValueSet={this.props.onValueSet} + onValueUnset={this.props.onValueUnset} + onValueListAppend={this.props.onValueListAppend} + onValueListSplice={this.props.onValueListSplice} + onValueListSort={this.props.onValueListSort} + baseRoute={this.props.baseRoute} + loadDomain={this.props.loadDomain} + containerPath={getChainPath(this.props.containerPath, '')} + />) })} ) diff --git a/src/components/formFields/common.tsx b/src/components/formFields/common.tsx index 9539759b1304936677c5c761ee15c365b09314af..42b527c10e140c2dba477a01e366d432b6ad8c4c 100644 --- a/src/components/formFields/common.tsx +++ b/src/components/formFields/common.tsx @@ -3,8 +3,10 @@ import { ColumnsConfig, ParamConfig } from '../../interface' import { FieldConfigs as getFieldConfigs } from './' import ParamHelper from '../../util/param' +import { updateCommonPrefixItem } from '../../util/value' import { ConditionConfig } from '../../util/condition' import { StatementConfig } from '../../util/statement' +import { isEqual, get } from 'lodash' /** * 表单项基类配置文件格式定义 @@ -79,7 +81,7 @@ export interface FieldProps { value: T, record: { [field: string]: any }, data: any[], - step: number, + // step: number, config: C // TODO 待删除 onChange: (value: T) => Promise @@ -94,6 +96,9 @@ export interface FieldProps { // 事件:修改值 - 列表 - 修改顺序 onValueListSort: (path: string, index: number, sortType: 'up' | 'down', validation: true | FieldError[]) => Promise baseRoute: string, + containerPath: string, // 容器组件所在路径以字段拼接展示 1.3.0新增 + onReportFields?: (field: string) => Promise // 向父组件上报依赖字段 1.3.0新增 + step: { [field: string]: any } // 传递formValue loadDomain: (domain: string) => Promise } @@ -127,6 +132,7 @@ export interface FieldInterface { * - S: 表单项的扩展状态 */ export class Field extends React.Component, S> implements IField { + dependentFields: string[] = [] // 组件param依赖字段存放数组 1.3.0新增 static defaultProps = { config: {} }; @@ -134,12 +140,12 @@ export class Field extends React.Component< /** * 获取默认值 */ - defaultValue = async () => { + defaultValue : () => Promise = async () => { const { config } = this.props if (config.defaultValue !== undefined) { - return ParamHelper(config.defaultValue, { record: this.props.record, data: this.props.data, step: this.props.step }) + return ParamHelper(config.defaultValue, { record: this.props.record, data: this.props.data, step: this.props.step }, this) } return undefined @@ -167,12 +173,52 @@ export class Field extends React.Component< didMount: () => Promise = async () => { } + /** + * 上报param依赖字段名称 + * @param field + */ + handleReportFields: (field: string) => void = async (field) => { + const update: string[] | boolean = updateCommonPrefixItem(this.dependentFields, field) + if (typeof update === 'boolean') return + this.dependentFields = update + this.props.onReportFields && await this.props.onReportFields(field) + } + renderComponent = (props: E) => { return 当前UI库未实现该表单类型 } + shouldComponentUpdate (nextProps: FieldProps, nextState: S) { + // console.log('nextProps', nextProps, this.props, nextProps.value == this.props.value); + + const dependentFieldsArr = this.dependentFields + // console.log('dependentFieldsArr',dependentFieldsArr); + let dependentIsChange = false + if (dependentFieldsArr && dependentFieldsArr.length) { + for (let i = dependentFieldsArr.length; i >= 0; i--) { + const nextDependentField = get(nextProps.step, dependentFieldsArr[i]) + const currentDependentField = get(this.props.step, dependentFieldsArr[i]) + + if ((nextDependentField || currentDependentField) && nextDependentField !== currentDependentField) { + dependentIsChange = true + break + } + } + } + + /** + * data提交前不变, 去掉这项的比较 + * record也不比较,需要比较的话就在dependentFieldsArr取出record绝对路径 + * */ + if (!dependentIsChange && isEqual(this.state, nextState) && nextProps.value === this.props.value && this.props.config === nextProps.config) { + // console.log('no update' ); + return false + } + return true + } + render = () => { return ( 当前UI库未实现该表单类型 @@ -184,7 +230,8 @@ export interface DisplayProps { value: T, record: { [field: string]: any }, data: any[], - step: number, + // step: number, + step: { [field: string]: any } config: C, // 事件:设置值 onValueSet: (path: string, value: T, validation: true | FieldError[]) => Promise diff --git a/src/components/formFields/custom/index.tsx b/src/components/formFields/custom/index.tsx index 492ef98aa19ad36950b83d0e562c06bc749f3cda..672607ee94060c76da248c1a81ade4a1713f2511 100644 --- a/src/components/formFields/custom/index.tsx +++ b/src/components/formFields/custom/index.tsx @@ -2,7 +2,7 @@ import React, { RefObject } from 'react' import { Field, FieldConfig, IField, FieldInterface, FieldProps, FieldError } from '../common' import { loadMicroApp, MicroApp } from 'qiankun' import moment from 'moment' -import { cloneDeep } from 'lodash' +// import { cloneDeep } from 'lodash' export interface CustomFieldConfig extends FieldConfig, FieldInterface { type: 'custom' @@ -53,7 +53,7 @@ export default class CustomField extends Field imple this.customField.update({ value: this.props.value, record: this.props.record, - data: cloneDeep(this.props.data), + data: this.props.data, form: this.props.form, step: this.props.step, config: this.props.config, @@ -82,7 +82,7 @@ export default class CustomField extends Field imple props: { value: this.props.value, record: this.props.record, - data: cloneDeep(this.props.data), + data: this.props.data, form: this.props.form, step: this.props.step, config: this.props.config, diff --git a/src/components/formFields/form/index.tsx b/src/components/formFields/form/index.tsx index d95ddeaeedadd3d4e9843f6ea100c67d497c0e56..dbc4b0ba4f2f2f12a42a208d31ab3cd358c2f339 100644 --- a/src/components/formFields/form/index.tsx +++ b/src/components/formFields/form/index.tsx @@ -1,8 +1,9 @@ import React from 'react' import { Field, FieldConfig, FieldConfigs, FieldError, FieldProps, IField } from '../common' import getALLComponents from '../' -import { getValue, listItemMove, setValue, getBoolean } from '../../../util/value' -import { cloneDeep } from 'lodash' +// import { cloneDeep } from 'lodash' +import { getValue, getBoolean, getChainPath } from '../../../util/value' +import { set, setValue, sort, splice } from '../../../util/produce' import ConditionHelper from '../../../util/condition' import StatementHelper from '../../../util/statement' @@ -128,7 +129,7 @@ export default class FormField extends Field { if (!this.formFieldsMountedList[index]) { - this.formFieldsMountedList[index] = [] + this.formFieldsMountedList = set(this.formFieldsMountedList, `[${index}]`, []) } if (this.formFieldsMountedList[index][formFieldIndex]) { return true } - this.formFieldsMountedList[index][formFieldIndex] = true + // this.formFieldsMountedList[index][formFieldIndex] = true + this.formFieldsMountedList = set(this.formFieldsMountedList, `[${index}][${formFieldIndex}]`, true) - const formDataList = cloneDeep(this.state.formDataList) - if (!formDataList[index]) formDataList[index] = [] + let formDataList = this.state.formDataList + if (!formDataList[index]) formDataList = set(formDataList, `[${index}]`, []) if (this.formFieldsList[index] && this.formFieldsList[index][formFieldIndex]) { const formField = this.formFieldsList[index][formFieldIndex] @@ -235,15 +237,16 @@ export default class FormField extends Field { const index = (this.props.value || []).length - const formDataList = cloneDeep(this.state.formDataList) - formDataList[index] = [] + const formDataList = set(this.state.formDataList, `[${index}]`, []) this.setState({ formDataList }) - this.formFieldsList[index] = [] - this.formFieldsMountedList[index] = [] + this.formFieldsList = set(this.formFieldsList, `${index}`, []) + this.formFieldsMountedList = set(this.formFieldsMountedList, `${index}`, []) - await this.props.onValueListAppend('', this.props.config.initialValues === undefined ? {} : cloneDeep(this.props.config.initialValues), true) + await this.props.onValueListAppend('', this.props.config.initialValues === undefined ? {} : this.props.config.initialValues, true) } handleRemove = async (index: number) => { - const formDataList = cloneDeep(this.state.formDataList) - formDataList.splice(index, 1) + const formDataList = splice(this.state.formDataList, '', index, 1) this.setState({ formDataList }) - - this.formFieldsList.splice(index, 1) - this.formFieldsMountedList.splice(index, 1) - + this.formFieldsList = splice(this.formFieldsList, '', index, 1) + this.formFieldsMountedList = splice(this.formFieldsMountedList, '', index, 1) await this.props.onValueListSplice('', index, 1, true) } handleSort = async (index: number, sortType: 'up' | 'down') => { - const formDataList = listItemMove(cloneDeep(this.state.formDataList), index, sortType) + const list = this.state.formDataList + const formDataList = sort(list, '', index, sortType) this.setState({ - formDataList + formDataList: formDataList }) - this.formFieldsList = listItemMove(this.formFieldsList, index, sortType) - this.formFieldsMountedList = listItemMove(this.formFieldsMountedList, index, sortType) + this.formFieldsList = sort(this.formFieldsList, '', index, sortType) + this.formFieldsMountedList = sort(this.formFieldsMountedList, '', index, sortType) await this.props.onValueListSort('', index, sortType, true) } @@ -322,22 +322,30 @@ export default class FormField extends Field { + let formDataList = this.state.formDataList + if (validation === true) { + formDataList = set(formDataList, `[${index}][${formFieldIndex}]`, { status: 'normal' }) + } else { + formDataList = set(formDataList, `[${index}][${formFieldIndex}]`, { status: 'error', message: validation[0].message }) + } + + this.setState({ + formDataList + }) + } + handleValueSet = async (index: number, formFieldIndex: number, path: string, value: any, validation: true | FieldError[]) => { const formFieldConfig = (this.props.config.fields || [])[formFieldIndex] if (formFieldConfig) { const fullPath = formFieldConfig.field === '' || path === '' ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` - await this.props.onValueSet(`[${index}]${fullPath}`, value, true) - - const formDataList = cloneDeep(this.state.formDataList) - if (validation === true) { - formDataList[index][formFieldIndex] = { status: 'normal' } - } else { - formDataList[index][formFieldIndex] = { status: 'error', message: validation[0].message } - } + await this.props.onValueSet(`[${index}].${fullPath}`, value, true) + // await this.props.onValueSet(`${index}.${fullPath}`, value, true) - this.setState({ - formDataList - }) + this.handleValueCallback(index, formFieldIndex, validation) } } @@ -347,16 +355,7 @@ export default class FormField extends Field { return - {this.renderItemComponent({ - index, - isLastIndex: value.length - 1 === index, - title: primaryField !== undefined ? getValue(itemValue, primaryField, '').toString() : index.toString(), - removeText: removeText === undefined - ? `删除 ${label}` - : removeText, - onRemove: canRemove ? async () => await this.handleRemove(index) : undefined, - onSort: canSort ? async (sortType: 'up' | 'down') => await this.handleSort(index, sortType) : undefined, - 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) || Field - - let status = ((this.state.formDataList[index] || [])[fieldIndex] || {}).status || 'normal' - - if (['group', 'import_subform', 'object', 'tabs', 'form'].some((type) => type === formFieldConfig.type)) { - status = 'normal' - } - // 渲染表单项容器 - return ( -
- { - this.renderItemFieldComponent({ - index: fieldIndex, - label: formFieldConfig.label, - status, - message: ((this.state.formDataList[index] || [])[fieldIndex] || {}).message || '', - extra: StatementHelper(formFieldConfig.extra, { record: this.props.record, data: this.props.data, step: this.props.step }), - required: getBoolean(formFieldConfig.required), - layout: formLayout, - fieldType: formFieldConfig.type, - children: ( - | null) => { - if (fieldRef) { - if (!this.formFieldsList[index]) this.formFieldsList[index] = [] - this.formFieldsList[index][fieldIndex] = fieldRef - this.handleMount(index, fieldIndex) - } - }} - form={this.props.form} - formLayout={formLayout} - value={getValue(value[index], formFieldConfig.field)} - record={value[index]} - data={cloneDeep(data)} - step={step} - config={formFieldConfig} - onChange={(value: any) => this.handleChange(index, fieldIndex, value)} - onValueSet={async (path, value, validation) => this.handleValueSet(index, fieldIndex, path, value, validation)} - onValueUnset={async (path, validation) => this.handleValueUnset(index, fieldIndex, path, validation)} - onValueListAppend={async (path, value, validation) => this.handleValueListAppend(index, fieldIndex, path, value, validation)} - onValueListSplice={async (path, _index, count, validation) => this.handleValueListSplice(index, fieldIndex, path, _index, count, validation)} - onValueListSort={async (path, _index, sortType, validation) => await this.handleValueListSort(index, fieldIndex, path, _index, sortType, validation)} - baseRoute={this.props.baseRoute} - loadDomain={async (domain: string) => await this.props.loadDomain(domain)} - /> - ) - }) - } -
- ) - }) + {this.renderItemComponent({ + index, + isLastIndex: value.length - 1 === index, + title: primaryField !== undefined ? getValue(itemValue, primaryField, '').toString() : index.toString(), + removeText: removeText === undefined + ? `删除 ${label}` + : removeText, + onRemove: canRemove ? async () => await this.handleRemove(index) : undefined, + onSort: canSort ? async (sortType: 'up' | 'down') => await this.handleSort(index, sortType) : undefined, + canCollapse, + children: (fields || []).map((formFieldConfig, fieldIndex) => { + if (!ConditionHelper(formFieldConfig.condition, { record: itemValue, data: this.props.data, step: this.props.step, extraContainerPath: getChainPath(this.props.config.field, index) }, this)) { + if (!this.formFieldsMountedList[index]) this.formFieldsMountedList = set(this.formFieldsMountedList, `${index}`, []) + // this.formFieldsMountedList[index][fieldIndex] = false + this.formFieldsMountedList = set(this.formFieldsMountedList, `${[index]}.${fieldIndex}`, false) + return null + } + const FormField = this.getALLComponents(formFieldConfig.type) || Field + + let status = ((this.state.formDataList[index] || [])[fieldIndex] || {}).status || 'normal' + + if (['group', 'import_subform', 'object', 'tabs', 'form'].some((type) => type === formFieldConfig.type)) { + status = 'normal' + } + // 渲染表单项容器 + return ( +
+ { + this.renderItemFieldComponent({ + index: fieldIndex, + label: formFieldConfig.label, + status, + message: ((this.state.formDataList[index] || [])[fieldIndex] || {}).message || '', + extra: StatementHelper(formFieldConfig.extra, { record: itemValue, data: this.props.data, step: this.props.step, extraContainerPath: getChainPath(this.props.config.field, index) }, this), + required: getBoolean(formFieldConfig.required), + layout: formLayout, + fieldType: formFieldConfig.type, + children: ( + | null) => { + if (fieldRef) { + if (!this.formFieldsList[index]) this.formFieldsList = set(this.formFieldsList, `[${index}]`, []) + // this.formFieldsList[index][fieldIndex] = fieldRef + this.formFieldsList = set(this.formFieldsList, `[${index}][${fieldIndex}]`, fieldRef) + this.handleMount(index, fieldIndex) + } + }} + form={this.props.form} + formLayout={formLayout} + value={getValue(value[index], formFieldConfig.field)} + record={value[index]} + step={this.props.step} + data={data} + // step={step} + config={formFieldConfig} + onChange={(value: any) => this.handleChange(index, fieldIndex, value)} + onValueSet={async (path, value, validation) => this.handleValueSet(index, fieldIndex, path, value, validation)} + onValueUnset={async (path, validation) => this.handleValueUnset(index, fieldIndex, path, validation)} + onValueListAppend={async (path, value, validation) => this.handleValueListAppend(index, fieldIndex, path, value, validation)} + onValueListSplice={async (path, _index, count, validation) => this.handleValueListSplice(index, fieldIndex, path, _index, count, validation)} + onValueListSort={async (path, _index, sortType, validation) => await this.handleValueListSort(index, fieldIndex, path, _index, sortType, validation)} + baseRoute={this.props.baseRoute} + loadDomain={async (domain: string) => await this.props.loadDomain(domain)} + containerPath={getChainPath(this.props.containerPath, this.props.config.field, index)} + onReportFields={async (field: string) => await this.handleReportFields(field)} + /> + ) + }) + } +
+ ) }) - } -
+ }) + } +
} ) : [] diff --git a/src/components/formFields/group/index.tsx b/src/components/formFields/group/index.tsx index 22347f70218252b2865cff54c581901b6af4e888..69ff1bf67a1e9c5fd5710c6d310b41b2f5646afa 100644 --- a/src/components/formFields/group/index.tsx +++ b/src/components/formFields/group/index.tsx @@ -1,9 +1,10 @@ import React from 'react' -import { setValue, getValue, getBoolean } from '../../../util/value' +import { getValue, getBoolean, getChainPath } from '../../../util/value' import { Field, FieldConfig, FieldError, FieldProps, IField } from '../common' import getALLComponents, { FieldConfigs } from '../' import { IFormItem } from '../../../steps/form' -import { cloneDeep } from 'lodash' +// import { cloneDeep } from 'lodash' +import { set, setValue } from '../../../util/produce' import ConditionHelper from '../../../util/condition' import StatementHelper from '../../../util/statement' import { ColumnsConfig } from '../../../interface' @@ -67,7 +68,7 @@ export default class GroupField extends Field { - formData[formFieldIndex] = { status: 'normal', name: formFieldConfig.label } - return { formData: cloneDeep(formData) } - }) - } else { - await this.setState(({ formData }) => { - formData[formFieldIndex] = { status: 'error', message: validation[0].message, name: formFieldConfig.label } - return { formData: cloneDeep(formData) } - }) - } + this.handleValueCallback(formFieldIndex, validation) } await formField.didMount() } @@ -182,21 +172,28 @@ export default class GroupField extends Field { + let formData = this.state.formData + if (validation === true) { + formData = set(formData, `[${formFieldIndex}]`, { status: 'normal' }) + } else { + formData = set(formData, `[${formFieldIndex}]`, { status: 'error', message: validation[0].message }) + } + + await this.setState({ + formData + }) + } + handleValueSet = async (formFieldIndex: number, path: string, value: any, validation: true | FieldError[]) => { const formFieldConfig = (this.props.config.fields || [])[formFieldIndex] if (formFieldConfig) { const fullPath = formFieldConfig.field === '' || path === '' ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` await this.props.onValueSet(fullPath, value, true) - const formData = cloneDeep(this.state.formData) - if (validation === true) { - formData[formFieldIndex] = { status: 'normal' } - } else { - formData[formFieldIndex] = { status: 'error', message: validation[0].message } - } - - this.setState({ - formData - }) + this.handleValueCallback(formFieldIndex, validation) } } @@ -205,16 +202,7 @@ export default class GroupField extends Field { - if (!ConditionHelper(formFieldConfig.condition, { record: value, data: this.props.data, step: this.props.step })) { - this.formFieldsMounted[formFieldIndex] = false + if (!ConditionHelper(formFieldConfig.condition, { record: value, data: this.props.data, step: this.props.step, extraContainerPath: this.props.config.field }, this)) { + this.formFieldsMounted = set(this.formFieldsMounted, `[${formFieldIndex}]`, false) return null } let hidden: boolean = true @@ -345,7 +305,7 @@ export default class GroupField extends Field | null) => { if (formField) { - this.formFields[formFieldIndex] = formField + this.formFields = set(this.formFields, `[${formFieldIndex}]`, formField) this.handleMount(formFieldIndex) } }} form={this.props.form} formLayout={formLayout} value={getValue(value, formFieldConfig.field)} - record={record} - data={cloneDeep(data)} + record={value} + data={data} step={step} config={formFieldConfig} onChange={async (value: any) => { await this.handleChange(formFieldIndex, value) }} @@ -374,6 +334,8 @@ export default class GroupField extends Field this.handleValueListSort(formFieldIndex, path, index, sortType, validation)} baseRoute={this.props.baseRoute} loadDomain={async (domain: string) => await this.props.loadDomain(domain)} + containerPath={getChainPath(this.props.containerPath, this.props.config.field)} + onReportFields={async (field: string) => await this.handleReportFields(field)} /> ) } diff --git a/src/components/formFields/importSubform/index.tsx b/src/components/formFields/importSubform/index.tsx index 5735eb9ac76be88b0f40b90b7af81d263cb5af57..d60bd69e7d5bb8fcec0f7fbb7b3ddc1502cb8202 100644 --- a/src/components/formFields/importSubform/index.tsx +++ b/src/components/formFields/importSubform/index.tsx @@ -1,10 +1,10 @@ import React from 'react' -import { setValue, getValue, getBoolean } from '../../../util/value' +import { getValue, getBoolean, getChainPath } from '../../../util/value' import { Field, FieldConfig, FieldError, FieldProps, IField } from '../common' import getALLComponents, { FieldConfigs } from '../' import { IFormItem } from '../../../steps/form' -import { cloneDeep } from 'lodash' +import { set, setValue } from '../../../util/produce' import ConditionHelper from '../../../util/condition' import InterfaceHelper, { InterfaceConfig } from '../../../util/interface' import StatementHelper from '../../../util/statement' @@ -49,7 +49,6 @@ export default class ImportSubformField extends Field | null> = [] formFieldsMounted: Array = [] - interfaceHelper = new InterfaceHelper() constructor (props: FieldProps) { @@ -62,13 +61,6 @@ export default class ImportSubformField extends Field { await this.setState({ didMount: true @@ -81,13 +73,14 @@ export default class ImportSubformField extends Field { - formData[formFieldIndex] = { status: 'normal' } - return { formData: cloneDeep(formData) } - }) - } else { - await this.setState(({ formData }) => { - formData[formFieldIndex] = { status: 'error', message: validation[0].message } - return { formData: cloneDeep(formData) } - }) - } + this.handleValueCallback(formFieldIndex, validation) } await formField.didMount() } @@ -214,98 +199,73 @@ export default class ImportSubformField extends Field { + let formData = this.state.formData + if (validation === true) { + formData = set(formData, `[${formFieldIndex}]`, { status: 'normal' }) + } else { + formData = set(formData, `[${formFieldIndex}]`, { status: 'error', message: validation[0].message }) + } + await this.setState({ + formData + }) + } + handleValueSet = async (formFieldIndex: number, path: string, value: any, validation: true | FieldError[]) => { const formFieldConfig = (this.state.fields || [])[formFieldIndex] if (formFieldConfig) { - const fullPath = this.getFullpath(formFieldConfig.field, path) + const withConfigPath = this.props.config.withConfig?.enable && this.props.config.withConfig?.dataField ? `${this.props.config.withConfig.dataField}` : '' + const fullPath = getChainPath(withConfigPath, formFieldConfig.field, path) await this.props.onValueSet(fullPath, value, true) - const formData = cloneDeep(this.state.formData) - if (validation === true) { - formData[formFieldIndex] = { status: 'normal' } - } else { - formData[formFieldIndex] = { status: 'error', message: validation[0].message } - } - - this.setState({ - formData - }) + this.handleValueCallback(formFieldIndex, validation) } } handleValueUnset = async (formFieldIndex: number, path: string, validation: true | FieldError[]) => { const formFieldConfig = (this.state.fields || [])[formFieldIndex] if (formFieldConfig) { - const fullPath = this.getFullpath(formFieldConfig.field, path) + const withConfigPath = this.props.config.withConfig?.enable && this.props.config.withConfig?.dataField ? `${this.props.config.withConfig.dataField}` : '' + const fullPath = getChainPath(withConfigPath, formFieldConfig.field, path) await this.props.onValueUnset(fullPath, true) - const formData = cloneDeep(this.state.formData) - if (validation === true) { - formData[formFieldIndex] = { status: 'normal' } - } else { - formData[formFieldIndex] = { status: 'error', message: validation[0].message } - } - - this.setState({ - formData - }) + this.handleValueCallback(formFieldIndex, validation) } } handleValueListAppend = async (formFieldIndex: number, path: string, value: any, validation: true | FieldError[]) => { const formFieldConfig = (this.state.fields || [])[formFieldIndex] if (formFieldConfig) { - const fullPath = this.getFullpath(formFieldConfig.field, path) + const withConfigPath = this.props.config.withConfig?.enable && this.props.config.withConfig?.dataField ? `${this.props.config.withConfig.dataField}` : '' + const fullPath = getChainPath(withConfigPath, formFieldConfig.field, path) await this.props.onValueListAppend(fullPath, value, true) - const formData = cloneDeep(this.state.formData) - if (validation === true) { - formData[formFieldIndex] = { status: 'normal' } - } else { - formData[formFieldIndex] = { status: 'error', message: validation[0].message } - } - - this.setState({ - formData - }) + this.handleValueCallback(formFieldIndex, validation) } } handleValueListSplice = async (formFieldIndex: number, path: string, index: number, count: number, validation: true | FieldError[]) => { const formFieldConfig = (this.state.fields || [])[formFieldIndex] if (formFieldConfig) { - const fullPath = this.getFullpath(formFieldConfig.field, path) + const withConfigPath = this.props.config.withConfig?.enable && this.props.config.withConfig?.dataField ? `${this.props.config.withConfig.dataField}` : '' + const fullPath = getChainPath(withConfigPath, formFieldConfig.field, path) await this.props.onValueListSplice(fullPath, index, count, true) - const formData = cloneDeep(this.state.formData) - if (validation === true) { - formData[formFieldIndex] = { status: 'normal' } - } else { - formData[formFieldIndex] = { status: 'error', message: validation[0].message } - } - - this.setState({ - formData - }) + this.handleValueCallback(formFieldIndex, validation) } } handleValueListSort = async (formFieldIndex: number, path: string, index: number, sortType: 'up' | 'down', validation: true | FieldError[]) => { const formFieldConfig = (this.state.fields || [])[formFieldIndex] if (formFieldConfig) { - const fullPath = this.getFullpath(formFieldConfig.field, path) + const withConfigPath = this.props.config.withConfig?.enable && this.props.config.withConfig?.dataField ? `${this.props.config.withConfig.dataField}` : '' + const fullPath = getChainPath(withConfigPath, formFieldConfig.field, path) await this.props.onValueListSort(fullPath, index, sortType, true) - const formData = cloneDeep(this.state.formData) - if (validation === true) { - formData[formFieldIndex] = { status: 'normal' } - } else { - formData[formFieldIndex] = { status: 'error', message: validation[0].message } - } - - this.setState({ - formData - }) + this.handleValueCallback(formFieldIndex, validation) } } @@ -331,7 +291,6 @@ export default class ImportSubformField extends Field { let dataToUnstringfy = data let dataToStringfy = JSON.stringify(data) @@ -367,14 +327,15 @@ export default class ImportSubformField extends Field } else { + const withConfigPath = this.props.config.withConfig?.enable && this.props.config.withConfig?.dataField ? `${this.props.config.withConfig.dataField}` : '' return ( {this.renderComponent({ columns: config.columns, children: this.state.didMount ? (Array.isArray(this.state.fields) ? this.state.fields : []).map((formFieldConfig, formFieldIndex) => { - if (!ConditionHelper(formFieldConfig.condition, { record: value, data, step })) { - this.formFieldsMounted[formFieldIndex] = false + if (!ConditionHelper(formFieldConfig.condition, { record: value, data, step, extraContainerPath: this.props.config.field }, this)) { + this.formFieldsMounted = set(this.formFieldsMounted, `[${formFieldIndex}]`, false) return null } let hidden: boolean = true @@ -412,7 +373,7 @@ export default class ImportSubformField extends Field | null) => { if (formField) { - this.formFields[formFieldIndex] = formField + this.formFields = set(this.formFields, `[${formFieldIndex}]`, formField) this.handleMount(formFieldIndex) } }} form={this.props.form} formLayout={formLayout} - value={getValue(value, this.getFullpath(formFieldConfig.field))} - record={record} - data={cloneDeep(data)} - step={step} + value={getValue(value, getChainPath(withConfigPath, formFieldConfig.field))} + record={value} + step={this.props.step} + data={data} + // step={step} config={formFieldConfig} onChange={async (value: any) => { await this.handleChange(formFieldIndex, value) }} onValueSet={async (path, value, validation) => this.handleValueSet(formFieldIndex, path, value, validation)} @@ -441,6 +403,8 @@ export default class ImportSubformField extends Field this.handleValueListSort(formFieldIndex, path, index, sortType, validation)} baseRoute={this.props.baseRoute} loadDomain={async (domain: string) => await this.props.loadDomain(domain)} + containerPath={getChainPath(this.props.containerPath, this.props.config.field)} + onReportFields={async (field: string) => await this.handleReportFields(field)} /> ) } diff --git a/src/components/formFields/index.tsx b/src/components/formFields/index.tsx index e7735a6dd6c6f12f256e6133b149ea6f00ea69cc..4842a892139f4267ba679a419dd07fe4da2b2fd7 100644 --- a/src/components/formFields/index.tsx +++ b/src/components/formFields/index.tsx @@ -18,7 +18,7 @@ import ImportSubformField, { ImportSubformFieldConfig } from './importSubform' import GroupField, { GroupFieldConfig } from './group' import AnyField, { AnyFieldConfig } from './any' import SwitchField, { SwitchFieldConfig } from './switch' -import ObjectField, { ObjectFieldConfig } from './object' +// import ObjectField, { ObjectFieldConfig } from './object' import HiddenField from './hidden' import TabsField, { TabsFieldConfig } from './tabs' import MultipleTextField, { MultipleTextFieldConfig } from './multipleText' @@ -64,7 +64,7 @@ export type FieldConfigs = ImportSubformFieldConfig | GroupFieldConfig | AnyFieldConfig | - ObjectFieldConfig | + // ObjectFieldConfig | TabsFieldConfig | MultipleTextFieldConfig | CustomFieldConfig @@ -90,7 +90,7 @@ export type componentType = 'group' | 'any' | 'switch' | - 'object' | + // 'object' | 'tabs' | 'multiple_text'| 'custom' @@ -114,7 +114,7 @@ export default { group: GroupField, any: AnyField, switch: SwitchField, - object: ObjectField, + // object: ObjectField, hidden: HiddenField, tabs: TabsField, multiple_text: MultipleTextField, diff --git a/src/components/formFields/object/index.tsx b/src/components/formFields/object/index.tsx index b0de063901f60ff11be62e7d7e9855d0ae8abca5..99e1c92ccd87cf3cf34e2905cb8f67e32e7a04e6 100644 --- a/src/components/formFields/object/index.tsx +++ b/src/components/formFields/object/index.tsx @@ -2,8 +2,9 @@ import { Field, FieldConfig, FieldConfigs, FieldError, FieldProps, IField } from import getALLComponents from '../' import React from 'react' import ConditionHelper from '../../../util/condition' -import { cloneDeep } from 'lodash' -import { getValue, setValue, getBoolean } from '../../../util/value' +// import { cloneDeep } from 'lodash' +import { set, setValue } from '../../../util/produce' +import { getValue, getBoolean, getChainPath, updateCommonPrefixItem } from '../../../util/value' import StatementHelper from '../../../util/statement' export interface ObjectFieldConfig extends FieldConfig { @@ -79,7 +80,7 @@ export default class ObjectField extends Field extends Field extends Field extends Field { if (!this.formFieldsMountedList[key]) { - this.formFieldsMountedList[key] = [] + this.formFieldsMountedList = set(this.formFieldsMountedList, `[${key}]`, []) } if (this.formFieldsMountedList[key][formFieldIndex]) { return true } - this.formFieldsMountedList[key][formFieldIndex] = true + this.formFieldsMountedList = set(this.formFieldsMountedList, `[${key}][${formFieldIndex}]`, true) if (this.formFieldsList[key] && this.formFieldsList[key][formFieldIndex]) { const formField = this.formFieldsList[key][formFieldIndex] if (formField) { const formFieldConfig = (this.props.config.fields || [])[formFieldIndex] - + let value = getValue(this.props.value[key], formFieldConfig.field) const source = value if ((formFieldConfig.defaultValue) && value === undefined) { @@ -164,15 +165,15 @@ export default class ObjectField extends Field { - if (!formDataList[key]) formDataList[key] = [] - formDataList[key][formFieldIndex] = { status: 'normal' } - return { formDataList: cloneDeep(formDataList) } + if (!formDataList[key]) formDataList = set(formDataList, `[${key}]`, []) + formDataList = set(formDataList, `[${key}][${formFieldIndex}]`, { status: 'normal' }) + return { formDataList: formDataList } }) } else { await this.setState(({ formDataList }) => { - if (!formDataList[key]) formDataList[key] = [] - formDataList[key][formFieldIndex] = { status: 'error', message: validation[0].message } - return { formDataList: cloneDeep(formDataList) } + if (!formDataList[key]) formDataList = set(formDataList, `[${key}]`, []) + formDataList = set(formDataList, `[${key}][${formFieldIndex}]`, { status: 'error', message: validation[0].message }) + return { formDataList: formDataList } }) } } @@ -190,11 +191,11 @@ export default class ObjectField extends Field { formDataList[key] = [] - return { formDataList: cloneDeep(formDataList) } + return { formDataList: formDataList } }) this.props.onValueSet(key, {}, true) @@ -203,27 +204,27 @@ export default class ObjectField extends Field { - delete this.formFieldsList[key] - delete this.formFieldsMountedList[key] + this.formFieldsList = set(this.formFieldsList, `${this.formFieldsList[key]}`) + this.formFieldsMountedList = set(this.formFieldsMountedList, `${this.formFieldsMountedList[key]}`) await this.setState(({ formDataList }) => { - delete formDataList[key] - return { formDataList: cloneDeep(formDataList) } + formDataList = set(formDataList, `${formDataList[key]}`) + return { formDataList: formDataList } }) this.props.onValueUnset(key, true) } handleChangeKey = async (prev: string, next: string) => { - this.formFieldsList[next] = this.formFieldsList[prev] - delete this.formFieldsList[prev] + this.formFieldsList = set(this.formFieldsList, `[${next}]`, this.formFieldsList[prev]) + this.formFieldsList = set(this.formFieldsList, `${this.formFieldsList[prev]}`) - this.formFieldsMountedList[next] = this.formFieldsMountedList[prev] - delete this.formFieldsMountedList[prev] + this.formFieldsMountedList = set(this.formFieldsMountedList, `[${next}]`, this.formFieldsMountedList[prev]) + this.formFieldsMountedList = set(this.formFieldsMountedList, `${this.formFieldsMountedList[prev]}`) await this.setState(({ formDataList }) => { - formDataList[next] = formDataList[prev] - delete formDataList[prev] - return { formDataList: cloneDeep(formDataList) } + formDataList = set(formDataList, `[${next}]`, formDataList[prev]) + formDataList = set(formDataList, `${formDataList[prev]}`) + return { formDataList: formDataList } }) this.props.onValueSet(next, this.props.value[prev], true) @@ -242,17 +243,17 @@ export default class ObjectField extends Field { - // if (!formDataList[key]) formDataList[key] = [] - // formDataList[key][formFieldIndex] = { value, status: 'normal' } + // if (!formDataList[key]) formDataList = set(formDataList, `[${key}]`, []) + // formDataList = set(formDataList, `[${key}][${formFieldIndex}]`, { status: 'normal' }) // return { formDataList: cloneDeep(formDataList) } // }) // } else { // await this.setState(({ formDataList }) => { - // if (!formDataList[key]) formDataList[key] = [] - // formDataList[key][formFieldIndex] = { value, status: 'error', message: validation[0].message } + // if (!formDataList[key]) formDataList = set(formDataList, `[${key}]`, []) + // formDataList = set(formDataList, `[${key}][${formFieldIndex}]`, { status: 'error', message: validation[0].message }) // return { formDataList: cloneDeep(formDataList) } // }) // } @@ -265,11 +266,11 @@ export default class ObjectField extends Field extends Field extends Field extends Field extends Field extends Field extends Field await this.handleChangeKey(key, value), onRemove: async () => await this.handleRemove(key), children: (Array.isArray(this.props.config.fields) ? this.props.config.fields : []).map((formFieldConfig, formFieldIndex) => { - if (!ConditionHelper(formFieldConfig.condition, { record: this.props.record, data: this.props.data, step: this.props.step })) { - if (!this.formFieldsMountedList[key]) this.formFieldsMountedList[key] = [] - this.formFieldsMountedList[key][formFieldIndex] = false + if (!ConditionHelper(formFieldConfig.condition, { record: this.props.record, data: this.props.data, step: this.props.step }, this)) { + if (!this.formFieldsMountedList[key]) this.formFieldsMountedList = set(this.formFieldsMountedList, `[${key}]`, []) + this.formFieldsMountedList = set(this.formFieldsMountedList, `[${key}][${formFieldIndex}]`, false) return null } let hidden: boolean = true @@ -459,8 +463,8 @@ export default class ObjectField extends Field | null) => { if (formField) { - if (!this.formFieldsList[key]) this.formFieldsList[key] = [] - this.formFieldsList[key][formFieldIndex] = formField + if (!this.formFieldsList[key]) this.formFieldsList = set(this.formFieldsList, `[${key}]`, []) + this.formFieldsList = set(this.formFieldsList, `[${key}][${formFieldIndex}]`, formField) this.handleMount(key, formFieldIndex) } }} @@ -468,7 +472,7 @@ export default class ObjectField extends Field this.handleChange(key, formFieldIndex, value)} @@ -479,6 +483,8 @@ export default class ObjectField extends Field this.handleValueListSort(key, formFieldIndex, path, _index, sortType, validation)} baseRoute={this.props.baseRoute} loadDomain={async (domain: string) => this.props.loadDomain(domain)} + containerPath={getChainPath(this.props.containerPath, this.props.config.field, key)} + onReportFields={async (field: string) => await this.handleReportFields(field)} /> ) }) diff --git a/src/components/formFields/readme.md b/src/components/formFields/readme.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d69849c4b2d5951816ade3b962b89adb3b9b1f6f 100644 --- a/src/components/formFields/readme.md +++ b/src/components/formFields/readme.md @@ -0,0 +1,67 @@ +#### immer + +Immer是一个简单易用、体量小巧、设计巧妙的immutable 库,以最小的成本实现了js的不可变数据结构。 + +Js的对象是引用类型的,在使用过程中常常会不经意的修改。为了解决这个问题,除了可以使用深度拷贝的方法(但成本高,影响性能),还可以使用一些不可变数据结构的库,Immer就是其中的佼佼者。 +immer基于copy-on-write机制——在当前的状态数据上复制出一份临时的草稿,然后对这份草稿修改,最后生成新的状态数据。借力于ES6的Proxy,跟响应式的自动跟踪是一样的。 +https://immerjs.github.io/immer/api + +#### immer写法 + +```js +import produce from "immer" + +this.state = { + members: [{name: 'user', age: 18}] +} + +// 一般写法 +this.setState(state=>{ + return produce(state,draft=>{ + draft.members[0].age++; + }) +}) +// 高阶写法 +this.setState(state=>{ + return produce(draft=>{ + draft.members[0].age++; + })(state) +}) +// 简洁写法 +this.setState(produce(draft => { + draft.members[0].age++; +})) +``` +#### immer拓展 +1. 返回值问题 +以高阶函数写法为例 +语法:produce(recipe: (draftState) => void | draftState, ?PatchListener)(currentState): nextState +recipe 是否有返回值,nextState 的生成过程是不同的: +- recipe 没有返回值时:nextState是根据recipe 函数内的 draftState生成的; +- recipe有返回值时:nextState是根据 recipe 函数的返回值生成的 + +了解了这些,方便在开发中更加灵活地运用immer +2. Auto freezing(自动冻结) +Immer 会自动冻结使用 produce 修改过的状态树,这样可以防止在变更函数外部修改状态树。这个特性会带来性能影响,所以需要在生产环境中关闭。可以使用 setAutoFreeze(true / false) 打开或者关闭。在开发环境中建议打开,可以避免不可预测的状态树更改 +https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze + +#### immer结合shouldComponentUpdate做了哪些事? + SCU的对于引用类型对象的比较,如果通过比较它们的值往往很耗费性能,有了数据结构共享,我们可以通过比较复杂对象的每一个节点的指针指向是否相同,大大节省性能损耗 + +#### 项目中数据依赖字段上报规则及注意record(当前记录)注意事项 +1. 每一个组件在handleMount以后知道它们的this.props.containerPath和自身的field路径,以containerPath字段向下传递给子组件,我们在paramHelper里面对应record里的依赖项的处理是,拿到父级的路径和record依赖的字段(举例为DepField),通过getChainPath(this.props.containerPath, DepField)拿到完整的链式路径,onReportFields上报给父组件有哪些依赖项,参见下: + +containerPath=this.props.containerPath+field ----传递子组件---> containerPath=this.props.containerPath+field +父组件 <----onReportFields--- getChainPath(this.props.containerPath, DepField) + +2. 正常来说,父组件的value就是子组件的record, 特殊的是tabs和form两个组件对应分别多了tab.field和index,这里通过Helper传递record时,取值分别对应下探到tab.field和index。 +示例: + +```js +// import_subform和group用法: +ConditionHelper(formFieldConfig.condition, { record: this.props.value,...}) +ConditionHelper(formFieldConfig.condition, { record: this.props.value,...}) +// tabs和form用法: +ConditionHelper(formFieldConfig.condition, { record: this.props.value[tab.field],...}) +ConditionHelper(formFieldConfig.condition, { record: this.props.value[index],...}) +``` \ No newline at end of file diff --git a/src/components/formFields/select/common.tsx b/src/components/formFields/select/common.tsx index 528b21d335fcb614216e17a0cbb4b14ae9adf729..2141c97aa236f634159dbd18a1c14bf0313be324 100644 --- a/src/components/formFields/select/common.tsx +++ b/src/components/formFields/select/common.tsx @@ -37,7 +37,7 @@ export default class SelectField extends Fiel config: EnumerationOptionsConfig | undefined ) => { if (config) { - EnumerationHelper.options(config, (config, source) => this.interfaceHelper.request(config, source, { record: this.props.record, data: this.props.data, step: this.props.step }, { loadDomain: this.props.loadDomain })).then((options) => { + EnumerationHelper.options(config, (config, source) => this.interfaceHelper.request(config, source, { record: this.props.record, data: this.props.data, step: this.props.step }, { loadDomain: this.props.loadDomain }, this)).then((options) => { if (JSON.stringify(this.state.options) !== JSON.stringify(options)) { this.setState({ options diff --git a/src/components/formFields/tabs/index.tsx b/src/components/formFields/tabs/index.tsx index a32b1157b5b184b5c38df4136ce26cb4743a4a6d..f9ee327b31f29bba1fdac0f09ca9383bf4390eb5 100644 --- a/src/components/formFields/tabs/index.tsx +++ b/src/components/formFields/tabs/index.tsx @@ -2,8 +2,8 @@ import { Field, FieldConfig, FieldConfigs, FieldError, FieldProps, IField } from import getALLComponents from '../' import React from 'react' import ConditionHelper from '../../../util/condition' -import { cloneDeep } from 'lodash' -import { getValue, setValue, getBoolean } from '../../../util/value' +import { set, setValue } from '../../../util/produce' +import { getValue, getBoolean, getChainPath } from '../../../util/value' import StatementHelper from '../../../util/statement' export type TabsFieldConfig = TabsFieldConfig_Same | TabsFieldConfig_Diff @@ -90,7 +90,7 @@ export default class TabsField extends Field extends Field extends Field extends Field { if (!this.formFieldsMountedList[index]) { - this.formFieldsMountedList[index] = [] + this.formFieldsMountedList = set(this.formFieldsMountedList, `[${index}]`, []) } if (this.formFieldsMountedList[index][formFieldIndex]) { return true } - this.formFieldsMountedList[index][formFieldIndex] = true + this.formFieldsMountedList = set(this.formFieldsMountedList, `[${index}][${formFieldIndex}]`, true) const tab = (this.props.config.tabs || [])[index] @@ -180,19 +180,7 @@ export default class TabsField extends Field { - if (!formDataList[index]) formDataList[index] = [] - formDataList[index][formFieldIndex] = { status: 'normal' } - return { formDataList: cloneDeep(formDataList) } - }) - } else { - await this.setState(({ formDataList }) => { - if (!formDataList[index]) formDataList[index] = [] - formDataList[index][formFieldIndex] = { status: 'error', message: validation[0].message } - return { formDataList: cloneDeep(formDataList) } - }) - } + this.handleValueCallback(index, formFieldIndex, validation) } await formField.didMount() } @@ -202,6 +190,23 @@ export default class TabsField extends Field { } + /** + * 处理set、unset、append、splice、sort后的操作 + */ + handleValueCallback = async (index: number, formFieldIndex: number, validation: true | FieldError[]) => { + let formDataList = this.state.formDataList + // if (!formDataList[index]) formDataList = set(formDataList, `[${index}]`, []) + if (validation === true) { + formDataList = set(formDataList, `[${index}][${formFieldIndex}]`, { status: 'normal' }) + } else { + formDataList = set(formDataList, `[${index}][${formFieldIndex}]`, { status: 'error', message: validation[0].message }) + } + + this.setState({ + formDataList + }) + } + handleValueSet = async (index: number, formFieldIndex: number, path: string, value: any, validation: true | FieldError[]) => { const tab = (this.props.config.tabs || [])[index] @@ -212,17 +217,7 @@ export default class TabsField extends Field extends Field extends Field extends Field extends Field extends Field { - return + renderItemComponent = (props: ITabsFieldItem) => { + return 您当前使用的UI版本没有实现FormField组件的renderItemComponent方法。 - } + } - /** - * 用于展示子表单组件中的每一子项中的每一个子表单项组件 - * @param props - * @returns - */ - renderItemFieldComponent = (props: ITabsFieldItemField) => { - return + /** + * 用于展示子表单组件中的每一子项中的每一个子表单项组件 + * @param props + * @returns + */ + renderItemFieldComponent = (props: ITabsFieldItemField) => { + return 您当前使用的UI版本没有实现FormField组件的renderItemFieldComponent方法。 - } + } render = () => { const { - value = {}, - config: { - label - } + value = {} } = this.props return ( @@ -372,84 +324,86 @@ export default class TabsField extends Field { 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) || Field - - let status = ((this.state.formDataList[index] || [])[formFieldIndex] || {}).status || 'normal' - - if (['group', 'import_subform', 'object', 'tabs', 'form'].some((type) => type === formFieldConfig.type)) { - status = 'normal' - } - - // 渲染表单项容器 - if (hidden) { - return ( -
- {this.renderItemFieldComponent({ - index: formFieldIndex, - label: formFieldConfig.label, - status, - message: ((this.state.formDataList[index] || [])[formFieldIndex] || {}).message || '', - required: getBoolean(formFieldConfig.required), - extra: StatementHelper(formFieldConfig.extra, { record: this.props.record, data: this.props.data, step: this.props.step }), - layout: this.props.formLayout, - fieldType: formFieldConfig.type, - children: ( - | null) => { - if (!this.formFieldsList[index]) this.formFieldsList[index] = [] - this.formFieldsList[index][formFieldIndex] = formField - this.handleMount(index, formFieldIndex) - }} - form={this.props.form} - formLayout={this.props.formLayout} - value={getValue(getValue(value, tab.field), formFieldConfig.field)} - record={getValue(value, tab.field)} - data={cloneDeep(this.props.data)} - step={this.props.step} - config={formFieldConfig} - onChange={(value: any) => this.handleChange(index, formFieldIndex, value)} - 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)} - onValueListSort={async (path, _index, sortType, validation) => this.handleValueListSort(index, formFieldIndex, path, _index, sortType, validation)} - baseRoute={this.props.baseRoute} - loadDomain={async (domain: string) => await this.props.loadDomain(domain)} - /> - ) - })} -
- ) - } else { - return - } - }) - })} - + + {this.renderItemComponent({ + key: index.toString(), + label: tab.label, + children: fields.map((formFieldConfig, formFieldIndex) => { + if (!ConditionHelper(formFieldConfig.condition, { record: getValue(value, tab.field), data: this.props.data, step: this.props.step, extraContainerPath: getChainPath(this.props.config.field, tab.field) }, this)) { + if (!this.formFieldsMountedList[index]) this.formFieldsMountedList = set(this.formFieldsMountedList, `[${index}]`, []) + this.formFieldsMountedList = set(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) || Field + + let status = ((this.state.formDataList[index] || [])[formFieldIndex] || {}).status || 'normal' + + if (['group', 'import_subform', 'object', 'tabs', 'form'].some((type) => type === formFieldConfig.type)) { + status = 'normal' + } + + // 渲染表单项容器 + if (hidden) { + return ( +
+ {this.renderItemFieldComponent({ + index: formFieldIndex, + label: formFieldConfig.label, + status, + message: ((this.state.formDataList[index] || [])[formFieldIndex] || {}).message || '', + required: getBoolean(formFieldConfig.required), + extra: StatementHelper(formFieldConfig.extra, { record: getValue(value, tab.field), data: this.props.data, step: this.props.step, extraContainerPath: getChainPath(this.props.config.field, tab.field) }, this), + layout: this.props.formLayout, + fieldType: formFieldConfig.type, + children: ( + | null) => { + if (!this.formFieldsList[index]) this.formFieldsList = set(this.formFieldsList, `[${index}]`, []) + this.formFieldsList = set(this.formFieldsList, `[${index}][${formFieldIndex}]`, formField) + this.handleMount(index, formFieldIndex) + }} + form={this.props.form} + formLayout={this.props.formLayout} + value={getValue(value, getChainPath(tab.field, formFieldConfig.field))} + record={getValue(value, tab.field)} + data={this.props.data} + step={this.props.step} + config={formFieldConfig} + onChange={(value: any) => this.handleChange(index, formFieldIndex, value)} + 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)} + onValueListSort={async (path, _index, sortType, validation) => this.handleValueListSort(index, formFieldIndex, path, _index, sortType, validation)} + baseRoute={this.props.baseRoute} + loadDomain={async (domain: string) => await this.props.loadDomain(domain)} + containerPath={getChainPath(this.props.containerPath, this.props.config.field, tab.field)} + onReportFields={async (field: string) => await this.handleReportFields(field)} + /> + ) + })} +
+ ) + } else { + return + } + }) + })} + ) }) : [] diff --git a/src/components/formFields/text/index.test.tsx b/src/components/formFields/text/index.test.tsx index 1633d3f9fd0b2d5af27c8f2de189a4cae175b263..61e58445e074855ee87b29aea8f6978b770c68e9 100644 --- a/src/components/formFields/text/index.test.tsx +++ b/src/components/formFields/text/index.test.tsx @@ -89,7 +89,7 @@ test('文本框默认值 -数据值 query', () => { cleanup() resolve(true) }} - config={{ field: 'jest', label: 'jest', type: 'text', defaultValue: { source: 'query', filed: 'default' } }} + config={{ field: 'jest', label: 'jest', type: 'text', defaultValue: { source: 'query', field: 'default' } }} /> ) }) @@ -108,7 +108,7 @@ test('文本框默认值 -数据值 hash', () => { cleanup() resolve(true) }} - config={{ field: 'jest', label: 'jest', type: 'text', defaultValue: { source: 'hash', filed: 'default' } }} + config={{ field: 'jest', label: 'jest', type: 'text', defaultValue: { source: 'hash', field: 'default' } }} /> ) }) diff --git a/src/components/formFields/text/index.tsx b/src/components/formFields/text/index.tsx index e3d80c9fe58a3038833654c6d78aa01a76769783..b423a19bca0a992a5c6e327d3b10c9b8128cdf6d 100644 --- a/src/components/formFields/text/index.tsx +++ b/src/components/formFields/text/index.tsx @@ -1,4 +1,5 @@ import React from 'react' +import { isEqual, get } from 'lodash' import { getBoolean } from '../../../util/value' import { Field, FieldConfig, FieldError, IField } from '../common' diff --git a/src/components/formFields/treeSelect/index.tsx b/src/components/formFields/treeSelect/index.tsx index 6d3973ad1f9f559c9ada26bc685d458ec4a8406d..13d8211e870205226cca02b2aef833f3317dbf68 100644 --- a/src/components/formFields/treeSelect/index.tsx +++ b/src/components/formFields/treeSelect/index.tsx @@ -99,7 +99,7 @@ export default class TreeSelectField extends Field { if (config) { @@ -113,7 +113,8 @@ export default class TreeSelectField extends Field { this.setState({ interfaceOptionsData: this.formatTree( diff --git a/src/components/formFields/upload/index.tsx b/src/components/formFields/upload/index.tsx index 5b8fc83ecb6a6ce2d1a7483372cbf60e3a772e7d..29f4372f6f0aa6f18a7779c34e7a99e4e498d6af 100644 --- a/src/components/formFields/upload/index.tsx +++ b/src/components/formFields/upload/index.tsx @@ -161,7 +161,8 @@ export default class UploadField extends Field = { label: 'test', valueType: 'string', multiple: true, + align: 'left', options: { from: 'manual', data: [{ extra: 'a', // todo label: '1', - key: 'filed' + key: 'field' }] }, defaultValue: '' diff --git a/src/interface.ts b/src/interface.ts index 1f559664d9d0d57597e44744d185fff77e557cf3..7b2d150df51aee7852fb564e6ed51fe005ce3b6c 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -40,11 +40,11 @@ interface URLParamConfig { } interface QueryParamConfig { source: 'query', - filed: any + field: string } interface HashParamConfig { source: 'hash', - filed: any + field: string } interface InterfaceParamConfig { source: 'interface', diff --git a/src/main.tsx b/src/main.tsx index d580507d2e1a2b24e2458002ae7a33ad6c2f49d0..1dac8e5f773c5b5e2e67f3616ccc54bf84c9e5e5 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,9 +1,8 @@ -import React from 'react' +import React, { forwardRef } from 'react' import marked from 'marked' import Step, { StepProps } from './steps/common' import StepComponents, { StepConfigs } from './steps' import { RichStringConfig } from './interface' - /** * 页面配置文件格式定义 * - basic: 页面基本配置 @@ -38,7 +37,7 @@ export interface ICCMS { * - config: 页面配置文件 * - sourceData: 传入数据 */ -export interface CCMSProps { +export interface CCMSProps { config: CCMSConfig sourceData: any baseRoute: string @@ -50,6 +49,7 @@ export interface CCMSProps { handlePageRedirect?: (path: string) => void callback: (success: boolean) => void onMount?: () => void + handleFormValue?: (payload: object) => object } /** @@ -58,7 +58,7 @@ export interface CCMSProps { * - viewStep: 界面当前所在步骤 * - data: 各步骤数据 */ -interface CCMSState { +export interface CCMSState { realStep: number viewStep: number[] data: any[] @@ -67,7 +67,7 @@ interface CCMSState { /** * 页面组件 */ -export default class CCMS extends React.Component { +export default class CCMS1 extends React.Component { getStepComponent = (key: string) => StepComponents[key] /** @@ -236,7 +236,7 @@ export default class CCMS extends React.Component { loadDomain, handlePageRedirect } = this.props - + const handleFormValue = this.props.handleFormValue ? this.props.handleFormValue : (payload: object) => ({}) const { realStep, viewStep, @@ -265,7 +265,8 @@ export default class CCMS extends React.Component { const props: StepProps = { ref: (e) => { this.steps[index] = e }, data, - step: index, + // step: index, + step: data[index], onSubmit: (data: any, unmountView: boolean = true) => this.handleSubmit(index, data, unmountView), onMount: () => this.handleMount(index), onUnmount: (reload: boolean = false, data?: any) => this.handleUnmount(index, reload, data), @@ -276,12 +277,13 @@ export default class CCMS extends React.Component { loadPageFrameURL, loadPageConfig, loadDomain, - handlePageRedirect + handlePageRedirect, + handleFormValue } - + const StepComponent = this.getStepComponent(currentStep.type) const children = ( - StepComponent ? : 您当前使用的UI版本没有实现{currentStep.type}步骤组件。 + StepComponent ? : 您当前使用的UI版本没有实现{currentStep.type}步骤组件。 ) return (
{children}
diff --git a/src/steps/common.tsx b/src/steps/common.tsx index 88da0fd7de79b099718f3c48cb43adaee7c70647..ecd4181acdf2f29b79ac2a8b60b74a10a118f0e0 100644 --- a/src/steps/common.tsx +++ b/src/steps/common.tsx @@ -20,7 +20,8 @@ export interface StepConfig { export interface StepProps { ref: (instance: Step | null) => void data: any[] - step: number + step: {[field: string]: any} + // step: number config: C onChange?: (data: any) => Promise onSubmit: (data: any, unmountView?: boolean) => Promise @@ -33,6 +34,7 @@ export interface StepProps { baseRoute: string loadDomain: (domain: string) => Promise handlePageRedirect?: (path: string) => void + handleFormValue?: (payload: object) => object } /** diff --git a/src/steps/detail/index.tsx b/src/steps/detail/index.tsx index 0ff404cb3539f6440545d44dde0507f2ef42bb2f..179273f1d335d4f261c526c14ace423ab5d95391 100644 --- a/src/steps/detail/index.tsx +++ b/src/steps/detail/index.tsx @@ -389,8 +389,7 @@ export default class DetailStep extends Step { const { ready, - detailValue, - detailData + detailValue } = this.state if (ready) { @@ -455,8 +454,9 @@ export default class DetailStep extends Step { formLayout={layout} value={detailFieldConfig.field !== undefined ? getValue(detailValue, detailFieldConfig.field) || detailFieldConfig.defaultValue : undefined} record={detailValue} + step={cloneDeep(detailValue)} + // step={step} data={cloneDeep(data)} - step={step} config={detailFieldConfig} onChange={async (value: any) => { await this.handleChange(detailFieldIndex, value) }} onValueSet={async (path, value, validation) => await this.handleValueSet(detailFieldIndex, path, value, validation)} diff --git a/src/steps/fetch/index.tsx b/src/steps/fetch/index.tsx index 19f631ef62e86074f73d93dd3b2886d42223bdb2..954526360d3a568401cc340244b97a8760ea9d14 100644 --- a/src/steps/fetch/index.tsx +++ b/src/steps/fetch/index.tsx @@ -40,7 +40,7 @@ export default class FetchStep extends Step { try { const content = await this.interfaceHelper.request( merge(config.interface, { cache: { disabled: true } }), - {...(this.popData || {}), ...(init_data || {}), ...(this.props.data[this.props.step] || {})}, + {...(this.popData || {}), ...(init_data || {}), ...(this.props.step || {})}, { data: this.props.data, step: this.props.step diff --git a/src/steps/filter/index.tsx b/src/steps/filter/index.tsx index 3f1bd371a4ba70596afeb6743f0f862c83b924db..bb6e103e7f683d3517dd01d7a7bd6728cc889f6f 100644 --- a/src/steps/filter/index.tsx +++ b/src/steps/filter/index.tsx @@ -4,9 +4,10 @@ import Step, { StepConfig, StepProps } from '../common' import getALLComponents from '../../components/formFields' import { ParamConfig } from '../../interface' import ParamHelper from '../../util/param' -import { cloneDeep, get, set, unset } from 'lodash' +import { get } from 'lodash' +import { push, splice, sort, set, setValue } from '../../util/produce' import ConditionHelper from '../../util/condition' -import { getValue, setValue, listItemMove } from '../../util/value' +import { getValue } from '../../util/value' /** * 表单步骤配置文件格式定义 @@ -88,6 +89,7 @@ export default class FilterStep extends Step { // 各表单项所使用的UI组件的实例 formFields: Array | null> = [] formFieldsMounted: Array = [] + dependentFields_: string[] = [] formValue: { [field: string]: any } = {} formData: { status: 'normal' | 'error' | 'loading', message?: string }[] = [] @@ -118,13 +120,13 @@ export default class FilterStep extends Step { const formFieldConfig = (this.props.config.fields || [])[formFieldIndex] const value = (formFieldConfig.field === undefined || formFieldConfig.field === '') ? formDefault : get(formDefault, formFieldConfig.field) this.formValue = setValue(this.formValue, formFieldConfig.field, value) - this.formData[formFieldIndex] = { status: 'normal' } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'normal' }) } } await this.setState({ formValue: this.formValue, - formData: cloneDeep(this.formData) + formData: this.formData }) // 表单初始化结束,展示表单界面。 @@ -141,7 +143,7 @@ export default class FilterStep extends Step { return true } - this.formFieldsMounted[formFieldIndex] = true + this.formFieldsMounted = set(this.formFieldsMounted, `[${formFieldIndex}]`, true) if (this.formFields[formFieldIndex]) { const formField = this.formFields[formFieldIndex] @@ -157,16 +159,16 @@ export default class FilterStep extends Step { if (value !== undefined) { const validation = await formField.validate(value) if (validation === true) { - this.formData[formFieldIndex] = { status: 'normal' } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'normal' }) } else { - this.formData[formFieldIndex] = { status: 'error', message: validation[0].message } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'error', message: validation[0].message }) } } } } await this.setState({ formValue: this.formValue, - formData: cloneDeep(this.formData) + formData: this.formData }) } @@ -198,7 +200,7 @@ export default class FilterStep extends Step { console.info('表单校验通过', data) await this.setState({ - formData: cloneDeep(this.formData) + formData: this.formData }) if (canSubmit && this.props.onSubmit) { @@ -235,16 +237,16 @@ export default class FilterStep extends Step { const validation = await formField.validate(value) if (validation === true) { - this.formData[formFieldIndex] = { status: 'normal' } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'normal' }) } else { - this.formData[formFieldIndex] = { status: 'error', message: validation[0].message } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'error', message: validation[0].message }) } } } await this.setState({ formValue: this.formValue, - formData: cloneDeep(this.formData) + formData: this.formData }) this.handleSubmit() @@ -263,14 +265,14 @@ export default class FilterStep extends Step { const validation = await formField.validate(value) if (validation === true) { - this.formData[formFieldIndex] = { status: 'normal' } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'normal' }) } else { - this.formData[formFieldIndex] = { status: 'error', message: validation[0].message } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'error', message: validation[0].message }) } await this.setState({ formValue: this.formValue, - formData: cloneDeep(this.formData) + formData: this.formData }) if (this.props.onChange) { this.props.onChange(this.formValue) @@ -283,7 +285,7 @@ export default class FilterStep extends Step { if (formFieldConfig) { const fullPath = formFieldConfig.field === '' || path === '' ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` - set(this.formValue, fullPath, value) + this.formValue = set(this.formValue, fullPath, value) this.setState({ formValue: this.formValue }) @@ -292,13 +294,13 @@ export default class FilterStep extends Step { } if (validation === true) { - this.formData[formFieldIndex] = { status: 'normal' } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'normal' }) } else { - this.formData[formFieldIndex] = { status: 'error', message: validation[0].message } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'error', message: validation[0].message }) } await this.setState({ - formData: cloneDeep(this.formData) + formData: this.formData }) } } @@ -308,7 +310,8 @@ export default class FilterStep extends Step { if (formFieldConfig) { const fullPath = formFieldConfig.field === '' || path === '' ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` - unset(this.formValue, fullPath) + // unset(this.formValue, fullPath) + this.formValue = set(this.formValue, fullPath) this.setState({ formValue: this.formValue }) @@ -317,13 +320,13 @@ export default class FilterStep extends Step { } if (validation === true) { - this.formData[formFieldIndex] = { status: 'normal' } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'normal' }) } else { - this.formData[formFieldIndex] = { status: 'error', message: validation[0].message } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'error', message: validation[0].message }) } await this.setState({ - formData: cloneDeep(this.formData) + formData: this.formData }) } } @@ -333,9 +336,7 @@ export default class FilterStep extends Step { if (formFieldConfig) { const fullPath = formFieldConfig.field === '' || path === '' ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` - const list = get(this.formValue, fullPath, []) - list.push(value) - set(this.formValue, fullPath, list) + this.formValue = push(this.formValue, fullPath, value) this.setState({ formValue: this.formValue }) @@ -344,13 +345,13 @@ export default class FilterStep extends Step { } if (validation === true) { - this.formData[formFieldIndex] = { status: 'normal' } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'normal' }) } else { - this.formData[formFieldIndex] = { status: 'error', message: validation[0].message } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'error', message: validation[0].message }) } await this.setState({ - formData: cloneDeep(this.formData) + formData: this.formData }) } } @@ -360,9 +361,7 @@ export default class FilterStep extends Step { if (formFieldConfig) { const fullPath = formFieldConfig.field === '' || path === '' ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` - const list = get(this.formValue, fullPath, []) - list.splice(index, count) - set(this.formValue, fullPath, list) + this.formValue = splice(this.formValue, fullPath, index, count) this.setState({ formValue: this.formValue }) @@ -371,23 +370,23 @@ export default class FilterStep extends Step { } if (validation === true) { - this.formData[formFieldIndex] = { status: 'normal' } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'normal' }) } else { - this.formData[formFieldIndex] = { status: 'error', message: validation[0].message } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'error', message: validation[0].message }) } await this.setState({ - formData: cloneDeep(this.formData) + formData: this.formData }) } } + handleValueListSort = async (formFieldIndex: number, path: string, index: number, sortType: 'up' | 'down', validation: true | FieldError[]) => { const formFieldConfig = (this.props.config.fields || [])[formFieldIndex] if (formFieldConfig) { const fullPath = formFieldConfig.field === '' || path === '' ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` - const list = listItemMove(get(this.formValue, fullPath, []), index, sortType) - set(this.formValue, fullPath, list) + this.formValue = sort(this.formValue, fullPath, index, sortType) this.setState({ formValue: this.formValue }) @@ -396,13 +395,13 @@ export default class FilterStep extends Step { } if (validation === true) { - this.formData[formFieldIndex] = { status: 'normal' } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'normal' }) } else { - this.formData[formFieldIndex] = { status: 'error', message: validation[0].message } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'error', message: validation[0].message }) } await this.setState({ - formData: cloneDeep(this.formData) + formData: this.formData }) } } @@ -449,11 +448,11 @@ export default class FilterStep extends Step { {this.renderComponent({ onSubmit: this.props.config?.hiddenSubmit ? undefined : async () => this.handleSubmit(), onReset: this.props.config?.hiddenReset ? undefined : async () => this.handleReset(), - submitText: this.props.config?.submitText?.replace(/(^\s*)|(\s*$)/g, ""), - resetText: this.props.config?.resetText?.replace(/(^\s*)|(\s*$)/g, ""), + submitText: this.props.config?.submitText?.replace(/(^\s*)|(\s*$)/g, ''), + resetText: this.props.config?.resetText?.replace(/(^\s*)|(\s*$)/g, ''), children: fields.map((formFieldConfig, formFieldIndex) => { if (!ConditionHelper(formFieldConfig.condition, { record: formValue, data, step })) { - this.formFieldsMounted[formFieldIndex] = false + this.formFieldsMounted = set(this.formFieldsMounted, `[${formFieldIndex}]`, false) return null } let hidden: boolean = true @@ -488,7 +487,7 @@ export default class FilterStep extends Step { key={formFieldIndex} ref={(formField: Field | null) => { if (formFieldIndex !== null) { - this.formFields[formFieldIndex] = formField + this.formFields = set(this.formFields, `[${formFieldIndex}]`, formField) this.handleFormFieldMount(formFieldIndex) } }} @@ -496,8 +495,9 @@ export default class FilterStep extends Step { formLayout={'inline'} value={formFieldConfig.field !== undefined ? get(formValue, formFieldConfig.field) : undefined} record={formValue} - data={cloneDeep(data)} - step={step} + step={formValue} + // step={step} + data={data} config={formFieldConfig} onChange={async (value: any) => { await this.handleChange(formFieldIndex, value) }} onValueSet={async (path, value, validation) => await this.handleValueSet(formFieldIndex, path, value, validation)} @@ -507,6 +507,7 @@ export default class FilterStep extends Step { onValueListSort={async (path, index, sortType, validation) => await this.handleValueListSort(formFieldIndex, path, index, sortType, validation)} baseRoute={this.props.baseRoute} loadDomain={async (domain: string) => await this.props.loadDomain(domain)} + containerPath={''} /> ) } diff --git a/src/steps/form/index.tsx b/src/steps/form/index.tsx index 5be24f068f51c1210dbba4e48eaa5d87f2f7fb52..79114bf3b279b56a2861f78e8d7a9753633832c2 100644 --- a/src/steps/form/index.tsx +++ b/src/steps/form/index.tsx @@ -2,10 +2,10 @@ import React from 'react' import { Field, FieldConfigs, FieldError } from '../../components/formFields/common' import Step, { StepConfig, StepProps } from '../common' import getALLComponents from '../../components/formFields' -import { getValue, setValue, listItemMove, getBoolean } from '../../util/value' import { ColumnsConfig, ParamConfig } from '../../interface' +import { getValue, getBoolean } from '../../util/value' import ParamHelper from '../../util/param' -import { cloneDeep, get, set, unset } from 'lodash' +import { push, splice, sort, set, setValue } from '../../util/produce' import ConditionHelper, { ConditionConfig } from '../../util/condition' import StatementHelper, { StatementConfig } from '../../util/statement' import OperationHelper, { OperationConfig } from '../../util/operation' @@ -178,7 +178,7 @@ interface FormState { /** * 表单步骤组件 */ -export default class FormStep extends Step { +export default class FormStep extends Step { // ts对class的声明文件报错,临时解决 // 各表单项对应的类型所使用的UI组件的类 getALLComponents = (type: any): typeof Field => getALLComponents[type] OperationHelper = OperationHelper @@ -186,6 +186,7 @@ export default class FormStep extends Step { // 各表单项所使用的UI组件的实例 formFields: Array | null> = [] formFieldsMounted: Array = [] + dependentFields_: string[] = [] formValue: { [field: string]: any } = {} formData: { status: 'normal' | 'error' | 'loading', message?: string, name: string }[] = [] @@ -239,15 +240,16 @@ export default class FormStep extends Step { for (const formFieldIndex in formFieldsConfig) { const formFieldConfig = formFieldsConfig[formFieldIndex] const value = getValue(formDefault, formFieldConfig.field) + this.formValue = setValue(this.formValue, formFieldConfig.field, value) - this.formData[formFieldIndex] = { status: 'normal', name: formFieldConfig.label } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'normal', name: formFieldConfig.label }) } } await this.setState({ ready: true, formValue: this.formValue, - formData: cloneDeep(this.formData) + formData: this.formData }) // 表单初始化结束,展示表单界面。 @@ -258,13 +260,12 @@ export default class FormStep extends Step { if (this.formFieldsMounted[formFieldIndex]) { return true } - this.formFieldsMounted[formFieldIndex] = true + this.formFieldsMounted = set(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.formValue, formFieldConfig.field) if ((formFieldConfig.defaultValue) && value === undefined) { value = await formField.reset() @@ -275,9 +276,9 @@ export default class FormStep extends Step { if (value !== undefined) { const validation = await formField.validate(value) if (validation === true) { - this.formData[formFieldIndex] = { status: 'normal', name: formFieldConfig.label } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'normal', name: formFieldConfig.label }) } else { - this.formData[formFieldIndex] = { status: 'error', message: validation[0].message, name: formFieldConfig.label } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'error', message: validation[0].message, name: formFieldConfig.label }) } } await formField.didMount() @@ -286,7 +287,7 @@ export default class FormStep extends Step { await this.setState({ formValue: this.formValue, - formData: cloneDeep(this.formData) + formData: this.formData }) } @@ -298,9 +299,9 @@ export default class FormStep extends Step { this.submitData = {} if (this.props.config.validations) { for (const validation of this.props.config.validations) { - if (!ConditionHelper(validation.condition, { record: this.state.formValue, data: this.props.data, step: this.props.step })) { + if (!ConditionHelper(validation.condition, { record: this.state.formValue, data: this.props.data, step: this.formValue })) { this.canSubmit = false - const message = StatementHelper(validation.message, { record: this.state.formValue, data: this.props.data, step: this.props.step }) || '未填写失败文案或失败文案配置异常' + const message = StatementHelper(validation.message, { record: this.state.formValue, data: this.props.data, step: this.formValue }) || '未填写失败文案或失败文案配置异常' this.renderModalComponent({ message }) return } @@ -318,7 +319,7 @@ export default class FormStep extends Step { if (validation !== true) { console.warn('表单项中存在问题', value, formFieldConfig) - this.formData[formFieldIndex] = { status: 'error', message: validation[0].message, name: formFieldConfig.label } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'error', message: validation[0].message, name: formFieldConfig.label }) this.canSubmit = false } this.submitData = setValue(this.submitData, formFieldConfig.field, value) @@ -334,7 +335,7 @@ export default class FormStep extends Step { } await this.setState({ - formData: cloneDeep(this.formData) + formData: this.formData }) } @@ -375,14 +376,14 @@ export default class FormStep extends Step { const validation = await formField.validate(value) if (validation === true) { - this.formData[formFieldIndex] = { status: 'normal', name: formFieldConfig.label } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'normal', name: formFieldConfig.label }) } else { - this.formData[formFieldIndex] = { status: 'error', message: validation[0].message, name: formFieldConfig.label } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'error', message: validation[0].message, name: formFieldConfig.label }) } await this.setState({ formValue: this.formValue, - formData: cloneDeep(this.formData) + formData: this.formData }) if (this.props.onChange) { this.props.onChange(this.formValue) @@ -395,24 +396,25 @@ export default class FormStep extends Step { if (formFieldConfig) { const fullPath = formFieldConfig.field === '' || path === '' ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` - set(this.formValue, fullPath, value) - this.setState({ + this.formValue = set(this.formValue, fullPath, value) + this.setState(({ formValue }) => ({ formValue: this.formValue - }) + })) + if (this.props.onChange) { this.props.onChange(this.formValue) } if (validation === true) { - this.formData[formFieldIndex] = { status: 'normal', name: formFieldConfig.label } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'normal', name: formFieldConfig.label }) } else { - this.formData[formFieldIndex] = { status: 'error', message: validation[0].message, name: formFieldConfig.label } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'error', message: validation[0].message, name: formFieldConfig.label }) } console.log('form set data', this.formData) await this.setState({ - formData: cloneDeep(this.formData) + formData: this.formData }) } } @@ -422,7 +424,8 @@ export default class FormStep extends Step { if (formFieldConfig) { const fullPath = formFieldConfig.field === '' || path === '' ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` - unset(this.formValue, fullPath) + // unset(this.formValue, fullPath) + this.formValue = set(this.formValue, fullPath) this.setState({ formValue: this.formValue }) @@ -431,13 +434,13 @@ export default class FormStep extends Step { } if (validation === true) { - this.formData[formFieldIndex] = { status: 'normal', name: formFieldConfig.label } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'normal', name: formFieldConfig.label }) } else { - this.formData[formFieldIndex] = { status: 'error', message: validation[0].message, name: formFieldConfig.label } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'error', message: validation[0].message, name: formFieldConfig.label }) } await this.setState({ - formData: cloneDeep(this.formData) + formData: this.formData }) } } @@ -447,10 +450,7 @@ export default class FormStep extends Step { if (formFieldConfig) { const fullPath = formFieldConfig.field === '' || path === '' ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` - let list = get(this.formValue, fullPath, []) - if (!Array.isArray(list)) list = [] - list.push(value) - set(this.formValue, fullPath, list) + this.formValue = push(this.formValue, fullPath, value) // 向this.formValue的fullPath下的值添加value this.setState({ formValue: this.formValue }) @@ -459,13 +459,13 @@ export default class FormStep extends Step { } if (validation === true) { - this.formData[formFieldIndex] = { status: 'normal', name: formFieldConfig.label } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'normal', name: formFieldConfig.label }) } else { - this.formData[formFieldIndex] = { status: 'error', message: validation[0].message, name: formFieldConfig.label } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'error', message: validation[0].message, name: formFieldConfig.label }) } await this.setState({ - formData: cloneDeep(this.formData) + formData: this.formData }) } } @@ -475,9 +475,7 @@ export default class FormStep extends Step { if (formFieldConfig) { const fullPath = formFieldConfig.field === '' || path === '' ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` - const list = get(this.formValue, fullPath, []) - list.splice(index, count) - set(this.formValue, fullPath, list) + this.formValue = splice(this.formValue, fullPath, index, count) this.setState({ formValue: this.formValue }) @@ -486,13 +484,13 @@ export default class FormStep extends Step { } if (validation === true) { - this.formData[formFieldIndex] = { status: 'normal', name: formFieldConfig.label } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'normal', name: formFieldConfig.label }) } else { - this.formData[formFieldIndex] = { status: 'error', message: validation[0].message, name: formFieldConfig.label } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'error', message: validation[0].message, name: formFieldConfig.label }) } await this.setState({ - formData: cloneDeep(this.formData) + formData: this.formData }) } } @@ -502,8 +500,7 @@ export default class FormStep extends Step { if (formFieldConfig) { const fullPath = formFieldConfig.field === '' || path === '' ? `${formFieldConfig.field}${path}` : `${formFieldConfig.field}.${path}` - const list = listItemMove(get(this.formValue, fullPath, []), index, sortType) - set(this.formValue, fullPath, list) + this.formValue = sort(this.formValue, fullPath, index, sortType) this.setState({ formValue: this.formValue }) @@ -512,13 +509,13 @@ export default class FormStep extends Step { } if (validation === true) { - this.formData[formFieldIndex] = { status: 'normal', name: formFieldConfig.label } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'normal', name: formFieldConfig.label }) } else { - this.formData[formFieldIndex] = { status: 'error', message: validation[0].message, name: formFieldConfig.label } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'error', message: validation[0].message, name: formFieldConfig.label }) } await this.setState({ - formData: cloneDeep(this.formData) + formData: this.formData }) } } @@ -582,7 +579,6 @@ export default class FormStep extends Step { render () { const { data, - step, config: { columns, // layout = 'horizontal', @@ -604,7 +600,7 @@ export default class FormStep extends Step { if (Object.prototype.toString.call(actions) === '[object Array]') { actions_ = [] for (let index = 0, len = actions.length; index < len; index++) { - if (!ConditionHelper(actions[index].condition, { record: formValue, data, step })) { + if (!ConditionHelper(actions[index].condition, { record: formValue, data, step: formValue })) { continue } if (actions[index].type === 'submit') { @@ -645,7 +641,7 @@ export default class FormStep extends Step { } } } - + if (ready) { return ( @@ -659,8 +655,8 @@ export default class FormStep extends Step { submitText: this.props.config?.submitText?.replace(/(^\s*)|(\s*$)/g, ''), // TODO 待删除 cancelText: this.props.config?.cancelText?.replace(/(^\s*)|(\s*$)/g, ''), // TODO 待删除 children: fields.map((formFieldConfig, formFieldIndex) => { - if (!ConditionHelper(formFieldConfig.condition, { record: formValue, data, step })) { - this.formFieldsMounted[formFieldIndex] = false + if (!ConditionHelper(formFieldConfig.condition, { record: formValue, data, step: formValue })) { + this.formFieldsMounted = set(this.formFieldsMounted, `[${formFieldIndex}]`, false) return null } let hidden: boolean = true @@ -678,7 +674,7 @@ export default class FormStep extends Step { // 隐藏项同时打标录入数据并清空填写项 if (!hidden) { - this.formData[formFieldIndex] = { status: 'normal', name: formFieldConfig.label } + this.formData = set(this.formData, `[${formFieldIndex}]`, { status: 'normal', name: formFieldConfig.label }) } const FormField = this.getALLComponents(formFieldConfig.type) || Field @@ -703,7 +699,7 @@ export default class FormStep extends Step { : undefined, status, message: formData[formFieldIndex]?.message || '', - extra: StatementHelper(formFieldConfig.extra, { data: this.props.data, step: this.props.step }), + extra: StatementHelper(formFieldConfig.extra, { data: this.props.data, step: formValue }), required: getBoolean(formFieldConfig.required), layout, visitable: display, @@ -713,7 +709,7 @@ export default class FormStep extends Step { key={formFieldIndex} ref={(formField: Field | null) => { if (formField !== null) { - this.formFields[formFieldIndex] = formField + this.formFields = set(this.formFields, `[${formFieldIndex}]`, formField) this.handleFormFieldMount(formFieldIndex) } }} @@ -721,8 +717,9 @@ export default class FormStep extends Step { value={formFieldConfig.field !== undefined ? getValue(formValue, formFieldConfig.field) : undefined} record={formValue} form={this} - data={cloneDeep(data)} - step={step} + data={data} + // step={step} + step={formValue} config={formFieldConfig} onChange={async (value: any) => { await this.handleChange(formFieldIndex, value) }} onValueSet={async (path, value, validation) => await this.handleValueSet(formFieldIndex, path, value, validation)} @@ -732,6 +729,7 @@ export default class FormStep extends Step { onValueListSort={async (path, index, sortType, validation) => await this.handleValueListSort(formFieldIndex, path, index, sortType, validation)} baseRoute={this.props.baseRoute} loadDomain={async (domain: string) => await this.props.loadDomain(domain)} + containerPath={''} /> ) } diff --git a/src/steps/header/index.tsx b/src/steps/header/index.tsx index a7ec2d747c76be2b6eee07b26283931e10e340cf..2b8b574bb4f9fabc3b0f4b6ee5e3f79d854d1154 100644 --- a/src/steps/header/index.tsx +++ b/src/steps/header/index.tsx @@ -75,7 +75,6 @@ export interface statisticContentConfig extends basicContentConfig { statistics?: (valueStatisticConfig | enumerationStatisticConfig)[] } - interface basicStatisticConfig { label?: string value?: ParamConfig @@ -337,20 +336,19 @@ export default class HeaderStep extends Step { switch (mainContent.type) { case 'plain': props.mainContent = this.handlePlainContent(mainContent, 'main') - break; + break case 'markdown': props.mainContent = this.handleMarkdownContent(mainContent, 'main') - break; + break case 'html': props.mainContent = this.handleHTMLContent(mainContent, 'main') - break; + break case 'detail': props.mainContent = this.handleDetailContent(mainContent, 'main') - break; + break case 'statistic': props.mainContent = this.handleStatisticContent(mainContent, 'main') - default: - break; + break } } @@ -359,12 +357,12 @@ export default class HeaderStep extends Step { switch (extraContent.type) { case 'statistic': props.extraContent = this.handleStatisticContent(extraContent, 'extra') - break; + break case 'image': props.extraContent = this.handleImageContent(extraContent, 'extra') - break; + break default: - break; + break } } diff --git a/src/steps/skip/index.tsx b/src/steps/skip/index.tsx index 955b5490c57882bd45a760a31783d34d193f4687..1579c48e33ff1b29ae53faff24d0796bad9805b2 100644 --- a/src/steps/skip/index.tsx +++ b/src/steps/skip/index.tsx @@ -52,11 +52,11 @@ export default class SkipStep extends Step { } break case 'data': - if (data && data[step]) { + if (data && step) { if (defaultField) { - formDefault = getValue(data[step], defaultField) + formDefault = getValue(step, defaultField) } else { - formDefault = data[step] + formDefault = step } } break diff --git a/src/steps/table/index.tsx b/src/steps/table/index.tsx index 72111dcd7afab0aa26d9ed5b7bffabf80a36ac0c..9c05ee7d1840c48186425077f99fa34cdca229fd 100644 --- a/src/steps/table/index.tsx +++ b/src/steps/table/index.tsx @@ -477,7 +477,7 @@ export default class TableStep extends Step { pageAuth } = this.state - let getDate = field ? getValue(data[step], field) : data[step] + let getDate = field ? getValue(step, field) : step if (Object.prototype.toString.call(getDate) !== '[object Array]') { getDate = [] } @@ -565,9 +565,9 @@ export default class TableStep extends Step { } if (pagination && pagination.mode === 'server') { - const paginationCurrent = Number((pagination.current === undefined || pagination.current === '') ? data[step] : get(data[step], pagination.current, 1)) - const paginationPageSize = Number((pagination.pageSize === undefined || pagination.pageSize === '') ? data[step] : get(data[step], pagination.pageSize, 10)) - const paginationTotal = Number((pagination.total === undefined || pagination.total === '') ? data[step] : get(data[step], pagination.total, 0)) + const paginationCurrent = Number((pagination.current === undefined || pagination.current === '') ? step : get(step, pagination.current, 1)) + const paginationPageSize = Number((pagination.pageSize === undefined || pagination.pageSize === '') ? step : get(step, pagination.pageSize, 10)) + const paginationTotal = Number((pagination.total === undefined || pagination.total === '') ? step : get(step, pagination.total, 0)) props.pagination = { current: Number.isNaN(paginationCurrent) ? 1 : paginationCurrent, diff --git a/src/util/condition.ts b/src/util/condition.ts index 7d7542bb753a6db9bfba42cc9ee22feaea8e4ee0..6a2331b80c2a0212a533a107dd8ac107a6457ab0 100644 --- a/src/util/condition.ts +++ b/src/util/condition.ts @@ -1,6 +1,8 @@ -import { set, cloneDeep, template } from "lodash" -import { ParamConfig } from "../interface"; -import ParamHelper from "./param"; +import { template } from 'lodash' +import { set } from '../util/produce' +import { ParamConfig } from '../interface' +import ParamHelper from './param' +import { Field } from '../components/formFields/common' export interface ConditionConfig { /** @@ -22,7 +24,7 @@ export interface ConditionConfig { debug?: boolean } -export default function ConditionHelper(condition: ConditionConfig | undefined, datas: { record?: object, data: object[], step: number }): boolean { +export default function ConditionHelper (condition: ConditionConfig | undefined, datas: { record?: object, data: object[], step: { [field: string]: any; }, extraContainerPath?: string }, _this?: Field): boolean { if (condition === undefined || ((condition.statement === undefined || condition.statement === '') && (condition.template === undefined || condition.template === ''))) { return true } else { @@ -32,11 +34,11 @@ export default function ConditionHelper(condition: ConditionConfig | undefined, if (condition.params) { condition.params.forEach((param) => { if (param.field !== undefined && param.data !== undefined) { - const value = ParamHelper(param.data, cloneDeep(datas)) + const value = ParamHelper(param.data, datas, _this) if (param.field === '') { statementParams = value === undefined ? 'undefined' : JSON.stringify(value) } else { - set(statementParams, param.field, value === undefined ? 'undefined' : JSON.stringify(value)) + statementParams = set(statementParams, param.field, value === undefined ? 'undefined' : JSON.stringify(value)) } } }) @@ -85,14 +87,13 @@ export default function ConditionHelper(condition: ConditionConfig | undefined, condition.params.forEach((param) => { if (param.field !== undefined && param.data !== undefined) { if (param.field === '') { - statementParams = ParamHelper(param.data, cloneDeep(datas)) + statementParams = ParamHelper(param.data, datas, _this) } else { - set(statementParams, param.field, ParamHelper(param.data, cloneDeep(datas))) + statementParams = set(statementParams, param.field, ParamHelper(param.data, datas, _this)) } } }) } - try { const statement = statementTemplate(statementParams) try { diff --git a/src/util/enumeration.ts b/src/util/enumeration.ts index 0b3c41d09c39b7e18f7678360c86e0a0251de4a7..67b0c3d213d9245d75d5b377fac494faa57b2fb3 100644 --- a/src/util/enumeration.ts +++ b/src/util/enumeration.ts @@ -1,5 +1,5 @@ -import { InterfaceConfig } from "./interface"; -import { getValue } from "./value"; +import { InterfaceConfig } from './interface' +import { getValue } from './value' export type EnumerationOptionsConfig = ManualEnumerationOptionsConfig | InterfaceEnumerationOptionsConfig diff --git a/src/util/interface.ts b/src/util/interface.ts index 5e4783bec28d6ae8dca0560c7a3a7038a9e45f41..3b0c9fdf6f3ac8d0eaba3c276ac68006c451266d 100644 --- a/src/util/interface.ts +++ b/src/util/interface.ts @@ -1,8 +1,11 @@ -import { isEqual, cloneDeep, template, get, set, merge } from "lodash" +// import { isEqual, cloneDeep, template, get, set, merge } from "lodash" +import { isEqual, template, get, merge } from "lodash" +import { set } from '../util/produce' import axios, { AxiosRequestConfig } from 'axios' -import { ParamConfig } from "../interface"; -import ParamHelper from "./param"; -import { getValue } from "./value"; +import { ParamConfig } from '../interface' +import ParamHelper from './param' +import { getValue } from './value' +import { Field } from '../components/formFields/common' export interface InterfaceConfig { domain?: string @@ -20,12 +23,12 @@ export interface InterfaceConfig { enable?: boolean, field?: string, value?: any, - success?: { type: 'none' } | - { type: 'modal', content?: { type: 'static', content?: string } | - { type: 'field', field?: string }}, - fail?: { type: 'none' } | + success?: { type: 'none' } | + { type: 'modal', content?: { type: 'static', content?: string } | + { type: 'field', field?: string }}, + fail?: { type: 'none' } | { type: 'modal', content?: {type: 'static', content?: string } | - {type: 'field', field?: string }} + {type: 'field', field?: string }} } response?: { @@ -96,11 +99,12 @@ export default class InterfaceHelper { public request ( config: InterfaceConfig, source: any, - datas: { record?: object, data: object[], step: number }, + datas: { record?: object, data: object[], step: { [field: string]: any; }, extraContainerPath?: string }, option?: { loadDomain?: (domain: string) => Promise extra_data?: { params?: any, data?: any } - } + }, + _this?: Field ): Promise { return new Promise(async (resolve, reject) => { // 处理URL @@ -110,9 +114,9 @@ export default class InterfaceHelper { config.urlParams.forEach((param) => { if (param.field !== undefined && param.data !== undefined) { if (param.field === '') { - urlParams = ParamHelper(param.data, datas) + urlParams = ParamHelper(param.data, datas, _this) } else { - set(urlParams, param.field, ParamHelper(param.data, datas)) + urlParams = set(urlParams, param.field, ParamHelper(param.data, datas, _this)) } } }) @@ -143,9 +147,9 @@ export default class InterfaceHelper { config.params.forEach((param) => { if (param.field !== undefined && param.data !== undefined) { if (param.field === '') { - params = ParamHelper(param.data, datas) + params = ParamHelper(param.data, datas, _this) } else { - set(params, param.field, ParamHelper(param.data, datas)) + params = set(params, param.field, ParamHelper(param.data, datas, _this)) } } }) @@ -157,7 +161,7 @@ export default class InterfaceHelper { if (config.data) { config.data.forEach((param) => { if (param.field !== undefined && param.data !== undefined) { - (data as FormData).append(param.field, ParamHelper(param.data, datas)) + (data as FormData).append(param.field, ParamHelper(param.data, datas, _this)) } }) } @@ -171,9 +175,9 @@ export default class InterfaceHelper { config.data.forEach((param) => { if (param.field !== undefined && param.data !== undefined) { if (param.field === '') { - data = ParamHelper(param.data, datas) + data = ParamHelper(param.data, datas, _this) } else { - set(data, param.field, ParamHelper(param.data, datas)) + data = set(data, param.field, ParamHelper(param.data, datas, _this)) } } }) @@ -182,12 +186,12 @@ export default class InterfaceHelper { merge(data, option.extra_data.data) } } - + // 缓存判断 if (config.cache && config.cache.global && Object.keys(InterfaceHelper.cache).includes(config.cache.global)) { resolve(InterfaceHelper.cache[config.cache.global]) } else if ( - (!config.cache || !config.cache.disabled) && + (!config.cache || !config.cache.disabled) && isEqual(this._config, config) && isEqual(this._url, url) && isEqual(this._params, params) && @@ -195,11 +199,11 @@ export default class InterfaceHelper { ) { return this._response } else { - this._config = cloneDeep(config) + this._config = config this._url = url - this._params = cloneDeep(params) - this._data = cloneDeep(data) - + this._params = params + this._data = data + const request: AxiosRequestConfig = { url, method: config.method || 'GET', @@ -210,10 +214,10 @@ export default class InterfaceHelper { if (config.method === 'POST') { request.data = data } - + try { const response = await axios(request).then((response) => response.data) - + if (config.condition && config.condition.enable) { if (get(response, config.condition.field || '') === config.condition.value) { if (config.condition.success) { @@ -243,7 +247,7 @@ export default class InterfaceHelper { return } } - + if (config.response) { if (Array.isArray(config.response)) { let content = {} @@ -252,7 +256,7 @@ export default class InterfaceHelper { if (field === undefined || field === '') { content = value } else { - set(content, field, value) + content = set(content, field, value) } } this._response = content @@ -284,4 +288,4 @@ export default class InterfaceHelper { } }) } -} \ No newline at end of file +} diff --git a/src/util/operation.tsx b/src/util/operation.tsx index 3f9f3b907252ecd5b5a3e3dc3cf9e8599a8bc7cf..aa3c6c9255a82aaa77ac141cbfdada49db0fa2a7 100644 --- a/src/util/operation.tsx +++ b/src/util/operation.tsx @@ -1,9 +1,10 @@ -import React from 'react'; -import queryString from 'query-string'; -import { set } from "lodash"; -import { ParamConfig } from "../interface"; -import { CCMSConfig, CCMSProps } from "../main"; -import { getParam } from "./value"; +import React from 'react' +import queryString from 'query-string' +// import { set } from "lodash"; +import { set } from '../util/produce' +import { ParamConfig } from '../interface' +import { CCMSConfig, CCMSProps } from '../main' +import { getParam } from './value' export type OperationConfig = CCMSOperationConfig @@ -50,8 +51,8 @@ interface CCMSInvisibleOperationConfig extends _CCMSOperationConfig { type CCMSOperationConfig = CCMSPopupOperationConfig | CCMSRedirectOperationConfig | CCMSWindowOperationConfig | CCMSInvisibleOperationConfig interface OperationHelperProps { - config?: OperationConfig, - datas: { record?: object, data: object[], step: number }, + config?: OperationConfig, + datas: { record?: object, data: object[], step: { [field: string]: any; } }, checkPageAuth: (pageID: any) => Promise, loadPageURL: (pageID: any) => Promise, loadPageFrameURL: (pageID: any) => Promise, @@ -77,7 +78,7 @@ export default class OperationHelper extends React.Component 您当前使用的UI版本没有实现OpertionHelper组件。 @@ -100,16 +101,16 @@ export default class OperationHelper extends React.Component { if (config.type === 'ccms') { - const sourceData = {} + let sourceData = {} if (config.params === undefined) { for (const [field, param] of Object.entries(config.data || {})) { const value = getParam(param, datas) - set(sourceData, field, value) + sourceData = set(sourceData, field, value) } } else { - for (const {field, data} of config.params) { + for (const { field, data } of config.params) { const value = getParam(data, datas) - set(sourceData, field, value) + sourceData = set(sourceData, field, value) } } if (config.mode === 'popup' || config.mode === 'invisible') { @@ -135,7 +136,6 @@ export default class OperationHelper extends React.Component) { // 1.3.0新增 step由索引变为formValue switch (config.source) { case 'record': if (datas.record) { + if (_this) { + const fullPath = datas.extraContainerPath ? getChainPath(_this.props.containerPath, datas.extraContainerPath, config.field) : getChainPath(_this.props.containerPath, config.field) + _this.handleReportFields && _this.handleReportFields(getChainPath(fullPath)) + } if (config.field === '') { return datas.record } else { @@ -14,11 +20,12 @@ export default function ParamHelper ( config: ParamConfig, datas: { record?: obj } break case 'data': - if (datas.data[datas.step]) { + if (datas.step) { + _this && _this.handleReportFields && _this.handleReportFields(`${config.field}`) if (config.field === '') { - return datas.data[datas.step] + return datas.step } else { - return get(datas.data[datas.step], config.field) + return get(datas.step, config.field) } } break @@ -33,6 +40,7 @@ export default function ParamHelper ( config: ParamConfig, datas: { record?: obj break case 'step': if (datas.data[config.step]) { + _this && _this.handleReportFields && _this.handleReportFields(`${config.field}`) if (config.field === '') { return datas.data[config.step] } else { @@ -46,8 +54,10 @@ export default function ParamHelper ( config: ParamConfig, datas: { record?: obj } else { return get(qs.parse(window.location.search, { arrayFormat: 'bracket' }), config.field) } + break case 'static': return config.value + break } return undefined -} \ No newline at end of file +} diff --git a/src/util/produce.tsx b/src/util/produce.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2d97d10bb473e2d9ab5a20ac2e851afcb6e1b190 --- /dev/null +++ b/src/util/produce.tsx @@ -0,0 +1,126 @@ +import produce, { setAutoFreeze } from 'immer' +import lodash from 'lodash' +import { listItemMove } from '../util/value' + + +/** + * setAutoFreeze + * 默认为true, 防止外部修改,维护数据不可变 + * 为false 可以修改数据源 + * 开发环境打开,生产环境关闭 + */ +setAutoFreeze(false) + +/** + * 对应loadsh 的set + * @param current + * @param path + * @param value + * @returns + */ +export function set(current: any, path?: string, value?: any) { + const target = produce(current, (draft: any) => { + if (path) { + if (arguments.length === 2) { // 移除对象路径的属性 参数改动时同步修改这块 + lodash.unset(draft, path) + } else { + return lodash.set(draft, path, value) + } + } + return draft + }) + return target +} +/** + * current指定路径下的数组添加元素 + * @param current + * @param path + * @param value + * @returns + */ +export const push = (current: any, path: string = '', value?: any) => { + const target = produce(current, (draft: any) => { + const list = lodash.get(draft, path) + if (!Array.isArray(list)) { // 如果指定路径下不是数组类型 + var tempArr = [] + tempArr.push(value) + lodash.set(draft, path, tempArr) + } else { + list.push(value) + } + }) + return target +} + +/** + * current指定路径下的数组删除元素 + * @param current + * @param path + * @param index + * @param count + * @returns + */ +export const splice = (current: any, path: string = '', index: number, count: number) => { + const target = produce(current, (draft: any) => { + const list = lodash.get(draft, path, []) + list.splice(index, count) + }) + return target +} + +/** + * current指定路径下数组排序 + * @param current + * @param path + * @param index + * @param sortType + * @returns + */ +export const sort = (current: any, path: string = '', index: number, sortType: 'up' | 'down') => { + const target = produce(current, (draft: any) => { + const list = lodash.get(draft, path, []) + listItemMove(list, index, sortType) + }) + return target +} + +/** + * lodash 递归合并来源对象的自身和继承的可枚举属性到目标对象 + * @param a 目标对象 + * @param b 来源对象 + * @returns + */ +const merge = (a: any, b: any): any => { + return lodash.assignInWith(a, b, (a, b) => { + if (lodash.isUndefined(a) && lodash.isArray(b)) { + a = [] + } + if (lodash.isObject(b)) { + if (lodash.isArray(a)) { + return merge(a, b).filter((i: any) => i !== undefined) + } else { + return merge(a, b) + } + } + }) +} + +export const setValue = (obj: any, path: string = '', value: any) => { + const target = produce(obj, (draft: any) => { + if (path === '') { + if (Object.prototype.toString.call(value) === '[object Object]') { + draft = merge(draft, value) + } else if (value !== undefined) { + draft = value + } + } else { + const source = lodash.get(draft, path) + if (Object.prototype.toString.call(value) === '[object Object]' && Object.prototype.toString.call(source) === '[object Object]') { + lodash.set(draft, path, merge(source, value)) + } else { + lodash.set(draft, path, value) + } + } + }) + return target +} diff --git a/src/util/statement.ts b/src/util/statement.ts index 81ea1e5820eaebf01fc0b0877b0a04948cb4f65e..9329a8af18d329a81ebf2634a67117d44e1878da 100644 --- a/src/util/statement.ts +++ b/src/util/statement.ts @@ -1,13 +1,15 @@ -import { set, cloneDeep, template } from "lodash" -import { ParamConfig } from "../interface"; -import ParamHelper from "./param"; +import { template } from 'lodash' +import { set } from '../util/produce' +import { ParamConfig } from '../interface' +import ParamHelper from './param' +import { Field } from '../components/formFields/common' export interface StatementConfig { statement: string params: { field: string, data: ParamConfig }[] } -export default function StatementHelper(config: StatementConfig | undefined, datas: { record?: object, data: object[], step: number }): string { +export default function StatementHelper (config: StatementConfig | undefined, datas: { record?: object, data: object[], step: { [field: string]: any; }, extraContainerPath?: string }, _this?: Field): string { if (config === undefined || config.statement === undefined || config.statement === '') { return '' } else { @@ -17,14 +19,14 @@ export default function StatementHelper(config: StatementConfig | undefined, dat config.params.forEach((param) => { if (param.field !== undefined && param.data !== undefined) { if (param.field === '') { - statementParams = ParamHelper(param.data, cloneDeep(datas)) + statementParams = ParamHelper(param.data, datas, _this) } else { - set(statementParams, param.field, ParamHelper(param.data, cloneDeep(datas))) + statementParams = set(statementParams, param.field, ParamHelper(param.data, datas, _this)) } } }) } - + try { const statement = statementTemplate(statementParams) return statement @@ -33,4 +35,4 @@ export default function StatementHelper(config: StatementConfig | undefined, dat return '' } } -} \ No newline at end of file +} diff --git a/src/util/value.ts b/src/util/value.ts index 35a8463c108798c0d5b2015bbddbd7714fa1833a..a94db1d780a1966c097de5e3056038c0133b102e 100644 --- a/src/util/value.ts +++ b/src/util/value.ts @@ -51,14 +51,14 @@ export const getParam = ( datas: { record?: object data: object[] - step: number + step: { [field: string]: any; } } ) => { switch (config.source) { case 'record': return getValue(datas.record || {}, config.field) case 'data': - return getValue(datas.data[datas.step], config.field) + return getValue(datas.step, config.field) case 'source': return getValue(datas.data[0] || {}, config.field) case 'step': @@ -79,7 +79,7 @@ export const getParamText = ( datas: { record?: object data: object[] - step: number + step: { [field: string]: any; } } ) => { for (const { field, data } of params) { @@ -114,10 +114,85 @@ export const listItemMove = (list: any[], currentIndex: number, sortType: 'up' | switch (sortType) { case 'up': currentIndex !== 0 && (list[currentIndex] = list.splice(currentIndex - 1, 1, list[currentIndex])[0]) - break; + break case 'down': currentIndex < list.length - 1 && (list[currentIndex] = list.splice(currentIndex + 1, 1, list[currentIndex])[0]) - break; + break } return list } + +// 参数转化为链式路径 +export const getChainPath = (...arg: any[]) => { + const _fullPath = arg.join('.') + const fullPath = _fullPath.replace(/(^\.*)|(\.*$)|(\.){2,}/g, '$3') + return fullPath +} + +/** + * @param source 来源字符串 + * @param find 目标字符串 + * @returns 返回目标字符串出现在来源字符串中所有索引 + */ +function indexes (source: string, find: string) { + const result = [] + for (let i = 0; i < source.length; ++i) { + if (source.substring(i, i + find.length) === find) { + result.push(i) + } + } + return result +} + +/** + * 获取一个数组中最长共同前缀 + *@param arr 入参数组的元素是二级及以上路径,为节省性能,该方法没有适配处理多个一级路径的共同前缀,一级路径没有共同前缀 + * + * eg: 根据项目使用场景,如['81.mode', '81.me.1', '81.my.1', '81.m']的共同前缀为81,不可以是81.m + */ +export const getLongestCommonPrefix = (arr: string[]) => { + if (arr.length === 0 || arr[0].length === 0) { return '' } + for (let i = 0, len1 = arr[0].length; i < len1; i++) { + const c = arr[0].charAt(i) + for (let j = 1, len2 = arr.length; j < len2; j++) { + if (i === arr[j].length || arr[j].charAt(i) !== c || (i === len1 - 1 && arr[j].length > len1 && arr[j].charAt(len1) !== '.')) { + const _indexes = indexes(arr[0], '.') + const res = arr[0].substring(0, i).replace(/\.+$/, '') // 去掉尾部'.' + for (let n = 0; n < _indexes.length; n++) { + if (res.length === _indexes[n]) { + return res + } + } + return res.replace(/\.+[^\\.]+$/, '') + } + } + } + return arr[0] +} + +/** + * @param arr 目标数组 + * @param sourceField 来源字段 + * @returns 与来源字段比较共同前缀后更新的数组 | 是否更新并上报 + * + * eg: ['a.0', 'b']不会插入'a.0.c',会插入'a'替换'a.0',原数组改变为['a', 'b'] + */ +export const updateCommonPrefixItem = (arr: string[], sourceField: string): string[] | boolean => { + const reg = /[^\\.]+(?=\.?)/ + const sourceFieldPrefix = sourceField.match(reg)?.[0] + const commonPrefixItemS = [sourceField] + for (let i = arr.length - 1; i >= 0; i--) { + const arrItem = arr[i] + if (sourceField === arrItem) { + return false + } + const arrItemPrefix = arrItem.match(reg)?.[0] + if (arrItemPrefix && arrItemPrefix === sourceFieldPrefix) { + arr.splice(i, 1) + commonPrefixItemS.push(arrItem) + } + } + + arr.push(getLongestCommonPrefix(commonPrefixItemS)) + return arr +}