@nocobase/client-v2 2.1.0-beta.43 → 2.1.0-beta.45
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/BaseApplication.d.ts +9 -0
- package/es/authRedirect.d.ts +12 -0
- package/es/collection-manager/filter-operators/index.d.ts +1 -0
- package/es/components/form/filter/CollectionFilterItem.d.ts +5 -1
- package/es/components/form/filter/FilterValueInput.d.ts +5 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +1 -0
- package/es/flow/admin-shell/admin-layout/AppListRender.d.ts +11 -0
- package/es/flow/admin-shell/admin-layout/index.d.ts +1 -0
- package/es/flow/admin-shell/admin-layout/useApplications.d.ts +3 -2
- package/es/flow/components/ConditionBuilder.d.ts +1 -0
- package/es/flow/components/FieldAssignRulesEditor.d.ts +3 -1
- package/es/flow/components/FieldAssignValueInput.d.ts +1 -0
- package/es/flow/components/fieldAssignOptions.d.ts +10 -0
- package/es/flow/components/filter/LinkageFilterItem.d.ts +1 -0
- package/es/flow/components/filter/VariableFilterItem.d.ts +1 -0
- package/es/flow/components/filter/metaTreeAssociationDepth.d.ts +13 -0
- package/es/flow/internal/utils/operatorSchemaHelper.d.ts +26 -3
- package/es/flow/models/base/AssociationFieldGroupModel.d.ts +2 -33
- package/es/flow/models/blocks/filter-manager/utils.d.ts +23 -1
- package/es/flow/models/blocks/table/TableColumnModel.d.ts +2 -0
- package/es/index.mjs +179 -101
- package/lib/index.js +187 -109
- package/package.json +7 -7
- package/src/BaseApplication.tsx +13 -0
- package/src/__tests__/browserChecker.test.ts +103 -0
- package/src/__tests__/dataSourceRuntime.test.tsx +22 -1
- package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +5 -0
- package/src/authRedirect.ts +43 -0
- package/src/collection-field-interface/CollectionFieldInterface.ts +14 -7
- package/src/collection-manager/filter-operators/index.ts +29 -2
- package/src/components/form/filter/CollectionFilterItem.tsx +10 -2
- package/src/components/form/filter/FilterValueInput.tsx +27 -2
- package/src/components/form/filter/__tests__/FilterValueInput.test.tsx +39 -0
- package/src/flow/__tests__/FlowRoute.test.tsx +8 -1
- package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +369 -2
- package/src/flow/actions/dataScope.tsx +8 -1
- package/src/flow/actions/linkageRules.tsx +56 -1
- package/src/flow/actions/setTargetDataScope.tsx +1 -1
- package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +31 -3
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +15 -1
- package/src/flow/admin-shell/admin-layout/AppListRender.tsx +139 -0
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +75 -1
- package/src/flow/admin-shell/admin-layout/index.ts +1 -0
- package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.test.ts +11 -0
- package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.ts +2 -4
- package/src/flow/admin-shell/admin-layout/useApplications.tsx +34 -1
- package/src/flow/common/Markdown/Edit.tsx +32 -4
- package/src/flow/components/ConditionBuilder.tsx +12 -2
- package/src/flow/components/DefaultValue.tsx +14 -8
- package/src/flow/components/FieldAssignRulesEditor.tsx +37 -46
- package/src/flow/components/FieldAssignValueInput.tsx +14 -14
- package/src/flow/components/__tests__/FieldAssignRulesEditor.test.tsx +112 -1
- package/src/flow/components/__tests__/fieldAssignOptions.test.ts +71 -1
- package/src/flow/components/fieldAssignOptions.ts +23 -6
- package/src/flow/components/filter/LinkageFilterItem.tsx +26 -12
- package/src/flow/components/filter/VariableFilterItem.tsx +32 -13
- package/src/flow/components/filter/__tests__/fieldsToOptions.test.ts +48 -0
- package/src/flow/components/filter/__tests__/metaTreeAssociationDepth.test.ts +60 -0
- package/src/flow/components/filter/fieldsToOptions.ts +9 -4
- package/src/flow/components/filter/metaTreeAssociationDepth.ts +69 -0
- package/src/flow/internal/utils/operatorSchemaHelper.ts +112 -5
- package/src/flow/models/actions/FilterActionModel.tsx +15 -3
- package/src/flow/models/base/AssociationFieldGroupModel.tsx +20 -3
- package/src/flow/models/base/GridModel.tsx +1 -1
- package/src/flow/models/base/PageModel/ChildPageModel.tsx +5 -1
- package/src/flow/models/base/PageModel/__tests__/ChildPageModel.test.ts +12 -0
- package/src/flow/models/base/__tests__/AssociationFieldGroupModel.test.ts +86 -0
- package/src/flow/models/base/__tests__/GridModel.render.test.tsx +53 -0
- package/src/flow/models/blocks/assign-form/AssignFormGridModel.tsx +11 -0
- package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +14 -12
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +6 -1
- package/src/flow/models/blocks/filter-manager/__tests__/getDefaultOperator.test.ts +35 -1
- package/src/flow/models/blocks/filter-manager/flow-actions/__tests__/customizeFilterRender.test.tsx +148 -0
- package/src/flow/models/blocks/filter-manager/flow-actions/__tests__/defaultOperator.test.tsx +85 -0
- package/src/flow/models/blocks/filter-manager/flow-actions/connectFields.tsx +48 -6
- package/src/flow/models/blocks/filter-manager/flow-actions/customizeFilterRender.tsx +14 -44
- package/src/flow/models/blocks/filter-manager/flow-actions/defaultOperator.tsx +3 -5
- package/src/flow/models/blocks/filter-manager/flow-actions/operatorComponentProps.tsx +2 -12
- package/src/flow/models/blocks/filter-manager/utils.ts +143 -4
- package/src/flow/models/blocks/form/FormAssociationItemModel.tsx +9 -0
- package/src/flow/models/blocks/form/FormItemModel.tsx +9 -0
- package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +17 -0
- package/src/flow/models/blocks/form/__tests__/FormItemModel.defineChildren.test.ts +27 -2
- package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +29 -0
- package/src/flow/models/blocks/form/value-runtime/runtime.ts +5 -1
- package/src/flow/models/blocks/table/TableColumnModel.tsx +15 -5
- package/src/flow/models/blocks/table/TableCustomColumnModel.tsx +2 -2
- package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +18 -0
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.tsx +6 -7
- package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +5 -3
- package/src/flow/models/fields/mobile-components/__tests__/MobileSelect.test.tsx +70 -0
|
@@ -9,9 +9,15 @@
|
|
|
9
9
|
|
|
10
10
|
import React from 'react';
|
|
11
11
|
import { fireEvent, render, screen } from '@testing-library/react';
|
|
12
|
-
import { FlowSettingsContextProvider } from '@nocobase/flow-engine';
|
|
12
|
+
import { FlowEngine, FlowModel, FlowSettingsContextProvider } from '@nocobase/flow-engine';
|
|
13
13
|
import { describe, expect, it, vi } from 'vitest';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
fieldLinkageRules,
|
|
16
|
+
linkageAssignField,
|
|
17
|
+
linkageSetFieldProps,
|
|
18
|
+
subFormFieldLinkageRules,
|
|
19
|
+
subFormLinkageSetFieldProps,
|
|
20
|
+
} from '../linkageRules';
|
|
15
21
|
|
|
16
22
|
const createSubFormFieldModel = ({
|
|
17
23
|
uid,
|
|
@@ -335,6 +341,110 @@ describe('subFormLinkageSetFieldProps action', () => {
|
|
|
335
341
|
expect(row0CollectedModel.hidden).toBe(false);
|
|
336
342
|
expect(row1CollectedModel.hidden).toBe(true);
|
|
337
343
|
});
|
|
344
|
+
|
|
345
|
+
it('should clear the current row value when a subform list field is hidden without reserving value', async () => {
|
|
346
|
+
const setFormValues = vi.fn(async () => undefined);
|
|
347
|
+
const form = {
|
|
348
|
+
getFieldValue: vi.fn((path: Array<string | number>) => {
|
|
349
|
+
if (JSON.stringify(path) === JSON.stringify(['items', 0, 'a'])) {
|
|
350
|
+
return 'row value';
|
|
351
|
+
}
|
|
352
|
+
return undefined;
|
|
353
|
+
}),
|
|
354
|
+
setFieldValue: vi.fn(),
|
|
355
|
+
};
|
|
356
|
+
const engine = new FlowEngine();
|
|
357
|
+
|
|
358
|
+
const rowGridFork = new FlowModel({ uid: 'row-grid-fork', flowEngine: engine }) as any;
|
|
359
|
+
rowGridFork.hidden = false;
|
|
360
|
+
rowGridFork.context.defineProperty('fieldKey', { value: ['items:0'] });
|
|
361
|
+
rowGridFork.context.defineProperty('fieldIndex', { value: ['items:0'] });
|
|
362
|
+
rowGridFork.context.defineProperty('form', { value: form });
|
|
363
|
+
rowGridFork.context.defineProperty('setFormValues', { value: setFormValues });
|
|
364
|
+
rowGridFork.context.defineProperty('app', {
|
|
365
|
+
value: {
|
|
366
|
+
jsonLogic: {
|
|
367
|
+
apply: () => true,
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
rowGridFork.getAction = vi.fn((name: string) => {
|
|
372
|
+
if (name === 'subFormLinkageSetFieldProps') {
|
|
373
|
+
return subFormLinkageSetFieldProps;
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
const targetFieldFork: any = {
|
|
378
|
+
uid: 'field-a',
|
|
379
|
+
isFork: true,
|
|
380
|
+
hidden: false,
|
|
381
|
+
props: {},
|
|
382
|
+
context: {
|
|
383
|
+
fieldIndex: ['items:0'],
|
|
384
|
+
},
|
|
385
|
+
getStepParams: vi.fn((flowKey: string, stepKey: string) => {
|
|
386
|
+
if (flowKey === 'fieldSettings' && stepKey === 'init') {
|
|
387
|
+
return { fieldPath: 'a' };
|
|
388
|
+
}
|
|
389
|
+
}),
|
|
390
|
+
setProps(key: any, value?: any) {
|
|
391
|
+
if (typeof key === 'string') {
|
|
392
|
+
this.props[key] = value;
|
|
393
|
+
} else {
|
|
394
|
+
this.props = { ...this.props, ...key };
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
const formItemModel: any = {
|
|
400
|
+
uid: 'field-a',
|
|
401
|
+
getFork: vi.fn((key: string) => (key === 'items:0:field-a' ? targetFieldFork : undefined)),
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
engine.getModel = vi.fn((uid: string) => (uid === 'field-a' ? formItemModel : undefined)) as any;
|
|
405
|
+
|
|
406
|
+
await subFormFieldLinkageRules.handler(
|
|
407
|
+
{
|
|
408
|
+
model: {
|
|
409
|
+
hidden: false,
|
|
410
|
+
subModels: {
|
|
411
|
+
grid: {
|
|
412
|
+
forks: [rowGridFork],
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
flowKey: 'eventSettings',
|
|
417
|
+
} as any,
|
|
418
|
+
{
|
|
419
|
+
value: [
|
|
420
|
+
{
|
|
421
|
+
key: 'rule-1',
|
|
422
|
+
enable: true,
|
|
423
|
+
condition: { logic: '$and', items: [] },
|
|
424
|
+
actions: [
|
|
425
|
+
{
|
|
426
|
+
name: 'subFormLinkageSetFieldProps',
|
|
427
|
+
params: {
|
|
428
|
+
value: {
|
|
429
|
+
fields: ['field-a'],
|
|
430
|
+
state: 'hidden',
|
|
431
|
+
},
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
],
|
|
435
|
+
},
|
|
436
|
+
],
|
|
437
|
+
},
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
expect(form.getFieldValue).toHaveBeenCalledWith(['items', 0, 'a']);
|
|
441
|
+
expect(targetFieldFork.hidden).toBe(true);
|
|
442
|
+
expect(setFormValues).toHaveBeenCalledWith(
|
|
443
|
+
[{ path: ['items', 0, 'a'], value: undefined }],
|
|
444
|
+
expect.objectContaining({ source: 'linkage' }),
|
|
445
|
+
);
|
|
446
|
+
expect(form.setFieldValue).not.toHaveBeenCalled();
|
|
447
|
+
});
|
|
338
448
|
});
|
|
339
449
|
|
|
340
450
|
describe('linkageSetFieldProps action', () => {
|
|
@@ -541,6 +651,263 @@ describe('linkageSetFieldProps action', () => {
|
|
|
541
651
|
expect(form.setFieldValue).not.toHaveBeenCalled();
|
|
542
652
|
});
|
|
543
653
|
|
|
654
|
+
it('should clear form value when a field is hidden without reserving value', async () => {
|
|
655
|
+
const setFormValues = vi.fn(async () => undefined);
|
|
656
|
+
const form = {
|
|
657
|
+
getFieldValue: vi.fn(() => '123'),
|
|
658
|
+
setFieldValue: vi.fn(),
|
|
659
|
+
};
|
|
660
|
+
const fieldModel: any = {
|
|
661
|
+
uid: 'name-field',
|
|
662
|
+
hidden: false,
|
|
663
|
+
context: { form },
|
|
664
|
+
props: {
|
|
665
|
+
label: 'Name',
|
|
666
|
+
},
|
|
667
|
+
getStepParams: vi.fn((flowKey: string, stepKey: string) => {
|
|
668
|
+
if (flowKey === 'fieldSettings' && stepKey === 'init') {
|
|
669
|
+
return { fieldPath: 'name' };
|
|
670
|
+
}
|
|
671
|
+
}),
|
|
672
|
+
setProps(key: any, value?: any) {
|
|
673
|
+
if (typeof key === 'string') {
|
|
674
|
+
this.props[key] = value;
|
|
675
|
+
} else {
|
|
676
|
+
this.props = { ...this.props, ...key };
|
|
677
|
+
}
|
|
678
|
+
},
|
|
679
|
+
};
|
|
680
|
+
const ctx: any = {
|
|
681
|
+
app: {
|
|
682
|
+
jsonLogic: {
|
|
683
|
+
apply: vi.fn(() => true),
|
|
684
|
+
},
|
|
685
|
+
},
|
|
686
|
+
model: {
|
|
687
|
+
context: { form },
|
|
688
|
+
subModels: {
|
|
689
|
+
grid: {
|
|
690
|
+
subModels: {
|
|
691
|
+
items: [fieldModel],
|
|
692
|
+
},
|
|
693
|
+
},
|
|
694
|
+
},
|
|
695
|
+
},
|
|
696
|
+
setFormValues,
|
|
697
|
+
getAction: (name: string) => (name === 'linkageSetFieldProps' ? linkageSetFieldProps : null),
|
|
698
|
+
resolveJsonTemplate: vi.fn(async (value) => value),
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
await fieldLinkageRules.handler(ctx, {
|
|
702
|
+
value: [
|
|
703
|
+
{
|
|
704
|
+
key: 'rule-1',
|
|
705
|
+
enable: true,
|
|
706
|
+
condition: { logic: '$and', items: [] },
|
|
707
|
+
actions: [
|
|
708
|
+
{
|
|
709
|
+
key: 'action-1',
|
|
710
|
+
name: 'linkageSetFieldProps',
|
|
711
|
+
params: {
|
|
712
|
+
value: {
|
|
713
|
+
fields: ['name-field'],
|
|
714
|
+
state: 'hidden',
|
|
715
|
+
},
|
|
716
|
+
},
|
|
717
|
+
},
|
|
718
|
+
],
|
|
719
|
+
},
|
|
720
|
+
],
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
expect(fieldModel.hidden).toBe(true);
|
|
724
|
+
expect(setFormValues).toHaveBeenCalledWith(
|
|
725
|
+
[{ path: ['name'], value: undefined }],
|
|
726
|
+
expect.objectContaining({ source: 'linkage' }),
|
|
727
|
+
);
|
|
728
|
+
expect(form.setFieldValue).not.toHaveBeenCalled();
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
it('should keep hidden clear after same-round assignment for the same field', async () => {
|
|
732
|
+
const formValues: { name?: string } = {};
|
|
733
|
+
const setFormValues = vi.fn(async (patches: Array<{ path: Array<string | number>; value: string | undefined }>) => {
|
|
734
|
+
for (const patch of patches) {
|
|
735
|
+
if (patch.path.length === 1 && patch.path[0] === 'name') {
|
|
736
|
+
formValues.name = patch.value;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
const form = {
|
|
741
|
+
getFieldValue: vi.fn((path: Array<string | number>) => {
|
|
742
|
+
if (path.length === 1 && path[0] === 'name') {
|
|
743
|
+
return formValues.name;
|
|
744
|
+
}
|
|
745
|
+
return undefined;
|
|
746
|
+
}),
|
|
747
|
+
setFieldValue: vi.fn(),
|
|
748
|
+
};
|
|
749
|
+
const fieldModel: any = {
|
|
750
|
+
uid: 'name-field',
|
|
751
|
+
hidden: false,
|
|
752
|
+
context: { form },
|
|
753
|
+
props: {
|
|
754
|
+
label: 'Name',
|
|
755
|
+
},
|
|
756
|
+
getStepParams: vi.fn((flowKey: string, stepKey: string) => {
|
|
757
|
+
if (flowKey === 'fieldSettings' && stepKey === 'init') {
|
|
758
|
+
return { fieldPath: 'name' };
|
|
759
|
+
}
|
|
760
|
+
}),
|
|
761
|
+
setProps(key: any, value?: any) {
|
|
762
|
+
if (typeof key === 'string') {
|
|
763
|
+
this.props[key] = value;
|
|
764
|
+
} else {
|
|
765
|
+
this.props = { ...this.props, ...key };
|
|
766
|
+
}
|
|
767
|
+
},
|
|
768
|
+
};
|
|
769
|
+
const ctx: any = {
|
|
770
|
+
app: {
|
|
771
|
+
jsonLogic: {
|
|
772
|
+
apply: vi.fn(() => true),
|
|
773
|
+
},
|
|
774
|
+
},
|
|
775
|
+
model: {
|
|
776
|
+
context: { form },
|
|
777
|
+
subModels: {
|
|
778
|
+
grid: {
|
|
779
|
+
subModels: {
|
|
780
|
+
items: [fieldModel],
|
|
781
|
+
},
|
|
782
|
+
},
|
|
783
|
+
},
|
|
784
|
+
},
|
|
785
|
+
setFormValues,
|
|
786
|
+
getAction: (name: string) => {
|
|
787
|
+
if (name === 'linkageSetFieldProps') return linkageSetFieldProps;
|
|
788
|
+
if (name === 'linkageAssignField') return linkageAssignField;
|
|
789
|
+
return null;
|
|
790
|
+
},
|
|
791
|
+
resolveJsonTemplate: vi.fn(async (value) => value),
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
await fieldLinkageRules.handler(ctx, {
|
|
795
|
+
value: [
|
|
796
|
+
{
|
|
797
|
+
key: 'rule-1',
|
|
798
|
+
enable: true,
|
|
799
|
+
condition: { logic: '$and', items: [] },
|
|
800
|
+
actions: [
|
|
801
|
+
{
|
|
802
|
+
key: 'action-1',
|
|
803
|
+
name: 'linkageSetFieldProps',
|
|
804
|
+
params: {
|
|
805
|
+
value: {
|
|
806
|
+
fields: ['name-field'],
|
|
807
|
+
state: 'hidden',
|
|
808
|
+
},
|
|
809
|
+
},
|
|
810
|
+
},
|
|
811
|
+
{
|
|
812
|
+
key: 'action-2',
|
|
813
|
+
name: 'linkageAssignField',
|
|
814
|
+
params: {
|
|
815
|
+
value: [
|
|
816
|
+
{
|
|
817
|
+
key: 'assign-1',
|
|
818
|
+
enable: true,
|
|
819
|
+
targetPath: 'name',
|
|
820
|
+
value: 'assigned',
|
|
821
|
+
},
|
|
822
|
+
],
|
|
823
|
+
},
|
|
824
|
+
},
|
|
825
|
+
],
|
|
826
|
+
},
|
|
827
|
+
],
|
|
828
|
+
});
|
|
829
|
+
|
|
830
|
+
expect(fieldModel.hidden).toBe(true);
|
|
831
|
+
expect(formValues.name).toBeUndefined();
|
|
832
|
+
expect(setFormValues).not.toHaveBeenCalled();
|
|
833
|
+
expect(form.setFieldValue).not.toHaveBeenCalled();
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
it('should not clear form value when a field is hidden with reserved value', async () => {
|
|
837
|
+
const setFormValues = vi.fn(async () => undefined);
|
|
838
|
+
const form = {
|
|
839
|
+
getFieldValue: vi.fn(() => '123'),
|
|
840
|
+
setFieldValue: vi.fn(),
|
|
841
|
+
};
|
|
842
|
+
const fieldModel: any = {
|
|
843
|
+
uid: 'name-field',
|
|
844
|
+
hidden: false,
|
|
845
|
+
context: { form },
|
|
846
|
+
props: {
|
|
847
|
+
label: 'Name',
|
|
848
|
+
},
|
|
849
|
+
getStepParams: vi.fn((flowKey: string, stepKey: string) => {
|
|
850
|
+
if (flowKey === 'fieldSettings' && stepKey === 'init') {
|
|
851
|
+
return { fieldPath: 'name' };
|
|
852
|
+
}
|
|
853
|
+
}),
|
|
854
|
+
setProps(key: any, value?: any) {
|
|
855
|
+
if (typeof key === 'string') {
|
|
856
|
+
this.props[key] = value;
|
|
857
|
+
} else {
|
|
858
|
+
this.props = { ...this.props, ...key };
|
|
859
|
+
}
|
|
860
|
+
},
|
|
861
|
+
};
|
|
862
|
+
const ctx: any = {
|
|
863
|
+
app: {
|
|
864
|
+
jsonLogic: {
|
|
865
|
+
apply: vi.fn(() => true),
|
|
866
|
+
},
|
|
867
|
+
},
|
|
868
|
+
model: {
|
|
869
|
+
context: { form },
|
|
870
|
+
subModels: {
|
|
871
|
+
grid: {
|
|
872
|
+
subModels: {
|
|
873
|
+
items: [fieldModel],
|
|
874
|
+
},
|
|
875
|
+
},
|
|
876
|
+
},
|
|
877
|
+
},
|
|
878
|
+
setFormValues,
|
|
879
|
+
getAction: (name: string) => (name === 'linkageSetFieldProps' ? linkageSetFieldProps : null),
|
|
880
|
+
resolveJsonTemplate: vi.fn(async (value) => value),
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
await fieldLinkageRules.handler(ctx, {
|
|
884
|
+
value: [
|
|
885
|
+
{
|
|
886
|
+
key: 'rule-1',
|
|
887
|
+
enable: true,
|
|
888
|
+
condition: { logic: '$and', items: [] },
|
|
889
|
+
actions: [
|
|
890
|
+
{
|
|
891
|
+
key: 'action-1',
|
|
892
|
+
name: 'linkageSetFieldProps',
|
|
893
|
+
params: {
|
|
894
|
+
value: {
|
|
895
|
+
fields: ['name-field'],
|
|
896
|
+
state: 'hiddenReservedValue',
|
|
897
|
+
},
|
|
898
|
+
},
|
|
899
|
+
},
|
|
900
|
+
],
|
|
901
|
+
},
|
|
902
|
+
],
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
expect(fieldModel.hidden).toBe(false);
|
|
906
|
+
expect(fieldModel.props.hidden).toBe(true);
|
|
907
|
+
expect(setFormValues).not.toHaveBeenCalled();
|
|
908
|
+
expect(form.setFieldValue).not.toHaveBeenCalled();
|
|
909
|
+
});
|
|
910
|
+
|
|
544
911
|
it('should keep all options selectable after options were limited once', async () => {
|
|
545
912
|
const fieldComponentModel: any = {
|
|
546
913
|
uid: 'status-field-component',
|
|
@@ -33,7 +33,14 @@ export const dataScope = defineAction({
|
|
|
33
33
|
return (
|
|
34
34
|
<FilterGroup
|
|
35
35
|
value={props.value}
|
|
36
|
-
FilterItem={(p) =>
|
|
36
|
+
FilterItem={(p) => (
|
|
37
|
+
<VariableFilterItem
|
|
38
|
+
{...p}
|
|
39
|
+
model={flowContext.model}
|
|
40
|
+
rightAsVariable
|
|
41
|
+
maxAssociationFieldDepth={flowContext.model instanceof FieldModel ? undefined : 2}
|
|
42
|
+
/>
|
|
43
|
+
)}
|
|
37
44
|
/>
|
|
38
45
|
);
|
|
39
46
|
},
|
|
@@ -1606,7 +1606,9 @@ const LinkageRulesUI = observer(
|
|
|
1606
1606
|
<div style={{ paddingLeft: 12 }}>
|
|
1607
1607
|
<FilterGroup
|
|
1608
1608
|
value={rule.condition}
|
|
1609
|
-
FilterItem={(props) =>
|
|
1609
|
+
FilterItem={(props) => (
|
|
1610
|
+
<LinkageFilterItem model={ctx.model} value={props.value} maxAssociationFieldDepth={2} />
|
|
1611
|
+
)}
|
|
1610
1612
|
/>
|
|
1611
1613
|
</div>
|
|
1612
1614
|
</div>
|
|
@@ -1766,6 +1768,7 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
1766
1768
|
const allModels: FlowModel[] = ctx.model.__allModels || (ctx.model.__allModels = []);
|
|
1767
1769
|
const modelsToApply = new Set<FlowModel>(allModels);
|
|
1768
1770
|
const patchPropsByModel = new Map<FlowModel, any>();
|
|
1771
|
+
const clearValueOnHiddenModelUids = new Set<string>();
|
|
1769
1772
|
const directValuePatches: Array<{ path: Array<string | number>; value: any; whenEmpty?: boolean }> = [];
|
|
1770
1773
|
const rootCollection = getCollectionFromModel((ctx.model as any)?.context?.blockModel ?? ctx.model);
|
|
1771
1774
|
const isSafeToWriteAssociationSubpath = (namePath: any): boolean => {
|
|
@@ -1876,6 +1879,16 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
1876
1879
|
...(whenEmpty ? { whenEmpty: true } : {}),
|
|
1877
1880
|
});
|
|
1878
1881
|
};
|
|
1882
|
+
const removePendingFormValuePatches = (path: any) => {
|
|
1883
|
+
const resolvedPath = resolveNamePathForPatch(path);
|
|
1884
|
+
if (!resolvedPath) return;
|
|
1885
|
+
const resolvedPathKey = namePathToPathKey(resolvedPath);
|
|
1886
|
+
for (let i = directValuePatches.length - 1; i >= 0; i--) {
|
|
1887
|
+
if (namePathToPathKey(directValuePatches[i].path) === resolvedPathKey) {
|
|
1888
|
+
directValuePatches.splice(i, 1);
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
};
|
|
1879
1892
|
|
|
1880
1893
|
const getModelTargetPathForPatch = (model: any): string | null => {
|
|
1881
1894
|
if (!model || typeof model !== 'object') return null;
|
|
@@ -1971,6 +1984,23 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
1971
1984
|
|
|
1972
1985
|
return out;
|
|
1973
1986
|
};
|
|
1987
|
+
const getModelTargetPathForHiddenClear = (model: any): string | Array<string | number> | null => {
|
|
1988
|
+
const fieldPathArray = normalizeNamePathForKey(model?.context?.fieldPathArray);
|
|
1989
|
+
const targetPath = getModelTargetPathForPatch(model);
|
|
1990
|
+
if (fieldPathArray) {
|
|
1991
|
+
const targetPathLastString = targetPath
|
|
1992
|
+
? ([...parsePathString(targetPath)].reverse().find((seg) => typeof seg === 'string') as string | undefined)
|
|
1993
|
+
: undefined;
|
|
1994
|
+
const fieldPathArrayLastString = [...fieldPathArray].reverse().find((seg) => typeof seg === 'string');
|
|
1995
|
+
if (!targetPathLastString || targetPathLastString === fieldPathArrayLastString) {
|
|
1996
|
+
return fieldPathArray;
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
if (!targetPath) return null;
|
|
2001
|
+
|
|
2002
|
+
return resolveIndexedRelativePath(targetPath, model?.context?.fieldIndex) || targetPath;
|
|
2003
|
+
};
|
|
1974
2004
|
const getModelTargetPathKeys = (model: any): Set<string> => {
|
|
1975
2005
|
const keys = new Set<string>();
|
|
1976
2006
|
const fieldPathArray = normalizeNamePathForKey(model?.context?.fieldPathArray);
|
|
@@ -2058,6 +2088,13 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
2058
2088
|
...props,
|
|
2059
2089
|
});
|
|
2060
2090
|
|
|
2091
|
+
if (
|
|
2092
|
+
(action.name === 'linkageSetFieldProps' || action.name === 'subFormLinkageSetFieldProps') &&
|
|
2093
|
+
props?.hiddenModel === true
|
|
2094
|
+
) {
|
|
2095
|
+
clearValueOnHiddenModelUids.add(model?.uid || String(model));
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2061
2098
|
if (allModels.indexOf(model) === -1) {
|
|
2062
2099
|
allModels.push(model);
|
|
2063
2100
|
}
|
|
@@ -2145,6 +2182,24 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
2145
2182
|
}
|
|
2146
2183
|
}
|
|
2147
2184
|
|
|
2185
|
+
if (
|
|
2186
|
+
clearValueOnHiddenModelUids.has(uid) &&
|
|
2187
|
+
Object.prototype.hasOwnProperty.call(patchProps, 'hiddenModel') &&
|
|
2188
|
+
patchProps.hiddenModel === true
|
|
2189
|
+
) {
|
|
2190
|
+
const targetPath = getModelTargetPathForHiddenClear(model);
|
|
2191
|
+
if (!targetPath) {
|
|
2192
|
+
console.warn('[linkageRules] Skip clearing hidden field value due to missing target path', {
|
|
2193
|
+
flowKey: ctx.flowKey,
|
|
2194
|
+
modelUid: ctx.model?.uid,
|
|
2195
|
+
targetUid: model?.uid,
|
|
2196
|
+
});
|
|
2197
|
+
} else {
|
|
2198
|
+
removePendingFormValuePatches(targetPath);
|
|
2199
|
+
addFormValuePatch({ path: targetPath, value: undefined });
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2148
2203
|
model.__props = null;
|
|
2149
2204
|
});
|
|
2150
2205
|
hiddenStatePatches.forEach(({ model, hidden }) => {
|
|
@@ -135,7 +135,7 @@ function SetTargetDataScope(props) {
|
|
|
135
135
|
return (
|
|
136
136
|
<FilterGroup
|
|
137
137
|
value={props.value}
|
|
138
|
-
FilterItem={(p) => <VariableFilterItem {...p} model={targetModel} rightAsVariable />}
|
|
138
|
+
FilterItem={(p) => <VariableFilterItem {...p} model={targetModel} rightAsVariable maxAssociationFieldDepth={2} />}
|
|
139
139
|
/>
|
|
140
140
|
);
|
|
141
141
|
}
|
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
FLOW_SETTINGS_PREFERENCE_STORAGE_KEY,
|
|
43
43
|
readFlowSettingsPreference,
|
|
44
44
|
} from './flowSettingsPreference';
|
|
45
|
+
import { useAppListRender } from './AppListRender';
|
|
45
46
|
|
|
46
47
|
const className1 = css`
|
|
47
48
|
height: var(--nb-header-height);
|
|
@@ -391,7 +392,8 @@ export const AdminLayoutComponent = observer((props: any) => {
|
|
|
391
392
|
);
|
|
392
393
|
const designable = !isMobileSider && preferredFlowSettingsEnabled;
|
|
393
394
|
const { styles } = useHeaderStyle();
|
|
394
|
-
const {
|
|
395
|
+
const { appList } = useApplications();
|
|
396
|
+
const appListRender = useAppListRender();
|
|
395
397
|
const flowSettingsSyncRef = useRef(0);
|
|
396
398
|
const desiredFlowSettingsEnabledRef = useRef(false);
|
|
397
399
|
const handleLayoutContentElementChange = useCallback(
|
|
@@ -601,6 +603,31 @@ export const AdminLayoutComponent = observer((props: any) => {
|
|
|
601
603
|
overflowedIndicatorPopupClassName: styles.headerPopup,
|
|
602
604
|
};
|
|
603
605
|
}, [styles.headerPopup]);
|
|
606
|
+
const appSwitcherStyle = useMemo(
|
|
607
|
+
() => css`
|
|
608
|
+
.ant-layout-header.ant-pro-layout-header,
|
|
609
|
+
.ant-pro-top-nav-header {
|
|
610
|
+
z-index: 101 !important;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.ant-pro-layout-apps-popover {
|
|
614
|
+
z-index: 2000 !important;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
.ant-pro-layout-apps-icon {
|
|
618
|
+
color: ${customToken.colorTextHeaderMenu || 'rgba(255, 255, 255, 0.85)'};
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
.ant-pro-layout-apps-icon:hover,
|
|
622
|
+
.ant-pro-layout-apps-icon-active {
|
|
623
|
+
color: ${customToken.colorTextHeaderMenuHover ||
|
|
624
|
+
customToken.colorTextHeaderMenu ||
|
|
625
|
+
'rgba(255, 255, 255, 0.85)'};
|
|
626
|
+
background: ${customToken.colorBgHeaderMenuHover || 'rgba(255, 255, 255, 0.12)'};
|
|
627
|
+
}
|
|
628
|
+
`,
|
|
629
|
+
[customToken.colorBgHeaderMenuHover, customToken.colorTextHeaderMenu, customToken.colorTextHeaderMenuHover],
|
|
630
|
+
);
|
|
604
631
|
|
|
605
632
|
const closeMobileMenu = useCallback(() => {
|
|
606
633
|
if (!isMobileSider) {
|
|
@@ -634,16 +661,17 @@ export const AdminLayoutComponent = observer((props: any) => {
|
|
|
634
661
|
{...props}
|
|
635
662
|
contentStyle={contentStyle}
|
|
636
663
|
siderWidth={customToken.siderWidth || 200}
|
|
637
|
-
className={resetStyle}
|
|
664
|
+
className={`${resetStyle} ${appSwitcherStyle}`}
|
|
638
665
|
location={location}
|
|
639
666
|
route={route}
|
|
640
667
|
actionsRender={props.actionsRender || actionsRender}
|
|
641
668
|
logo={
|
|
642
669
|
<div style={{ display: 'flex', alignItems: 'center' }}>
|
|
643
|
-
{AppsComponent && <AppsComponent />}
|
|
644
670
|
<NocoBaseLogo />
|
|
645
671
|
</div>
|
|
646
672
|
}
|
|
673
|
+
appList={appList}
|
|
674
|
+
appListRender={appListRender}
|
|
647
675
|
title={''}
|
|
648
676
|
layout="mix"
|
|
649
677
|
splitMenus
|
|
@@ -55,6 +55,15 @@ const insertPositionToMethod = {
|
|
|
55
55
|
afterEnd: 'insertAfter',
|
|
56
56
|
} as const;
|
|
57
57
|
|
|
58
|
+
const omitMenuRuntimeProps = (props: Record<string, unknown> = {}) => {
|
|
59
|
+
const persistableProps = { ...props };
|
|
60
|
+
delete persistableProps.item;
|
|
61
|
+
delete persistableProps.dom;
|
|
62
|
+
delete persistableProps.options;
|
|
63
|
+
delete persistableProps.renderType;
|
|
64
|
+
return persistableProps;
|
|
65
|
+
};
|
|
66
|
+
|
|
58
67
|
export class AdminLayoutMenuItemModel extends FlowModel<AdminLayoutMenuItemStructure> {
|
|
59
68
|
private creationPersisted = false;
|
|
60
69
|
private persistedStateHydrated = false;
|
|
@@ -261,6 +270,12 @@ export class AdminLayoutMenuItemModel extends FlowModel<AdminLayoutMenuItemStruc
|
|
|
261
270
|
return currentFlowCount > 0 || initialFlowCount > 0;
|
|
262
271
|
}
|
|
263
272
|
|
|
273
|
+
serialize(): ReturnType<FlowModel['serialize']> {
|
|
274
|
+
const data = super.serialize();
|
|
275
|
+
data.props = omitMenuRuntimeProps(data.props);
|
|
276
|
+
return data;
|
|
277
|
+
}
|
|
278
|
+
|
|
264
279
|
setHidden(value: boolean) {
|
|
265
280
|
const previous = this.hidden;
|
|
266
281
|
super.setHidden(value);
|
|
@@ -698,7 +713,6 @@ AdminLayoutMenuItemModel.registerFlow({
|
|
|
698
713
|
AdminLayoutMenuItemModel.registerFlow({
|
|
699
714
|
key: 'menuSettings',
|
|
700
715
|
title: 'Menu settings',
|
|
701
|
-
manual: true,
|
|
702
716
|
steps: {
|
|
703
717
|
edit: {
|
|
704
718
|
title: 'Edit',
|