fix: 将自定义筛选字段移到core/client (#1438)

Co-authored-by: sealday <zhanglin@daoyoucloud.com>
Reviewed-on: daoyoucloud/tachybase#1438
Reviewed-by: sealday <zhanglin@daoyoucloud.com>
Co-authored-by: wjh <wwwjh0710@163.com>
Co-committed-by: wjh <wwwjh0710@163.com>
This commit is contained in:
wjh 2024-08-08 14:25:54 +08:00 committed by sealday
parent 1742fa19fb
commit bffb42601a
36 changed files with 1008 additions and 1320 deletions

View File

@ -22,7 +22,7 @@ import { useCollection_deprecated, useCollectionManager_deprecated } from '../..
import { useFilterBlock } from '../../filter-provider/FilterProvider'; import { useFilterBlock } from '../../filter-provider/FilterProvider';
import { mergeFilter, transformToFilter } from '../../filter-provider/utils'; import { mergeFilter, transformToFilter } from '../../filter-provider/utils';
import { useRecord } from '../../record-provider'; import { useRecord } from '../../record-provider';
import { removeNullCondition, useActionContext, useCompile } from '../../schema-component'; import { getCustomCondition, removeNullCondition, useActionContext, useCompile } from '../../schema-component';
import { isSubMode } from '../../schema-component/antd/association-field/util'; import { isSubMode } from '../../schema-component/antd/association-field/util';
import { useCurrentUserContext } from '../../user'; import { useCurrentUserContext } from '../../user';
import { useLocalVariables, useVariables } from '../../variables'; import { useLocalVariables, useVariables } from '../../variables';
@ -393,7 +393,6 @@ export const useFilterBlockActionProps = () => {
const { getCollectionJoinField } = useCollectionManager_deprecated(); const { getCollectionJoinField } = useCollectionManager_deprecated();
actionField.data = actionField.data || {}; actionField.data = actionField.data || {};
return { return {
async onClick() { async onClick() {
const { targets = [], uid } = findFilterTargets(fieldSchema); const { targets = [], uid } = findFilterTargets(fieldSchema);
@ -407,16 +406,44 @@ export const useFilterBlockActionProps = () => {
if (!target) return; if (!target) return;
const param = block.service.params?.[0] || {}; const param = block.service.params?.[0] || {};
for (const key in form.values) {
if (
(typeof form.values[key] === 'object' &&
(JSON.stringify(form.values[key]) === '{}' || JSON.stringify(form.values[key]) === '[]')) ||
!form.values[key]
) {
delete form.values[key];
}
}
// 保留原有的 filter // 保留原有的 filter
const storedFilter = block.service.params?.[1]?.filters || {}; const storedFilter = block.service.params?.[1]?.filters || {};
const filter = {
formValues: {},
customValues: {},
customFilter: {},
};
if (Object.keys(form.values)?.includes('custom')) {
const values = { ...form.values };
delete values['custom'];
filter.formValues = { ...values };
for (const key in form.values['custom']) {
if (form.values['custom'][key]) {
filter.customValues[key] = form.values['custom'][key];
}
}
filter.customFilter = getCustomCondition(filter.customValues, fieldSchema);
}
storedFilter[uid] = removeNullCondition( storedFilter[uid] = removeNullCondition(
transformToFilter(form.values, fieldSchema, getCollectionJoinField, name), transformToFilter(filter.formValues, fieldSchema, getCollectionJoinField, name),
); );
const mergedFilter = mergeFilter([ const mergedFilter = mergeFilter([
...Object.values(storedFilter).map((filter) => removeNullCondition(filter)), ...Object.values(storedFilter).map((filter) => removeNullCondition(filter)),
block.defaultFilter, block.defaultFilter,
filter.customFilter,
]); ]);
if (block.dataLoadingMode === 'manual' && _.isEmpty(mergedFilter)) { if (block.dataLoadingMode === 'manual' && _.isEmpty(mergedFilter)) {

View File

@ -51,11 +51,3 @@ export * from 'react-i18next';
export { useHotkeys } from 'react-hotkeys-hook'; export { useHotkeys } from 'react-hotkeys-hook';
export * from './modules/blocks/useParentRecordCommon'; export * from './modules/blocks/useParentRecordCommon';
export * as __UNSAFE__ from './unsafe';
export type {
DynamicComponentProps as __UNSAFE__DynamicComponentProps,
VariablesContextType as __UNSAFE__VariablesContextType,
VariableOption as __UNSAFE__VariableOption,
Option as __UNSAFE__VariableInputOption,
} from './unsafe';

View File

@ -939,5 +939,15 @@
"Show Data":"显示数据", "Show Data":"显示数据",
"Setting Down Title":"设置文件名称", "Setting Down Title":"设置文件名称",
"The current return type is not a document type":"当前返回类型不是文档类型", "The current return type is not a document type":"当前返回类型不是文档类型",
"Test Data":"测试数据" "Test Data":"测试数据",
"Custom filter field": "自定义筛选字段",
"Custom option label": "自定义选项标签",
"Add custom field":"添加自定义字段",
"Input":"文本",
"AutoComplete":"文本选择",
"Field collection":"选择数据",
"AssociationCascader":"关系型级联选择",
"Edit Collection":"修改数据",
"Field Component":"字段组件",
"Custom filter":"自定义筛选"
} }

View File

@ -0,0 +1,294 @@
import { Field, ISchema, useField, useFieldSchema } from '@tachybase/schema';
import _ from 'lodash';
import { useTranslation } from 'react-i18next';
import { useCollection_deprecated, useCollectionManager_deprecated } from '../../../..//collection-manager';
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
import { useFormBlockContext } from '../../../../block-provider';
import { useCompile, useDesignable } from '../../../../schema-component';
import {
EditFormulaTitleField,
FilterCustomVariableInput,
SchemaSettingCollection,
SchemaSettingComponent,
SchemaSettingsCustomRemove,
SchemaSettingsDataScope,
} from '../../../../schema-settings';
export const FilterItemCustomSettings = new SchemaSettings({
name: 'fieldSettings:FilterFormCustomSettings',
items: [
{
name: 'editFieldTitle',
type: 'modal',
useComponentProps() {
const { t } = useTranslation();
const { dn } = useDesignable();
const fieldSchema = useFieldSchema();
const field = useField<Field>();
const { getCollectionJoinField } = useCollectionManager_deprecated();
const { getField } = useCollection_deprecated();
const collectionField =
getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']);
return {
title: t('Edit field title'),
schema: {
type: 'object',
title: t('Edit field title'),
properties: {
title: {
title: t('Field title'),
default: field?.title,
description: `${t('Original field title: ')}${collectionField?.uiSchema?.title}`,
'x-decorator': 'FormItem',
'x-component': 'Input',
'x-component-props': {},
},
},
} as ISchema,
onSubmit({ title }) {
if (title) {
field.title = title;
fieldSchema.title = title;
dn.emit('patch', {
schema: {
'x-uid': fieldSchema['x-uid'],
title: fieldSchema.title,
},
});
}
dn.refresh();
},
};
},
},
{
name: 'editDescription',
type: 'modal',
useComponentProps() {
const { t } = useTranslation();
const { dn } = useDesignable();
const field = useField<Field>();
const fieldSchema = useFieldSchema();
return {
title: t('Edit description'),
schema: {
type: 'object',
title: t('Edit description'),
properties: {
description: {
// title: t('Description'),
default: field?.description,
'x-decorator': 'FormItem',
'x-component': 'Input.TextArea',
'x-component-props': {},
},
},
},
onSubmit({ description }) {
field.description = description;
fieldSchema.description = description;
dn.emit('patch', {
schema: {
'x-uid': fieldSchema['x-uid'],
description: fieldSchema.description,
},
});
dn.refresh();
},
};
},
},
{
name: 'setTheDataScope',
Component: SchemaSettingsDataScope,
useComponentProps() {
const fieldSchema = useFieldSchema();
const { form } = useFormBlockContext();
const field = useField();
const { dn } = useDesignable();
const fieldName = fieldSchema['name'] as string;
const name = fieldName.includes('custom') ? fieldSchema['collectionName'] : fieldName;
return {
collectionName: name,
defaultFilter: fieldSchema?.['x-component-props']?.params?.filter || {},
form: form,
onSubmit: ({ filter }) => {
_.set(field.componentProps, 'params', {
...field.componentProps?.params,
filter,
});
fieldSchema['x-component-props']['params'] = field.componentProps.params;
dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
'x-component-props': fieldSchema['x-component-props'],
},
});
},
};
},
useVisible() {
const fieldSchema = useFieldSchema();
const component = fieldSchema['x-component'];
return component !== 'Input';
},
},
{
name: 'fieldCollection',
Component: SchemaSettingCollection,
useVisible() {
const fieldSchema = useFieldSchema();
const component = fieldSchema['x-component'];
return component === 'Select' || component === 'AutoComplete';
},
},
{
name: 'fieldComponent',
Component: SchemaSettingComponent,
useVisible() {
const fieldSchema = useFieldSchema();
const component = fieldSchema['x-component'];
return component === 'Select' || component === 'AutoComplete';
},
},
{
name: 'titleField',
type: 'select',
useComponentProps() {
const { getCollectionFields, getCollectionJoinField } = useCollectionManager_deprecated();
const { getField } = useCollection_deprecated();
const field = useField<Field>();
const fieldSchema = useFieldSchema();
const { t } = useTranslation();
const { dn } = useDesignable();
const compile = useCompile();
const collectionManage = useCollectionManager_deprecated();
const isCustomFilterItem = ((fieldSchema?.name as string) ?? '').startsWith('custom.');
const collectionManageField = isCustomFilterItem
? collectionManage.collections.filter((value) => value.name === fieldSchema['x-decorator-props'])[0]
: {};
const collectionField =
getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']);
let targetFields = [];
if (collectionField) {
targetFields = collectionField?.target
? getCollectionFields(collectionField?.target)
: getCollectionFields(collectionField?.targetCollection) ?? [];
} else if (collectionManageField) {
targetFields = collectionManageField['fields'];
}
const options = targetFields
.filter((field) => !field?.target && field.type !== 'boolean')
.map((field) => ({
value: field?.name,
label: compile(field?.uiSchema?.title) || field?.name,
}));
return {
title: t('Title field'),
options,
value: fieldSchema['x-component-props']?.['fieldNames']?.label || 'id',
onChange(label) {
const schema = {
['x-uid']: fieldSchema['x-uid'],
};
const fieldNames = {
...collectionField?.uiSchema?.['x-component-props']?.['fieldNames'],
...field.componentProps.fieldNames,
label,
};
if (isCustomFilterItem) fieldNames['value'] = label;
fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
fieldSchema['x-component-props']['fieldNames'] = fieldNames;
schema['x-component-props'] = fieldSchema['x-component-props'];
field.componentProps = fieldSchema['x-component-props'];
dn.emit('patch', {
schema,
});
dn.refresh();
},
};
},
useVisible() {
const fieldSchema = useFieldSchema();
const component = fieldSchema['x-component'];
return component === 'Select' || component === 'AutoComplete';
},
},
{
name: 'EditFormulaTitleField',
Component: EditFormulaTitleField,
useVisible() {
const fieldSchema = useFieldSchema();
const component = fieldSchema['x-component'];
return component === 'Select' || component === 'AutoComplete';
},
},
{
name: 'EditCustomDefaultValue',
type: 'modal',
useComponentProps() {
const { t } = useTranslation();
const { dn } = useDesignable();
const field = useField<Field>();
const fieldSchema = useFieldSchema();
const title = fieldSchema.title;
return {
title: t('Set default value'),
schema: {
type: 'void',
title: t('Set default value'),
properties: {
default: {
title,
'x-decorator': 'FormItem',
'x-component': FilterCustomVariableInput,
'x-component-props': {
fieldSchema,
},
},
},
} as ISchema,
onSubmit({ default: { value } }) {
field.setValue(value);
fieldSchema['default'] = value;
fieldSchema['x-component-props']['defaultValue'] = value;
dn.emit('patch', {
schema: {
'x-uid': fieldSchema['x-uid'],
['x-component-props']: fieldSchema['x-component-props'],
default: fieldSchema.default,
},
});
dn.refresh();
},
};
},
},
{
name: 'divider',
type: 'divider',
},
{
name: 'delete',
// Component: SchemaSettingsCustomRemove,
// useComponentProps() {
// const { t } = useTranslation();
// return {
// key: 'remove',
// confirm: {
// title: t('Delete field'),
// },
// removeParentsIfNoChildren: true,
// breakRemoveOn: {
// 'x-component': 'Grid',
// },
// };
// },
type: 'remove',
},
],
});

View File

@ -11,3 +11,4 @@ export * from './formActionInitializers';
export * from './formItemInitializers'; export * from './formItemInitializers';
export * from './updateFormActionInitializers'; export * from './updateFormActionInitializers';
export * from './createEditFormBlockUISchema'; export * from './createEditFormBlockUISchema';
export * from './FilterItemCustomSettings';

View File

@ -37,6 +37,12 @@ export const filterFormItemInitializers_deprecated = new CompatibleSchemaInitial
Component: 'MarkdownFormItemInitializer', Component: 'MarkdownFormItemInitializer',
name: 'addText', name: 'addText',
}, },
{
title: '{{t("Custom filter field")}}',
name: 'custom',
type: 'item',
Component: 'FilterFormItemCustom',
},
], ],
}); });
@ -70,6 +76,12 @@ export const filterFormItemInitializers = new CompatibleSchemaInitializer(
Component: 'MarkdownFormItemInitializer', Component: 'MarkdownFormItemInitializer',
name: 'addText', name: 'addText',
}, },
{
title: '{{t("Custom filter field")}}',
name: 'custom',
type: 'item',
Component: 'FilterFormItemCustom',
},
], ],
}, },
filterFormItemInitializers_deprecated, filterFormItemInitializers_deprecated,

View File

@ -20,7 +20,13 @@ import {
useTitleFieldOptions, useTitleFieldOptions,
} from '../../../../schema-component/antd/form-item/FormItem.Settings'; } from '../../../../schema-component/antd/form-item/FormItem.Settings';
import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator'; import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator';
import { getShouldChange, VariableInput } from '../../../../schema-settings'; import {
EditCustomDefaultValue,
EditFormulaTitleField,
getShouldChange,
useFormulaTitleVisible,
VariableInput,
} from '../../../../schema-settings';
import { useIsShowMultipleSwitch } from '../../../../schema-settings/hooks/useIsShowMultipleSwitch'; import { useIsShowMultipleSwitch } from '../../../../schema-settings/hooks/useIsShowMultipleSwitch';
import { SchemaSettingsDataScope } from '../../../../schema-settings/SchemaSettingsDataScope'; import { SchemaSettingsDataScope } from '../../../../schema-settings/SchemaSettingsDataScope';
import { SchemaSettingsSortingRule } from '../../../../schema-settings/SchemaSettingsSortingRule'; import { SchemaSettingsSortingRule } from '../../../../schema-settings/SchemaSettingsSortingRule';
@ -381,5 +387,14 @@ export const selectComponentFieldSettings = new SchemaSettings({
return useIsAssociationField() && readPretty; return useIsAssociationField() && readPretty;
}, },
}, },
{
name: 'formulatitleField',
Component: EditFormulaTitleField,
useVisible: useFormulaTitleVisible,
},
{
name: 'editDefaultValue',
Component: EditCustomDefaultValue,
},
], ],
}); });

View File

@ -20,16 +20,19 @@ import { isInitializersSame, useApp } from '../../../application';
import { usePlugin } from '../../../application/hooks'; import { usePlugin } from '../../../application/hooks';
import { SchemaSettingOptions, SchemaSettings } from '../../../application/schema-settings'; import { SchemaSettingOptions, SchemaSettings } from '../../../application/schema-settings';
import { useSchemaToolbar } from '../../../application/schema-toolbar'; import { useSchemaToolbar } from '../../../application/schema-toolbar';
import { useFormBlockContext } from '../../../block-provider'; import { useFormBlockContext, useFormBlockType } from '../../../block-provider';
import { import {
joinCollectionName, joinCollectionName,
useCollection_deprecated, useCollection_deprecated,
useCollectionFilterOptions,
useCollectionManager_deprecated, useCollectionManager_deprecated,
} from '../../../collection-manager'; } from '../../../collection-manager';
import { DataSourceProvider, useDataSourceKey } from '../../../data-source'; import { DataSourceProvider, useCollection, useDataSourceKey } from '../../../data-source';
import { FlagProvider } from '../../../flag-provider'; import { FlagProvider } from '../../../flag-provider';
import { SaveMode } from '../../../modules/actions/submit/createSubmitActionSettings'; import { SaveMode } from '../../../modules/actions/submit/createSubmitActionSettings';
import { useRecord } from '../../../record-provider';
import { SchemaSettingOpenModeSchemaItems } from '../../../schema-items'; import { SchemaSettingOpenModeSchemaItems } from '../../../schema-items';
import { FormFilterScope } from '../../../schema-settings/filter-form/FormFilterScope';
import { GeneralSchemaDesigner } from '../../../schema-settings/GeneralSchemaDesigner'; import { GeneralSchemaDesigner } from '../../../schema-settings/GeneralSchemaDesigner';
import { DefaultValueProvider } from '../../../schema-settings/hooks/useIsAllowToSetDefaultValue'; import { DefaultValueProvider } from '../../../schema-settings/hooks/useIsAllowToSetDefaultValue';
import { import {
@ -41,6 +44,8 @@ import {
SchemaSettingsRemove, SchemaSettingsRemove,
SchemaSettingsSwitchItem, SchemaSettingsSwitchItem,
} from '../../../schema-settings/SchemaSettings'; } from '../../../schema-settings/SchemaSettings';
import { useSchemaTemplateManager } from '../../../schema-templates';
import { useLocalVariables, useVariables } from '../../../variables';
import { useLinkageAction } from './hooks'; import { useLinkageAction } from './hooks';
import { requestSettingsSchema } from './utils'; import { requestSettingsSchema } from './utils';
@ -598,6 +603,93 @@ export function WorkflowConfig() {
); );
} }
const findGridSchema = (fieldSchema) => {
return fieldSchema.reduceProperties((buf, s) => {
if (s['x-component'] === 'FormV2') {
const f = s.reduceProperties((buf, s) => {
if (s['x-component'] === 'Grid' || s['x-component'] === 'BlockTemplate') {
return s;
}
return buf;
}, null);
if (f) {
return f;
}
}
return buf;
}, null);
};
export const useSetFilterScopeVisible = () => {
const fieldSchema = useFieldSchema();
return (
fieldSchema['x-component-props']?.useProps === '{{ useFilterBlockActionProps }}' ||
fieldSchema['x-use-component-props'] === 'useFilterBlockActionProps'
);
};
export const SetFilterScope = (props) => {
const { t } = useTranslation();
const fieldSchema = useFieldSchema();
const { collectionName } = props;
const gridSchema = findGridSchema(fieldSchema) || fieldSchema;
const { form } = useFormBlockContext();
const type = props?.type || ['Action', 'Action.Link'].includes(fieldSchema['x-component']) ? 'button' : 'field';
const variables = useVariables();
const localVariables = useLocalVariables();
const record = useRecord();
const { type: formBlockType } = useFormBlockType();
const schema = useMemo<ISchema>(
() => ({
type: 'object',
title: t('Custom filter'),
properties: {
fieldReaction: {
'x-component': FormFilterScope,
'x-component-props': {
useProps: () => {
const options = useCollectionFilterOptions(collectionName);
return {
options,
defaultValues: gridSchema?.['x-filter-rules'] || fieldSchema?.['x-filter-rules'],
type,
collectionName,
form,
variables,
localVariables,
record,
formBlockType,
};
},
},
},
},
}),
[],
);
const { getTemplateById } = useSchemaTemplateManager();
const { dn } = useDesignable();
const onSubmit = useCallback(
(v) => {
const rules = v.fieldReaction.condition;
const templateId = gridSchema['x-component'] === 'BlockTemplate' && gridSchema['x-component-props'].templateId;
const uid = (templateId && getTemplateById(templateId).uid) || gridSchema['x-uid'];
const schema = {
['x-uid']: uid,
};
gridSchema['x-filter-rules'] = rules;
schema['x-filter-rules'] = rules;
dn.emit('patch', {
schema,
});
dn.refresh();
},
[dn, getTemplateById, gridSchema],
);
return <SchemaSettingsModalItem title={t('Custom filter')} width={770} schema={schema} onSubmit={onSubmit} />;
};
export const actionSettingsItems: SchemaSettingOptions['items'] = [ export const actionSettingsItems: SchemaSettingOptions['items'] = [
{ {
name: 'Customize', name: 'Customize',
@ -735,6 +827,17 @@ export const actionSettingsItems: SchemaSettingOptions['items'] = [
}; };
}, },
}, },
{
name: 'Customize.setFilterScope',
Component: SetFilterScope,
useVisible: useSetFilterScopeVisible,
useComponentProps() {
const collection = useCollection();
return {
collectionName: collection.name,
};
},
},
{ {
name: 'remove', name: 'remove',
sort: 100, sort: 100,

View File

@ -8,6 +8,7 @@ import { useBlockRequestContext } from '../../../block-provider';
import { useCollection_deprecated, useCollectionManager_deprecated } from '../../../collection-manager'; import { useCollection_deprecated, useCollectionManager_deprecated } from '../../../collection-manager';
import { mergeFilter } from '../../../filter-provider/utils'; import { mergeFilter } from '../../../filter-provider/utils';
import { useDataLoadingMode } from '../../../modules/blocks/data-blocks/details-multi/setDataLoadingModeSettingsItem'; import { useDataLoadingMode } from '../../../modules/blocks/data-blocks/details-multi/setDataLoadingModeSettingsItem';
import { hasDuplicateKeys } from './utils';
export const useGetFilterOptions = () => { export const useGetFilterOptions = () => {
const { getCollectionFields } = useCollectionManager_deprecated(); const { getCollectionFields } = useCollectionManager_deprecated();
@ -149,8 +150,42 @@ const isEmpty = (obj) => {
); );
}; };
export const removeNullCondition = (filter) => { export const getCustomCondition = (filter, fieldSchema, customFlat = flat) => {
const items = flat(filter || {}); const filterSchema = fieldSchema ? fieldSchema['x-filter-rules'] : '';
const filterSchemaItem = customFlat(filterSchema || '') as any;
const items = customFlat(filter || {}) as any;
const values = {};
const isCustomFilter = filterSchema?.$and?.length || filterSchema?.$or?.length;
const isFilterCustom = isCustomFilter ? hasDuplicateKeys(items, filterSchemaItem) : false;
if (!isFilterCustom) {
if (isCustomFilter) {
for (const filterKey in filterSchemaItem) {
const match = filterSchemaItem[filterKey]?.slice(11, -2);
const collection = match?.split('.')[0];
const filterItems = Object.keys(items).filter((item) => item.includes(collection))[0];
if (filterItems) {
filterSchemaItem[filterKey] = items[filterItems];
}
}
for (const item in filterSchemaItem) {
if (filterSchemaItem[item].includes('$nFilter')) {
delete filterSchemaItem[item];
}
}
const flatFieldSchema = customFlat.unflatten(filterSchemaItem);
flatFieldSchema['$and'] = flatFieldSchema?.['$and']?.filter(Boolean);
flatFieldSchema['$or'] = flatFieldSchema?.['$or']?.filter(Boolean);
return flatFieldSchema;
} else {
return customFlat.unflatten({});
}
} else {
return customFlat.unflatten(items);
}
};
export const removeNullCondition = (filter, customFlat = flat) => {
const items = customFlat(filter || {}) as any;
const values = {}; const values = {};
for (const key in items) { for (const key in items) {
const value = items[key]; const value = items[key];
@ -158,7 +193,7 @@ export const removeNullCondition = (filter) => {
values[key] = value; values[key] = value;
} }
} }
return flat.unflatten(values); return customFlat.unflatten(values);
}; };
export const useFilterActionProps = () => { export const useFilterActionProps = () => {
@ -179,7 +214,7 @@ export const useFilterFieldProps = ({ options, service, params }) => {
// filter parameter for the block // filter parameter for the block
const defaultFilter = params.filter; const defaultFilter = params.filter;
// filter parameter for the filter action // filter parameter for the filter action
const filter = removeNullCondition(values?.filter); const filter = removeNullCondition(values?.filter) as any;
if (dataLoadingMode === 'manual' && _.isEmpty(filter)) { if (dataLoadingMode === 'manual' && _.isEmpty(filter)) {
return service.mutate(undefined); return service.mutate(undefined);

View File

@ -0,0 +1,12 @@
// 数组去重
const getArrayOfNoDuplicateValue = (arr: Array<string | number>): Array<string | number> => {
return [...new Set(arr)];
};
// 判断两个对象是否包含有相同的key
export function hasDuplicateKeys(A: Object, B: Object): boolean {
const keysA = getArrayOfNoDuplicateValue(Object.keys(A));
const keysB = getArrayOfNoDuplicateValue(Object.keys(B));
const keysA_B = getArrayOfNoDuplicateValue([...keysA, ...keysB]);
return keysA_B.length < keysA.length + keysB.length;
}

View File

@ -558,23 +558,32 @@ export const EditTitleField = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { dn } = useDesignable(); const { dn } = useDesignable();
const compile = useCompile(); const compile = useCompile();
const collectionManage = useCollectionManager_deprecated();
const isCustomFilterItem = ((fieldSchema?.name as string) ?? '').startsWith('custom.');
const collectionManageField = isCustomFilterItem
? collectionManage.collections.filter((value) => value.name === fieldSchema['x-decorator-props'])[0]
: {};
const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']); const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']);
const targetFields = collectionField?.target let targetFields = [];
if (collectionField) {
targetFields = collectionField?.target
? getCollectionFields(collectionField?.target) ? getCollectionFields(collectionField?.target)
: getCollectionFields(collectionField?.targetCollection) ?? []; : getCollectionFields(collectionField?.targetCollection) ?? [];
} else if (collectionManageField) {
targetFields = collectionManageField['fields'];
}
const options = targetFields const options = targetFields
.filter((field) => !field?.target && field.type !== 'boolean') .filter((field) => !field?.target && field.type !== 'boolean')
.map((field) => ({ .map((field) => ({
value: field?.name, value: field?.name,
label: compile(field?.uiSchema?.title) || field?.name, label: compile(field?.uiSchema?.title) || field?.name,
})); }));
return options.length > 0 && (fieldSchema['x-component'] === 'CollectionField' || isCustomFilterItem) ? (
return options.length > 0 && fieldSchema['x-component'] === 'CollectionField' ? (
<SchemaSettingsSelectItem <SchemaSettingsSelectItem
key="title-field" key="title-field"
title={t('Title field')} title={t('Title field')}
options={options} options={options}
value={field?.componentProps?.fieldNames?.label} value={fieldSchema['x-component-props']?.['fieldNames']?.label || 'id'}
onChange={(label) => { onChange={(label) => {
const schema = { const schema = {
['x-uid']: fieldSchema['x-uid'], ['x-uid']: fieldSchema['x-uid'],
@ -584,10 +593,11 @@ export const EditTitleField = () => {
...field.componentProps.fieldNames, ...field.componentProps.fieldNames,
label, label,
}; };
if (isCustomFilterItem) fieldNames['value'] = label;
fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {}; fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
fieldSchema['x-component-props']['fieldNames'] = fieldNames; fieldSchema['x-component-props']['fieldNames'] = fieldNames;
schema['x-component-props'] = fieldSchema['x-component-props']; schema['x-component-props'] = fieldSchema['x-component-props'];
field.componentProps.fieldNames = fieldSchema['x-component-props'].fieldNames; field.componentProps = fieldSchema['x-component-props'];
dn.emit('patch', { dn.emit('patch', {
schema, schema,
}); });

View File

@ -119,6 +119,7 @@ import {
} from './buttons'; } from './buttons';
import * as initializerComponents from './components'; import * as initializerComponents from './components';
import * as items from './items'; import * as items from './items';
import { FilterFormItemCustom } from './items';
export * from './buttons'; export * from './buttons';
export * from './items'; export * from './items';
@ -176,6 +177,7 @@ export class SchemaInitializerPlugin extends Plugin {
DisassociateActionInitializer, DisassociateActionInitializer,
FilterActionInitializer, FilterActionInitializer,
RefreshActionInitializer, RefreshActionInitializer,
FilterFormItemCustom,
} as any); } as any);
this.app.schemaInitializerManager.add(blockInitializers_deprecated); this.app.schemaInitializerManager.add(blockInitializers_deprecated);

View File

@ -1,32 +1,4 @@
import React, { memo, Profiler, useCallback, useContext, useMemo } from 'react'; import React, { memo, useCallback, useContext, useMemo } from 'react';
import {
ACLCollectionFieldProvider,
BlockItem,
css,
cx,
EditComponent,
EditDescription,
FormDialog,
FormItem,
GeneralSchemaDesigner,
gridRowColWrap,
HTMLEncode,
i18n,
SchemaComponent,
SchemaComponentOptions,
SchemaInitializerItem,
SchemaSettingsDataScope,
SchemaSettingsDivider,
Select,
useAPIClient,
useCollectionManager,
useCollectionManager_deprecated,
useCompile,
useDesignable,
useFormBlockContext,
useGlobalTheme,
useSchemaInitializerItem,
} from '@tachybase/client';
import { ArrayItems, FormLayout } from '@tachybase/components'; import { ArrayItems, FormLayout } from '@tachybase/components';
import { import {
Field, Field,
@ -37,22 +9,48 @@ import {
uid, uid,
useField, useField,
useFieldSchema, useFieldSchema,
useForm,
} from '@tachybase/schema'; } from '@tachybase/schema';
import { ConfigProvider, Radio, Space } from 'antd'; import { ConfigProvider, Space } from 'antd';
import _ from 'lodash'; import _ from 'lodash';
import { useTranslation } from 'react-i18next';
import { tval, useTranslation } from '../../locale'; import { useAPIClient } from '../../api-client';
import { SchemaInitializerItem, useSchemaInitializerItem } from '../../application';
import { SchemaSettings } from '../../application/schema-settings/SchemaSettings';
import { useFormBlockContext } from '../../block-provider';
import { ACLCollectionFieldProvider } from '../../built-in/acl';
import { useCollectionManager_deprecated } from '../../collection-manager';
import { useCollectionManager } from '../../data-source';
import { i18n } from '../../i18n';
import { import {
EditDefaultValue, BlockItem,
EditFormulaTitleField, EditDescription,
EditTitle, EditTitle,
EditTitleField, EditTitleField,
FormDialog,
FormItem,
HTMLEncode,
SchemaComponent,
SchemaComponentOptions,
Select,
useCompile,
useDesignable,
} from '../../schema-component';
import {
EditCustomDefaultValue,
EditFormulaTitleField,
GeneralSchemaDesigner,
SchemaSettingCollection, SchemaSettingCollection,
SchemaSettingComponent, SchemaSettingComponent,
SchemaSettingsCustomRemove,
SchemaSettingsDataScope,
SchemaSettingsDivider,
SchemaSettingsRemove,
} from '../../schema-settings'; } from '../../schema-settings';
import { SchemaSettingsRemove } from '../../schema-settings/SchemaSettingsRemove'; import { css, cx } from '../../style';
import { useGlobalTheme } from '../../style/theme';
import { gridRowColWrap } from '../utils';
const FieldComponentProps: React.FC = observer( const FieldComponentProps: React.FC = observer(
(props) => { (props) => {
@ -228,7 +226,7 @@ export const FilterCustomItemInitializer: React.FC<{
}, },
associationField: { associationField: {
type: 'string', type: 'string',
title: tval('Association field'), title: t('Association field'),
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
'x-component': 'Select', 'x-component': 'Select',
'x-visible': false, 'x-visible': false,
@ -290,7 +288,7 @@ export const FilterCustomItemInitializer: React.FC<{
}); });
const { title, component, collection, associationField } = values; const { title, component, collection, associationField } = values;
const defaultSchema = getInterface(component)?.default?.uiSchema || {}; const defaultSchema = getInterface(component)?.default?.uiSchema || {};
const titleField = cm.getCollection(collection).titleField; const titleField = cm.getCollection(collection)?.titleField;
const name = uid(); const name = uid();
insert( insert(
gridRowColWrap({ gridRowColWrap({
@ -300,7 +298,8 @@ export const FilterCustomItemInitializer: React.FC<{
name: 'custom.' + name, name: 'custom.' + name,
required: false, required: false,
'x-component': component, 'x-component': component,
'x-designer': 'FilterItemCustomDesigner', 'x-toolbar': 'FormItemSchemaToolbar',
'x-settings': 'fieldSettings:FilterFormCustomSettings',
'x-decorator': 'FilterFormItem', 'x-decorator': 'FilterFormItem',
'x-decorator-props': collection, 'x-decorator-props': collection,
'x-component-props': { 'x-component-props': {
@ -364,18 +363,9 @@ export const FilterItemCustomDesigner: React.FC = () => {
{component === 'Select' || component === 'AutoComplete' ? <SchemaSettingComponent /> : null} {component === 'Select' || component === 'AutoComplete' ? <SchemaSettingComponent /> : null}
{component === 'Select' || component === 'AutoComplete' ? <EditTitleField /> : null} {component === 'Select' || component === 'AutoComplete' ? <EditTitleField /> : null}
{component === 'Select' || component === 'AutoComplete' ? <EditFormulaTitleField /> : null} {component === 'Select' || component === 'AutoComplete' ? <EditFormulaTitleField /> : null}
<EditDefaultValue /> <EditCustomDefaultValue />
<SchemaSettingsDivider /> <SchemaSettingsDivider />
<SchemaSettingsRemove <SchemaSettingsRemove />
key="remove"
confirm={{
title: t('Delete field'),
}}
removeParentsIfNoChildren
breakRemoveOn={{
'x-component': 'Grid',
}}
/>
</GeneralSchemaDesigner> </GeneralSchemaDesigner>
); );
}; };

View File

@ -21,3 +21,4 @@ export * from './RecordReadPrettyAssociationFormBlockInitializer';
export * from './SelectActionInitializer'; export * from './SelectActionInitializer';
export * from './SubmitActionInitializer'; export * from './SubmitActionInitializer';
export * from './TableActionColumnInitializer'; export * from './TableActionColumnInitializer';
export * from './CustomFilterFormItemInitializer';

View File

@ -0,0 +1,97 @@
import React, { useEffect, useMemo } from 'react';
import { Field, useField, useFieldSchema } from '@tachybase/schema';
import { useMemoizedFn } from 'ahooks';
import { useTranslation } from 'react-i18next';
import { SchemaComponent, useDesignable, VariableScopeProvider } from '../schema-component';
import { SchemaSettingsModalItem } from './SchemaSettings';
import { getShouldChange, useCurrentUserVariable, useDatetimeVariable, VariableInput } from './VariableInput';
export const EditCustomDefaultValue = () => {
const { t } = useTranslation();
const { dn } = useDesignable();
const field = useField<Field>();
const fieldSchema = useFieldSchema();
const collectionName = fieldSchema['collectionName'];
const title = fieldSchema.title;
return (
<SchemaSettingsModalItem
key="set field default value"
title={t('Set default value')}
schema={{
type: 'void',
title: t('Set default value'),
properties: {
default: {
title,
'x-decorator': 'FormItem',
'x-component': FilterCustomVariableInput,
'x-component-props': {
fieldSchema,
},
},
},
}}
onSubmit={({ default: { value } }) => {
field.setValue(value);
fieldSchema['default'] = value;
fieldSchema['x-component-props']['defaultValue'] = value;
dn.emit('patch', {
schema: {
'x-uid': fieldSchema['x-uid'],
['x-component-props']: fieldSchema['x-component-props'],
default: fieldSchema.default,
},
});
dn.refresh();
}}
/>
);
};
export const FilterCustomVariableInput = (props: any) => {
const { value, onChange, fieldSchema } = props;
const { currentUserSettings } = useCurrentUserVariable({
collectionField: { uiSchema: fieldSchema },
uiSchema: fieldSchema,
});
const { datetimeSettings } = useDatetimeVariable({
operator: fieldSchema['x-component-props']?.['filter-operator'],
schema: fieldSchema,
noDisabled: true,
});
const options = useMemo(
() => [currentUserSettings, datetimeSettings].filter(Boolean),
[datetimeSettings, currentUserSettings],
);
const schema = {
...fieldSchema,
'x-component': fieldSchema['x-component'] || 'Input',
'x-decorator': '',
title: '',
name: 'value',
};
const componentProps = fieldSchema['x-component-props'] || {};
const handleChange = useMemoizedFn(onChange);
useEffect(() => {
if (fieldSchema.default) {
handleChange({ value: fieldSchema.default });
}
}, [fieldSchema.default, handleChange]);
return (
<VariableScopeProvider scope={options}>
<VariableInput
{...componentProps}
renderSchemaComponent={() => <SchemaComponent schema={schema} />}
fieldNames={{}}
value={value?.value}
scope={options}
onChange={(v: any) => {
onChange({ value: v });
}}
shouldChange={getShouldChange({} as any)}
/>
</VariableScopeProvider>
);
};

View File

@ -0,0 +1,124 @@
import React from 'react';
import { Field, ISchema, useField, useFieldSchema } from '@tachybase/schema';
import { useTranslation } from 'react-i18next';
import { useCollection_deprecated, useCollectionManager_deprecated } from '../collection-manager';
import { useCompile, useDesignable } from '../schema-component';
import { SchemaSettingsModalItem } from './SchemaSettings';
export const useFormulaTitleOptions = () => {
const compile = useCompile();
const { getCollectionJoinField, getCollectionFields } = useCollectionManager_deprecated();
const { getField } = useCollection_deprecated();
const fieldSchema = useFieldSchema();
const collectionManage = useCollectionManager_deprecated();
const collectionManageField = collectionManage.collections.filter(
(value) => value.name === fieldSchema['x-decorator-props'],
)[0];
const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']);
let fields = [];
if (collectionField) {
fields = getCollectionFields(
collectionField
? collectionField.target
? collectionField.target
: collectionField.collectionName
: fieldSchema['name'],
);
} else if (collectionManageField) {
fields = collectionManageField['fields'];
}
const options = [];
fields?.forEach((field) => {
if (field.interface !== 'm2m') {
if (field.uiSchema) {
options.push({
label: compile(field.uiSchema.title),
value: field.name,
children:
getCollectionFields(field.target)
?.filter((subField) => subField.uiSchema)
.map((subField) => ({
label: subField.uiSchema ? compile(subField.uiSchema.title) : '',
value: subField.name,
})) ?? [],
});
}
}
});
return options;
};
export const EditFormulaTitleField = () => {
const { getCollectionJoinField, collections } = useCollectionManager_deprecated();
const { getField } = useCollection_deprecated();
const field = useField<Field>();
const fieldSchema = useFieldSchema();
const { t } = useTranslation();
const { dn } = useDesignable();
let collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']);
collectionField = collectionField
? collectionField
: collections.find((value) => value.name === fieldSchema['x-decorator-props'])?.fields;
const options = useFormulaTitleOptions();
const editTitle = async (formula) => {
const schema = {
['x-uid']: fieldSchema['x-uid'],
};
const fieldNames = {
...collectionField?.uiSchema?.['x-component-props']?.['fieldNames'],
...field.componentProps.fieldNames,
formula,
};
fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
fieldSchema['x-component-props']['fieldNames'] = fieldNames;
schema['x-component-props'] = fieldSchema['x-component-props'];
field.componentProps.fieldNames = fieldSchema['x-component-props'].fieldNames;
dn.emit('patch', {
schema,
});
dn.refresh();
};
return (
<SchemaSettingsModalItem
key="edit-field-title"
title={t('Custom option label')}
schema={
{
type: 'object',
title: t('Custom option label'),
properties: {
formula: {
required: true,
'x-decorator': 'FormItem',
'x-component': 'Variable.TextArea',
'x-component-props': {
scope: options,
},
default: field?.componentProps?.fieldNames?.formula || '',
},
},
} as ISchema
}
onSubmit={({ formula }) => {
if (formula) {
editTitle(formula);
}
}}
/>
);
};
export const useFormulaTitleVisible = () => {
const fieldSchema = useFieldSchema();
const options = useFormulaTitleOptions();
// FIXME 这里现在只有当设置为 select默认为 select 的时候看不到
return (
options.length > 0 &&
(fieldSchema['x-component-props']?.mode === 'Select' || fieldSchema['x-component'] === 'CollectionField') &&
fieldSchema['x-component-props']?.fieldNames?.value !== undefined
);
};

View File

@ -0,0 +1,54 @@
import React from 'react';
import { Field, useField, useFieldSchema } from '@tachybase/schema';
import { useCollectionManager } from '../data-source';
import { useDesignable } from '../schema-component';
import { SchemaSettingsSelectItem } from './SchemaSettings';
export const SchemaSettingCollection = () => {
const fieldSchema = useFieldSchema();
const collections = useCollectionManager();
const field = useField<Field>();
const options = collections?.dataSource['options']?.collections.map((value) => {
return {
label: value.title,
value: value.name,
};
});
const { dn } = useDesignable();
return (
<SchemaSettingsSelectItem
key="component-field"
title="Edit Collection"
options={options}
value={fieldSchema['collectionName']}
onChange={(name) => {
field.setValue('');
const titleField = collections.getCollection(name).titleField;
fieldSchema['collectionName'] = name;
fieldSchema.default = '';
fieldSchema['x-component-props'] = {
...fieldSchema['x-component-props'],
collection: name,
fieldNames: {
label: titleField,
value: titleField,
},
defaultValue: '',
};
fieldSchema['x-decorator-props'] = name;
field.componentProps = fieldSchema['x-component-props'];
void dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
collectionName: fieldSchema['collectionName'],
['x-component-props']: fieldSchema['x-component-props'],
['x-decorator-props']: fieldSchema['x-decorator-props'],
default: '',
},
});
dn.refresh();
}}
/>
);
};

View File

@ -0,0 +1,33 @@
import React from 'react';
import { useField, useFieldSchema } from '@tachybase/schema';
import { useDesignable } from '../schema-component';
import { useFieldComponents } from '../schema-initializer';
import { SchemaSettingsSelectItem } from './SchemaSettings';
export const SchemaSettingComponent = () => {
const fieldSchema = useFieldSchema();
const field = useField();
const { options } = useFieldComponents();
const { dn } = useDesignable();
return (
<SchemaSettingsSelectItem
key="component-field"
title="Field Component"
options={options}
value={fieldSchema['x-component']}
onChange={(mode) => {
field.component = mode;
fieldSchema['x-component'] = mode;
field.componentProps = fieldSchema['x-component-props'];
void dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
['x-component']: fieldSchema['x-component'],
},
});
dn.refresh();
}}
/>
);
};

View File

@ -23,6 +23,7 @@ import {
detailsBlockSettings, detailsBlockSettings,
singleDataDetailsBlockSettings, singleDataDetailsBlockSettings,
} from '../modules/blocks/data-blocks/details-single/detailsBlockSettings'; } from '../modules/blocks/data-blocks/details-single/detailsBlockSettings';
import { FilterItemCustomSettings } from '../modules/blocks/data-blocks/form';
import { import {
createFormBlockSettings, createFormBlockSettings,
creationFormBlockSettings, creationFormBlockSettings,
@ -95,6 +96,7 @@ export class SchemaSettingsPlugin extends Plugin {
// field settings // field settings
this.schemaSettingsManager.add(fieldSettingsFormItem); this.schemaSettingsManager.add(fieldSettingsFormItem);
this.schemaSettingsManager.add(FilterItemCustomSettings);
this.schemaSettingsManager.add(tableColumnSettings); this.schemaSettingsManager.add(tableColumnSettings);
this.schemaSettingsManager.add(filterCollapseItemFieldSettings); this.schemaSettingsManager.add(filterCollapseItemFieldSettings);

View File

@ -1,19 +1,20 @@
import React, { useCallback } from 'react'; import React, { useCallback, useMemo } from 'react';
// @ts-ignore // @ts-ignore
import { Form, Schema } from '@tachybase/schema'; import { Form, Schema, useField } from '@tachybase/schema';
import _ from 'lodash'; import _ from 'lodash';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { CollectionFieldOptions_deprecated } from '../../collection-manager'; import { CollectionFieldOptions_deprecated, useCollectionManager_deprecated } from '../../collection-manager';
import { useVariableScope, Variable } from '../../schema-component'; import { useCompile, useVariableScope, Variable } from '../../schema-component';
import { useValues } from '../../schema-component/antd/filter/useValues'; import { useValues } from '../../schema-component/antd/filter/useValues';
import { VariableOption, VariablesContextType } from '../../variables/types'; import { VariableOption, VariablesContextType } from '../../variables/types';
import { isVariable } from '../../variables/utils/isVariable'; import { isVariable } from '../../variables/utils/isVariable';
import { useDateVariable } from './hooks';
import { useBlockCollection } from './hooks/useBlockCollection'; import { useBlockCollection } from './hooks/useBlockCollection';
import { useContextAssociationFields } from './hooks/useContextAssociationFields'; import { useContextAssociationFields } from './hooks/useContextAssociationFields';
import { useCurrentRecordVariable } from './hooks/useRecordVariable'; import { useCurrentRecordVariable } from './hooks/useRecordVariable';
import { useCurrentUserVariable } from './hooks/useUserVariable'; import { useCurrentUserVariable, useUserVariable } from './hooks/useUserVariable';
import { useVariableOptions } from './hooks/useVariableOptions'; import { useVariableOptions } from './hooks/useVariableOptions';
import { Option } from './type'; import { Option } from './type';

View File

@ -0,0 +1,61 @@
import { useMemo } from 'react';
import { Field, useField } from '@tachybase/schema';
import { useTranslation } from 'react-i18next';
import { useFormActiveFields } from '../../../block-provider';
import { useFilterContext } from '../../filter-form/context';
/**
* `自定义筛选`
* @param props
* @returns
*/
export const useFilterVariable = (props: any = {}) => {
const customFilter = useFilterContext();
const field = useField<Field>();
const { getActiveFieldsName } = useFormActiveFields();
const activeFields = getActiveFieldsName('form');
const fields = props?.form?.fields;
const { t } = useTranslation();
const settings = useMemo(() => {
const customFields = [];
for (const field in fields) {
customFields.push({
key: field,
...fields[field],
});
}
const options = customFields
.filter((value) => value?.props?.name.includes('custom.'))
.map((custom) => {
if (activeFields.includes(custom?.props?.name)) {
const value = custom?.props?.name.replace(/^custom\./, '');
return {
key: custom?.props?.name,
value,
label: custom.title,
};
} else {
return null;
}
})
.filter(Boolean);
return {
label: t('Current filter'),
value: '$nFilter',
key: '$nFilter',
children: options,
};
}, [fields]);
return {
/** 变量配置 */
currentCustomFilterSetting: settings,
/** 变量值 */
currentCustonFilterCtx: field?.value,
/** 用于判断是否需要显示配置项 */
shouldDisplayCustomFilter: !!customFilter,
};
};

View File

@ -4,6 +4,7 @@ import { Form, ISchema, Schema } from '@tachybase/schema';
import { CollectionFieldOptions_deprecated } from '../../../collection-manager'; import { CollectionFieldOptions_deprecated } from '../../../collection-manager';
import { useBlockCollection } from './useBlockCollection'; import { useBlockCollection } from './useBlockCollection';
import { useDatetimeVariable } from './useDateVariable'; import { useDatetimeVariable } from './useDateVariable';
import { useFilterVariable } from './useFilterVariable';
import { useCurrentFormVariable } from './useFormVariable'; import { useCurrentFormVariable } from './useFormVariable';
import { useCurrentObjectVariable } from './useIterationVariable'; import { useCurrentObjectVariable } from './useIterationVariable';
import { useCurrentParentRecordVariable } from './useParentRecordVariable'; import { useCurrentParentRecordVariable } from './useParentRecordVariable';
@ -96,6 +97,14 @@ export const useVariableOptions = ({
targetFieldSchema, targetFieldSchema,
}); });
const { currentCustomFilterSetting, shouldDisplayCustomFilter } = useFilterVariable({
schema: uiSchema,
form,
collectionName: blockParentCollectionName,
collectionField,
targetFieldSchema,
});
return useMemo(() => { return useMemo(() => {
return [ return [
currentUserSettings, currentUserSettings,
@ -106,6 +115,7 @@ export const useVariableOptions = ({
shouldDisplayCurrentRecord && currentRecordSettings, shouldDisplayCurrentRecord && currentRecordSettings,
shouldDisplayCurrentParentRecord && currentParentRecordSettings, shouldDisplayCurrentParentRecord && currentParentRecordSettings,
shouldDisplayPopupRecord && popupRecordSettings, shouldDisplayPopupRecord && popupRecordSettings,
shouldDisplayCustomFilter && currentCustomFilterSetting,
].filter(Boolean); ].filter(Boolean);
}, [ }, [
currentUserSettings, currentUserSettings,
@ -121,5 +131,7 @@ export const useVariableOptions = ({
currentParentRecordSettings, currentParentRecordSettings,
shouldDisplayPopupRecord, shouldDisplayPopupRecord,
popupRecordSettings, popupRecordSettings,
currentCustomFilterSetting,
shouldDisplayCustomFilter,
]); ]);
}; };

View File

@ -1,34 +1,30 @@
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import {
__UNSAFE__DynamicComponentProps,
__UNSAFE__VariableOption,
__UNSAFE__VariablesContextType,
css,
FormBlockContext,
getShouldChange,
RecordProvider,
SchemaComponent,
useCollectionManager_deprecated,
} from '@tachybase/client';
import { ArrayCollapse } from '@tachybase/components'; import { ArrayCollapse } from '@tachybase/components';
import { Form, observer, useField, useFieldSchema } from '@tachybase/schema'; import { Form, observer, useField, useFieldSchema } from '@tachybase/schema';
import { css } from 'antd-style';
import { FormBlockContext } from '../../block-provider';
import { useCollectionManager_deprecated } from '../../collection-manager';
import { RecordProvider } from '../../record-provider';
import { SchemaComponent } from '../../schema-component';
import { DynamicComponentProps } from '../../schema-component/antd/filter/DynamicComponent';
import { VariableOption, VariablesContextType } from '../../variables/types';
import { getShouldChange, VariableInput } from '../VariableInput';
import { FilterContext } from './context'; import { FilterContext } from './context';
import { VariableInput } from './VariableInput';
interface usePropsReturn { interface usePropsReturn {
options: any; options: any;
defaultValues: any[]; defaultValues: any[];
collectionName: string; collectionName: string;
form: Form; form: Form;
variables: __UNSAFE__VariablesContextType; variables: VariablesContextType;
localVariables: __UNSAFE__VariableOption | __UNSAFE__VariableOption[]; localVariables: VariableOption | VariableOption[];
record: Record<string, any>; record: Record<string, any>;
/** /**
* create update * create update
*/ */
formBlockType: 'create' | 'update'; formBlockType: 'create' | 'update';
fields: any;
} }
interface Props { interface Props {
@ -40,7 +36,7 @@ export const FormFilterScope = observer(
(props: Props) => { (props: Props) => {
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const { useProps, dynamicComponent } = props; const { useProps, dynamicComponent } = props;
const { options, defaultValues, collectionName, form, formBlockType, variables, localVariables, record, fields } = const { options, defaultValues, collectionName, form, formBlockType, variables, localVariables, record } =
useProps(); useProps();
const { getAllCollectionsInheritChain } = useCollectionManager_deprecated(); const { getAllCollectionsInheritChain } = useCollectionManager_deprecated();
const components = useMemo(() => ({ ArrayCollapse }), []); const components = useMemo(() => ({ ArrayCollapse }), []);
@ -63,7 +59,7 @@ export const FormFilterScope = observer(
`, `,
}; };
}, },
dynamicComponent: (props: __UNSAFE__DynamicComponentProps) => { dynamicComponent: (props: DynamicComponentProps) => {
const { collectionField } = props; const { collectionField } = props;
return ( return (
<VariableInput <VariableInput
@ -77,7 +73,6 @@ export const FormFilterScope = observer(
localVariables, localVariables,
getAllCollectionsInheritChain, getAllCollectionsInheritChain,
})} })}
fields={fields}
/> />
); );
}, },
@ -85,15 +80,20 @@ export const FormFilterScope = observer(
}, },
}, },
}), }),
[collectionName, defaultValues, form, localVariables, options, props, record, variables, fields], [collectionName, defaultValues, form, localVariables, options, props, record, variables],
); );
const value = useMemo( const value = useMemo(
() => ({ field: options, fieldSchema, dynamicComponent, options: options || [] }), () => ({
field: options,
fieldSchema,
dynamicComponent,
options: options || [],
}),
[dynamicComponent, fieldSchema, options], [dynamicComponent, fieldSchema, options],
); );
return ( return (
<FormBlockContext.Provider value={{ form, type: formBlockType }}> <FormBlockContext.Provider value={{ form, type: formBlockType, collectionName: collectionName }}>
<RecordProvider record={record}> <RecordProvider record={record}>
<FilterContext.Provider value={value}> <FilterContext.Provider value={value}>
<SchemaComponent components={components} schema={schema} /> <SchemaComponent components={components} schema={schema} />

View File

@ -1,4 +1,4 @@
import { createContext } from 'react'; import { createContext, useContext } from 'react';
import { ObjectField, Schema } from '@tachybase/schema'; import { ObjectField, Schema } from '@tachybase/schema';
export interface FilterContextProps { export interface FilterContextProps {
@ -8,8 +8,13 @@ export interface FilterContextProps {
options?: any[]; options?: any[];
disabled?: boolean; disabled?: boolean;
collectionName?: string; collectionName?: string;
customFilter?: boolean;
} }
export const RemoveConditionContext = createContext(null); export const RemoveConditionContext = createContext(null);
export const FilterContext = createContext<FilterContextProps>(null); export const FilterContext = createContext<FilterContextProps>(null);
export const FilterLogicContext = createContext(null); export const FilterLogicContext = createContext(null);
export const useFilterContext = () => {
return useContext(FilterContext);
};

View File

@ -9,6 +9,10 @@ export * from './SchemaSettingsDataScope';
export * from './SchemaSettingsDefaultValue'; export * from './SchemaSettingsDefaultValue';
export * from './SchemaSettingsDateFormat'; export * from './SchemaSettingsDateFormat';
export * from './SchemaSettingsSortingRule'; export * from './SchemaSettingsSortingRule';
export * from './SchemaSettingCollection';
export * from './SchemaSettingComponent';
export * from './EditFormulaTitleField';
export * from './EditCustomDefaultValue';
export { default as useParseDataScopeFilter } from './hooks/useParseDataScopeFilter'; export { default as useParseDataScopeFilter } from './hooks/useParseDataScopeFilter';

View File

@ -1,58 +0,0 @@
export {
removeGridFormItem,
useInheritsFormItemInitializerFields,
useFormItemInitializerFields,
useAssociatedFormItemInitializerFields,
useFilterInheritsFormItemInitializerFields,
useFilterFormItemInitializerFields,
} from './schema-initializer/utils';
export { parseVariables } from './schema-component/common/utils/uitls';
export { linkageAction } from './schema-component/antd/action/utils';
export { useCollectionState } from './schema-settings/DataTemplates/hooks/useCollectionState';
export { useSyncFromForm } from './schema-settings/DataTemplates/utils';
export { ColumnFieldProvider } from './schema-component/antd/table-v2/components/ColumnFieldProvider';
export { default as schema } from './schema-component/antd/association-field/schema';
export { ReadPrettyInternalViewer } from './schema-component/antd/association-field/InternalViewer';
export { useSetAriaLabelForPopover } from './schema-component/antd/action/hooks/useSetAriaLabelForPopover';
export { InternalFileManager } from './schema-component/antd/association-field/FileManager';
export { CreateRecordAction } from './schema-component/antd/association-field/components/CreateRecordAction';
export { AssociationSelect } from './schema-component/antd/association-field/AssociationSelect';
export { AssociationFieldProvider } from './schema-component/antd/association-field/AssociationFieldProvider';
export { InternalCascadeSelect } from './schema-component/antd/association-field/InternalCascadeSelect';
export { InternalNester } from './schema-component/antd/association-field/InternalNester';
export { InternalPicker } from './schema-component/antd/association-field/InternalPicker';
export { InternalSubTable } from './schema-component/antd/association-field/InternalSubTable';
export { InternaPopoverNester } from './schema-component/antd/association-field/InternalPopoverNester';
export { InternalCascader } from './schema-component/antd/association-field/InternalCascader';
export { Nester } from './schema-component/antd/association-field/Nester';
export { SubTable } from './schema-component/antd/association-field/SubTable';
export { ReadPretty } from './schema-component/antd/association-field/ReadPretty';
export { SubFormProvider } from './schema-component/antd/association-field/hooks';
export { useAssociationFieldContext } from './schema-component/antd/association-field/hooks';
export { FlagProvider } from './flag-provider';
export { isVariable } from './variables/utils/isVariable';
export { transformVariableValue } from './variables/utils/transformVariableValue';
export { useVariables } from './variables';
export { VariableInput, getShouldChange } from './schema-settings/VariableInput/VariableInput';
export { useLocalVariables } from './variables';
export { VariablesContext } from './variables/VariablesProvider';
export {
EditComponent,
EditDescription,
EditOperator,
EditTitle,
EditTitleField,
EditTooltip,
EditValidationRules,
} from './schema-component/antd/form-item/SchemaSettingOptions';
export type { DynamicComponentProps } from './schema-component/antd/filter/DynamicComponent';
export { DetailsBlockProvider, useDetailsBlockContext } from './block-provider/DetailsBlockProvider';
export { getInnermostKeyAndValue } from './schema-component/common/utils/uitls';
export { default as useServiceOptions } from './schema-component/antd/association-field/hooks';
export type { VariablesContextType, VariableOption } from './variables/types';
export type { Option } from './schema-settings/VariableInput/type';
export { useBlockCollection } from './schema-settings/VariableInput/hooks/useBlockCollection';
export { useValues } from './schema-component/antd/filter/useValues';
export { useVariableOptions } from './schema-settings/VariableInput/hooks/useVariableOptions';
export { useContextAssociationFields } from './schema-settings/VariableInput/hooks/useContextAssociationFields';
export { useRecordVariable } from './schema-settings/VariableInput/hooks/useRecordVariable';

View File

@ -47,11 +47,13 @@ export const nextTick = (fn: () => void) => {
}; };
export function fuzzysearch(needle: string, haystack: string): boolean { export function fuzzysearch(needle: string, haystack: string): boolean {
const hlen = haystack.length; if (!needle || !haystack) {
const nlen = needle.length;
if (nlen > hlen) {
return false; return false;
} }
const hlen = haystack.length;
const nlen = needle.length;
if (nlen === hlen) { if (nlen === hlen) {
return needle === haystack; return needle === haystack;
} }

View File

@ -1,399 +0,0 @@
import React, { useCallback, useContext, useMemo } from 'react';
import {
__UNSAFE__,
__UNSAFE__VariableInputOption,
__UNSAFE__VariableOption,
__UNSAFE__VariablesContextType,
CollectionFieldOptions,
isVariable,
useCollectionManager_deprecated,
useCompile,
useDateVariable,
useUserVariable,
useVariableScope,
Variable,
} from '@tachybase/client';
// @ts-ignore
import { Form, Schema, SchemaOptionsContext, useField, useFieldSchema } from '@tachybase/schema';
import _ from 'lodash';
import { useTranslation } from 'react-i18next';
import { FilterContext } from './context';
const { useBlockCollection, useValues, useVariableOptions, useContextAssociationFields, useRecordVariable } =
__UNSAFE__;
interface GetShouldChangeProps {
collectionField: CollectionFieldOptions;
variables: __UNSAFE__VariablesContextType;
localVariables: __UNSAFE__VariableOption | __UNSAFE__VariableOption[];
/** `useCollectionManager` 返回的 */
getAllCollectionsInheritChain: (collectionName: string) => string[];
}
interface RenderSchemaComponentProps {
value: any;
onChange: (value: any) => void;
}
type Props = {
value: any;
onChange: (value: any, optionPath?: any[]) => void;
renderSchemaComponent: (props: RenderSchemaComponentProps) => any;
schema?: any;
/** 消费变量值的字段 */
targetFieldSchema?: Schema;
children?: any;
className?: string;
style?: React.CSSProperties;
collectionField: CollectionFieldOptions;
contextCollectionName?: string;
/**指定当前表单数据表 */
currentFormCollectionName?: string;
/**指定当前对象数据表 */
currentIterationCollectionName?: string;
/**
* `onChange` `onChange`
* @param value `onChange`
* @returns `true` `onChange`
*/
shouldChange?: (value: any, optionPath?: any[]) => Promise<boolean>;
form?: Form;
/**
*
*/
record?: Record<string, any>;
/**
* scope
* @param scope
* @returns
*/
returnScope?: (scope: __UNSAFE__VariableInputOption[]) => any[];
fields: any;
};
/**
*
* -
* -
* @param props
* @returns
*/
export const VariableInput = (props: Props) => {
const {
value,
onChange,
renderSchemaComponent: RenderSchemaComponent,
style,
schema,
className,
contextCollectionName,
collectionField,
shouldChange,
form,
record,
returnScope = _.identity,
targetFieldSchema,
currentFormCollectionName,
currentIterationCollectionName,
fields,
} = props;
const { name: blockCollectionName } = useBlockCollection();
const scope = useVariableScope();
const { operator, schema: uiSchema = collectionField?.uiSchema } = useValues();
const variableOptions = useVariableOptions({
collectionField,
form,
record,
operator,
uiSchema,
targetFieldSchema,
});
const contextVariable = useContextAssociationFields({ schema, maxDepth: 2, contextCollectionName, collectionField });
const { compatOldVariables } = useCompatOldVariables({
collectionField,
uiSchema,
targetFieldSchema,
blockCollectionName,
});
if (contextCollectionName && variableOptions.every((item) => item.value !== contextVariable.value)) {
variableOptions.push(contextVariable);
}
const handleChange = useCallback(
(value: any, optionPath: any[]) => {
if (!shouldChange) {
return onChange(value);
}
// `shouldChange` 这个函数的运算量比较大,会导致展开变量列表时有明显的卡顿感,在这里加个延迟能有效解决这个问题
setTimeout(async () => {
if (await shouldChange(value, optionPath)) {
onChange(value);
}
});
},
[onChange, shouldChange],
);
const options = useVariableCustomOptions(fields);
return (
<Variable.Input
className={className}
value={value}
onChange={handleChange}
scope={options}
style={style}
changeOnSelect
>
<RenderSchemaComponent value={value} onChange={onChange} />
</Variable.Input>
);
};
/**
*
*/
export const getShouldChange = ({
collectionField,
variables,
localVariables,
getAllCollectionsInheritChain,
}: GetShouldChangeProps) => {
const collectionsInheritChain = collectionField ? getAllCollectionsInheritChain(collectionField.target) : [];
return async (value: any, optionPath: any[]) => {
if (_.isString(value) && value.includes('$nRole')) {
return true;
}
if (!isVariable(value) || !variables || !collectionField) {
return true;
}
// `json` 可以选择任意类型的变量详见https://tachybase.feishu.cn/docx/EmNEdEBOnoQohUx2UmBcqIQ5nyh#FPLfdSRDEoXR65xW0mBcdfL5n0c
if (collectionField.interface === 'json') {
return true;
}
const lastOption = optionPath[optionPath.length - 1];
// 点击叶子节点时,必须更新 value
if (lastOption && _.isEmpty(lastOption.children) && !lastOption.loadChildren) {
return true;
}
const collectionFieldOfVariable = await variables.getCollectionField(value, localVariables);
if (!collectionField) {
return false;
}
// `一对一` 和 `一对多` 的不能用于设置默认值,因为其具有唯一性
if (['o2o', 'o2m', 'oho'].includes(collectionFieldOfVariable?.interface)) {
return false;
}
if (!collectionField.target && collectionFieldOfVariable?.target) {
return false;
}
if (collectionField.target && !collectionFieldOfVariable?.target) {
return false;
}
if (
collectionField.target &&
collectionFieldOfVariable?.target &&
!collectionsInheritChain.includes(collectionFieldOfVariable?.target)
) {
return false;
}
return true;
};
};
export interface FormatVariableScopeParam {
children: any[];
disabled: boolean;
name: string;
title: string;
}
export interface FormatVariableScopeReturn {
value: string;
key: string;
label: string;
disabled: boolean;
children?: any[];
}
/**
*
* @param variables
*/
export function useCompatOldVariables(props: {
uiSchema: any;
collectionField: CollectionFieldOptions;
blockCollectionName: string;
noDisabled?: boolean;
targetFieldSchema?: Schema;
}) {
const { uiSchema, collectionField, noDisabled, targetFieldSchema, blockCollectionName } = props;
const { t } = useTranslation();
const lowLevelUserVariable = useUserVariable({
maxDepth: 1,
uiSchema: uiSchema,
collectionField,
noDisabled,
targetFieldSchema,
});
const currentRecordVariable = useRecordVariable({
schema: uiSchema,
collectionName: blockCollectionName,
collectionField,
noDisabled,
targetFieldSchema,
});
const compatOldVariables = useCallback(
(variables: __UNSAFE__VariableInputOption[], { value }) => {
if (!isVariable(value)) {
return variables;
}
variables = _.cloneDeep(variables);
const systemVariable: __UNSAFE__VariableInputOption = {
value: '$system',
key: '$system',
label: t('System variables'),
isLeaf: false,
children: [
{
value: 'now',
key: 'now',
label: t('Current time'),
isLeaf: true,
depth: 1,
},
],
depth: 0,
};
const currentTime = {
value: 'currentTime',
label: t('Current time'),
children: null,
};
if (value.includes('$system')) {
variables.push(systemVariable);
}
if (value.includes(`${blockCollectionName}.`)) {
const variable = variables.find((item) => item.value === '$nForm' || item.value === '$nRecord');
if (variable) {
variable.value = blockCollectionName;
}
}
if (value.includes('$form')) {
const variable = variables.find((item) => item.value === '$nForm');
if (variable) {
variable.value = '$form';
}
}
if (value.includes('currentUser')) {
const userVariable = variables.find((item) => item.value === '$user');
if (userVariable) {
userVariable.value = 'currentUser';
} else {
variables.unshift({ ...lowLevelUserVariable, value: 'currentUser' });
}
}
if (value.includes('currentRecord')) {
const formVariable = variables.find((item) => item.value === '$nRecord');
if (formVariable) {
formVariable.value = 'currentRecord';
} else {
variables.unshift({ ...currentRecordVariable, value: 'currentRecord' });
}
}
if (value.includes('currentTime')) {
variables.push(currentTime);
}
if (value.includes('$date')) {
const formVariable = variables.find((item) => item.value === '$nDate');
if (formVariable) {
formVariable.value = '$date';
}
}
return variables;
},
[blockCollectionName],
);
return { compatOldVariables };
}
export const useVariableCustomOptions = (fields) => {
const field = useField<any>();
const { operator, schema } = field.data || {};
const userVariable = useUserVariable({
collectionField: { uiSchema: schema },
uiSchema: schema,
});
const dateVariable = useDateVariable({ operator, schema });
const filterVariable = useFilterVariable(fields);
const result = useMemo(
() => [userVariable, dateVariable, filterVariable].filter(Boolean),
[dateVariable, userVariable, filterVariable],
);
if (!operator || !schema) return [];
return result;
};
export const useFilterVariable = (fields) => {
const { t } = useTranslation();
const compile = useCompile();
const { collections } = useCollectionManager_deprecated();
const customFields = [];
for (const field in fields) {
customFields.push({
key: field,
...fields[field],
});
}
const options = customFields
.filter((value) => value?.props?.name.includes('custom.'))
.map((custom) => {
const value = custom?.props?.name.replace(/^custom\./, '');
return {
key: custom?.props?.name,
value,
label: custom.title,
};
});
const result = useMemo(
() => ({
label: t('Current filter'),
value: '$nFilter',
key: '$nFilter',
children: options,
}),
[options, t],
);
if (!options.length) return null;
return result;
};

View File

@ -1,57 +0,0 @@
import { Plugin, useCollection } from '@tachybase/client';
import { useFilterBlockActionProps } from '../../hooks/useFilterBlockActionProps';
import { useFilterFormCustomProps } from '../../hooks/useFilterFormCustomProps';
import { tval } from '../../locale';
import { FilterFormItem, FilterFormItemCustom, FilterItemCustomDesigner } from '../../schema-initializer';
import {
EditDefaultValue,
EditFormulaTitleField,
FilterVariableInput,
SetFilterScope,
useFormulaTitleVisible,
useSetFilterScopeVisible,
} from '../../schema-settings';
export class PluginExtendedFilterForm extends Plugin {
async load() {
this.app.addScopes({
useFilterBlockActionProps,
useFilterFormCustomProps,
});
this.app.addComponents({
FilterFormItem,
FilterFormItemCustom,
FilterItemCustomDesigner,
FilterVariableInput,
});
this.schemaSettingsManager.addItem('fieldSettings:component:Select', 'formulatitleField', {
Component: EditFormulaTitleField,
useVisible: useFormulaTitleVisible,
});
this.schemaSettingsManager.addItem('fieldSettings:component:Select', 'editDefaultValue', {
Component: EditDefaultValue,
});
const customItem = {
title: tval('Custom filter field'),
name: 'custom',
type: 'item',
Component: 'FilterFormItemCustom',
};
this.app.schemaInitializerManager.addItem('filterForm:configureFields', 'custom-item-divider', {
type: 'divider',
});
this.app.schemaInitializerManager.addItem('filterForm:configureFields', customItem.name, customItem);
this.schemaSettingsManager.addItem('ActionSettings', 'Customize.setFilterScope', {
Component: SetFilterScope,
useVisible: useSetFilterScopeVisible,
useComponentProps() {
const collection = useCollection();
return {
collectionName: collection.name,
};
},
});
}
}

View File

@ -1,128 +0,0 @@
import {
findFilterTargets,
mergeFilter,
transformToFilter,
useCollection_deprecated,
useCollectionManager_deprecated,
useFilterBlock,
} from '@tachybase/client';
import { isEmpty, useField, useFieldSchema, useForm } from '@tachybase/schema';
import flat from 'flat';
import { hasDuplicateKeys } from '../utils';
export const removeNullCondition = (filter, fieldSchema?) => {
const filterSchema = fieldSchema ? fieldSchema['x-filter-rules'] : '';
const filterSchemaItem = flat(filterSchema || '');
const items = flat(filter || {});
const values = {};
const isCustomFilter = filterSchema?.$and?.length || filterSchema?.$or?.length;
const isFilterCustom = isCustomFilter ? hasDuplicateKeys(items, filterSchemaItem) : false;
if (!isFilterCustom) {
if (isCustomFilter) {
for (const filterKey in filterSchemaItem) {
const match = filterSchemaItem[filterKey]?.slice(11, -2);
const collection = match?.split('.')[0];
const filterItems = Object.keys(items).filter((item) => item.includes(collection))[0];
if (filterItems) {
filterSchemaItem[filterKey] = items[filterItems];
}
}
for (const item in items) {
if (!item.includes('custom')) {
values[item] = items[item];
}
}
for (const item in filterSchemaItem) {
if (filterSchemaItem[item].includes('$nFilter')) {
delete filterSchemaItem[item];
}
}
const flatValue = flat.unflatten(values);
const flatFieldSchema = flat.unflatten(filterSchemaItem);
flatValue['$and'] = flatValue['$and']?.filter(Boolean);
flatFieldSchema['$and'] = flatFieldSchema['$and']?.filter(Boolean);
return {
$and: [flatValue, flatFieldSchema],
};
} else {
for (const key in items) {
const value = items[key];
if (!key.includes('custom') && value != null && !isEmpty(value)) {
values[key] = value;
}
}
return flat.unflatten(values);
}
} else {
return flat.unflatten(items);
}
};
export const useFilterBlockActionProps = () => {
const form = useForm();
const actionField = useField();
const fieldSchema = useFieldSchema();
const { getDataBlocks } = useFilterBlock();
const { name } = useCollection_deprecated();
const { getCollectionJoinField } = useCollectionManager_deprecated();
actionField.data = actionField.data || {};
return {
async onClick() {
const { targets = [], uid } = findFilterTargets(fieldSchema);
actionField.data.loading = true;
try {
// 收集 filter 的值
await Promise.all(
getDataBlocks().map(async (block) => {
const target = targets.find((target) => target.uid === block.uid);
if (!target) return;
const param = block.service.params?.[0] || {};
for (const key in form.values) {
if (
(typeof form.values[key] === 'object' &&
(JSON.stringify(form.values[key]) === '{}' || JSON.stringify(form.values[key]) === '[]')) ||
!form.values[key]
) {
delete form.values[key];
}
}
// 保留原有的 filter
const storedFilter = block.service.params?.[1]?.filters || {};
storedFilter[uid] = removeNullCondition(
transformToFilter(form.values, fieldSchema, getCollectionJoinField, name),
fieldSchema,
);
if (block.defaultFilter) {
getDataBlocks().forEach((getblock) => {
if (getblock.uid !== block.uid && getblock.collection.name === block.collection.name) {
getblock['defaultFilter'] = block.defaultFilter;
}
});
}
const mergedFilter = mergeFilter([
...Object.values(storedFilter).map((filter) => removeNullCondition(filter, fieldSchema)),
block.defaultFilter,
]);
return block.doFilter(
{
...param,
page: 1,
filter: mergedFilter,
},
{ filters: storedFilter },
);
}),
);
actionField.data.loading = false;
} catch (error) {
console.error(error);
actionField.data.loading = false;
}
},
};
};

View File

@ -1,4 +1,4 @@
import { EditTitleField, Plugin } from '@tachybase/client'; import { Plugin, useFormulaTitleVisible } from '@tachybase/client';
import { CodeMirror } from '@tachybase/components'; import { CodeMirror } from '@tachybase/components';
import { autorun, isValid, useFieldSchema } from '@tachybase/schema'; import { autorun, isValid, useFieldSchema } from '@tachybase/schema';
@ -18,10 +18,8 @@ import { PluginAssistant } from './features/assistant';
import { PluginGroupBlock } from './features/block-group'; import { PluginGroupBlock } from './features/block-group';
import { PluginContextMenu } from './features/context-menu'; import { PluginContextMenu } from './features/context-menu';
import { PluginCustomComponents } from './features/custom-components'; import { PluginCustomComponents } from './features/custom-components';
import { PluginDemo } from './features/demo';
import { DepartmentsPlugin } from './features/departments'; import { DepartmentsPlugin } from './features/departments';
import { EmbedPlugin } from './features/embed'; import { EmbedPlugin } from './features/embed';
import { PluginExtendedFilterForm } from './features/extended-filter-form';
import { PluginFieldAppends } from './features/field-appends'; import { PluginFieldAppends } from './features/field-appends';
import { PluginHeraVersion } from './features/hera-version'; import { PluginHeraVersion } from './features/hera-version';
import { PluginOutbound } from './features/outbound'; import { PluginOutbound } from './features/outbound';
@ -45,14 +43,7 @@ import { AutoComplete } from './schema-components';
import AssociationCascader from './schema-components/association-cascader/AssociationCascader'; import AssociationCascader from './schema-components/association-cascader/AssociationCascader';
import { CreateSubmitActionInitializer, SettingBlockInitializer } from './schema-initializer'; import { CreateSubmitActionInitializer, SettingBlockInitializer } from './schema-initializer';
import { useCreateActionProps } from './schema-initializer/actions/hooks/useCreateActionProps'; import { useCreateActionProps } from './schema-initializer/actions/hooks/useCreateActionProps';
import { import { EditTitle, IsTablePageSize, PageModeSetting, usePaginationVisible } from './schema-settings';
EditFormulaTitleField,
EditTitle,
IsTablePageSize,
PageModeSetting,
useFormulaTitleVisible,
usePaginationVisible,
} from './schema-settings';
import { SchemaSettingsDatePickerType } from './schema-settings/SchemaSettingsDatePickerType'; import { SchemaSettingsDatePickerType } from './schema-settings/SchemaSettingsDatePickerType';
import { import {
SchemaSettingsDatePresets, SchemaSettingsDatePresets,
@ -77,7 +68,6 @@ export class PluginCoreClient extends Plugin {
await this.app.pm.add(PluginContextMenu); await this.app.pm.add(PluginContextMenu);
await this.app.pm.add(PluginAssistant); await this.app.pm.add(PluginAssistant);
await this.app.pm.add(PluginPDF); await this.app.pm.add(PluginPDF);
await this.app.pm.add(PluginExtendedFilterForm);
await this.app.pm.add(PluginOutbound); await this.app.pm.add(PluginOutbound);
// await this.app.pm.add(PluginModeHighlight); // await this.app.pm.add(PluginModeHighlight);
await this.app.pm.add(PluginFieldAppends); await this.app.pm.add(PluginFieldAppends);
@ -108,10 +98,6 @@ export class PluginCoreClient extends Plugin {
return v1 || v2; return v1 || v2;
}, },
}); });
this.schemaSettingsManager.addItem('FormItemSettings', 'formulatitleField', {
Component: EditFormulaTitleField,
useVisible: useFormulaTitleVisible,
});
this.schemaSettingsManager.addItem('FormItemSettings', 'isTablePageSize', { this.schemaSettingsManager.addItem('FormItemSettings', 'isTablePageSize', {
Component: IsTablePageSize, Component: IsTablePageSize,
useVisible: usePaginationVisible, useVisible: usePaginationVisible,
@ -147,7 +133,6 @@ export class PluginCoreClient extends Plugin {
CustomComponentStub, CustomComponentStub,
CustomField, CustomField,
EditTitle, EditTitle,
EditTitleField,
Expression, Expression,
PageLayout, PageLayout,
SettingBlock: SettingBlockInitializer, SettingBlock: SettingBlockInitializer,

View File

@ -10,7 +10,7 @@ import { fuzzysearch } from '../../utils';
export const AutoComplete = connect((props) => { export const AutoComplete = connect((props) => {
const { fieldNames, params: fieldFilter } = props; const { fieldNames, params: fieldFilter } = props;
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const targetKey = fieldNames.value; const targetKey = fieldNames?.value;
const api = useAPIClient(); const api = useAPIClient();
const [defultOptions, setDefultOptions] = useState([]); const [defultOptions, setDefultOptions] = useState([]);
const [options, setOptions] = useState([]); const [options, setOptions] = useState([]);

View File

@ -1,3 +1,2 @@
export * from './items/CustomFilterFormItemInitializer';
export * from './actions/CreateSubmitActionInitializer'; export * from './actions/CreateSubmitActionInitializer';
export * from './blocks/SettingBlockInitializer'; export * from './blocks/SettingBlockInitializer';

View File

@ -1,66 +0,0 @@
import React, { FC } from 'react';
import { SchemaSettingsItem, useFormActiveFields, useSchemaSettings } from '@tachybase/client';
import { Field, ISchema, useField, useFieldSchema, useForm } from '@tachybase/schema';
import { App, ModalFuncProps } from 'antd';
import { useTranslation } from '../locale';
export interface SchemaSettingsRemoveProps {
confirm?: ModalFuncProps;
removeParentsIfNoChildren?: boolean;
breakRemoveOn?: ISchema | ((s: ISchema) => boolean);
}
export const SchemaSettingsRemove: FC<SchemaSettingsRemoveProps> = (props) => {
const { confirm, removeParentsIfNoChildren, breakRemoveOn } = props;
const { dn, template } = useSchemaSettings();
const { t } = useTranslation();
const field = useField<Field>();
const fieldSchema = useFieldSchema();
const form = useForm();
const { modal } = App.useApp();
const { removeActiveFieldName } = useFormActiveFields() || {};
return (
<SchemaSettingsItem
title="Delete"
eventKey="remove"
onClick={() => {
modal.confirm({
title: t('Delete block'),
content: t('Are you sure you want to delete it?'),
...confirm,
async onOk() {
const options = {
removeParentsIfNoChildren,
breakRemoveOn,
};
const name = fieldSchema.name as string;
const fieldName = name.split('.')[1];
const title = fieldSchema.title;
if (field?.required) {
field.required = false;
fieldSchema['required'] = false;
}
await dn.remove(null, options);
await confirm?.onOk?.();
if (form.values['custom']) {
delete form.values['custom'][fieldName];
}
for (const key in form.fields) {
if (key.includes(name) && form.fields[key].title === title) {
delete form.fields[key];
}
}
removeActiveFieldName?.(fieldSchema.name as string);
if (field?.setInitialValue && field?.reset) {
field.setInitialValue(null);
field.reset();
}
},
});
}}
>
{t('Delete')}
</SchemaSettingsItem>
);
};

View File

@ -1,23 +1,14 @@
import React, { useCallback, useEffect, useMemo } from 'react'; import React, { useEffect, useMemo } from 'react';
import { import {
getShouldChange, getShouldChange,
SchemaComponent, SchemaComponent,
SchemaSettingsModalItem, SchemaSettingsModalItem,
SchemaSettingsSelectItem,
SchemaSettingsSwitchItem, SchemaSettingsSwitchItem,
useCollection_deprecated, useCollection_deprecated,
useCollectionManager,
useCollectionManager_deprecated, useCollectionManager_deprecated,
useCompile,
useCurrentUserVariable, useCurrentUserVariable,
useDatetimeVariable, useDatetimeVariable,
useDesignable, useDesignable,
useFormBlockContext,
useFormBlockType,
useLocalVariables,
useRecord,
useSchemaTemplateManager,
useVariables,
VariableInput, VariableInput,
VariableScopeProvider, VariableScopeProvider,
} from '@tachybase/client'; } from '@tachybase/client';
@ -26,227 +17,13 @@ import { Field, ISchema, useField, useFieldSchema } from '@tachybase/schema';
import { useMemoizedFn } from 'ahooks'; import { useMemoizedFn } from 'ahooks';
import _ from 'lodash'; import _ from 'lodash';
import { FormFilterScope } from '../components/filter-form/FormFilterScope';
import { useTranslation } from '../locale'; import { useTranslation } from '../locale';
import { useFieldComponents } from '../schema-initializer';
export const useFormulaTitleOptions = () => {
const compile = useCompile();
const { getCollectionJoinField, getCollectionFields } = useCollectionManager_deprecated();
const { getField } = useCollection_deprecated();
const fieldSchema = useFieldSchema();
const collectionManage = useCollectionManager_deprecated();
const collectionManageField = collectionManage.collections.filter(
(value) => value.name === fieldSchema['x-decorator-props'],
)[0];
const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']);
let fields = [];
if (collectionField) {
fields = getCollectionFields(
collectionField
? collectionField.target
? collectionField.target
: collectionField.collectionName
: fieldSchema['name'],
);
} else if (collectionManageField) {
fields = collectionManageField['fields'];
}
const options = [];
fields?.forEach((field) => {
if (field.interface !== 'm2m') {
if (field.uiSchema) {
options.push({
label: compile(field.uiSchema.title),
value: field.name,
children:
getCollectionFields(field.target)
?.filter((subField) => subField.uiSchema)
.map((subField) => ({
label: subField.uiSchema ? compile(subField.uiSchema.title) : '',
value: subField.name,
})) ?? [],
});
}
}
});
return options;
};
export const useFormulaTitleVisible = () => {
const fieldSchema = useFieldSchema();
const options = useFormulaTitleOptions();
// FIXME 这里现在只有当设置为 select默认为 select 的时候看不到
return (
options.length > 0 &&
(fieldSchema['x-component-props']?.mode === 'Select' || fieldSchema['x-component'] === 'CollectionField') &&
fieldSchema['x-component-props']?.fieldNames?.value !== undefined
);
};
export const EditFormulaTitleField = () => {
const { getCollectionJoinField, collections } = useCollectionManager_deprecated();
const { getField } = useCollection_deprecated();
const field = useField<Field>();
const fieldSchema = useFieldSchema();
const { t } = useTranslation();
const { dn } = useDesignable();
let collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']);
collectionField = collectionField
? collectionField
: collections.find((value) => value.name === fieldSchema['x-decorator-props'])?.fields;
const options = useFormulaTitleOptions();
const editTitle = async (formula) => {
const schema = {
['x-uid']: fieldSchema['x-uid'],
};
const fieldNames = {
...collectionField?.uiSchema?.['x-component-props']?.['fieldNames'],
...field.componentProps.fieldNames,
formula,
};
fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
fieldSchema['x-component-props']['fieldNames'] = fieldNames;
schema['x-component-props'] = fieldSchema['x-component-props'];
field.componentProps.fieldNames = fieldSchema['x-component-props'].fieldNames;
dn.emit('patch', {
schema,
});
dn.refresh();
};
return (
<SchemaSettingsModalItem
key="edit-field-title"
title={t('Custom option label')}
schema={
{
type: 'object',
title: t('Custom option label'),
properties: {
formula: {
required: true,
'x-decorator': 'FormItem',
'x-component': 'Variable.TextArea',
'x-component-props': {
scope: options,
},
default: field?.componentProps?.fieldNames?.formula || '',
},
},
} as ISchema
}
onSubmit={({ formula }) => {
if (formula) {
editTitle(formula);
}
}}
/>
);
};
export const EditDefaultValue = () => {
const { t } = useTranslation();
const { dn } = useDesignable();
const field = useField<Field>();
const fieldSchema = useFieldSchema();
const collectionName = fieldSchema['collectionName'];
const title = fieldSchema.title;
return (
<SchemaSettingsModalItem
key="set field default value"
title={t('Set default value')}
schema={{
type: 'void',
title: t('Set default value'),
properties: {
default: {
title,
'x-decorator': 'FormItem',
'x-component': 'FilterVariableInput',
'x-component-props': {
fieldSchema,
},
},
},
}}
onSubmit={({ default: { value } }) => {
field.setValue(value);
fieldSchema['default'] = value;
fieldSchema['x-component-props']['defaultValue'] = value;
dn.emit('patch', {
schema: {
'x-uid': fieldSchema['x-uid'],
['x-component-props']: fieldSchema['x-component-props'],
default: fieldSchema.default,
},
});
dn.refresh();
}}
/>
);
};
export const FilterVariableInput: React.FC<any> = (props) => {
const { value, onChange, fieldSchema } = props;
const { currentUserSettings } = useCurrentUserVariable({
collectionField: { uiSchema: fieldSchema },
uiSchema: fieldSchema,
});
const { datetimeSettings } = useDatetimeVariable({
operator: fieldSchema['x-component-props']?.['filter-operator'],
schema: fieldSchema,
noDisabled: true,
});
const options = useMemo(
() => [currentUserSettings, datetimeSettings].filter(Boolean),
[datetimeSettings, currentUserSettings],
);
const schema = {
...fieldSchema,
'x-component': fieldSchema['x-component'] || 'Input',
'x-decorator': '',
title: '',
name: 'value',
};
const componentProps = fieldSchema['x-component-props'] || {};
const handleChange = useMemoizedFn(onChange);
useEffect(() => {
if (fieldSchema.default) {
handleChange({ value: fieldSchema.default });
}
}, [fieldSchema.default, handleChange]);
return (
<VariableScopeProvider scope={options}>
<VariableInput
{...componentProps}
renderSchemaComponent={() => <SchemaComponent schema={schema} />}
fieldNames={{}}
value={value?.value}
scope={options}
onChange={(v: any) => {
onChange({ value: v });
}}
shouldChange={getShouldChange({} as any)}
/>
</VariableScopeProvider>
);
};
export const usePaginationVisible = () => { export const usePaginationVisible = () => {
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
return fieldSchema['x-component-props']?.mode === 'SubTable'; return fieldSchema['x-component-props']?.mode === 'SubTable';
}; };
export const useSetFilterScopeVisible = () => {
const fieldSchema = useFieldSchema();
return (
fieldSchema['x-component-props']?.useProps === '{{ useFilterBlockActionProps }}' ||
fieldSchema['x-use-component-props'] === 'useFilterBlockActionProps'
);
};
export const EditTitle = () => { export const EditTitle = () => {
const { getCollectionJoinField } = useCollectionManager_deprecated(); const { getCollectionJoinField } = useCollectionManager_deprecated();
const { getField } = useCollection_deprecated(); const { getField } = useCollection_deprecated();
@ -294,64 +71,6 @@ export const EditTitle = () => {
); );
}; };
export const EditTitleField = () => {
const { getCollectionFields, getCollectionJoinField } = useCollectionManager_deprecated();
const { getField } = useCollection_deprecated();
const field = useField<Field>();
const fieldSchema = useFieldSchema();
const { t } = useTranslation();
const { dn } = useDesignable();
const compile = useCompile();
const collectionManage = useCollectionManager_deprecated();
const isCustomFilterItem = ((fieldSchema?.name as string) ?? '').startsWith('custom.');
const collectionManageField = isCustomFilterItem
? collectionManage.collections.filter((value) => value.name === fieldSchema['x-decorator-props'])[0]
: {};
const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']);
let targetFields = [];
if (collectionField) {
targetFields = collectionField?.target
? getCollectionFields(collectionField?.target)
: getCollectionFields(collectionField?.targetCollection) ?? [];
} else if (collectionManageField) {
targetFields = collectionManageField['fields'];
}
const options = targetFields
.filter((field) => !field?.target && field.type !== 'boolean')
.map((field) => ({
value: field?.name,
label: compile(field?.uiSchema?.title) || field?.name,
}));
return options.length > 0 && (fieldSchema['x-component'] === 'CollectionField' || isCustomFilterItem) ? (
<SchemaSettingsSelectItem
key="title-field"
title={t('Title field')}
options={options}
value={field?.componentProps?.fieldNames?.label}
onChange={(label) => {
const schema = {
['x-uid']: fieldSchema['x-uid'],
};
const fieldNames = {
...collectionField?.uiSchema?.['x-component-props']?.['fieldNames'],
...field.componentProps.fieldNames,
label,
value: label,
};
fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
fieldSchema['x-component-props']['fieldNames'] = fieldNames;
schema['x-component-props'] = fieldSchema['x-component-props'];
field.componentProps.fieldNames = fieldSchema['x-component-props'].fieldNames;
dn.emit('patch', {
schema,
});
dn.refresh();
}}
/>
) : null;
};
export const IsTablePageSize = () => { export const IsTablePageSize = () => {
const { dn } = useDesignable(); const { dn } = useDesignable();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
@ -382,141 +101,6 @@ export const IsTablePageSize = () => {
); );
}; };
const findGridSchema = (fieldSchema) => {
return fieldSchema.reduceProperties((buf, s) => {
if (s['x-component'] === 'FormV2') {
const f = s.reduceProperties((buf, s) => {
if (s['x-component'] === 'Grid' || s['x-component'] === 'BlockTemplate') {
return s;
}
return buf;
}, null);
if (f) {
return f;
}
}
return buf;
}, null);
};
export const useCollectionFilterOptions = (collectionName: string) => {
const { getCollectionFields, getInterface } = useCollectionManager_deprecated();
const fields = getCollectionFields(collectionName);
const field2option = (field, depth) => {
if (!field.interface) {
return;
}
const fieldInterface = getInterface(field.interface);
if (!fieldInterface?.filterable) {
return;
}
const { nested, children, operators } = fieldInterface.filterable;
const option = {
name: field.name,
title: field?.uiSchema?.title || field.name,
schema: field?.uiSchema,
operators:
operators?.filter?.((operator) => {
return !operator?.visible || operator.visible(field);
}) || [],
interface: field.interface,
};
if (field.target && depth > 2) {
return;
}
if (depth > 2) {
return option;
}
if (children?.length) {
option['children'] = children;
}
if (nested) {
const targetFields = getCollectionFields(field.target);
const options = getOptions(targetFields, depth + 1).filter(Boolean);
option['children'] = option['children'] || [];
option['children'].push(...options);
}
return option;
};
const getOptions = (fields, depth) => {
const options = [];
fields.forEach((field) => {
const option = field2option(field, depth);
if (option) {
options.push(option);
}
});
return options;
};
const options = getOptions(fields, 1);
return options;
};
export const SetFilterScope = (props) => {
const { t } = useTranslation();
const fieldSchema = useFieldSchema();
const field = useField<Field>();
const fields = field.form.fields;
const { collectionName } = props;
const gridSchema = findGridSchema(fieldSchema) || fieldSchema;
const { form } = useFormBlockContext();
const type = props?.type || ['Action', 'Action.Link'].includes(fieldSchema['x-component']) ? 'button' : 'field';
const variables = useVariables();
const localVariables = useLocalVariables();
const record = useRecord();
const { type: formBlockType } = useFormBlockType();
const schema = useMemo<ISchema>(
() => ({
type: 'object',
title: t('Custom filter'),
properties: {
fieldReaction: {
'x-component': FormFilterScope,
'x-component-props': {
useProps: () => {
const options = useCollectionFilterOptions(collectionName);
return {
options,
defaultValues: gridSchema?.['x-filter-rules'] || fieldSchema?.['x-filter-rules'],
type,
collectionName,
form,
variables,
localVariables,
record,
formBlockType,
fields,
};
},
},
},
},
}),
[],
);
const { getTemplateById } = useSchemaTemplateManager();
const { dn } = useDesignable();
const onSubmit = useCallback(
(v) => {
const rules = v.fieldReaction.condition;
const templateId = gridSchema['x-component'] === 'BlockTemplate' && gridSchema['x-component-props'].templateId;
const uid = (templateId && getTemplateById(templateId).uid) || gridSchema['x-uid'];
const schema = {
['x-uid']: uid,
};
gridSchema['x-filter-rules'] = rules;
schema['x-filter-rules'] = rules;
dn.emit('patch', {
schema,
});
dn.refresh();
},
[dn, getTemplateById, gridSchema],
);
return <SchemaSettingsModalItem title={t('Custom filter')} width={770} schema={schema} onSubmit={onSubmit} />;
};
// 添加跳转页面选项 // 添加跳转页面选项
export const PageModeSetting = () => { export const PageModeSetting = () => {
const { dn } = useDesignable(); const { dn } = useDesignable();
@ -540,74 +124,3 @@ export const PageModeSetting = () => {
/> />
); );
}; };
export const SchemaSettingComponent = () => {
const fieldSchema = useFieldSchema();
const field = useField();
const { options } = useFieldComponents();
const { dn } = useDesignable();
return (
<SchemaSettingsSelectItem
key="component-field"
title="Field Component"
options={options}
value={fieldSchema['x-component']}
onChange={(mode) => {
field.component = mode;
fieldSchema['x-component'] = mode;
void dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
['x-component']: fieldSchema['x-component'],
},
});
dn.refresh();
}}
/>
);
};
export const SchemaSettingCollection = () => {
const fieldSchema = useFieldSchema();
const collections = useCollectionManager();
const field = useField<Field>();
const options = collections?.dataSource['options']?.collections.map((value) => {
return {
label: value.title,
value: value.name,
};
});
const cm = useCollectionManager();
const { dn } = useDesignable();
return (
<SchemaSettingsSelectItem
key="component-field"
title="Edit Collection"
options={options}
value={fieldSchema['collectionName']}
onChange={(name) => {
field.setValue('');
const titleField = cm.getCollection(name).titleField;
fieldSchema['collectionName'] = name;
fieldSchema.default = '';
fieldSchema['x-component-props'] = {
...fieldSchema['x-component-props'],
fieldNames: {
label: titleField,
value: titleField,
},
defaultValue: '',
};
void dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
collectionName: fieldSchema['collectionName'],
['x-component-props']: fieldSchema['x-component-props'],
default: '',
},
});
dn.refresh();
}}
/>
);
};