@nocobase/client-v2 2.4.0-alpha.4 → 2.4.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/index.mjs +4 -4
- package/lib/index.js +26 -26
- package/package.json +7 -7
- package/src/collection-manager/__tests__/field-configure.test.ts +52 -0
- package/src/collection-manager/field-configure.ts +2 -2
- package/src/flow/models/blocks/form/FormBlockModel.tsx +26 -5
- package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +145 -0
- package/src/flow/models/fields/DisplayAssociationField/DisplaySubTableFieldModel.tsx +42 -7
- package/src/flow/models/fields/DisplayAssociationField/__tests__/DisplaySubTableFieldModel.test.tsx +351 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/client-v2",
|
|
3
|
-
"version": "2.4.0-alpha.
|
|
3
|
+
"version": "2.4.0-alpha.5",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "es/index.mjs",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"@formily/antd-v5": "1.2.3",
|
|
28
28
|
"@formily/react": "^2.2.27",
|
|
29
29
|
"@formily/shared": "^2.2.27",
|
|
30
|
-
"@nocobase/evaluators": "2.4.0-alpha.
|
|
31
|
-
"@nocobase/flow-engine": "2.4.0-alpha.
|
|
32
|
-
"@nocobase/sdk": "2.4.0-alpha.
|
|
33
|
-
"@nocobase/shared": "2.4.0-alpha.
|
|
34
|
-
"@nocobase/utils": "2.4.0-alpha.
|
|
30
|
+
"@nocobase/evaluators": "2.4.0-alpha.5",
|
|
31
|
+
"@nocobase/flow-engine": "2.4.0-alpha.5",
|
|
32
|
+
"@nocobase/sdk": "2.4.0-alpha.5",
|
|
33
|
+
"@nocobase/shared": "2.4.0-alpha.5",
|
|
34
|
+
"@nocobase/utils": "2.4.0-alpha.5",
|
|
35
35
|
"ahooks": "^3.7.2",
|
|
36
36
|
"antd": "5.24.2",
|
|
37
37
|
"antd-style": "3.7.1",
|
|
@@ -48,5 +48,5 @@
|
|
|
48
48
|
"react-i18next": "^11.15.1",
|
|
49
49
|
"react-router-dom": "^6.30.1"
|
|
50
50
|
},
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "ce4b0948641b13d2964c44905cb30bebe877a657"
|
|
52
52
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, expect, it } from 'vitest';
|
|
11
|
+
import { getCoreFieldConfigureState, reverseFieldConfigureItems } from '../field-configure';
|
|
12
|
+
|
|
13
|
+
describe('inverse relationship configuration', () => {
|
|
14
|
+
it.each(['hasOne', 'hasMany', 'belongsTo', 'belongsToMany'])(
|
|
15
|
+
'keeps the %s inverse relationship type disabled when creating or editing a field',
|
|
16
|
+
(type) => {
|
|
17
|
+
for (const createOnly of [true, false]) {
|
|
18
|
+
for (const showReverseFieldConfig of [true, false]) {
|
|
19
|
+
const values = {
|
|
20
|
+
autoCreateReverseField: true,
|
|
21
|
+
reverseField: { type, ...(createOnly ? {} : { key: 'existing-reverse-field' }) },
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
expect(
|
|
25
|
+
getCoreFieldConfigureState('reverseField.type', values, { createOnly, showReverseFieldConfig }),
|
|
26
|
+
).toEqual({
|
|
27
|
+
disabled: true,
|
|
28
|
+
hidden: !showReverseFieldConfig,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
it.each(['reverseField.name', 'reverseField.uiSchema.title'])(
|
|
36
|
+
'allows editing %s only when the inverse configuration is visible',
|
|
37
|
+
(name) => {
|
|
38
|
+
for (const showReverseFieldConfig of [true, false]) {
|
|
39
|
+
expect(getCoreFieldConfigureState(name, {}, { showReverseFieldConfig })).toEqual({
|
|
40
|
+
disabled: !showReverseFieldConfig,
|
|
41
|
+
hidden: !showReverseFieldConfig,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
it('also disables the inverse relationship type in explicit configure items', () => {
|
|
48
|
+
const item = reverseFieldConfigureItems().find(({ name }) => name === 'reverseField.type');
|
|
49
|
+
|
|
50
|
+
expect(item?.disabled).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -271,7 +271,7 @@ export function getCoreFieldConfigureState(
|
|
|
271
271
|
|
|
272
272
|
if (name.startsWith('reverseField.')) {
|
|
273
273
|
return {
|
|
274
|
-
disabled: !context.showReverseFieldConfig,
|
|
274
|
+
disabled: name === 'reverseField.type' || !context.showReverseFieldConfig,
|
|
275
275
|
hidden: !context.showReverseFieldConfig,
|
|
276
276
|
};
|
|
277
277
|
}
|
|
@@ -514,7 +514,7 @@ export function reverseFieldConfigureItems(): FieldConfigureItem[] {
|
|
|
514
514
|
{ label: "{{t('BelongsToMany')}}", value: 'belongsToMany' },
|
|
515
515
|
],
|
|
516
516
|
hidden: ({ context, values }) => !context.showReverseFieldConfig && !get(values, 'autoCreateReverseField'),
|
|
517
|
-
disabled:
|
|
517
|
+
disabled: true,
|
|
518
518
|
},
|
|
519
519
|
{
|
|
520
520
|
name: 'reverseField.uiSchema.title',
|
|
@@ -399,20 +399,41 @@ export class FormBlockModel<
|
|
|
399
399
|
if (Array.isArray(topValue) && topValue.length === 0) return false;
|
|
400
400
|
|
|
401
401
|
// 本地优先:支持对多关系的 dot 聚合路径(例如 assignees.name)。
|
|
402
|
-
//
|
|
403
|
-
// 因而这里先用 getValuesByPath 做一次前端可解析性检查,命中则直接前端解析。
|
|
402
|
+
// 关联字段只有在本地值包含目标标题字段时才算完整;标量外键或仅含主键的轻量对象仍需服务端补全。
|
|
404
403
|
const formValuesSnapshot = runtime.getFormValuesSnapshot();
|
|
404
|
+
let shouldResolveAssociationValueOnServer = false;
|
|
405
405
|
if (formValuesSnapshot && typeof formValuesSnapshot === 'object') {
|
|
406
|
-
const localResolved = getValuesByPath(formValuesSnapshot as Record<string,
|
|
406
|
+
const localResolved = getValuesByPath(formValuesSnapshot as Record<string, unknown>, subPath);
|
|
407
407
|
if (typeof localResolved !== 'undefined') {
|
|
408
|
-
|
|
408
|
+
const fieldPath = subPath
|
|
409
|
+
.replace(/\[\d+\]/g, '')
|
|
410
|
+
.split('.')
|
|
411
|
+
.filter((segment) => !/^\d+$/.test(segment))
|
|
412
|
+
.join('.');
|
|
413
|
+
const resolvedField = this.collection?.getFieldByPath?.(fieldPath);
|
|
414
|
+
const titleFieldName = resolvedField?.targetCollectionTitleFieldName;
|
|
415
|
+
const isLoadedAssociationRecord = (value: unknown) => {
|
|
416
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
417
|
+
if (!titleFieldName) return true;
|
|
418
|
+
return typeof (value as Record<string, unknown>)[titleFieldName] !== 'undefined';
|
|
419
|
+
};
|
|
420
|
+
const isAssociationValueLoaded =
|
|
421
|
+
localResolved === null ||
|
|
422
|
+
(Array.isArray(localResolved)
|
|
423
|
+
? localResolved.length === 0 ||
|
|
424
|
+
localResolved.every((value) => value === null || isLoadedAssociationRecord(value))
|
|
425
|
+
: isLoadedAssociationRecord(localResolved));
|
|
426
|
+
if (!resolvedField?.isAssociationField?.() || isAssociationValueLoaded) {
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
shouldResolveAssociationValueOnServer = true;
|
|
409
430
|
}
|
|
410
431
|
}
|
|
411
432
|
|
|
412
433
|
// 已配置字段:仅关联字段的子路径按需服务端补全(保持现有语义)
|
|
413
434
|
const assocResolver = createAssociationSubpathResolver(
|
|
414
435
|
() => this.collection,
|
|
415
|
-
() => runtime.getFormValuesSnapshot(),
|
|
436
|
+
shouldResolveAssociationValueOnServer ? undefined : () => runtime.getFormValuesSnapshot(),
|
|
416
437
|
);
|
|
417
438
|
return assocResolver(subPath);
|
|
418
439
|
}
|
|
@@ -77,6 +77,7 @@ async function setupFormModel() {
|
|
|
77
77
|
ds.addCollection({
|
|
78
78
|
name: 'levels',
|
|
79
79
|
filterTargetKey: 'id',
|
|
80
|
+
titleField: 'name',
|
|
80
81
|
fields: [
|
|
81
82
|
{ name: 'id', type: 'integer', interface: 'number' },
|
|
82
83
|
{ name: 'name', type: 'string', interface: 'text' },
|
|
@@ -549,6 +550,150 @@ describe('FormBlockModel (form/formValues injection & server resolve anchors)',
|
|
|
549
550
|
expect(api.request).toHaveBeenCalledTimes(1);
|
|
550
551
|
});
|
|
551
552
|
|
|
553
|
+
it('resolves a configured nested association from the server when the local value is only its key', async () => {
|
|
554
|
+
const model = await setupFormModel();
|
|
555
|
+
const api = {
|
|
556
|
+
request: vi.fn(async (config: any) => {
|
|
557
|
+
const batch = config?.data?.values?.batch || [];
|
|
558
|
+
const item = batch[0] || {};
|
|
559
|
+
expect(item.template).toEqual({ level: '{{ ctx.formValues.customer.level }}' });
|
|
560
|
+
expect(Object.keys(item.contextParams || {})).toEqual(['formValues.customer']);
|
|
561
|
+
expect(item.contextParams['formValues.customer']).toMatchObject({
|
|
562
|
+
collection: 'customers',
|
|
563
|
+
filterByTk: 9,
|
|
564
|
+
});
|
|
565
|
+
return {
|
|
566
|
+
data: {
|
|
567
|
+
data: {
|
|
568
|
+
results: [{ id: item.id, data: { level: { id: 'level-1', name: 'Level 1' } } }],
|
|
569
|
+
},
|
|
570
|
+
},
|
|
571
|
+
} as any;
|
|
572
|
+
}),
|
|
573
|
+
} as any;
|
|
574
|
+
(model.flowEngine.context as any).defineProperty('api', { value: api });
|
|
575
|
+
|
|
576
|
+
function HookCaller() {
|
|
577
|
+
model.useHooksBeforeRender();
|
|
578
|
+
return null;
|
|
579
|
+
}
|
|
580
|
+
render(React.createElement(HookCaller));
|
|
581
|
+
|
|
582
|
+
const mem: Record<string, any> = {};
|
|
583
|
+
const fakeForm = {
|
|
584
|
+
setFieldsValue: (values: Record<string, any>) => Object.assign(mem, values),
|
|
585
|
+
getFieldsValue: () => ({ ...mem }),
|
|
586
|
+
getFieldValue: (namePath: any) => getByPath(mem, namePath),
|
|
587
|
+
setFieldValue: (key: string, value: any) => (mem[key] = value),
|
|
588
|
+
};
|
|
589
|
+
(model.context as any).defineProperty('form', { value: fakeForm });
|
|
590
|
+
fakeForm.setFieldsValue({ customer: { id: 9, level: 'level-1' } });
|
|
591
|
+
mockFormGridEnabledFields(model, ['customer']);
|
|
592
|
+
|
|
593
|
+
const output = await (model.context as any).resolveJsonTemplate({
|
|
594
|
+
level: '{{ ctx.formValues.customer.level }}',
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
expect(api.request).toHaveBeenCalledTimes(1);
|
|
598
|
+
expect(output).toEqual({ level: { id: 'level-1', name: 'Level 1' } });
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
it('resolves a configured nested association when its local record does not include the title field', async () => {
|
|
602
|
+
const model = await setupFormModel();
|
|
603
|
+
const api = {
|
|
604
|
+
request: vi.fn(async (config: any) => {
|
|
605
|
+
const item = config?.data?.values?.batch?.[0] || {};
|
|
606
|
+
return {
|
|
607
|
+
data: {
|
|
608
|
+
data: {
|
|
609
|
+
results: [{ id: item.id, data: { level: { id: 'level-1', name: 'Level 1' } } }],
|
|
610
|
+
},
|
|
611
|
+
},
|
|
612
|
+
} as any;
|
|
613
|
+
}),
|
|
614
|
+
} as any;
|
|
615
|
+
(model.flowEngine.context as any).defineProperty('api', { value: api });
|
|
616
|
+
|
|
617
|
+
function HookCaller() {
|
|
618
|
+
model.useHooksBeforeRender();
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
621
|
+
render(React.createElement(HookCaller));
|
|
622
|
+
|
|
623
|
+
const mem: Record<string, any> = {};
|
|
624
|
+
const fakeForm = {
|
|
625
|
+
setFieldsValue: (values: Record<string, any>) => Object.assign(mem, values),
|
|
626
|
+
getFieldsValue: () => ({ ...mem }),
|
|
627
|
+
getFieldValue: (namePath: any) => getByPath(mem, namePath),
|
|
628
|
+
setFieldValue: (key: string, value: any) => (mem[key] = value),
|
|
629
|
+
};
|
|
630
|
+
(model.context as any).defineProperty('form', { value: fakeForm });
|
|
631
|
+
fakeForm.setFieldsValue({ customer: { id: 9, level: { id: 'level-1' } } });
|
|
632
|
+
mockFormGridEnabledFields(model, ['customer']);
|
|
633
|
+
|
|
634
|
+
const output = await (model.context as any).resolveJsonTemplate({
|
|
635
|
+
level: '{{ ctx.formValues.customer.level }}',
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
expect(api.request).toHaveBeenCalledTimes(1);
|
|
639
|
+
expect(output).toEqual({ level: { id: 'level-1', name: 'Level 1' } });
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
it('uses a configured nested association locally when its record is already loaded', async () => {
|
|
643
|
+
const model = await setupFormModel();
|
|
644
|
+
const api = { request: vi.fn(async () => ({ data: {} }) as any) } as any;
|
|
645
|
+
(model.flowEngine.context as any).defineProperty('api', { value: api });
|
|
646
|
+
|
|
647
|
+
function HookCaller() {
|
|
648
|
+
model.useHooksBeforeRender();
|
|
649
|
+
return null;
|
|
650
|
+
}
|
|
651
|
+
render(React.createElement(HookCaller));
|
|
652
|
+
|
|
653
|
+
const mem: Record<string, any> = {};
|
|
654
|
+
const fakeForm = {
|
|
655
|
+
setFieldsValue: (values: Record<string, any>) => Object.assign(mem, values),
|
|
656
|
+
getFieldsValue: () => ({ ...mem }),
|
|
657
|
+
getFieldValue: (namePath: any) => getByPath(mem, namePath),
|
|
658
|
+
setFieldValue: (key: string, value: any) => (mem[key] = value),
|
|
659
|
+
};
|
|
660
|
+
(model.context as any).defineProperty('form', { value: fakeForm });
|
|
661
|
+
const level = { id: 'level-1', name: 'Level 1' };
|
|
662
|
+
fakeForm.setFieldsValue({ customer: { id: 9, level } });
|
|
663
|
+
mockFormGridEnabledFields(model, ['customer']);
|
|
664
|
+
|
|
665
|
+
const output = await (model.context as any).resolveJsonTemplate({
|
|
666
|
+
level: '{{ ctx.formValues.customer.level }}',
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
expect(output).toEqual({ level });
|
|
670
|
+
expect(api.request).not.toHaveBeenCalled();
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
it('keeps indexed paths local when the nested association record is already loaded', async () => {
|
|
674
|
+
const model = await setupFormModel();
|
|
675
|
+
|
|
676
|
+
function HookCaller() {
|
|
677
|
+
model.useHooksBeforeRender();
|
|
678
|
+
return null;
|
|
679
|
+
}
|
|
680
|
+
render(React.createElement(HookCaller));
|
|
681
|
+
|
|
682
|
+
const mem: Record<string, any> = {};
|
|
683
|
+
const fakeForm = {
|
|
684
|
+
setFieldsValue: (values: Record<string, any>) => Object.assign(mem, values),
|
|
685
|
+
getFieldsValue: () => ({ ...mem }),
|
|
686
|
+
getFieldValue: (namePath: any) => getByPath(mem, namePath),
|
|
687
|
+
setFieldValue: (key: string, value: any) => (mem[key] = value),
|
|
688
|
+
};
|
|
689
|
+
(model.context as any).defineProperty('form', { value: fakeForm });
|
|
690
|
+
fakeForm.setFieldsValue({ assignees: [{ id: 3, org: { id: 1, name: 'Org 1' } }] });
|
|
691
|
+
mockFormGridEnabledFields(model, ['assignees']);
|
|
692
|
+
|
|
693
|
+
const options = (model.context as any).getPropertyOptions('formValues');
|
|
694
|
+
expect(options.resolveOnServer('assignees[0].org.name')).toBe(false);
|
|
695
|
+
});
|
|
696
|
+
|
|
552
697
|
it('configured toMany dot aggregation path uses local value and skips server', async () => {
|
|
553
698
|
const model = await setupFormModel();
|
|
554
699
|
|
|
@@ -17,14 +17,16 @@ import {
|
|
|
17
17
|
observer,
|
|
18
18
|
} from '@nocobase/flow-engine';
|
|
19
19
|
import { Table } from 'antd';
|
|
20
|
+
import type { TableProps } from 'antd';
|
|
20
21
|
import classNames from 'classnames';
|
|
21
22
|
import { DragEndEvent } from '@dnd-kit/core';
|
|
22
23
|
import { css } from '@emotion/css';
|
|
23
|
-
import { isEmpty } from 'lodash';
|
|
24
|
+
import { get, isEmpty, orderBy } from 'lodash';
|
|
24
25
|
import React, { useEffect, useMemo, useState, useCallback } from 'react';
|
|
25
26
|
import { useTranslation } from 'react-i18next';
|
|
26
27
|
import { FieldModel } from '../../base';
|
|
27
28
|
import { DetailsItemModel } from '../../blocks/details/DetailsItemModel';
|
|
29
|
+
import { FormAssociationItemModel } from '../../blocks/form/FormAssociationItemModel';
|
|
28
30
|
import { adjustColumnOrder } from '../../blocks/table/utils';
|
|
29
31
|
|
|
30
32
|
const HeaderWrapperComponent = React.memo((props) => {
|
|
@@ -65,11 +67,31 @@ const AddFieldColumn = ({ model }) => {
|
|
|
65
67
|
};
|
|
66
68
|
|
|
67
69
|
const DisplayTable = (props) => {
|
|
68
|
-
const { pageSize, value, size, collection, baseColumns, enableIndexColumn = true, model } = props;
|
|
70
|
+
const { pageSize, value: rawValue, size, collection, baseColumns, enableIndexColumn = true, model } = props;
|
|
71
|
+
const isFormAssociation = model.parent instanceof FormAssociationItemModel;
|
|
69
72
|
const [currentPage, setCurrentPage] = useState(1);
|
|
70
73
|
const [currentPageSize, setCurrentPageSize] = useState(pageSize);
|
|
74
|
+
const [localSort, setLocalSort] = useState<{ field: string; order: 'asc' | 'desc' }>();
|
|
71
75
|
const { t } = useTranslation();
|
|
72
76
|
|
|
77
|
+
const value = useMemo(() => {
|
|
78
|
+
if (!isFormAssociation || Array.isArray(rawValue)) return rawValue;
|
|
79
|
+
if (rawValue && Array.isArray(rawValue.rows)) return rawValue.rows;
|
|
80
|
+
return rawValue && typeof rawValue === 'object' ? [rawValue] : [];
|
|
81
|
+
}, [isFormAssociation, rawValue]);
|
|
82
|
+
|
|
83
|
+
const sortedValue = useMemo(
|
|
84
|
+
() =>
|
|
85
|
+
isFormAssociation && localSort
|
|
86
|
+
? orderBy(value, [(record) => get(record, localSort.field)], [localSort.order])
|
|
87
|
+
: value,
|
|
88
|
+
[isFormAssociation, localSort, value],
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (isFormAssociation) setCurrentPage(1);
|
|
93
|
+
}, [isFormAssociation, rawValue]);
|
|
94
|
+
|
|
73
95
|
useEffect(() => {
|
|
74
96
|
setCurrentPageSize(pageSize);
|
|
75
97
|
}, [pageSize]);
|
|
@@ -89,7 +111,7 @@ const DisplayTable = (props) => {
|
|
|
89
111
|
return t('Total {{count}} items', { count: total });
|
|
90
112
|
},
|
|
91
113
|
} as any;
|
|
92
|
-
}, [currentPage, currentPageSize, value]);
|
|
114
|
+
}, [currentPage, currentPageSize, value, t]);
|
|
93
115
|
|
|
94
116
|
const getColumns = () => {
|
|
95
117
|
const cols = adjustColumnOrder(
|
|
@@ -119,8 +141,20 @@ const DisplayTable = (props) => {
|
|
|
119
141
|
}
|
|
120
142
|
return cols;
|
|
121
143
|
};
|
|
122
|
-
const handleChange = useCallback(
|
|
123
|
-
async (pagination, filters,
|
|
144
|
+
const handleChange = useCallback<NonNullable<TableProps<Record<string, unknown>>['onChange']>>(
|
|
145
|
+
async (pagination, filters, sorters, extra) => {
|
|
146
|
+
const sorter = Array.isArray(sorters) ? sorters[0] : sorters;
|
|
147
|
+
if (isFormAssociation) {
|
|
148
|
+
if (extra.action !== 'sort') return;
|
|
149
|
+
const column = sorter?.column as { sortField?: string } | undefined;
|
|
150
|
+
const sortField = column?.sortField || sorter?.field;
|
|
151
|
+
const fullPath = Array.isArray(sortField) ? sortField.join('.') : String(sortField ?? '');
|
|
152
|
+
const prefix = `${model.context.fieldPath}.`;
|
|
153
|
+
const field = fullPath.startsWith(prefix) ? fullPath.slice(prefix.length) : fullPath;
|
|
154
|
+
setLocalSort(sorter?.order && field ? { field, order: sorter.order === 'ascend' ? 'asc' : 'desc' } : undefined);
|
|
155
|
+
setCurrentPage(1);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
124
158
|
//支持列点击排序
|
|
125
159
|
if (!isEmpty(sorter)) {
|
|
126
160
|
const resource = model.context.blockModel.resource;
|
|
@@ -138,7 +172,7 @@ const DisplayTable = (props) => {
|
|
|
138
172
|
await resource.refresh();
|
|
139
173
|
}
|
|
140
174
|
},
|
|
141
|
-
[model],
|
|
175
|
+
[isFormAssociation, model],
|
|
142
176
|
);
|
|
143
177
|
|
|
144
178
|
return (
|
|
@@ -147,7 +181,7 @@ const DisplayTable = (props) => {
|
|
|
147
181
|
size={size}
|
|
148
182
|
rowKey={collection.filterTargetKey}
|
|
149
183
|
scroll={{ x: 'max-content' }}
|
|
150
|
-
dataSource={
|
|
184
|
+
dataSource={sortedValue}
|
|
151
185
|
columns={getColumns()}
|
|
152
186
|
pagination={pagination}
|
|
153
187
|
onChange={handleChange}
|
|
@@ -269,3 +303,4 @@ DisplaySubTableFieldModel.define({
|
|
|
269
303
|
});
|
|
270
304
|
|
|
271
305
|
DetailsItemModel.bindModelToInterface('DisplaySubTableFieldModel', ['m2m', 'o2m', 'mbm']);
|
|
306
|
+
FormAssociationItemModel.bindModelToInterface('DisplaySubTableFieldModel', ['m2m', 'o2m', 'mbm']);
|