diff --git a/packages/core/client/docs/en-US/core/ui-schema/demos/schema-settings-render.tsx b/packages/core/client/docs/en-US/core/ui-schema/demos/schema-settings-render.tsx index 089b17cd29..c0fde38eec 100644 --- a/packages/core/client/docs/en-US/core/ui-schema/demos/schema-settings-render.tsx +++ b/packages/core/client/docs/en-US/core/ui-schema/demos/schema-settings-render.tsx @@ -11,7 +11,7 @@ import { SchemaSettingsModalItem, createDesignable, useAPIClient, - useSchemaComponentContext, + useRefreshFieldSchema, useSchemaSettings, useSchemaSettingsRender, } from '@nocobase/client'; @@ -82,16 +82,16 @@ const Demo = () => { const fieldSchema = useFieldSchema(); const field = useField(); const api = useAPIClient(); - const { refresh } = useSchemaComponentContext(); + const refreshFieldSchema = useRefreshFieldSchema(); const dn = useMemo( () => createDesignable({ current: fieldSchema.parent, model: field.parent, api, - refresh, + refresh: () => refreshFieldSchema({ refreshParentSchema: true }), }), - [], + [api, field.parent, fieldSchema.parent, refreshFieldSchema], ); const { render, exists } = useSchemaSettingsRender(fieldSchema['x-settings'], { fieldSchema: dn.current, diff --git a/packages/core/client/docs/zh-CN/core/ui-schema/demos/schema-settings-render.tsx b/packages/core/client/docs/zh-CN/core/ui-schema/demos/schema-settings-render.tsx index 089b17cd29..c0fde38eec 100644 --- a/packages/core/client/docs/zh-CN/core/ui-schema/demos/schema-settings-render.tsx +++ b/packages/core/client/docs/zh-CN/core/ui-schema/demos/schema-settings-render.tsx @@ -11,7 +11,7 @@ import { SchemaSettingsModalItem, createDesignable, useAPIClient, - useSchemaComponentContext, + useRefreshFieldSchema, useSchemaSettings, useSchemaSettingsRender, } from '@nocobase/client'; @@ -82,16 +82,16 @@ const Demo = () => { const fieldSchema = useFieldSchema(); const field = useField(); const api = useAPIClient(); - const { refresh } = useSchemaComponentContext(); + const refreshFieldSchema = useRefreshFieldSchema(); const dn = useMemo( () => createDesignable({ current: fieldSchema.parent, model: field.parent, api, - refresh, + refresh: () => refreshFieldSchema({ refreshParentSchema: true }), }), - [], + [api, field.parent, fieldSchema.parent, refreshFieldSchema], ); const { render, exists } = useSchemaSettingsRender(fieldSchema['x-settings'], { fieldSchema: dn.current, diff --git a/packages/core/client/src/application/schema-initializer/hooks/useSchemaInitializerRender.tsx b/packages/core/client/src/application/schema-initializer/hooks/useSchemaInitializerRender.tsx index 6cf0d294dd..b2cbffeedd 100644 --- a/packages/core/client/src/application/schema-initializer/hooks/useSchemaInitializerRender.tsx +++ b/packages/core/client/src/application/schema-initializer/hooks/useSchemaInitializerRender.tsx @@ -17,7 +17,7 @@ import { SchemaInitializer } from '../SchemaInitializer'; import { SchemaInitializerOptions } from '../types'; import { withInitializer } from '../withInitializer'; -const InitializerComponent: FC> = React.memo((options) => { +const InitializerComponent: FC> = (options) => { const Component: any = options.Component || SchemaInitializerButton; const ItemsComponent: any = options.ItemsComponent || SchemaInitializerItems; @@ -31,7 +31,8 @@ const InitializerComponent: FC> = React.memo( const C = useMemo(() => withInitializer(Component), [Component]); return React.createElement(C, options, React.createElement(ItemsComponent, itemsComponentProps)); -}); +}; + InitializerComponent.displayName = 'InitializerComponent'; export function useSchemaInitializerRender( diff --git a/packages/core/client/src/application/schema-initializer/withInitializer.tsx b/packages/core/client/src/application/schema-initializer/withInitializer.tsx index 17f5c79eac..cd5d5de987 100644 --- a/packages/core/client/src/application/schema-initializer/withInitializer.tsx +++ b/packages/core/client/src/application/schema-initializer/withInitializer.tsx @@ -54,7 +54,7 @@ export function withInitializer(C: ComponentType) { insertAdjacent(insertPosition, wrapCallback(schema, { isInSubTable }), { onSuccess }); } }, - [insertCallback, wrapCallback, insertAdjacent, insertPosition, onSuccess], + [insertCallback, wrapCallback, isInSubTable, insertAdjacent, insertPosition, onSuccess], ); const { wrapSSR, hashId, componentCls } = useSchemaInitializerStyles(); diff --git a/packages/core/client/src/block-provider/BlockProvider.tsx b/packages/core/client/src/block-provider/BlockProvider.tsx index 549c2911c5..ccfc56bed0 100644 --- a/packages/core/client/src/block-provider/BlockProvider.tsx +++ b/packages/core/client/src/block-provider/BlockProvider.tsx @@ -10,7 +10,7 @@ import { Field, GeneralField } from '@formily/core'; import { RecursionField, useField, useFieldSchema } from '@formily/react'; import { Col, Row } from 'antd'; -import { isArray } from 'lodash'; +import _, { isArray } from 'lodash'; import template from 'lodash/template'; import React, { createContext, useContext, useMemo } from 'react'; import { Link } from 'react-router-dom'; @@ -33,10 +33,12 @@ import { useCollectionManager_deprecated, useCollection_deprecated, } from '../collection-manager'; +import { RefreshComponentProvider } from '../formily/NocoBaseRecursionField'; import { useSourceId } from '../modules/blocks/useSourceId'; import { RecordProvider, useRecordIndex } from '../record-provider'; import { useAssociationNames } from './hooks'; import { useDataBlockParentRecord } from './hooks/useDataBlockParentRecord'; +import { useUpdate } from 'ahooks'; /** * @deprecated @@ -243,6 +245,7 @@ export const BlockProvider = (props: { }) => { const { name, dataSource, useParams, parentRecord } = props; const parentRecordFromHook = useCompatDataBlockParentRecord(props); + const refresh = useUpdate(); // 新版(1.0)已弃用 useParams,这里之所以继续保留是为了兼容旧版的 UISchema const paramsFromHook = useParams?.(); @@ -261,7 +264,7 @@ export const BlockProvider = (props: { - {props.children} + {props.children} diff --git a/packages/core/client/src/block-provider/hooks/index.ts b/packages/core/client/src/block-provider/hooks/index.ts index 14ef586d6a..2183643163 100644 --- a/packages/core/client/src/block-provider/hooks/index.ts +++ b/packages/core/client/src/block-provider/hooks/index.ts @@ -1590,7 +1590,7 @@ export const useAssociationNames = (dataSource?: string) => { const { getCollectionJoinField, getCollection } = useCollectionManager_deprecated(dataSource); const fieldSchema = useFieldSchema(); - const getAssociationAppends = () => { + const getAssociationAppends = useCallback(() => { const updateAssociationValues = new Set([]); let appends = new Set([]); @@ -1606,7 +1606,7 @@ export const useAssociationNames = (dataSource?: string) => { appends = fillParentFields(appends); return { appends: [...appends], updateAssociationValues: [...updateAssociationValues] }; - }; + }, [dataSource, fieldSchema, getCollection, getCollectionJoinField]); return { getAssociationAppends }; }; diff --git a/packages/core/client/src/data-source/components/CollectionDeletedPlaceholder.tsx b/packages/core/client/src/data-source/components/CollectionDeletedPlaceholder.tsx index 024b938f38..1c41e27a46 100644 --- a/packages/core/client/src/data-source/components/CollectionDeletedPlaceholder.tsx +++ b/packages/core/client/src/data-source/components/CollectionDeletedPlaceholder.tsx @@ -16,7 +16,6 @@ import { useDataSourceManager } from '../data-source'; import { DEFAULT_DATA_SOURCE_KEY } from '../../data-source/data-source/DataSourceManager'; import { useCollection } from '../collection'; import { BlockItemCard } from '../../schema-component/antd/block-item/BlockItemCard'; -import { AnyKindOfDictionary } from 'lodash'; export interface CollectionDeletedPlaceholderProps { type: 'Collection' | 'Field' | 'Data Source' | 'Block template'; @@ -99,6 +98,7 @@ export const CollectionDeletedPlaceholder: FC ...confirm, onOk() { dn.remove(null, { removeParentsIfNoChildren: true, breakRemoveOn: { 'x-component': 'Grid' } }); + dn.refresh({ refreshParentSchema: true }); }, }) } diff --git a/packages/core/client/src/data-source/data-block/DataBlockRequestProvider.tsx b/packages/core/client/src/data-source/data-block/DataBlockRequestProvider.tsx index 7428d397e2..6e14728d76 100644 --- a/packages/core/client/src/data-source/data-block/DataBlockRequestProvider.tsx +++ b/packages/core/client/src/data-source/data-block/DataBlockRequestProvider.tsx @@ -225,10 +225,27 @@ export const useDataBlockRequest = (): UseRequestResult<{ data: T }; /** - * Compared to `useDataBlockRequest`, the advantage of this hook is that it prevents unnecessary re-renders. - * For example, if you only need to use methods like `refresh` or `run`, it's recommended to use this hook, - * as it avoids component re-rendering when re-triggering requests. - * @returns + * Compared to `useDataBlockRequest`, this Hook helps prevent unnecessary re-renders. + * + * This Hook returns a stable function reference that won't change between renders. When you only need + * methods like `refresh` or `run`, using this Hook is recommended because: + * + * 1. It returns a memoized object containing only the getter function + * 2. The getter function accesses the latest request data through a ref, avoiding re-renders + * 3. Unlike useDataBlockRequest which returns request state directly, this Hook provides indirect access + * through a getter, breaking the reactive dependency chain + * + * For example: + * ```ts + * // This will re-render when request state changes + * const { refresh } = useDataBlockRequest(); + * + * // This won't re-render when request state changes + * const { getDataBlockRequest } = useDataBlockRequestGetter(); + * const refresh = getDataBlockRequest().refresh; + * ``` + * + * @returns An object containing the getDataBlockRequest method that provides access to the request instance */ export const useDataBlockRequestGetter = () => { const contextRef = useContext(BlockRequestRefContext); diff --git a/packages/core/client/src/formily/NocoBaseField.tsx b/packages/core/client/src/formily/NocoBaseField.tsx index b3060bf429..b6f168fb49 100644 --- a/packages/core/client/src/formily/NocoBaseField.tsx +++ b/packages/core/client/src/formily/NocoBaseField.tsx @@ -7,23 +7,32 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { FieldContext, IFieldProps, JSXComponent, Schema, useField, useForm } from '@formily/react'; -import React from 'react'; +import { FieldContext, IFieldProps, JSXComponent, Schema, useFieldSchema } from '@formily/react'; +import React, { useMemo } from 'react'; import { useCompile } from '../schema-component/hooks/useCompile'; import { NocoBaseReactiveField } from './NocoBaseReactiveField'; import { createNocoBaseField } from './createNocoBaseField'; +/** + * To maintain high performance of Table, this component removes Formily-related components + * @param props component props + * @returns + */ export const NocoBaseField = ( props: IFieldProps & { schema: Schema }, ) => { const compile = useCompile(); - const form = useForm(); - const parent = useField(); - const field = createNocoBaseField.call(form, { basePath: parent?.address, compile, ...props }); + const fieldSchema = useFieldSchema(); + // eslint-disable-next-line react-hooks/exhaustive-deps + const field = useMemo(() => createNocoBaseField({ ...props, compile }), []); + + // update componentProps to rerender field component + Object.assign(field.componentProps, fieldSchema['x-component-props']); + field.value = props.value; return ( - - {props.children} + + {props.children} ); }; diff --git a/packages/core/client/src/formily/NocoBaseReactiveField.tsx b/packages/core/client/src/formily/NocoBaseReactiveField.tsx index 9557b47d5a..3715f6003e 100644 --- a/packages/core/client/src/formily/NocoBaseReactiveField.tsx +++ b/packages/core/client/src/formily/NocoBaseReactiveField.tsx @@ -7,9 +7,8 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { Form, GeneralField, isVoidField } from '@formily/core'; +import { Form, GeneralField } from '@formily/core'; import { RenderPropsChildren, SchemaComponentsContext } from '@formily/react'; -import { observer } from '@formily/reactive-react'; import { FormPath, isFn } from '@formily/shared'; import React, { Fragment, useContext } from 'react'; interface IReactiveFieldProps { @@ -34,19 +33,16 @@ const renderChildren = (children: RenderPropsChildren, field?: Gen isFn(children) ? children(field, form) : children; /** - * Based on @formily/react v2.3.2 ReactiveInternal component - * Modified to better adapt to NocoBase's needs + * To maintain high performance of Table, this component removes Formily-related code */ -const ReactiveInternal: React.FC = (props) => { +const NocoBaseReactiveInternal: React.FC = (props) => { const components = useContext(SchemaComponentsContext); - if (!props.field) { - return {renderChildren(props.children)}; - } - const field = props.field; + const field: any = props.field; const content = mergeChildren( renderChildren(props.children, field, field.form), field.content ?? field.componentProps.children, ); + if (field.display !== 'visible') return null; const getComponent = (target: any) => { @@ -63,27 +59,8 @@ const ReactiveInternal: React.FC = (props) => { const renderComponent = () => { if (!field.componentType) return content; - const value = !isVoidField(field) ? field.value : undefined; - const onChange = !isVoidField(field) - ? (...args: any[]) => { - field.onInput(...args); - field.componentProps?.onChange?.(...args); - } - : field.componentProps?.onChange; - const onFocus = !isVoidField(field) - ? (...args: any[]) => { - field.onFocus(...args); - field.componentProps?.onFocus?.(...args); - } - : field.componentProps?.onFocus; - const onBlur = !isVoidField(field) - ? (...args: any[]) => { - field.onBlur(...args); - field.componentProps?.onBlur?.(...args); - } - : field.componentProps?.onBlur; - const disabled = !isVoidField(field) ? field.pattern === 'disabled' || field.pattern === 'readPretty' : undefined; - const readOnly = !isVoidField(field) ? field.pattern === 'readOnly' : undefined; + const disabled = true; + const readOnly = true; return React.createElement( getComponent(field.componentType), @@ -91,10 +68,7 @@ const ReactiveInternal: React.FC = (props) => { disabled, readOnly, ...field.componentProps, - value, - onChange, - onFocus, - onBlur, + value: field.value, }, content, ); @@ -103,14 +77,12 @@ const ReactiveInternal: React.FC = (props) => { return renderDecorator(renderComponent()); }; -ReactiveInternal.displayName = 'NocoBaseReactiveInternal'; +NocoBaseReactiveInternal.displayName = 'NocoBaseReactiveInternal'; /** * Based on @formily/react v2.3.2 NocoBaseReactiveField component * Modified to better adapt to NocoBase's needs */ -export const NocoBaseReactiveField = observer(ReactiveInternal, { - forwardRef: true, -}); +export const NocoBaseReactiveField = NocoBaseReactiveInternal; NocoBaseReactiveField.displayName = 'NocoBaseReactiveField'; diff --git a/packages/core/client/src/formily/NocoBaseRecursionField.tsx b/packages/core/client/src/formily/NocoBaseRecursionField.tsx index e74858d379..4c48351125 100644 --- a/packages/core/client/src/formily/NocoBaseRecursionField.tsx +++ b/packages/core/client/src/formily/NocoBaseRecursionField.tsx @@ -24,10 +24,11 @@ import { import { isBool, isFn, isValid, merge } from '@formily/shared'; import { useUpdate } from 'ahooks'; import _ from 'lodash'; -import React, { FC, Fragment, useCallback, useMemo } from 'react'; +import React, { FC, Fragment, useCallback, useContext, useMemo, useRef } from 'react'; import { CollectionFieldOptions } from '../data-source/collection/Collection'; import { useCollectionManager } from '../data-source/collection/CollectionManagerProvider'; import { useCollection } from '../data-source/collection/CollectionProvider'; +import { SchemaComponentOnChangeContext } from '../schema-component/core/SchemaComponent'; import { EMPTY_OBJECT } from '../variables'; import { NocoBaseField } from './NocoBaseField'; @@ -51,22 +52,36 @@ interface INocoBaseRecursionFieldProps extends IRecursionFieldProps { const CollectionFieldUISchemaContext = React.createContext({}); -const RefreshContext = React.createContext<(options?: { refreshParent?: boolean }) => void>(_.noop); +const RefreshFieldSchemaContext = React.createContext<(options?: { refreshParentSchema?: boolean }) => void>(_.noop); -const RefreshProvider: FC<{ refresh: (options?: { refreshParent?: boolean }) => void }> = ({ children, refresh }) => { +const RefreshFieldSchemaProvider: FC<{ refresh: (options?: { refreshParentSchema?: boolean }) => void }> = ({ + children, + refresh, +}) => { const refreshParent = useRefreshFieldSchema(); const value = useCallback( - (options?: { refreshParent?: boolean }) => { - if (options?.refreshParent) { + (options?: { refreshParentSchema?: boolean }) => { + refresh(); + + if (options?.refreshParentSchema) { refreshParent?.(); } - refresh(); }, [refreshParent, refresh], ); - return {children}; + return {children}; +}; + +const RefreshComponentContext = React.createContext<() => void>(_.noop); + +export const RefreshComponentProvider: FC<{ refresh: () => void }> = ({ children, refresh }) => { + return {children}; +}; + +export const useRefreshComponent = () => { + return React.useContext(RefreshComponentContext); }; /** @@ -74,7 +89,7 @@ const RefreshProvider: FC<{ refresh: (options?: { refreshParent?: boolean }) => * @returns */ export const useRefreshFieldSchema = () => { - return React.useContext(RefreshContext); + return React.useContext(RefreshFieldSchemaContext); }; /** @@ -121,15 +136,11 @@ const useFieldProps = (schema: Schema) => { const useBasePath = (props: IRecursionFieldProps) => { const parent = useField(); if (props.onlyRenderProperties) { - return props.basePath || parent?.address.concat(props.name); + return props.basePath || parent?.address?.concat(props.name); } return props.basePath || parent?.address; }; -const createSchemaInstance = _.memoize((schema: ISchema): Schema => { - return new Schema(schema); -}); - const createMergedSchemaInstance = (schema: Schema, uiSchema: ISchema, onlyRenderProperties: boolean) => { const clonedSchema = schema.toJSON(); @@ -257,14 +268,32 @@ export const NocoBaseRecursionField: ReactFC = Rea uiSchema, } = props; const basePath = useBasePath(props); - const fieldSchema = createSchemaInstance(schema); + const newFieldSchemaRef = useRef(null); + const oldFieldSchema = useMemo(() => { + newFieldSchemaRef.current = null; + return new Schema(schema); + }, [schema]); const { uiSchema: collectionFiledUiSchema, defaultValue } = useCollectionFieldUISchema(); const update = useUpdate(); + const { onChange: onChangeFromContext } = useContext(SchemaComponentOnChangeContext); + + const fieldSchema: Schema = newFieldSchemaRef.current || oldFieldSchema; const refresh = useCallback(() => { - createSchemaInstance.cache.delete(schema); + const parent = fieldSchema.parent; + newFieldSchemaRef.current = new Schema(fieldSchema.toJSON(), parent); + + if (parent?.properties) { + Object.keys(parent.properties).forEach((key) => { + if (key === fieldSchema.name) { + parent.properties[key] = newFieldSchemaRef.current; + } + }); + } + update(); - }, [schema, update]); + onChangeFromContext?.(); + }, [fieldSchema, onChangeFromContext, update]); // Merge default Schema of collection fields const mergedFieldSchema = useMemo(() => { @@ -330,7 +359,7 @@ export const NocoBaseRecursionField: ReactFC = Rea // some default schema values would also be saved in fieldSchema. return ( - {render()} + {render()} ); }); diff --git a/packages/core/client/src/formily/createNocoBaseField.ts b/packages/core/client/src/formily/createNocoBaseField.ts index a8fdba57f9..0b1ff109ba 100644 --- a/packages/core/client/src/formily/createNocoBaseField.ts +++ b/packages/core/client/src/formily/createNocoBaseField.ts @@ -7,29 +7,12 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { FormPath, FormPathPattern, IFieldFactoryProps, IFieldProps, LifeCycleTypes } from '@formily/core'; -import { Field } from '@formily/core/esm/models/Field'; -import { locateNode } from '@formily/core/esm/shared/internals'; +import { IFieldProps } from '@formily/core'; import { JSXComponent, Schema } from '@formily/react'; -import { batch, define, observable, raw } from '@formily/reactive'; import { toArr } from '@formily/shared'; -export function createNocoBaseField( - props: IFieldFactoryProps & { compile: (source: any) => any }, -): Field { - const address = FormPath.parse(props.basePath).concat(props.name); - const identifier = address.toString(); - if (!identifier) return; - if (!this.fields[identifier]) { - batch(() => { - new NocoBaseField(address, props, this, this.props.designable); - }); - this.notify(LifeCycleTypes.ON_FORM_GRAPH_CHANGE); - } - - this.fields[identifier].value = props.value; - - return this.fields[identifier] as any; +export function createNocoBaseField(props: any) { + return new NocoBaseField(props); } /** @@ -40,35 +23,79 @@ class NocoBaseField< Component extends JSXComponent = any, TextType = any, ValueType = any, -> extends Field { - declare props: IFieldProps & { +> { + props: IFieldProps & { schema: Schema; compile: (source: any) => any; }; + initialized: boolean; + loading: boolean; + validating: boolean; + submitting: boolean; + selfModified: boolean; + active: boolean; + visited: boolean; + mounted: boolean; + unmounted: boolean; + inputValues: any[]; + inputValue: any; + feedbacks: any[]; + title: string; + description: string; + display: string; + pattern: string; + editable: boolean; + disabled: boolean; + readOnly: boolean; + readPretty: boolean; + visible: boolean; + hidden: boolean; + dataSource: any[]; + validator: any; + required: boolean; + content: string; + initialValue: any; + value: any; + data: any; + decorator: any[]; + component: any[]; + decoratorProps: any; + componentProps: any; + decoratorType: any; + componentType: any; + path: any; + form: any; + + constructor(props: any) { + this.props = props; + this.initialize(); + // this.makeObservable(); + } protected initialize() { const compile = this.props.compile; - this.initialized = false; + this.pattern = 'readPretty'; + this.readPretty = true; + + this.initialized = true; this.loading = false; this.validating = false; this.submitting = false; this.selfModified = false; this.active = false; - this.visited = false; - this.mounted = false; + this.visited = true; + this.mounted = true; this.unmounted = false; this.inputValues = []; this.inputValue = null; this.feedbacks = []; this.title = compile(this.props.title || this.props.schema?.title); this.description = compile(this.props.description || this.props.schema?.['description']); - this.display = this.props.display || this.props.schema?.['x-display']; - this.pattern = this.props.pattern || this.props.schema?.['x-pattern']; + this.display = 'visible'; this.editable = this.props.editable || this.props.schema?.['x-editable']; this.disabled = this.props.disabled || this.props.schema?.['x-disabled']; this.readOnly = this.props.readOnly || this.props.schema?.['x-read-only']; - this.readPretty = this.props.readPretty || this.props.schema?.['x-read-pretty']; this.visible = this.props.visible || this.props.schema?.['x-visible']; this.hidden = this.props.hidden || this.props.schema?.['x-hidden']; this.dataSource = compile(this.props.dataSource || (this.props.schema?.enum as any)); @@ -84,19 +111,18 @@ class NocoBaseField< this.component = this.props.component ? toArr(this.props.component) : [this.props.schema?.['x-component'], this.props.schema?.['x-component-props']]; + this.decoratorProps = this.props.schema?.['x-decorator-props'] || {}; + this.componentProps = this.props.schema?.['x-component-props'] || {}; + this.decoratorType = this.props.schema?.['x-decorator']; + this.componentType = this.props.schema?.['x-component']; + + this.path = {}; + this.form = {}; } - locate(address: FormPathPattern) { - raw(this.form.fields)[address.toString()] = this as any; - locateNode(this as any, address); - } - - protected makeObservable() { - define(this, { - componentProps: observable, - }); - } - - // Set as an empty function to prevent parent class from executing this method - protected makeReactive() {} + // protected makeObservable() { + // define(this, { + // componentProps: observable, + // }); + // } } diff --git a/packages/core/client/src/index.ts b/packages/core/client/src/index.ts index 06d05a3312..b0ccc8591f 100644 --- a/packages/core/client/src/index.ts +++ b/packages/core/client/src/index.ts @@ -41,6 +41,7 @@ export * from './global-theme'; export * from './hooks'; export * from './i18n'; export * from './icon'; +export * from './lazy-helper'; export { default as locale } from './locale'; export * from './nocobase-buildin-plugin'; export * from './plugin-manager'; @@ -58,7 +59,6 @@ export * from './system-settings'; export * from './testUtils'; export * from './user'; export * from './variables'; -export * from './lazy-helper'; export { withDynamicSchemaProps } from './hoc/withDynamicSchemaProps'; export { withSkeletonComponent } from './hoc/withSkeletonComponent'; @@ -83,7 +83,8 @@ export { languageCodes } from './locale'; // Override Formily API export { - NocoBaseRecursionField, CollectionFieldUISchemaProvider, IsInNocoBaseRecursionFieldContext, + NocoBaseRecursionField, + useRefreshFieldSchema, } from './formily/NocoBaseRecursionField'; diff --git a/packages/core/client/src/modules/actions/__e2e__/add-new/inherit.test.ts b/packages/core/client/src/modules/actions/__e2e__/add-new/inherit.test.ts index ec3a21997a..fc2091d28e 100644 --- a/packages/core/client/src/modules/actions/__e2e__/add-new/inherit.test.ts +++ b/packages/core/client/src/modules/actions/__e2e__/add-new/inherit.test.ts @@ -17,7 +17,7 @@ test.describe('Add new: inherit', () => { // 1. click the "Add new" button, and then create a block, the block's collection should be "parent" await page.getByRole('button', { name: 'plus Add new' }).click(); await page.getByLabel('schema-initializer-Grid-popup').hover(); - await page.getByRole('menuitem', { name: 'form Form right' }).hover(); + await page.getByRole('menuitem', { name: 'Form right' }).hover(); await page.getByRole('menuitem', { name: 'Current collection' }).click(); await page.getByLabel('block-item-CardItem-parent-form').hover(); await expect(page.getByLabel('block-item-CardItem-parent-form').getByText('parent')).toBeVisible(); @@ -28,7 +28,7 @@ test.describe('Add new: inherit', () => { await page.getByRole('button', { name: 'down' }).hover(); await page.getByRole('menuitem', { name: 'child1' }).click(); await page.getByLabel('schema-initializer-Grid-popup').hover(); - await page.getByRole('menuitem', { name: 'form Form right' }).hover(); + await page.getByRole('menuitem', { name: 'Form right' }).hover(); await page.getByRole('menuitem', { name: 'Current collection' }).click(); await page.getByLabel('block-item-CardItem-child1-').hover(); await expect(page.getByLabel('block-item-CardItem-child1-').getByText('child1')).toBeVisible(); @@ -39,7 +39,7 @@ test.describe('Add new: inherit', () => { await page.getByRole('button', { name: 'down' }).hover(); await page.getByRole('menuitem', { name: 'child2' }).click(); await page.getByLabel('schema-initializer-Grid-popup').hover(); - await page.getByRole('menuitem', { name: 'form Form right' }).hover(); + await page.getByRole('menuitem', { name: 'Form right' }).hover(); await page.getByRole('menuitem', { name: 'Current collection' }).click(); await page.getByLabel('block-item-CardItem-child2-').hover(); await expect(page.getByLabel('block-item-CardItem-child2-').getByText('child2')).toBeVisible(); diff --git a/packages/core/client/src/modules/actions/disassociate/__e2e__/disassociate.test.ts b/packages/core/client/src/modules/actions/disassociate/__e2e__/disassociate.test.ts index 12ae505ddd..f9e2832325 100644 --- a/packages/core/client/src/modules/actions/disassociate/__e2e__/disassociate.test.ts +++ b/packages/core/client/src/modules/actions/disassociate/__e2e__/disassociate.test.ts @@ -17,7 +17,7 @@ test('basic', async ({ page, mockPage, mockRecord }) => { // 1. 打开弹窗,并创建一个 Table 关系区块 await page.getByLabel('action-Action.Link-Edit record-update-collection1-table-0').click(); await page.getByLabel('schema-initializer-Grid-popup').hover(); - await page.getByRole('menuitem', { name: 'table Table right' }).hover(); + await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('menuitem', { name: 'Associated records' }).hover(); await page.getByRole('menuitem', { name: 'manyToMany' }).click(); diff --git a/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings.test.ts b/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings.test.ts index 9db67e4644..9c175766b3 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings.test.ts @@ -337,7 +337,7 @@ test.describe('set default value', () => { // 3. Table 数据选择器中使用 `Current popup record` // 创建 Table 区块 await page.getByLabel('schema-initializer-Grid-popup').hover(); - await page.getByRole('menuitem', { name: 'table Table right' }).hover(); + await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('menuitem', { name: 'Other records right' }).hover(); await page.getByRole('menuitem', { name: 'Users' }).click(); await page.mouse.move(300, 0); @@ -465,7 +465,7 @@ test.describe('set default value', () => { // 3. Table 数据选择器中使用 `Parent popup record` // 创建 Table 区块 await page.getByLabel('schema-initializer-Grid-popup').nth(1).hover(); - await page.getByRole('menuitem', { name: 'table Table right' }).hover(); + await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('menuitem', { name: 'Other records right' }).hover(); await page.getByRole('menuitem', { name: 'Users' }).click(); await page.mouse.move(300, 0); @@ -595,7 +595,7 @@ test.describe('set default value', () => { // 3. Table 数据选择器中使用 `Parent popup record` // 创建 Table 区块 await page.getByLabel('schema-initializer-Grid-popup').nth(1).hover(); - await page.getByRole('menuitem', { name: 'table Table right' }).hover(); + await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('menuitem', { name: 'Other records right' }).hover(); await page.getByRole('menuitem', { name: 'Users' }).click(); await page.mouse.move(300, 0); @@ -733,7 +733,7 @@ test.describe('set default value', () => { // 3. Table 数据选择器中使用 `Parent popup record` // 创建 Table 区块 await page.getByLabel('schema-initializer-Grid-popup').nth(1).hover(); - await page.getByRole('menuitem', { name: 'table Table right' }).hover(); + await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('menuitem', { name: 'Other records right' }).hover(); await page.getByRole('menuitem', { name: 'Users' }).click(); await page.mouse.move(300, 0); diff --git a/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings1.test.ts b/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings1.test.ts index 42fa98da66..8fa90b3ec4 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings1.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/form/__e2e__/form-create/schemaSettings1.test.ts @@ -143,7 +143,7 @@ test.describe('creation form block schema settings', () => { // 创建区块的时候,可以选择刚才保存的模板 -------------------------------------------------- await page.getByLabel('schema-initializer-Grid-page:addBlock').hover(); - await page.getByRole('menuitem', { name: 'form Form right' }).first().hover(); + await page.getByRole('menuitem', { name: 'Form right' }).first().hover(); await page.getByRole('menuitem', { name: 'General right' }).hover(); // Duplicate template @@ -152,7 +152,7 @@ test.describe('creation form block schema settings', () => { // Reference template await page.getByLabel('schema-initializer-Grid-page:addBlock').hover(); - await page.getByRole('menuitem', { name: 'form Form right' }).first().hover(); + await page.getByRole('menuitem', { name: 'Form right' }).first().hover(); await page.getByRole('menuitem', { name: 'General right' }).hover(); await page.getByRole('menuitem', { name: 'General right' }).click(); await page.getByRole('menuitem', { name: 'Reference template right' }).click(); diff --git a/packages/core/client/src/modules/blocks/data-blocks/list/__e2e__/schemaInitializer.test.ts b/packages/core/client/src/modules/blocks/data-blocks/list/__e2e__/schemaInitializer.test.ts index 56e208ff0d..3c7db0668b 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/list/__e2e__/schemaInitializer.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/list/__e2e__/schemaInitializer.test.ts @@ -32,7 +32,7 @@ test.describe('where list block can be added', () => { // 1. 打开弹窗,通过 Associated records 创建一个列表区块 await page.getByLabel('action-Action.Link-View').click(); await page.getByLabel('schema-initializer-Grid-popup').hover(); - await page.getByRole('menuitem', { name: 'ordered-list List right' }).hover(); + await page.getByRole('menuitem', { name: 'List right' }).hover(); await page.getByRole('menuitem', { name: 'Associated records right' }).hover(); await page.getByRole('menuitem', { name: 'Roles' }).click(); await page.mouse.move(300, 0); @@ -46,7 +46,7 @@ test.describe('where list block can be added', () => { // 2. 通过 Other records 创建一个列表区块 await page.getByLabel('schema-initializer-Grid-popup').hover(); - await page.getByRole('menuitem', { name: 'ordered-list List right' }).hover(); + await page.getByRole('menuitem', { name: 'List right' }).hover(); await page.getByRole('menuitem', { name: 'Other records right' }).hover(); await page.getByRole('menuitem', { name: 'Users' }).click(); await page.mouse.move(300, 0); diff --git a/packages/core/client/src/modules/blocks/data-blocks/table-selector/__e2e__/schemaSettings.test.ts b/packages/core/client/src/modules/blocks/data-blocks/table-selector/__e2e__/schemaSettings.test.ts index f325942a51..4c82450427 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table-selector/__e2e__/schemaSettings.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/table-selector/__e2e__/schemaSettings.test.ts @@ -137,12 +137,15 @@ test.describe('table data selector schema settings', () => { await expect( page.getByLabel('block-item-CardItem-table-selector-data-scope-variable-table-selector').getByRole('row'), ).toHaveCount(2); // 这里之所以是 2,是因为表头也是一个 row + await expect( page .getByLabel('block-item-CardItem-table-selector-data-scope-variable-table-selector') .getByRole('row') .getByText(record['manyToMany'][0]['singleLineText']), - ).toBeVisible(); + ).toBeVisible({ + timeout: 3000, + }); }); }); diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/blockTemplate.test.ts b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/blockTemplate.test.ts index a8e08d6771..57837da786 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/blockTemplate.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/blockTemplate.test.ts @@ -30,7 +30,7 @@ test.describe('block template', () => { // The template saved above cannot be used to create a association block. await page.getByLabel('action-Action.Link-View-view-').click(); await page.getByLabel('schema-initializer-Grid-popup').hover(); - await page.getByRole('menuitem', { name: 'table Table right' }).hover(); + await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('menuitem', { name: 'Associated records right' }).hover(); // The saved template should not be displayed (no arrow should be shown) @@ -79,7 +79,7 @@ test.describe('block template', () => { // The template saved above cannot be used to create a non-association block. await page.getByLabel('schema-initializer-Grid-page:').hover(); - await page.getByRole('menuitem', { name: 'table Table right' }).hover(); + await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('menuitem', { name: 'Roles right' }).hover(); await page.getByRole('menuitem', { name: 'Duplicate template right' }).hover(); await expect(page.getByRole('menuitem', { name: 'Roles_Table' })).toBeVisible(); diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/pagination.test.ts b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/pagination.test.ts index 3f322bbe20..bb2b0246e3 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/pagination.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/pagination.test.ts @@ -23,7 +23,7 @@ test.describe('pagination', () => { // 1. 创建一个 Table await page.getByLabel('schema-initializer-Grid-page:').hover(); - await page.getByRole('menuitem', { name: 'table Table right' }).hover(); + await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('menuitem', { name: 'collectionName' }).click(); // 显示出 ID await page.getByLabel('schema-initializer-TableV2-').hover(); diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaInitializer1.test.ts b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaInitializer1.test.ts index 45331cf1cd..05e7877d27 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaInitializer1.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaInitializer1.test.ts @@ -36,7 +36,7 @@ test.describe('where table block can be added', () => { // 添加当前表关系区块 await page.getByLabel('schema-initializer-Grid-popup').hover(); - await page.getByRole('menuitem', { name: 'table Table right' }).hover(); + await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('menuitem', { name: 'Associated records' }).hover(); await page.getByRole('menuitem', { name: 'childAssociationField' }).click(); await page @@ -47,7 +47,7 @@ test.describe('where table block can be added', () => { // 添加父表关系区块 await page.getByLabel('schema-initializer-Grid-popup').hover(); - await page.getByRole('menuitem', { name: 'table Table right' }).hover(); + await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('menuitem', { name: 'Associated records' }).hover(); await page.getByRole('menuitem', { name: 'parentAssociationField' }).click(); await page.getByLabel('schema-initializer-TableV2-table:configureColumns-parentTargetCollection').hover(); @@ -69,7 +69,7 @@ test.describe('where table block can be added', () => { // 通过 Other records 创建一个表格区块 await page.getByLabel('schema-initializer-Grid-popup').hover(); - await page.getByRole('menuitem', { name: 'table Table right' }).hover(); + await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('menuitem', { name: 'Other records right' }).hover(); await page.getByRole('menuitem', { name: 'Users' }).click(); await page.mouse.move(300, 0); @@ -99,7 +99,7 @@ test.describe('where table block can be added', () => { await page.getByLabel('action-Action.Link-View-view-').first().click(); //表格关系区块 await page.getByLabel('schema-initializer-Grid-popup').hover(); - await page.getByRole('menuitem', { name: 'table Table right' }).click(); + await page.getByRole('menuitem', { name: 'Table right' }).click(); await page.getByText('Associated records').hover(); const [request] = await Promise.all([ page.waitForRequest((request) => request.url().includes('uiSchemas:insertAdjacent')), diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaSettings.test.ts b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaSettings.test.ts index 3dfed1f5f8..34e99c5444 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaSettings.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaSettings.test.ts @@ -104,7 +104,7 @@ test.describe('actions schema settings', () => { // 配置出一个表单 await page.getByLabel('schema-initializer-Grid-popup').hover(); - await page.getByRole('menuitem', { name: 'form Form right' }).hover(); + await page.getByRole('menuitem', { name: 'Form right' }).hover(); await page.getByRole('menuitem', { name: 'Current collection' }).click(); await page.getByLabel('schema-initializer-Grid-form:').hover(); diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaSettings2.test.ts b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaSettings2.test.ts index 4eafb9234d..7e3ae3d6a4 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaSettings2.test.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/table/__e2e__/schemaSettings2.test.ts @@ -14,7 +14,7 @@ test.describe('save as template', () => { // 1. 创建一个区块,然后保存为模板 await mockPage().goto(); await page.getByLabel('schema-initializer-Grid-page:').hover(); - await page.getByRole('menuitem', { name: 'table Table right' }).hover(); + await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('menuitem', { name: 'Users' }).click(); await page.getByLabel('block-item-CardItem-users-').hover(); await page.getByLabel('designer-schema-settings-CardItem-blockSettings:table-users').hover(); diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/tableColumnSettings.tsx b/packages/core/client/src/modules/blocks/data-blocks/table/tableColumnSettings.tsx index 49f6f40996..ed9952bbfd 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/tableColumnSettings.tsx +++ b/packages/core/client/src/modules/blocks/data-blocks/table/tableColumnSettings.tsx @@ -19,6 +19,7 @@ import { useCollectionManager_deprecated } from '../../../../collection-manager' import { useFieldComponentName } from '../../../../common/useFieldComponentName'; import { useCollection } from '../../../../data-source'; import { fieldComponentSettingsItem } from '../../../../data-source/commonsSettingsItem'; +import { useFlag } from '../../../../flag-provider/hooks/useFlag'; import { useDesignable } from '../../../../schema-component'; import { useAssociationFieldContext } from '../../../../schema-component/antd/association-field/hooks'; import { useColumnSchema } from '../../../../schema-component/antd/table-v2/Table.Column.Decorator'; @@ -89,7 +90,13 @@ export const tableColumnSettings = new SchemaSettings({ }, useVisible() { const { fieldSchema } = useColumnSchema(); + const { isInSubTable } = useFlag(); const field: any = useField(); + + if (!isInSubTable) { + return true; + } + const path = field.path?.splice(field.path?.length - 1, 1); if (fieldSchema) { const isReadPretty = field.form.query(`${path.concat(`*.` + fieldSchema.name)}`).get('readPretty'); diff --git a/packages/core/client/src/modules/blocks/filter-blocks/__e2e__/schemaInitializer.test.ts b/packages/core/client/src/modules/blocks/filter-blocks/__e2e__/schemaInitializer.test.ts index c390d86974..ebdc65a7c9 100644 --- a/packages/core/client/src/modules/blocks/filter-blocks/__e2e__/schemaInitializer.test.ts +++ b/packages/core/client/src/modules/blocks/filter-blocks/__e2e__/schemaInitializer.test.ts @@ -18,7 +18,7 @@ test.describe('where filter block can be added', () => { // 1. 页面中创建一个 filter form,一个 filter collapse await page.getByLabel('schema-initializer-Grid-page:').hover(); - await page.getByRole('menuitem', { name: 'form Form right' }).nth(1).hover(); + await page.getByRole('menuitem', { name: 'Form right' }).nth(1).hover(); await page.getByRole('menuitem', { name: 'Users' }).click(); await page.getByLabel('schema-initializer-Grid-page:').hover(); await page.getByRole('menuitem', { name: 'Collapse right' }).hover(); @@ -99,7 +99,7 @@ test.describe('where filter block can be added', () => { await page.getByLabel('action-Action.Link-View record-view-users-table-1').click(); await page.waitForTimeout(1000); await page.getByLabel('schema-initializer-Grid-popup').hover(); - await page.getByRole('menuitem', { name: 'form Form right' }).hover(); + await page.getByRole('menuitem', { name: 'Form right' }).hover(); await page.getByRole('menuitem', { name: 'Roles' }).click(); await page.getByLabel('schema-initializer-Grid-filterForm:configureFields-roles').hover(); await page.getByRole('menuitem', { name: 'Role UID' }).click(); @@ -151,7 +151,7 @@ test.describe('where filter block can be added', () => { // 2. 测试用表单筛选其它区块 await page.getByLabel('schema-initializer-Grid-popup').hover(); - await page.getByRole('menuitem', { name: 'form Form right' }).hover(); + await page.getByRole('menuitem', { name: 'Form right' }).hover(); await page.getByRole('menuitem', { name: 'Users' }).click(); await page.getByLabel('schema-initializer-Grid-filterForm:configureFields-users').hover(); await page.getByRole('menuitem', { name: 'Nickname' }).click(); diff --git a/packages/core/client/src/modules/fields/component/Input/inputComponentSettings.tsx b/packages/core/client/src/modules/fields/component/Input/inputComponentSettings.tsx index 77932455cf..ca35671a72 100644 --- a/packages/core/client/src/modules/fields/component/Input/inputComponentSettings.tsx +++ b/packages/core/client/src/modules/fields/component/Input/inputComponentSettings.tsx @@ -42,6 +42,15 @@ export const ellipsisSettingsItem: SchemaSettingsItemType = { checked: !!schema['x-component-props']?.ellipsis, hidden, onChange: async (checked) => { + if (tableFieldSchema && tableFieldInstanceList) { + tableFieldInstanceList.forEach((fieldInstance) => { + fieldInstance.componentProps.ellipsis = checked; + }); + schema['x-component-props']['ellipsis'] = checked; + } else { + formField.componentProps.ellipsis = checked; + } + await dn.emit('patch', { schema: { 'x-uid': schema['x-uid'], @@ -51,19 +60,6 @@ export const ellipsisSettingsItem: SchemaSettingsItemType = { }, }, }); - - if (tableFieldSchema && tableFieldInstanceList) { - tableFieldInstanceList.forEach((fieldInstance) => { - fieldInstance.componentProps.ellipsis = checked; - }); - schema['x-component-props']['ellipsis'] = checked; - const path = formField.path?.splice(formField.path?.length - 1, 1); - formField.form.query(`${path.concat(`*.` + fieldSchema.name)}`).forEach((f) => { - f.componentProps.ellipsis = checked; - }); - } else { - formField.componentProps.ellipsis = checked; - } }, }; }, diff --git a/packages/core/client/src/modules/menu/__e2e__/schemaSettings1.test.ts b/packages/core/client/src/modules/menu/__e2e__/schemaSettings1.test.ts index 841738c711..77cd85e654 100644 --- a/packages/core/client/src/modules/menu/__e2e__/schemaSettings1.test.ts +++ b/packages/core/client/src/modules/menu/__e2e__/schemaSettings1.test.ts @@ -66,8 +66,6 @@ test.describe('group page menus schema settings', () => { await page.locator('.ant-select-dropdown').getByText('anchor page').click(); await page.getByLabel('Inner').click(); await page.getByRole('button', { name: 'OK', exact: true }).click(); - // 当前页面菜单会消失 - await expect(page.getByLabel('group page', { exact: true })).not.toBeVisible(); // 跳转到 anchor page 页面,会有一个名为 group page 的子页面菜单 await page.getByLabel('anchor page').click(); await expect(page.locator('.ant-layout-sider').getByLabel('group page')).toBeVisible(); diff --git a/packages/core/client/src/modules/page/__e2e__/blockInitializers.test.ts b/packages/core/client/src/modules/page/__e2e__/blockInitializers.test.ts index 161b30c0a0..2eb428f607 100644 --- a/packages/core/client/src/modules/page/__e2e__/blockInitializers.test.ts +++ b/packages/core/client/src/modules/page/__e2e__/blockInitializers.test.ts @@ -13,7 +13,7 @@ test.describe('page:addBlock', () => { test('当搜索不到数据时显示空状态', async ({ page, mockPage }) => { await mockPage().goto(); await page.getByLabel('schema-initializer-Grid-').hover(); - await page.getByRole('menuitem', { name: 'table Table right' }).hover(); + await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('textbox', { name: 'Search and select collection' }).fill('no match'); await expect(page.getByRole('menuitem', { name: 'No data' })).toBeVisible(); diff --git a/packages/core/client/src/modules/popup/__e2e__/schemaInitializer.test.ts b/packages/core/client/src/modules/popup/__e2e__/schemaInitializer.test.ts index 8ff0236d18..6ec7d36c03 100644 --- a/packages/core/client/src/modules/popup/__e2e__/schemaInitializer.test.ts +++ b/packages/core/client/src/modules/popup/__e2e__/schemaInitializer.test.ts @@ -86,7 +86,7 @@ test.describe('add blocks to the popup', () => { // 通过 Association records 创建关系区块 await page.getByLabel('schema-initializer-Grid-popup').hover(); - await page.getByRole('menuitem', { name: 'table Table right' }).hover(); + await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('menuitem', { name: 'Associated records' }).hover(); await page.getByRole('menuitem', { name: 'manyToMany' }).click(); await page.mouse.move(-300, 0); @@ -134,7 +134,7 @@ test.describe('add blocks to the popup', () => { // 通过 Association records 创建一个关系区块 await page.getByLabel('schema-initializer-Grid-popup').hover(); - await page.getByRole('menuitem', { name: 'table Table right' }).hover(); + await page.getByRole('menuitem', { name: 'Table right' }).hover(); await page.getByRole('menuitem', { name: 'Associated records' }).hover(); await page.getByRole('menuitem', { name: 'Roles' }).click(); await page diff --git a/packages/core/client/src/schema-component/antd/action/Action.Drawer.tsx b/packages/core/client/src/schema-component/antd/action/Action.Drawer.tsx index 5c8cb47e84..f552e4ecdc 100644 --- a/packages/core/client/src/schema-component/antd/action/Action.Drawer.tsx +++ b/packages/core/client/src/schema-component/antd/action/Action.Drawer.tsx @@ -13,6 +13,7 @@ import classNames from 'classnames'; // @ts-ignore import React, { FC, startTransition, useCallback, useEffect, useMemo, useState } from 'react'; import { ErrorBoundary, FallbackProps } from 'react-error-boundary'; +import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField'; import { ErrorFallback } from '../error-fallback'; import { useCurrentPopupContext } from '../page/PagePopups'; import { TabsContextProvider, useTabsContext } from '../tabs/context'; @@ -63,7 +64,7 @@ const ActionDrawerContent: FC<{ footerNodeName: string; field: any; schema: any } return ( - = observer( (props) => { - const { footerNodeName = 'Action.Drawer.Footer', zIndex: _zIndex, ...others } = props; + const { footerNodeName = 'Action.Drawer.Footer', zIndex: _zIndex, onClose: onCloseFromProps, ...others } = props; const { visible, setVisible, openSize = 'middle', drawerProps } = useActionContext(); const schema = useFieldSchema(); const field = useField(); @@ -105,7 +106,13 @@ export const InternalActionDrawer: React.FC = observer( const zIndex = _zIndex || parentZIndex + (props.level || 0); - const onClose = useCallback(() => setVisible(false, true), [setVisible]); + const onClose = useCallback( + (e) => { + setVisible(false, true); + onCloseFromProps?.(e); + }, + [setVisible, onCloseFromProps], + ); const keepFooterNode = useCallback( (s) => { return s['x-component'] === footerNodeName; diff --git a/packages/core/client/src/schema-component/antd/action/ActionBar.tsx b/packages/core/client/src/schema-component/antd/action/ActionBar.tsx index caa90551e4..12c8883c80 100644 --- a/packages/core/client/src/schema-component/antd/action/ActionBar.tsx +++ b/packages/core/client/src/schema-component/antd/action/ActionBar.tsx @@ -8,9 +8,9 @@ */ import { cx } from '@emotion/css'; -import { observer, useFieldSchema } from '@formily/react'; +import { useFieldSchema } from '@formily/react'; import { Space, SpaceProps } from 'antd'; -import React, { CSSProperties, useContext } from 'react'; +import React, { CSSProperties, FC, useContext } from 'react'; import { createPortal } from 'react-dom'; import { useSchemaInitializerRender } from '../../../application'; import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField'; @@ -58,92 +58,96 @@ const Portal: React.FC = (props) => { ); }; -export const ActionBar = withDynamicSchemaProps( - observer((props: any) => { - const { forceProps = {} } = useActionBarContext(); - // 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema - const { layout = 'two-columns', style, spaceProps, ...others } = { ...useProps(props), ...forceProps } as any; +const InternalActionBar: FC = (props: any) => { + const { forceProps = {} } = useActionBarContext(); + // 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema + const { layout = 'two-columns', style, spaceProps, ...others } = { ...useProps(props), ...forceProps } as any; - const fieldSchema = useFieldSchema(); - const { render } = useSchemaInitializerRender(fieldSchema['x-initializer'], fieldSchema['x-initializer-props']); - const { designable } = useDesignable(); + const fieldSchema = useFieldSchema(); + const { render } = useSchemaInitializerRender(fieldSchema['x-initializer'], fieldSchema['x-initializer-props']); + const { designable } = useDesignable(); - if (layout === 'one-column') { - return ( - - -
- {props.children && ( -
- - {fieldSchema.mapProperties((schema, key) => { - return ; - })} - -
- )} - {render({ style: { margin: '0 !important' } })} -
-
-
- ); - } - const hasActions = Object.keys(fieldSchema.properties ?? {}).length > 0; + if (layout === 'one-column') { return ( -
-
- - - {fieldSchema.mapProperties((schema, key) => { - if (schema['x-align'] !== 'left') { - return null; - } - return ; - })} - - - {fieldSchema.mapProperties((schema, key) => { - if (schema['x-align'] === 'left') { - return null; - } - return ; - })} - - -
- {render()} -
+ + +
+ { +
+ + {fieldSchema.mapProperties((schema, key) => { + return ; + })} + +
+ } + {render({ style: { margin: '0 !important' } })} +
+
+
); - }), + } + const hasActions = Object.keys(fieldSchema.properties ?? {}).length > 0; + return ( +
+
+ + + {fieldSchema.mapProperties((schema, key) => { + if (schema['x-align'] !== 'left') { + return null; + } + return ; + })} + + + {fieldSchema.mapProperties((schema, key) => { + if (schema['x-align'] === 'left') { + return null; + } + return ; + })} + + +
+ {render()} +
+ ); +}; + +export const ActionBar = withDynamicSchemaProps( + (props: any) => { + return ; + }, { displayName: 'ActionBar' }, ); diff --git a/packages/core/client/src/schema-component/antd/association-field/InternalNester.tsx b/packages/core/client/src/schema-component/antd/association-field/InternalNester.tsx index 623b0b2e67..0483d86cc8 100644 --- a/packages/core/client/src/schema-component/antd/association-field/InternalNester.tsx +++ b/packages/core/client/src/schema-component/antd/association-field/InternalNester.tsx @@ -54,9 +54,11 @@ export const InternalNester = observer( labelWidth = 120, labelWrap = true, } = fieldSchema?.['x-component-props'] || {}; + useEffect(() => { insertNester(schema.Nester); }, []); + return ( diff --git a/packages/core/client/src/schema-component/antd/association-field/InternalViewer.tsx b/packages/core/client/src/schema-component/antd/association-field/InternalViewer.tsx index c0d654da57..268197af1e 100644 --- a/packages/core/client/src/schema-component/antd/association-field/InternalViewer.tsx +++ b/packages/core/client/src/schema-component/antd/association-field/InternalViewer.tsx @@ -155,7 +155,7 @@ const RenderRecord = React.memo( ellipsisWithTooltipRef, enableLink, fieldNames?.label, - fieldSchema?.properties, + fieldSchema, getLabelUiSchema, insertViewer, isTreeCollection, diff --git a/packages/core/client/src/schema-component/antd/association-field/hooks.tsx b/packages/core/client/src/schema-component/antd/association-field/hooks.tsx index f298c3ef08..f44f67906f 100644 --- a/packages/core/client/src/schema-component/antd/association-field/hooks.tsx +++ b/packages/core/client/src/schema-component/antd/association-field/hooks.tsx @@ -37,7 +37,7 @@ export const useInsertSchema = (component) => { insertAfterBegin(cloneDeep(ss)); } }, - [component], + [component, fieldSchema, insertAfterBegin], ); return insert; }; diff --git a/packages/core/client/src/schema-component/antd/association-filter/ActionBarAssociationFilterAction.tsx b/packages/core/client/src/schema-component/antd/association-filter/ActionBarAssociationFilterAction.tsx index 6937249a0d..dd869fe976 100644 --- a/packages/core/client/src/schema-component/antd/association-filter/ActionBarAssociationFilterAction.tsx +++ b/packages/core/client/src/schema-component/antd/association-filter/ActionBarAssociationFilterAction.tsx @@ -8,12 +8,13 @@ */ import { Schema, useFieldSchema } from '@formily/react'; -import React, { useContext } from 'react'; +import React from 'react'; import { useTranslation } from 'react-i18next'; -import { SchemaComponentContext, createDesignable } from '../..'; +import { createDesignable } from '../..'; import { useAPIClient } from '../../../api-client'; import { useBlockRequestContext } from '../../../block-provider'; import { mergeFilter } from '../../../filter-provider/utils'; +import { useRefreshFieldSchema } from '../../../formily/NocoBaseRecursionField'; import { ActionInitializerItem } from '../../../schema-initializer/items/ActionInitializerItem'; /** @@ -22,11 +23,11 @@ import { ActionInitializerItem } from '../../../schema-initializer/items/ActionI * @returns */ export const ActionBarAssociationFilterAction = (props) => { - const { refresh } = useContext(SchemaComponentContext); + const refreshFieldSchema = useRefreshFieldSchema(); const fieldSchema = useFieldSchema(); const api = useAPIClient(); const { t } = useTranslation(); - const dn = createDesignable({ t, api, refresh, current: fieldSchema }); + const dn = createDesignable({ t, api, refresh: refreshFieldSchema, current: fieldSchema }); const { service, props: blockProps } = useBlockRequestContext(); dn.loadAPIClientEvents(); diff --git a/packages/core/client/src/schema-component/antd/form/Form.tsx b/packages/core/client/src/schema-component/antd/form/Form.tsx index 4408f715c6..688b19d7c3 100644 --- a/packages/core/client/src/schema-component/antd/form/Form.tsx +++ b/packages/core/client/src/schema-component/antd/form/Form.tsx @@ -9,13 +9,14 @@ import { FormLayout } from '@formily/antd-v5'; import { createForm } from '@formily/core'; -import { FieldContext, FormContext, observer, RecursionField, useField, useFieldSchema } from '@formily/react'; +import { FieldContext, FormContext, observer, useField, useFieldSchema } from '@formily/react'; import { Options, Result } from 'ahooks/es/useRequest/src/types'; import { ConfigProvider, Spin } from 'antd'; import React, { createContext, useContext, useEffect, useMemo } from 'react'; import { useAttach, useComponent } from '../..'; import { useRequest } from '../../../api-client'; import { useCollection_deprecated } from '../../../collection-manager'; +import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField'; import { GeneralSchemaDesigner, SchemaSettingsDivider, SchemaSettingsRemove } from '../../../schema-settings'; import { SchemaSettingsTemplate } from '../../../schema-settings/SchemaSettingsTemplate'; import { useSchemaTemplate } from '../../../schema-templates'; @@ -39,7 +40,7 @@ const FormComponent: React.FC = (props) => { - + @@ -65,7 +66,7 @@ const FormDecorator: React.FC = (props) => { - + {/* {children} */} diff --git a/packages/core/client/src/schema-component/antd/grid-card/GridCard.Designer.tsx b/packages/core/client/src/schema-component/antd/grid-card/GridCard.Designer.tsx index aeff4cb361..3296984d7f 100644 --- a/packages/core/client/src/schema-component/antd/grid-card/GridCard.Designer.tsx +++ b/packages/core/client/src/schema-component/antd/grid-card/GridCard.Designer.tsx @@ -52,33 +52,6 @@ export const GridCardDesigner = () => { const defaultResource = fieldSchema?.['x-decorator-props']?.resource || fieldSchema?.['x-decorator-props']?.association; - const columnCountSchema = useMemo(() => { - return { - 'x-component': 'Slider', - 'x-decorator': 'FormItem', - 'x-component-props': { - min: 1, - max: 24, - marks: columnCountMarks, - tooltip: { - formatter: (value) => `${value}${t('Column')}`, - }, - step: null, - }, - }; - }, [t]); - - const columnCountProperties = useMemo(() => { - return gridSizes.reduce((o, k) => { - o[k] = { - ...columnCountSchema, - title: t(screenSizeTitleMaps[k]), - description: `${t('Screen size')} ${screenSizeMaps[k]} ${t('pixels')}`, - }; - return o; - }, {}); - }, [columnCountSchema, t]); - const sort = defaultSort?.map((item: string) => { return item.startsWith('-') ? { diff --git a/packages/core/client/src/schema-component/antd/grid-card/GridCard.tsx b/packages/core/client/src/schema-component/antd/grid-card/GridCard.tsx index 956bbef36e..c9d385dd09 100644 --- a/packages/core/client/src/schema-component/antd/grid-card/GridCard.tsx +++ b/packages/core/client/src/schema-component/antd/grid-card/GridCard.tsx @@ -10,10 +10,11 @@ import { css, cx } from '@emotion/css'; import { FormLayout } from '@formily/antd-v5'; import { ArrayField } from '@formily/core'; -import { RecursionField, Schema, useField, useFieldSchema } from '@formily/react'; +import { Schema, useField, useFieldSchema } from '@formily/react'; import { List as AntdList, Col, PaginationProps } from 'antd'; -import React, { useCallback, useState } from 'react'; +import React, { useCallback } from 'react'; import { getCardItemSchema } from '../../../block-provider'; +import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField'; import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps'; import { withSkeletonComponent } from '../../../hoc/withSkeletonComponent'; import { SortableItem } from '../../common'; @@ -127,25 +128,18 @@ const InternalGridCard = withSkeletonComponent( const field = useField(); const Designer = useDesigner(); const height = useGridCardBodyHeight(); - const [schemaMap] = useState(new Map()); const getSchema = useCallback( (key) => { - if (!schemaMap.has(key)) { - schemaMap.set( - key, - new Schema({ - type: 'object', - properties: { - [key]: { - ...fieldSchema.properties['item'], - }, - }, - }), - ); - } - return schemaMap.get(key); + return new Schema({ + type: 'object', + properties: { + [key]: { + ...fieldSchema.properties['item'], + }, + }, + }); }, - [fieldSchema.properties, schemaMap], + [fieldSchema.properties], ); const onPaginationChange: PaginationProps['onChange'] = useCallback( @@ -218,13 +212,13 @@ const InternalGridCard = withSkeletonComponent( renderItem={(item, index) => { return ( - + > ); }} diff --git a/packages/core/client/src/schema-component/antd/grid/Grid.tsx b/packages/core/client/src/schema-component/antd/grid/Grid.tsx index 09f329790e..9d39a58ad8 100644 --- a/packages/core/client/src/schema-component/antd/grid/Grid.tsx +++ b/packages/core/client/src/schema-component/antd/grid/Grid.tsx @@ -297,7 +297,7 @@ const useRowProperties = () => { } return buf; }, []); - }, [Object.keys(fieldSchema.properties || {}).join(',')]); + }, [fieldSchema]); }; const useColProperties = () => { @@ -309,7 +309,7 @@ const useColProperties = () => { } return buf; }, []); - }, [Object.keys(fieldSchema.properties || {}).join(',')]); + }, [fieldSchema]); }; const DndWrapper = (props) => { @@ -361,7 +361,7 @@ export const Grid: any = observer( useEffect(() => { gridRef.current && setPrintContent?.(gridRef.current); - }, [gridRef.current]); + }, [setPrintContent]); const gridContextValue = useMemo(() => { return { @@ -498,7 +498,7 @@ Grid.Col = observer( width = `calc(${w}% - ${token.marginBlock}px * ${(showDivider ? cols.length + 1 : 0) / cols.length})`; } return { width }; - }, [cols?.length, schema?.['x-component-props']?.['width'], token.marginBlock]); + }, [cols.length, schema, showDivider, token.marginBlock]); const { isOver, setNodeRef } = useDroppable({ id: field.address.toString(), diff --git a/packages/core/client/src/schema-component/antd/list/List.tsx b/packages/core/client/src/schema-component/antd/list/List.tsx index 3741acba55..a1652a9082 100644 --- a/packages/core/client/src/schema-component/antd/list/List.tsx +++ b/packages/core/client/src/schema-component/antd/list/List.tsx @@ -10,10 +10,11 @@ import { css, cx } from '@emotion/css'; import { FormLayout } from '@formily/antd-v5'; import { ArrayField } from '@formily/core'; -import { RecursionField, Schema, useField, useFieldSchema } from '@formily/react'; +import { Schema, useField, useFieldSchema } from '@formily/react'; import { List as AntdList, PaginationProps, theme } from 'antd'; import React, { useCallback, useState } from 'react'; import { getCardItemSchema } from '../../../block-provider'; +import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField'; import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps'; import { withSkeletonComponent } from '../../../hoc/withSkeletonComponent'; import { SortableItem } from '../../common'; @@ -159,13 +160,13 @@ const InternalList = withSkeletonComponent( {field.value?.length ? field.value.map((item, index) => { return ( - + > ); }) : null} diff --git a/packages/core/client/src/schema-component/antd/menu/Menu.tsx b/packages/core/client/src/schema-component/antd/menu/Menu.tsx index 9087e1058a..2e9c8dffa7 100644 --- a/packages/core/client/src/schema-component/antd/menu/Menu.tsx +++ b/packages/core/client/src/schema-component/antd/menu/Menu.tsx @@ -11,7 +11,6 @@ import { css } from '@emotion/css'; import { FieldContext, observer, - RecursionField, SchemaContext, SchemaExpressionScopeContext, useField, @@ -23,14 +22,22 @@ import { Menu as AntdMenu, MenuProps } from 'antd'; import { createPortal } from 'react-dom'; import { useTranslation } from 'react-i18next'; import { createDesignable, DndContext, SchemaComponentContext, SortableItem, useDesignable, useDesigner } from '../..'; -import { Icon, useAPIClient, useParseURLAndParams, useSchemaInitializerRender } from '../../../'; +import { + Icon, + NocoBaseRecursionField, + useAPIClient, + useParseURLAndParams, + useSchemaInitializerRender, +} from '../../../'; import { useCollectMenuItems, useMenuItem } from '../../../hooks/useMenuItem'; import { useProps } from '../../hooks/useProps'; import { useMenuTranslation } from './locale'; import { MenuDesigner } from './Menu.Designer'; import { findKeysByUid, findMenuItem } from './util'; +import { useUpdate } from 'ahooks'; import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; +import { useRefreshComponent, useRefreshFieldSchema } from '../../../formily/NocoBaseRecursionField'; const subMenuDesignerCss = css` position: relative; @@ -210,7 +217,6 @@ const HeaderMenu = React.memo<{ onChange: any; onFocus: any; theme: any; - refreshId: number; }>( ({ schema, @@ -228,10 +234,6 @@ const HeaderMenu = React.memo<{ onChange, onFocus, theme, - /** - * Used to refresh the component - */ - refreshId, }) => { const { Component, getMenuItems } = useMenuItem(); const items = useMemo(() => { @@ -255,7 +257,7 @@ const HeaderMenu = React.memo<{ return result; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [children, designable, refreshId]); + }, [children, designable]); const handleSelect = useCallback( (info: { item; key; keyPath; domEvent }) => { @@ -327,17 +329,24 @@ const SideMenu = React.memo( t, api, designable, - refreshId, - refresh, }) => { - // Used to refresh component - refreshId; - const { Component, getMenuItems } = useMenuItem(); - // 使用 ref 用来防止闭包问题 - const sideMenuSchemaRef = useRef(sideMenuSchema); - sideMenuSchemaRef.current = sideMenuSchema; + const update = useUpdate(); + const refreshFieldSchema = useRefreshFieldSchema(); + const refreshComponent = useRefreshComponent(); + const refresh = useCallback( + (options?: { refreshParentSchema?: boolean }) => { + console.log('refresh'); + // refresh current component + update(); + // refresh fieldSchema context value + refreshFieldSchema?.(options); + // refresh component context value + refreshComponent?.(); + }, + [update, refreshFieldSchema, refreshComponent], + ); const handleSelect = useCallback( (info) => { @@ -348,7 +357,7 @@ const SideMenu = React.memo( const items = useMemo(() => { const result = getMenuItems(() => { - return ; + return ; }); if (designable) { @@ -362,7 +371,7 @@ const SideMenu = React.memo( t, api, refresh: refresh, - current: sideMenuSchemaRef.current, + current: sideMenuSchema, }); dn.loadAPIClientEvents(); dn.insertAdjacent('beforeEnd', s); @@ -374,8 +383,7 @@ const SideMenu = React.memo( } return result; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [api, designable, getMenuItems, refresh, render, sideMenuSchema, t, refreshId]); + }, [api, designable, getMenuItems, refresh, render, sideMenuSchema, t]); return ( mode === 'mix' && @@ -497,57 +505,44 @@ export const Menu: ComposedMenu = React.memo((props) => { }, [defaultSelectedKeys]); const ctx = useContext(SchemaComponentContext); - const refreshIdRef = useRef(0); - const ctxRefresh = ctx.refresh; - const refresh = useCallback(() => { - refreshIdRef.current += 1; - ctxRefresh?.(); - }, [ctxRefresh]); - - const newCtx = useMemo(() => ({ ...ctx, refresh }), [ctx, refresh]); return ( - - - - - {children} - - - - - + + + + {children} + + + + ); }); @@ -728,7 +723,7 @@ Menu.SubMenu = observer( ), children: getMenuItems(() => { - return ; + return ; }), }; }, [field.title, icon, schema, children, Designer]); diff --git a/packages/core/client/src/schema-component/antd/page/Page.tsx b/packages/core/client/src/schema-component/antd/page/Page.tsx index 34d50ebc95..b679d7f285 100644 --- a/packages/core/client/src/schema-component/antd/page/Page.tsx +++ b/packages/core/client/src/schema-component/antd/page/Page.tsx @@ -35,7 +35,6 @@ import { KeepAliveProvider, useKeepAlive } from '../../../route-switch/antd/admi import { useGetAriaLabelOfSchemaInitializer } from '../../../schema-initializer/hooks/useGetAriaLabelOfSchemaInitializer'; import { DndContext } from '../../common'; import { SortableItem } from '../../common/sortable-item'; -import { SchemaComponentContext, useNewRefreshContext } from '../../context'; import { SchemaComponent, SchemaComponentOptions } from '../../core'; import { useDesignable } from '../../hooks'; import { useToken } from '../__builtins__'; @@ -348,7 +347,6 @@ const NocoBasePageHeader = React.memo(({ activeKey, className }: { activeKey: st const { setTitle: setDocumentTitle } = useDocumentTitle(); const { t } = useTranslation(); const [pageTitle, setPageTitle] = useState(() => t(fieldSchema.title)); - const newRefreshCtx = useNewRefreshContext(); const disablePageHeader = fieldSchema['x-component-props']?.disablePageHeader; const enablePageTabs = fieldSchema['x-component-props']?.enablePageTabs; @@ -376,7 +374,7 @@ const NocoBasePageHeader = React.memo(({ activeKey, className }: { activeKey: st ); return ( - + <> {!disablePageHeader && ( } /> )} - + ); }); diff --git a/packages/core/client/src/schema-component/antd/record-picker/InputRecordPicker.tsx b/packages/core/client/src/schema-component/antd/record-picker/InputRecordPicker.tsx index 73ad7dd0e7..b72c65f657 100644 --- a/packages/core/client/src/schema-component/antd/record-picker/InputRecordPicker.tsx +++ b/packages/core/client/src/schema-component/antd/record-picker/InputRecordPicker.tsx @@ -8,7 +8,7 @@ */ import { ArrayField } from '@formily/core'; -import { RecursionField, useField, useFieldSchema } from '@formily/react'; +import { useField, useFieldSchema } from '@formily/react'; import { toArr } from '@formily/shared'; import { Select } from 'antd'; import { differenceBy, unionBy } from 'lodash'; @@ -18,6 +18,7 @@ import { useTableSelectorProps as useTsp, } from '../../../block-provider/TableSelectorProvider'; import { CollectionProvider_deprecated, useCollection_deprecated } from '../../../collection-manager'; +import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField'; import { FormProvider, SchemaComponentOptions } from '../../core'; import { useCompile } from '../../hooks'; import { ActionContextProvider, useActionContext } from '../action'; @@ -277,7 +278,7 @@ const Drawer: React.FunctionComponent<{ - { diff --git a/packages/core/client/src/schema-component/antd/record-picker/ReadPrettyRecordPicker.tsx b/packages/core/client/src/schema-component/antd/record-picker/ReadPrettyRecordPicker.tsx index 2e533fd72f..8a56aa0033 100644 --- a/packages/core/client/src/schema-component/antd/record-picker/ReadPrettyRecordPicker.tsx +++ b/packages/core/client/src/schema-component/antd/record-picker/ReadPrettyRecordPicker.tsx @@ -7,7 +7,7 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { observer, RecursionField, useFieldSchema } from '@formily/react'; +import { observer, useFieldSchema } from '@formily/react'; import { toArr } from '@formily/shared'; import React, { Fragment, useRef, useState } from 'react'; import { WithoutTableFieldResource } from '../../../block-provider'; @@ -16,6 +16,7 @@ import { BlockAssociationContext } from '../../../block-provider/BlockProvider'; import { CollectionProvider_deprecated } from '../../../collection-manager'; import { useCollectionManager } from '../../../data-source/collection/CollectionManagerProvider'; import { useCollection } from '../../../data-source/collection/CollectionProvider'; +import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField'; import { RecordProvider, useRecord } from '../../../record-provider'; import { FormProvider } from '../../core'; import { useCompile } from '../../hooks'; @@ -94,7 +95,7 @@ export const ReadPrettyRecordPicker: React.FC = observer( const renderWithoutTableFieldResourceProvider = () => ( - { diff --git a/packages/core/client/src/schema-component/antd/table-v2/Table.tsx b/packages/core/client/src/schema-component/antd/table-v2/Table.tsx index 0cfb3a4974..191e154bdd 100644 --- a/packages/core/client/src/schema-component/antd/table-v2/Table.tsx +++ b/packages/core/client/src/schema-component/antd/table-v2/Table.tsx @@ -13,11 +13,11 @@ import { SortableContext, SortableContextProps, useSortable } from '@dnd-kit/sor import { css, cx } from '@emotion/css'; import { ArrayField } from '@formily/core'; import { spliceArrayState } from '@formily/core/esm/shared/internals'; -import { Schema, SchemaOptionsContext, observer, useField, useFieldSchema } from '@formily/react'; +import { observer, Schema, SchemaOptionsContext, useField, useFieldSchema } from '@formily/react'; import { action } from '@formily/reactive'; import { uid } from '@formily/shared'; import { isPortalInBody } from '@nocobase/utils/client'; -import { useCreation, useDeepCompareEffect, useMemoizedFn } from 'ahooks'; +import { useDeepCompareEffect, useMemoizedFn } from 'ahooks'; import { Table as AntdTable, TableColumnProps } from 'antd'; import { default as classNames, default as cls } from 'classnames'; import _, { omit } from 'lodash'; @@ -29,11 +29,13 @@ import { BlockRequestLoadingContext, RecordIndexProvider, RecordProvider, + useAssociationNames, useCollection, useCollectionParentRecordData, useDataBlockProps, useDataBlockRequest, useDataBlockRequestData, + useDataBlockRequestGetter, useFlag, useSchemaInitializerRender, useTableSelectorContext, @@ -41,10 +43,15 @@ import { import { useACLFieldWhitelist } from '../../../acl/ACLProvider'; import { useTableBlockContext } from '../../../block-provider/TableBlockProvider'; import { isNewRecord } from '../../../data-source/collection-record/isNewRecord'; -import { NocoBaseRecursionField } from '../../../formily/NocoBaseRecursionField'; +import { + NocoBaseRecursionField, + RefreshComponentProvider, + useRefreshFieldSchema, +} from '../../../formily/NocoBaseRecursionField'; import { withDynamicSchemaProps } from '../../../hoc/withDynamicSchemaProps'; import { withSkeletonComponent } from '../../../hoc/withSkeletonComponent'; -import { useSatisfiedActionValues } from '../../../schema-settings/LinkageRules/useActionValues'; +import { LinkageRuleDataKeyMap } from '../../../schema-settings/LinkageRules/type'; +import { GetStyleRules } from '../../../schema-settings/LinkageRules/useActionValues'; import { HighPerformanceSpin } from '../../common/high-performance-spin/HighPerformanceSpin'; import { useToken } from '../__builtins__'; import { useAssociationFieldContext } from '../association-field/hooks'; @@ -99,17 +106,6 @@ function adjustColumnOrder(columns) { return [...leftFixedColumns, ...normalColumns, ...rightFixedColumns]; } -const useColumnsDeepMemoized = (columns: any[]) => { - const columnsJSON = getSchemaArrJSON(columns); - const oldObj = useCreation(() => ({ value: _.cloneDeep(columnsJSON) }), []); - - if (!_.isEqual(columnsJSON, oldObj.value)) { - oldObj.value = _.cloneDeep(columnsJSON); - } - - return oldObj.value; -}; - const TableCellRender: FC<{ record: any; columnSchema: Schema; @@ -118,7 +114,7 @@ const TableCellRender: FC<{ schemaToolbarBigger: string; field: ArrayField; index: number; -}> = React.memo(({ record, columnSchema, uiSchema, filterProperties, schemaToolbarBigger, field, index }) => { +}> = ({ record, columnSchema, uiSchema, filterProperties, schemaToolbarBigger, field, index }) => { const basePath = field.address.concat(record.__index || index); return ( @@ -135,9 +131,29 @@ const TableCellRender: FC<{ /> ); -}); +}; -TableCellRender.displayName = 'TableCellRender'; +const useRefreshTableColumns = () => { + const { params: blockParams, dataSource } = useDataBlockProps() || {}; + const { getDataBlockRequest } = useDataBlockRequestGetter(); + const { getAssociationAppends } = useAssociationNames(dataSource); + const prevParamsRef = useRef(blockParams); + const refreshFieldSchema = useRefreshFieldSchema(); + + const refresh = useCallback(() => { + const { appends } = getAssociationAppends(); + const service = getDataBlockRequest(); + + if (!_.isEqual(prevParamsRef.current.appends, appends)) { + prevParamsRef.current = { ...blockParams, appends }; + service.run(prevParamsRef.current); + } + + refreshFieldSchema?.(); + }, [blockParams, getAssociationAppends, getDataBlockRequest, refreshFieldSchema]); + + return { refresh }; +}; const useTableColumns = (props: { showDel?: any; isSubTable?: boolean }, paginationProps) => { const { token } = useToken(); @@ -146,15 +162,17 @@ const useTableColumns = (props: { showDel?: any; isSubTable?: boolean }, paginat const { schemaInWhitelist } = useACLFieldWhitelist(); const { designable } = useDesignable(); const { exists, render } = useSchemaInitializerRender(schema['x-initializer'], schema['x-initializer-props']); - const columnsSchemas = schema.reduceProperties((buf, s) => { - if (isColumnComponent(s) && schemaInWhitelist(Object.values(s.properties || {}).pop())) { - return buf.concat([s]); - } - return buf; - }, []); + const columnsSchemas = useMemo(() => { + return schema.reduceProperties((buf, s) => { + if (isColumnComponent(s) && schemaInWhitelist(Object.values(s.properties || {}).pop())) { + return buf.concat([s]); + } + return buf; + }, []); + }, [schema, schemaInWhitelist]); const { current, pageSize } = paginationProps; - const hasChangedColumns = useColumnsDeepMemoized(columnsSchemas); const { isPopupVisibleControlledByURL } = usePopupSettings(); + const { refresh } = useRefreshTableColumns(); const filterProperties = useCallback( (schema) => @@ -191,12 +209,14 @@ const useTableColumns = (props: { showDel?: any; isSubTable?: boolean }, paginat return { title: ( - + + + ), dataIndex, key: columnSchema.name, @@ -222,7 +242,6 @@ const useTableColumns = (props: { showDel?: any; isSubTable?: boolean }, paginat record, schema: columnSchema, rowIndex, - isSubTable: props.isSubTable, columnHidden, }; }, @@ -234,9 +253,7 @@ const useTableColumns = (props: { showDel?: any; isSubTable?: boolean }, paginat } as TableColumnProps; }), - // 这里不能把 columnsSchema 作为依赖,因为其每次都会变化,这里使用 hasChangedColumns 作为依赖 - // eslint-disable-next-line react-hooks/exhaustive-deps - [hasChangedColumns, field.address, collection, schemaToolbarBigger, designable, filterProperties], + [columnsSchemas, collection, refresh, designable, filterProperties, schemaToolbarBigger, field], ); const tableColumns = useMemo(() => { @@ -246,7 +263,7 @@ const useTableColumns = (props: { showDel?: any; isSubTable?: boolean }, paginat const res = [ ...columns, { - title: render(), + title: {render()}, dataIndex: 'TABLE_COLUMN_INITIALIZER', key: 'TABLE_COLUMN_INITIALIZER', render: designable @@ -602,19 +619,27 @@ const InternalBodyCellComponent = React.memo((props) => const inView = useContext(InViewContext); const isIndex = props.className?.includes('selection-column'); const { record, schema, rowIndex, isSubTable, ...others } = props; - const { valueMap } = useSatisfiedActionValues({ formValues: record, category: 'style', schema }); - const style = useMemo(() => Object.assign({ ...props.style }, valueMap), [props.style, valueMap]); - const skeletonStyle = { - height: '1em', - backgroundColor: token.colorFillSecondary, - borderRadius: `${token.borderRadiusSM}px`, - }; + const styleRules = schema?.[LinkageRuleDataKeyMap['style']]; + const [dynamicStyle, setDynamicStyle] = useState({}); + const style = useMemo(() => ({ ...props.style, ...dynamicStyle }), [props.style, dynamicStyle]); + const skeletonStyle = useMemo( + () => ({ + height: '1em', + backgroundColor: token.colorFillSecondary, + borderRadius: `${token.borderRadiusSM}px`, + }), + [token.borderRadiusSM, token.colorFillSecondary], + ); return ( - - {/* Lazy rendering cannot be used in sub-tables. */} - {isSubTable || inView || isIndex ? props.children :
} - + <> + {/* To improve rendering performance, do not render GetStyleRules component when no style rules are set */} + {!_.isEmpty(styleRules) && } + + {/* Lazy rendering cannot be used in sub-tables. */} + {isSubTable || inView || isIndex ? props.children :
} + + ); }); diff --git a/packages/core/client/src/schema-component/common/dnd-context/index.tsx b/packages/core/client/src/schema-component/common/dnd-context/index.tsx index c63b142af5..0774d30b17 100644 --- a/packages/core/client/src/schema-component/common/dnd-context/index.tsx +++ b/packages/core/client/src/schema-component/common/dnd-context/index.tsx @@ -47,7 +47,7 @@ const useDragEnd = (onDragEnd) => { const dn = createDesignable({ t, api, - refresh, + refresh: ({ refreshParentSchema = true } = {}) => refresh({ refreshParentSchema }), current: overSchema, }); @@ -70,7 +70,7 @@ const useDragEnd = (onDragEnd) => { return; } }, - [onDragEnd], + [api, onDragEnd, refresh, t], ); }; diff --git a/packages/core/client/src/schema-component/common/sortable-item/SortableItem.tsx b/packages/core/client/src/schema-component/common/sortable-item/SortableItem.tsx index 233614f825..7874a3fd02 100644 --- a/packages/core/client/src/schema-component/common/sortable-item/SortableItem.tsx +++ b/packages/core/client/src/schema-component/common/sortable-item/SortableItem.tsx @@ -78,7 +78,7 @@ const useSortableItemId = (props) => { if (props.id) { return props.id; } - return field.address.toString(); + return field.address?.toString(); }; interface SortableItemProps extends HTMLAttributes { diff --git a/packages/core/client/src/schema-component/context.ts b/packages/core/client/src/schema-component/context.ts index 6baec3ae7d..8a0c22f222 100644 --- a/packages/core/client/src/schema-component/context.ts +++ b/packages/core/client/src/schema-component/context.ts @@ -7,39 +7,8 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { useUpdate } from 'ahooks'; -import { createContext, useCallback, useContext, useMemo } from 'react'; -import { useRefreshFieldSchema } from '../formily/NocoBaseRecursionField'; +import { createContext } from 'react'; import { ISchemaComponentContext } from './types'; export const SchemaComponentContext = createContext({}); SchemaComponentContext.displayName = 'SchemaComponentContext.Provider'; - -/** - * Get a new refresh context, used to refresh the block that uses this hook - * @returns - */ -export const useNewRefreshContext = (refresh?: () => void) => { - const oldCtx = useContext(SchemaComponentContext); - const newCtx = useMemo(() => ({ ...oldCtx }), [oldCtx]); - const refreshFieldSchema = useRefreshFieldSchema(); - const update = useUpdate(); - - const _refresh = useCallback( - (options?: { refreshParent?: boolean }) => { - // refresh fieldSchema - refreshFieldSchema(options); - // refresh current component - update(); - refresh?.(); - }, - [refreshFieldSchema, update, refresh], - ); - - if (oldCtx) { - Object.assign(newCtx, oldCtx); - newCtx.refresh = _refresh; - } - - return newCtx; -}; diff --git a/packages/core/client/src/schema-component/core/DesignableSwitch.tsx b/packages/core/client/src/schema-component/core/DesignableSwitch.tsx index 888e80e4dc..c7f1181cc0 100644 --- a/packages/core/client/src/schema-component/core/DesignableSwitch.tsx +++ b/packages/core/client/src/schema-component/core/DesignableSwitch.tsx @@ -15,16 +15,19 @@ import { useTranslation } from 'react-i18next'; import { useDesignable } from '..'; import { useToken } from '../../style'; +const designableStyle = { + backgroundColor: 'var(--colorSettings) !important', +}; + +const unDesignableStyle = { + backgroundColor: 'transparent', +}; + export const DesignableSwitch = () => { const { designable, setDesignable } = useDesignable(); const { t } = useTranslation(); const { token } = useToken(); - const style = {}; - if (designable) { - style['backgroundColor'] = 'var(--colorSettings)'; - } else { - style['backgroundColor'] = 'transparent'; - } + const style = designable ? designableStyle : unDesignableStyle; // 快捷键切换编辑状态 useHotkeys('Ctrl+Shift+U', () => setDesignable(!designable), [designable]); diff --git a/packages/core/client/src/schema-component/core/SchemaComponent.tsx b/packages/core/client/src/schema-component/core/SchemaComponent.tsx index ecc2f0631e..d99b6b21ee 100644 --- a/packages/core/client/src/schema-component/core/SchemaComponent.tsx +++ b/packages/core/client/src/schema-component/core/SchemaComponent.tsx @@ -8,14 +8,14 @@ */ import { IRecursionFieldProps, ISchemaFieldProps, Schema } from '@formily/react'; -import { useUpdate } from 'ahooks'; -import React, { memo, useContext, useMemo } from 'react'; +import _ from 'lodash'; +import React, { createContext, memo, useContext, useMemo } from 'react'; import { NocoBaseRecursionField } from '../../formily/NocoBaseRecursionField'; import { SchemaComponentContext } from '../context'; import { SchemaComponentOptions } from './SchemaComponentOptions'; type SchemaComponentOnChange = { - onChange?: (s: Schema) => void; + onChange?: (s?: Schema) => void; }; function toSchema(schema?: any) { @@ -45,32 +45,49 @@ interface DistributedProps { distributed?: boolean; } +/** + * Used to pass the onChange callback function. + * + * The onChange callback will be triggered whenever a descendant Schema changes. + */ +export const SchemaComponentOnChangeContext = createContext({ onChange: _.noop }); + const RecursionSchemaComponent = memo((props: ISchemaFieldProps & SchemaComponentOnChange & DistributedProps) => { - const { components, scope, schema: _schema, distributed, onChange, ...others } = props; + const { components, scope, schema: _schema, distributed, onChange: _onChange, ...others } = props; const ctx = useContext(SchemaComponentContext); const schema = useMemo(() => toSchema(_schema), [_schema]); - const refresh = useUpdate(); const value = useMemo( () => ({ ...ctx, distributed: ctx.distributed == false ? false : distributed, - refresh: () => { - refresh(); - if (ctx.distributed === false || distributed === false) { - ctx.refresh?.(); - } - onChange?.(schema); + /** + * @deprecated + */ + refresh: _.noop, + }), + [ctx, distributed], + ); + + const { onChange: onChangeFromContext } = useContext(SchemaComponentOnChangeContext); + + const onChangeValue = useMemo( + () => ({ + onChange: () => { + _onChange?.(schema); + onChangeFromContext?.(); }, }), - [ctx, distributed, onChange, refresh, schema], + [_onChange, onChangeFromContext, schema], ); return ( - - - - - + + + + + + + ); }); diff --git a/packages/core/client/src/schema-component/core/SchemaComponentProvider.tsx b/packages/core/client/src/schema-component/core/SchemaComponentProvider.tsx index a5407e5297..00be1ed975 100644 --- a/packages/core/client/src/schema-component/core/SchemaComponentProvider.tsx +++ b/packages/core/client/src/schema-component/core/SchemaComponentProvider.tsx @@ -10,7 +10,7 @@ import { createForm } from '@formily/core'; import { FormProvider, Schema } from '@formily/react'; import { uid } from '@formily/shared'; -import { useUpdate } from 'ahooks'; +import _ from 'lodash'; import React, { useCallback, useContext, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { SchemaComponentContext } from '../context'; @@ -56,7 +56,6 @@ export const SchemaComponentProvider: React.FC = (prop const { designable, onDesignableChange, components, children } = props; const ctx = useContext(SchemaComponentContext); const ctxOptions = useSchemaOptionsContext(); - const refresh = useUpdate(); const [formId, setFormId] = useState(() => uid()); const form = useMemo(() => props.form || createForm(), [formId]); const { t } = useTranslation(); @@ -89,11 +88,14 @@ export const SchemaComponentProvider: React.FC = (prop scope, components, reset, - refresh, + /** + * @deprecated + */ + refresh: _.noop, designable: designableValue, setDesignable, }), - [components, designableValue, refresh, reset, scope, setDesignable], + [components, designableValue, reset, scope, setDesignable], ); return ( diff --git a/packages/core/client/src/schema-component/hooks/useDesignable.tsx b/packages/core/client/src/schema-component/hooks/useDesignable.tsx index d6e61ff161..655a83a952 100644 --- a/packages/core/client/src/schema-component/hooks/useDesignable.tsx +++ b/packages/core/client/src/schema-component/hooks/useDesignable.tsx @@ -10,6 +10,7 @@ import { GeneralField, Query } from '@formily/core'; import { ISchema, Schema, SchemaOptionsContext, useField, useFieldSchema } from '@formily/react'; import { uid } from '@formily/shared'; +import { useUpdate } from 'ahooks'; import { message } from 'antd'; import cloneDeep from 'lodash/cloneDeep'; import get from 'lodash/get'; @@ -17,6 +18,7 @@ import set from 'lodash/set'; import React, { ComponentType, useCallback, useContext, useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { APIClient, useAPIClient } from '../../api-client'; +import { useRefreshComponent, useRefreshFieldSchema } from '../../formily/NocoBaseRecursionField'; import { LAZY_COMPONENT_KEY } from '../../lazy-helper'; import { SchemaComponentContext } from '../context'; import { addAppVersion } from './addAppVersion'; @@ -29,7 +31,7 @@ interface CreateDesignableProps { model?: GeneralField; query?: Query; api?: APIClient; - refresh?: (options?: { refreshParent?: boolean }) => void; + refresh?: (options?: { refreshParentSchema?: boolean }) => void; onSuccess?: any; t?: any; /** @@ -219,7 +221,6 @@ export class Designable { message.success(t('Saved successfully'), 0.2); }); this.on('initializeActionContext', async ({ schema }) => { - this.refresh(); if (!schema?.['x-uid']) { return; } @@ -316,7 +317,7 @@ export class Designable { return false; } - refresh(options?: { refreshParent?: boolean }) { + refresh(options?: { refreshParentSchema?: boolean }) { const { refresh } = this.options; return refresh?.(options); } @@ -743,7 +744,7 @@ export function useFindComponent() { // TODO export function useDesignable() { - const { designable, setDesignable, refresh, reset } = useContext(SchemaComponentContext); + const { designable, setDesignable, refresh: refreshFromContext, reset } = useContext(SchemaComponentContext); const schemaOptions = useContext(SchemaOptionsContext); const components = useMemo(() => schemaOptions?.components || {}, [schemaOptions]); const DesignableBar = useMemo( @@ -752,6 +753,21 @@ export function useDesignable() { }, [], ); + const update = useUpdate(); + const refreshFieldSchema = useRefreshFieldSchema(); + const refreshComponent = useRefreshComponent(); + const refresh = useCallback( + (options?: { refreshParentSchema?: boolean }) => { + refreshFromContext?.(); + // refresh current component + update(); + // refresh fieldSchema context value + refreshFieldSchema?.(options); + // refresh component context value + refreshComponent?.(); + }, + [refreshFromContext, update, refreshFieldSchema, refreshComponent], + ); const field = useField(); const fieldSchema = useFieldSchema(); const api = useAPIClient(); diff --git a/packages/core/client/src/schema-component/types.ts b/packages/core/client/src/schema-component/types.ts index 9b0a2a9112..5fda6a962d 100644 --- a/packages/core/client/src/schema-component/types.ts +++ b/packages/core/client/src/schema-component/types.ts @@ -14,7 +14,7 @@ import React from 'react'; export interface ISchemaComponentContext { scope?: any; components?: SchemaReactComponents; - refresh?: (options?: { refreshParent?: boolean }) => void; + refresh?: (options?: { refreshParentSchema?: boolean }) => void; reset?: () => void; designable?: boolean; setDesignable?: (value: boolean) => void; diff --git a/packages/core/client/src/schema-initializer/items/InitializerWithSwitch.tsx b/packages/core/client/src/schema-initializer/items/InitializerWithSwitch.tsx index dead375076..071da29f09 100644 --- a/packages/core/client/src/schema-initializer/items/InitializerWithSwitch.tsx +++ b/packages/core/client/src/schema-initializer/items/InitializerWithSwitch.tsx @@ -10,6 +10,7 @@ import { merge } from '@formily/shared'; import React from 'react'; +import { useUpdate } from 'ahooks'; import { SchemaInitializerSwitch, useSchemaInitializer } from '../../application'; import { useCurrentSchema } from '../utils'; @@ -23,6 +24,7 @@ export const InitializerWithSwitch = (props) => { schema?.name || item?.schema?.name, ); const { insert } = useSchemaInitializer(); + const update = useUpdate(); return ( { return; } if (exists) { - return remove(); + remove(); + return update(); } const s = merge(schema || {}, item.schema || {}); item?.schemaInitialize?.(s); insert(s); + update(); }} /> ); diff --git a/packages/core/client/src/schema-settings/LinkageRules/useActionValues.ts b/packages/core/client/src/schema-settings/LinkageRules/useActionValues.ts index 0a4cdd7089..f54ee21e0d 100644 --- a/packages/core/client/src/schema-settings/LinkageRules/useActionValues.ts +++ b/packages/core/client/src/schema-settings/LinkageRules/useActionValues.ts @@ -7,14 +7,15 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { useState, useEffect, useCallback } from 'react'; -import { uid } from '@formily/shared'; import { Form, onFormValuesChange } from '@formily/core'; -import { useVariables, useLocalVariables } from '../../variables'; -import { useFieldSchema } from '@formily/react'; -import { LinkageRuleCategory, LinkageRuleDataKeyMap } from './type'; -import { getSatisfiedValueMap } from './compute-rules'; +import { Schema, useFieldSchema } from '@formily/react'; +import { uid } from '@formily/shared'; import { isEmpty } from 'lodash'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useLocalVariables, useVariables } from '../../variables'; +import { getSatisfiedValueMap } from './compute-rules'; +import { LinkageRuleCategory, LinkageRuleDataKeyMap } from './type'; + export function useSatisfiedActionValues({ formValues, category = 'default', @@ -33,10 +34,11 @@ export function useSatisfiedActionValues({ const variables = useVariables(); const localVariables = useLocalVariables({ currentForm: { values: formValues } as any }); const localSchema = schema ?? fieldSchema; - const linkageRules = rules ?? localSchema[LinkageRuleDataKeyMap[category]]; + const styleRules = rules ?? localSchema[LinkageRuleDataKeyMap[category]]; + const compute = useCallback(() => { - if (linkageRules && formValues) { - getSatisfiedValueMap({ rules: linkageRules, variables, localVariables }) + if (styleRules && formValues) { + getSatisfiedValueMap({ rules: styleRules, variables, localVariables }) .then((valueMap) => { if (!isEmpty(valueMap)) { setValueMap(valueMap); @@ -46,11 +48,11 @@ export function useSatisfiedActionValues({ throw new Error(err.message); }); } - }, [variables, localVariables, linkageRules, formValues]); + }, [variables, localVariables, styleRules, formValues]); + useEffect(() => { compute(); - }, [compute]); - useEffect(() => { + if (form) { const id = uid(); form.addEffects(id, () => { @@ -63,5 +65,22 @@ export function useSatisfiedActionValues({ }; } }, [form, compute]); + return { valueMap }; } + +export const GetStyleRules: React.FC<{ + record: Record; + schema: Schema; + onStyleChange?: (value: Record) => void; +}> = React.memo(({ record, schema, onStyleChange }) => { + const { valueMap } = useSatisfiedActionValues({ formValues: record, category: 'style', schema }); + + useEffect(() => { + onStyleChange(valueMap); + }, [onStyleChange, valueMap]); + + return null; +}); + +GetStyleRules.displayName = 'GetStyleRules'; diff --git a/packages/core/client/src/schema-settings/SchemaSettings.tsx b/packages/core/client/src/schema-settings/SchemaSettings.tsx index 49b4a918ad..974134321f 100644 --- a/packages/core/client/src/schema-settings/SchemaSettings.tsx +++ b/packages/core/client/src/schema-settings/SchemaSettings.tsx @@ -96,7 +96,7 @@ import { useRecord } from '../record-provider'; import { ActionContextProvider } from '../schema-component/antd/action/context'; import { SubFormProvider, useSubFormValue } from '../schema-component/antd/association-field/hooks'; import { FormDialog } from '../schema-component/antd/form-dialog'; -import { SchemaComponentContext, useNewRefreshContext } from '../schema-component/context'; +import { SchemaComponentContext } from '../schema-component/context'; import { FormProvider } from '../schema-component/core/FormProvider'; import { RemoteSchemaComponent } from '../schema-component/core/RemoteSchemaComponent'; import { SchemaComponent } from '../schema-component/core/SchemaComponent'; @@ -174,14 +174,6 @@ export const SchemaSettingsDropdown: React.FC = React.memo( // 单测中需要在首次就把菜单渲染出来,否则不会触发菜单的渲染进而报错。原因未知。 const [openDropdown, setOpenDropdown] = useState(process.env.__TEST__ ? true : false); const toolbarVisible = useContext(SchemaToolbarVisibleContext); - const refreshCtx = useContext(SchemaComponentContext); - const newRefreshCtx = useNewRefreshContext(refreshCtx.refresh); - - const newDn: any = useMemo(() => { - const result = Object.create(dn); - result.refresh = newRefreshCtx.refresh; - return result; - }, [dn, newRefreshCtx.refresh]); useEffect(() => { if (toolbarVisible) { @@ -214,30 +206,28 @@ export const SchemaSettingsDropdown: React.FC = React.memo( const items = getMenuItems(() => props.children); return ( - - - - + + -
{typeof title === 'string' ? {title} : title}
-
-
-
+ `} + menu={ + { + items, + 'data-testid': 'schema-settings-menu', + style: { maxHeight: dropdownMaxHeight, overflowY: 'auto' }, + } as any + } + > +
{typeof title === 'string' ? {title} : title}
+ + ); }); @@ -300,7 +290,6 @@ export const SchemaSettingsFormItemTemplate = function FormItemTemplate(props) { const sdn = createDesignable({ t, api, - refresh: dn.refresh.bind(dn), current: templateSchema.parent, }); sdn.loadAPIClientEvents(); @@ -371,7 +360,6 @@ export const SchemaSettingsFormItemTemplate = function FormItemTemplate(props) { const sdn = createDesignable({ t, api, - refresh: dn.refresh.bind(dn), current: gridSchema.parent, }); sdn.loadAPIClientEvents(); @@ -400,6 +388,7 @@ export const SchemaSettingsFormItemTemplate = function FormItemTemplate(props) { 'x-template-key': key, }, }); + dn.refresh(); }} > {t('Save as block template')} @@ -537,6 +526,7 @@ export const SchemaSettingsRemove: FC = (props) => { } await confirm?.onOk?.(); delete form.values[fieldSchema.name]; + dn.refresh({ refreshParentSchema: true }); removeActiveFieldName?.(fieldSchema.name as string); form?.query(new RegExp(`${fieldSchema.parent.name}.${fieldSchema.name}$`)).forEach((field: Field) => { // 如果字段被删掉,那么在提交的时候不应该提交这个字段 diff --git a/packages/core/client/src/schema-templates/BlockTemplateDetails.tsx b/packages/core/client/src/schema-templates/BlockTemplateDetails.tsx index f731316060..ffe60094cc 100644 --- a/packages/core/client/src/schema-templates/BlockTemplateDetails.tsx +++ b/packages/core/client/src/schema-templates/BlockTemplateDetails.tsx @@ -9,7 +9,7 @@ import { PageHeader as AntdPageHeader } from '@ant-design/pro-layout'; import { Input, Spin } from 'antd'; -import React, { useContext, useState } from 'react'; +import React, { useContext, useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; import { useAPIClient, useRequest, useSchemaTemplateManager } from '..'; import { useNavigateNoUpdate } from '../application/CustomRouterContextProvider'; @@ -73,6 +73,8 @@ export const BlockTemplateDetails = () => { const params = useParams(); const key = params?.key; const value = useContext(SchemaComponentContext); + const schemaComponentContext = useMemo(() => ({ ...value, designable: true }), [value]); + const { data, loading } = useRequest<{ data: any; }>({ @@ -82,9 +84,11 @@ export const BlockTemplateDetails = () => { filterByTk: key, }, }); + if (loading) { return ; } + return (
{ title={} />
- +
diff --git a/packages/plugins/@nocobase/plugin-acl/src/client/RolesManagement.tsx b/packages/plugins/@nocobase/plugin-acl/src/client/RolesManagement.tsx index af2133134d..d77138ec12 100644 --- a/packages/plugins/@nocobase/plugin-acl/src/client/RolesManagement.tsx +++ b/packages/plugins/@nocobase/plugin-acl/src/client/RolesManagement.tsx @@ -7,23 +7,22 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import React, { useState } from 'react'; -import { Card, Row, Col, Tabs, Divider } from 'antd'; +import { ISchema, Schema } from '@formily/react'; import { - CollectionProvider, CollectionProvider_deprecated, ResourceActionProvider, SchemaComponentContext, usePlugin, useSchemaComponentContext, } from '@nocobase/client'; -import { ISchema, Schema } from '@formily/react'; +import { Card, Col, Divider, Row, Tabs } from 'antd'; +import React, { useMemo, useState } from 'react'; +import ACLPlugin from '.'; +import { NewRole } from './NewRole'; +import { RolesManagerContext } from './RolesManagerProvider'; import { RolesMenu } from './RolesMenu'; import { useACLTranslation } from './locale'; -import ACLPlugin from '.'; -import { RolesManagerContext } from './RolesManagerProvider'; import { Permissions } from './permissions/Permissions'; -import { NewRole } from './NewRole'; const collection = { name: 'roles', @@ -75,9 +74,10 @@ export const RolesManagement: React.FC = () => { })); const [role, setRole] = useState(null); const scCtx = useSchemaComponentContext(); + const schemaComponentContext = useMemo(() => ({ ...scCtx, designable: false }), [scCtx]); return ( - + diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateAction.tsx b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateAction.tsx index dfcd999ac7..f67e9caeeb 100644 --- a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateAction.tsx +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/DuplicateAction.tsx @@ -8,11 +8,12 @@ */ import { css, cx } from '@emotion/css'; -import { RecursionField, observer, useField, useFieldSchema } from '@formily/react'; +import { observer, useField, useFieldSchema } from '@formily/react'; import { ActionContextProvider, CollectionProvider_deprecated, FormBlockContext, + NocoBaseRecursionField, PopupSettingsProvider, RecordProvider, TabsContextProvider, @@ -208,7 +209,7 @@ export const DuplicateAction = observer( - + diff --git a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/schemaSettings.test.ts b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/schemaSettings.test.ts index b415f8f645..f84db3e8a2 100644 --- a/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/schemaSettings.test.ts +++ b/packages/plugins/@nocobase/plugin-action-duplicate/src/client/__e2e__/schemaSettings.test.ts @@ -124,7 +124,7 @@ test.describe('direct duplicate & copy into the form and continue to fill in', ( .getByTestId('drawer-Action.Container-general2-Duplicate') .getByLabel('schema-initializer-Grid-popup') .hover(); - await page.getByRole('menuitem', { name: 'form Form right' }).hover(); + await page.getByRole('menuitem', { name: 'Form right' }).hover(); await page.getByRole('menuitem', { name: 'Current collection' }).click(); await page .getByTestId('drawer-Action.Container-general2-Duplicate') diff --git a/packages/plugins/@nocobase/plugin-block-workbench/src/client/WorkbenchBlock.tsx b/packages/plugins/@nocobase/plugin-block-workbench/src/client/WorkbenchBlock.tsx index f30474e5f1..1d2e459915 100644 --- a/packages/plugins/@nocobase/plugin-block-workbench/src/client/WorkbenchBlock.tsx +++ b/packages/plugins/@nocobase/plugin-block-workbench/src/client/WorkbenchBlock.tsx @@ -7,20 +7,21 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { RecursionField, observer, useFieldSchema } from '@formily/react'; +import { css } from '@emotion/css'; +import { observer, useFieldSchema } from '@formily/react'; import { CollectionContext, DataSourceContext, DndContext, + Icon, + NocoBaseRecursionField, + useBlockHeight, useDesignable, useSchemaInitializerRender, withDynamicSchemaProps, - Icon, - useBlockHeight, } from '@nocobase/client'; -import { css } from '@emotion/css'; -import { Space, List, Avatar, theme } from 'antd'; -import React, { createContext, useState, useEffect } from 'react'; +import { Avatar, List, Space, theme } from 'antd'; +import React, { createContext, useEffect, useState } from 'react'; import { WorkbenchLayout } from './workbenchBlockSettings'; const ConfigureActionsButton = observer( @@ -72,7 +73,7 @@ const InternalIcons = () => { {layout === WorkbenchLayout.Grid ? ( {fieldSchema.mapProperties((s, key) => ( - + ))} ) : ( @@ -103,7 +104,7 @@ const InternalIcons = () => { > } />} - title={} + title={} > ); diff --git a/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Calendar.tsx b/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Calendar.tsx index ac3d2be4d6..040ecfe89f 100644 --- a/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Calendar.tsx +++ b/packages/plugins/@nocobase/plugin-calendar/src/client/calendar/Calendar.tsx @@ -8,44 +8,44 @@ */ import { LeftOutlined, RightOutlined } from '@ant-design/icons'; -import { RecursionField, Schema, observer, useFieldSchema, useField } from '@formily/react'; +import { Schema, useField, useFieldSchema } from '@formily/react'; import { + ActionContextProvider, + CollectionProvider, + NocoBaseRecursionField, PopupContextProvider, RecordProvider, + SchemaComponentOptions, getLabelFormatValue, + handleDateChangeOnForm, useACLRoleContext, + useActionContext, useCollection, useCollectionParentRecordData, + useDesignable, + useFormBlockContext, useLazy, usePopupUtils, useProps, useToken, withDynamicSchemaProps, - useDesignable, - ActionContextProvider, - useActionContext, - CollectionProvider, - SchemaComponentOptions, - useFormBlockContext, - handleDateChangeOnForm, withSkeletonComponent, } from '@nocobase/client'; import type { Dayjs } from 'dayjs'; import dayjs from 'dayjs'; -import { get, cloneDeep } from 'lodash'; -import React, { useMemo, useState, useEffect, useCallback } from 'react'; -import { Calendar as BigCalendar, View, dayjsLocalizer } from 'react-big-calendar'; -import * as dates from 'react-big-calendar/lib/utils/dates'; +import { cloneDeep, get } from 'lodash'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { View } from 'react-big-calendar'; import { i18nt, useTranslation } from '../../locale'; import { CalendarRecordViewer, findEventSchema } from './CalendarRecordViewer'; import Header from './components/Header'; import { CalendarToolbarContext } from './context'; import GlobalStyle from './global.style'; import { useCalenderHeight } from './hook'; +import { addNew } from './schema'; import useStyle from './style'; import type { ToolbarProps } from './types'; import { formatDate } from './utils'; -import { addNew } from './schema'; interface Event { id: string; colorFieldValue: string; @@ -93,7 +93,7 @@ function Toolbar(props: ToolbarProps) { ); return ( - + ); } @@ -464,7 +464,7 @@ export const Calendar: any = withDynamicSchemaProps( - { @@ -18,7 +19,7 @@ export const CalendarRecordViewer: FC = (props) => { return null; } - return ; + return ; }; export function findEventSchema(schema: Schema) { diff --git a/packages/plugins/@nocobase/plugin-charts/src/client/ChartBlockInitializer.tsx b/packages/plugins/@nocobase/plugin-charts/src/client/ChartBlockInitializer.tsx index f37c66f88d..ef1d53362c 100644 --- a/packages/plugins/@nocobase/plugin-charts/src/client/ChartBlockInitializer.tsx +++ b/packages/plugins/@nocobase/plugin-charts/src/client/ChartBlockInitializer.tsx @@ -9,11 +9,12 @@ import { FormLayout } from '@formily/antd-v5'; import { Field } from '@formily/core'; -import { RecursionField, Schema, SchemaOptionsContext, observer, useField, useForm } from '@formily/react'; +import { Schema, SchemaOptionsContext, observer, useField, useForm } from '@formily/react'; import { APIClientProvider, FormDialog, FormProvider, + NocoBaseRecursionField, SchemaComponent, SchemaComponentOptions, css, @@ -49,7 +50,7 @@ export const Options = observer( setSchema(new Schema(template.configurableProperties || {})); } }, [form.values.type]); - return ; + return ; }, { displayName: 'Options' }, ); diff --git a/packages/plugins/@nocobase/plugin-data-source-main/src/client/__e2e__/fields/belongsTo/schemaInitializer.test.ts b/packages/plugins/@nocobase/plugin-data-source-main/src/client/__e2e__/fields/belongsTo/schemaInitializer.test.ts index 9d436d00f2..126786a75d 100644 --- a/packages/plugins/@nocobase/plugin-data-source-main/src/client/__e2e__/fields/belongsTo/schemaInitializer.test.ts +++ b/packages/plugins/@nocobase/plugin-data-source-main/src/client/__e2e__/fields/belongsTo/schemaInitializer.test.ts @@ -74,10 +74,26 @@ test.describe('form item & view form', () => { }, supportedOptions: ['oneToMany', 'manyToOne', 'manyToMany', 'oneToOneBelongsTo', 'oneToOneHasOne'], expectValue: async () => { - await expect(page.getByText(record.oneToMany.map((item: any) => item.id).join(','))).toBeVisible(); - await expect(page.getByText(record.manyToOne.id)).toBeVisible(); - await expect(page.getByText(record.manyToMany.map((item: any) => item.id).join(','))).toBeVisible(); - await expect(page.getByText(record.oneToOneBelongsTo.id)).toBeVisible(); + await expect( + page + .getByLabel('block-item-CollectionField-general-form-general.oneToMany-oneToMany') + .getByText(record.oneToMany.map((item: any) => item.id).join(',')), + ).toBeVisible(); + await expect( + page + .getByLabel('block-item-CollectionField-general-form-general.manyToOne-manyToOne') + .getByText(record.manyToOne.id), + ).toBeVisible(); + await expect( + page + .getByLabel('block-item-CollectionField-general-form-general.manyToMany-manyToMany') + .getByText(record.manyToMany.map((item: any) => item.id).join(',')), + ).toBeVisible(); + await expect( + page + .getByLabel('block-item-CollectionField-general-form-general.oneToOneBelongsTo-') + .getByText(record.oneToOneBelongsTo.id), + ).toBeVisible(); }, }); }); diff --git a/packages/plugins/@nocobase/plugin-data-source-main/src/client/__e2e__/fields/singleLineText/schemaInitializer.test.ts b/packages/plugins/@nocobase/plugin-data-source-main/src/client/__e2e__/fields/singleLineText/schemaInitializer.test.ts index 1bcef77994..205a1f8e58 100644 --- a/packages/plugins/@nocobase/plugin-data-source-main/src/client/__e2e__/fields/singleLineText/schemaInitializer.test.ts +++ b/packages/plugins/@nocobase/plugin-data-source-main/src/client/__e2e__/fields/singleLineText/schemaInitializer.test.ts @@ -15,7 +15,7 @@ test.describe('form item & filter form', () => { // 在页面上创建一个筛选表单,并在表单中添加一个字段 await page.getByLabel('schema-initializer-Grid-page:').hover(); - await page.getByRole('menuitem', { name: 'form Form right' }).nth(1).click(); + await page.getByRole('menuitem', { name: 'Form right' }).nth(1).click(); await page.getByRole('menuitem', { name: 'Users' }).click(); await page.getByLabel('schema-initializer-Grid-filterForm:configureFields-users').hover(); await page.getByRole('menuitem', { name: 'Nickname' }).click(); diff --git a/packages/plugins/@nocobase/plugin-data-source-manager/src/client/component/CollectionsManager/ConfigurationTable.tsx b/packages/plugins/@nocobase/plugin-data-source-manager/src/client/component/CollectionsManager/ConfigurationTable.tsx index 3076e395f6..50a871dee4 100644 --- a/packages/plugins/@nocobase/plugin-data-source-manager/src/client/component/CollectionsManager/ConfigurationTable.tsx +++ b/packages/plugins/@nocobase/plugin-data-source-manager/src/client/component/CollectionsManager/ConfigurationTable.tsx @@ -186,8 +186,10 @@ export const ConfigurationTable = () => { }); }); }; + const schemaComponentContext = useMemo(() => ({ ...ctx, designable: false, dataSourceData }), [ctx, dataSourceData]); + return ( - + { }; const ctx = useContext(SchemaComponentContext); + const schemaComponentContext = useMemo(() => ({ ...ctx, designable: false }), [ctx]); + return ( - + { return useContext(AvailableActionsContext); }; +const schemaComponentContext = { designable: false }; export const DataSourceTable = () => { const record = useRecord(); const plugin = usePlugin(PluginDataSourceManagerClient); return (
- + { return null; } - return ; + return ; }; diff --git a/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/gantt.tsx b/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/gantt.tsx index 3e495a47e6..27ad8c0123 100644 --- a/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/gantt.tsx +++ b/packages/plugins/@nocobase/plugin-gantt/src/client/components/gantt/gantt.tsx @@ -8,8 +8,9 @@ */ import { css, cx } from '@emotion/css'; -import { RecursionField, useFieldSchema } from '@formily/react'; +import { useFieldSchema } from '@formily/react'; import { + NocoBaseRecursionField, PopupContextProvider, RecordProvider, useAPIClient, @@ -520,8 +521,8 @@ export const Gantt: any = withDynamicSchemaProps((props: any) => { - - + +
{ // 1. 在页面中添加地图区块,因为没有配置 Access key 等信息,所以会显示错误提示 await page.getByLabel('schema-initializer-Grid-page:').hover(); - await page.getByRole('menuitem', { name: 'table Map right' }).hover(); + await page.getByRole('menuitem', { name: 'Map right' }).hover(); await page.getByRole('menuitem', { name: 'map', exact: true }).click(); await page.getByRole('button', { name: 'OK', exact: true }).click(); await expect( diff --git a/packages/plugins/@nocobase/plugin-map/src/client/components/MapBlockDrawer.tsx b/packages/plugins/@nocobase/plugin-map/src/client/components/MapBlockDrawer.tsx index eae0a4c608..441c0ff721 100644 --- a/packages/plugins/@nocobase/plugin-map/src/client/components/MapBlockDrawer.tsx +++ b/packages/plugins/@nocobase/plugin-map/src/client/components/MapBlockDrawer.tsx @@ -7,8 +7,13 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { RecursionField, useFieldSchema } from '@formily/react'; -import { useCollection, useCollectionRecordData, VariablePopupRecordProvider } from '@nocobase/client'; +import { useFieldSchema } from '@formily/react'; +import { + NocoBaseRecursionField, + useCollection, + useCollectionRecordData, + VariablePopupRecordProvider, +} from '@nocobase/client'; import React, { FC, useMemo } from 'react'; export const MapBlockDrawer: FC = (props) => { @@ -32,7 +37,7 @@ export const MapBlockDrawer: FC = (props) => { return ( - + ); }; diff --git a/packages/plugins/@nocobase/plugin-mobile-client/src/client/core/schema/components/page/Page.tsx b/packages/plugins/@nocobase/plugin-mobile-client/src/client/core/schema/components/page/Page.tsx index bcd2fc08aa..0705339124 100644 --- a/packages/plugins/@nocobase/plugin-mobile-client/src/client/core/schema/components/page/Page.tsx +++ b/packages/plugins/@nocobase/plugin-mobile-client/src/client/core/schema/components/page/Page.tsx @@ -7,8 +7,16 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { RecursionField, useField, useFieldSchema } from '@formily/react'; -import { ActionBarProvider, SortableItem, TabsContextProvider, css, cx, useDesigner } from '@nocobase/client'; +import { useField, useFieldSchema } from '@formily/react'; +import { + ActionBarProvider, + NocoBaseRecursionField, + SortableItem, + TabsContextProvider, + css, + cx, + useDesigner, +} from '@nocobase/client'; import { TabsProps } from 'antd'; import React, { useCallback } from 'react'; import { useSearchParams } from 'react-router-dom'; @@ -106,34 +114,34 @@ const InternalPage: React.FC = (props) => { })} > {isHeaderEnabled && ( - { return 'MHeader' === s['x-component']; }} - > + > )} - { return 'Tabs' === s['x-component'] || 'Grid' === s['x-component'] || 'Grid.Row' === s['x-component']; }} - > + >
{!tabsSchema && ( - { return s['x-component'] !== 'MHeader'; }} - > + >
)} diff --git a/packages/plugins/@nocobase/plugin-mobile/src/client/__e2e__/pageHeader.test.ts b/packages/plugins/@nocobase/plugin-mobile/src/client/__e2e__/pageHeader.test.ts index ab9d113afc..c6786e5a06 100644 --- a/packages/plugins/@nocobase/plugin-mobile/src/client/__e2e__/pageHeader.test.ts +++ b/packages/plugins/@nocobase/plugin-mobile/src/client/__e2e__/pageHeader.test.ts @@ -172,7 +172,7 @@ test.describe('PageHeader', () => { test('Item:add and remove', async ({ page }) => { // 添加页面内容 await page.getByLabel('schema-initializer-Grid-mobile:addBlock').hover(); - await page.getByRole('menuitem', { name: 'form Markdown' }).click(); + await page.getByRole('menuitem', { name: 'Markdown' }).click(); await expect(page.getByLabel('block-item-Markdown.Void-')).toBeVisible(); await page.getByLabel('action-Action-undefined').click(); diff --git a/packages/plugins/@nocobase/plugin-mobile/src/client/adaptor-of-desktop/ActionDrawer.tsx b/packages/plugins/@nocobase/plugin-mobile/src/client/adaptor-of-desktop/ActionDrawer.tsx index ba7744de0b..25295c119c 100644 --- a/packages/plugins/@nocobase/plugin-mobile/src/client/adaptor-of-desktop/ActionDrawer.tsx +++ b/packages/plugins/@nocobase/plugin-mobile/src/client/adaptor-of-desktop/ActionDrawer.tsx @@ -7,8 +7,15 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { ISchema, observer, RecursionField, useField, useFieldSchema } from '@formily/react'; -import { Action, SchemaComponent, useActionContext, useZIndexContext, zIndexContext } from '@nocobase/client'; +import { ISchema, observer, useField, useFieldSchema } from '@formily/react'; +import { + Action, + NocoBaseRecursionField, + SchemaComponent, + useActionContext, + useZIndexContext, + zIndexContext, +} from '@nocobase/client'; import { ConfigProvider } from 'antd'; import { Popup } from 'antd-mobile'; import { CloseOutline } from 'antd-mobile-icons'; @@ -109,7 +116,7 @@ export const ActionDrawerUsedInMobile: any = observer((props: { footerNodeName?: )} {footerSchema ? (
- { {footerSchema && (
- { const result = fieldSchema.mapProperties((schema, key) => { - return } key={key}>; + return ( + } key={key}> + ); }); return result; diff --git a/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/header/navigation-bar/MobilePageNavigationBar.tsx b/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/header/navigation-bar/MobilePageNavigationBar.tsx index b94ac7009d..ba7b08ff38 100644 --- a/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/header/navigation-bar/MobilePageNavigationBar.tsx +++ b/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/header/navigation-bar/MobilePageNavigationBar.tsx @@ -7,8 +7,8 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { RecursionField, useFieldSchema } from '@formily/react'; -import { cx, SchemaToolbarProvider } from '@nocobase/client'; +import { useFieldSchema } from '@formily/react'; +import { cx, NocoBaseRecursionField, SchemaToolbarProvider } from '@nocobase/client'; import { NavBar } from 'antd-mobile'; import React, { FC } from 'react'; @@ -31,12 +31,12 @@ export const MobilePageNavigationBar: FC = () => { back={null} left={ - + } right={ - + } > @@ -44,7 +44,7 @@ export const MobilePageNavigationBar: FC = () => { - +
); diff --git a/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/header/navigation-bar/actions/MobileNavigationActionBar.tsx b/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/header/navigation-bar/actions/MobileNavigationActionBar.tsx index 9f3967383b..29e46f6c3b 100644 --- a/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/header/navigation-bar/actions/MobileNavigationActionBar.tsx +++ b/packages/plugins/@nocobase/plugin-mobile/src/client/pages/dynamic-page/header/navigation-bar/actions/MobileNavigationActionBar.tsx @@ -8,16 +8,17 @@ */ import { cx } from '@emotion/css'; -import { SpaceProps } from 'antd'; -import React, { CSSProperties, useContext } from 'react'; -import { ISchema, RecursionField, observer, useFieldSchema } from '@formily/react'; +import { ISchema, observer, useFieldSchema } from '@formily/react'; import { DndContext, + NocoBaseRecursionField, useProps, useSchemaInitializerRender, useSchemaToolbar, withDynamicSchemaProps, } from '@nocobase/client'; +import { SpaceProps } from 'antd'; +import React, { CSSProperties, useContext } from 'react'; export interface ActionBarProps { style?: CSSProperties; @@ -81,7 +82,7 @@ export const MobileNavigationActionBar = withDynamicSchemaProps( {position === 'left' && render({})} {props.children && (
- schema['x-position'] === position} @@ -91,7 +92,7 @@ export const MobileNavigationActionBar = withDynamicSchemaProps( {position === 'right' && render({})}
) : ( - schema['x-position'] === position} diff --git a/packages/plugins/@nocobase/plugin-notification-manager/src/client/manager/channel/components/index.tsx b/packages/plugins/@nocobase/plugin-notification-manager/src/client/manager/channel/components/index.tsx index 8bb221d201..0f1ab890e9 100644 --- a/packages/plugins/@nocobase/plugin-notification-manager/src/client/manager/channel/components/index.tsx +++ b/packages/plugins/@nocobase/plugin-notification-manager/src/client/manager/channel/components/index.tsx @@ -18,8 +18,8 @@ import { useAsyncData, useSchemaComponentContext, } from '@nocobase/client'; -import { Button, Dropdown, Empty, Card } from 'antd'; -import React, { useState } from 'react'; +import { Button, Card, Dropdown, Empty } from 'antd'; +import React, { useMemo, useState } from 'react'; import channelCollection from '../../../../collections/channel'; import messageLogCollection from '../../../../collections/messageLog'; import { useNotificationTranslation } from '../../../locale'; @@ -125,10 +125,11 @@ export const ChannelManager = () => { const { t } = useNotificationTranslation(); const notificationTypes = useNotificationTypes(); const scCtx = useSchemaComponentContext(); + const schemaComponentContext = useMemo(() => ({ ...scCtx, designable: false }), [scCtx]); return ( - + { const { t } = useNotificationTranslation(); const scCtx = useSchemaComponentContext(); const notificationTypes = useNotificationTypes(); + const schemaComponentContext = useMemo(() => ({ ...scCtx, designable: false }), [scCtx]); + return ( - + {}); const nodeComponents = {}; nodes.forEach((item) => { @@ -452,6 +452,8 @@ export function SchemaConfig({ value, onChange }) { background: var(--nb-box-bg); } `, + // Using ref to call refresh ensures accessing the latest refresh function + onClose: () => refreshRef.current(), }, properties: { tabs: { @@ -501,6 +503,8 @@ export function SchemaConfig({ value, onChange }) { [form, onChange, schema], ); + refreshRef.current = refresh; + return (