feat: support to use variables to set default value (#1726)

* fix: fix the default value being overwriitten

* feat: supports association fields to set default value

* feat: support to toggle multiple

* feat: support to use variables to set default value

* refactor: migrate hooks

* fix: fix the title disappearing

* feat: suport to use current-user variable

* fix: fix error

* fix: one to one and one to many should not support to set default value

* fix: fix error when searching

* style: set the width of modal

* fix: should not support to set default value for system fields

* refactor: fix lint error

* style: optimize inputNumber width

* refactor: extract to one function

* refactor: extract function

* fix: should not display default when field is readPretty

* style: optimize width of select

* fix: avoid error

* fix: should not display multiple option when subtable

* fix: should not display default when attachment

* fix: chinaRegion

* chore: optimize code

* fix: build error

* fix: multiple should be true by default

* refactor: optimize code

* fix: fix Select multiple mode

* refactor: optimize code

* fix: width
This commit is contained in:
被雨水过滤的空气-Rairn 2023-04-23 18:27:21 +08:00 committed by GitHub
parent eac3c6f0c8
commit b1068f2d51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 878 additions and 458 deletions

View File

@ -1,6 +1,7 @@
import { createForm, onFormValuesChange } from '@formily/core';
import { useField } from '@formily/react';
import { autorun } from '@formily/reactive';
import { forEach } from '@nocobase/utils/client';
import { Spin } from 'antd';
import React, { createContext, useContext, useEffect, useMemo } from 'react';
import { RecordProvider } from '../record-provider';
@ -35,7 +36,14 @@ const InternalFormFieldProvider = (props) => {
// 当使用数据模板时,会 formBlockCtx.form.values 会被整体赋值,这时候需要同步到 form.values
useEffect(() => {
const dispose = autorun(() => {
form.values = formBlockCtx?.form?.values[fieldName] || form.values;
const data = formBlockCtx?.form?.values[fieldName] || {};
// 先清空表单值,再赋值,避免当值为空时,表单未被清空
form.reset();
forEach(data, (value, key) => {
if (value) {
form.values[key] = value;
}
});
});
return dispose;

View File

@ -7,12 +7,18 @@ import { useActionContext, useCompile, useComponent, useFormBlockContext, useRec
import { CollectionFieldProvider } from './CollectionFieldProvider';
import { useCollectionField } from './hooks';
type Props = {
component: any;
children?: React.ReactNode;
};
// TODO: 初步适配
const InternalField: React.FC = (props) => {
const InternalField: React.FC = (props: Props) => {
const { component } = props;
const field = useField<Field>();
const fieldSchema = useFieldSchema();
const { uiSchema, defaultValue } = useCollectionField();
const component = useComponent(uiSchema?.['x-component'] || 'Input');
const Component = useComponent(component || uiSchema?.['x-component'] || 'Input');
const compile = useCompile();
const setFieldProps = (key, value) => {
field[key] = typeof field[key] === 'undefined' ? value : field[key];
@ -57,12 +63,12 @@ const InternalField: React.FC = (props) => {
field.dataSource = uiSchema.enum;
const originalProps = compile(uiSchema['x-component-props']) || {};
const componentProps = merge(originalProps, field.componentProps || {});
field.component = [component, componentProps];
field.component = [Component, componentProps];
}, [JSON.stringify(uiSchema)]);
if (!uiSchema) {
return null;
}
return React.createElement(component, props, props.children);
return React.createElement(Component, props, props.children);
};
export const InternalFallbackField = () => {

View File

@ -13,8 +13,8 @@ import { useResourceActionContext, useResourceContext } from '../ResourceActionP
import { useCancelAction } from '../action-hooks';
import { useCollectionManager } from '../hooks';
import * as components from './components';
import { templateOptions } from './templates';
import { TemplateSummay } from './components/TemplateSummay';
import { templateOptions } from './templates';
const getSchema = (schema, category, compile): ISchema => {
if (!schema) {

View File

@ -32,9 +32,14 @@ export const multipleSelect: IField = {
operators: operators.array,
},
schemaInitialize(schema: ISchema, { block }) {
const props = (schema['x-component-props'] = schema['x-component-props'] || {});
props.style = {
...(props.style || {}),
width: '100%',
};
if (['Table', 'Kanban'].includes(block)) {
schema['x-component-props'] = schema['x-component-props'] || {};
schema['x-component-props']['ellipsis'] = true;
props['ellipsis'] = true;
}
},
};

View File

@ -1,4 +1,5 @@
import { registerValidateRules } from '@formily/core';
import { ISchema } from '@formily/react';
import { i18n } from '../../i18n';
import { defaultProps, operators, unique } from './properties';
import { IField } from './types';
@ -67,6 +68,13 @@ export const percent: IField = {
},
},
},
schemaInitialize(schema: ISchema, { field, block, readPretty, action }) {
const props = (schema['x-component-props'] = schema['x-component-props'] || {});
schema['x-component-props'].style = {
...(props.style || {}),
width: '100%',
};
},
availableTypes: ['float'],
hasDefaultValue: true,
properties: {

View File

@ -29,9 +29,14 @@ export const select: IField = {
operators: operators.enumType,
},
schemaInitialize(schema: ISchema, { block }) {
const props = (schema['x-component-props'] = schema['x-component-props'] || {});
props.style = {
...(props.style || {}),
width: '100%',
};
if (['Table', 'Kanban'].includes(block)) {
schema['x-component-props'] = schema['x-component-props'] || {};
schema['x-component-props']['ellipsis'] = true;
props['ellipsis'] = true;
}
},
};

View File

@ -14,7 +14,7 @@ import {
useSortFields,
} from '../../../collection-manager';
import { isTitleField } from '../../../collection-manager/Configuration/CollectionFields';
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
import { GeneralSchemaDesigner, SchemaSettings, isPatternDisabled, isShowDefaultValue } from '../../../schema-settings';
import { useCompile, useDesignable, useFieldComponentOptions, useFieldTitle } from '../../hooks';
import { removeNullCondition } from '../filter';
import { RemoteSelect, RemoteSelectProps } from '../remote-select';
@ -94,7 +94,7 @@ export const AssociationSelect = InternalAssociationSelect as unknown as Associa
AssociationSelect.Designer = function Designer() {
const { getCollectionFields, getInterface, getCollectionJoinField, getCollection } = useCollectionManager();
const { getField, template } = useCollection();
const { getField } = useCollection();
const { form } = useFormBlockContext();
const field = useField<Field>();
const fieldSchema = useFieldSchema();
@ -390,41 +390,52 @@ AssociationSelect.Designer = function Designer() {
}}
/>
)}
{form && !form?.readPretty && collectionField?.uiSchema?.type && (
<SchemaSettings.ModalItem
title={t('Set default value')}
components={{ ArrayCollapse, FormLayout }}
schema={
{
type: 'object',
title: t('Set default value'),
properties: {
default: {
...collectionField.uiSchema,
name: 'default',
title: t('Default value'),
'x-decorator': 'FormItem',
default: fieldSchema.default || collectionField.defaultValue,
{form &&
!form?.readPretty &&
isShowDefaultValue(collectionField, getInterface) &&
!isPatternDisabled(fieldSchema) && (
<SchemaSettings.ModalItem
title={t('Set default value')}
components={{ ArrayCollapse, FormLayout }}
width={800}
schema={
{
type: 'object',
title: t('Set default value'),
properties: {
default: {
...(fieldSchema || {}),
'x-decorator': 'FormItem',
'x-component-props': {
...fieldSchema['x-component-props'],
component: collectionField?.target ? 'AssociationSelect' : undefined,
service: {
resource: collectionField?.target,
},
},
name: 'default',
title: t('Default value'),
default: fieldSchema.default || collectionField.defaultValue,
},
},
},
} as ISchema
}
onSubmit={(v) => {
const schema: ISchema = {
['x-uid']: fieldSchema['x-uid'],
};
if (field.value !== v.default) {
field.value = v.default;
} as ISchema
}
fieldSchema.default = v.default;
schema.default = v.default;
dn.emit('patch', {
schema,
});
refresh();
}}
/>
)}
onSubmit={(v) => {
const schema: ISchema = {
['x-uid']: fieldSchema['x-uid'],
};
if (field.value !== v.default) {
field.value = v.default;
}
fieldSchema.default = v.default;
schema.default = v.default;
dn.emit('patch', {
schema,
});
refresh();
}}
/>
)}
{form && !isSubFormAssociationField && fieldComponentOptions && (
<SchemaSettings.SelectItem
title={t('Field component')}
@ -474,6 +485,36 @@ AssociationSelect.Designer = function Designer() {
}}
/>
)}
{form &&
!form?.readPretty &&
['o2m', 'm2m'].includes(collectionField.interface) &&
fieldSchema['x-component'] !== 'TableField' && (
<SchemaSettings.SwitchItem
key="multiple"
title={t('Multiple')}
checked={
fieldSchema['x-component-props']?.multiple === undefined
? true
: fieldSchema['x-component-props'].multiple
}
onChange={(value) => {
const schema = {
['x-uid']: fieldSchema['x-uid'],
};
fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
field.componentProps = field.componentProps || {};
fieldSchema['x-component-props'].multiple = value;
field.componentProps.multiple = value;
schema['x-component-props'] = fieldSchema['x-component-props'];
dn.emit('patch', {
schema,
});
refresh();
}}
/>
)}
<SchemaSettings.ModalItem
title={t('Set the data scope')}
schema={
@ -594,7 +635,7 @@ AssociationSelect.Designer = function Designer() {
});
}}
/>
{form && !form?.readPretty && fieldSchema?.['x-component-props']?.['pattern-disable'] != true && (
{form && !form?.readPretty && !isPatternDisabled(fieldSchema) && (
<SchemaSettings.SelectItem
key="pattern"
title={t('Pattern')}

View File

@ -7,13 +7,66 @@ import { useBlockRequestContext } from '../../../block-provider';
import { mergeFilter } from '../../../block-provider/SharedFilterProvider';
import { useCollection, useCollectionManager } from '../../../collection-manager';
export const useFilterOptions = (collectionName: string) => {
export const useGetFilterOptions = () => {
const { getCollectionFields } = useCollectionManager();
const fields = getCollectionFields(collectionName);
const options=useFilterFieldOptions(fields)
const compile = useCompile();
const { getChildrenCollections } = useCollectionManager();
const collection = useCollection();
const getFilterFieldOptions = useGetFilterFieldOptions();
return (collectionName) => {
const fields = getCollectionFields(collectionName);
const options = getFilterFieldOptions(fields);
const childrenCollections = getChildrenCollections(collection.name);
if (childrenCollections.length > 0 && !options.find((v) => v.name == 'tableoid')) {
options.push({
name: 'tableoid',
type: 'string',
title: '{{t("Table OID(Inheritance)")}}',
schema: {
'x-component': 'Select',
enum: [{ value: collection.name, label: compile(collection.title) }].concat(
childrenCollections.map((v) => {
return {
value: v.name,
label: compile(v.title),
};
}),
),
},
operators: [
{
label: '{{t("contains")}}',
value: '$childIn',
schema: {
'x-component': 'Select',
'x-component-props': { mode: 'tags' },
},
},
{
label: '{{t("does not contain")}}',
value: '$childNotIn',
schema: {
'x-component': 'Select',
'x-component-props': { mode: 'tags' },
},
},
],
});
}
return options;
};
};
export const useFilterOptions = (collectionName: string) => {
const { getCollectionFields } = useCollectionManager();
const compile = useCompile();
const { getChildrenCollections } = useCollectionManager();
const collection = useCollection();
const fields = getCollectionFields(collectionName);
const options = useFilterFieldOptions(fields);
const childrenCollections = getChildrenCollections(collection.name);
if (childrenCollections.length > 0 && !options.find((v) => v.name == 'tableoid')) {
options.push({
@ -54,6 +107,63 @@ export const useFilterOptions = (collectionName: string) => {
return options;
};
export const useGetFilterFieldOptions = () => {
const fieldSchema = useFieldSchema();
const nonfilterable = fieldSchema?.['x-component-props']?.nonfilterable || [];
const { getCollectionFields, getInterface } = useCollectionManager();
const field2option = (field, depth) => {
if (nonfilterable.length && depth === 1 && nonfilterable.includes(field.name)) {
return;
}
if (!field.interface) {
return;
}
const fieldInterface = getInterface(field.interface);
if (!fieldInterface.filterable) {
return;
}
const { nested, children, operators } = fieldInterface.filterable;
const option = {
name: field.name,
type: field.type,
target: field.target,
title: field?.uiSchema?.title || field.name,
schema: field?.uiSchema,
operators:
operators?.filter?.((operator) => {
return !operator?.visible || operator.visible(field);
}) || [],
};
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;
};
return (fields) => getOptions(fields, 1);
};
export const useFilterFieldOptions = (fields) => {
const fieldSchema = useFieldSchema();
const nonfilterable = fieldSchema?.['x-component-props']?.nonfilterable || [];

View File

@ -22,7 +22,8 @@ const findOption = (dataIndex = [], options) => {
export const useValues = () => {
const field = useField<any>();
const { options } = useContext(FilterContext);
const { options } = useContext(FilterContext) || {};
const data2value = () => {
field.value = flat.unflatten({
[`${field.data.dataIndex?.join('.')}.${field.data?.operator?.value}`]: field.data?.value,
@ -32,7 +33,7 @@ export const useValues = () => {
field.data = field.data || {};
const values = flat(field.value);
const path = Object.keys(values).shift() || '';
if (!path) {
if (!path || !options) {
return;
}
const [fieldPath = '', otherPath = ''] = path.split('.$');
@ -50,7 +51,7 @@ export const useValues = () => {
useEffect(value2data, [field.path.entire]);
return {
fields: options,
...field.data,
...(field?.data || {}),
setDataIndex(dataIndex) {
const option = findOption(dataIndex, options);
const operator = option?.operators?.[0];

View File

@ -1,16 +1,19 @@
import { css } from '@emotion/css';
import { ArrayCollapse, FormLayout, FormItem as Item } from '@formily/antd';
import { Field } from '@formily/core';
import { ISchema, observer, useField, useFieldSchema } from '@formily/react';
import { ISchema, Schema, observer, useField, useFieldSchema } from '@formily/react';
import { uid } from '@formily/shared';
import _ from 'lodash';
import React, { useContext, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { ACLCollectionFieldProvider } from '../../../acl/ACLProvider';
import { BlockRequestContext, useFilterByTk, useFormBlockContext } from '../../../block-provider';
import { Collection, useCollection, useCollectionManager } from '../../../collection-manager';
import { Collection, CollectionFieldOptions, useCollection, useCollectionManager } from '../../../collection-manager';
import { isTitleField } from '../../../collection-manager/Configuration/CollectionFields';
import { GeneralSchemaDesigner, SchemaSettings } from '../../../schema-settings';
import { GeneralSchemaDesigner, SchemaSettings, isPatternDisabled, isShowDefaultValue } from '../../../schema-settings';
import { VariableInput } from '../../../schema-settings/VariableInput/VariableInput';
import { isVariable, parseVariables, useVariablesCtx } from '../../common/utils/uitls';
import { SchemaComponent } from '../../core';
import { useCompile, useDesignable, useFieldComponentOptions } from '../../hooks';
import { BlockItem } from '../block-item';
import { HTMLEncode } from '../input/shared';
@ -33,15 +36,20 @@ const divWrap = (schema: ISchema) => {
export const FormItem: any = observer((props: any) => {
useEnsureOperatorsValid();
const field = useField();
const field = useField<Field>();
const ctx = useContext(BlockRequestContext);
const schema = useFieldSchema();
const variablesCtx = useVariablesCtx();
useEffect(() => {
if (ctx?.block === 'form') {
ctx.field.data = ctx.field.data || {};
ctx.field.data.activeFields = ctx.field.data.activeFields || new Set();
ctx.field.data.activeFields.add(schema.name);
// 如果默认值是一个变量,则需要解析之后再显示出来
if (isVariable(schema?.default)) {
field.setInitialValue?.(parseVariables(schema.default, variablesCtx));
}
}
}, []);
return (
@ -81,6 +89,8 @@ FormItem.Designer = function Designer() {
const { t } = useTranslation();
const { dn, refresh, insertAdjacent } = useDesignable();
const compile = useCompile();
const variablesCtx = useVariablesCtx();
const collectionField = getField(fieldSchema['name']) || getCollectionJoinField(fieldSchema['x-collection-field']);
const targetCollection = getCollection(collectionField?.target);
const interfaceConfig = getInterface(collectionField?.interface);
@ -396,41 +406,91 @@ FormItem.Designer = function Designer() {
}}
/>
)}
{form && !form?.readPretty && collectionField?.uiSchema?.type && (
<SchemaSettings.ModalItem
title={t('Set default value')}
components={{ ArrayCollapse, FormLayout }}
schema={
{
type: 'object',
title: t('Set default value'),
properties: {
default: {
...collectionField?.uiSchema,
name: 'default',
title: t('Default value'),
'x-decorator': 'FormItem',
default: fieldSchema.default || collectionField?.defaultValue,
{form &&
!form?.readPretty &&
isShowDefaultValue(collectionField, getInterface) &&
!isPatternDisabled(fieldSchema) && (
<SchemaSettings.ModalItem
title={t('Set default value')}
components={{ ArrayCollapse, FormLayout, VariableInput }}
width={800}
schema={
{
type: 'object',
title: t('Set default value'),
properties: {
// 关系字段不支持设置变量
default: collectionField?.target
? {
...(fieldSchema || {}),
'x-decorator': 'FormItem',
'x-component-props': {
...fieldSchema['x-component-props'],
component:
collectionField?.target && collectionField.interface !== 'chinaRegion'
? 'AssociationSelect'
: undefined,
service: {
resource: collectionField?.target,
},
},
name: 'default',
title: t('Default value'),
default: getFieldDefaultValue(fieldSchema, collectionField),
}
: {
...(fieldSchema || {}),
'x-decorator': 'FormItem',
'x-component': 'VariableInput',
'x-component-props': {
...(fieldSchema?.['x-component-props'] || {}),
collectionName: collectionField?.collectionName,
schema: collectionField?.uiSchema,
renderSchemaComponent: function Com(props) {
const s = _.cloneDeep(fieldSchema) || ({} as Schema);
s.title = '';
return (
<SchemaComponent
schema={{
...(s || {}),
'x-component-props': {
...s['x-component-props'],
onChange: props.onChange,
value: props.value,
defaultValue: getFieldDefaultValue(s, collectionField),
style: {
width: '100%',
},
},
}}
/>
);
},
},
name: 'default',
title: t('Default value'),
default: getFieldDefaultValue(fieldSchema, collectionField),
},
},
},
} as ISchema
}
onSubmit={(v) => {
const schema: ISchema = {
['x-uid']: fieldSchema['x-uid'],
};
if (field.value !== v.default) {
field.value = v.default;
} as ISchema
}
fieldSchema.default = v.default;
schema.default = v.default;
dn.emit('patch', {
schema,
});
refresh();
}}
/>
)}
onSubmit={(v) => {
const schema: ISchema = {
['x-uid']: fieldSchema['x-uid'],
};
if (field.value !== v.default) {
field.value = parseVariables(v.default, variablesCtx);
}
fieldSchema.default = v.default;
schema.default = v.default;
dn.emit('patch', {
schema,
});
refresh();
}}
/>
)}
{form && !isSubFormAssociationField && fieldComponentOptions && (
<SchemaSettings.SelectItem
title={t('Field component')}
@ -476,6 +536,36 @@ FormItem.Designer = function Designer() {
}}
/>
)}
{form &&
!form?.readPretty &&
['o2m', 'm2m'].includes(collectionField.interface) &&
fieldSchema['x-component'] !== 'TableField' && (
<SchemaSettings.SwitchItem
key="multiple"
title={t('Multiple')}
checked={
fieldSchema['x-component-props']?.multiple === undefined
? true
: fieldSchema['x-component-props'].multiple
}
onChange={(value) => {
const schema = {
['x-uid']: fieldSchema['x-uid'],
};
fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
field.componentProps = field.componentProps || {};
fieldSchema['x-component-props'].multiple = value;
field.componentProps.multiple = value;
schema['x-component-props'] = fieldSchema['x-component-props'];
dn.emit('patch', {
schema,
});
refresh();
}}
/>
)}
{field.readPretty && options.length > 0 && fieldSchema['x-component'] === 'CollectionField' && (
<SchemaSettings.SwitchItem
title={t('Enable link')}
@ -497,61 +587,58 @@ FormItem.Designer = function Designer() {
}}
/>
)}
{form &&
!form?.readPretty &&
collectionField?.interface !== 'o2m' &&
fieldSchema?.['x-component-props']?.['pattern-disable'] != true && (
<SchemaSettings.SelectItem
key="pattern"
title={t('Pattern')}
options={[
{ label: t('Editable'), value: 'editable' },
{ label: t('Readonly'), value: 'readonly' },
{ label: t('Easy-reading'), value: 'read-pretty' },
]}
value={readOnlyMode}
onChange={(v) => {
const schema: ISchema = {
['x-uid']: fieldSchema['x-uid'],
};
{form && !form?.readPretty && collectionField?.interface !== 'o2m' && !isPatternDisabled(fieldSchema) && (
<SchemaSettings.SelectItem
key="pattern"
title={t('Pattern')}
options={[
{ label: t('Editable'), value: 'editable' },
{ label: t('Readonly'), value: 'readonly' },
{ label: t('Easy-reading'), value: 'read-pretty' },
]}
value={readOnlyMode}
onChange={(v) => {
const schema: ISchema = {
['x-uid']: fieldSchema['x-uid'],
};
switch (v) {
case 'readonly': {
fieldSchema['x-read-pretty'] = false;
fieldSchema['x-disabled'] = true;
schema['x-read-pretty'] = false;
schema['x-disabled'] = true;
field.readPretty = false;
field.disabled = true;
break;
}
case 'read-pretty': {
fieldSchema['x-read-pretty'] = true;
fieldSchema['x-disabled'] = false;
schema['x-read-pretty'] = true;
schema['x-disabled'] = false;
field.readPretty = true;
break;
}
default: {
fieldSchema['x-read-pretty'] = false;
fieldSchema['x-disabled'] = false;
schema['x-read-pretty'] = false;
schema['x-disabled'] = false;
field.readPretty = false;
field.disabled = false;
break;
}
switch (v) {
case 'readonly': {
fieldSchema['x-read-pretty'] = false;
fieldSchema['x-disabled'] = true;
schema['x-read-pretty'] = false;
schema['x-disabled'] = true;
field.readPretty = false;
field.disabled = true;
break;
}
case 'read-pretty': {
fieldSchema['x-read-pretty'] = true;
fieldSchema['x-disabled'] = false;
schema['x-read-pretty'] = true;
schema['x-disabled'] = false;
field.readPretty = true;
break;
}
default: {
fieldSchema['x-read-pretty'] = false;
fieldSchema['x-disabled'] = false;
schema['x-read-pretty'] = false;
schema['x-disabled'] = false;
field.readPretty = false;
field.disabled = false;
break;
}
}
dn.emit('patch', {
schema,
});
dn.emit('patch', {
schema,
});
dn.refresh();
}}
/>
)}
dn.refresh();
}}
/>
)}
{options.length > 0 && fieldSchema['x-component'] === 'CollectionField' && (
<SchemaSettings.SelectItem
key="title-field"
@ -597,3 +684,7 @@ function isFileCollection(collection: Collection) {
}
FormItem.FilterFormDesigner = FilterFormDesigner;
export function getFieldDefaultValue(fieldSchema: ISchema, collectionField: CollectionFieldOptions) {
return fieldSchema?.default || collectionField?.defaultValue;
}

View File

@ -7,7 +7,7 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import { useFilterByTk, useFormBlockContext } from '../../../block-provider';
import { useCollection, useCollectionManager } from '../../../collection-manager';
import { SchemaSettings } from '../../../schema-settings';
import { SchemaSettings, isPatternDisabled } from '../../../schema-settings';
import { useCompile, useDesignable, useFieldComponentOptions } from '../../hooks';
import { useOperatorList } from '../filter/useOperators';
@ -448,10 +448,7 @@ export const EditPattern = () => {
readOnlyMode = 'read-pretty';
}
return form &&
!form?.readPretty &&
collectionField?.interface !== 'o2m' &&
fieldSchema?.['x-component-props']?.['pattern-disable'] != true ? (
return form && !form?.readPretty && collectionField?.interface !== 'o2m' && !isPatternDisabled(fieldSchema) ? (
<SchemaSettings.SelectItem
key="pattern"
title={t('Pattern')}

View File

@ -1,10 +1,12 @@
import { useFieldSchema } from '@formily/react';
import { forEach } from '@nocobase/utils/client';
import { Select } from 'antd';
import _ from 'lodash';
import React, { useCallback, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useAPIClient } from '../../../api-client';
import { findFormBlock } from '../../../block-provider';
import { useCollectionManager } from '../../../collection-manager';
interface ITemplate {
items: {
@ -40,26 +42,41 @@ const useDataTemplates = () => {
export const Templates = ({ style = {}, form }) => {
const { templates, display, enabled, defaultTemplate } = useDataTemplates();
const { getCollectionField } = useCollectionManager();
const [value, setValue] = React.useState(defaultTemplate?.key || 'none');
const api = useAPIClient();
const { t } = useTranslation();
useEffect(() => {
if (defaultTemplate) {
fetchTemplateData(api, defaultTemplate).then((data) => {
if (form) {
form.values = data;
}
});
fetchTemplateData(api, defaultTemplate)
.then((data) => {
if (form) {
forEach(data, (value, key) => {
if (value) {
form.values[key] = value;
}
});
}
})
.catch((err) => {
console.error(err);
});
}
}, []);
const handleChange = useCallback(async (value, option) => {
setValue(value);
if (option.key !== 'none') {
if (form) {
form.values = await fetchTemplateData(api, option);
}
fetchTemplateData(api, option).then((data) => {
if (form) {
forEach(data, (value, key) => {
if (value) {
form.values[key] = value;
}
});
}
});
} else {
form?.reset();
}

View File

@ -5,7 +5,7 @@ import { isValid } from '@formily/shared';
import { Button, Input, Popover } from 'antd';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { hasIcon, Icon, icons } from '../../../icon';
import { Icon, hasIcon, icons } from '../../../icon';
function IconField(props: any) {
const layout = useFormLayout();

View File

@ -236,7 +236,6 @@ const Drawer: React.FunctionComponent<{
fieldSchema,
options,
}) => {
console.log('collectionField', options);
const getFilter = () => {
const targetKey = collectionField.targetKey || 'id';
const list = options.map((option) => option[targetKey]).filter(Boolean);

View File

@ -38,7 +38,7 @@ const InternalRemoteSelect = connect(
const firstRun = useRef(false);
const fieldSchema = useFieldSchema();
const { getField } = useCollection();
const { getCollectionJoinField } = useCollectionManager();
const { getCollectionJoinField, getInterface } = useCollectionManager();
const collectionField = getField(fieldSchema.name);
const targetField =
_targetField ||
@ -46,6 +46,13 @@ const InternalRemoteSelect = connect(
fieldNames?.label &&
getCollectionJoinField(`${collectionField.target}.${fieldNames.label}`));
const operator = useMemo(() => {
if (targetField?.interface) {
return getInterface(targetField.interface)?.filterable?.operators[0].value || '$includes';
}
return '$includes';
}, [targetField]);
const mapOptionsToTags = useCallback(
(options) => {
try {
@ -138,7 +145,7 @@ const InternalRemoteSelect = connect(
filter: mergeFilter([
{
[fieldNames.label]: {
$includes: search,
[operator]: search,
},
},
service?.params?.filter,

View File

@ -71,24 +71,18 @@ const InternalSelect = connect(
(props: Props) => {
const { objectValue, value, ...others } = props;
let mode: any = props.multiple ? 'multiple' : props.mode;
if (mode === 'links') {
if (mode && !['multiple', 'tags'].includes(mode)) {
mode = undefined;
}
const toValue = (v) => {
if (['multiple', 'tags'].includes(mode)) {
return v || [];
}
return v;
};
if (objectValue) {
return <ObjectSelect {...others} value={toValue(value)} mode={mode} />;
return <ObjectSelect {...others} value={value} mode={mode} />;
}
return (
<AntdSelect
showSearch
filterOption={filterOption}
allowClear
value={toValue(value)}
value={value}
{...others}
onChange={(changed) => {
props.onChange?.(changed === undefined ? null : changed);

View File

@ -1,172 +1,13 @@
import React from 'react';
import { useCompile } from '../..';
import { useValues } from '../filter/useValues';
import { useVariableOptions } from '../../../schema-settings/VariableInput/hooks/useVariableOptions';
import { Variable } from '../variable';
import { useUserVariable } from './hooks/useUserVariable';
const useVariableOptions = () => {
const { operator, schema } = useValues();
const operatorValue = operator?.value || '';
const userVariable = useUserVariable({ schema, operator });
if (!operator || !schema) return [];
const disabled = !['DatePicker', 'DatePicker.RangePicker'].includes(schema['x-component']);
const dateOptions = [
{
key: 'now',
value: 'now',
label: `{{t("Now")}}`,
disabled: schema['x-component'] !== 'DatePicker' || operatorValue === '$dateBetween',
},
{
key: 'yesterday',
value: 'yesterday',
label: `{{t("Yesterday")}}`,
disabled,
},
{
key: 'today',
value: 'today',
label: `{{t("Today")}}`,
disabled,
},
{
key: 'tomorrow',
value: 'tomorrow',
label: `{{t("Tomorrow")}}`,
disabled,
},
{
key: 'lastIsoWeek',
value: 'lastIsoWeek',
label: `{{t("Last week")}}`,
disabled,
},
{
key: 'thisIsoWeek',
value: 'thisIsoWeek',
label: `{{t("This week")}}`,
disabled,
},
{
key: 'nextIsoWeek',
value: 'nextIsoWeek',
label: `{{t("Next week")}}`,
disabled,
},
{
key: 'lastMonth',
value: 'lastMonth',
label: `{{t("Last month")}}`,
disabled,
},
{
key: 'thisMonth',
value: 'thisMonth',
label: `{{t("This month")}}`,
disabled,
},
{
key: 'nextMonth',
value: 'nextMonth',
label: `{{t("Next month")}}`,
disabled,
},
{
key: 'lastQuarter',
value: 'lastQuarter',
label: `{{t("Last quarter")}}`,
disabled,
},
{
key: 'thisQuarter',
value: 'thisQuarter',
label: `{{t("This quarter")}}`,
disabled,
},
{
key: 'nextQuarter',
value: 'nextQuarter',
label: `{{t("Next quarter")}}`,
disabled,
},
{
key: 'lastYear',
value: 'lastYear',
label: `{{t("Last year")}}`,
disabled,
},
{
key: 'thisYear',
value: 'thisYear',
label: `{{t("This year")}}`,
disabled,
},
{
key: 'nextYear',
value: 'nextYear',
label: `{{t("Next year")}}`,
disabled,
},
{
key: 'last7Days',
value: 'last7Days',
label: `{{t("Last 7 days")}}`,
disabled,
},
{
key: 'next7Days',
value: 'next7Days',
label: `{{t("Next 7 days")}}`,
disabled,
},
{
key: 'last30Days',
value: 'last30Days',
label: `{{t("Last 30 days")}}`,
disabled,
},
{
key: 'next30Days',
value: 'next30Days',
label: `{{t("Next 30 days")}}`,
disabled,
},
{
key: 'last90Days',
value: 'last90Days',
label: `{{t("Last 90 days")}}`,
disabled,
},
{
key: 'next90Days',
value: 'next90Days',
label: `{{t("Next 90 days")}}`,
disabled,
},
];
return [
userVariable,
{
label: `{{t("Date variables")}}`,
value: '$date',
key: '$date',
disabled: dateOptions.every((option) => option.disabled),
children: dateOptions,
},
];
};
export function FilterDynamicComponent(props) {
const { value, onChange, renderSchemaComponent } = props;
const options = useVariableOptions();
const compile = useCompile();
const scope = compile(options);
return (
<Variable.Input value={value} onChange={onChange} scope={scope}>
<Variable.Input value={value} onChange={onChange} scope={options}>
{renderSchemaComponent()}
</Variable.Input>
);

View File

@ -4,10 +4,10 @@ import { css } from '@emotion/css';
import { ArrayField, Field } from '@formily/core';
import { RecursionField, Schema, observer, useField, useFieldSchema } from '@formily/react';
import { reaction } from '@formily/reactive';
import { useEventListener, useMemoizedFn } from 'ahooks';
import { useMemoizedFn } from 'ahooks';
import { Table as AntdTable, TableColumnProps } from 'antd';
import { default as classNames, default as cls } from 'classnames';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { DndContext, useDesignable, useTableSize } from '../..';
import {
@ -213,7 +213,7 @@ export const Table: any = observer((props: any) => {
useEffect(() => {
if (treeTable !== false) {
const keys = getIdsWithChildren(field.value?.slice());
const keys = getIdsWithChildren(field.value?.slice?.());
setAllIncludesChildren(keys);
}
}, [field.value]);

View File

@ -1,56 +0,0 @@
import { useFilterOptions } from '../../filter';
interface GetOptionsParams {
schema: any;
operator: string;
maxDepth: number;
count?: number;
}
const useOptions = (collectionName: string, { schema, operator, maxDepth, count = 1 }: GetOptionsParams) => {
if (count > maxDepth) {
return [];
}
const result = useFilterOptions(collectionName).map((option) => {
if ((option.type !== 'belongsTo' && option.type !== 'hasOne') || !option.target) {
return {
key: option.name,
value: option.name,
label: option.title,
// TODO: 现在是通过组件的名称来过滤能够被选择的选项,这样的坏处是不够精确,后续可以优化
disabled: schema?.['x-component'] !== option.schema?.['x-component'],
};
}
const children =
useOptions(option.target, {
schema,
operator,
maxDepth,
count: count + 1,
}) || [];
return {
key: option.name,
value: option.name,
label: option.title,
children,
disabled: children.every((child) => child.disabled),
};
});
return result;
};
export const useUserVariable = ({ schema, operator }) => {
const options = useOptions('users', { schema, operator, maxDepth: 3 }) || [];
return {
label: `{{t("Current user")}}`,
value: '$user',
key: '$user',
disabled: options.every((option) => option.disabled),
children: options,
};
};

View File

@ -1,7 +1,7 @@
import { CloseCircleFilled } from '@ant-design/icons';
import { css, cx } from '@emotion/css';
import { useForm } from '@formily/react';
import { Button, Cascader, DatePicker, Input as AntInput, InputNumber, Select, Tag } from 'antd';
import { Input as AntInput, Cascader, DatePicker, InputNumber, Select, Tag } from 'antd';
import moment from 'moment';
import React from 'react';
import { useTranslation } from 'react-i18next';
@ -117,7 +117,7 @@ export function Input(props) {
const compile = useCompile();
const form = useForm();
const { value = '', scope, onChange, children, button, useTypedConstant } = props;
const { value = '', scope, onChange, children, button, useTypedConstant, style } = props;
const parsed = parseValue(value);
const isConstant = typeof parsed === 'string';
const type = isConstant ? parsed : '';
@ -171,6 +171,7 @@ export function Input(props) {
return (
<AntInput.Group
compact
style={style}
className={css`
width: auto;
display: flex !important;
@ -186,67 +187,71 @@ export function Input(props) {
}
`}
>
{variable ? (
<div
className={css`
position: relative;
line-height: 0;
&:hover {
.ant-select-clear {
opacity: 0.8;
}
}
.ant-input {
overflow: auto;
white-space: nowrap;
${disabled ? '' : 'padding-right: 28px;'}
.ant-tag {
display: inline;
line-height: 19px;
margin: 0;
padding: 2px 7px;
border-radius: 10px;
}
}
`}
>
<div style={{ flex: 1 }}>
{variable ? (
<div
onInput={(e) => e.preventDefault()}
onKeyDown={(e) => {
if (e.key !== 'Backspace') {
e.preventDefault();
return;
className={css`
position: relative;
line-height: 0;
&:hover {
.ant-select-clear {
opacity: 0.8;
}
}
onChange(null);
}}
className={cx('ant-input', { 'ant-input-disabled': disabled })}
contentEditable={!disabled}
suppressContentEditableWarning
.ant-input {
overflow: auto;
white-space: nowrap;
${disabled ? '' : 'padding-right: 28px;'}
.ant-tag {
display: inline;
line-height: 19px;
margin: 0;
padding: 2px 7px;
border-radius: 10px;
}
}
`}
>
<Tag contentEditable={false} color="blue">
{variableText}
</Tag>
</div>
{!disabled ? (
<span
className={cx(
'ant-select-clear',
css`
user-select: 'none';
`,
)}
unselectable="on"
aria-hidden
onClick={() => onChange(null)}
<div
onInput={(e) => e.preventDefault()}
onKeyDown={(e) => {
if (e.key !== 'Backspace') {
e.preventDefault();
return;
}
onChange(null);
}}
className={cx('ant-input', { 'ant-input-disabled': disabled })}
contentEditable={!disabled}
suppressContentEditableWarning
>
<CloseCircleFilled />
</span>
) : null}
</div>
) : children ?? <ConstantComponent value={value} onChange={onChange} />}
<Tag contentEditable={false} color="blue">
{variableText}
</Tag>
</div>
{!disabled ? (
<span
className={cx(
'ant-select-clear',
css`
user-select: 'none';
`,
)}
unselectable="on"
aria-hidden
onClick={() => onChange(null)}
>
<CloseCircleFilled />
</span>
) : null}
</div>
) : (
children ?? <ConstantComponent value={value} onChange={onChange} />
)}
</div>
{options.length > 1 ? (
<Cascader
options={options}
@ -254,9 +259,7 @@ export function Input(props) {
onChange={onSwitch}
changeOnSelect
>
{button ?? (
<XButton type={variable ? 'primary' : 'default'} />
)}
{button ?? <XButton type={variable ? 'primary' : 'default'} />}
</Cascader>
) : null}
</AntInput.Group>

View File

@ -1,7 +1,46 @@
import { every, some, findIndex } from 'lodash';
import flat from 'flat';
import _, { every, findIndex, some } from 'lodash';
import moment from 'moment';
import { useCurrentUserContext } from '../../../user';
import jsonLogic from '../../common/utils/logic';
type VariablesCtx = {
/** 当前登录的用户 */
$user: Record<string, any>;
$date: Record<string, any>;
};
export const useVariablesCtx = (): VariablesCtx => {
const { data } = useCurrentUserContext() || {};
return {
$user: data?.data || {},
$date: {
now: () => moment().toISOString(),
},
};
};
export const isVariable = (str: unknown) => {
if (typeof str !== 'string') {
return false;
}
const regex = /{{(.*?)}}/;
const matches = str?.match?.(regex);
return matches ? true : false;
};
export const parseVariables = (str: string, ctx: VariablesCtx) => {
const regex = /{{(.*?)}}/;
const matches = str?.match?.(regex);
if (matches) {
const result = _.get(ctx, matches[1]);
return _.isFunction(result) ? result() : result;
} else {
return str;
}
};
function getInnermostKeyAndValue(obj) {
if (typeof obj !== 'object' || obj === null) {
return null;
@ -17,7 +56,8 @@ function getInnermostKeyAndValue(obj) {
}
return null;
}
const getValue = (str, values) => {
const getValue = (str: string, values) => {
const regex = /{{(.*?)}}/;
const matches = str?.match?.(regex);
if (matches) {

View File

@ -3,12 +3,12 @@ import { get } from 'lodash';
import { useContext } from 'react';
export const useComponent = (component: any, defaults?: any) => {
const { components } = useContext(SchemaOptionsContext);
if (!component) {
return defaults;
}
if (typeof component !== 'string') {
return component;
}
const { components } = useContext(SchemaOptionsContext);
return get(components, component) || defaults;
};

View File

@ -2,7 +2,14 @@ import React from 'react';
import { Variable } from '../../schema-component';
import { useVariableOptions } from './Variables';
export function FilterDynamicComponent(props) {
type Props = {
value: any;
onChange: (value: any) => void;
renderSchemaComponent: () => React.ReactNode;
collectionName: string;
};
export function FilterDynamicComponent(props: Props) {
const { value, onChange, renderSchemaComponent, collectionName } = props;
const scope = useVariableOptions(collectionName);
return (

View File

@ -25,6 +25,7 @@ import { useTranslation } from 'react-i18next';
import {
APIClientProvider,
ActionContext,
CollectionFieldOptions,
CollectionManagerContext,
Designable,
FormProvider,
@ -35,10 +36,10 @@ import {
findFormBlock,
useAPIClient,
useCollection,
useLinkageCollectionFilterOptions,
useCollectionManager,
useCompile,
useDesignable,
useLinkageCollectionFilterOptions,
} from '..';
import { findFilterTargets, updateFilterTargets } from '../block-provider/hooks';
import { FilterBlockType, isSameCollection, useSupportedBlocks } from '../filter-provider/utils';
@ -1127,3 +1128,21 @@ SchemaSettings.EnableChildCollections = function EnableChildCollections(props) {
/>
);
};
// 是否显示默认值配置项
export const isShowDefaultValue = (collectionField: CollectionFieldOptions, getInterface) => {
return (
!['o2o', 'oho', 'obo', 'o2m', 'attachment'].includes(collectionField?.interface) &&
!isSystemField(collectionField, getInterface)
);
};
// 是否是系统字段
export const isSystemField = (collectionField: CollectionFieldOptions, getInterface) => {
const i = getInterface?.(collectionField?.interface);
return i?.group === 'systemInfo';
};
export const isPatternDisabled = (fieldSchema: Schema) => {
return fieldSchema?.['x-component-props']?.['pattern-disable'] == true;
};

View File

@ -0,0 +1,44 @@
import React, { useMemo } from 'react';
import { Variable, useCompile } from '../../schema-component';
import { useUserVariable } from './hooks/useUserVariable';
type Props = {
value: any;
onChange: (value: any) => void;
collectionName: string;
renderSchemaComponent?: (props: any) => any;
style: React.CSSProperties;
schema: any;
operator: any;
children: any;
};
export const VariableInput = (props: Props) => {
const { value, onChange, renderSchemaComponent: RenderSchemaComponent, style, schema } = props;
const compile = useCompile();
const userVariable = useUserVariable({ schema, level: 1 });
const scope = useMemo(() => {
return [
userVariable,
compile({
label: `{{t("Date variables")}}`,
value: '$date',
key: '$date',
disabled: schema['x-component'] !== 'DatePicker',
children: [
{
key: 'now',
value: 'now',
label: `{{t("Now")}}`,
},
],
}),
];
}, []);
return (
<Variable.Input value={value} onChange={onChange} scope={scope} style={style}>
<RenderSchemaComponent value={value} onChange={onChange} />
</Variable.Input>
);
};

View File

@ -0,0 +1,152 @@
import { useCompile } from '../../../schema-component';
export const useDateVariable = ({ operator, schema }) => {
const compile = useCompile();
const operatorValue = operator?.value || '';
if (!operator || !schema) return null;
const disabled = !['DatePicker', 'DatePicker.RangePicker'].includes(schema['x-component']);
const dateOptions = [
{
key: 'now',
value: 'now',
label: `{{t("Now")}}`,
disabled: schema['x-component'] !== 'DatePicker' || operatorValue === '$dateBetween',
},
{
key: 'yesterday',
value: 'yesterday',
label: `{{t("Yesterday")}}`,
disabled,
},
{
key: 'today',
value: 'today',
label: `{{t("Today")}}`,
disabled,
},
{
key: 'tomorrow',
value: 'tomorrow',
label: `{{t("Tomorrow")}}`,
disabled,
},
{
key: 'lastIsoWeek',
value: 'lastIsoWeek',
label: `{{t("Last week")}}`,
disabled,
},
{
key: 'thisIsoWeek',
value: 'thisIsoWeek',
label: `{{t("This week")}}`,
disabled,
},
{
key: 'nextIsoWeek',
value: 'nextIsoWeek',
label: `{{t("Next week")}}`,
disabled,
},
{
key: 'lastMonth',
value: 'lastMonth',
label: `{{t("Last month")}}`,
disabled,
},
{
key: 'thisMonth',
value: 'thisMonth',
label: `{{t("This month")}}`,
disabled,
},
{
key: 'nextMonth',
value: 'nextMonth',
label: `{{t("Next month")}}`,
disabled,
},
{
key: 'lastQuarter',
value: 'lastQuarter',
label: `{{t("Last quarter")}}`,
disabled,
},
{
key: 'thisQuarter',
value: 'thisQuarter',
label: `{{t("This quarter")}}`,
disabled,
},
{
key: 'nextQuarter',
value: 'nextQuarter',
label: `{{t("Next quarter")}}`,
disabled,
},
{
key: 'lastYear',
value: 'lastYear',
label: `{{t("Last year")}}`,
disabled,
},
{
key: 'thisYear',
value: 'thisYear',
label: `{{t("This year")}}`,
disabled,
},
{
key: 'nextYear',
value: 'nextYear',
label: `{{t("Next year")}}`,
disabled,
},
{
key: 'last7Days',
value: 'last7Days',
label: `{{t("Last 7 days")}}`,
disabled,
},
{
key: 'next7Days',
value: 'next7Days',
label: `{{t("Next 7 days")}}`,
disabled,
},
{
key: 'last30Days',
value: 'last30Days',
label: `{{t("Last 30 days")}}`,
disabled,
},
{
key: 'next30Days',
value: 'next30Days',
label: `{{t("Next 30 days")}}`,
disabled,
},
{
key: 'last90Days',
value: 'last90Days',
label: `{{t("Last 90 days")}}`,
disabled,
},
{
key: 'next90Days',
value: 'next90Days',
label: `{{t("Next 90 days")}}`,
disabled,
},
];
return compile({
label: `{{t("Date variables")}}`,
value: '$date',
key: '$date',
disabled: dateOptions.every((option) => option.disabled),
children: dateOptions,
});
};

View File

@ -0,0 +1,67 @@
import { useMemo } from 'react';
import { useCompile, useGetFilterOptions } from '../../../schema-component';
interface GetOptionsParams {
schema: any;
operator?: string;
maxDepth: number;
count?: number;
getFilterOptions: (collectionName: string) => any[];
}
const getChildren = (options: any[], { schema, operator, maxDepth, count = 1, getFilterOptions }: GetOptionsParams) => {
if (count > maxDepth) {
return [];
}
const result = options.map((option) => {
if ((option.type !== 'belongsTo' && option.type !== 'hasOne') || !option.target) {
return {
key: option.name,
value: option.name,
label: option.title,
// TODO: 现在是通过组件的名称来过滤能够被选择的选项,这样的坏处是不够精确,后续可以优化
disabled: schema?.['x-component'] !== option.schema?.['x-component'],
};
}
const children =
getChildren(getFilterOptions(option.target), {
schema,
operator,
maxDepth,
count: count + 1,
getFilterOptions,
}) || [];
return {
key: option.name,
value: option.name,
label: option.title,
children,
disabled: children.every((child) => child.disabled),
};
});
return result;
};
export const useUserVariable = ({ operator, schema, level }: { operator?: any; schema: any; level?: number }) => {
const compile = useCompile();
const getFilterOptions = useGetFilterOptions();
const children = useMemo(
() => getChildren(getFilterOptions('users'), { schema, operator, maxDepth: level || 3, getFilterOptions }) || [],
[operator, schema],
);
return useMemo(() => {
return compile({
label: `{{t("Current user")}}`,
value: '$user',
key: '$user',
disabled: children.every((option) => option.disabled),
children: children,
});
}, [children]);
};

View File

@ -0,0 +1,13 @@
import { useValues } from '../../../schema-component/antd/filter/useValues';
import { useDateVariable } from './useDateVariable';
import { useUserVariable } from './useUserVariable';
export const useVariableOptions = () => {
const { operator, schema } = useValues();
const userVariable = useUserVariable({ operator, schema });
const dateVariable = useDateVariable({ operator, schema });
if (!operator || !schema) return [];
return [userVariable, dateVariable];
};

View File

@ -3,10 +3,11 @@ import { ISchema, useField, useFieldSchema } from '@formily/react';
import {
GeneralSchemaDesigner,
SchemaSettings,
isPatternDisabled,
useCollection,
useCollectionManager,
useDesignable,
useFormBlockContext
useFormBlockContext,
} from '@nocobase/client';
import set from 'lodash/set';
import React from 'react';
@ -155,7 +156,7 @@ const Designer = () => {
}}
/>
)}
{form && !form?.readPretty && fieldSchema?.['x-component-props']?.['pattern-disable'] != true && (
{form && !form?.readPretty && !isPatternDisabled(fieldSchema) && (
<SchemaSettings.SelectItem
key="pattern"
title={t('Pattern')}

View File

@ -26,7 +26,7 @@ const onTargetFieldChange = (field: Field) => {
!targetField.getState().disabled && targetField.setValue([]);
};
function makeFieldsPathOptions(fields, appends = []) {
function MakeFieldsPathOptions(fields, appends = []) {
const { getCollection } = useCollectionManager();
const options = [];
fields.forEach((field) => {
@ -41,7 +41,7 @@ function makeFieldsPathOptions(fields, appends = []) {
options.push({
label: field.uiSchema?.title ?? field.name,
value: field.name,
children: makeFieldsPathOptions(nextCollection.fields, nextAppends),
children: MakeFieldsPathOptions(nextCollection.fields, nextAppends),
});
}
} else {
@ -120,7 +120,7 @@ export const snapshot: IField = {
const { getCollection } = useCollectionManager();
const { fields } = getCollection(targetCollection);
const result = makeFieldsPathOptions(fields, appends);
const result = MakeFieldsPathOptions(fields, appends);
return [
{