feat: support sort option in appends (#4056)

* feat: support sort option in append

* chore: build

* refactor: sub-table support setDefaultSortingRules

* refactor: sub-table support setDefaultSortingRules

* fix: bug

---------

Co-authored-by: katherinehhh <katherine_15995@163.com>
This commit is contained in:
ChengLei Shao 2024-04-19 09:08:46 +08:00 committed by GitHub
parent 067042ac3a
commit c460354b69
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 211 additions and 5 deletions

View File

@ -1352,7 +1352,12 @@ export const useAssociationNames = (dataSource?: string) => {
appends.add(path);
appends.add(`${path}.parent` + '(recursively=true)');
} else {
appends.add(path);
if (s['x-component-props']?.sortArr) {
const sort = s['x-component-props']?.sortArr;
appends.add(`${path}(sort=${sort})`);
} else {
appends.add(path);
}
}
if (['Nester', 'SubTable', 'PopoverNester'].includes(s['x-component-props']?.mode)) {
updateAssociationValues.add(path);

View File

@ -11,6 +11,7 @@ import {
} from '../../../../schema-component/antd/form-item/FormItem.Settings';
import { useCollectionField } from '../../../../data-source';
import { useFormBlockType } from '../../../../block-provider';
import { setDefaultSortingRules } from '../SubTable/subTablePopoverComponentFieldSettings';
const allowMultiple: any = {
name: 'allowMultiple',
@ -131,5 +132,6 @@ export const subformComponentFieldSettings = new SchemaSettings({
};
},
},
setDefaultSortingRules,
],
});

View File

@ -1,6 +1,7 @@
import { Field } from '@formily/core';
import { useField, useFieldSchema } from '@formily/react';
import { useField, useFieldSchema, ISchema } from '@formily/react';
import { useTranslation } from 'react-i18next';
import { ArrayItems } from '@formily/antd-v5';
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
import { useFieldComponentName } from '../../../../common/useFieldComponentName';
import {
@ -10,6 +11,7 @@ import {
useIsFieldReadPretty,
} from '../../../../schema-component';
import { isSubMode } from '../../../../schema-component/antd/association-field/util';
import { useCollectionManager_deprecated, useSortFields } from '../../../../collection-manager';
const fieldComponent: any = {
name: 'fieldComponent',
@ -85,7 +87,130 @@ const allowSelectExistingRecord = {
};
},
};
export const setDefaultSortingRules = {
name: 'SetDefaultSortingRules',
type: 'modal',
useComponentProps() {
const { getCollectionJoinField } = useCollectionManager_deprecated();
const field = useField();
const fieldSchema = useFieldSchema();
const association = fieldSchema['x-collection-field'];
const { target } = getCollectionJoinField(association) || {};
const sortFields = useSortFields(target);
const { t } = useTranslation();
const { dn } = useDesignable();
const defaultSort = field.componentProps.sortArr || [];
const sort = defaultSort?.map((item: string) => {
return item?.startsWith('-')
? {
field: item.substring(1),
direction: 'desc',
}
: {
field: item,
direction: 'asc',
};
});
return {
title: t('Set default sorting rules'),
components: { ArrayItems },
schema: {
type: 'object',
title: t('Set default sorting rules'),
properties: {
sort: {
type: 'array',
default: sort,
'x-component': 'ArrayItems',
'x-decorator': 'FormItem',
items: {
type: 'object',
properties: {
space: {
type: 'void',
'x-component': 'Space',
properties: {
sort: {
type: 'void',
'x-decorator': 'FormItem',
'x-component': 'ArrayItems.SortHandle',
},
field: {
type: 'string',
enum: sortFields,
required: true,
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component-props': {
style: {
width: 260,
},
},
},
direction: {
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'Radio.Group',
'x-component-props': {
optionType: 'button',
},
enum: [
{
label: t('ASC'),
value: 'asc',
},
{
label: t('DESC'),
value: 'desc',
},
],
},
remove: {
type: 'void',
'x-decorator': 'FormItem',
'x-component': 'ArrayItems.Remove',
},
},
},
},
},
properties: {
add: {
type: 'void',
title: t('Add sort field'),
'x-component': 'ArrayItems.Addition',
},
},
},
},
} as ISchema,
onSubmit: ({ sort }) => {
const sortArr = sort.map((item) => {
return item.direction === 'desc' ? `-${item.field}` : item.field;
});
field.componentProps.sortArr = sortArr;
fieldSchema['x-component-props'].sortArr = sortArr;
dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
'x-component-props': fieldSchema['x-component-props'],
},
});
},
};
},
useVisible() {
const { getCollectionJoinField } = useCollectionManager_deprecated();
const fieldSchema = useFieldSchema();
const association = fieldSchema['x-collection-field'];
const { interface: targetInterface } = getCollectionJoinField(association) || {};
const readPretty = useIsFieldReadPretty();
return readPretty && ['m2m', 'o2m'].includes(targetInterface);
},
};
export const subTablePopoverComponentFieldSettings = new SchemaSettings({
name: 'fieldSettings:component:SubTable',
items: [fieldComponent, allowSelectExistingRecord],
items: [fieldComponent, allowSelectExistingRecord, setDefaultSortingRules],
});

View File

@ -1,6 +1,7 @@
import { mockDatabase } from '../index';
import Database from '@nocobase/database';
import { Collection } from '../../collection';
import qs from 'qs';
describe('find with associations', () => {
let db: Database;
@ -412,6 +413,64 @@ describe('find with associations', () => {
expect(findResult[0].get('name')).toEqual('u1');
});
it('should find with associations with sort params', async () => {
const User = db.collection({
name: 'users',
fields: [
{ type: 'string', name: 'name' },
{
type: 'hasMany',
name: 'posts',
},
],
});
const Post = db.collection({
name: 'posts',
fields: [
{ type: 'string', name: 'title' },
{ type: 'belongsTo', name: 'user' },
],
});
await db.sync();
await User.repository.create({
values: [
{
name: 'u1',
posts: [
{
title: 'u1p1',
},
{
title: 'u1p2',
},
],
},
{
name: 'u2',
posts: [
{
title: 'u2p1',
},
{
title: 'u2p2',
},
],
},
],
});
const appendArgs = [`posts(${qs.stringify({ sort: ['-id'] })})`];
const users = await User.repository.find({
appends: appendArgs,
});
expect(users[0].get('name')).toEqual('u1');
expect(users[0].get('posts')[0].get('title')).toEqual('u1p2');
});
});
describe('repository find', () => {

View File

@ -257,11 +257,26 @@ export class EagerLoadingTree {
const association = node.association;
const associationType = association.associationType;
let params: any = {};
const otherFindOptions = lodash.pick(node.includeOption, ['sort']) || {};
const collection = this.db.modelCollection.get(node.model);
if (collection && !lodash.isEmpty(otherFindOptions)) {
const parser = new OptionsParser(otherFindOptions, {
collection,
});
params = parser.toSequelizeParams();
}
if (associationType == 'HasOne' || associationType == 'HasMany') {
const foreignKey = association.foreignKey;
const foreignKeyValues = node.parent.instances.map((instance) => instance.get(association.sourceKey));
let where: any = { [foreignKey]: foreignKeyValues };
if (node.where) {
where = {
[Op.and]: [where, node.where],
@ -271,7 +286,7 @@ export class EagerLoadingTree {
const findOptions = {
where,
attributes: node.attributes,
order: orderOption(association),
order: params.order || orderOption(association),
transaction,
};
@ -358,7 +373,7 @@ export class EagerLoadingTree {
},
},
],
order: orderOption(association),
order: params.order || orderOption(association),
});
}
}