mirror of
https://gitee.com/nocobase/nocobase.git
synced 2024-12-02 04:07:50 +08:00
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:
parent
067042ac3a
commit
c460354b69
@ -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);
|
||||
|
@ -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,
|
||||
],
|
||||
});
|
||||
|
@ -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],
|
||||
});
|
||||
|
@ -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', () => {
|
||||
|
@ -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),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user