@nocobase/client-v2 2.2.0-alpha.1 → 2.2.0-alpha.3
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/PluginSettingsManager.d.ts +33 -0
- package/es/RouteRepository.d.ts +21 -6
- package/es/components/category-tabs/SortableCategoryTabs.d.ts +55 -0
- package/es/components/category-tabs/index.d.ts +9 -0
- package/es/components/form/JsonTextArea.d.ts +2 -1
- package/es/components/form/TypedVariableInput.d.ts +22 -4
- package/es/components/form/VariableJsonTextArea.d.ts +19 -0
- package/es/components/form/filter/CollectionFilterItem.d.ts +11 -1
- package/es/components/form/filter/index.d.ts +2 -0
- package/es/components/form/index.d.ts +1 -0
- package/es/components/index.d.ts +2 -0
- package/es/flow/actions/afterSuccess.d.ts +8 -1
- package/es/flow/actions/index.d.ts +1 -1
- package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +2 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.d.ts +2 -1
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +2 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.d.ts +3 -2
- package/es/flow/admin-shell/admin-layout/AdminLayoutSlotModels.d.ts +5 -0
- package/es/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.d.ts +6 -0
- package/es/flow/components/FieldAssignRulesEditor.d.ts +3 -1
- package/es/flow/components/FieldAssignValueInput.d.ts +2 -0
- package/es/flow/index.d.ts +1 -0
- package/es/flow/models/base/GridModel.d.ts +1 -1
- package/es/flow/models/blocks/filter-form/FilterFormBlockModel.d.ts +5 -0
- package/es/flow/models/blocks/form/FormBlockModel.d.ts +4 -0
- package/es/flow/models/blocks/form/submitHandler.d.ts +1 -1
- package/es/flow/models/blocks/form/value-runtime/rules.d.ts +21 -0
- package/es/flow/models/blocks/form/value-runtime/runtime.d.ts +30 -0
- package/es/flow/models/blocks/form/value-runtime/types.d.ts +2 -2
- package/es/flow/models/blocks/js-block/JSBlock.d.ts +1 -0
- package/es/flow/models/blocks/table/TableActionsColumnModel.d.ts +1 -0
- package/es/flow/models/blocks/table/TableBlockModel.d.ts +1 -0
- package/es/flow/models/blocks/table/dragSort/dragSortHooks.d.ts +1 -1
- package/es/flow/models/blocks/table/dragSort/dragSortSettings.d.ts +2 -1
- package/es/flow/models/blocks/table/dragSort/dragSortUtils.d.ts +6 -4
- package/es/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.d.ts +2 -0
- package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.d.ts +6 -0
- package/es/flow/resolveViewParamsToViewList.d.ts +1 -1
- package/es/flow/utils/dataScopeRowSnapshot.d.ts +59 -0
- package/es/flow/utils/dateTimeDisplayProps.d.ts +49 -0
- package/es/flow/utils/formValueDeps.d.ts +32 -0
- package/es/flow-compat/FieldValidation.d.ts +2 -1
- package/es/index.d.ts +2 -1
- package/es/index.mjs +233 -137
- package/lib/index.js +234 -138
- package/package.json +7 -7
- package/src/PluginSettingsManager.ts +53 -0
- package/src/RouteRepository.ts +126 -24
- package/src/__tests__/PluginSettingsManager.test.ts +13 -0
- package/src/__tests__/RouteRepository.test.ts +216 -0
- package/src/__tests__/browserChecker.test.ts +61 -0
- package/src/__tests__/exports.test.ts +16 -0
- package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +199 -0
- package/src/__tests__/plugin-manager.test.tsx +44 -2
- package/src/__tests__/settings-center.test.tsx +40 -1
- package/src/collection-manager/field-configure.ts +1 -1
- package/src/collection-manager/interfaces/id.ts +1 -1
- package/src/collection-manager/interfaces/m2m.tsx +2 -2
- package/src/collection-manager/interfaces/m2o.tsx +2 -2
- package/src/collection-manager/interfaces/nanoid.ts +1 -1
- package/src/collection-manager/interfaces/o2m.tsx +2 -2
- package/src/collection-manager/interfaces/obo.tsx +2 -2
- package/src/collection-manager/interfaces/oho.tsx +2 -2
- package/src/collection-manager/interfaces/properties/index.ts +2 -2
- package/src/collection-manager/interfaces/uuid.ts +1 -1
- package/src/components/AppComponents.tsx +19 -3
- package/src/components/README.md +7 -1
- package/src/components/README.zh-CN.md +6 -1
- package/src/components/category-tabs/SortableCategoryTabs.tsx +210 -0
- package/src/components/category-tabs/index.ts +10 -0
- package/src/components/form/JsonTextArea.tsx +21 -11
- package/src/components/form/TypedVariableInput.tsx +416 -93
- package/src/components/form/VariableJsonTextArea.tsx +175 -0
- package/src/components/form/__tests__/JsonTextArea.test.tsx +43 -0
- package/src/components/form/__tests__/TypedVariableInput.test.tsx +268 -9
- package/src/components/form/__tests__/VariableJsonTextArea.test.tsx +86 -0
- package/src/components/form/filter/CollectionFilterItem.tsx +32 -7
- package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +38 -0
- package/src/components/form/filter/index.ts +2 -0
- package/src/components/form/index.tsx +1 -0
- package/src/components/index.ts +2 -0
- package/src/flow/__tests__/FlowRoute.test.tsx +443 -5
- package/src/flow/__tests__/getKey.test.ts +7 -0
- package/src/flow/__tests__/resolveViewParamsToViewList.test.ts +34 -0
- package/src/flow/actions/__tests__/afterSuccess.test.ts +73 -0
- package/src/flow/actions/__tests__/dataScopeFilter.test.ts +58 -0
- package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +1022 -0
- package/src/flow/actions/__tests__/fieldLinkageRules.scopeDepth.test.ts +150 -1
- package/src/flow/actions/__tests__/linkageAssignField.legacy.test.ts +135 -0
- package/src/flow/actions/__tests__/linkageRules.hiddenSync.restore.test.ts +19 -5
- package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +92 -0
- package/src/flow/actions/__tests__/openView.defineProps.route.test.tsx +104 -0
- package/src/flow/actions/__tests__/subFormFieldLinkageRules.inputArgs.test.ts +2 -2
- package/src/flow/actions/afterSuccess.tsx +142 -3
- package/src/flow/actions/customVariable.tsx +1 -2
- package/src/flow/actions/dataScopeFilter.ts +10 -0
- package/src/flow/actions/dateTimeFormat.tsx +42 -28
- package/src/flow/actions/index.ts +1 -1
- package/src/flow/actions/linkageRules.tsx +123 -35
- package/src/flow/actions/linkageRulesFormValueRefresh.ts +22 -130
- package/src/flow/actions/openView.tsx +38 -4
- package/src/flow/actions/runjs.tsx +2 -14
- package/src/flow/actions/validation.tsx +62 -30
- package/src/flow/admin-shell/BaseLayoutModel.tsx +25 -3
- package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +5 -2
- package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +13 -0
- package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +34 -10
- package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.test.tsx +310 -0
- package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +44 -8
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.ts +5 -3
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +61 -9
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +71 -8
- package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +1 -1
- package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +23 -9
- package/src/flow/admin-shell/admin-layout/HelpLite.tsx +1 -3
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +188 -0
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +99 -0
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +82 -0
- package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +166 -2
- package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.test.ts +20 -0
- package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.ts +33 -5
- package/src/flow/components/DefaultValue.tsx +1 -2
- package/src/flow/components/DynamicFlowsIcon.tsx +447 -126
- package/src/flow/components/FieldAssignRulesEditor.tsx +128 -21
- package/src/flow/components/FieldAssignValueInput.tsx +10 -5
- package/src/flow/components/FlowRoute.tsx +56 -11
- package/src/flow/components/__tests__/DynamicFlowsIcon.test.tsx +148 -2
- package/src/flow/components/__tests__/FieldAssignRulesEditor.test.tsx +508 -4
- package/src/flow/components/__tests__/fieldAssignOptions.test.ts +151 -3
- package/src/flow/components/code-editor/__tests__/useCodeRunner.test.tsx +2 -17
- package/src/flow/components/code-editor/hooks/useCodeRunner.ts +2 -14
- package/src/flow/components/code-editor/runjsDiagnostics.ts +8 -45
- package/src/flow/components/fieldAssignOptions.ts +155 -27
- package/src/flow/flows/editMarkdownFlow.tsx +1 -1
- package/src/flow/getViewDiffAndUpdateHidden.tsx +1 -0
- package/src/flow/index.ts +2 -1
- package/src/flow/internal/utils/operatorSchemaHelper.ts +15 -1
- package/src/flow/models/actions/JSActionModel.tsx +2 -7
- package/src/flow/models/actions/JSCollectionActionModel.tsx +2 -7
- package/src/flow/models/actions/JSItemActionModel.tsx +2 -7
- package/src/flow/models/actions/JSRecordActionModel.tsx +2 -7
- package/src/flow/models/base/ActionModel.tsx +12 -1
- package/src/flow/models/base/ActionModelCore.tsx +6 -4
- package/src/flow/models/base/GridModel.tsx +38 -7
- package/src/flow/models/base/PageModel/PageModel.tsx +4 -1
- package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +28 -4
- package/src/flow/models/base/__tests__/ActionModel.test.ts +83 -0
- package/src/flow/models/base/__tests__/ActionModelCore.render.test.tsx +37 -0
- package/src/flow/models/base/__tests__/GridModel.dragSnapshotContainer.test.ts +239 -1
- package/src/flow/models/base/__tests__/transformRowsToSingleColumn.test.ts +48 -0
- package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +31 -3
- package/src/flow/models/blocks/filter-form/FilterFormJSActionModel.tsx +2 -7
- package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +77 -0
- package/src/flow/models/blocks/filter-form/fields/FilterFormCustomFieldModel.tsx +1 -1
- package/src/flow/models/blocks/filter-manager/flow-actions/__tests__/customizeFilterRender.test.tsx +56 -1
- package/src/flow/models/blocks/form/EditFormModel.tsx +1 -0
- package/src/flow/models/blocks/form/FormActionModel.tsx +1 -1
- package/src/flow/models/blocks/form/FormBlockModel.tsx +7 -0
- package/src/flow/models/blocks/form/JSFormActionModel.tsx +2 -7
- package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +17 -0
- package/src/flow/models/blocks/form/__tests__/submitHandler.test.ts +54 -1
- package/src/flow/models/blocks/form/submitHandler.ts +17 -3
- package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +940 -75
- package/src/flow/models/blocks/form/value-runtime/rules.ts +445 -13
- package/src/flow/models/blocks/form/value-runtime/runtime.ts +348 -13
- package/src/flow/models/blocks/form/value-runtime/types.ts +2 -2
- package/src/flow/models/blocks/js-block/JSBlock.tsx +225 -9
- package/src/flow/models/blocks/js-block/__tests__/JSBlockModel.test.tsx +150 -0
- package/src/flow/models/blocks/table/JSColumnModel.tsx +1 -9
- package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +36 -13
- package/src/flow/models/blocks/table/TableBlockModel.tsx +38 -17
- package/src/flow/models/blocks/table/TableColumnModel.tsx +36 -3
- package/src/flow/models/blocks/table/__tests__/TableActionsColumnModel.test.tsx +111 -1
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.dragSort.test.ts +127 -0
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.mobileSettingsButtons.test.tsx +78 -0
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.rowSelection.test.tsx +1 -0
- package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +159 -1
- package/src/flow/models/blocks/table/dragSort/dragSortHooks.tsx +28 -17
- package/src/flow/models/blocks/table/dragSort/dragSortSettings.ts +13 -6
- package/src/flow/models/blocks/table/dragSort/dragSortUtils.ts +15 -2
- package/src/flow/models/fields/AssociationFieldModel/AssociationFieldModel.tsx +1 -1
- package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +29 -6
- package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +16 -4
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.tsx +141 -19
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +13 -1
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableColumnModel.rowRecord.test.ts +197 -0
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableFieldModel.reset.test.ts +180 -0
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +6 -4
- package/src/flow/models/fields/AssociationFieldModel/__tests__/AssociationFieldModel.updateAssociation.test.ts +72 -0
- package/src/flow/models/fields/AssociationFieldModel/__tests__/RecordPickerFieldModel.itemContext.test.ts +23 -0
- package/src/flow/models/fields/ClickableFieldModel.tsx +5 -0
- package/src/flow/models/fields/DisplayDateTimeFieldModel.tsx +29 -1
- package/src/flow/models/fields/InputFieldModel.tsx +40 -2
- package/src/flow/models/fields/JSEditableFieldModel.tsx +2 -11
- package/src/flow/models/fields/JSFieldModel.tsx +2 -7
- package/src/flow/models/fields/JSItemModel.tsx +2 -14
- package/src/flow/models/fields/SelectFieldModel.tsx +6 -4
- package/src/flow/models/fields/TextareaFieldModel.tsx +49 -3
- package/src/flow/models/fields/__tests__/DisplayDateTimeFieldModel.test.tsx +96 -0
- package/src/flow/models/fields/__tests__/InputFieldModel.test.tsx +117 -1
- package/src/flow/models/fields/__tests__/SelectFieldModel.test.tsx +43 -0
- package/src/flow/models/fields/__tests__/TextareaFieldModel.test.tsx +100 -0
- package/src/flow/models/topbar/TopbarActionModel.tsx +93 -10
- package/src/flow/resolveViewParamsToViewList.tsx +11 -3
- package/src/flow/utils/__tests__/dateTimeFormat.test.ts +258 -0
- package/src/flow/utils/dataScopeFormValueClear.ts +218 -81
- package/src/flow/utils/dataScopeRowSnapshot.ts +616 -0
- package/src/flow/utils/dateTimeDisplayProps.ts +135 -0
- package/src/flow/utils/formValueDeps.ts +170 -0
- package/src/flow-compat/FieldValidation.tsx +122 -60
- package/src/flow-compat/Popover.tsx +43 -4
- package/src/flow-compat/__tests__/Popover.test.tsx +34 -0
- package/src/index.ts +8 -1
- package/src/layout-manager/__tests__/LayoutRoute.test.tsx +41 -1
- package/src/nocobase-buildin-plugin/index.tsx +44 -14
- package/src/settings-center/AdminSettingsLayout.tsx +10 -2
- package/src/settings-center/SystemSettingsPage.tsx +1 -2
- package/src/settings-center/plugin-manager/PluginCard.tsx +2 -2
- package/src/settings-center/plugin-manager/index.tsx +3 -0
- package/src/settings-center/utils.tsx +0 -6
|
@@ -15,18 +15,50 @@ import {
|
|
|
15
15
|
SubTableColumnModel,
|
|
16
16
|
getLatestSubTableRowRecord,
|
|
17
17
|
buildRowPathFromFieldIndex,
|
|
18
|
+
buildSubTableColumnNamePath,
|
|
18
19
|
isSubTableColumnConfiguredReadPretty,
|
|
19
20
|
getSubTableColumnTitleField,
|
|
20
21
|
getSubTableColumnReadPrettyFieldProps,
|
|
21
22
|
isSubTableColumnReadPretty,
|
|
22
23
|
} from '../SubTableColumnModel';
|
|
23
24
|
|
|
25
|
+
function createMockCollection(name: string, fields: any[] = []) {
|
|
26
|
+
const collection: any = {
|
|
27
|
+
name,
|
|
28
|
+
title: name,
|
|
29
|
+
dataSourceKey: 'main',
|
|
30
|
+
filterTargetKey: 'id',
|
|
31
|
+
fields,
|
|
32
|
+
getFields: () => collection.fields,
|
|
33
|
+
getField: (fieldName: string) => collection.fields.find((field) => field.name === fieldName),
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
collection.fields.forEach((field) => {
|
|
37
|
+
field.collection = field.collection ?? collection;
|
|
38
|
+
field.filterable = field.filterable ?? true;
|
|
39
|
+
field.isAssociationField = field.isAssociationField ?? (() => false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return collection;
|
|
43
|
+
}
|
|
44
|
+
|
|
24
45
|
describe('SubTableColumnModel row record helpers', () => {
|
|
25
46
|
it('builds the row path from fieldIndex entries', () => {
|
|
26
47
|
expect(buildRowPathFromFieldIndex(['roles:0'])).toEqual(['roles', 0]);
|
|
27
48
|
expect(buildRowPathFromFieldIndex(['users:1', 'roles:2'])).toEqual(['users', 1, 'roles', 2]);
|
|
28
49
|
});
|
|
29
50
|
|
|
51
|
+
it('builds absolute form item paths for nested sub-table columns', () => {
|
|
52
|
+
expect(buildSubTableColumnNamePath(['roles'], 0, 'name', ['roles:0'])).toEqual(['roles', 0, 'name']);
|
|
53
|
+
expect(buildSubTableColumnNamePath(['orders', 'lines'], 1, 'product', ['orders:0', 'lines:1'])).toEqual([
|
|
54
|
+
'orders',
|
|
55
|
+
0,
|
|
56
|
+
'lines',
|
|
57
|
+
1,
|
|
58
|
+
'product',
|
|
59
|
+
]);
|
|
60
|
+
});
|
|
61
|
+
|
|
30
62
|
it('prefers the latest row value from form over the fallback record', () => {
|
|
31
63
|
const form = {
|
|
32
64
|
getFieldValue: vi.fn((path: any) => {
|
|
@@ -51,6 +83,171 @@ describe('SubTableColumnModel row record helpers', () => {
|
|
|
51
83
|
expect(getLatestSubTableRowRecord(form, ['roles:0'], fallback)).toBe(fallback);
|
|
52
84
|
});
|
|
53
85
|
|
|
86
|
+
it('provides current item meta for sub-table column data scope variables', async () => {
|
|
87
|
+
const engine = new FlowEngine();
|
|
88
|
+
engine.registerModels({ SubTableColumnModel });
|
|
89
|
+
|
|
90
|
+
const tagsCollection = createMockCollection('tags', [
|
|
91
|
+
{ name: 'id', title: 'ID', type: 'integer', interface: 'integer' },
|
|
92
|
+
{ name: 'name', title: 'Name', type: 'string', interface: 'input' },
|
|
93
|
+
]);
|
|
94
|
+
const rolesCollection = createMockCollection('roles', [
|
|
95
|
+
{ name: 'id', title: 'ID', type: 'integer', interface: 'integer' },
|
|
96
|
+
{ name: 'name', title: 'Name', type: 'string', interface: 'input' },
|
|
97
|
+
{
|
|
98
|
+
name: 'tags',
|
|
99
|
+
title: 'Tags',
|
|
100
|
+
type: 'hasMany',
|
|
101
|
+
interface: 'o2m',
|
|
102
|
+
target: 'tags',
|
|
103
|
+
targetCollection: tagsCollection,
|
|
104
|
+
isAssociationField: () => true,
|
|
105
|
+
},
|
|
106
|
+
]);
|
|
107
|
+
const usersCollection = createMockCollection('users', [
|
|
108
|
+
{ name: 'id', title: 'ID', type: 'integer', interface: 'integer' },
|
|
109
|
+
{ name: 'nickname', title: 'Nickname', type: 'string', interface: 'input' },
|
|
110
|
+
]);
|
|
111
|
+
const rolesField = {
|
|
112
|
+
name: 'roles',
|
|
113
|
+
title: 'Roles',
|
|
114
|
+
type: 'hasMany',
|
|
115
|
+
interface: 'o2m',
|
|
116
|
+
collection: usersCollection,
|
|
117
|
+
target: 'roles',
|
|
118
|
+
targetCollection: rolesCollection,
|
|
119
|
+
isAssociationField: () => true,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const form = {
|
|
123
|
+
getFieldValue: vi.fn((path: any) => {
|
|
124
|
+
if (JSON.stringify(path) === JSON.stringify(['roles', 0])) {
|
|
125
|
+
return { id: 11, name: 'Admin', tags: [{ id: 7 }] };
|
|
126
|
+
}
|
|
127
|
+
}),
|
|
128
|
+
};
|
|
129
|
+
const blockModel: any = engine.createModel({ use: 'FlowModel', uid: 'form-block', structure: {} as any });
|
|
130
|
+
blockModel.context.defineProperty('form', { value: form });
|
|
131
|
+
blockModel.context.defineProperty('formValues', {
|
|
132
|
+
value: {
|
|
133
|
+
id: 1,
|
|
134
|
+
nickname: 'Alice',
|
|
135
|
+
roles: [{ id: 11, name: 'Admin' }],
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const subTableFieldModel: any = engine.createModel({
|
|
140
|
+
use: 'FlowModel',
|
|
141
|
+
uid: 'users.roles',
|
|
142
|
+
structure: {} as any,
|
|
143
|
+
});
|
|
144
|
+
subTableFieldModel.collection = rolesCollection;
|
|
145
|
+
subTableFieldModel.setProps({ value: [{ id: 11, name: 'Admin' }] });
|
|
146
|
+
subTableFieldModel.context.defineProperty('collectionField', { value: rolesField });
|
|
147
|
+
subTableFieldModel.context.defineProperty('fieldPath', { value: 'roles' });
|
|
148
|
+
subTableFieldModel.context.defineProperty('blockModel', { value: blockModel });
|
|
149
|
+
|
|
150
|
+
const column: any = engine.createModel({
|
|
151
|
+
use: 'SubTableColumnModel',
|
|
152
|
+
uid: 'users.roles.tags',
|
|
153
|
+
stepParams: {
|
|
154
|
+
fieldSettings: {
|
|
155
|
+
init: {
|
|
156
|
+
fieldPath: 'roles.tags',
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
structure: {} as any,
|
|
161
|
+
});
|
|
162
|
+
column.setParent(subTableFieldModel);
|
|
163
|
+
column.context.defineProperty('blockModel', { value: blockModel });
|
|
164
|
+
column.subModels = { field: [] };
|
|
165
|
+
|
|
166
|
+
const designTimeItemOptions = column.context.getPropertyOptions('item');
|
|
167
|
+
expect(typeof designTimeItemOptions.meta).toBe('function');
|
|
168
|
+
const designTimeMeta = await designTimeItemOptions.meta();
|
|
169
|
+
expect(designTimeMeta.properties.value.title).toBe('Attributes');
|
|
170
|
+
expect(designTimeMeta.properties.parentItem.properties.value.title).toBe('Attributes');
|
|
171
|
+
|
|
172
|
+
const renderCell = column.renderItem();
|
|
173
|
+
renderCell({
|
|
174
|
+
id: 'cell-roles-tags-0',
|
|
175
|
+
rowIdx: 0,
|
|
176
|
+
record: { id: 11, name: 'Stale admin', tags: [{ id: 7 }] },
|
|
177
|
+
parentItem: {
|
|
178
|
+
value: { id: 1, nickname: 'Alice' },
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const [rowFork] = Array.from(column.forks ?? []) as any[];
|
|
183
|
+
const rowItemOptions = rowFork.context.getPropertyOptions('item');
|
|
184
|
+
expect(typeof rowItemOptions.meta).toBe('function');
|
|
185
|
+
expect(rowItemOptions.resolveOnServer('value.tags.name')).toBe(true);
|
|
186
|
+
|
|
187
|
+
const rowMeta = await rowItemOptions.meta();
|
|
188
|
+
expect(rowMeta.properties.value.title).toBe('Attributes');
|
|
189
|
+
expect(rowMeta.properties.parentItem.properties.value.title).toBe('Attributes');
|
|
190
|
+
expect(rowFork.context.item).toMatchObject({
|
|
191
|
+
index: 0,
|
|
192
|
+
length: 1,
|
|
193
|
+
value: { id: 11, name: 'Admin', tags: [{ id: 7 }] },
|
|
194
|
+
parentItem: {
|
|
195
|
+
value: { id: 1, nickname: 'Alice' },
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('detects variable data scope columns for immediate row commits', () => {
|
|
201
|
+
const engine = new FlowEngine();
|
|
202
|
+
engine.registerModels({ SubTableColumnModel });
|
|
203
|
+
|
|
204
|
+
const inputColumn: any = engine.createModel({
|
|
205
|
+
use: 'SubTableColumnModel',
|
|
206
|
+
uid: 'users.roles.name',
|
|
207
|
+
structure: {} as any,
|
|
208
|
+
});
|
|
209
|
+
const scopedColumn: any = engine.createModel({
|
|
210
|
+
use: 'SubTableColumnModel',
|
|
211
|
+
uid: 'users.roles.tags',
|
|
212
|
+
structure: {} as any,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
inputColumn.subModels = {
|
|
216
|
+
field: {
|
|
217
|
+
getStepParams: vi.fn(() => undefined),
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
scopedColumn.subModels = {
|
|
221
|
+
field: {
|
|
222
|
+
getStepParams: vi.fn((flowKey: string, stepKey: string) => {
|
|
223
|
+
if (flowKey !== 'selectSettings' || stepKey !== 'dataScope') {
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
filter: {
|
|
228
|
+
logic: '$and',
|
|
229
|
+
items: [{ path: 'name', operator: '$eq', value: '{{ ctx.item.value.name }}' }],
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
}),
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const parent: any = engine.createModel({
|
|
237
|
+
use: 'FlowModel',
|
|
238
|
+
uid: 'users.roles-table',
|
|
239
|
+
structure: {} as any,
|
|
240
|
+
});
|
|
241
|
+
parent.mapSubModels = vi.fn((key: string, iterator: (column: SubTableColumnModel) => unknown) => {
|
|
242
|
+
if (key !== 'columns') return [];
|
|
243
|
+
return [inputColumn, scopedColumn].map(iterator);
|
|
244
|
+
});
|
|
245
|
+
inputColumn.setParent(parent);
|
|
246
|
+
scopedColumn.setParent(parent);
|
|
247
|
+
|
|
248
|
+
expect(inputColumn.hasFormValueDrivenDataScopeColumn).toBe(true);
|
|
249
|
+
});
|
|
250
|
+
|
|
54
251
|
it('treats a display-only column pattern as read-pretty mode', () => {
|
|
55
252
|
expect(isSubTableColumnReadPretty({ props: { pattern: 'readPretty' } })).toBe(true);
|
|
56
253
|
expect(isSubTableColumnReadPretty({ props: { readPretty: true } })).toBe(true);
|
|
@@ -0,0 +1,180 @@
|
|
|
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 React from 'react';
|
|
11
|
+
import { act, render } from '@nocobase/test/client';
|
|
12
|
+
import { FlowEngine, type FlowModel } from '@nocobase/flow-engine';
|
|
13
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
14
|
+
import { PopupSubTableFieldModel } from '../../PopupSubTableFieldModel';
|
|
15
|
+
import { SubTableFieldModel } from '..';
|
|
16
|
+
|
|
17
|
+
vi.mock('react-i18next', async (importOriginal) => ({
|
|
18
|
+
...(await importOriginal<any>()),
|
|
19
|
+
useTranslation: () => ({
|
|
20
|
+
t: (value: string) => value,
|
|
21
|
+
}),
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
vi.mock('antd', async () => {
|
|
25
|
+
const actual = await vi.importActual<any>('antd');
|
|
26
|
+
return {
|
|
27
|
+
...actual,
|
|
28
|
+
Table: ({ dataSource = [], columns = [] }: any) =>
|
|
29
|
+
React.createElement(
|
|
30
|
+
'div',
|
|
31
|
+
{ 'data-testid': 'subtable' },
|
|
32
|
+
dataSource.map((record: any, rowIdx: number) =>
|
|
33
|
+
React.createElement(
|
|
34
|
+
'div',
|
|
35
|
+
{ 'data-testid': `row-${rowIdx}`, key: record.__index__ || rowIdx },
|
|
36
|
+
columns.map((column: any) =>
|
|
37
|
+
React.createElement(
|
|
38
|
+
'div',
|
|
39
|
+
{ 'data-testid': `cell-${rowIdx}-${String(column.dataIndex || column.key)}`, key: column.key },
|
|
40
|
+
column.render?.(record[column.dataIndex], record, rowIdx),
|
|
41
|
+
),
|
|
42
|
+
),
|
|
43
|
+
),
|
|
44
|
+
),
|
|
45
|
+
),
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
function createSubTableFieldModel(props: Record<string, unknown> = {}) {
|
|
50
|
+
const engine = new FlowEngine();
|
|
51
|
+
engine.registerModels({ SubTableFieldModel });
|
|
52
|
+
const setFieldValue = vi.fn();
|
|
53
|
+
|
|
54
|
+
const blockModel = engine.createModel<FlowModel>({
|
|
55
|
+
use: 'FlowModel',
|
|
56
|
+
uid: 'form-block',
|
|
57
|
+
});
|
|
58
|
+
(blockModel as FlowModel & { setFieldValue: typeof setFieldValue }).setFieldValue = setFieldValue;
|
|
59
|
+
const parent = engine.createModel<FlowModel>({
|
|
60
|
+
use: 'FlowModel',
|
|
61
|
+
uid: 'form-item',
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
parent.context.defineProperty('blockModel', { value: blockModel });
|
|
65
|
+
parent.context.defineProperty('collectionField', {
|
|
66
|
+
value: {
|
|
67
|
+
target: 'roles',
|
|
68
|
+
targetCollection: {
|
|
69
|
+
filterTargetKey: 'id',
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
parent.context.defineProperty('fieldPathArray', { value: ['roles'] });
|
|
74
|
+
|
|
75
|
+
const model = engine.createModel<SubTableFieldModel>({
|
|
76
|
+
use: 'SubTableFieldModel',
|
|
77
|
+
uid: 'roles-subtable',
|
|
78
|
+
parentId: parent.uid,
|
|
79
|
+
props,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return { blockModel, model, setFieldValue };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function createPopupSubTableFieldModel(props: Record<string, unknown> = {}) {
|
|
86
|
+
const engine = new FlowEngine();
|
|
87
|
+
engine.registerModels({ PopupSubTableFieldModel });
|
|
88
|
+
const setFieldValue = vi.fn();
|
|
89
|
+
|
|
90
|
+
const blockModel = engine.createModel<FlowModel>({
|
|
91
|
+
use: 'FlowModel',
|
|
92
|
+
uid: 'popup-form-block',
|
|
93
|
+
});
|
|
94
|
+
(blockModel as FlowModel & { setFieldValue: typeof setFieldValue }).setFieldValue = setFieldValue;
|
|
95
|
+
const parent = engine.createModel<FlowModel>({
|
|
96
|
+
use: 'FlowModel',
|
|
97
|
+
uid: 'popup-form-item',
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
parent.context.defineProperty('blockModel', { value: blockModel });
|
|
101
|
+
parent.context.defineProperty('collectionField', {
|
|
102
|
+
value: {
|
|
103
|
+
target: 'roles',
|
|
104
|
+
targetCollection: {
|
|
105
|
+
filterTargetKey: 'id',
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
parent.context.defineProperty('fieldPathArray', { value: ['roles'] });
|
|
110
|
+
|
|
111
|
+
const model = engine.createModel<PopupSubTableFieldModel>({
|
|
112
|
+
use: 'PopupSubTableFieldModel',
|
|
113
|
+
uid: 'roles-popup-subtable',
|
|
114
|
+
parentId: parent.uid,
|
|
115
|
+
props,
|
|
116
|
+
});
|
|
117
|
+
(model.subModels as Record<string, unknown>).subTableColumns = [];
|
|
118
|
+
await model.onDispatchEventStart('beforeRender');
|
|
119
|
+
|
|
120
|
+
return { blockModel, model, setFieldValue };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
describe('SubTable field reset', () => {
|
|
124
|
+
it('resets the rendered fork through its own form field path', () => {
|
|
125
|
+
const onChange = vi.fn();
|
|
126
|
+
const { blockModel, model, setFieldValue } = createSubTableFieldModel({
|
|
127
|
+
onChange,
|
|
128
|
+
value: [{ id: 1 }],
|
|
129
|
+
});
|
|
130
|
+
const fork = model.createFork({ value: [{ id: 1 }] }, 'orders.0.lines');
|
|
131
|
+
fork.context.defineProperty('fieldPathArray', { value: ['orders', 0, 'lines'] });
|
|
132
|
+
|
|
133
|
+
render(React.createElement(React.Fragment, null, fork.render()));
|
|
134
|
+
|
|
135
|
+
act(() => {
|
|
136
|
+
blockModel.emitter.emit('onFieldReset');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
expect(fork.props.value).toEqual([]);
|
|
140
|
+
expect(setFieldValue).toHaveBeenCalledWith(['orders', 0, 'lines'], []);
|
|
141
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('resets the non-forked model through the parent form field path', () => {
|
|
145
|
+
const onChange = vi.fn();
|
|
146
|
+
const { blockModel, model, setFieldValue } = createSubTableFieldModel({
|
|
147
|
+
onChange,
|
|
148
|
+
value: [{ id: 1 }],
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
render(React.createElement(React.Fragment, null, model.render()));
|
|
152
|
+
|
|
153
|
+
act(() => {
|
|
154
|
+
blockModel.emitter.emit('onFieldReset');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
expect(model.props.value).toEqual([]);
|
|
158
|
+
expect(setFieldValue).toHaveBeenCalledWith(['roles'], []);
|
|
159
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('resets popup sub-table form values without relying on onChange', async () => {
|
|
163
|
+
const onChange = vi.fn();
|
|
164
|
+
const { blockModel, model, setFieldValue } = await createPopupSubTableFieldModel({
|
|
165
|
+
onChange,
|
|
166
|
+
pageSize: 10,
|
|
167
|
+
value: [{ id: 1 }],
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
render(React.createElement(React.Fragment, null, model.render()));
|
|
171
|
+
|
|
172
|
+
act(() => {
|
|
173
|
+
blockModel.emitter.emit('onFieldReset');
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
expect(model.props.value).toEqual([]);
|
|
177
|
+
expect(setFieldValue).toHaveBeenCalledWith(['roles'], []);
|
|
178
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
179
|
+
});
|
|
180
|
+
});
|
|
@@ -112,6 +112,11 @@ export class SubTableFieldModel extends AssociationFieldModel {
|
|
|
112
112
|
};
|
|
113
113
|
const isConfigMode = !!this.context.flowSettingsEnabled;
|
|
114
114
|
const fieldPathArray = this.context.fieldPathArray ?? this.parent?.context?.fieldPathArray;
|
|
115
|
+
const onResetFieldValue = () => {
|
|
116
|
+
const value = [];
|
|
117
|
+
this.setProps({ value });
|
|
118
|
+
this.context.blockModel?.setFieldValue?.(fieldPathArray, value);
|
|
119
|
+
};
|
|
115
120
|
return (
|
|
116
121
|
<SubTableField
|
|
117
122
|
{...this.props}
|
|
@@ -124,6 +129,7 @@ export class SubTableFieldModel extends AssociationFieldModel {
|
|
|
124
129
|
formValuesChangeEmitter={this.context.blockModel?.emitter}
|
|
125
130
|
fieldPathArray={fieldPathArray}
|
|
126
131
|
getCurrentValue={() => this.getCurrentValue()}
|
|
132
|
+
onResetFieldValue={onResetFieldValue}
|
|
127
133
|
/>
|
|
128
134
|
);
|
|
129
135
|
}
|
|
@@ -136,10 +142,6 @@ export class SubTableFieldModel extends AssociationFieldModel {
|
|
|
136
142
|
this.context.defineProperty('collection', {
|
|
137
143
|
get: () => this.context.collectionField.targetCollection,
|
|
138
144
|
});
|
|
139
|
-
// 监听表单reset
|
|
140
|
-
this.context.blockModel.emitter.on('onFieldReset', () => {
|
|
141
|
-
this.props.onChange([]);
|
|
142
|
-
});
|
|
143
145
|
this.onSelectExitRecordClick = (setCurrentPage, currentPageSize) => {
|
|
144
146
|
this.setCurrentPage = setCurrentPage;
|
|
145
147
|
this.currentPageSize = currentPageSize;
|
|
@@ -0,0 +1,72 @@
|
|
|
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, FlowModel } from '@nocobase/flow-engine';
|
|
11
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
12
|
+
import { AssociationFieldModel } from '../AssociationFieldModel';
|
|
13
|
+
|
|
14
|
+
class DefaultAssociationFieldModel extends AssociationFieldModel {}
|
|
15
|
+
|
|
16
|
+
class UpdateAssociationFieldModel extends AssociationFieldModel {
|
|
17
|
+
updateAssociation = true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function runAssociationFieldInit(FieldModelClass: typeof AssociationFieldModel) {
|
|
21
|
+
const engine = new FlowEngine();
|
|
22
|
+
engine.registerModels({
|
|
23
|
+
DefaultAssociationFieldModel,
|
|
24
|
+
UpdateAssociationFieldModel,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const addUpdateAssociationValues = vi.fn();
|
|
28
|
+
const blockModel = engine.createModel<FlowModel>({
|
|
29
|
+
use: 'FlowModel',
|
|
30
|
+
uid: 'form-block',
|
|
31
|
+
});
|
|
32
|
+
blockModel.context.defineProperty('resource', {
|
|
33
|
+
value: { addUpdateAssociationValues },
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const formItem = engine.createModel<FlowModel>({
|
|
37
|
+
use: 'FlowModel',
|
|
38
|
+
uid: 'form-item',
|
|
39
|
+
});
|
|
40
|
+
(formItem as FlowModel & { fieldPath: string }).fieldPath = 'author';
|
|
41
|
+
formItem.context.defineProperty('blockModel', { value: blockModel });
|
|
42
|
+
|
|
43
|
+
const field = engine.createModel<AssociationFieldModel>({
|
|
44
|
+
use: FieldModelClass,
|
|
45
|
+
uid: 'association-field',
|
|
46
|
+
parentId: formItem.uid,
|
|
47
|
+
});
|
|
48
|
+
field.context.defineProperty('collectionField', {
|
|
49
|
+
value: {
|
|
50
|
+
dataSourceKey: 'main',
|
|
51
|
+
target: 'users',
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
await field.applyFlow('AssociationFieldInit');
|
|
56
|
+
|
|
57
|
+
return addUpdateAssociationValues;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
describe('AssociationFieldModel updateAssociation', () => {
|
|
61
|
+
it('does not add updateAssociationValues by default', async () => {
|
|
62
|
+
const addUpdateAssociationValues = await runAssociationFieldInit(DefaultAssociationFieldModel);
|
|
63
|
+
|
|
64
|
+
expect(addUpdateAssociationValues).not.toHaveBeenCalled();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('adds updateAssociationValues for opt-in association fields', async () => {
|
|
68
|
+
const addUpdateAssociationValues = await runAssociationFieldInit(UpdateAssociationFieldModel);
|
|
69
|
+
|
|
70
|
+
expect(addUpdateAssociationValues).toHaveBeenCalledWith('author');
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
normalizeRecordPickerValue,
|
|
29
29
|
shouldClearRecordPickerValueOnMultipleChange,
|
|
30
30
|
} from '../RecordPickerFieldModel';
|
|
31
|
+
import { getAssociationHydrationNamePath, getAssociationHydrationSetterContext } from '../RecordSelectFieldModel';
|
|
31
32
|
|
|
32
33
|
function createMockCollection() {
|
|
33
34
|
return {
|
|
@@ -93,6 +94,28 @@ describe('RecordPickerFieldModel item context', () => {
|
|
|
93
94
|
expect(shouldClearRecordPickerValueOnMultipleChange({ type: 'belongsTo' }, true, false)).toBe(false);
|
|
94
95
|
});
|
|
95
96
|
|
|
97
|
+
it('uses the current field context for association hydration writes', () => {
|
|
98
|
+
const setFormValue = vi.fn();
|
|
99
|
+
const blockSetFormValue = vi.fn();
|
|
100
|
+
const model = {
|
|
101
|
+
props: {
|
|
102
|
+
name: 'orders',
|
|
103
|
+
},
|
|
104
|
+
context: {
|
|
105
|
+
fieldPathArray: ['orders', 0, 'lines', 1, 'product'],
|
|
106
|
+
setFormValue,
|
|
107
|
+
blockModel: {
|
|
108
|
+
context: {
|
|
109
|
+
setFormValue: blockSetFormValue,
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
expect(getAssociationHydrationNamePath(model)).toEqual(['orders', 0, 'lines', 1, 'product']);
|
|
116
|
+
expect(getAssociationHydrationSetterContext(model)).toBe(model.context);
|
|
117
|
+
});
|
|
118
|
+
|
|
96
119
|
it('itemChain helpers: createParentItemAccessorsFromInputArgs works', () => {
|
|
97
120
|
const inputArgs = {
|
|
98
121
|
parentItem: { value: { id: 1 } },
|
|
@@ -8,14 +8,42 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { DisplayItemModel, tExpr } from '@nocobase/flow-engine';
|
|
11
|
+
import { getDateTimeFormat, getPickerFormat } from '@nocobase/utils/client';
|
|
11
12
|
import dayjs from 'dayjs';
|
|
12
13
|
import React from 'react';
|
|
13
14
|
import { ClickableFieldModel } from './ClickableFieldModel';
|
|
14
15
|
|
|
16
|
+
const stripTimeFromFormat = (format?: string) =>
|
|
17
|
+
format ? format.replace(/\s*[Hh]{1,2}:mm(?::ss)?(?:\.SSS)?(?:\s*[aA])?/g, '').trim() : format;
|
|
18
|
+
|
|
19
|
+
interface DisplayDateTimeFormatProps {
|
|
20
|
+
dateOnly?: boolean;
|
|
21
|
+
picker?: string;
|
|
22
|
+
format?: string;
|
|
23
|
+
dateFormat?: string;
|
|
24
|
+
showTime?: boolean;
|
|
25
|
+
timeFormat?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const resolveDisplayDateTimeFormat = (props: DisplayDateTimeFormatProps) => {
|
|
29
|
+
const { dateOnly, picker = 'date', format, dateFormat, showTime, timeFormat } = props;
|
|
30
|
+
const normalizedFormat = stripTimeFromFormat(format);
|
|
31
|
+
if (picker !== 'date') {
|
|
32
|
+
return dateFormat || normalizedFormat || getPickerFormat(picker);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!dateOnly && !dateFormat && typeof showTime === 'undefined' && !timeFormat && normalizedFormat) {
|
|
36
|
+
return format;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const normalizedDateFormat = dateFormat || normalizedFormat || getPickerFormat(picker);
|
|
40
|
+
return getDateTimeFormat(picker, normalizedDateFormat, showTime, timeFormat);
|
|
41
|
+
};
|
|
42
|
+
|
|
15
43
|
export class DisplayDateTimeFieldModel extends ClickableFieldModel {
|
|
16
44
|
public renderComponent(value) {
|
|
17
45
|
const { className, style } = this.props;
|
|
18
|
-
const finalFormat = this.props
|
|
46
|
+
const finalFormat = resolveDisplayDateTimeFormat(this.props);
|
|
19
47
|
let formattedValue = '';
|
|
20
48
|
if (value) {
|
|
21
49
|
const day = dayjs(value);
|
|
@@ -9,18 +9,56 @@
|
|
|
9
9
|
|
|
10
10
|
import { EditableItemModel, FilterableItemModel, tExpr } from '@nocobase/flow-engine';
|
|
11
11
|
import { Input } from 'antd';
|
|
12
|
-
import
|
|
12
|
+
import type { InputProps } from 'antd';
|
|
13
|
+
import React, { useEffect, useState } from 'react';
|
|
13
14
|
import { customAlphabet as Alphabet } from 'nanoid';
|
|
14
15
|
import { FieldModel } from '../base/FieldModel';
|
|
15
16
|
import { ScanInput } from '../../../components/form/ScanInput';
|
|
16
17
|
|
|
18
|
+
function IMESafeInput(props: InputProps) {
|
|
19
|
+
const { value, onChange, onCompositionStart, onCompositionEnd, ...rest } = props;
|
|
20
|
+
const [inputValue, setInputValue] = useState<InputProps['value']>(() => normalizeInputValue(value));
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
setInputValue(normalizeInputValue(value));
|
|
24
|
+
}, [value]);
|
|
25
|
+
|
|
26
|
+
const getEventValue = (event: React.ChangeEvent<HTMLInputElement> | React.CompositionEvent<HTMLInputElement>) =>
|
|
27
|
+
event.currentTarget.value;
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Input
|
|
31
|
+
{...rest}
|
|
32
|
+
value={inputValue}
|
|
33
|
+
onChange={(event) => {
|
|
34
|
+
setInputValue(getEventValue(event));
|
|
35
|
+
onChange?.(event);
|
|
36
|
+
}}
|
|
37
|
+
onCompositionStart={(event) => {
|
|
38
|
+
setInputValue(getEventValue(event));
|
|
39
|
+
onCompositionStart?.(event);
|
|
40
|
+
}}
|
|
41
|
+
onCompositionEnd={(event) => {
|
|
42
|
+
setInputValue(getEventValue(event));
|
|
43
|
+
onCompositionEnd?.(event);
|
|
44
|
+
}}
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function normalizeInputValue(nextValue: unknown): InputProps['value'] {
|
|
50
|
+
if (typeof nextValue === 'bigint') return String(nextValue);
|
|
51
|
+
if (nextValue == null) return '';
|
|
52
|
+
return nextValue as InputProps['value'];
|
|
53
|
+
}
|
|
54
|
+
|
|
17
55
|
export class InputFieldModel extends FieldModel {
|
|
18
56
|
render() {
|
|
19
57
|
if (this.props.enableScan) {
|
|
20
58
|
return <ScanInput {...this.props} />;
|
|
21
59
|
}
|
|
22
60
|
const { enableScan, disableManualInput, ...inputProps } = this.props;
|
|
23
|
-
return <
|
|
61
|
+
return <IMESafeInput {...inputProps} />;
|
|
24
62
|
}
|
|
25
63
|
}
|
|
26
64
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { ElementProxy, tExpr
|
|
10
|
+
import { ElementProxy, tExpr } from '@nocobase/flow-engine';
|
|
11
11
|
import React, { useEffect, useMemo, useRef } from 'react';
|
|
12
12
|
import { Input } from 'antd';
|
|
13
13
|
import { FieldModel } from '../base/FieldModel';
|
|
@@ -398,16 +398,7 @@ JSEditableFieldModel.registerFlow({
|
|
|
398
398
|
get: () => isReadOnlyMode(ctx.model),
|
|
399
399
|
cache: false,
|
|
400
400
|
});
|
|
401
|
-
|
|
402
|
-
await ctx.runjs(
|
|
403
|
-
code,
|
|
404
|
-
{
|
|
405
|
-
window: createSafeWindow({ navigator }),
|
|
406
|
-
document: createSafeDocument(),
|
|
407
|
-
navigator,
|
|
408
|
-
},
|
|
409
|
-
{ version },
|
|
410
|
-
);
|
|
401
|
+
await ctx.runjs(code, undefined, { version });
|
|
411
402
|
});
|
|
412
403
|
},
|
|
413
404
|
},
|