diff --git a/packages/core/client/src/block-provider/hooks/useDataBlockSourceId.tsx b/packages/core/client/src/block-provider/hooks/useDataBlockSourceId.tsx index d677198b7..3b2ff81af 100644 --- a/packages/core/client/src/block-provider/hooks/useDataBlockSourceId.tsx +++ b/packages/core/client/src/block-provider/hooks/useDataBlockSourceId.tsx @@ -5,12 +5,19 @@ import { useCollectionParentRecordData, useCollectionRecordData, } from '../..'; +import { useSourceKey } from '../../modules/blocks/useSourceKey'; /** * @internal + * @deprecated + * 已弃用(该方法现在只是用来兼容旧版 Schema 的),请通过各个区块的 x-use-decorator-props 中获取 sourceId + * * 注意:这里有一个需要更改 schema 才能解决的问题,就是在获取 sourceId 的时候无法确定(在关系字段和当前表同表时) * 是需要从 recordData 还是 parentRecordData 中获取;解决方法是通过更改 schema,在不同类型的关系区块中 * (`通过点击关系字段按钮打开的弹窗中创建的非关系字段区块`和`关系字段区块`)使用不同的 hook。 + * + * 更新:上面所说的“需要更改 schema 才能解决的问题”已在这个任务中更改:https://nocobase.height.app/T-3848/description + * * @param param0 * @returns */ @@ -18,26 +25,19 @@ export const useDataBlockSourceId = ({ association }: { association: string }) = const recordData = useCollectionRecordData(); const parentRecordData = useCollectionParentRecordData(); const cm = useCollectionManager(); - const collectionOutsideBlock = useCollection(); + const currentRecordCollection = useCollection(); + const sourceKey = useSourceKey(association); if (!association) return; - const associationField = cm.getCollectionField(association); - const associationCollection = cm.getCollection(associationField.collectionName); + const sourceCollection = cm.getCollection(association.split('.')[0]); if ( - collectionOutsideBlock.name === associationCollection.name || - collectionOutsideBlock.getParentCollectionsName?.().includes(associationCollection.name) + currentRecordCollection.name === sourceCollection.name || + currentRecordCollection.getParentCollectionsName?.().includes(sourceCollection.name) ) { - return recordData?.[ - associationField.sourceKey || - associationCollection.filterTargetKey || - associationCollection.getPrimaryKey() || - 'id' - ]; + return recordData?.[sourceKey]; } - return parentRecordData?.[ - associationField.sourceKey || associationCollection.filterTargetKey || associationCollection.getPrimaryKey() || 'id' - ]; + return parentRecordData?.[sourceKey]; }; 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 c8d361d61..c557f8b35 100644 --- a/packages/core/client/src/data-source/data-block/DataBlockRequestProvider.tsx +++ b/packages/core/client/src/data-source/data-block/DataBlockRequestProvider.tsx @@ -7,6 +7,7 @@ import { AllDataBlockProps, useDataBlockProps } from './DataBlockProvider'; import { useDataBlockResource } from './DataBlockResourceProvider'; import { useDataSourceHeaders } from '../utils'; import { useDataLoadingMode } from '../../modules/blocks/data-blocks/details-multi/setDataLoadingModeSettingsItem'; +import { useSourceKey } from '../../modules/blocks/useSourceKey'; export const BlockRequestContext = createContext>(null); BlockRequestContext.displayName = 'BlockRequestContext'; @@ -57,14 +58,15 @@ function useParentRequest(options: Omit) { const api = useAPIClient(); const dataBlockProps = useDataBlockProps(); const headers = useDataSourceHeaders(dataBlockProps.dataSource); + const sourceKey = useSourceKey(association); return useRequest( async () => { if (parentRecord) return Promise.resolve({ data: parentRecord }); - if (!association) return Promise.resolve({ data: undefined }); + if (!association || !sourceKey) return Promise.resolve({ data: undefined }); // "association": "Collection.Field" const arr = association.split('.'); // :get/ - const url = `${arr[0]}:get/${sourceId}`; + const url = `${arr[0]}:get?filter[${sourceKey}]=${sourceId}`; const res = await api.request({ url, headers }); return res.data; }, diff --git a/packages/core/client/src/index.ts b/packages/core/client/src/index.ts index eb23cc2e2..b8586d3f2 100644 --- a/packages/core/client/src/index.ts +++ b/packages/core/client/src/index.ts @@ -61,6 +61,7 @@ export * from './modules/blocks/data-blocks/table'; export * from './modules/blocks/data-blocks/form'; export * from './modules/blocks/data-blocks/table-selector'; export * from './modules/blocks/data-blocks/details-multi/setDataLoadingModeSettingsItem'; +export * from './modules/blocks/useSourceIdCommon'; export * as __UNSAFE__ from './unsafe'; export type { diff --git a/packages/core/client/src/modules/blocks/data-blocks/details-multi/hooks/useDetailsWithPaginationDecoratorProps.ts b/packages/core/client/src/modules/blocks/data-blocks/details-multi/hooks/useDetailsWithPaginationDecoratorProps.ts index 5d3935fde..68f9750dc 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/details-multi/hooks/useDetailsWithPaginationDecoratorProps.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/details-multi/hooks/useDetailsWithPaginationDecoratorProps.ts @@ -1,4 +1,4 @@ -import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId'; +import { useSourceIdCommon } from '../../../useSourceIdCommon'; export function useDetailsWithPaginationDecoratorProps(props) { let sourceId; @@ -6,7 +6,7 @@ export function useDetailsWithPaginationDecoratorProps(props) { // association 的值是固定不变的,所以可以在条件中使用 hooks if (props.association) { // eslint-disable-next-line react-hooks/rules-of-hooks - sourceId = useDataBlockSourceId({ association: props.association }); + sourceId = useSourceIdCommon(props.association); } return { diff --git a/packages/core/client/src/modules/blocks/data-blocks/details-single/RecordReadPrettyFormBlockInitializer.tsx b/packages/core/client/src/modules/blocks/data-blocks/details-single/RecordReadPrettyFormBlockInitializer.tsx index 6d6127fbb..b7e09bd9a 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/details-single/RecordReadPrettyFormBlockInitializer.tsx +++ b/packages/core/client/src/modules/blocks/data-blocks/details-single/RecordReadPrettyFormBlockInitializer.tsx @@ -69,6 +69,7 @@ export function useCreateSingleDetailsSchema() { ? { association, dataSource: item.dataSource, + isCurrent: true, } : { collectionName: item.collectionName || item.name, diff --git a/packages/core/client/src/modules/blocks/data-blocks/details-single/createDetailsUISchema.ts b/packages/core/client/src/modules/blocks/data-blocks/details-single/createDetailsUISchema.ts index 6c8aeab53..ded79d788 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/details-single/createDetailsUISchema.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/details-single/createDetailsUISchema.ts @@ -6,9 +6,14 @@ export function createDetailsUISchema(options: { collectionName?: string; association?: string; templateSchema?: ISchema; + /** + * 如果为 true,则表示当前创建的区块 record 就是 useRecord 返回的 record + */ + isCurrent?: boolean; }): ISchema { const { collectionName, dataSource, association, templateSchema } = options; const resourceName = association || collectionName; + const isCurrentObj = options.isCurrent ? { 'x-is-current': true } : {}; if (!dataSource) { throw new Error('dataSource are required'); @@ -29,6 +34,7 @@ export function createDetailsUISchema(options: { 'x-toolbar': 'BlockSchemaToolbar', 'x-settings': 'blockSettings:details', 'x-component': 'CardItem', + ...isCurrentObj, properties: { [uid()]: { type: 'void', diff --git a/packages/core/client/src/modules/blocks/data-blocks/details-single/hooks/useDetailsDecoratorProps.ts b/packages/core/client/src/modules/blocks/data-blocks/details-single/hooks/useDetailsDecoratorProps.ts index 71cd7e870..27562dcdb 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/details-single/hooks/useDetailsDecoratorProps.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/details-single/hooks/useDetailsDecoratorProps.ts @@ -1,6 +1,16 @@ +import { useFieldSchema } from '@formily/react'; import { useParamsFromRecord } from '../../../../../block-provider/BlockProvider'; -import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId'; +import { + useCollectionParentRecordData, + useCollectionRecordData, +} from '../../../../../data-source/collection-record/CollectionRecordProvider'; +import { useSourceKey } from '../../../useSourceKey'; +/** + * 应用在通过 Current record 选项创建的区块中(弹窗中的 Add block 菜单) + * @param props + * @returns + */ export function useDetailsDecoratorProps(props) { const params = useParamsFromRecord(); let sourceId; @@ -8,7 +18,7 @@ export function useDetailsDecoratorProps(props) { // association 的值是固定不变的,所以可以在条件中使用 hooks if (props.association) { // eslint-disable-next-line react-hooks/rules-of-hooks - sourceId = useDataBlockSourceId({ association: props.association }); + sourceId = useDetailsSourceId(props.association); } return { @@ -16,3 +26,18 @@ export function useDetailsDecoratorProps(props) { sourceId, }; } + +export function useDetailsSourceId(association: string) { + const fieldSchema = useFieldSchema(); + const recordData = useCollectionRecordData(); + const parentRecordData = useCollectionParentRecordData(); + const sourceKey = useSourceKey(association); + + if (!association) return; + + if (fieldSchema['x-is-current']) { + return parentRecordData?.[sourceKey]; + } + + return recordData?.[sourceKey]; +} diff --git a/packages/core/client/src/modules/blocks/data-blocks/form/RecordFormBlockInitializer.tsx b/packages/core/client/src/modules/blocks/data-blocks/form/RecordFormBlockInitializer.tsx index c207b7550..560f34e6a 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/form/RecordFormBlockInitializer.tsx +++ b/packages/core/client/src/modules/blocks/data-blocks/form/RecordFormBlockInitializer.tsx @@ -49,6 +49,7 @@ export function useCreateEditFormBlock() { ? { association, dataSource: item.dataSource, + isCurrent: true, } : { collectionName: item.collectionName || item.name, diff --git a/packages/core/client/src/modules/blocks/data-blocks/form/createEditFormBlockUISchema.ts b/packages/core/client/src/modules/blocks/data-blocks/form/createEditFormBlockUISchema.ts index 7a8372d0c..9d9021a42 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/form/createEditFormBlockUISchema.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/form/createEditFormBlockUISchema.ts @@ -7,11 +7,16 @@ interface EditFormBlockOptions { collectionName?: string; association?: string; templateSchema?: ISchema; + /** + * 如果为 true,则表示当前创建的区块 record 就是 useRecord 返回的 record + */ + isCurrent?: boolean; } export function createEditFormBlockUISchema(options: EditFormBlockOptions): ISchema { const { collectionName, dataSource, association, templateSchema } = options; const resourceName = association || collectionName; + const isCurrentObj = options.isCurrent ? { 'x-is-current': true } : {}; if (!dataSource) { throw new Error('dataSource are required'); @@ -34,6 +39,7 @@ export function createEditFormBlockUISchema(options: EditFormBlockOptions): ISch 'x-toolbar': 'BlockSchemaToolbar', 'x-settings': 'blockSettings:editForm', 'x-component': 'CardItem', + ...isCurrentObj, properties: { [uid()]: { type: 'void', diff --git a/packages/core/client/src/modules/blocks/data-blocks/form/hooks/useCreateFormBlockDecoratorProps.ts b/packages/core/client/src/modules/blocks/data-blocks/form/hooks/useCreateFormBlockDecoratorProps.ts index d0e78303c..87fdb9c64 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/form/hooks/useCreateFormBlockDecoratorProps.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/form/hooks/useCreateFormBlockDecoratorProps.ts @@ -1,4 +1,4 @@ -import { useFormBlockSourceId } from './useFormBlockSourceId'; +import { useSourceIdCommon } from '../../../useSourceIdCommon'; export function useCreateFormBlockDecoratorProps(props) { let sourceId; @@ -6,7 +6,7 @@ export function useCreateFormBlockDecoratorProps(props) { // association 的值是固定不变的,所以这里可以使用 hooks if (props.association) { // eslint-disable-next-line react-hooks/rules-of-hooks - sourceId = useFormBlockSourceId(props); + sourceId = useSourceIdCommon(props.association); } return { diff --git a/packages/core/client/src/modules/blocks/data-blocks/form/hooks/useEditFormBlockDecoratorProps.ts b/packages/core/client/src/modules/blocks/data-blocks/form/hooks/useEditFormBlockDecoratorProps.ts index af759e656..47df14192 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/form/hooks/useEditFormBlockDecoratorProps.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/form/hooks/useEditFormBlockDecoratorProps.ts @@ -1,5 +1,5 @@ import { useParamsFromRecord } from '../../../../../block-provider/BlockProvider'; -import { useFormBlockSourceId } from './useFormBlockSourceId'; +import { useDetailsSourceId } from '../../details-single/hooks/useDetailsDecoratorProps'; export function useEditFormBlockDecoratorProps(props) { const params = useFormBlockParams(); @@ -7,8 +7,9 @@ export function useEditFormBlockDecoratorProps(props) { // association 的值是固定不变的,所以这里可以使用 hooks if (props.association) { + // 复用详情区块的 sourceId 获取逻辑 // eslint-disable-next-line react-hooks/rules-of-hooks - sourceId = useFormBlockSourceId(props); + sourceId = useDetailsSourceId(props.association); } return { diff --git a/packages/core/client/src/modules/blocks/data-blocks/form/hooks/useFormBlockSourceId.ts b/packages/core/client/src/modules/blocks/data-blocks/form/hooks/useFormBlockSourceId.ts deleted file mode 100644 index 5863c1d6f..000000000 --- a/packages/core/client/src/modules/blocks/data-blocks/form/hooks/useFormBlockSourceId.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId'; - -export function useFormBlockSourceId(props) { - return useDataBlockSourceId(props); -} diff --git a/packages/core/client/src/modules/blocks/data-blocks/grid-card/hooks/useGridCardBlockDecoratorProps.ts b/packages/core/client/src/modules/blocks/data-blocks/grid-card/hooks/useGridCardBlockDecoratorProps.ts index 1dac89fee..4fd4c36ef 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/grid-card/hooks/useGridCardBlockDecoratorProps.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/grid-card/hooks/useGridCardBlockDecoratorProps.ts @@ -1,5 +1,5 @@ -import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId'; import { useGridCardBlockParams } from './useGridCardBlockParams'; +import { useSourceIdCommon } from '../../../useSourceIdCommon'; export function useGridCardBlockDecoratorProps(props) { const params = useGridCardBlockParams(props); @@ -8,7 +8,7 @@ export function useGridCardBlockDecoratorProps(props) { // 因为 association 是固定的,所以可以在条件中使用 hooks if (props.association) { // eslint-disable-next-line react-hooks/rules-of-hooks - sourceId = useDataBlockSourceId({ association: props.association }); + sourceId = useSourceIdCommon(props.association); } return { diff --git a/packages/core/client/src/modules/blocks/data-blocks/list/hooks/useListBlockDecoratorProps.ts b/packages/core/client/src/modules/blocks/data-blocks/list/hooks/useListBlockDecoratorProps.ts index c63d2936f..98825fc06 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/list/hooks/useListBlockDecoratorProps.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/list/hooks/useListBlockDecoratorProps.ts @@ -1,4 +1,4 @@ -import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId'; +import { useSourceIdCommon } from '../../../useSourceIdCommon'; export function useListBlockDecoratorProps(props) { let sourceId; @@ -6,7 +6,7 @@ export function useListBlockDecoratorProps(props) { // 因为 association 的值是固定的,所以这里可以使用 hooks if (props.association) { // eslint-disable-next-line react-hooks/rules-of-hooks - sourceId = useDataBlockSourceId({ association: props.association }); + sourceId = useSourceIdCommon(props.association); } return { diff --git a/packages/core/client/src/modules/blocks/data-blocks/table/hooks/useTableBlockDecoratorProps.ts b/packages/core/client/src/modules/blocks/data-blocks/table/hooks/useTableBlockDecoratorProps.ts index b36a8cbed..e728076a8 100644 --- a/packages/core/client/src/modules/blocks/data-blocks/table/hooks/useTableBlockDecoratorProps.ts +++ b/packages/core/client/src/modules/blocks/data-blocks/table/hooks/useTableBlockDecoratorProps.ts @@ -1,7 +1,7 @@ import { useFieldSchema } from '@nocobase/schema'; import { useParsedFilter } from '../../../../../block-provider/hooks/useParsedFilter'; import { useMemo } from 'react'; -import { useDataBlockSourceId } from '../../../../../block-provider/hooks/useDataBlockSourceId'; +import { useSourceIdCommon } from '../../../useSourceIdCommon'; export const useTableBlockDecoratorProps = (props) => { const params = useTableBlockParams(props); @@ -44,7 +44,7 @@ function useTableBlockSourceId(props) { // 因为 association 是固定不变的,所以在条件中使用 hooks 是安全的 if (props.association) { // eslint-disable-next-line react-hooks/rules-of-hooks - sourceId = useDataBlockSourceId({ association: props.association }); + sourceId = useSourceIdCommon(props.association); } return sourceId; diff --git a/packages/core/client/src/modules/blocks/useSourceIdCommon.ts b/packages/core/client/src/modules/blocks/useSourceIdCommon.ts new file mode 100644 index 000000000..f3bfa7156 --- /dev/null +++ b/packages/core/client/src/modules/blocks/useSourceIdCommon.ts @@ -0,0 +1,17 @@ +import { useCollectionRecordData } from '../../data-source/collection-record/CollectionRecordProvider'; +import { useSourceKey } from './useSourceKey'; + +/** + * @internal + * 大部分区块(除了详情和编辑表单)都适用的获取 sourceId 的 hook + * @param association + * @returns + */ +export const useSourceIdCommon = (association: string) => { + const recordData = useCollectionRecordData(); + const sourceKey = useSourceKey(association); + + if (!association) return; + + return recordData?.[sourceKey]; +}; diff --git a/packages/core/client/src/modules/blocks/useSourceKey.ts b/packages/core/client/src/modules/blocks/useSourceKey.ts new file mode 100644 index 000000000..5b63e2ed1 --- /dev/null +++ b/packages/core/client/src/modules/blocks/useSourceKey.ts @@ -0,0 +1,26 @@ +import { InheritanceCollectionMixin } from '../../collection-manager/mixins/InheritanceCollectionMixin'; +import { useCollectionManager } from '../../data-source/collection/CollectionManagerProvider'; + +/** + * 用于获取关系字段的 source collection 的 key + * @param association string + * @returns + */ +export const useSourceKey = (association: string) => { + const cm = useCollectionManager(); + + if (!association) return; + + const associationField = cm.getCollectionField(association); + + if (!associationField) { + return; + } + + const sourceCollection = cm.getCollection(association.split('.')[0]); + + // 1. hasOne 和 hasMany 和 belongsToMany 的字段存在 sourceKey,所以会直接返回 sourceKey; + // 2. belongsTo 不存在 sourceKey,所以会使用 filterTargetKey; + // 3. 后面的主键和 id 是为了保险起见加上的; + return associationField.sourceKey || sourceCollection.filterTargetKey || sourceCollection.getPrimaryKey() || 'id'; +}; diff --git a/packages/plugins/@nocobase/plugin-calendar/src/client/hooks/useCalendarBlockDecoratorProps.ts b/packages/plugins/@nocobase/plugin-calendar/src/client/hooks/useCalendarBlockDecoratorProps.ts index edc67ee95..85baa46be 100644 --- a/packages/plugins/@nocobase/plugin-calendar/src/client/hooks/useCalendarBlockDecoratorProps.ts +++ b/packages/plugins/@nocobase/plugin-calendar/src/client/hooks/useCalendarBlockDecoratorProps.ts @@ -1,4 +1,4 @@ -import { useDataBlockSourceId } from '@nocobase/client'; +import { useSourceIdCommon } from '@nocobase/client'; import { useCalendarBlockParams } from './useCalendarBlockParams'; export function useCalendarBlockDecoratorProps(props) { @@ -8,7 +8,7 @@ export function useCalendarBlockDecoratorProps(props) { // 因为 association 是一个固定的值,所以可以在 hooks 中直接使用 if (props.association) { // eslint-disable-next-line react-hooks/rules-of-hooks - sourceId = useDataBlockSourceId({ association: props.association }); + sourceId = useSourceIdCommon(props.association); } return {