Merge branch 'next' into develop

This commit is contained in:
nocobase[bot]
2026-06-10 01:04:03 +00:00
2 changed files with 67 additions and 26 deletions
@@ -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<string, any>[]; total: number } | undefined,
collectionName: string,
values: Record<string, any>,
) {
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<typeof Form.useForm<CollectionFormValues>>[0];
form: FormInstance<CollectionFormValues>;
}> = ({ form }) => {
const t = useT();
const fields = Form.useWatch('fields', form);
@@ -1468,10 +1476,20 @@ function CollectionsPage(props: CollectionsPageProps) {
),
width: '80%',
closable: true,
content: () => <FieldsPage dataSourceKey={props.dataSourceKey} collection={collection} />,
content: () => (
<FieldsPage
dataSourceKey={props.dataSourceKey}
collection={collection}
onCollectionChange={(collectionName, values) => {
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) => {
@@ -45,6 +45,7 @@ import { FieldForm } from './FieldForm';
interface FieldsPageProps {
dataSourceKey: string;
collection: Record<string, any>;
onCollectionChange?: (collectionName: string, values: Record<string, any>) => 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<string, any>) {
return (
collection.titleField ||
ctx.dataSourceManager
.getDataSource(dataSourceKey)
?.collectionManager?.getCollection(collection.name)
?.getOption?.('titleField')
);
}
function setCollectionTitleField(ctx: any, dataSourceKey: string, collection: Record<string, any>, titleField: string) {
ctx.dataSourceManager
.getDataSource(dataSourceKey)
?.collectionManager?.getCollection(collection.name)
?.setOption?.('titleField', titleField);
collection.titleField = titleField;
}
function isViewCollection(collection: Record<string, any>) {
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<React.Key[]>([]);
const [titleField, setTitleField] = useState<string | undefined>(props.collection.titleField);
const [titleField, setTitleField] = useState<string | undefined>(() =>
getCollectionTitleField(ctx, props.dataSourceKey, props.collection),
);
const [titleFieldLoadingKey, setTitleFieldLoadingKey] = useState<React.Key>();
const [displayNameLoadingKey, setDisplayNameLoadingKey] = useState<React.Key>();
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 () => {