mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 21:49:25 +08:00
fix(kanban): fix incorrect appends parameter issue (#5592)
* refactor(kanban): optimize get appends function * fix(kanban): fix incorrect appends parameter issue * chore: remove useless code
This commit is contained in:
parent
ee1dd2375f
commit
a6f16acf1c
@ -0,0 +1,248 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Schema } from '@formily/json-schema';
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { getAppends } from '../../hooks/index';
|
||||||
|
|
||||||
|
describe('getAppends', () => {
|
||||||
|
const mockGetCollectionJoinField = (name: string) => {
|
||||||
|
const fields = {
|
||||||
|
'users.profile': {
|
||||||
|
type: 'hasOne',
|
||||||
|
target: 'profiles',
|
||||||
|
},
|
||||||
|
'users.posts': {
|
||||||
|
type: 'hasMany',
|
||||||
|
target: 'posts',
|
||||||
|
},
|
||||||
|
'posts.author': {
|
||||||
|
type: 'belongsTo',
|
||||||
|
target: 'users',
|
||||||
|
},
|
||||||
|
'users.roles': {
|
||||||
|
type: 'belongsToMany',
|
||||||
|
target: 'roles',
|
||||||
|
},
|
||||||
|
'users.categories': {
|
||||||
|
type: 'belongsToArray',
|
||||||
|
target: 'categories',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return fields[name];
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockGetCollection = (name: string) => {
|
||||||
|
const collections = {
|
||||||
|
categories: {
|
||||||
|
template: 'tree',
|
||||||
|
},
|
||||||
|
users: {
|
||||||
|
template: 'general',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return collections[name];
|
||||||
|
};
|
||||||
|
|
||||||
|
const createSchema = (properties) => {
|
||||||
|
return new Schema({
|
||||||
|
properties,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should handle basic association fields', () => {
|
||||||
|
const schema = createSchema({
|
||||||
|
profile: {
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-collection-field': 'users.profile',
|
||||||
|
name: 'profile',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateAssociationValues = new Set<string>();
|
||||||
|
const appends = new Set<string>();
|
||||||
|
|
||||||
|
getAppends({
|
||||||
|
schema,
|
||||||
|
prefix: '',
|
||||||
|
updateAssociationValues,
|
||||||
|
appends,
|
||||||
|
getCollectionJoinField: mockGetCollectionJoinField,
|
||||||
|
getCollection: mockGetCollection,
|
||||||
|
dataSource: 'main',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(Array.from(appends)).toEqual(['profile']);
|
||||||
|
expect(Array.from(updateAssociationValues)).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle tree collection fields', () => {
|
||||||
|
const schema = createSchema({
|
||||||
|
categories: {
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-collection-field': 'users.categories',
|
||||||
|
name: 'categories',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateAssociationValues = new Set<string>();
|
||||||
|
const appends = new Set<string>();
|
||||||
|
|
||||||
|
getAppends({
|
||||||
|
schema,
|
||||||
|
prefix: '',
|
||||||
|
updateAssociationValues,
|
||||||
|
appends,
|
||||||
|
getCollectionJoinField: mockGetCollectionJoinField,
|
||||||
|
getCollection: mockGetCollection,
|
||||||
|
dataSource: 'main',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(Array.from(appends)).toEqual(['categories', 'categories.parent(recursively=true)']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle nested fields with sorting', () => {
|
||||||
|
const schema = createSchema({
|
||||||
|
posts: {
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-collection-field': 'users.posts',
|
||||||
|
'x-component-props': {
|
||||||
|
sortArr: 'createdAt',
|
||||||
|
},
|
||||||
|
name: 'posts',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateAssociationValues = new Set<string>();
|
||||||
|
const appends = new Set<string>();
|
||||||
|
|
||||||
|
getAppends({
|
||||||
|
schema,
|
||||||
|
prefix: '',
|
||||||
|
updateAssociationValues,
|
||||||
|
appends,
|
||||||
|
getCollectionJoinField: mockGetCollectionJoinField,
|
||||||
|
getCollection: mockGetCollection,
|
||||||
|
dataSource: 'main',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(Array.from(appends)).toEqual(['posts(sort=createdAt)']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle nested SubTable mode', () => {
|
||||||
|
const schema = createSchema({
|
||||||
|
posts: {
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-collection-field': 'users.posts',
|
||||||
|
'x-component-props': {
|
||||||
|
mode: 'SubTable',
|
||||||
|
},
|
||||||
|
name: 'posts',
|
||||||
|
properties: {
|
||||||
|
author: {
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-collection-field': 'posts.author',
|
||||||
|
name: 'author',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateAssociationValues = new Set<string>();
|
||||||
|
const appends = new Set<string>();
|
||||||
|
|
||||||
|
getAppends({
|
||||||
|
schema,
|
||||||
|
prefix: '',
|
||||||
|
updateAssociationValues,
|
||||||
|
appends,
|
||||||
|
getCollectionJoinField: mockGetCollectionJoinField,
|
||||||
|
getCollection: mockGetCollection,
|
||||||
|
dataSource: 'main',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(Array.from(appends)).toEqual(['posts', 'posts.author']);
|
||||||
|
expect(Array.from(updateAssociationValues)).toEqual(['posts']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore TableField components', () => {
|
||||||
|
const schema = createSchema({
|
||||||
|
posts: {
|
||||||
|
'x-component': 'TableField',
|
||||||
|
'x-collection-field': 'users.posts',
|
||||||
|
name: 'posts',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateAssociationValues = new Set<string>();
|
||||||
|
const appends = new Set<string>();
|
||||||
|
|
||||||
|
getAppends({
|
||||||
|
schema,
|
||||||
|
prefix: '',
|
||||||
|
updateAssociationValues,
|
||||||
|
appends,
|
||||||
|
getCollectionJoinField: mockGetCollectionJoinField,
|
||||||
|
getCollection: mockGetCollection,
|
||||||
|
dataSource: 'main',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(Array.from(appends)).toEqual([]);
|
||||||
|
expect(Array.from(updateAssociationValues)).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore Kanban.CardViewer components', () => {
|
||||||
|
const schema = createSchema({
|
||||||
|
cardViewer: {
|
||||||
|
'x-component': 'Kanban.CardViewer',
|
||||||
|
name: 'cardViewer',
|
||||||
|
properties: {
|
||||||
|
drawer: {
|
||||||
|
name: 'drawer',
|
||||||
|
type: 'void',
|
||||||
|
properties: {
|
||||||
|
grid: {
|
||||||
|
name: 'grid',
|
||||||
|
type: 'void',
|
||||||
|
properties: {
|
||||||
|
field1: {
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-collection-field': 'users.posts',
|
||||||
|
name: 'field1',
|
||||||
|
},
|
||||||
|
field2: {
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-collection-field': 'posts.author',
|
||||||
|
name: 'field2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateAssociationValues = new Set<string>();
|
||||||
|
const appends = new Set<string>();
|
||||||
|
|
||||||
|
getAppends({
|
||||||
|
schema,
|
||||||
|
prefix: '',
|
||||||
|
updateAssociationValues,
|
||||||
|
appends,
|
||||||
|
getCollectionJoinField: mockGetCollectionJoinField,
|
||||||
|
getCollection: mockGetCollection,
|
||||||
|
dataSource: 'main',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(Array.from(appends)).toEqual([]);
|
||||||
|
expect(Array.from(updateAssociationValues)).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
@ -35,7 +35,7 @@ import {
|
|||||||
import { useAPIClient, useRequest } from '../../api-client';
|
import { useAPIClient, useRequest } from '../../api-client';
|
||||||
import { useNavigateNoUpdate } from '../../application/CustomRouterContextProvider';
|
import { useNavigateNoUpdate } from '../../application/CustomRouterContextProvider';
|
||||||
import { useFormBlockContext } from '../../block-provider/FormBlockProvider';
|
import { useFormBlockContext } from '../../block-provider/FormBlockProvider';
|
||||||
import { useCollectionManager_deprecated, useCollection_deprecated } from '../../collection-manager';
|
import { CollectionOptions, useCollectionManager_deprecated, useCollection_deprecated } from '../../collection-manager';
|
||||||
import { DataBlock, useFilterBlock } from '../../filter-provider/FilterProvider';
|
import { DataBlock, useFilterBlock } from '../../filter-provider/FilterProvider';
|
||||||
import { mergeFilter, transformToFilter } from '../../filter-provider/utils';
|
import { mergeFilter, transformToFilter } from '../../filter-provider/utils';
|
||||||
import { useTreeParentRecord } from '../../modules/blocks/data-blocks/table/TreeRecordProvider';
|
import { useTreeParentRecord } from '../../modules/blocks/data-blocks/table/TreeRecordProvider';
|
||||||
@ -1494,14 +1494,25 @@ export function getAssociationPath(str) {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useAssociationNames = (dataSource?: string) => {
|
export const getAppends = ({
|
||||||
let updateAssociationValues = new Set([]);
|
schema,
|
||||||
let appends = new Set([]);
|
prefix: defaultPrefix,
|
||||||
const { getCollectionJoinField, getCollection } = useCollectionManager_deprecated(dataSource);
|
updateAssociationValues,
|
||||||
const fieldSchema = useFieldSchema();
|
appends,
|
||||||
const _getAssociationAppends = (schema, str) => {
|
getCollectionJoinField,
|
||||||
|
getCollection,
|
||||||
|
dataSource,
|
||||||
|
}: {
|
||||||
|
schema: any;
|
||||||
|
prefix: string;
|
||||||
|
updateAssociationValues: Set<string>;
|
||||||
|
appends: Set<string>;
|
||||||
|
getCollectionJoinField: (name: string, dataSource: string) => any;
|
||||||
|
getCollection: (name: any, customDataSource?: string) => CollectionOptions;
|
||||||
|
dataSource: string;
|
||||||
|
}) => {
|
||||||
schema.reduceProperties((pre, s) => {
|
schema.reduceProperties((pre, s) => {
|
||||||
const prefix = pre || str;
|
const prefix = pre || defaultPrefix;
|
||||||
const collectionField = s['x-collection-field'] && getCollectionJoinField(s['x-collection-field'], dataSource);
|
const collectionField = s['x-collection-field'] && getCollectionJoinField(s['x-collection-field'], dataSource);
|
||||||
const isAssociationSubfield = s.name.includes('.');
|
const isAssociationSubfield = s.name.includes('.');
|
||||||
const isAssociationField =
|
const isAssociationField =
|
||||||
@ -1534,8 +1545,10 @@ export const useAssociationNames = (dataSource?: string) => {
|
|||||||
collectAppends(condition);
|
collectAppends(condition);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const isTreeCollection =
|
const isTreeCollection =
|
||||||
isAssociationField && getCollection(collectionField.target, dataSource)?.template === 'tree';
|
isAssociationField && getCollection(collectionField.target, dataSource)?.template === 'tree';
|
||||||
|
|
||||||
if (collectionField && (isAssociationField || isAssociationSubfield) && s['x-component'] !== 'TableField') {
|
if (collectionField && (isAssociationField || isAssociationSubfield) && s['x-component'] !== 'TableField') {
|
||||||
const fieldPath = !isAssociationField && isAssociationSubfield ? getAssociationPath(s.name) : s.name;
|
const fieldPath = !isAssociationField && isAssociationSubfield ? getAssociationPath(s.name) : s.name;
|
||||||
const path = prefix === '' || !prefix ? fieldPath : prefix + '.' + fieldPath;
|
const path = prefix === '' || !prefix ? fieldPath : prefix + '.' + fieldPath;
|
||||||
@ -1550,10 +1563,18 @@ export const useAssociationNames = (dataSource?: string) => {
|
|||||||
appends.add(path);
|
appends.add(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (['Nester', 'SubTable', 'PopoverNester'].includes(s['x-component-props']?.mode)) {
|
if (isSubMode(s)) {
|
||||||
updateAssociationValues.add(path);
|
updateAssociationValues.add(path);
|
||||||
const bufPrefix = prefix && prefix !== '' ? prefix + '.' + s.name : s.name;
|
const bufPrefix = prefix && prefix !== '' ? prefix + '.' + s.name : s.name;
|
||||||
_getAssociationAppends(s, bufPrefix);
|
getAppends({
|
||||||
|
schema: s,
|
||||||
|
prefix: bufPrefix,
|
||||||
|
updateAssociationValues,
|
||||||
|
appends,
|
||||||
|
getCollectionJoinField,
|
||||||
|
getCollection,
|
||||||
|
dataSource,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
![
|
![
|
||||||
@ -1567,17 +1588,43 @@ export const useAssociationNames = (dataSource?: string) => {
|
|||||||
'AssociationField.Selector',
|
'AssociationField.Selector',
|
||||||
'AssociationField.AddNewer',
|
'AssociationField.AddNewer',
|
||||||
'TableField',
|
'TableField',
|
||||||
|
'Kanban.CardViewer',
|
||||||
].includes(s['x-component'])
|
].includes(s['x-component'])
|
||||||
) {
|
) {
|
||||||
_getAssociationAppends(s, str);
|
getAppends({
|
||||||
|
schema: s,
|
||||||
|
prefix: defaultPrefix,
|
||||||
|
updateAssociationValues,
|
||||||
|
appends,
|
||||||
|
getCollectionJoinField,
|
||||||
|
getCollection,
|
||||||
|
dataSource,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, str);
|
}, defaultPrefix);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useAssociationNames = (dataSource?: string) => {
|
||||||
|
const { getCollectionJoinField, getCollection } = useCollectionManager_deprecated(dataSource);
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
|
||||||
const getAssociationAppends = () => {
|
const getAssociationAppends = () => {
|
||||||
updateAssociationValues = new Set([]);
|
const updateAssociationValues = new Set([]);
|
||||||
appends = new Set([]);
|
let appends = new Set([]);
|
||||||
_getAssociationAppends(fieldSchema, '');
|
|
||||||
|
getAppends({
|
||||||
|
schema: fieldSchema,
|
||||||
|
prefix: '',
|
||||||
|
updateAssociationValues,
|
||||||
|
appends,
|
||||||
|
getCollectionJoinField,
|
||||||
|
getCollection,
|
||||||
|
dataSource,
|
||||||
|
});
|
||||||
appends = fillParentFields(appends);
|
appends = fillParentFields(appends);
|
||||||
|
|
||||||
|
console.log('appends', appends);
|
||||||
|
|
||||||
return { appends: [...appends], updateAssociationValues: [...updateAssociationValues] };
|
return { appends: [...appends], updateAssociationValues: [...updateAssociationValues] };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,17 +10,17 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { FormLayout } from '@formily/antd-v5';
|
import { FormLayout } from '@formily/antd-v5';
|
||||||
import { createForm } from '@formily/core';
|
import { createForm } from '@formily/core';
|
||||||
import { observer, RecursionField, useFieldSchema } from '@formily/react';
|
import { RecursionField, useFieldSchema } from '@formily/react';
|
||||||
import {
|
import {
|
||||||
DndContext,
|
DndContext,
|
||||||
FormProvider,
|
FormProvider,
|
||||||
|
getCardItemSchema,
|
||||||
PopupContextProvider,
|
PopupContextProvider,
|
||||||
useCollection,
|
useCollection,
|
||||||
useCollectionRecordData,
|
useCollectionRecordData,
|
||||||
usePopupSettings,
|
usePopupSettings,
|
||||||
usePopupUtils,
|
usePopupUtils,
|
||||||
VariablePopupRecordProvider,
|
VariablePopupRecordProvider,
|
||||||
getCardItemSchema,
|
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { Schema } from '@nocobase/utils';
|
import { Schema } from '@nocobase/utils';
|
||||||
import { Card } from 'antd';
|
import { Card } from 'antd';
|
||||||
@ -68,8 +68,7 @@ const cardCss = css`
|
|||||||
const MemorizedRecursionField = React.memo(RecursionField);
|
const MemorizedRecursionField = React.memo(RecursionField);
|
||||||
MemorizedRecursionField.displayName = 'MemorizedRecursionField';
|
MemorizedRecursionField.displayName = 'MemorizedRecursionField';
|
||||||
|
|
||||||
export const KanbanCard: any = observer(
|
export const KanbanCard: any = () => {
|
||||||
() => {
|
|
||||||
const collection = useCollection();
|
const collection = useCollection();
|
||||||
const { setDisableCardDrag } = useContext(KanbanCardContext) || {};
|
const { setDisableCardDrag } = useContext(KanbanCardContext) || {};
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
@ -157,9 +156,7 @@ export const KanbanCard: any = observer(
|
|||||||
</PopupContextProvider>
|
</PopupContextProvider>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
};
|
||||||
{ displayName: 'KanbanCard' },
|
|
||||||
);
|
|
||||||
|
|
||||||
function getPopupSchemaFromParent(fieldSchema: Schema) {
|
function getPopupSchemaFromParent(fieldSchema: Schema) {
|
||||||
if (fieldSchema.parent?.properties?.cardViewer?.properties?.drawer) {
|
if (fieldSchema.parent?.properties?.cardViewer?.properties?.drawer) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user