import React, { useCallback, useEffect, useState, useRef, forwardRef, useImperativeHandle, } from 'react'; import { // CnCard, CnForm as UICnForm, // CnFormField, CnFormProps, CnBox, CnButton, formilyCore, CnAgreement, CnFormField, formilyReact, FormConsumer, } from '@cainiaofe/cn-ui-m'; import { toJS } from '@formily/reactive'; import isPlainObject from 'lodash/isPlainObject'; import isEmpty from 'lodash/isEmpty'; import { calculateTextExprValue, calculateWaitComponentList, executeObjectExpr, getFormDefaultValue, getRealizeValue, getRealResponse, getRunTimeItem, isArrayNotEmpty, isDesignMode, isRecursionComponent, makeFormItemSchema, transProxyToObject, } from '@/common/util/util'; import { componentMap, getFormExtraComponents, } from '@/common/manager/filter-item'; import cloneDeep from 'lodash/cloneDeep'; import cloneDeepWith from 'lodash/cloneDeepWith'; import { getButtonAction } from '@/common/manager/button'; import { ButtonPosition } from '@/type/button-position'; import isEqualWith from 'lodash/isEqualWith'; import { __arrayTableCurrentRow__, __dataSource__, __extraParam__, __filterValue__, __formValue__, __stateValueOfSplit__, } from '@/common/util/expr-const'; import get from 'lodash/get'; import { columnSuffix, dataOriginStatic } from '@/common/util/const'; import { makeRequest } from '@/common/manager/request'; import { _getFormValues } from '@/common/util/biz-component-prop-name'; const { Observer } = formilyReact || {}; // import { withI18n } from 'panda-i18n'; // import locale from '@/locale'; const CnAgreementView = (props) => { return ; }; const { createForm, onFormValuesChange, onFieldValueChange, onFieldReact, onFieldInputValueChange, onFieldChange, } = formilyCore || {}; // !!! 注意 ViewProps 会被 ../index 使用 export declare interface FormProps extends CnFormProps { /** * 样式类名 */ className?: string; /** * 表单项配置 */ config?: Array<{ componentName: string; options: Record; [key: string]: any; }>; } const CnCard = 'CnCard'; const CnCardSubCard = 'CnCardSubCard'; const CnFormStepItem = 'CnFormStepItem'; const CnFormGrid = 'CnFormGrid'; function View(props: FormProps, ref): JSX.Element { const { children, config, _context, submitProps, resetProps, hasFooterSubmit, buttons, _dataSourceName, formStyle, defaultParams, dynamicConfig, formInstance: parentFormInstance, _dataSource, onCreateForm, isCnForm, ...otherProps } = props; const usedComponentList: any[] = []; const formInstance = useRef(null); const oldSchema = useRef(null); const isDesign = isDesignMode(props); const formRef = useRef(null); const urlParams = _context?.state?.urlParams || {}; const { readOnly: formReadOnly, labelAlign, wrapperAlign, labelWidth, } = formStyle || {}; const isFormReadOnly = executeObjectExpr( formReadOnly, { [__formValue__]: {}, [__filterValue__]: {}, [__dataSource__]: _context?.state, [__extraParam__]: { openDialogMode: get( _context?.state?.valueOf, `${_dataSourceName}${__stateValueOfSplit__}openDialogMode`, ), }, }, {}, _context?.state, ); const oldFormReadOnly = useRef(isFormReadOnly); const { deferRender, dataOrigin } = defaultParams || {}; const [defer, setDefer] = useState( deferRender === true && dataOrigin === 'request', ); // 记录默认值请求需要等待哪些组件请求完成 const waitComponentList = useRef([]); const waitComponentMap = useRef({}); const configMap = useRef({}); const getFormValue = useCallback(() => { if (typeof _dataSource === 'object') { return transProxyToObject(_dataSource); } return null; }, []); const getUrlParams = () => { return _context?.state?.urlParams || {}; }; // 设计态默认值的兼容逻辑 if (isDesign === true) { if (dataOrigin === dataOriginStatic) { let tempDefaultValue = getFormDefaultValue(defaultParams, { urlParamsDataSource: urlParams, state: _context?.state, isDesign, formConfig: config, }); if (isArrayNotEmpty(config)) { config.forEach((item) => { if (item?.name && item?.componentName) { if (item.componentName === 'CnArraySubAreaCard') { if (!isPlainObject(tempDefaultValue)) { tempDefaultValue = {}; } if (!tempDefaultValue[item.name]) { tempDefaultValue[item.name] = [{}]; } } } }); } if (isPlainObject(tempDefaultValue)) { formInstance?.current?.setValues?.(tempDefaultValue, 'overwrite'); } } } useImperativeHandle(ref, () => ({ load() { getRemoteDefaultValue(); }, getFormInstance() { return formInstance.current; }, getFormRef() { return formRef?.current; }, reRender() { // reRender(); }, })); const getRemoteDefaultValue = () => { const { dataOrigin, requestConfig = {}, afterRequest = {}, } = defaultParams || {}; const formValue = formInstance?.current?.values; if (dataOrigin === 'request') { const p = getFormDefaultValue(defaultParams, { urlParamsDataSource: urlParams, state: _context?.state, isDesign, formConfig: config, getExtraParam: () => { return { openDialogMode: get( _context?.state?.valueOf, `${_dataSourceName}${__stateValueOfSplit__}openDialogMode`, ), }; }, }); let newFormValue; if (isPlainObject(p)) { newFormValue = { ...(formValue || {}), ...(p || {}) }; formInstance?.current?.setInitialValues?.( cloneDeep(newFormValue), 'overwrite', ); } else if (p?.then) { p.then( (result) => { if (isPlainObject(result)) { newFormValue = { ...(result || {}) }; formInstance?.current?.setInitialValues?.( cloneDeep(newFormValue), 'overwrite', ); } setDefer(false); }, () => { setDefer(false); }, ).then((res) => { if (dataOrigin === 'request' && requestConfig?.url) { if (afterRequest?.optType) { const action = getButtonAction({ ...afterRequest, position: ButtonPosition.formDefaultValueAfterRequestSuccess, }); action?.({ position: ButtonPosition.formDefaultValueAfterRequestSuccess, urlParamsDataSource: urlParams, recordDataSource: newFormValue, state: _context?.state, buttonConfig: afterRequest, _context, }); } } }); } } }; useEffect(() => { const { requestConfig } = dynamicConfig || {}; if (isPlainObject(requestConfig) && requestConfig.url) { makeRequest({ buttonConfig: { options: { requestConfig, }, }, state: _context?.state, needSuccessToast: false, isDesign, urlParamsDataSource: urlParams, }).then((res) => { if (res) { const newRes = getRealResponse(res); const { success, data } = newRes || {}; if (success && Array.isArray(data) && data.length > 0) { // setConfig(data); } } getRemoteDefaultValue(); }); } else { getRemoteDefaultValue(); } return () => { if (_dataSourceName) { const temp = _context?.state?.[_dataSourceName]; if (isPlainObject(temp) && !isEmpty(temp)) { _context?.setState({ [_dataSourceName]: {}, }); } } }; }, []); const generateForm = (formValue, otherConfig) => { const { setInitialValues } = otherConfig || {}; const formOptions = { effects() { onFormValuesChange((form) => { if (form.values) { const temp = cloneDeep(toJS(form.values)); if (_dataSourceName && temp) { _context?.setState({ [_dataSourceName]: temp, }); } } }); function handleButtons(buttonList, parentName) { if (Array.isArray(buttonList) && buttonList.length > 0) { for (const item of buttonList) { const { primaryKey, hidden, disabled, optType } = item || {}; if (primaryKey && parentName) { let buttonName = `${parentName}.*.${primaryKey}`; // 树表格兼容逻辑 const secondButtonName = `${parentName}.*.children.*.${primaryKey}`; const isArrayTableAdd = optType === 'arrayTableAdd'; if (isArrayTableAdd) { buttonName = `${parentName}.${primaryKey}`; } if (hidden !== undefined && hidden !== '' && hidden !== false) { onFieldReact(buttonName, (field, form) => { const objExprArgMap = { [__dataSource__]: _context?.state, [__formValue__]: form?.values, }; const { index } = field; let currentRow = {}; if (typeof index === 'number') { const temp = field?.query?.(parentName)?.value?.(); if (temp && temp[index]) { currentRow = temp[index]; } } objExprArgMap[__arrayTableCurrentRow__] = currentRow; field.hidden = executeObjectExpr( hidden, objExprArgMap, currentRow, _context?.state, index, form?.values || {}, ); }); // onFieldReact(secondButtonName,(field, form)=>{ // const objExprArgMap = { // [__dataSource__]: _context?.state, // [__formValue__]: form?.values, // }; // let currentRow = {}; // const index = field.index; // if(field?.path?.segments?.length === 5) { // let path = field.path.segments.slice(0, 4); // const tempRow = field?.form?.getValuesIn?.(path); // if(isPlainObject(tempRow)) { // currentRow = tempRow; // } // } // objExprArgMap[__arrayTableCurrentRow__] = currentRow // field.hidden = executeObjectExpr(hidden, objExprArgMap, currentRow, _context?.state, index, form?.values || {}); // }) } if ( disabled !== undefined && disabled !== '' && disabled !== false ) { onFieldReact(buttonName, (field, form) => { const objExprArgMap = { [__dataSource__]: _context?.state, [__formValue__]: form?.values, }; const { index } = field; let currentRow = {}; if (typeof index === 'number') { const temp = field?.query?.(parentName)?.value?.(); if (temp && temp[index]) { currentRow = temp[index]; } } objExprArgMap[__arrayTableCurrentRow__] = currentRow; if (isArrayTableAdd) { field.disabled = executeObjectExpr( disabled, objExprArgMap, currentRow, _context?.state, index, form?.values || {}, ); } else { field.setComponentProps({ disabled: executeObjectExpr( disabled, objExprArgMap, currentRow, _context?.state, index, form?.values || {}, ), }); } }); } } } } } function handleConfig(list, parentName, extraConfig) { if (isArrayNotEmpty(list)) { const { parentComponentName } = extraConfig || {}; // 父组件是Compose组件 const isCompose = false; for (const item of list) { const { events, name, componentName, options, hidden, disabled, readOnly, notSubmitWhenHidden, } = item || {}; if (name) { let fieldName = name; if (parentName) { if (isCompose) { fieldName = `${parentName}.${name}`; } else { fieldName = `${parentName}.*.${name}`; } } if (Array.isArray(events) && events.length > 0) { for (const event of events) { const { name: eventName, optType, jsFunction, flowList, } = event || {}; let hook; if (eventName === 'onFieldValueChange') { hook = onFieldValueChange; } else if (eventName === 'onFieldInputValueChange') { hook = onFieldInputValueChange; } else if (eventName === 'onBlur') { hook = onFieldChange; } else if (eventName === 'onFieldReact') { hook = onFieldReact; } if (hook) { if ( optType === 'jsAction' && typeof jsFunction === 'function' ) { if (eventName === 'onBlur') { hook(fieldName, ['active'], (field) => { if ( field?.mounted === true && field?.active === false ) { jsFunction.call( null, field, formInstance.current, ); } }); } else { hook(fieldName, (field) => { jsFunction.call(null, field, formInstance.current); }); } } else { const action = getButtonAction({ ...event, position: ButtonPosition.formItemEvent, }); if (typeof action === 'function') { const callback = (field) => { action({ buttonConfig: { ...event, position: ButtonPosition.formItemEvent, options: { ...event, }, }, position: ButtonPosition.formItemEvent, componentProps: props, state: _context?.state, urlParamsDataSource: getUrlParams(), recordDataSource: { realize: () => { return formInstance?.current?.values; }, }, formInstance: { realize: () => { return formInstance?.current; }, }, _context, formRef: { realize: () => { return formRef?.current; }, }, field, }); }; if (eventName === 'onBlur') { hook(fieldName, ['active'], (field) => { if ( field?.visited === true && field?.active === false ) { callback(field); } }); } else { hook(fieldName, callback); } } } } } } if (hidden !== undefined && hidden !== '' && hidden !== false) { onFieldReact(fieldName, (field, form) => { const objExprArgMap = { [__dataSource__]: _context?.state, [__formValue__]: form?.values, [__filterValue__]: form?.values, [__extraParam__]: { openDialogMode: get( _context?.state?.valueOf, `${_dataSourceName}${__stateValueOfSplit__}openDialogMode`, ), }, }; let currentRow = {}; if (parentName && !isCompose) { const { index } = field; if (typeof index === 'number') { const temp = field?.query?.(parentName)?.value?.(); if (temp && temp[index]) { currentRow = temp[index]; } } objExprArgMap[__arrayTableCurrentRow__] = currentRow; } const isHidden = executeObjectExpr( hidden, objExprArgMap, form?.values || {}, _context?.state, currentRow, ); if (isHidden === true) { if (notSubmitWhenHidden === true) { field.display = 'none'; } else { field.hidden = isHidden; } } else { field.hidden = isHidden; field.display = 'visible'; } if (parentName && !isCompose) { const { group } = hidden || {}; if (group !== __arrayTableCurrentRow__) { if (isHidden) { field .query(`..${name}${columnSuffix}`) .take((target) => { target.hidden = true; }); } else { field .query(`..${name}${columnSuffix}`) .take((target) => { target.hidden = false; }); } } } }); } if ( disabled !== undefined && disabled !== '' && disabled !== false ) { onFieldReact(fieldName, (field, form) => { const objExprArgMap = { [__dataSource__]: _context?.state, [__formValue__]: form?.values, [__filterValue__]: form?.values, [__extraParam__]: { openDialogMode: get( _context?.state?.valueOf, `${_dataSourceName}${__stateValueOfSplit__}openDialogMode`, ), }, }; let currentRow = {}; if (parentName && !isCompose) { const { index } = field; if (typeof index === 'number') { const temp = field?.query?.(parentName)?.value?.(); if (temp && temp[index]) { currentRow = temp[index]; } } objExprArgMap[__arrayTableCurrentRow__] = currentRow; } field.disabled = executeObjectExpr( disabled, objExprArgMap, form?.values || {}, _context?.state, currentRow, ); }); } if (readOnly !== undefined && readOnly !== '') { onFieldReact(fieldName, (field, form) => { const objExprArgMap = { [__dataSource__]: _context?.state, [__formValue__]: form?.values, [__filterValue__]: form?.values, [__extraParam__]: { openDialogMode: get( _context?.state?.valueOf, `${_dataSourceName}${__stateValueOfSplit__}openDialogMode`, ), }, }; let currentRow = {}; if (parentName && !isCompose) { const { index } = field; if (typeof index === 'number') { const temp = field?.query?.(parentName)?.value?.(); if (temp && temp[index]) { currentRow = temp[index]; } } objExprArgMap[__arrayTableCurrentRow__] = currentRow; } field.readOnly = executeObjectExpr( readOnly, objExprArgMap, form?.values || {}, _context?.state, currentRow, ); }); } } if (isArrayNotEmpty(options?.config)) { if (isRecursionComponent(componentName)) { handleConfig(options?.config, name); if (componentName !== 'CnArraySubAreaCard') { handleButtons(options?.buttons, name); } } } } } } handleConfig(config); }, }; if (formValue && Object.keys(formValue).length > 0) { formOptions.values = cloneDeep(formValue); if (setInitialValues) { formOptions.initialValues = cloneDeep(formValue); } } if (isFormReadOnly) { formOptions.readOnly = isFormReadOnly; } const result = createForm(formOptions); onCreateForm && onCreateForm(result); return result; }; if (!formInstance.current) { if (parentFormInstance) { formInstance.current = parentFormInstance; } else { let formDefaultValue = getFormValue(); const tempResult = calculateWaitComponentList(config, defaultParams); if (isArrayNotEmpty(tempResult.waitComponentList)) { waitComponentList.current = tempResult.waitComponentList; } if (!isEmpty(tempResult.configMap)) { configMap.current = tempResult.configMap; } if (!isEmpty(tempResult.waitComponentMap)) { waitComponentMap.current = tempResult.waitComponentMap; } if (dataOrigin === dataOriginStatic) { const p = getFormDefaultValue(defaultParams, { urlParamsDataSource: urlParams, state: _context?.state, isDesign, formConfig: config, ignoreDefaultSelectFormRequest: true, }); if (isPlainObject(p)) { formDefaultValue = { ...(formDefaultValue || {}), ...(p || {}) }; if (_dataSourceName) { _context?.setState({ [_dataSourceName]: { ...formDefaultValue }, }); } } } formInstance.current = generateForm(formDefaultValue, { setInitialValues: true, }); } } const generateFormItemSchema = (item) => { const formValue = toJS(formInstance.current?.values); const formItem = makeFormItemSchema({ formValue, formItemConfig: item, isDesign, urlParams, state: _context?.state, usedComponentList, formProps: props, _context, formInstance: formInstance?.current, getFormInstance: () => { return formInstance.current; }, }); if (formItem) { return formItem; } }; const generateSchema = () => { if (isArrayNotEmpty(config)) { return generateNormalFormSchema(config); } }; const generateNormalFormSchema = (tempFormConfig) => { let defaultParent = 'root'; const tree: any = { root: { type: 'object', properties: { [`root_${CnFormGrid}`]: { type: 'void', 'x-component': CnFormGrid, 'x-component-props': {}, properties: {}, }, }, _componentName: 'root', _name: 'root', }, }; if (Array.isArray(tempFormConfig) && tempFormConfig.length > 0) { const newConfig = cloneDeepWith(tempFormConfig, (value) => { if (React.isValidElement(value)) { return value; } }); newConfig.forEach((item = {}) => { const { name, componentName } = item; const componentDefine = getRunTimeItem(componentMap, componentName); const { isFormContainer } = componentDefine || {}; const parent = tree[defaultParent]; if (isFormContainer) { if (componentName === CnCard) { tree[name] = generateFormItemSchema(item); tree[name]._parent = 'root'; } else if (componentName === CnCardSubCard) { if ( parent?._componentName === CnCard || parent?._componentName === 'root' ) { tree[name] = generateFormItemSchema(item); if (tree[name]) { tree[name]._parent = defaultParent; } } else if (parent?._componentName === CnCardSubCard) { const temp = parent?._parent || 'root'; tree[name] = generateFormItemSchema(item); if (tree[name]) { tree[name]._parent = tree[temp]._name || 'root'; } } } defaultParent = name; } else { tree[name] = generateFormItemSchema(item); if (tree[name]) { tree[name]._parent = defaultParent; } } if (tree[name]) { tree[name]._name = name; tree[name]._componentName = componentName; } if (tree[name]?._parent) { const parentSchema = tree[tree[name]._parent]; if (parentSchema?._componentName === CnCardSubCard) { const { _name } = parentSchema; const formGrid = parentSchema.properties[`${_name}_${CnFormGrid}`]; if (formGrid?.properties) { formGrid.properties[name] = tree[name]; } } else if ( parentSchema?._componentName === 'root' && !isFormContainer ) { const formGrid = parentSchema.properties[`root_${CnFormGrid}`]; if (formGrid?.properties) { formGrid.properties[name] = tree[name]; } } else if (parentSchema?._componentName === CnCard) { if (componentName === CnCardSubCard) { parentSchema.properties[name] = tree[name]; } else { const { _name } = parentSchema; const formGrid = parentSchema.properties[`${_name}_${CnFormGrid}`]; if (formGrid?.properties) { formGrid.properties[name] = tree[name]; } } } else if (parentSchema?.properties) { parentSchema.properties[name] = tree[name]; } } }); } return tree.root; }; const getAgreement = (formValue) => { const result = []; if (isArrayNotEmpty(config)) { config.forEach((item) => { if (item.componentName === 'CnAgreement') { const { name, options } = item; if (name) { const { labelText } = options || {}; result.push( , ); } } }); } return result; }; const getButtons = () => { if (isCnForm) { const btnDom = []; if (isArrayNotEmpty(buttons)) { const formValue = formInstance?.current?.values; const state = _context?.state; buttons.forEach((item, index) => { const { iconType, primaryKey, optType, options = {}, children, type, hidden, disabled, ...rest } = item; const isHidden = executeObjectExpr( hidden, { [__filterValue__]: formValue || {}, [__formValue__]: formValue || {}, [__dataSource__]: state, [__extraParam__]: {}, }, formValue || {}, state, ); if (isHidden) { return null; } const action = getButtonAction({ ...item, position: ButtonPosition.form, }); const componentDefine = getRunTimeItem({}, optType); const btnProps = { size: 'large', fullWidth: true, type, }; let needShowLoading; if (optType === 'submit') { needShowLoading = true; } else if (optType === 'flowAction') { const hasSubmitAction = options?.flowList?.find?.( (item) => item?.optType === 'submit', ); if (hasSubmitAction) { needShowLoading = true; } } if (needShowLoading) { btnProps.loading = formInstance?.current?.submitting; btnProps.disabled = formInstance?.current?.submitting; } if (componentDefine?.component) { const component = getRealizeValue(componentDefine.component); if (component) { btnDom.push( React.createElement(component, { children, ...btnProps, buttonConfig: item, state: _context?.state, _context, [_getFormValues]: () => { return formInstance?.current?.values; }, }), ); } } else if (action) { btnProps.onClick = action.bind(this, { componentProps: props, buttonConfig: item, position: ButtonPosition.form, state: _context?.state, urlParamsDataSource: urlParams, recordDataSource: { realize: () => { return formInstance?.current?.values; }, }, formInstance: { realize: () => { return formInstance?.current; }, }, _context, jsParamList: [ { form: formInstance?.current, state: _context?.state, }, ], }); btnDom.push( {children} , ); } }); if (isArrayNotEmpty(btnDom)) { return ( <> {getAgreement(formValue)} {btnDom} ); } } } }; // 设计态默认值的兼容逻辑 if (isDesign === true) { if (dataOrigin === dataOriginStatic) { let tempDefaultValue = getFormDefaultValue(defaultParams, { urlParamsDataSource: urlParams, state: _context?.state, isDesign, formConfig: config, ignoreDefaultSelectFormRequest: true, }); if (isArrayNotEmpty(config)) { config.forEach((item) => { if (item?.name && item?.componentName) { if (item.componentName === 'CnFormArrayCard') { if (!isPlainObject(tempDefaultValue)) { tempDefaultValue = {}; } if (!tempDefaultValue[item.name]) { tempDefaultValue[item.name] = [{}]; } } } }); } if (isPlainObject(tempDefaultValue)) { formInstance?.current?.setValues?.(tempDefaultValue, 'overwrite'); } } } let needCreateNewForm = false; const schema = generateSchema(); if (schema && oldSchema.current) { if ( !isEqualWith(schema, oldSchema.current, (objValue, othValue) => { if (objValue instanceof Function && othValue instanceof Function) { return true; } }) ) { needCreateNewForm = true; } } if (oldFormReadOnly?.current !== isFormReadOnly) { needCreateNewForm = true; } if (needCreateNewForm) { formInstance.current = generateForm(toJS(formInstance.current?.values)); } oldSchema.current = { ...schema }; oldFormReadOnly.current = isFormReadOnly; const extraConfig = {}; const buttonDom = ( {() => { return getButtons(); }} ); if (buttonDom) { extraConfig.hasFooterSubmit = false; extraConfig.footerConfig = { customRender:
, children: buttonDom, }; } else { extraConfig.hasFooterSubmit = false; } extraConfig.formLayoutProps = {}; if (labelAlign) { extraConfig.formLayoutProps.labelAlign = labelAlign; } if (wrapperAlign) { extraConfig.formLayoutProps.wrapperAlign = wrapperAlign; } if (labelWidth) { extraConfig.formLayoutProps.labelWidth = labelWidth; } if (!isDesign && defer) { return null; } // form用到的组件列表 const formComponents = getFormExtraComponents(usedComponentList); return ( ); } const CnForm = forwardRef(View); (CnForm as any).displayName = 'CnForm'; export { CnForm };