@nocobase/client-v2 2.1.0-alpha.45 → 2.1.0-alpha.47
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/authRedirect.d.ts +12 -0
- package/es/collection-manager/filter-operators/index.d.ts +1 -0
- package/es/components/form/TypedVariableInput.d.ts +75 -0
- package/es/components/form/filter/CollectionFilterItem.d.ts +5 -1
- package/es/components/form/filter/FilterValueInput.d.ts +5 -0
- package/es/components/form/index.d.ts +1 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +1 -0
- package/es/flow/components/ConditionBuilder.d.ts +1 -0
- package/es/flow/components/FieldAssignRulesEditor.d.ts +3 -1
- package/es/flow/components/FieldAssignValueInput.d.ts +1 -0
- package/es/flow/components/fieldAssignOptions.d.ts +10 -0
- package/es/flow/components/filter/LinkageFilterItem.d.ts +1 -0
- package/es/flow/components/filter/VariableFilterItem.d.ts +1 -0
- package/es/flow/components/filter/metaTreeAssociationDepth.d.ts +13 -0
- package/es/flow/internal/utils/operatorSchemaHelper.d.ts +26 -3
- package/es/flow/models/base/AssociationFieldGroupModel.d.ts +2 -33
- package/es/flow/models/blocks/assign-form/assignFieldValuesFlow.d.ts +8 -0
- package/es/flow/models/blocks/filter-manager/utils.d.ts +23 -1
- package/es/flow/models/blocks/table/TableColumnModel.d.ts +2 -0
- package/es/index.mjs +106 -106
- package/lib/index.js +106 -106
- package/package.json +7 -7
- package/src/__tests__/browserChecker.test.ts +103 -0
- package/src/__tests__/dataSourceRuntime.test.tsx +22 -1
- package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +5 -0
- package/src/authRedirect.ts +43 -0
- package/src/collection-field-interface/CollectionFieldInterface.ts +14 -7
- package/src/collection-manager/filter-operators/index.ts +29 -2
- package/src/components/README.md +48 -0
- package/src/components/README.zh-CN.md +48 -0
- package/src/components/form/TypedVariableInput.tsx +441 -0
- package/src/components/form/__tests__/TypedVariableInput.test.tsx +152 -0
- package/src/components/form/filter/CollectionFilterItem.tsx +10 -2
- package/src/components/form/filter/FilterValueInput.tsx +27 -2
- package/src/components/form/filter/__tests__/FilterValueInput.test.tsx +39 -0
- package/src/components/form/index.tsx +1 -0
- package/src/flow/__tests__/FlowRoute.test.tsx +8 -1
- package/src/flow/actions/__tests__/linkageRules.hiddenSync.restore.test.ts +100 -0
- package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +369 -2
- package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +23 -1
- package/src/flow/actions/dataScope.tsx +8 -1
- package/src/flow/actions/linkageRules.tsx +61 -5
- package/src/flow/actions/linkageRulesRefresh.tsx +2 -3
- package/src/flow/actions/setTargetDataScope.tsx +1 -1
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +15 -1
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +75 -1
- package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.test.ts +11 -0
- package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.ts +2 -4
- package/src/flow/common/Markdown/Edit.tsx +32 -4
- package/src/flow/components/ConditionBuilder.tsx +12 -2
- package/src/flow/components/DefaultValue.tsx +14 -8
- package/src/flow/components/FieldAssignRulesEditor.tsx +37 -46
- package/src/flow/components/FieldAssignValueInput.tsx +14 -14
- package/src/flow/components/__tests__/FieldAssignRulesEditor.test.tsx +112 -1
- package/src/flow/components/__tests__/fieldAssignOptions.test.ts +71 -1
- package/src/flow/components/code-editor/__tests__/runjsCompletions.test.ts +272 -27
- package/src/flow/components/code-editor/runjsCompletions.ts +490 -9
- package/src/flow/components/fieldAssignOptions.ts +23 -6
- package/src/flow/components/filter/LinkageFilterItem.tsx +26 -12
- package/src/flow/components/filter/VariableFilterItem.tsx +32 -13
- package/src/flow/components/filter/__tests__/fieldsToOptions.test.ts +48 -0
- package/src/flow/components/filter/__tests__/metaTreeAssociationDepth.test.ts +60 -0
- package/src/flow/components/filter/fieldsToOptions.ts +9 -4
- package/src/flow/components/filter/metaTreeAssociationDepth.ts +69 -0
- package/src/flow/internal/utils/operatorSchemaHelper.ts +112 -5
- package/src/flow/models/actions/FilterActionModel.tsx +15 -3
- package/src/flow/models/actions/__tests__/AssignFormRefill.test.ts +90 -0
- package/src/flow/models/base/AssociationFieldGroupModel.tsx +20 -3
- package/src/flow/models/base/GridModel.tsx +1 -1
- package/src/flow/models/base/PageModel/ChildPageModel.tsx +5 -1
- package/src/flow/models/base/PageModel/__tests__/ChildPageModel.test.ts +12 -0
- package/src/flow/models/base/__tests__/AssociationFieldGroupModel.test.ts +86 -0
- package/src/flow/models/base/__tests__/GridModel.render.test.tsx +53 -0
- package/src/flow/models/blocks/assign-form/AssignFormGridModel.tsx +12 -3
- package/src/flow/models/blocks/assign-form/AssignFormItemModel.tsx +14 -4
- package/src/flow/models/blocks/assign-form/__tests__/assignFieldValuesFlow.editor.test.tsx +216 -0
- package/src/flow/models/blocks/assign-form/assignFieldValuesFlow.tsx +142 -40
- package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +14 -12
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +6 -1
- package/src/flow/models/blocks/filter-manager/__tests__/getDefaultOperator.test.ts +35 -1
- package/src/flow/models/blocks/filter-manager/flow-actions/__tests__/customizeFilterRender.test.tsx +148 -0
- package/src/flow/models/blocks/filter-manager/flow-actions/__tests__/defaultOperator.test.tsx +85 -0
- package/src/flow/models/blocks/filter-manager/flow-actions/connectFields.tsx +48 -6
- package/src/flow/models/blocks/filter-manager/flow-actions/customizeFilterRender.tsx +14 -44
- package/src/flow/models/blocks/filter-manager/flow-actions/defaultOperator.tsx +3 -5
- package/src/flow/models/blocks/filter-manager/flow-actions/operatorComponentProps.tsx +2 -12
- package/src/flow/models/blocks/filter-manager/utils.ts +143 -4
- package/src/flow/models/blocks/form/FormAssociationItemModel.tsx +9 -0
- package/src/flow/models/blocks/form/FormItemModel.tsx +9 -0
- package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +17 -0
- package/src/flow/models/blocks/form/__tests__/FormItemModel.defineChildren.test.ts +27 -2
- package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +29 -0
- package/src/flow/models/blocks/form/value-runtime/runtime.ts +5 -1
- package/src/flow/models/blocks/table/TableColumnModel.tsx +15 -5
- package/src/flow/models/blocks/table/TableCustomColumnModel.tsx +2 -2
- package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +18 -0
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.tsx +6 -7
- package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +5 -3
- package/src/flow/models/fields/mobile-components/__tests__/MobileSelect.test.tsx +70 -0
|
@@ -1606,7 +1606,9 @@ const LinkageRulesUI = observer(
|
|
|
1606
1606
|
<div style={{ paddingLeft: 12 }}>
|
|
1607
1607
|
<FilterGroup
|
|
1608
1608
|
value={rule.condition}
|
|
1609
|
-
FilterItem={(props) =>
|
|
1609
|
+
FilterItem={(props) => (
|
|
1610
|
+
<LinkageFilterItem model={ctx.model} value={props.value} maxAssociationFieldDepth={2} />
|
|
1611
|
+
)}
|
|
1610
1612
|
/>
|
|
1611
1613
|
</div>
|
|
1612
1614
|
</div>
|
|
@@ -1766,6 +1768,7 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
1766
1768
|
const allModels: FlowModel[] = ctx.model.__allModels || (ctx.model.__allModels = []);
|
|
1767
1769
|
const modelsToApply = new Set<FlowModel>(allModels);
|
|
1768
1770
|
const patchPropsByModel = new Map<FlowModel, any>();
|
|
1771
|
+
const clearValueOnHiddenModelUids = new Set<string>();
|
|
1769
1772
|
const directValuePatches: Array<{ path: Array<string | number>; value: any; whenEmpty?: boolean }> = [];
|
|
1770
1773
|
const rootCollection = getCollectionFromModel((ctx.model as any)?.context?.blockModel ?? ctx.model);
|
|
1771
1774
|
const isSafeToWriteAssociationSubpath = (namePath: any): boolean => {
|
|
@@ -1876,6 +1879,16 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
1876
1879
|
...(whenEmpty ? { whenEmpty: true } : {}),
|
|
1877
1880
|
});
|
|
1878
1881
|
};
|
|
1882
|
+
const removePendingFormValuePatches = (path: any) => {
|
|
1883
|
+
const resolvedPath = resolveNamePathForPatch(path);
|
|
1884
|
+
if (!resolvedPath) return;
|
|
1885
|
+
const resolvedPathKey = namePathToPathKey(resolvedPath);
|
|
1886
|
+
for (let i = directValuePatches.length - 1; i >= 0; i--) {
|
|
1887
|
+
if (namePathToPathKey(directValuePatches[i].path) === resolvedPathKey) {
|
|
1888
|
+
directValuePatches.splice(i, 1);
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
};
|
|
1879
1892
|
|
|
1880
1893
|
const getModelTargetPathForPatch = (model: any): string | null => {
|
|
1881
1894
|
if (!model || typeof model !== 'object') return null;
|
|
@@ -1971,6 +1984,23 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
1971
1984
|
|
|
1972
1985
|
return out;
|
|
1973
1986
|
};
|
|
1987
|
+
const getModelTargetPathForHiddenClear = (model: any): string | Array<string | number> | null => {
|
|
1988
|
+
const fieldPathArray = normalizeNamePathForKey(model?.context?.fieldPathArray);
|
|
1989
|
+
const targetPath = getModelTargetPathForPatch(model);
|
|
1990
|
+
if (fieldPathArray) {
|
|
1991
|
+
const targetPathLastString = targetPath
|
|
1992
|
+
? ([...parsePathString(targetPath)].reverse().find((seg) => typeof seg === 'string') as string | undefined)
|
|
1993
|
+
: undefined;
|
|
1994
|
+
const fieldPathArrayLastString = [...fieldPathArray].reverse().find((seg) => typeof seg === 'string');
|
|
1995
|
+
if (!targetPathLastString || targetPathLastString === fieldPathArrayLastString) {
|
|
1996
|
+
return fieldPathArray;
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
if (!targetPath) return null;
|
|
2001
|
+
|
|
2002
|
+
return resolveIndexedRelativePath(targetPath, model?.context?.fieldIndex) || targetPath;
|
|
2003
|
+
};
|
|
1974
2004
|
const getModelTargetPathKeys = (model: any): Set<string> => {
|
|
1975
2005
|
const keys = new Set<string>();
|
|
1976
2006
|
const fieldPathArray = normalizeNamePathForKey(model?.context?.fieldPathArray);
|
|
@@ -2058,6 +2088,13 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
2058
2088
|
...props,
|
|
2059
2089
|
});
|
|
2060
2090
|
|
|
2091
|
+
if (
|
|
2092
|
+
(action.name === 'linkageSetFieldProps' || action.name === 'subFormLinkageSetFieldProps') &&
|
|
2093
|
+
props?.hiddenModel === true
|
|
2094
|
+
) {
|
|
2095
|
+
clearValueOnHiddenModelUids.add(model?.uid || String(model));
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2061
2098
|
if (allModels.indexOf(model) === -1) {
|
|
2062
2099
|
allModels.push(model);
|
|
2063
2100
|
}
|
|
@@ -2099,16 +2136,17 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
2099
2136
|
mergedByUid.forEach((model: any, uid) => {
|
|
2100
2137
|
const patchProps = mergedPropsByUid.get(uid) || {};
|
|
2101
2138
|
const newProps = { ...model.__originalProps, ...patchProps };
|
|
2102
|
-
const
|
|
2139
|
+
const prevHidden = !!model.hidden;
|
|
2140
|
+
const nextHidden = !!newProps.hiddenModel;
|
|
2103
2141
|
|
|
2104
2142
|
model.setProps(_.omit(newProps, ['hiddenModel', 'value', 'hiddenText']));
|
|
2105
2143
|
syncFieldOptionsToForks(model, patchProps);
|
|
2106
2144
|
if (typeof model.setHidden === 'function') {
|
|
2107
2145
|
model.setHidden(!!newProps.hiddenModel);
|
|
2108
2146
|
} else {
|
|
2109
|
-
model.hidden =
|
|
2110
|
-
if (
|
|
2111
|
-
hiddenStatePatches.push({ model, hidden:
|
|
2147
|
+
model.hidden = nextHidden;
|
|
2148
|
+
if (prevHidden !== nextHidden) {
|
|
2149
|
+
hiddenStatePatches.push({ model, hidden: nextHidden });
|
|
2112
2150
|
}
|
|
2113
2151
|
}
|
|
2114
2152
|
|
|
@@ -2144,6 +2182,24 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
2144
2182
|
}
|
|
2145
2183
|
}
|
|
2146
2184
|
|
|
2185
|
+
if (
|
|
2186
|
+
clearValueOnHiddenModelUids.has(uid) &&
|
|
2187
|
+
Object.prototype.hasOwnProperty.call(patchProps, 'hiddenModel') &&
|
|
2188
|
+
patchProps.hiddenModel === true
|
|
2189
|
+
) {
|
|
2190
|
+
const targetPath = getModelTargetPathForHiddenClear(model);
|
|
2191
|
+
if (!targetPath) {
|
|
2192
|
+
console.warn('[linkageRules] Skip clearing hidden field value due to missing target path', {
|
|
2193
|
+
flowKey: ctx.flowKey,
|
|
2194
|
+
modelUid: ctx.model?.uid,
|
|
2195
|
+
targetUid: model?.uid,
|
|
2196
|
+
});
|
|
2197
|
+
} else {
|
|
2198
|
+
removePendingFormValuePatches(targetPath);
|
|
2199
|
+
addFormValuePatch({ path: targetPath, value: undefined });
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2147
2203
|
model.__props = null;
|
|
2148
2204
|
});
|
|
2149
2205
|
hiddenStatePatches.forEach(({ model, hidden }) => {
|
|
@@ -41,16 +41,15 @@ export const linkageRulesRefresh = defineAction({
|
|
|
41
41
|
const flowSettingsEnabled = Boolean(
|
|
42
42
|
(ctx as any)?.flowSettingsEnabled || (model as any)?.context?.flowSettingsEnabled,
|
|
43
43
|
);
|
|
44
|
-
const
|
|
44
|
+
const hasForkWithFlow =
|
|
45
45
|
!model?.isFork &&
|
|
46
46
|
!!model?.forks?.size &&
|
|
47
47
|
Array.from(model?.forks || []).some((fork: any) => {
|
|
48
48
|
if (!fork || fork.disposed) return false;
|
|
49
|
-
if (!fork?.context?.ref?.current) return false;
|
|
50
49
|
return !!fork?.getFlow?.(flowKey);
|
|
51
50
|
});
|
|
52
51
|
const isMasterMounted = Boolean((model as any)?.context?.ref?.current);
|
|
53
|
-
if (
|
|
52
|
+
if (hasForkWithFlow && !isMasterMounted && !flowSettingsEnabled) {
|
|
54
53
|
return;
|
|
55
54
|
}
|
|
56
55
|
|
|
@@ -135,7 +135,7 @@ function SetTargetDataScope(props) {
|
|
|
135
135
|
return (
|
|
136
136
|
<FilterGroup
|
|
137
137
|
value={props.value}
|
|
138
|
-
FilterItem={(p) => <VariableFilterItem {...p} model={targetModel} rightAsVariable />}
|
|
138
|
+
FilterItem={(p) => <VariableFilterItem {...p} model={targetModel} rightAsVariable maxAssociationFieldDepth={2} />}
|
|
139
139
|
/>
|
|
140
140
|
);
|
|
141
141
|
}
|
|
@@ -55,6 +55,15 @@ const insertPositionToMethod = {
|
|
|
55
55
|
afterEnd: 'insertAfter',
|
|
56
56
|
} as const;
|
|
57
57
|
|
|
58
|
+
const omitMenuRuntimeProps = (props: Record<string, unknown> = {}) => {
|
|
59
|
+
const persistableProps = { ...props };
|
|
60
|
+
delete persistableProps.item;
|
|
61
|
+
delete persistableProps.dom;
|
|
62
|
+
delete persistableProps.options;
|
|
63
|
+
delete persistableProps.renderType;
|
|
64
|
+
return persistableProps;
|
|
65
|
+
};
|
|
66
|
+
|
|
58
67
|
export class AdminLayoutMenuItemModel extends FlowModel<AdminLayoutMenuItemStructure> {
|
|
59
68
|
private creationPersisted = false;
|
|
60
69
|
private persistedStateHydrated = false;
|
|
@@ -261,6 +270,12 @@ export class AdminLayoutMenuItemModel extends FlowModel<AdminLayoutMenuItemStruc
|
|
|
261
270
|
return currentFlowCount > 0 || initialFlowCount > 0;
|
|
262
271
|
}
|
|
263
272
|
|
|
273
|
+
serialize(): ReturnType<FlowModel['serialize']> {
|
|
274
|
+
const data = super.serialize();
|
|
275
|
+
data.props = omitMenuRuntimeProps(data.props);
|
|
276
|
+
return data;
|
|
277
|
+
}
|
|
278
|
+
|
|
264
279
|
setHidden(value: boolean) {
|
|
265
280
|
const previous = this.hidden;
|
|
266
281
|
super.setHidden(value);
|
|
@@ -698,7 +713,6 @@ AdminLayoutMenuItemModel.registerFlow({
|
|
|
698
713
|
AdminLayoutMenuItemModel.registerFlow({
|
|
699
714
|
key: 'menuSettings',
|
|
700
715
|
title: 'Menu settings',
|
|
701
|
-
manual: true,
|
|
702
716
|
steps: {
|
|
703
717
|
edit: {
|
|
704
718
|
title: 'Edit',
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import React from 'react';
|
|
11
11
|
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
12
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
12
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
13
13
|
import { FlowEngine, FlowEngineProvider } from '@nocobase/flow-engine';
|
|
14
14
|
import { observer } from '@nocobase/flow-engine';
|
|
15
15
|
import { MemoryRouter } from 'react-router-dom';
|
|
@@ -45,6 +45,9 @@ describe('AdminLayoutModel menu items', () => {
|
|
|
45
45
|
let modalConfirmMock: ReturnType<typeof vi.fn>;
|
|
46
46
|
|
|
47
47
|
beforeEach(() => {
|
|
48
|
+
// Fixtures mount the modern client under the `v2` segment; tell the
|
|
49
|
+
// runtime-prefix helper so v2-runtime detection matches.
|
|
50
|
+
(globalThis.window as any).__nocobase_modern_client_prefix__ = 'v2';
|
|
48
51
|
engine = new FlowEngine();
|
|
49
52
|
modalConfirmMock = vi.fn().mockResolvedValue(true);
|
|
50
53
|
engine.registerModels({
|
|
@@ -104,6 +107,10 @@ describe('AdminLayoutModel menu items', () => {
|
|
|
104
107
|
vi.spyOn(window, 'open').mockImplementation(() => null);
|
|
105
108
|
});
|
|
106
109
|
|
|
110
|
+
afterEach(() => {
|
|
111
|
+
delete (globalThis.window as any).__nocobase_modern_client_prefix__;
|
|
112
|
+
});
|
|
113
|
+
|
|
107
114
|
const createRoute = (options?: Partial<import('../../../../flow-compat').NocoBaseDesktopRoute>) => ({
|
|
108
115
|
id: 1,
|
|
109
116
|
title: 'Page 1',
|
|
@@ -950,6 +957,73 @@ describe('AdminLayoutModel menu items', () => {
|
|
|
950
957
|
});
|
|
951
958
|
});
|
|
952
959
|
|
|
960
|
+
it('should save menu linkage rules without serializing runtime render props', async () => {
|
|
961
|
+
type SerializedFlowModel = Record<string, unknown> & {
|
|
962
|
+
props?: Record<string, unknown>;
|
|
963
|
+
subModels?: unknown;
|
|
964
|
+
};
|
|
965
|
+
const save = vi.fn(
|
|
966
|
+
async (targetModel: { serialize: () => SerializedFlowModel }, options?: { onlyStepParams?: boolean }) => {
|
|
967
|
+
const data = targetModel.serialize();
|
|
968
|
+
if (options?.onlyStepParams) {
|
|
969
|
+
delete data.subModels;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
expect(data.props?.item).toBeUndefined();
|
|
973
|
+
expect(data.props?.dom).toBeUndefined();
|
|
974
|
+
expect(data.props?.options).toBeUndefined();
|
|
975
|
+
expect(data.props?.renderType).toBeUndefined();
|
|
976
|
+
expect(() => JSON.stringify(data)).not.toThrow();
|
|
977
|
+
return data;
|
|
978
|
+
},
|
|
979
|
+
);
|
|
980
|
+
const updateRoute = vi.fn().mockResolvedValue(undefined);
|
|
981
|
+
engine.setModelRepository({ save } as Parameters<FlowEngine['setModelRepository']>[0]);
|
|
982
|
+
engine.context.routeRepository.updateRoute = updateRoute;
|
|
983
|
+
|
|
984
|
+
const route = createRoute();
|
|
985
|
+
const model = engine.createModel<AdminLayoutMenuItemModel>({
|
|
986
|
+
uid: 'menu-item-linkage-runtime-props',
|
|
987
|
+
use: AdminLayoutMenuItemModel,
|
|
988
|
+
props: {
|
|
989
|
+
route,
|
|
990
|
+
},
|
|
991
|
+
});
|
|
992
|
+
const item = {
|
|
993
|
+
name: 'Page 1',
|
|
994
|
+
path: '/admin/page-1',
|
|
995
|
+
_route: route,
|
|
996
|
+
_model: model,
|
|
997
|
+
};
|
|
998
|
+
|
|
999
|
+
model.setProps({
|
|
1000
|
+
item,
|
|
1001
|
+
dom: React.createElement('span', null, 'Page 1'),
|
|
1002
|
+
renderType: 'item',
|
|
1003
|
+
options: { collapsed: false },
|
|
1004
|
+
});
|
|
1005
|
+
model.setStepParams('menuSettings', 'linkageRules', {
|
|
1006
|
+
value: [
|
|
1007
|
+
{
|
|
1008
|
+
key: 'r1',
|
|
1009
|
+
title: 'Hide menu item',
|
|
1010
|
+
enable: true,
|
|
1011
|
+
condition: { logic: '$and', items: [] },
|
|
1012
|
+
actions: [{ key: 'a1', name: 'linkageSetMenuItemProps', params: { value: 'hidden' } }],
|
|
1013
|
+
},
|
|
1014
|
+
],
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
await model.saveStepParams();
|
|
1018
|
+
|
|
1019
|
+
expect(save).toHaveBeenCalledWith(model, { onlyStepParams: true });
|
|
1020
|
+
expect(updateRoute).toHaveBeenCalledWith(1, {
|
|
1021
|
+
options: {
|
|
1022
|
+
hasPersistedMenuInstanceFlow: true,
|
|
1023
|
+
},
|
|
1024
|
+
});
|
|
1025
|
+
});
|
|
1026
|
+
|
|
953
1027
|
it('should clear persisted menu linkage rules when no persisted state remains', async () => {
|
|
954
1028
|
const saveModel = vi.spyOn(engine, 'saveModel').mockResolvedValue(undefined as any);
|
|
955
1029
|
const destroy = vi.fn().mockResolvedValue(true);
|
|
@@ -23,6 +23,17 @@ const app = {
|
|
|
23
23
|
},
|
|
24
24
|
} as any;
|
|
25
25
|
|
|
26
|
+
// These fixtures mount the modern client under the `v2` segment; tell the
|
|
27
|
+
// runtime-prefix helper so `isV2AdminRuntime` detects it (the server injects
|
|
28
|
+
// this in production).
|
|
29
|
+
beforeAll(() => {
|
|
30
|
+
(window as any).__nocobase_modern_client_prefix__ = 'v2';
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterAll(() => {
|
|
34
|
+
delete (window as any).__nocobase_modern_client_prefix__;
|
|
35
|
+
});
|
|
36
|
+
|
|
26
37
|
describe('resolveAdminRouteRuntimeTarget', () => {
|
|
27
38
|
it('should resolve flowPage to v2 spa runtime target', () => {
|
|
28
39
|
expect(
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { getV2EffectiveBasePath } from '../../../authRedirect';
|
|
10
|
+
import { getModernClientPrefix, getV2EffectiveBasePath } from '../../../authRedirect';
|
|
11
11
|
import type { BaseApplication } from '../../../BaseApplication';
|
|
12
12
|
import { NocoBaseDesktopRouteType, type NocoBaseDesktopRoute } from '../../../flow-compat';
|
|
13
13
|
|
|
@@ -26,8 +26,6 @@ export type AdminRouteRuntimeTarget = {
|
|
|
26
26
|
reason: AdminRouteRuntimeTargetReason;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
const V2_PUBLIC_PATH_SUFFIX = '/v2/';
|
|
30
|
-
|
|
31
29
|
type LocationLike = {
|
|
32
30
|
pathname: string;
|
|
33
31
|
search?: string;
|
|
@@ -69,7 +67,7 @@ function normalizePublicPath(value = '/') {
|
|
|
69
67
|
}
|
|
70
68
|
|
|
71
69
|
export function isV2AdminRuntime(app?: ResolveAdminRouteRuntimeTargetOptions['app']) {
|
|
72
|
-
return !!app?.getPublicPath && normalizePublicPath(app.getPublicPath()).endsWith(
|
|
70
|
+
return !!app?.getPublicPath && normalizePublicPath(app.getPublicPath()).endsWith(`/${getModernClientPrefix()}/`);
|
|
73
71
|
}
|
|
74
72
|
|
|
75
73
|
export function toRouterNavigationPath(pathname: string, basename?: string) {
|
|
@@ -34,13 +34,41 @@ const defaultToolbar = [
|
|
|
34
34
|
'code',
|
|
35
35
|
'inline-code',
|
|
36
36
|
'upload',
|
|
37
|
-
'fullscreen',
|
|
38
37
|
];
|
|
39
38
|
|
|
40
39
|
const NAMESPACE = 'block-markdown';
|
|
41
40
|
|
|
42
41
|
const locales = ['en_US', 'fr_FR', 'pt_BR', 'ja_JP', 'ko_KR', 'ru_RU', 'sv_SE', 'zh_CN', 'zh_TW'];
|
|
43
42
|
|
|
43
|
+
type MarkdownToolbarItem =
|
|
44
|
+
| string
|
|
45
|
+
| {
|
|
46
|
+
name: string;
|
|
47
|
+
tipPosition?: string;
|
|
48
|
+
toolbar?: MarkdownToolbarItem[];
|
|
49
|
+
[key: string]: unknown;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
function placeToolbarTooltipsBelow(toolbar: MarkdownToolbarItem[]) {
|
|
53
|
+
return toolbar.map((item) => {
|
|
54
|
+
if (typeof item === 'string') {
|
|
55
|
+
if (item === '|' || item === 'br') {
|
|
56
|
+
return item;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
name: item,
|
|
60
|
+
tipPosition: 's',
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
...item,
|
|
66
|
+
tipPosition: 's',
|
|
67
|
+
toolbar: item.toolbar ? placeToolbarTooltipsBelow(item.toolbar) : item.toolbar,
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
44
72
|
const Edit = (props) => {
|
|
45
73
|
const { disabled, onChange, value, fileCollection, toolbar, vditorRef } = props;
|
|
46
74
|
const flowCtx = useFlowContext();
|
|
@@ -73,7 +101,7 @@ const Edit = (props) => {
|
|
|
73
101
|
useEffect(() => {
|
|
74
102
|
if (!containerRef.current) return;
|
|
75
103
|
|
|
76
|
-
const toolbarConfig = toolbar ?? defaultToolbar;
|
|
104
|
+
const toolbarConfig = placeToolbarTooltipsBelow(toolbar ?? defaultToolbar);
|
|
77
105
|
const vditor = new Vditor(containerRef.current, {
|
|
78
106
|
value: value ?? '',
|
|
79
107
|
lang,
|
|
@@ -321,7 +349,7 @@ export const MarkdownWithContextSelector: React.FC<MarkdownWithContextSelectorPr
|
|
|
321
349
|
if (quoteFlag !== false) {
|
|
322
350
|
setInnerValue(value);
|
|
323
351
|
}
|
|
324
|
-
}, [value]);
|
|
352
|
+
}, [quoteFlag, value]);
|
|
325
353
|
const handleTextChange = useCallback(
|
|
326
354
|
(e) => {
|
|
327
355
|
const val = e ?? '';
|
|
@@ -375,7 +403,7 @@ export const MarkdownWithContextSelector: React.FC<MarkdownWithContextSelectorPr
|
|
|
375
403
|
editor.focus();
|
|
376
404
|
});
|
|
377
405
|
},
|
|
378
|
-
[
|
|
406
|
+
[onChange],
|
|
379
407
|
);
|
|
380
408
|
|
|
381
409
|
const handleVariableSelected = useCallback(
|
|
@@ -13,14 +13,24 @@ import { FilterGroup, LinkageFilterItem } from './filter';
|
|
|
13
13
|
import { evaluateConditions, FilterGroupType, removeInvalidFilterItems } from '@nocobase/utils/client';
|
|
14
14
|
|
|
15
15
|
export const ConditionBuilder = observer(
|
|
16
|
-
(props: {
|
|
16
|
+
(props: {
|
|
17
|
+
value: FilterGroupType;
|
|
18
|
+
onChange: (value: FilterGroupType) => void;
|
|
19
|
+
extraMetaTree?: MetaTreeNode[];
|
|
20
|
+
maxAssociationFieldDepth?: number;
|
|
21
|
+
}) => {
|
|
17
22
|
const ctx = useFlowContext();
|
|
18
23
|
|
|
19
24
|
return (
|
|
20
25
|
<FilterGroup
|
|
21
26
|
value={props.value || { logic: '$and', items: [] }}
|
|
22
27
|
FilterItem={(itemProps) => (
|
|
23
|
-
<LinkageFilterItem
|
|
28
|
+
<LinkageFilterItem
|
|
29
|
+
model={ctx.model}
|
|
30
|
+
value={itemProps.value}
|
|
31
|
+
extraMetaTree={props.extraMetaTree}
|
|
32
|
+
maxAssociationFieldDepth={props.maxAssociationFieldDepth}
|
|
33
|
+
/>
|
|
24
34
|
)}
|
|
25
35
|
onChange={props.onChange}
|
|
26
36
|
/>
|
|
@@ -31,7 +31,7 @@ import { FieldModel } from '../models';
|
|
|
31
31
|
import { RecordSelectFieldModel } from '../models/fields/AssociationFieldModel';
|
|
32
32
|
import { InputFieldModel } from '../models/fields/InputFieldModel';
|
|
33
33
|
import { ensureOptionsFromUiSchemaEnumIfAbsent } from '../internal/utils/enumOptionsUtils';
|
|
34
|
-
import { resolveOperatorComponent } from '../internal/utils/operatorSchemaHelper';
|
|
34
|
+
import { pickOperatorStyle as pickStyle, resolveOperatorComponent } from '../internal/utils/operatorSchemaHelper';
|
|
35
35
|
import { RunJSValueEditor } from './RunJSValueEditor';
|
|
36
36
|
import { buildDynamicNamePath } from '../models/blocks/form/dynamicNamePath';
|
|
37
37
|
|
|
@@ -592,7 +592,7 @@ export const DefaultValue = connect((props: Props) => {
|
|
|
592
592
|
}
|
|
593
593
|
}
|
|
594
594
|
}
|
|
595
|
-
}, [inputProps
|
|
595
|
+
}, [inputProps]);
|
|
596
596
|
return (
|
|
597
597
|
<div style={{ flexGrow: 1 }}>
|
|
598
598
|
<FlowModelRenderer model={tempRoot} showFlowSettings={false} />
|
|
@@ -600,11 +600,13 @@ export const DefaultValue = connect((props: Props) => {
|
|
|
600
600
|
);
|
|
601
601
|
};
|
|
602
602
|
return ConstantValueEditor;
|
|
603
|
-
}, [tempRoot]);
|
|
603
|
+
}, [tempRoot, value]);
|
|
604
|
+
|
|
605
|
+
const modelOperator = (model as { operator?: string })?.operator;
|
|
604
606
|
|
|
605
607
|
// 文本类多关键词:根据已注册的 operator schema 渲染(用于默认值配置)
|
|
606
608
|
React.useEffect(() => {
|
|
607
|
-
const operator =
|
|
609
|
+
const operator = modelOperator;
|
|
608
610
|
const fieldModel = tempRoot?.subModels?.fields?.[0];
|
|
609
611
|
if (!operator || !fieldModel) return;
|
|
610
612
|
const originalRender = fieldModel['__originalRender'] || fieldModel.render;
|
|
@@ -622,17 +624,21 @@ export const DefaultValue = connect((props: Props) => {
|
|
|
622
624
|
<Comp
|
|
623
625
|
{...fieldModel.props}
|
|
624
626
|
{...xProps}
|
|
625
|
-
style={{
|
|
627
|
+
style={{
|
|
628
|
+
width: '100%',
|
|
629
|
+
...pickStyle((fieldModel.props as Record<string, unknown>)?.style),
|
|
630
|
+
...pickStyle(xProps?.style),
|
|
631
|
+
}}
|
|
626
632
|
/>
|
|
627
633
|
);
|
|
628
634
|
} else if (typeof originalRender === 'function') {
|
|
629
635
|
fieldModel.render = originalRender;
|
|
630
636
|
}
|
|
631
|
-
}, [model, tempRoot,
|
|
637
|
+
}, [model, tempRoot, modelOperator]);
|
|
632
638
|
|
|
633
639
|
// 根据操作符 schema 的 x-component-props 补全临时字段的输入属性(如多选)
|
|
634
640
|
React.useEffect(() => {
|
|
635
|
-
const operator =
|
|
641
|
+
const operator = modelOperator;
|
|
636
642
|
const fieldModel = tempRoot?.subModels?.fields?.[0];
|
|
637
643
|
if (!fieldModel || !operator) return;
|
|
638
644
|
const ops = model.collectionField?.filterable?.operators || [];
|
|
@@ -641,7 +647,7 @@ export const DefaultValue = connect((props: Props) => {
|
|
|
641
647
|
if (xComponentProps) {
|
|
642
648
|
fieldModel.setProps(xComponentProps);
|
|
643
649
|
}
|
|
644
|
-
}, [model, tempRoot,
|
|
650
|
+
}, [model, tempRoot, modelOperator]);
|
|
645
651
|
|
|
646
652
|
const NullComponent = useMemo(() => {
|
|
647
653
|
function NullValuePlaceholder() {
|