UNPKG

@cainiaofe/cn-ui-lowcode

Version:
817 lines (778 loc) 24.8 kB
import React, { useCallback, useMemo, useEffect, useRef, useState, forwardRef, useImperativeHandle, } from 'react'; import { CnFilterPro as UICnFilterPro, formilyCore, formilyReact, } from '@cainiaofe/cn-ui'; import { getFormDefaultValue, isDesignMode, transProxyToObject, executeEventWithoutJS, executeObjectExpr, isArrayNotEmpty, makeFormItemSchema, isEmptyButNotZero, calculateWaitComponentList, setDataToDs, handleFilterDefaultValue, executeFunction, isRecursionComponent, makeButtons, } from '@/common/util/util'; import isEqualWith from 'lodash/isEqualWith'; import { getButtonAction } from '@/common/manager/button'; import { ButtonPosition } from '@/common/manager/position/button-position'; import isPlainObject from 'lodash/isPlainObject'; import cloneDeep from 'lodash/cloneDeep'; import cloneDeepWith from 'lodash/cloneDeepWith'; import { toJS } from '@formily/reactive'; import { dataOriginRequest, dataOriginStatic } from '@/common/util/const'; import { CnFilterDefaultValueFinished, CnFilterOnChange, CnFilterOnSearch, CnPageRequestFinish, emitEvent, onEvent, } from '@/common/util/event-name'; import { __dataSource__, __filterValue__, __formValue__, } from '@/common/util/expr-const'; import { getFormExtraComponents } from '@/common/manager/filter-item'; import isEmpty from 'lodash/isEmpty'; import { getBizExtendProps } from '@/common/manager/plugin'; import merge from 'lodash/merge'; import debounce from 'lodash/debounce'; const { createForm, onFormValuesChange, onFieldReact, onFieldInputValueChange, onFieldChange, onFormInputChange, onFieldInit } = formilyCore || {}; const realCreateForm = UICnFilterPro?.createForm || createForm; const Compose = 'Compose'; const CnFilterPro = forwardRef((props, ref) => { const { config = [], _dataSource, _dataSourceName, _context, forwardedRef, _bindTable, defaultParams, filterStyle, title, events: filterEvents, handleProps, className, mobileProps, extendButtons, isHighPerformance, ...otherProps } = props; const isDesign = isDesignMode(props); const formInstance = useRef(null); const urlParams = _context?.state?.urlParams || {}; const oldSchema = useRef(null); let extraProps = {}; const usedComponentList = []; const filterRef = useRef(null); const { dataOrigin, deferRender } = defaultParams || {}; // 记录默认值请求需要等待哪些组件请求完成 const waitComponentList = useRef([]); const configMap = useRef({}); let defaultDeferRender; if (!isDesign && dataOrigin === dataOriginRequest && !!deferRender) { defaultDeferRender = !!deferRender; } // const loading = useRef(dataOrigin === dataOriginRequest); const debounceCnFilterOnChange = useMemo(() => { return debounce( (value) => { // onSearch(value); // 0719更新逻辑:筛选栏触发onSearch使用CnFilterPro原生提供的方法。 formInstance?.current?.filterSearch?.(); }, 500, { leading: false, trailing: true, }, ); }, []); const debounceCnTableRender = useRef(debounce( (form) => { const temp = cloneDeep(toJS(form.values)); if (_dataSourceName && temp) { _context?.setState({ [_dataSourceName]: temp, }); } emitEvent(CnFilterOnChange, { componentProps: props, payload: form?.values, }); tableForceUpdate(); }, 500, { leading: true, trailing: true, }, )) const debounceTableUpdate = useMemo(() => { return debounce( (value) => { tableForceUpdate(); }, 500, { leading: false, trailing: true, }, ); }, []); const [defer, setDefer] = useState(defaultDeferRender); const [forceUpdate, setForceUpdate] = useState(); // 缓存默认值,用于重置还原 const cacheDefaultValue = useRef(null); const { afterResetNeedQuery, hideButton } = filterStyle || {}; useImperativeHandle(ref, () => ({ reset() { formInstance?.current?.filterReset?.(); }, getFormInstance() { return formInstance.current; }, search() { formInstance?.current?.filterSearch?.(); }, })); const getFormValue = useCallback(() => { if (typeof _dataSource === 'object') { return transProxyToObject(_dataSource); } return null; }, []); const componentRequestFinish = useCallback((tempConfig) => { const { name, dataSource } = tempConfig || {}; const defaultValues = cacheDefaultValue.current; const oldLength = waitComponentList.current?.length; if ( isPlainObject(defaultValues) && name && waitComponentList.current?.includes(name) ) { waitComponentList.current = waitComponentList.current.filter( (item) => item !== name, ); const current = defaultValues[name]; if ( typeof current?.value === 'number' && current?.valueType === 'DefaultSelect' ) { const currentConfig = configMap?.current?.[name]; const temp = dataSource?.[current.value]; let realValue = temp?.value; if (currentConfig?.options?.mode === 'multiple') { if (isEmptyButNotZero(realValue)) { realValue = []; } else { realValue = [realValue]; } } defaultValues[name] = realValue; } const newLength = waitComponentList.current.length; if (newLength === 0 && oldLength > 0) { formInstance?.current?.setInitialValues(defaultValues); const table = _context?.$(_bindTable); if (table?.props?.manual === true) { } else { setTimeout(() => { tableLoad(); }); } emitEvent(CnFilterDefaultValueFinished, { componentProps: props, formValues: defaultValues, }); executeEventWithoutJS({ eventType: 'onDefaultValueFinished', events: filterEvents, _context, position: ButtonPosition.filterEvent, urlParamsDataSource: urlParams, recordDataSource: {}, }); } } }, []); // 设计态默认值的兼容逻辑 if (isDesign === true) { if (dataOrigin === dataOriginStatic) { const tempDefaultValue = getFormDefaultValue(defaultParams, { urlParamsDataSource: urlParams, state: _context?.state, isDesign, formConfig: config, }); if (isPlainObject(tempDefaultValue)) { formInstance?.current?.setValues?.(tempDefaultValue, 'overwrite'); } } } const getRemoteDefaultValue = () => { const { dataOrigin, requestConfig = {}, afterRequest = {}, } = defaultParams || {}; if (dataOrigin === 'request') { const p = getFormDefaultValue(defaultParams, { urlParamsDataSource: urlParams, state: _context?.state, isDesign, formConfig: config, }); let newFormValue; if (p?.then) { p.then( (result) => { // loading.current = false; if (isPlainObject(result)) { newFormValue = { ...(result || {}) }; formInstance?.current?.setInitialValues?.( cloneDeep(newFormValue), 'overwrite', ); } setDefer(false); emitEvent(CnFilterDefaultValueFinished, { componentProps: props, formValues: newFormValue, }); }, () => { // loading.current = false; setDefer(false); emitEvent(CnFilterDefaultValueFinished, { componentProps: props, }); }, ).then((res) => { executeEventWithoutJS({ eventType: 'onDefaultValueFinished', events: filterEvents, _context, position: ButtonPosition.filterEvent, urlParamsDataSource: urlParams, recordDataSource: {}, }); if (afterRequest?.optType) { const action = getButtonAction({ ...afterRequest, position: ButtonPosition.filterDefaultValueAfterRequestSuccess, }); action?.({ position: ButtonPosition.filterDefaultValueAfterRequestSuccess, urlParamsDataSource: urlParams, recordDataSource: newFormValue, state: _context?.state, buttonConfig: afterRequest, _context, }); } }); } else { // loading.current = false; } } }; const getStaticDefaultValue = (originalFormValue, extraConfig) => { let result = originalFormValue; if (dataOrigin === dataOriginStatic) { const p = getFormDefaultValue(defaultParams, { urlParamsDataSource: urlParams, state: _context?.state, isDesign, formConfig: config, ...extraConfig, }); if (isPlainObject(p)) { result = { ...(originalFormValue || {}), ...(p || {}) }; } } return result; }; // didMount查询默认值 useEffect(() => { getRemoteDefaultValue(); onEvent(CnPageRequestFinish, () => { setForceUpdate(Date.now()); }); 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) { // debounceCnFilterOnChange?.(form?.values, loading?.current) if(isHighPerformance) { return debounceCnTableRender?.current?.(form); } const temp = cloneDeep(toJS(form.values)); if (_dataSourceName && temp) { _context?.setState({ [_dataSourceName]: temp, }); } emitEvent(CnFilterOnChange, { componentProps: props, payload: form?.values, }); debounceTableUpdate(); } }); if (hideButton === true) { onFormInputChange((form) => { debounceCnFilterOnChange(form?.values); }); } function handleConfig(list, parentName, extraConfig) { if (isArrayNotEmpty(list)) { const { parentComponentName } = extraConfig || {}; // 父组件是Compose组件 const isCompose = parentComponentName === Compose; for (const item of list) { const { events, name, componentName, options, hidden, disabled, notSubmitWhenHidden, } = item || {}; if (name) { let fieldName = name; if (parentName) { if (isCompose) { fieldName = `${parentName}.${name}`; } else { fieldName = `${parentName}.*.${name}`; } } if (isArrayNotEmpty(events)) { for (const event of events) { const { name: eventName, optType, jsFunction, } = event || {}; let hook; if (eventName === 'onFieldValueChange') { hook = onFieldInputValueChange; } else if (eventName === 'onBlur') { hook = onFieldChange; } else if (eventName === 'onFieldInit') { hook = onFieldInit; } 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.filterItemEvent, }); if (typeof action === 'function') { const callback = (field) => { action({ buttonConfig: { ...event, position: ButtonPosition.filterItemEvent, options: { ...event, }, }, position: ButtonPosition.filterItemEvent, componentProps: props, state: _context?.state, urlParamsDataSource: urlParams, recordDataSource: { realize: () => { return formInstance?.current?.values; }, }, formInstance: { realize: () => { return formInstance?.current; }, }, _context, 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, }; const isHidden = executeObjectExpr( hidden, objExprArgMap, form?.values || {}, _context?.state, ); if (isHidden === true) { if (notSubmitWhenHidden === true) { field.display = 'none'; } else { field.hidden = isHidden; } } else { field.hidden = false; field.display = 'visible'; } }); } if ( disabled !== undefined && disabled !== '' && disabled !== false ) { onFieldReact(fieldName, (field, form) => { const objExprArgMap = { [__dataSource__]: _context?.state, [__formValue__]: form?.values, [__filterValue__]: form?.values, }; field.disabled = executeObjectExpr( disabled, objExprArgMap, form?.values || {}, _context?.state, ); }); } } if (isArrayNotEmpty(options?.config)) { if (componentName === Compose) { handleConfig(options?.config, name, { parentComponentName: componentName, }); } } } } } handleConfig(config); }, }; if (formValue && Object.keys(formValue).length > 0) { if (setInitialValues) { formOptions.initialValues = cloneDeep(formValue); } else { const oldInitialValues = formInstance?.current?.initialValues; if(isPlainObject(oldInitialValues) && !isEmpty(oldInitialValues)) { formOptions.initialValues = cloneDeep(oldInitialValues); } formOptions.values = cloneDeep(formValue); } } return realCreateForm?.(formOptions); }; if (!formInstance.current) { const formDefaultValue = getFormValue(); const tempResult = calculateWaitComponentList(config, defaultParams); if (isArrayNotEmpty(tempResult.waitComponentList)) { waitComponentList.current = tempResult.waitComponentList; } if (!isEmpty(tempResult.configMap)) { configMap.current = tempResult.configMap; } let staticFormDefaultValue = getStaticDefaultValue(formDefaultValue); const hasWaitComponentList = isArrayNotEmpty(waitComponentList?.current); if (hasWaitComponentList) { cacheDefaultValue.current = staticFormDefaultValue; staticFormDefaultValue = getStaticDefaultValue(formDefaultValue, { ignoreDefaultSelectFormRequest: true, }); } else { if (dataOrigin === dataOriginStatic) { emitEvent(CnFilterDefaultValueFinished, { componentProps: props, formValues: staticFormDefaultValue, }); } executeEventWithoutJS({ eventType: 'onDefaultValueFinished', events: filterEvents, _context, position: ButtonPosition.filterEvent, urlParamsDataSource: urlParams, recordDataSource: {}, }); // const tempValue = handleFilterDefaultValue(staticFormDefaultValue, filterCache); // if (_dataSourceName && isPlainObject(tempValue)) { // _context?.setState({ // [_dataSourceName]: tempValue, // }); // } } formInstance.current = generateForm(staticFormDefaultValue, { setInitialValues: true, }); if (!hasWaitComponentList) { const tempFormValue = formInstance.current?.values; if (_dataSourceName && isPlainObject(tempFormValue)) { _context?.setState({ [_dataSourceName]: tempFormValue, }); } } } const onSearch = useCallback((value) => { emitEvent(CnFilterOnSearch, { componentProps: props, payload: value, }); executeEventWithoutJS({ eventType: 'onSearch', events: filterEvents, _context, position: ButtonPosition.filterEvent, urlParamsDataSource: urlParams, recordDataSource: value, }); tableLoad(); }, []); const generateFormItemSchema = (item, index) => { 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; }, isInCnFilterPro: true, // 当组件请求完成时,调用回调告知表单组件我已请求完成,从而实现下拉框默认勾选第一项操作 componentRequestFinish, customDecoratorComponent: 'CnFilterProItem', index, }); if (formItem) { return formItem; } }; const tableForceUpdate = useCallback(() => { if (_bindTable && _context) { const table = _context?.$?.(_bindTable); table?.forceUpdate?.(); } }, []); const tableLoad = useCallback(() => { if (_bindTable && _context) { const table = _context.$(_bindTable); table?.load?.(); } }, []); // 自定义的onReset,用于避免触发表格重新查询 const onReset = useCallback(() => { // tableForceUpdate(); }, []); const generateSchema = () => { const tree = { type: 'object', properties: {}, }; const newConfig = cloneDeepWith(config, (value) => { if (React.isValidElement(value)) { return value; } }); // const originalConfigMap = {} // config.forEach((item,index)=>{ // if(item?.name && item?.componentName) { // originalConfigMap[item.name] = { // ...item, // }; // } // }) newConfig.forEach((item, index) => { const { name, componentName } = item || {}; if (name && componentName) { const current = generateFormItemSchema(item, index); current._name = name; current._componentName = componentName; current._parent = 'root'; if (current) { tree.properties[name] = current; } } }); return tree; }; 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 (needCreateNewForm) { console.log('new filter pro'); formInstance.current = generateForm(toJS(formInstance.current?.values)); } oldSchema.current = { ...schema }; extraProps.showBottomLine = true; if (isPlainObject(filterStyle)) { extraProps = { ...extraProps, ...filterStyle, }; } if (className) { extraProps.className = className; } if (mobileProps) { extraProps.mobileProps = mobileProps; } if (!isDesign && defer) { return null; } if (afterResetNeedQuery === false) { extraProps.onReset = onReset; } else { extraProps.onReset = ()=>{ const formInstance1 = formInstance.current; formInstance1.filterChange(formInstance1.getState().values); setTimeout(()=>{ formInstance1.filterSearch() }, 510); }; } if (isArrayNotEmpty(extendButtons)) { extraProps.extendButtons = makeButtons({ buttons: extendButtons?.map((item) => { let realChildren = item?.children; if (typeof item?.children === 'function') { realChildren = executeFunction(item.children, {}, _context?.state); } return { ...item, position: ButtonPosition.filterExtendButton, children: realChildren, }; }), _context, state: _context?.state, recordDataSource: {}, }); } // form用到的组件列表 const formComponents = getFormExtraComponents(usedComponentList); let realProps = { ...getBizExtendProps({ componentName: 'CnFilterPro', props, }), schema, ...extraProps, }; if (typeof handleProps === 'function') { const tempProps = executeFunction( handleProps, { ...realProps }, _context?.state, ); if (isPlainObject(tempProps)) { realProps = merge(tempProps, { ...realProps }); } } realProps.ref = filterRef; realProps.onSearch = onSearch; realProps.form = formInstance?.current; realProps.components = formComponents; return <UICnFilterPro {...realProps} />; }); CnFilterPro.displayName = 'CnFilterPro'; export default CnFilterPro;