@nocobase/client-v2 2.1.0-beta.36 → 2.1.0-beta.38
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/Application.d.ts +1 -0
- package/es/BaseApplication.d.ts +4 -0
- package/es/RouterManager.d.ts +1 -0
- package/es/components/KeepAlive.d.ts +22 -0
- package/es/components/RouterBridge.d.ts +9 -0
- package/es/components/form/DialogFormLayout.d.ts +5 -29
- package/es/components/form/filter/CollectionFilter.d.ts +41 -0
- package/es/components/form/filter/CollectionFilterItem.d.ts +41 -0
- package/es/components/form/filter/DateFilterDynamicComponent.d.ts +57 -0
- package/es/components/form/filter/FilterValueInput.d.ts +29 -0
- package/es/components/form/filter/index.d.ts +11 -0
- package/es/components/form/filter/useFilterActionProps.d.ts +96 -0
- package/es/components/form/index.d.ts +1 -0
- package/es/data-source/ExtendCollectionsProvider.d.ts +50 -0
- package/es/data-source/index.d.ts +9 -0
- package/es/flow/FlowPage.d.ts +2 -1
- package/es/flow/admin-shell/AdminLayoutRouteCoordinator.d.ts +8 -40
- package/es/flow/admin-shell/BaseLayoutModel.d.ts +89 -0
- package/es/flow/admin-shell/BaseLayoutRouteCoordinator.d.ts +74 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +12 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutModel.d.ts +7 -92
- package/es/flow/admin-shell/admin-layout/index.d.ts +2 -0
- package/es/flow/admin-shell/useAdminLayoutRoutePage.d.ts +2 -2
- package/es/flow/admin-shell/useLayoutRoutePage.d.ts +23 -0
- package/es/flow/components/FlowRoute.d.ts +10 -1
- package/es/flow/components/filter/index.d.ts +2 -0
- package/es/flow/components/filter/useFilterOptions.d.ts +54 -0
- package/es/flow/index.d.ts +4 -0
- package/es/flow/models/base/PageModel/PageModel.d.ts +3 -1
- package/es/flow/models/blocks/form/FormActionGroupModel.d.ts +1 -0
- package/es/flow/models/blocks/table/TableBlockModel.d.ts +10 -0
- package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.d.ts +1 -1
- package/es/flow-compat/passwordUtils.d.ts +1 -1
- package/es/index.d.ts +2 -0
- package/es/index.mjs +491 -439
- package/es/layout-manager/LayoutContentRoute.d.ts +14 -0
- package/es/layout-manager/LayoutManager.d.ts +22 -0
- package/es/layout-manager/LayoutRoute.d.ts +14 -0
- package/es/layout-manager/index.d.ts +13 -0
- package/es/layout-manager/types.d.ts +20 -0
- package/es/layout-manager/utils.d.ts +14 -0
- package/es/nocobase-buildin-plugin/index.d.ts +3 -10
- package/es/settings-center/index.d.ts +1 -1
- package/es/settings-center/plugin-manager/BulkEnableButton.d.ts +15 -0
- package/es/settings-center/plugin-manager/PluginCard.d.ts +15 -0
- package/es/settings-center/plugin-manager/PluginDetail.d.ts +16 -0
- package/es/settings-center/{PluginManagerPage.d.ts → plugin-manager/index.d.ts} +1 -7
- package/es/settings-center/plugin-manager/types.d.ts +34 -0
- package/lib/index.js +491 -439
- package/package.json +8 -7
- package/src/Application.tsx +27 -12
- package/src/BaseApplication.tsx +19 -0
- package/src/PluginSettingsManager.ts +1 -1
- package/src/RouterManager.tsx +17 -1
- package/src/__tests__/PluginSettingsManager.test.ts +41 -2
- package/src/__tests__/app.test.tsx +17 -1
- package/src/__tests__/globalDeps.test.ts +1 -0
- package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +45 -2
- package/src/__tests__/plugin-manager.test.tsx +177 -0
- package/src/__tests__/settings-center.test.tsx +24 -2
- package/src/components/KeepAlive.tsx +131 -0
- package/src/components/README.md +89 -6
- package/src/components/README.zh-CN.md +89 -7
- package/src/components/RouterBridge.tsx +28 -4
- package/src/components/__tests__/KeepAlive.test.tsx +63 -0
- package/src/components/__tests__/RouterBridge.test.tsx +27 -0
- package/src/components/form/DialogFormLayout.tsx +5 -29
- package/src/components/form/filter/CollectionFilter.tsx +101 -0
- package/src/components/form/filter/CollectionFilterItem.tsx +176 -0
- package/src/components/form/filter/DateFilterDynamicComponent.tsx +283 -0
- package/src/components/form/filter/FilterValueInput.tsx +198 -0
- package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +205 -0
- package/src/components/form/filter/__tests__/DateFilterDynamicComponent.test.tsx +148 -0
- package/src/components/form/filter/__tests__/FilterValueInput.test.tsx +243 -0
- package/src/components/form/filter/__tests__/compileFilterGroup.test.ts +146 -0
- package/src/components/form/filter/index.ts +13 -0
- package/src/components/form/filter/useFilterActionProps.ts +200 -0
- package/src/components/form/index.tsx +1 -0
- package/src/data-source/ExtendCollectionsProvider.tsx +144 -0
- package/src/data-source/__tests__/ExtendCollectionsProvider.test.tsx +264 -0
- package/src/data-source/index.ts +10 -0
- package/src/flow/FlowPage.tsx +35 -7
- package/src/flow/__tests__/FlowPage.test.tsx +79 -0
- package/src/flow/__tests__/FlowRoute.test.tsx +529 -2
- package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +191 -0
- package/src/flow/actions/__tests__/openView.subModelKey.test.tsx +33 -0
- package/src/flow/actions/aclCheck.tsx +4 -0
- package/src/flow/actions/aclCheckRefresh.tsx +4 -0
- package/src/flow/actions/dateTimeFormat.tsx +12 -8
- package/src/flow/actions/linkageRules.tsx +122 -0
- package/src/flow/actions/openView.tsx +28 -4
- package/src/flow/admin-shell/AdminLayoutRouteCoordinator.ts +11 -329
- package/src/flow/admin-shell/BaseLayoutModel.tsx +455 -0
- package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +502 -0
- package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +547 -3
- package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +4 -4
- package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +160 -0
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +0 -12
- package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +28 -201
- package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +11 -2
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +1 -26
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +149 -27
- package/src/flow/admin-shell/admin-layout/index.ts +2 -0
- package/src/flow/admin-shell/useAdminLayoutRoutePage.ts +10 -26
- package/src/flow/admin-shell/useLayoutRoutePage.ts +61 -0
- package/src/flow/components/AdminLayout.tsx +4 -154
- package/src/flow/components/FlowRoute.tsx +105 -15
- package/src/flow/components/filter/index.ts +3 -0
- package/src/flow/components/filter/useFilterOptions.ts +80 -0
- package/src/flow/index.ts +4 -0
- package/src/flow/models/base/ActionModel.tsx +8 -1
- package/src/flow/models/base/PageModel/PageModel.tsx +51 -18
- package/src/flow/models/base/PageModel/RootPageModel.tsx +6 -13
- package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +102 -1
- package/src/flow/models/base/RouteModel.tsx +1 -1
- package/src/flow/models/blocks/form/FormActionGroupModel.tsx +14 -0
- package/src/flow/models/blocks/form/FormItemModel.tsx +8 -1
- package/src/flow/models/blocks/form/__tests__/FormActionGroupModel.test.ts +46 -0
- package/src/flow/models/blocks/form/submitValues.ts +4 -1
- package/src/flow/models/blocks/table/TableBlockModel.tsx +118 -16
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.rowSelection.test.tsx +114 -0
- package/src/flow/models/fields/AssociationFieldModel/SubFormFieldModel.tsx +7 -1
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +1 -1
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +6 -5
- package/src/flow/models/fields/ClickableFieldModel.tsx +9 -1
- package/src/flow/models/fields/DisplayTimeFieldModel.tsx +1 -1
- package/src/flow/models/fields/TimeFieldModel.tsx +1 -1
- package/src/flow/models/fields/__tests__/TimeFieldModel.test.tsx +61 -0
- package/src/flow/models/fields/mobile-components/MobileDatePicker.tsx +19 -3
- package/src/flow/models/fields/mobile-components/__tests__/MobileDatePicker.test.tsx +94 -0
- package/src/flow/models/topbar/TopbarActionModel.tsx +1 -1
- package/src/flow/utils/__tests__/dateTimeFormat.test.ts +91 -0
- package/src/index.ts +2 -0
- package/src/layout-manager/LayoutContentRoute.tsx +90 -0
- package/src/layout-manager/LayoutManager.tsx +185 -0
- package/src/layout-manager/LayoutRoute.tsx +138 -0
- package/src/layout-manager/__tests__/LayoutManager.test.tsx +335 -0
- package/src/layout-manager/__tests__/LayoutRoute.test.tsx +473 -0
- package/src/layout-manager/index.ts +14 -0
- package/src/layout-manager/types.ts +22 -0
- package/src/layout-manager/utils.ts +37 -0
- package/src/nocobase-buildin-plugin/index.tsx +69 -67
- package/src/nocobase-buildin-plugin/plugins/LocalePlugin.ts +1 -0
- package/src/settings-center/index.ts +1 -1
- package/src/settings-center/plugin-manager/BulkEnableButton.tsx +111 -0
- package/src/settings-center/plugin-manager/PluginCard.tsx +270 -0
- package/src/settings-center/plugin-manager/PluginDetail.tsx +195 -0
- package/src/settings-center/plugin-manager/index.tsx +254 -0
- package/src/settings-center/plugin-manager/types.ts +35 -0
- package/src/settings-center/utils.tsx +8 -1
- package/src/theme/__tests__/globalStyles.test.ts +24 -0
- package/src/theme/globalStyles.ts +10 -0
- package/src/utils/globalDeps.ts +2 -0
- package/src/settings-center/PluginManagerPage.tsx +0 -162
|
@@ -13,6 +13,113 @@ import { FlowSettingsContextProvider } from '@nocobase/flow-engine';
|
|
|
13
13
|
import { describe, expect, it, vi } from 'vitest';
|
|
14
14
|
import { fieldLinkageRules, linkageSetFieldProps, subFormLinkageSetFieldProps } from '../linkageRules';
|
|
15
15
|
|
|
16
|
+
const createSubFormFieldModel = ({
|
|
17
|
+
uid,
|
|
18
|
+
fieldPath,
|
|
19
|
+
hidden,
|
|
20
|
+
blockUid = 'block-1',
|
|
21
|
+
fieldIndex,
|
|
22
|
+
fieldPathArray,
|
|
23
|
+
}: {
|
|
24
|
+
uid: string;
|
|
25
|
+
fieldPath: string;
|
|
26
|
+
hidden: boolean;
|
|
27
|
+
blockUid?: string;
|
|
28
|
+
fieldIndex?: string[];
|
|
29
|
+
fieldPathArray?: Array<string | number>;
|
|
30
|
+
}) => ({
|
|
31
|
+
uid,
|
|
32
|
+
hidden,
|
|
33
|
+
props: {},
|
|
34
|
+
context: {
|
|
35
|
+
blockModel: { uid: blockUid },
|
|
36
|
+
...(fieldIndex ? { fieldIndex } : {}),
|
|
37
|
+
...(fieldPathArray ? { fieldPathArray } : {}),
|
|
38
|
+
},
|
|
39
|
+
forks: new Set(),
|
|
40
|
+
getStepParams: (flowKey: string, stepKey: string) => {
|
|
41
|
+
if (flowKey === 'fieldSettings' && stepKey === 'init') {
|
|
42
|
+
return { fieldPath };
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
setProps(key: any, value?: any) {
|
|
46
|
+
if (typeof key === 'string') {
|
|
47
|
+
this.props[key] = value;
|
|
48
|
+
} else {
|
|
49
|
+
this.props = { ...this.props, ...key };
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const runSubFormFieldStateRule = async ({
|
|
55
|
+
state,
|
|
56
|
+
fieldUid,
|
|
57
|
+
fieldKey,
|
|
58
|
+
targetModel,
|
|
59
|
+
models,
|
|
60
|
+
}: {
|
|
61
|
+
state: 'visible' | 'hidden';
|
|
62
|
+
fieldUid: string;
|
|
63
|
+
fieldKey: string[];
|
|
64
|
+
targetModel: any;
|
|
65
|
+
models: any[];
|
|
66
|
+
}) => {
|
|
67
|
+
const formItemModel: any = {
|
|
68
|
+
uid: fieldUid,
|
|
69
|
+
forks: new Set([targetModel]),
|
|
70
|
+
getFork: vi.fn((key: string) => (key === `${fieldKey}:${fieldUid}` ? targetModel : undefined)),
|
|
71
|
+
};
|
|
72
|
+
const allModels = [formItemModel, ...models];
|
|
73
|
+
const ctx: any = {
|
|
74
|
+
app: {
|
|
75
|
+
jsonLogic: {
|
|
76
|
+
apply: vi.fn(() => true),
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
model: {
|
|
80
|
+
context: {
|
|
81
|
+
blockModel: { uid: 'block-1' },
|
|
82
|
+
fieldKey,
|
|
83
|
+
},
|
|
84
|
+
flowEngine: {
|
|
85
|
+
forEachModel: (visitor: (model: any) => void) => {
|
|
86
|
+
allModels.forEach(visitor);
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
engine: {
|
|
91
|
+
getModel: vi.fn((uid: string) => (uid === fieldUid ? formItemModel : undefined)),
|
|
92
|
+
forEachModel: (visitor: (model: any) => void) => {
|
|
93
|
+
allModels.forEach(visitor);
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
getAction: (name: string) => (name === 'subFormLinkageSetFieldProps' ? subFormLinkageSetFieldProps : null),
|
|
97
|
+
resolveJsonTemplate: vi.fn(async (value) => value),
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
await fieldLinkageRules.handler(ctx, {
|
|
101
|
+
value: [
|
|
102
|
+
{
|
|
103
|
+
key: 'rule-1',
|
|
104
|
+
enable: true,
|
|
105
|
+
condition: { logic: '$and', items: [] },
|
|
106
|
+
actions: [
|
|
107
|
+
{
|
|
108
|
+
key: 'action-1',
|
|
109
|
+
name: 'subFormLinkageSetFieldProps',
|
|
110
|
+
params: {
|
|
111
|
+
value: {
|
|
112
|
+
fields: [fieldUid],
|
|
113
|
+
state,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
|
|
16
123
|
describe('subFormLinkageSetFieldProps action', () => {
|
|
17
124
|
it('should not throw when engine.getModel returns undefined', () => {
|
|
18
125
|
const setProps = vi.fn();
|
|
@@ -144,6 +251,90 @@ describe('subFormLinkageSetFieldProps action', () => {
|
|
|
144
251
|
|
|
145
252
|
expect(setProps).toHaveBeenCalledWith(forkModel, { options: selectedOptions });
|
|
146
253
|
});
|
|
254
|
+
|
|
255
|
+
it('should sync hidden state when a subform field changes from hidden to visible', async () => {
|
|
256
|
+
const targetModel = createSubFormFieldModel({
|
|
257
|
+
uid: 'field-a',
|
|
258
|
+
fieldPath: 'a',
|
|
259
|
+
hidden: true,
|
|
260
|
+
fieldIndex: ['items:0'],
|
|
261
|
+
});
|
|
262
|
+
const collectedModel = createSubFormFieldModel({
|
|
263
|
+
uid: 'field-a-collected',
|
|
264
|
+
fieldPath: 'a',
|
|
265
|
+
hidden: true,
|
|
266
|
+
fieldPathArray: ['items', 0, 'a'],
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
await runSubFormFieldStateRule({
|
|
270
|
+
state: 'visible',
|
|
271
|
+
fieldUid: 'field-a',
|
|
272
|
+
fieldKey: ['items:0'],
|
|
273
|
+
targetModel,
|
|
274
|
+
models: [collectedModel],
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
expect(targetModel.hidden).toBe(false);
|
|
278
|
+
expect(collectedModel.hidden).toBe(false);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should sync hidden state when a subform field changes from visible to hidden', async () => {
|
|
282
|
+
const targetModel = createSubFormFieldModel({
|
|
283
|
+
uid: 'field-a',
|
|
284
|
+
fieldPath: 'a',
|
|
285
|
+
hidden: false,
|
|
286
|
+
fieldIndex: ['items:0'],
|
|
287
|
+
});
|
|
288
|
+
const collectedModel = createSubFormFieldModel({
|
|
289
|
+
uid: 'field-a-collected',
|
|
290
|
+
fieldPath: 'a',
|
|
291
|
+
hidden: false,
|
|
292
|
+
fieldPathArray: ['items', 0, 'a'],
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
await runSubFormFieldStateRule({
|
|
296
|
+
state: 'hidden',
|
|
297
|
+
fieldUid: 'field-a',
|
|
298
|
+
fieldKey: ['items:0'],
|
|
299
|
+
targetModel,
|
|
300
|
+
models: [collectedModel],
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
expect(targetModel.hidden).toBe(true);
|
|
304
|
+
expect(collectedModel.hidden).toBe(true);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('should only sync hidden state for the current subform row', async () => {
|
|
308
|
+
const targetModel = createSubFormFieldModel({
|
|
309
|
+
uid: 'field-a',
|
|
310
|
+
fieldPath: 'a',
|
|
311
|
+
hidden: true,
|
|
312
|
+
fieldIndex: ['items:0'],
|
|
313
|
+
});
|
|
314
|
+
const row0CollectedModel = createSubFormFieldModel({
|
|
315
|
+
uid: 'field-a-row-0',
|
|
316
|
+
fieldPath: 'a',
|
|
317
|
+
hidden: true,
|
|
318
|
+
fieldPathArray: ['items', 0, 'a'],
|
|
319
|
+
});
|
|
320
|
+
const row1CollectedModel = createSubFormFieldModel({
|
|
321
|
+
uid: 'field-a-row-1',
|
|
322
|
+
fieldPath: 'a',
|
|
323
|
+
hidden: true,
|
|
324
|
+
fieldPathArray: ['items', 1, 'a'],
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
await runSubFormFieldStateRule({
|
|
328
|
+
state: 'visible',
|
|
329
|
+
fieldUid: 'field-a',
|
|
330
|
+
fieldKey: ['items:0'],
|
|
331
|
+
targetModel,
|
|
332
|
+
models: [row0CollectedModel, row1CollectedModel],
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
expect(row0CollectedModel.hidden).toBe(false);
|
|
336
|
+
expect(row1CollectedModel.hidden).toBe(true);
|
|
337
|
+
});
|
|
147
338
|
});
|
|
148
339
|
|
|
149
340
|
describe('linkageSetFieldProps action', () => {
|
|
@@ -28,6 +28,7 @@ describe('openView action - subModelKey behavior', () => {
|
|
|
28
28
|
flowEngine: { context: { themeToken: { colorBgLayout: '#fff' } } },
|
|
29
29
|
},
|
|
30
30
|
layoutContentElement: {},
|
|
31
|
+
layoutContext: { source: 'layout-context' },
|
|
31
32
|
view: {},
|
|
32
33
|
viewer: {
|
|
33
34
|
open: vi.fn(async (_opts: any) => undefined),
|
|
@@ -90,6 +91,38 @@ describe('openView action - subModelKey behavior', () => {
|
|
|
90
91
|
// FlowPage should receive parent model uid
|
|
91
92
|
expect(capturedElement?.type).toBe(FlowPage);
|
|
92
93
|
expect(capturedElement?.props?.parentId).toBe('parent-model-uid');
|
|
94
|
+
expect(capturedElement?.props).not.toHaveProperty('layoutContext');
|
|
93
95
|
expect(capturedElement?.props?.pageModelClass).toBe('ChildPageModel');
|
|
94
96
|
});
|
|
97
|
+
|
|
98
|
+
it('uses current layout child page model as default pageModelClass', async () => {
|
|
99
|
+
const { ctx } = createCtx();
|
|
100
|
+
ctx.layout = {
|
|
101
|
+
childPageModelClass: 'MobileChildPageModel',
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
let capturedElement: any;
|
|
105
|
+
(ctx.viewer.open as any).mockImplementation(async (opts) => {
|
|
106
|
+
const currentView = { close: vi.fn(), update: vi.fn() };
|
|
107
|
+
capturedElement = opts.content(currentView);
|
|
108
|
+
return undefined;
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
await openView.handler(ctx, { navigation: false });
|
|
112
|
+
|
|
113
|
+
expect(capturedElement?.type).toBe(FlowPage);
|
|
114
|
+
expect(capturedElement?.props?.pageModelClass).toBe('MobileChildPageModel');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('uses current layout child page model in default params', async () => {
|
|
118
|
+
const { ctx } = createCtx();
|
|
119
|
+
ctx.layout = {
|
|
120
|
+
childPageModelClass: 'MobileChildPageModel',
|
|
121
|
+
};
|
|
122
|
+
ctx.getPropertyMetaTree = vi.fn(() => []);
|
|
123
|
+
|
|
124
|
+
await expect(openView.defaultParams(ctx)).resolves.toMatchObject({
|
|
125
|
+
pageModelClass: 'MobileChildPageModel',
|
|
126
|
+
});
|
|
127
|
+
});
|
|
95
128
|
});
|
|
@@ -24,6 +24,10 @@ const getRecordPkValue = (value: any) => {
|
|
|
24
24
|
export const aclCheck = defineAction({
|
|
25
25
|
name: 'aclCheck',
|
|
26
26
|
async handler(ctx, params) {
|
|
27
|
+
if (ctx.skipAclCheck) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
27
31
|
const result = await ctx.aclCheck({
|
|
28
32
|
dataSourceKey: ctx.dataSource?.key,
|
|
29
33
|
resourceName: ctx.collectionField?.collectionName || ctx.resourceName,
|
|
@@ -17,7 +17,7 @@ export const dateTimeFormat = defineAction({
|
|
|
17
17
|
name: 'dateDisplayFormat',
|
|
18
18
|
uiSchema: (ctx) => {
|
|
19
19
|
const { collectionField } = ctx.model.context as any;
|
|
20
|
-
const
|
|
20
|
+
const isTimeField = collectionField.type === 'time' || collectionField.interface === 'time';
|
|
21
21
|
const timeFormatField = {
|
|
22
22
|
type: 'string',
|
|
23
23
|
title: '{{t("Time format")}}',
|
|
@@ -41,7 +41,7 @@ export const dateTimeFormat = defineAction({
|
|
|
41
41
|
],
|
|
42
42
|
'x-reactions': [
|
|
43
43
|
(field) => {
|
|
44
|
-
if (
|
|
44
|
+
if (!isTimeField) {
|
|
45
45
|
const { showTime, picker } = field.form.values || {};
|
|
46
46
|
field.hidden = !showTime || picker !== 'date';
|
|
47
47
|
}
|
|
@@ -49,7 +49,7 @@ export const dateTimeFormat = defineAction({
|
|
|
49
49
|
],
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
-
if (
|
|
52
|
+
if (isTimeField) {
|
|
53
53
|
return {
|
|
54
54
|
timeFormat: timeFormatField,
|
|
55
55
|
};
|
|
@@ -159,24 +159,28 @@ export const dateTimeFormat = defineAction({
|
|
|
159
159
|
};
|
|
160
160
|
},
|
|
161
161
|
defaultParams: (ctx: any) => {
|
|
162
|
-
const { showTime, dateFormat, timeFormat, picker }: any = {
|
|
162
|
+
const { showTime, dateFormat, format, timeFormat, picker }: any = {
|
|
163
163
|
...ctx.model.context.collectionField.getComponentProps(),
|
|
164
164
|
...ctx.model.props,
|
|
165
165
|
};
|
|
166
|
+
const collectionField = ctx.model.context.collectionField;
|
|
167
|
+
const isTimeField = collectionField.type === 'time' || collectionField.interface === 'time';
|
|
166
168
|
return {
|
|
167
169
|
picker: picker || 'date',
|
|
168
170
|
dateFormat: dateFormat || 'YYYY-MM-DD',
|
|
169
|
-
timeFormat: timeFormat || 'HH:mm:ss',
|
|
171
|
+
timeFormat: timeFormat || (isTimeField ? format : undefined) || 'HH:mm:ss',
|
|
170
172
|
showTime,
|
|
171
173
|
};
|
|
172
174
|
},
|
|
173
175
|
handler(ctx: any, params) {
|
|
174
176
|
const { collectionField } = ctx.model.context as any;
|
|
175
|
-
const
|
|
176
|
-
if (
|
|
177
|
+
const isTimeField = collectionField.type === 'time' || collectionField.interface === 'time';
|
|
178
|
+
if (isTimeField) {
|
|
179
|
+
const timeFormat = params?.timeFormat || params?.format || 'HH:mm:ss';
|
|
177
180
|
ctx.model.setProps({
|
|
178
181
|
...params,
|
|
179
|
-
|
|
182
|
+
timeFormat,
|
|
183
|
+
format: timeFormat,
|
|
180
184
|
});
|
|
181
185
|
} else {
|
|
182
186
|
ctx.model.setProps({
|
|
@@ -1916,6 +1916,120 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
1916
1916
|
|
|
1917
1917
|
return null;
|
|
1918
1918
|
};
|
|
1919
|
+
const normalizeNamePathForKey = (namePath: any): Array<string | number> | null => {
|
|
1920
|
+
if (!Array.isArray(namePath) || !namePath.length) return null;
|
|
1921
|
+
const normalized = namePath.filter((seg) => typeof seg === 'string' || typeof seg === 'number') as Array<
|
|
1922
|
+
string | number
|
|
1923
|
+
>;
|
|
1924
|
+
return normalized.length ? normalized : null;
|
|
1925
|
+
};
|
|
1926
|
+
const getFieldIndexEntries = (fieldIndex: any): Array<{ name: string; index: number }> => {
|
|
1927
|
+
if (!Array.isArray(fieldIndex)) return [];
|
|
1928
|
+
return fieldIndex
|
|
1929
|
+
.map((entry) => {
|
|
1930
|
+
if (typeof entry !== 'string') return null;
|
|
1931
|
+
const [name, indexText] = entry.split(':');
|
|
1932
|
+
const index = Number(indexText);
|
|
1933
|
+
if (!name || !Number.isFinite(index)) return null;
|
|
1934
|
+
return { name, index };
|
|
1935
|
+
})
|
|
1936
|
+
.filter(Boolean) as Array<{ name: string; index: number }>;
|
|
1937
|
+
};
|
|
1938
|
+
const resolveIndexedRelativePath = (path: string, fieldIndex: any): Array<string | number> | null => {
|
|
1939
|
+
const pathSegs = parsePathString(path).filter((seg) => typeof seg === 'string' || typeof seg === 'number') as Array<
|
|
1940
|
+
string | number
|
|
1941
|
+
>;
|
|
1942
|
+
if (!pathSegs.length) return null;
|
|
1943
|
+
|
|
1944
|
+
const entries = getFieldIndexEntries(fieldIndex);
|
|
1945
|
+
if (!entries.length) return null;
|
|
1946
|
+
|
|
1947
|
+
const firstSeg = typeof pathSegs[0] === 'string' ? pathSegs[0] : '';
|
|
1948
|
+
const matchedEntryIndex = firstSeg ? entries.findIndex((entry) => entry.name === firstSeg) : -1;
|
|
1949
|
+
const prefixEntries = matchedEntryIndex >= 0 ? entries.slice(0, matchedEntryIndex) : entries;
|
|
1950
|
+
const remainingEntries = matchedEntryIndex >= 0 ? entries.slice(matchedEntryIndex) : [];
|
|
1951
|
+
const out: Array<string | number> = [];
|
|
1952
|
+
|
|
1953
|
+
prefixEntries.forEach((entry) => {
|
|
1954
|
+
out.push(entry.name, entry.index);
|
|
1955
|
+
});
|
|
1956
|
+
|
|
1957
|
+
let entryIndex = 0;
|
|
1958
|
+
pathSegs.forEach((seg, index) => {
|
|
1959
|
+
out.push(seg);
|
|
1960
|
+
const entry = remainingEntries[entryIndex];
|
|
1961
|
+
if (typeof seg !== 'string' || !entry || entry.name !== seg) return;
|
|
1962
|
+
|
|
1963
|
+
const nextSeg = pathSegs[index + 1];
|
|
1964
|
+
if (typeof nextSeg === 'number') {
|
|
1965
|
+
entryIndex += 1;
|
|
1966
|
+
return;
|
|
1967
|
+
}
|
|
1968
|
+
out.push(entry.index);
|
|
1969
|
+
entryIndex += 1;
|
|
1970
|
+
});
|
|
1971
|
+
|
|
1972
|
+
return out;
|
|
1973
|
+
};
|
|
1974
|
+
const getModelTargetPathKeys = (model: any): Set<string> => {
|
|
1975
|
+
const keys = new Set<string>();
|
|
1976
|
+
const fieldPathArray = normalizeNamePathForKey(model?.context?.fieldPathArray);
|
|
1977
|
+
if (fieldPathArray) {
|
|
1978
|
+
keys.add(namePathToPathKey(fieldPathArray));
|
|
1979
|
+
return keys;
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
const targetPath = getModelTargetPathForPatch(model);
|
|
1983
|
+
if (targetPath) {
|
|
1984
|
+
const fieldIndexEntries = getFieldIndexEntries(model?.context?.fieldIndex);
|
|
1985
|
+
if (!fieldIndexEntries.length) {
|
|
1986
|
+
keys.add(`raw:${targetPath}`);
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
const resolved = normalizeNamePathForKey(resolveDynamicNamePath(targetPath, model?.context?.fieldIndex));
|
|
1990
|
+
if (resolved) {
|
|
1991
|
+
keys.add(namePathToPathKey(resolved));
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
const indexedRelativePath = resolveIndexedRelativePath(targetPath, model?.context?.fieldIndex);
|
|
1995
|
+
if (indexedRelativePath) {
|
|
1996
|
+
keys.add(namePathToPathKey(indexedRelativePath));
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
return keys;
|
|
2001
|
+
};
|
|
2002
|
+
const forEachModelIncludingForks = (visitor: (model: any) => void) => {
|
|
2003
|
+
const engine = (ctx as any)?.engine || ctx.model?.flowEngine;
|
|
2004
|
+
if (!engine?.forEachModel) return;
|
|
2005
|
+
engine.forEachModel((model: any) => {
|
|
2006
|
+
visitor(model);
|
|
2007
|
+
|
|
2008
|
+
const forks: any = model?.forks;
|
|
2009
|
+
if (forks && typeof forks.forEach === 'function') {
|
|
2010
|
+
forks.forEach((fork: any) => {
|
|
2011
|
+
if (!fork || fork.disposed) return;
|
|
2012
|
+
visitor(fork);
|
|
2013
|
+
});
|
|
2014
|
+
}
|
|
2015
|
+
});
|
|
2016
|
+
};
|
|
2017
|
+
const syncHiddenStateByTargetPath = (targetModel: any, hidden: boolean) => {
|
|
2018
|
+
const targetPathKeys = getModelTargetPathKeys(targetModel);
|
|
2019
|
+
if (!targetPathKeys.size) return;
|
|
2020
|
+
|
|
2021
|
+
const targetBlockUid = targetModel?.context?.blockModel?.uid;
|
|
2022
|
+
if (!targetBlockUid) return;
|
|
2023
|
+
forEachModelIncludingForks((model: any) => {
|
|
2024
|
+
if (!model || typeof model !== 'object') return;
|
|
2025
|
+
const modelBlockUid = model?.context?.blockModel?.uid;
|
|
2026
|
+
if (String(modelBlockUid) !== String(targetBlockUid)) return;
|
|
2027
|
+
const modelPathKeys = getModelTargetPathKeys(model);
|
|
2028
|
+
if (!Array.from(modelPathKeys).some((key) => targetPathKeys.has(key))) return;
|
|
2029
|
+
|
|
2030
|
+
model.hidden = hidden;
|
|
2031
|
+
});
|
|
2032
|
+
};
|
|
1919
2033
|
|
|
1920
2034
|
// 1. 运行所有的联动规则
|
|
1921
2035
|
for (const rule of linkageRules.filter((r) => r.enable)) {
|
|
@@ -1981,9 +2095,11 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
1981
2095
|
}
|
|
1982
2096
|
});
|
|
1983
2097
|
|
|
2098
|
+
const hiddenStatePatches: Array<{ model: any; hidden: boolean }> = [];
|
|
1984
2099
|
mergedByUid.forEach((model: any, uid) => {
|
|
1985
2100
|
const patchProps = mergedPropsByUid.get(uid) || {};
|
|
1986
2101
|
const newProps = { ...model.__originalProps, ...patchProps };
|
|
2102
|
+
const hasHiddenModelPatch = Object.prototype.hasOwnProperty.call(patchProps, 'hiddenModel');
|
|
1987
2103
|
|
|
1988
2104
|
model.setProps(_.omit(newProps, ['hiddenModel', 'value', 'hiddenText']));
|
|
1989
2105
|
syncFieldOptionsToForks(model, patchProps);
|
|
@@ -1991,6 +2107,9 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
1991
2107
|
model.setHidden(!!newProps.hiddenModel);
|
|
1992
2108
|
} else {
|
|
1993
2109
|
model.hidden = !!newProps.hiddenModel;
|
|
2110
|
+
if (hasHiddenModelPatch) {
|
|
2111
|
+
hiddenStatePatches.push({ model, hidden: model.hidden });
|
|
2112
|
+
}
|
|
1994
2113
|
}
|
|
1995
2114
|
|
|
1996
2115
|
if (newProps.required === true) {
|
|
@@ -2027,6 +2146,9 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
2027
2146
|
|
|
2028
2147
|
model.__props = null;
|
|
2029
2148
|
});
|
|
2149
|
+
hiddenStatePatches.forEach(({ model, hidden }) => {
|
|
2150
|
+
syncHiddenStateByTargetPath(model, hidden);
|
|
2151
|
+
});
|
|
2030
2152
|
|
|
2031
2153
|
const allPatches = [...directValuePatches];
|
|
2032
2154
|
if (!allPatches.length) return;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { defineAction, tExpr, FlowModelContext, FlowModel, FlowExitAllException } from '@nocobase/flow-engine';
|
|
11
11
|
import React from 'react';
|
|
12
12
|
import { FlowPage } from '../FlowPage';
|
|
13
|
-
import { RootPageModel } from '../models';
|
|
13
|
+
import { PageModel, RootPageModel } from '../models';
|
|
14
14
|
import _ from 'lodash';
|
|
15
15
|
|
|
16
16
|
type DirtyAwareFlowModel = FlowModel & {
|
|
@@ -230,7 +230,7 @@ export const openView = defineAction({
|
|
|
230
230
|
return {
|
|
231
231
|
mode: 'drawer',
|
|
232
232
|
size: 'medium',
|
|
233
|
-
pageModelClass: 'ChildPageModel',
|
|
233
|
+
pageModelClass: ctx.layout?.childPageModelClass || 'ChildPageModel',
|
|
234
234
|
uid: ctx.model?.uid,
|
|
235
235
|
...(filterByTkExpr ? { filterByTk: filterByTkExpr } : {}),
|
|
236
236
|
...(sourceIdExpr ? { sourceId: sourceIdExpr } : {}),
|
|
@@ -371,7 +371,8 @@ export const openView = defineAction({
|
|
|
371
371
|
embed: {},
|
|
372
372
|
};
|
|
373
373
|
|
|
374
|
-
const pageModelClass =
|
|
374
|
+
const pageModelClass =
|
|
375
|
+
ctx.inputArgs.pageModelClass || params.pageModelClass || ctx.layout?.childPageModelClass || 'ChildPageModel';
|
|
375
376
|
const size = ctx.inputArgs.size || params.size || 'medium';
|
|
376
377
|
let pageModelUid: string | null = null;
|
|
377
378
|
let pageModelRef: FlowModel | null = null;
|
|
@@ -414,7 +415,12 @@ export const openView = defineAction({
|
|
|
414
415
|
associationName: runtimeAssociationName,
|
|
415
416
|
tabUid: mergedTabUid,
|
|
416
417
|
openerUids,
|
|
417
|
-
pageActive:
|
|
418
|
+
pageActive:
|
|
419
|
+
typeof inputArgs.pageActive === 'boolean'
|
|
420
|
+
? inputArgs.pageActive
|
|
421
|
+
: typeof ctx.inputArgs.pageActive === 'boolean'
|
|
422
|
+
? ctx.inputArgs.pageActive
|
|
423
|
+
: true,
|
|
418
424
|
};
|
|
419
425
|
// Ensure runtime keys propagate to view.inputArgs
|
|
420
426
|
finalInputArgs.filterByTk = mergedFilterByTk;
|
|
@@ -477,6 +483,24 @@ export const openView = defineAction({
|
|
|
477
483
|
});
|
|
478
484
|
}
|
|
479
485
|
|
|
486
|
+
if (ctx.inputArgs.activateRef) {
|
|
487
|
+
ctx.inputArgs.activateRef.current = (forceRefresh = false) => {
|
|
488
|
+
currentView.inputArgs.pageActive = true;
|
|
489
|
+
if (pageModel instanceof PageModel) {
|
|
490
|
+
pageModel.activateCurrentTab(forceRefresh);
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (ctx.inputArgs.deactivateRef) {
|
|
496
|
+
ctx.inputArgs.deactivateRef.current = () => {
|
|
497
|
+
currentView.inputArgs.pageActive = false;
|
|
498
|
+
if (pageModel instanceof PageModel) {
|
|
499
|
+
pageModel.deactivateCurrentTab();
|
|
500
|
+
}
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
|
|
480
504
|
Object.entries(defineProperties as Record<string, any>).forEach(([key, p]) => {
|
|
481
505
|
pageModel.context.defineProperty(key, p);
|
|
482
506
|
});
|