From dde673ee070b6efa391432c569c0fe6b03c20f83 Mon Sep 17 00:00:00 2001 From: Katherine Date: Wed, 10 Jun 2026 09:03:30 +0800 Subject: [PATCH] fix(plugin-data-source-manager): sync title field in v2 fields drawer (#9720) --- .../pages/components/CollectionsPage.tsx | 62 ++++++++++++------- .../client-v2/pages/components/FieldsPage.tsx | 31 ++++++++-- 2 files changed, 67 insertions(+), 26 deletions(-) diff --git a/packages/plugins/@nocobase/plugin-data-source-manager/src/client-v2/pages/components/CollectionsPage.tsx b/packages/plugins/@nocobase/plugin-data-source-manager/src/client-v2/pages/components/CollectionsPage.tsx index 80d624b0c2a..a093c3d9002 100644 --- a/packages/plugins/@nocobase/plugin-data-source-manager/src/client-v2/pages/components/CollectionsPage.tsx +++ b/packages/plugins/@nocobase/plugin-data-source-manager/src/client-v2/pages/components/CollectionsPage.tsx @@ -18,27 +18,19 @@ import { ReloadOutlined, SyncOutlined, } from '@ant-design/icons'; -import { - DndContext, - DragOverlay, - MouseSensor, - useDraggable, - useDroppable, - useSensor, - useSensors, - type DragEndEvent, - type DragStartEvent, -} from '@dnd-kit/core'; +import { DndContext, DragOverlay, MouseSensor, useDraggable, useDroppable, useSensor, useSensors } from '@dnd-kit/core'; +import type { DragEndEvent, DragStartEvent } from '@dnd-kit/core'; import { DEFAULT_PAGE_SIZE, DrawerFormLayout, FilterContent, Table, normalizeCollectionTemplateFields, - type CollectionTemplateField, } from '@nocobase/client-v2'; +import type { CollectionTemplateField } from '@nocobase/client-v2'; import { observable, observer, randomId, useFlowContext } from '@nocobase/flow-engine'; -import { transformFilter, type FilterGroupType } from '@nocobase/utils/client'; +import { transformFilter } from '@nocobase/utils/client'; +import type { FilterGroupType } from '@nocobase/utils/client'; import { useRequest } from 'ahooks'; import { App, @@ -61,14 +53,13 @@ import { theme, Transfer, } from 'antd'; +import type { FormInstance } from 'antd'; import type { ColumnsType } from 'antd/es/table'; -import React, { type FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import type { FC } from 'react'; import { useT } from '../../locale'; -import { - type CollectionPresetFieldOptions, - type CollectionTemplateOptions, - PluginDataSourceManagerClientV2, -} from '../../plugin'; +import type { CollectionPresetFieldOptions, CollectionTemplateOptions } from '../../plugin'; +import { PluginDataSourceManagerClientV2 } from '../../plugin'; import { compileLegacyTemplate, preferLegacyTemplateTitle } from '../../utils/compileLegacyTemplate'; import { getErrorMessage, isFormValidationError } from '../../utils/error'; import { getCollectionFieldActionUrl } from './collectionFieldApi'; @@ -355,6 +346,23 @@ function normalizeListResponse(response: any) { }; } +function updateCollectionListRecord( + data: { records: Record[]; total: number } | undefined, + collectionName: string, + values: Record, +) { + if (!data?.records) { + return data; + } + + return { + ...data, + records: data.records.map((collection) => + collection.name === collectionName ? { ...collection, ...values } : collection, + ), + }; +} + function normalizeArrayResponse(response: any) { const payload = response?.data?.data; return Array.isArray(payload) ? payload : Array.isArray(payload?.data) ? payload.data : []; @@ -808,7 +816,7 @@ const CollectionTemplateConfigureItems: FC<{ }; const CollectionCreateFilterTargetKey: FC<{ - form: ReturnType>[0]; + form: FormInstance; }> = ({ form }) => { const t = useT(); const fields = Form.useWatch('fields', form); @@ -1468,10 +1476,20 @@ function CollectionsPage(props: CollectionsPageProps) { ), width: '80%', closable: true, - content: () => , + content: () => ( + { + Object.assign(collection, values); + request.mutate(updateCollectionListRecord(request.data, collectionName, values)); + request.refresh(); + }} + /> + ), }); }, - [ctx.viewer, props.dataSourceKey, t], + [ctx.viewer, props.dataSourceKey, request, t], ); const handleCategoryChange = useCallback((key: string) => { diff --git a/packages/plugins/@nocobase/plugin-data-source-manager/src/client-v2/pages/components/FieldsPage.tsx b/packages/plugins/@nocobase/plugin-data-source-manager/src/client-v2/pages/components/FieldsPage.tsx index dca9184c763..7be8099eb6a 100644 --- a/packages/plugins/@nocobase/plugin-data-source-manager/src/client-v2/pages/components/FieldsPage.tsx +++ b/packages/plugins/@nocobase/plugin-data-source-manager/src/client-v2/pages/components/FieldsPage.tsx @@ -45,6 +45,7 @@ import { FieldForm } from './FieldForm'; interface FieldsPageProps { dataSourceKey: string; collection: Record; + onCollectionChange?: (collectionName: string, values: Record) => void; } type FieldInterfaceOption = { @@ -321,6 +322,24 @@ function getCollectionUpdateActionUrl(dataSourceKey: string) { return dataSourceKey === 'main' ? 'collections:update' : `dataSources/${dataSourceKey}/collections:update`; } +function getCollectionTitleField(ctx: any, dataSourceKey: string, collection: Record) { + return ( + collection.titleField || + ctx.dataSourceManager + .getDataSource(dataSourceKey) + ?.collectionManager?.getCollection(collection.name) + ?.getOption?.('titleField') + ); +} + +function setCollectionTitleField(ctx: any, dataSourceKey: string, collection: Record, titleField: string) { + ctx.dataSourceManager + .getDataSource(dataSourceKey) + ?.collectionManager?.getCollection(collection.name) + ?.setOption?.('titleField', titleField); + collection.titleField = titleField; +} + function isViewCollection(collection: Record) { return collection.template === 'view' || collection.view; } @@ -842,7 +861,9 @@ export default function FieldsPage(props: FieldsPageProps) { const appInfo = useCurrentAppInfo<{ database?: { dialect?: string } }>(); const { message, modal, notification } = App.useApp(); const [selectedRowKeys, setSelectedRowKeys] = useState([]); - const [titleField, setTitleField] = useState(props.collection.titleField); + const [titleField, setTitleField] = useState(() => + getCollectionTitleField(ctx, props.dataSourceKey, props.collection), + ); const [titleFieldLoadingKey, setTitleFieldLoadingKey] = useState(); const [displayNameLoadingKey, setDisplayNameLoadingKey] = useState(); const [syncFieldsLoading, setSyncFieldsLoading] = useState(false); @@ -861,8 +882,8 @@ export default function FieldsPage(props: FieldsPageProps) { }); useEffect(() => { - setTitleField(props.collection.titleField); - }, [props.collection.titleField]); + setTitleField(getCollectionTitleField(ctx, props.dataSourceKey, props.collection)); + }, [ctx, props.collection, props.collection.titleField, props.dataSourceKey]); const dataSource = ctx.dataSourceManager.getDataSource(props.dataSourceKey); const dataSourceType = ctx.app.pm.get(PluginDataSourceManagerClientV2)?.getType?.(dataSource?.options?.type); @@ -1099,7 +1120,9 @@ export default function FieldsPage(props: FieldsPageProps) { params: { filterByTk: props.collection.name }, data: { titleField: nextTitleField }, }); + setCollectionTitleField(ctx, props.dataSourceKey, props.collection, nextTitleField); setTitleField(nextTitleField); + props.onCollectionChange?.(props.collection.name, { titleField: nextTitleField }); await ctx.dataSourceManager.getDataSource(props.dataSourceKey)?.reload(); message.success(t('Saved successfully')); } catch (error) { @@ -1111,7 +1134,7 @@ export default function FieldsPage(props: FieldsPageProps) { setTitleFieldLoadingKey(undefined); } }, - [ctx.api, ctx.dataSourceManager, message, notification, props.collection.name, props.dataSourceKey, t], + [ctx, message, notification, props, t], ); const handleSyncFields = useCallback(async () => {