mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 21:49:25 +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);
|
||||||
appends.add(`${path}.parent` + '(recursively=true)');
|
appends.add(`${path}.parent` + '(recursively=true)');
|
||||||
} else {
|
} 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)) {
|
if (['Nester', 'SubTable', 'PopoverNester'].includes(s['x-component-props']?.mode)) {
|
||||||
updateAssociationValues.add(path);
|
updateAssociationValues.add(path);
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
} from '../../../../schema-component/antd/form-item/FormItem.Settings';
|
} from '../../../../schema-component/antd/form-item/FormItem.Settings';
|
||||||
import { useCollectionField } from '../../../../data-source';
|
import { useCollectionField } from '../../../../data-source';
|
||||||
import { useFormBlockType } from '../../../../block-provider';
|
import { useFormBlockType } from '../../../../block-provider';
|
||||||
|
import { setDefaultSortingRules } from '../SubTable/subTablePopoverComponentFieldSettings';
|
||||||
|
|
||||||
const allowMultiple: any = {
|
const allowMultiple: any = {
|
||||||
name: 'allowMultiple',
|
name: 'allowMultiple',
|
||||||
@ -131,5 +132,6 @@ export const subformComponentFieldSettings = new SchemaSettings({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
setDefaultSortingRules,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Field } from '@formily/core';
|
import { Field } from '@formily/core';
|
||||||
import { useField, useFieldSchema } from '@formily/react';
|
import { useField, useFieldSchema, ISchema } from '@formily/react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { ArrayItems } from '@formily/antd-v5';
|
||||||
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
|
import { SchemaSettings } from '../../../../application/schema-settings/SchemaSettings';
|
||||||
import { useFieldComponentName } from '../../../../common/useFieldComponentName';
|
import { useFieldComponentName } from '../../../../common/useFieldComponentName';
|
||||||
import {
|
import {
|
||||||
@ -10,6 +11,7 @@ import {
|
|||||||
useIsFieldReadPretty,
|
useIsFieldReadPretty,
|
||||||
} from '../../../../schema-component';
|
} from '../../../../schema-component';
|
||||||
import { isSubMode } from '../../../../schema-component/antd/association-field/util';
|
import { isSubMode } from '../../../../schema-component/antd/association-field/util';
|
||||||
|
import { useCollectionManager_deprecated, useSortFields } from '../../../../collection-manager';
|
||||||
|
|
||||||
const fieldComponent: any = {
|
const fieldComponent: any = {
|
||||||
name: 'fieldComponent',
|
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({
|
export const subTablePopoverComponentFieldSettings = new SchemaSettings({
|
||||||
name: 'fieldSettings:component:SubTable',
|
name: 'fieldSettings:component:SubTable',
|
||||||
items: [fieldComponent, allowSelectExistingRecord],
|
items: [fieldComponent, allowSelectExistingRecord, setDefaultSortingRules],
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { mockDatabase } from '../index';
|
import { mockDatabase } from '../index';
|
||||||
import Database from '@nocobase/database';
|
import Database from '@nocobase/database';
|
||||||
import { Collection } from '../../collection';
|
import { Collection } from '../../collection';
|
||||||
|
import qs from 'qs';
|
||||||
|
|
||||||
describe('find with associations', () => {
|
describe('find with associations', () => {
|
||||||
let db: Database;
|
let db: Database;
|
||||||
@ -412,6 +413,64 @@ describe('find with associations', () => {
|
|||||||
|
|
||||||
expect(findResult[0].get('name')).toEqual('u1');
|
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', () => {
|
describe('repository find', () => {
|
||||||
|
@ -257,11 +257,26 @@ export class EagerLoadingTree {
|
|||||||
const association = node.association;
|
const association = node.association;
|
||||||
const associationType = association.associationType;
|
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') {
|
if (associationType == 'HasOne' || associationType == 'HasMany') {
|
||||||
const foreignKey = association.foreignKey;
|
const foreignKey = association.foreignKey;
|
||||||
const foreignKeyValues = node.parent.instances.map((instance) => instance.get(association.sourceKey));
|
const foreignKeyValues = node.parent.instances.map((instance) => instance.get(association.sourceKey));
|
||||||
|
|
||||||
let where: any = { [foreignKey]: foreignKeyValues };
|
let where: any = { [foreignKey]: foreignKeyValues };
|
||||||
|
|
||||||
if (node.where) {
|
if (node.where) {
|
||||||
where = {
|
where = {
|
||||||
[Op.and]: [where, node.where],
|
[Op.and]: [where, node.where],
|
||||||
@ -271,7 +286,7 @@ export class EagerLoadingTree {
|
|||||||
const findOptions = {
|
const findOptions = {
|
||||||
where,
|
where,
|
||||||
attributes: node.attributes,
|
attributes: node.attributes,
|
||||||
order: orderOption(association),
|
order: params.order || orderOption(association),
|
||||||
transaction,
|
transaction,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -358,7 +373,7 @@ export class EagerLoadingTree {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
order: orderOption(association),
|
order: params.order || orderOption(association),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user