@nocobase/client-v2 2.3.0-alpha.1 → 2.3.0-beta.10
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/APIClient.d.ts +12 -0
- package/es/components/form/ScanInput/useCodeScanner.d.ts +12 -2
- package/es/components/form/ScanInput/zxingWasmDecoder.d.ts +9 -0
- package/es/flow/actions/linkageRules.d.ts +25 -0
- package/es/flow/components/FieldAssignValueInput.d.ts +6 -30
- package/es/flow/components/FlowRoute.d.ts +2 -2
- package/es/flow/components/field-value-variable/DateVariableEditor.d.ts +24 -0
- package/es/flow/components/field-value-variable/FieldValueVariableInput.d.ts +32 -0
- package/es/flow/components/field-value-variable/dateValue.d.ts +36 -0
- package/es/flow/components/field-value-variable/index.d.ts +11 -0
- package/es/flow/components/filter/VariableFilterItem.d.ts +7 -0
- package/es/flow/components/placeholders/BlockPlaceholder.d.ts +1 -0
- package/es/flow/internal/registerDeviceTypeContext.d.ts +10 -0
- package/es/flow/internal/utils/operatorSchemaHelper.d.ts +2 -2
- package/es/flow/models/actions/UpdateRecordActionUtils.d.ts +8 -2
- package/es/flow/models/base/ActionModelCore.d.ts +4 -2
- package/es/flow/models/base/BlockGridModel.d.ts +3 -0
- package/es/flow/models/base/CollectionBlockModel.d.ts +3 -0
- package/es/flow/models/blocks/details/DetailsBlockModel.d.ts +3 -0
- package/es/flow/models/blocks/filter-form/FilterFormGridModel.d.ts +1 -0
- package/es/flow/models/blocks/filter-form/FilterFormSubmitActionModel.d.ts +3 -1
- package/es/flow/models/blocks/form/FormBlockModel.d.ts +7 -0
- package/es/flow/models/blocks/form/FormGridModel.d.ts +6 -0
- package/es/flow/models/blocks/form/value-runtime/rules.d.ts +1 -0
- package/es/flow/models/blocks/table/JSColumnModel.d.ts +2 -0
- package/es/flow/models/blocks/table/TableBlockModel.d.ts +1 -0
- package/es/flow/models/blocks/table/TableColumnModel.d.ts +12 -0
- package/es/flow/models/blocks/table/utils.d.ts +1 -0
- package/es/flow/models/fields/AssociationFieldModel/RecordPickerFieldModel.d.ts +2 -1
- package/es/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.d.ts +0 -17
- package/es/flow/models/fields/AssociationFieldModel/recordSelectShared.d.ts +41 -0
- package/es/flow/models/fields/DateTimeFieldModel/dateLimit.d.ts +15 -7
- package/es/flow-compat/passwordUtils.d.ts +1 -1
- package/es/index.d.ts +1 -0
- package/es/index.mjs +148 -145
- package/es/ui-operation/index.d.ts +15 -0
- package/es/ui-operation/ui-operation-codec.d.ts +10 -0
- package/lib/index.js +170 -167
- package/lib/locale/languageCodes.js +1 -0
- package/package.json +10 -8
- package/src/APIClient.ts +92 -0
- package/src/Application.tsx +3 -1
- package/src/__tests__/APIClient.test.tsx +58 -0
- package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +7 -2
- package/src/__tests__/settings-center.test.tsx +270 -6
- package/src/acl/ACLProvider.tsx +2 -0
- package/src/acl/__tests__/ACLProvider.test.tsx +92 -0
- package/src/collection-manager/__tests__/field-configure.test.ts +52 -0
- package/src/collection-manager/field-configure.ts +2 -2
- package/src/components/form/ScanInput/CodeScanner.tsx +64 -35
- package/src/components/form/ScanInput/__tests__/CodeScanner.test.tsx +107 -0
- package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +294 -24
- package/src/components/form/ScanInput/__tests__/zxingWasmDecoder.test.ts +78 -0
- package/src/components/form/ScanInput/useCodeScanner.ts +281 -23
- package/src/components/form/ScanInput/zxingWasmDecoder.ts +64 -0
- package/src/components/form/filter/CollectionFilterItem.tsx +4 -2
- package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +17 -0
- package/src/components/form/table/Table.tsx +81 -6
- package/src/components/form/table/__tests__/Table.columnWidth.test.tsx +433 -0
- package/src/flow/__tests__/FlowRoute.test.tsx +126 -8
- package/src/flow/__tests__/PluginFlowEngine.test.ts +58 -0
- package/src/flow/actions/__tests__/actionLinkageRules.forkProps.test.ts +314 -0
- package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +102 -0
- package/src/flow/actions/__tests__/linkageRules.actionStates.test.ts +33 -0
- package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +72 -0
- package/src/flow/actions/linkageRules.tsx +59 -16
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +78 -59
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +228 -0
- package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +42 -23
- package/src/flow/common/Markdown/style.ts +4 -0
- package/src/flow/components/BlockItemCard.tsx +2 -2
- package/src/flow/components/FieldAssignValueInput.tsx +42 -621
- package/src/flow/components/FlowRoute.tsx +71 -29
- package/src/flow/components/field-value-variable/DateVariableEditor.tsx +181 -0
- package/src/flow/components/field-value-variable/FieldValueVariableInput.tsx +335 -0
- package/src/flow/components/field-value-variable/__tests__/FieldValueVariableInput.test.tsx +389 -0
- package/src/flow/components/field-value-variable/dateValue.ts +223 -0
- package/src/flow/components/field-value-variable/index.ts +12 -0
- package/src/flow/components/filter/VariableFilterItem.tsx +23 -6
- package/src/flow/components/filter/__tests__/VariableFilterItem.rightMetaTree.test.tsx +158 -0
- package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +125 -0
- package/src/flow/components/placeholders/BlockPlaceholder.tsx +70 -23
- package/src/flow/components/placeholders/__tests__/BlockPlaceholder.test.tsx +57 -7
- package/src/flow/index.ts +2 -0
- package/src/flow/internal/components/Markdown/DisplayMarkdown.tsx +14 -9
- package/src/flow/internal/components/Markdown/__tests__/DisplayMarkdown.test.tsx +46 -0
- package/src/flow/internal/registerDeviceTypeContext.ts +39 -0
- package/src/flow/internal/utils/operatorSchemaHelper.ts +3 -2
- package/src/flow/models/actions/UpdateRecordActionModel.tsx +18 -1
- package/src/flow/models/actions/UpdateRecordActionUtils.ts +18 -6
- package/src/flow/models/actions/__tests__/UpdateRecordActionModel.test.ts +76 -4
- package/src/flow/models/base/ActionModelCore.tsx +11 -2
- package/src/flow/models/base/BlockGridModel.tsx +26 -0
- package/src/flow/models/base/CollectionBlockModel.tsx +38 -3
- package/src/flow/models/base/GridModel.tsx +0 -1
- package/src/flow/models/base/PageModel/PageTabModel.tsx +67 -17
- package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +509 -12
- package/src/flow/models/base/__tests__/ActionModelCore.render.test.tsx +49 -0
- package/src/flow/models/base/__tests__/BlockGridModel.selectSceneActivation.test.ts +124 -0
- package/src/flow/models/base/__tests__/CollectionBlockModel.addAppends.test.ts +41 -0
- package/src/flow/models/base/__tests__/CollectionBlockModel.initialBeforeRenderRefresh.test.ts +125 -9
- package/src/flow/models/blocks/assign-form/AssignFormGridModel.tsx +22 -1
- package/src/flow/models/blocks/assign-form/AssignFormItemModel.tsx +36 -50
- package/src/flow/models/blocks/assign-form/__tests__/assignFieldValuesFlow.editor.test.tsx +140 -0
- package/src/flow/models/blocks/details/DetailsBlockModel.tsx +8 -3
- package/src/flow/models/blocks/details/__tests__/DetailsBlockModel.initialPaginationChangeRefresh.test.ts +32 -1
- package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +27 -3
- package/src/flow/models/blocks/filter-form/FilterFormGridModel.tsx +36 -5
- package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +129 -14
- package/src/flow/models/blocks/filter-form/FilterFormSubmitActionModel.tsx +57 -4
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormGridModel.onModelCreated.test.ts +174 -1
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormGridModel.toggleFormFieldsCollapse.test.ts +29 -0
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +359 -1
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.getFilterValue.test.ts +72 -0
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormSubmitActionModel.lifecycle.test.ts +90 -0
- package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +32 -4
- package/src/flow/models/blocks/filter-form/fields/FieldComponentProps.tsx +1 -1
- package/src/flow/models/blocks/filter-form/fields/__tests__/FieldComponentProps.options.test.tsx +61 -0
- package/src/flow/models/blocks/filter-manager/FilterManager.ts +5 -0
- package/src/flow/models/blocks/filter-manager/__tests__/FilterManager.test.ts +40 -0
- package/src/flow/models/blocks/form/FormBlockModel.tsx +73 -5
- package/src/flow/models/blocks/form/FormGridModel.tsx +4 -0
- package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +153 -2
- package/src/flow/models/blocks/form/__tests__/popupLinkage.test.tsx +175 -0
- package/src/flow/models/blocks/form/__tests__/runJsFormSubmit.test.ts +131 -0
- package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +435 -0
- package/src/flow/models/blocks/form/value-runtime/rules.ts +67 -14
- package/src/flow/models/blocks/form/value-runtime/runtime.ts +43 -19
- package/src/flow/models/blocks/js-block/JSBlock.tsx +1 -1
- package/src/flow/models/blocks/table/JSColumnModel.tsx +10 -1
- package/src/flow/models/blocks/table/TableBlockModel.tsx +28 -2
- package/src/flow/models/blocks/table/TableColumnModel.tsx +48 -9
- package/src/flow/models/blocks/table/__tests__/JSColumnModel.test.tsx +52 -5
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.filterReset.test.ts +78 -0
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.quickEditRefresh.test.ts +12 -0
- package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +33 -0
- package/src/flow/models/blocks/table/utils.ts +7 -0
- package/src/flow/models/fields/AssociationFieldModel/CascadeSelectFieldModel.tsx +33 -1
- package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +8 -0
- package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/__tests__/popupContext.test.ts +120 -0
- package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/actions/PopupSubTableEditActionModel.tsx +10 -2
- package/src/flow/models/fields/AssociationFieldModel/RecordPickerFieldModel.tsx +29 -3
- package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +9 -136
- package/src/flow/models/fields/AssociationFieldModel/__tests__/RecordPickerFieldModel.itemContext.test.ts +191 -3
- package/src/flow/models/fields/AssociationFieldModel/recordSelectShared.tsx +156 -0
- package/src/flow/models/fields/DateTimeFieldModel/__tests__/DateTimeNoTzFieldModel.dateLimit.test.tsx +149 -0
- package/src/flow/models/fields/DateTimeFieldModel/dateLimit.ts +149 -113
- package/src/flow/models/fields/DisplayAssociationField/DisplaySubTableFieldModel.tsx +42 -7
- package/src/flow/models/fields/DisplayAssociationField/__tests__/DisplaySubTableFieldModel.test.tsx +351 -0
- package/src/flow/models/fields/DisplayHtmlFieldModel.tsx +2 -1
- package/src/flow/models/fields/DisplayNumberFieldModel.tsx +1 -1
- package/src/flow/models/fields/JsonFieldModel.tsx +31 -8
- package/src/flow/models/fields/NumberFieldModel.tsx +1 -1
- package/src/flow/models/fields/RichTextFieldModel/__tests__/RichTextFieldModel.test.tsx +74 -0
- package/src/flow/models/fields/RichTextFieldModel/index.tsx +27 -6
- package/src/flow/models/fields/__tests__/DisplayNumberFieldModel.test.ts +33 -0
- package/src/flow/models/fields/__tests__/JsonFieldModel.test.ts +82 -0
- package/src/flow/models/fields/__tests__/NumberFieldModel.test.tsx +47 -0
- package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +6 -0
- package/src/flow/models/fields/mobile-components/MobileSelect.tsx +27 -2
- package/src/flow/models/fields/mobile-components/__tests__/MobileSelect.test.tsx +139 -13
- package/src/flow/models/topbar/TopbarActionModel.tsx +5 -5
- package/src/flow/utils/dataScopeFormValueClear.ts +4 -3
- package/src/index.ts +1 -0
- package/src/layout-manager/LayoutContentRoute.tsx +2 -1
- package/src/layout-manager/LayoutRoute.tsx +2 -1
- package/src/layout-manager/__tests__/LayoutRoute.test.tsx +87 -1
- package/src/locale/languageCodes.ts +1 -0
- package/src/settings-center/AdminSettingsLayout.tsx +23 -2
- package/src/ui-operation/index.ts +33 -0
- package/src/ui-operation/ui-operation-codec.ts +54 -0
|
@@ -0,0 +1,314 @@
|
|
|
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 { FlowEngine } from '@nocobase/flow-engine';
|
|
11
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
12
|
+
import { ActionModel as ConfiguredActionModel } from '../../models/base/ActionModel';
|
|
13
|
+
import { ActionModel } from '../../models/base/ActionModelCore';
|
|
14
|
+
import { actionLinkageRules, linkageSetActionProps, updateLinkageRules } from '../linkageRules';
|
|
15
|
+
|
|
16
|
+
class TestActionModel extends ActionModel {}
|
|
17
|
+
|
|
18
|
+
describe('action linkage rules on action forks', () => {
|
|
19
|
+
it('does not snapshot unrelated master props when disabling an action', async () => {
|
|
20
|
+
const engine = new FlowEngine();
|
|
21
|
+
engine.registerModels({ TestActionModel });
|
|
22
|
+
const master = engine.createModel<TestActionModel>({
|
|
23
|
+
use: 'TestActionModel',
|
|
24
|
+
props: { title: 'Edit' },
|
|
25
|
+
});
|
|
26
|
+
const fork = master.createFork({ className: 'row-action' });
|
|
27
|
+
|
|
28
|
+
const ctx = {
|
|
29
|
+
flowKey: 'buttonSettings',
|
|
30
|
+
model: fork,
|
|
31
|
+
app: {
|
|
32
|
+
jsonLogic: {
|
|
33
|
+
apply: vi.fn(() => true),
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
t: (value: string) => value,
|
|
37
|
+
resolveJsonTemplate: vi.fn(async (value: unknown) => value),
|
|
38
|
+
getAction: (name: string) => {
|
|
39
|
+
if (name !== 'linkageSetActionProps') return undefined;
|
|
40
|
+
return {
|
|
41
|
+
handler: async (_ctx: unknown, params: { setProps: Function }) => {
|
|
42
|
+
params.setProps(fork, { disabled: true });
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
} as never;
|
|
47
|
+
|
|
48
|
+
await actionLinkageRules.handler(ctx, {
|
|
49
|
+
value: [
|
|
50
|
+
{
|
|
51
|
+
key: 'disable-edit',
|
|
52
|
+
enable: true,
|
|
53
|
+
condition: { logic: '$and', items: [] },
|
|
54
|
+
actions: [{ name: 'linkageSetActionProps', params: { value: 'disabled' } }],
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
expect(fork.localProps.title).toBeUndefined();
|
|
60
|
+
expect(fork.__originalProps.title).toBeUndefined();
|
|
61
|
+
expect(fork.localProps.disabled).toBe(true);
|
|
62
|
+
|
|
63
|
+
master.setProps('title', 'Updated');
|
|
64
|
+
await actionLinkageRules.handler(ctx, {
|
|
65
|
+
value: [
|
|
66
|
+
{
|
|
67
|
+
key: 'disable-edit',
|
|
68
|
+
enable: true,
|
|
69
|
+
condition: { logic: '$and', items: [] },
|
|
70
|
+
actions: [{ name: 'linkageSetActionProps', params: { value: 'disabled' } }],
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
expect(fork.getProps().title).toBe('Updated');
|
|
76
|
+
expect(fork.serialize().props).toMatchObject({ title: 'Updated' });
|
|
77
|
+
expect(fork.serialize().props).not.toHaveProperty('disabled');
|
|
78
|
+
|
|
79
|
+
const refreshedFork = master.createFork({ className: 'row-action' });
|
|
80
|
+
expect(refreshedFork.getProps().title).toBe('Updated');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it.each(['disable', 'delete'] as const)(
|
|
84
|
+
'restores a row action when linkage rules are %sd without persisting disabled',
|
|
85
|
+
async (change) => {
|
|
86
|
+
const engine = new FlowEngine();
|
|
87
|
+
engine.registerModels({ ConfiguredActionModel });
|
|
88
|
+
engine.registerActions({ actionLinkageRules, linkageSetActionProps });
|
|
89
|
+
|
|
90
|
+
let savedData: ReturnType<ConfiguredActionModel['serialize']> | undefined;
|
|
91
|
+
engine.setModelRepository({
|
|
92
|
+
save: async (model) => {
|
|
93
|
+
savedData = model.serialize();
|
|
94
|
+
return savedData;
|
|
95
|
+
},
|
|
96
|
+
} as Parameters<FlowEngine['setModelRepository']>[0]);
|
|
97
|
+
|
|
98
|
+
const enabledRules = [
|
|
99
|
+
{
|
|
100
|
+
key: 'disable-edit',
|
|
101
|
+
enable: true,
|
|
102
|
+
condition: { logic: '$and', items: [] },
|
|
103
|
+
actions: [{ name: 'linkageSetActionProps', params: { value: 'disabled' } }],
|
|
104
|
+
},
|
|
105
|
+
];
|
|
106
|
+
const master = engine.createModel<ConfiguredActionModel>({
|
|
107
|
+
use: 'ConfiguredActionModel',
|
|
108
|
+
props: { title: 'Edit' },
|
|
109
|
+
stepParams: {
|
|
110
|
+
buttonSettings: {
|
|
111
|
+
linkageRules: { value: enabledRules },
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
const fork = master.createFork({ className: 'row-action' });
|
|
116
|
+
|
|
117
|
+
await fork.dispatchEvent('beforeRender', undefined, { useCache: false });
|
|
118
|
+
expect(fork.getProps().disabled).toBe(true);
|
|
119
|
+
|
|
120
|
+
const nextRules =
|
|
121
|
+
change === 'disable'
|
|
122
|
+
? updateLinkageRules(enabledRules, (rules) => {
|
|
123
|
+
rules[0].enable = false;
|
|
124
|
+
})
|
|
125
|
+
: [];
|
|
126
|
+
fork.setStepParams('buttonSettings', 'linkageRules', { value: nextRules });
|
|
127
|
+
await fork.saveStepParams();
|
|
128
|
+
await fork.rerender();
|
|
129
|
+
|
|
130
|
+
expect(savedData).toBeDefined();
|
|
131
|
+
expect(savedData?.props).not.toHaveProperty('disabled');
|
|
132
|
+
expect(savedData?.stepParams.buttonSettings.linkageRules.value).toEqual(nextRules);
|
|
133
|
+
expect(fork.getProps().disabled).toBeUndefined();
|
|
134
|
+
|
|
135
|
+
const reloadedEngine = new FlowEngine();
|
|
136
|
+
reloadedEngine.registerModels({ ConfiguredActionModel });
|
|
137
|
+
reloadedEngine.registerActions({ actionLinkageRules, linkageSetActionProps });
|
|
138
|
+
const reloadedMaster = reloadedEngine.createModel<ConfiguredActionModel>({
|
|
139
|
+
use: 'ConfiguredActionModel',
|
|
140
|
+
props: savedData?.props,
|
|
141
|
+
stepParams: savedData?.stepParams,
|
|
142
|
+
});
|
|
143
|
+
const reloadedFork = reloadedMaster.createFork({ className: 'row-action' });
|
|
144
|
+
|
|
145
|
+
await reloadedFork.dispatchEvent('beforeRender', undefined, { useCache: false });
|
|
146
|
+
expect(reloadedFork.getProps().disabled).toBeUndefined();
|
|
147
|
+
},
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
it.each(['disabled', 'deleted'] as const)(
|
|
151
|
+
'ignores a historical disabled prop after the linkage rule is %s',
|
|
152
|
+
async (state) => {
|
|
153
|
+
const engine = new FlowEngine();
|
|
154
|
+
engine.registerModels({ ConfiguredActionModel });
|
|
155
|
+
engine.registerActions({ actionLinkageRules, linkageSetActionProps });
|
|
156
|
+
const rules =
|
|
157
|
+
state === 'disabled'
|
|
158
|
+
? [
|
|
159
|
+
{
|
|
160
|
+
key: 'disable-edit',
|
|
161
|
+
enable: false,
|
|
162
|
+
condition: { logic: '$and', items: [] },
|
|
163
|
+
actions: [{ name: 'linkageSetActionProps', params: { value: 'disabled' } }],
|
|
164
|
+
},
|
|
165
|
+
]
|
|
166
|
+
: [];
|
|
167
|
+
const master = engine.createModel<ConfiguredActionModel>({
|
|
168
|
+
use: 'ConfiguredActionModel',
|
|
169
|
+
props: { title: 'Edit', disabled: true },
|
|
170
|
+
stepParams: {
|
|
171
|
+
buttonSettings: {
|
|
172
|
+
linkageRules: { value: rules },
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
const fork = master.createFork({ className: 'row-action' });
|
|
177
|
+
|
|
178
|
+
expect(master.getProps()).not.toHaveProperty('disabled');
|
|
179
|
+
await fork.dispatchEvent('beforeRender', undefined, { useCache: false });
|
|
180
|
+
|
|
181
|
+
expect(fork.getProps().disabled).toBeUndefined();
|
|
182
|
+
},
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
it('still applies an enabled linkage rule after dropping a historical disabled prop', async () => {
|
|
186
|
+
const engine = new FlowEngine();
|
|
187
|
+
engine.registerModels({ ConfiguredActionModel });
|
|
188
|
+
engine.registerActions({ actionLinkageRules, linkageSetActionProps });
|
|
189
|
+
const master = engine.createModel<ConfiguredActionModel>({
|
|
190
|
+
use: 'ConfiguredActionModel',
|
|
191
|
+
props: { title: 'Edit', disabled: true },
|
|
192
|
+
stepParams: {
|
|
193
|
+
buttonSettings: {
|
|
194
|
+
linkageRules: {
|
|
195
|
+
value: [
|
|
196
|
+
{
|
|
197
|
+
key: 'disable-edit',
|
|
198
|
+
enable: true,
|
|
199
|
+
condition: { logic: '$and', items: [] },
|
|
200
|
+
actions: [{ name: 'linkageSetActionProps', params: { value: 'disabled' } }],
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
const fork = master.createFork({ className: 'row-action' });
|
|
208
|
+
|
|
209
|
+
expect(master.getProps()).not.toHaveProperty('disabled');
|
|
210
|
+
await fork.dispatchEvent('beforeRender', undefined, { useCache: false });
|
|
211
|
+
|
|
212
|
+
expect(fork.getProps().disabled).toBe(true);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('preserves a default disabled state as runtime-only state', async () => {
|
|
216
|
+
class DefaultDisabledActionModel extends ConfiguredActionModel {
|
|
217
|
+
defaultProps = { ...this.defaultProps, disabled: true };
|
|
218
|
+
}
|
|
219
|
+
const engine = new FlowEngine();
|
|
220
|
+
engine.registerModels({ DefaultDisabledActionModel });
|
|
221
|
+
engine.registerActions({ actionLinkageRules, linkageSetActionProps });
|
|
222
|
+
const master = engine.createModel<DefaultDisabledActionModel>({
|
|
223
|
+
use: 'DefaultDisabledActionModel',
|
|
224
|
+
props: { title: 'Edit' },
|
|
225
|
+
stepParams: {
|
|
226
|
+
buttonSettings: {
|
|
227
|
+
linkageRules: { value: [] },
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
const fork = master.createFork({ className: 'row-action' });
|
|
232
|
+
|
|
233
|
+
await fork.dispatchEvent('beforeRender', undefined, { useCache: false });
|
|
234
|
+
|
|
235
|
+
expect(fork.getProps().disabled).toBe(true);
|
|
236
|
+
expect(fork.serialize().props).not.toHaveProperty('disabled');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('updates a disabled row action title through the real beforeRender flow', async () => {
|
|
240
|
+
const engine = new FlowEngine();
|
|
241
|
+
engine.registerModels({ ConfiguredActionModel });
|
|
242
|
+
engine.registerActions({ actionLinkageRules, linkageSetActionProps });
|
|
243
|
+
let savedData: ReturnType<ConfiguredActionModel['serialize']> | undefined;
|
|
244
|
+
engine.setModelRepository({
|
|
245
|
+
save: async (model) => {
|
|
246
|
+
savedData = model.serialize();
|
|
247
|
+
return savedData;
|
|
248
|
+
},
|
|
249
|
+
} as Parameters<FlowEngine['setModelRepository']>[0]);
|
|
250
|
+
const master = engine.createModel<ConfiguredActionModel>({
|
|
251
|
+
use: 'ConfiguredActionModel',
|
|
252
|
+
props: { title: 'Edit' },
|
|
253
|
+
stepParams: {
|
|
254
|
+
buttonSettings: {
|
|
255
|
+
general: { title: 'Edit' },
|
|
256
|
+
linkageRules: {
|
|
257
|
+
value: [
|
|
258
|
+
{
|
|
259
|
+
key: 'disable-edit',
|
|
260
|
+
enable: true,
|
|
261
|
+
condition: { logic: '$and', items: [] },
|
|
262
|
+
actions: [{ name: 'linkageSetActionProps', params: { value: 'disabled' } }],
|
|
263
|
+
},
|
|
264
|
+
],
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
const fork = master.createFork({ className: 'row-action' });
|
|
270
|
+
|
|
271
|
+
await fork.dispatchEvent('beforeRender', undefined, { useCache: false });
|
|
272
|
+
expect(fork.getProps()).toMatchObject({ title: 'Edit', disabled: true });
|
|
273
|
+
|
|
274
|
+
fork.setStepParams('buttonSettings', 'general', { title: 'Updated' });
|
|
275
|
+
await fork.saveStepParams();
|
|
276
|
+
await fork.rerender();
|
|
277
|
+
|
|
278
|
+
expect(savedData?.props).not.toHaveProperty('disabled');
|
|
279
|
+
expect(savedData?.stepParams.buttonSettings.general.title).toBe('Updated');
|
|
280
|
+
expect(fork.getProps()).toMatchObject({ title: 'Updated', disabled: true });
|
|
281
|
+
|
|
282
|
+
const reloadedEngine = new FlowEngine();
|
|
283
|
+
reloadedEngine.registerModels({ ConfiguredActionModel });
|
|
284
|
+
reloadedEngine.registerActions({ actionLinkageRules, linkageSetActionProps });
|
|
285
|
+
const reloadedMaster = reloadedEngine.createModel<ConfiguredActionModel>({
|
|
286
|
+
use: 'ConfiguredActionModel',
|
|
287
|
+
props: savedData?.props,
|
|
288
|
+
stepParams: savedData?.stepParams,
|
|
289
|
+
});
|
|
290
|
+
const reloadedFork = reloadedMaster.createFork({ className: 'row-action' });
|
|
291
|
+
|
|
292
|
+
await reloadedFork.dispatchEvent('beforeRender', undefined, { useCache: false });
|
|
293
|
+
expect(reloadedFork.getProps()).toMatchObject({ title: 'Updated', disabled: true });
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('creates a new rules value when disabling a linkage rule', () => {
|
|
297
|
+
const rules = [
|
|
298
|
+
{
|
|
299
|
+
key: 'disable-edit',
|
|
300
|
+
title: 'Linkage rule',
|
|
301
|
+
enable: true,
|
|
302
|
+
condition: { logic: '$and', items: [] },
|
|
303
|
+
actions: [],
|
|
304
|
+
},
|
|
305
|
+
];
|
|
306
|
+
const nextRules = updateLinkageRules(rules, (next) => {
|
|
307
|
+
next[0].enable = false;
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
expect(nextRules).toEqual([expect.objectContaining({ key: 'disable-edit', enable: false })]);
|
|
311
|
+
expect(nextRules).not.toBe(rules);
|
|
312
|
+
expect(rules[0].enable).toBe(true);
|
|
313
|
+
});
|
|
314
|
+
});
|
|
@@ -225,6 +225,108 @@ describe('ensureFormValueDrivenDataScopeClear', () => {
|
|
|
225
225
|
expect(onChange).toHaveBeenCalledWith(null);
|
|
226
226
|
});
|
|
227
227
|
|
|
228
|
+
it('does not clear a popup form field when data scope depends on the external parent item', () => {
|
|
229
|
+
const emitter = new EventEmitter();
|
|
230
|
+
const formBlock = {
|
|
231
|
+
uid: 'form-1',
|
|
232
|
+
disposed: false,
|
|
233
|
+
emitter,
|
|
234
|
+
context: {
|
|
235
|
+
form: {},
|
|
236
|
+
formValues: { staff_m2o: null },
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const onChange = vi.fn();
|
|
241
|
+
const model: any = {
|
|
242
|
+
disposed: false,
|
|
243
|
+
props: {
|
|
244
|
+
value: { id: 10 },
|
|
245
|
+
onChange,
|
|
246
|
+
},
|
|
247
|
+
context: {
|
|
248
|
+
blockModel: formBlock,
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const ctx: any = {
|
|
253
|
+
model,
|
|
254
|
+
flowKey: 'selectSettings',
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const filter = {
|
|
258
|
+
logic: '$and',
|
|
259
|
+
items: [{ path: 'orgId', operator: '$eq', value: '{{ ctx.item.parentItem.value.org_m2o.id }}' }],
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
ensureFormValueDrivenDataScopeClear(ctx, filter);
|
|
263
|
+
|
|
264
|
+
emitter.emit('formValuesChange', {
|
|
265
|
+
changedPaths: [['staff_m2o']],
|
|
266
|
+
allValues: { staff_m2o: { id: 10 } },
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('maps popup current item dependencies to the popup form root', () => {
|
|
273
|
+
const emitter = new EventEmitter();
|
|
274
|
+
const formBlock = {
|
|
275
|
+
uid: 'form-1',
|
|
276
|
+
disposed: false,
|
|
277
|
+
emitter,
|
|
278
|
+
context: {
|
|
279
|
+
form: {},
|
|
280
|
+
formValues: {
|
|
281
|
+
org_m2o: { id: 1 },
|
|
282
|
+
staff_m2o: { id: 10 },
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const onChange = vi.fn();
|
|
288
|
+
const model: any = {
|
|
289
|
+
disposed: false,
|
|
290
|
+
props: {
|
|
291
|
+
value: { id: 10 },
|
|
292
|
+
onChange,
|
|
293
|
+
},
|
|
294
|
+
context: {
|
|
295
|
+
blockModel: formBlock,
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const ctx: any = {
|
|
300
|
+
model,
|
|
301
|
+
flowKey: 'selectSettings',
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
const filter = {
|
|
305
|
+
logic: '$and',
|
|
306
|
+
items: [{ path: 'orgId', operator: '$eq', value: '{{ ctx.item.value.org_m2o.id }}' }],
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
ensureFormValueDrivenDataScopeClear(ctx, filter);
|
|
310
|
+
|
|
311
|
+
emitter.emit('formValuesChange', {
|
|
312
|
+
changedPaths: [['staff_m2o']],
|
|
313
|
+
allValues: {
|
|
314
|
+
org_m2o: { id: 1 },
|
|
315
|
+
staff_m2o: { id: 10 },
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
319
|
+
|
|
320
|
+
emitter.emit('formValuesChange', {
|
|
321
|
+
changedPaths: [['org_m2o']],
|
|
322
|
+
allValues: {
|
|
323
|
+
org_m2o: { id: 2 },
|
|
324
|
+
staff_m2o: { id: 10 },
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
expect(onChange).toHaveBeenCalledWith(null);
|
|
328
|
+
});
|
|
329
|
+
|
|
228
330
|
it('does not clear a row field when a sibling field changes but the item dependency value is unchanged', () => {
|
|
229
331
|
const emitter = new EventEmitter();
|
|
230
332
|
const formBlock = {
|
|
@@ -0,0 +1,33 @@
|
|
|
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 { getActionLinkageStateOptions } from '../linkageRules';
|
|
12
|
+
|
|
13
|
+
describe('getActionLinkageStateOptions', () => {
|
|
14
|
+
const t = (key: string) => key;
|
|
15
|
+
|
|
16
|
+
it('returns all states for regular action models', () => {
|
|
17
|
+
expect(getActionLinkageStateOptions({}, t).map((option) => option.value)).toEqual([
|
|
18
|
+
'visible',
|
|
19
|
+
'hidden',
|
|
20
|
+
'hiddenText',
|
|
21
|
+
'enabled',
|
|
22
|
+
'disabled',
|
|
23
|
+
]);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('filters states declared unsupported by the action model', () => {
|
|
27
|
+
expect(
|
|
28
|
+
getActionLinkageStateOptions({ supportedActionLinkageStates: ['visible', 'hidden'] }, t).map(
|
|
29
|
+
(option) => option.value,
|
|
30
|
+
),
|
|
31
|
+
).toEqual(['visible', 'hidden']);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
subFormFieldLinkageRules,
|
|
19
19
|
subFormLinkageSetFieldProps,
|
|
20
20
|
} from '../linkageRules';
|
|
21
|
+
import { CreateFormModel, EditFormModel, FormGridModel, FormItemModel, JSItemModel } from '../../models';
|
|
21
22
|
|
|
22
23
|
const createSubFormFieldModel = ({
|
|
23
24
|
uid,
|
|
@@ -1056,6 +1057,77 @@ describe('linkageSetFieldProps action', () => {
|
|
|
1056
1057
|
expect(await screen.findByText('Published')).toBeTruthy();
|
|
1057
1058
|
});
|
|
1058
1059
|
|
|
1060
|
+
it.each([
|
|
1061
|
+
['create form', CreateFormModel, linkageSetFieldProps, false],
|
|
1062
|
+
['edit form', EditFormModel, linkageSetFieldProps, false],
|
|
1063
|
+
['to-one subform', FlowModel, subFormLinkageSetFieldProps, false],
|
|
1064
|
+
['to-many subform row', FlowModel, subFormLinkageSetFieldProps, true],
|
|
1065
|
+
])('should exclude JS items from %s field state options', async (_, ModelClass, action, useGridFork) => {
|
|
1066
|
+
const engine = new FlowEngine();
|
|
1067
|
+
engine.registerModels({
|
|
1068
|
+
CreateFormModel,
|
|
1069
|
+
EditFormModel,
|
|
1070
|
+
FormGridModel,
|
|
1071
|
+
FormItemModel,
|
|
1072
|
+
JSItemModel,
|
|
1073
|
+
});
|
|
1074
|
+
const model = new ModelClass({
|
|
1075
|
+
flowEngine: engine,
|
|
1076
|
+
uid: 'form-model',
|
|
1077
|
+
use: ModelClass.name,
|
|
1078
|
+
}) as FlowModel;
|
|
1079
|
+
const grid = engine.createModel<FormGridModel>({
|
|
1080
|
+
use: 'FormGridModel',
|
|
1081
|
+
uid: 'form-grid',
|
|
1082
|
+
});
|
|
1083
|
+
const fieldModel = engine.createModel<FormItemModel>({
|
|
1084
|
+
use: 'FormItemModel',
|
|
1085
|
+
uid: 'name-field',
|
|
1086
|
+
props: { label: 'Name' },
|
|
1087
|
+
subModels: {
|
|
1088
|
+
field: {
|
|
1089
|
+
use: 'FlowModel',
|
|
1090
|
+
uid: 'name-field-component',
|
|
1091
|
+
},
|
|
1092
|
+
},
|
|
1093
|
+
});
|
|
1094
|
+
const jsItemModel = engine.createModel<JSItemModel>({
|
|
1095
|
+
use: 'JSItemModel',
|
|
1096
|
+
uid: 'js-item',
|
|
1097
|
+
props: { label: 'JS item' },
|
|
1098
|
+
});
|
|
1099
|
+
Object.assign(model.subModels, {
|
|
1100
|
+
grid: useGridFork
|
|
1101
|
+
? grid.createFork({}, 'row-0')
|
|
1102
|
+
: Object.assign(grid, {
|
|
1103
|
+
subModels: {
|
|
1104
|
+
...grid.subModels,
|
|
1105
|
+
items: [fieldModel, jsItemModel],
|
|
1106
|
+
},
|
|
1107
|
+
}),
|
|
1108
|
+
});
|
|
1109
|
+
Object.assign(model.subModels.grid.subModels, {
|
|
1110
|
+
items: [fieldModel, jsItemModel],
|
|
1111
|
+
});
|
|
1112
|
+
const ctx = model.context;
|
|
1113
|
+
const Comp: any = action.uiSchema.value['x-component'];
|
|
1114
|
+
const view = render(
|
|
1115
|
+
React.createElement(
|
|
1116
|
+
FlowSettingsContextProvider,
|
|
1117
|
+
{ value: ctx },
|
|
1118
|
+
React.createElement(Comp, {
|
|
1119
|
+
value: { fields: [] },
|
|
1120
|
+
onChange: () => {},
|
|
1121
|
+
}),
|
|
1122
|
+
),
|
|
1123
|
+
);
|
|
1124
|
+
|
|
1125
|
+
fireEvent.mouseDown(view.container.querySelector('.ant-select-selector') as Element);
|
|
1126
|
+
|
|
1127
|
+
expect(await screen.findByText('Name')).toBeTruthy();
|
|
1128
|
+
expect(screen.queryByText('JS item')).toBeNull();
|
|
1129
|
+
});
|
|
1130
|
+
|
|
1059
1131
|
it('should only show options state for supported single field selection', () => {
|
|
1060
1132
|
const supportedField: any = {
|
|
1061
1133
|
uid: 'status-field',
|
|
@@ -68,6 +68,12 @@ interface LinkageRule {
|
|
|
68
68
|
}[];
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
export function updateLinkageRules<T>(rules: T[], updater: (nextRules: T[]) => void): T[] {
|
|
72
|
+
const nextRules = _.cloneDeep(rules);
|
|
73
|
+
updater(nextRules);
|
|
74
|
+
return nextRules;
|
|
75
|
+
}
|
|
76
|
+
|
|
71
77
|
const previewValueForLog = (value: any) => {
|
|
72
78
|
if (value == null) return value;
|
|
73
79
|
const t = typeof value;
|
|
@@ -114,11 +120,16 @@ const isActionFlowModel = (model: any): boolean => {
|
|
|
114
120
|
return false;
|
|
115
121
|
};
|
|
116
122
|
|
|
123
|
+
const isFormFieldModel = (model: unknown): model is FlowModel => {
|
|
124
|
+
if (!model || typeof model !== 'object') return false;
|
|
125
|
+
return !!(model as FlowModel).subModels?.field;
|
|
126
|
+
};
|
|
127
|
+
|
|
117
128
|
// 获取表单中所有字段的 model 实例的通用函数
|
|
118
129
|
const getFormFields = (ctx: any) => {
|
|
119
130
|
try {
|
|
120
131
|
const fieldModels = ctx.model?.subModels?.grid?.subModels?.items || [];
|
|
121
|
-
return fieldModels.map((model
|
|
132
|
+
return fieldModels.filter(isFormFieldModel).map((model) => ({
|
|
122
133
|
label: model.props.label || model.props.name,
|
|
123
134
|
value: model.uid,
|
|
124
135
|
model,
|
|
@@ -340,8 +351,8 @@ const FieldStateEditor = ({
|
|
|
340
351
|
const getFormFieldsByForkModel = (ctx: any) => {
|
|
341
352
|
try {
|
|
342
353
|
const fieldModels = ctx.model?.subModels?.grid?.subModels?.items || [];
|
|
343
|
-
return fieldModels.map((model
|
|
344
|
-
const forkModel = Array.from(model.forks)[0] as
|
|
354
|
+
return fieldModels.filter(isFormFieldModel).map((model) => {
|
|
355
|
+
const forkModel = Array.from(model.forks)[0] as FlowModel | undefined;
|
|
345
356
|
|
|
346
357
|
if (forkModel) {
|
|
347
358
|
return {
|
|
@@ -654,6 +665,30 @@ export const linkageSetBlockProps = defineAction({
|
|
|
654
665
|
},
|
|
655
666
|
});
|
|
656
667
|
|
|
668
|
+
const ACTION_LINKAGE_STATE_OPTIONS = [
|
|
669
|
+
{ label: 'Visible', value: 'visible' },
|
|
670
|
+
{ label: 'Hidden', value: 'hidden' },
|
|
671
|
+
{ label: 'Hidden text', value: 'hiddenText' },
|
|
672
|
+
{ label: 'Enabled', value: 'enabled' },
|
|
673
|
+
{ label: 'Disabled', value: 'disabled' },
|
|
674
|
+
] as const;
|
|
675
|
+
|
|
676
|
+
type ActionLinkageState = (typeof ACTION_LINKAGE_STATE_OPTIONS)[number]['value'];
|
|
677
|
+
|
|
678
|
+
export function getActionLinkageStateOptions(
|
|
679
|
+
model: { supportedActionLinkageStates?: readonly ActionLinkageState[] },
|
|
680
|
+
t: (key: string) => string,
|
|
681
|
+
) {
|
|
682
|
+
const supportedStates = model.supportedActionLinkageStates ? new Set(model.supportedActionLinkageStates) : undefined;
|
|
683
|
+
|
|
684
|
+
return ACTION_LINKAGE_STATE_OPTIONS.filter((option) => !supportedStates || supportedStates.has(option.value)).map(
|
|
685
|
+
(option) => ({
|
|
686
|
+
label: t(option.label),
|
|
687
|
+
value: option.value,
|
|
688
|
+
}),
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
|
|
657
692
|
export const linkageSetActionProps = defineAction({
|
|
658
693
|
name: 'linkageSetActionProps',
|
|
659
694
|
title: tExpr('Set button state'),
|
|
@@ -674,13 +709,7 @@ export const linkageSetActionProps = defineAction({
|
|
|
674
709
|
onChange={onChange}
|
|
675
710
|
placeholder={t('Please select state')}
|
|
676
711
|
style={{ width: '100%' }}
|
|
677
|
-
options={
|
|
678
|
-
{ label: t('Visible'), value: 'visible' },
|
|
679
|
-
{ label: t('Hidden'), value: 'hidden' },
|
|
680
|
-
{ label: t('Hidden text'), value: 'hiddenText' },
|
|
681
|
-
{ label: t('Enabled'), value: 'enabled' },
|
|
682
|
-
{ label: t('Disabled'), value: 'disabled' },
|
|
683
|
-
]}
|
|
712
|
+
options={getActionLinkageStateOptions(ctx.model, t)}
|
|
684
713
|
allowClear
|
|
685
714
|
/>
|
|
686
715
|
);
|
|
@@ -1500,13 +1529,26 @@ async function resolveLinkageRulesParamsPreservingRunJsScripts(ctx: FlowContext,
|
|
|
1500
1529
|
}
|
|
1501
1530
|
|
|
1502
1531
|
const LinkageRulesUI = observer(
|
|
1503
|
-
(props: {
|
|
1504
|
-
|
|
1532
|
+
(props: {
|
|
1533
|
+
readonly value: LinkageRule[];
|
|
1534
|
+
onChange?: (value: LinkageRule[]) => void;
|
|
1535
|
+
supportedActions: string[];
|
|
1536
|
+
title?: string;
|
|
1537
|
+
}) => {
|
|
1538
|
+
const { value: rules = [], onChange, supportedActions } = props;
|
|
1505
1539
|
const ctx = useFlowContext();
|
|
1506
1540
|
const flowEngine = useFlowEngine();
|
|
1507
1541
|
const t = ctx.model.translate.bind(ctx.model);
|
|
1508
1542
|
const assignPriorityTip = t('Assignment takes precedence over form field assignment');
|
|
1509
1543
|
|
|
1544
|
+
const replaceRules = (updater: (nextRules: LinkageRule[]) => void) => {
|
|
1545
|
+
if (onChange) {
|
|
1546
|
+
onChange(updateLinkageRules(rules, updater));
|
|
1547
|
+
} else {
|
|
1548
|
+
updater(rules);
|
|
1549
|
+
}
|
|
1550
|
+
};
|
|
1551
|
+
|
|
1510
1552
|
// 创建新规则的默认值
|
|
1511
1553
|
const createNewRule = (): LinkageRule => ({
|
|
1512
1554
|
key: uid(),
|
|
@@ -1523,7 +1565,7 @@ const LinkageRulesUI = observer(
|
|
|
1523
1565
|
|
|
1524
1566
|
// 删除规则
|
|
1525
1567
|
const handleDeleteRule = (index: number) => {
|
|
1526
|
-
|
|
1568
|
+
replaceRules((nextRules) => nextRules.splice(index, 1));
|
|
1527
1569
|
};
|
|
1528
1570
|
|
|
1529
1571
|
// 上移规则
|
|
@@ -1562,7 +1604,9 @@ const LinkageRulesUI = observer(
|
|
|
1562
1604
|
|
|
1563
1605
|
// 切换规则启用状态
|
|
1564
1606
|
const handleToggleEnable = (index: number, enable: boolean) => {
|
|
1565
|
-
|
|
1607
|
+
replaceRules((nextRules) => {
|
|
1608
|
+
nextRules[index].enable = enable;
|
|
1609
|
+
});
|
|
1566
1610
|
};
|
|
1567
1611
|
|
|
1568
1612
|
// 获取可用的动作类型
|
|
@@ -2222,7 +2266,7 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
2222
2266
|
}
|
|
2223
2267
|
|
|
2224
2268
|
rememberOriginalProp(key, model.props?.[key]);
|
|
2225
|
-
if (key === 'hiddenText') {
|
|
2269
|
+
if (key === 'hiddenText' && normalizedProps[key]) {
|
|
2226
2270
|
rememberOriginalProp('title', model.props?.title);
|
|
2227
2271
|
}
|
|
2228
2272
|
if (key === 'required') {
|
|
@@ -2286,7 +2330,6 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
2286
2330
|
const newProps = { ...model.__originalProps, ...patchProps };
|
|
2287
2331
|
const prevHidden = !!model.hidden;
|
|
2288
2332
|
const nextHidden = !!newProps.hiddenModel;
|
|
2289
|
-
|
|
2290
2333
|
model.setProps(_.omit(newProps, ['hiddenModel', 'value', 'hiddenText', LINKAGE_ASSIGN_MODE_PROP]));
|
|
2291
2334
|
syncFieldOptionsToForks(model, patchProps);
|
|
2292
2335
|
if (typeof model.setHidden === 'function') {
|