@cainiaofe/cn-ui-lowcode
Version:
817 lines (778 loc) • 24.8 kB
JavaScript
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;