@nocobase/client-v2 2.1.0-alpha.40 → 2.1.0-alpha.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. package/es/Application.d.ts +7 -0
  2. package/es/BaseApplication.d.ts +13 -0
  3. package/es/RouterManager.d.ts +1 -0
  4. package/es/collection-field-interface/CollectionFieldInterface.d.ts +51 -15
  5. package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +82 -3
  6. package/es/collection-manager/field-configure.d.ts +80 -0
  7. package/es/collection-manager/field-validation.d.ts +43 -0
  8. package/es/collection-manager/filter-operators/index.d.ts +46 -0
  9. package/es/collection-manager/filter-operators/operators.d.ts +30 -0
  10. package/es/collection-manager/interfaces/checkbox.d.ts +1 -41
  11. package/es/collection-manager/interfaces/checkboxGroup.d.ts +12 -44
  12. package/es/collection-manager/interfaces/collection.d.ts +12 -51
  13. package/es/collection-manager/interfaces/color.d.ts +1 -16
  14. package/es/collection-manager/interfaces/createdAt.d.ts +1 -44
  15. package/es/collection-manager/interfaces/createdBy.d.ts +0 -4
  16. package/es/collection-manager/interfaces/dateOnly.d.ts +7 -44
  17. package/es/collection-manager/interfaces/datetime.d.ts +1 -44
  18. package/es/collection-manager/interfaces/datetimeNoTz.d.ts +1 -44
  19. package/es/collection-manager/interfaces/email.d.ts +1 -29
  20. package/es/collection-manager/interfaces/id.d.ts +1 -16
  21. package/es/collection-manager/interfaces/index.d.ts +2 -3
  22. package/es/collection-manager/interfaces/input.d.ts +1 -102
  23. package/es/collection-manager/interfaces/integer.d.ts +1 -95
  24. package/es/collection-manager/interfaces/json.d.ts +16 -7
  25. package/es/collection-manager/interfaces/m2m.d.ts +11 -19
  26. package/es/collection-manager/interfaces/m2o.d.ts +11 -19
  27. package/es/collection-manager/interfaces/markdown.d.ts +1 -63
  28. package/es/collection-manager/interfaces/multipleSelect.d.ts +12 -44
  29. package/es/collection-manager/interfaces/nanoid.d.ts +1 -34
  30. package/es/collection-manager/interfaces/number.d.ts +1 -87
  31. package/es/collection-manager/interfaces/o2m.d.ts +12 -24
  32. package/es/collection-manager/interfaces/obo.d.ts +207 -0
  33. package/es/collection-manager/interfaces/oho.d.ts +207 -0
  34. package/es/collection-manager/interfaces/password.d.ts +1 -56
  35. package/es/collection-manager/interfaces/percent.d.ts +1 -84
  36. package/es/collection-manager/interfaces/phone.d.ts +1 -25
  37. package/es/collection-manager/interfaces/properties/index.d.ts +0 -28
  38. package/es/collection-manager/interfaces/radioGroup.d.ts +1 -29
  39. package/es/collection-manager/interfaces/richText.d.ts +1 -63
  40. package/es/collection-manager/interfaces/select.d.ts +12 -44
  41. package/es/collection-manager/interfaces/snowflake-id.d.ts +1 -34
  42. package/es/collection-manager/interfaces/tableoid.d.ts +1 -10
  43. package/es/collection-manager/interfaces/textarea.d.ts +1 -51
  44. package/es/collection-manager/interfaces/time.d.ts +1 -16
  45. package/es/collection-manager/interfaces/types.d.ts +3 -12
  46. package/es/collection-manager/interfaces/unixTimestamp.d.ts +1 -44
  47. package/es/collection-manager/interfaces/updatedAt.d.ts +1 -44
  48. package/es/collection-manager/interfaces/updatedBy.d.ts +0 -4
  49. package/es/collection-manager/interfaces/url.d.ts +1 -20
  50. package/es/collection-manager/interfaces/uuid.d.ts +1 -34
  51. package/es/collection-manager/template-fields.d.ts +53 -0
  52. package/es/components/KeepAlive.d.ts +22 -0
  53. package/es/components/RouterBridge.d.ts +9 -0
  54. package/es/components/form/DialogFormLayout.d.ts +5 -29
  55. package/es/components/form/VariableInput.d.ts +53 -2
  56. package/es/components/form/filter/CollectionFilter.d.ts +49 -0
  57. package/es/components/form/filter/CollectionFilterItem.d.ts +49 -0
  58. package/es/components/form/filter/DateFilterDynamicComponent.d.ts +57 -0
  59. package/es/components/form/filter/FilterValueInput.d.ts +29 -0
  60. package/es/components/form/filter/index.d.ts +11 -0
  61. package/es/components/form/filter/useFilterActionProps.d.ts +96 -0
  62. package/es/components/form/index.d.ts +1 -0
  63. package/es/data-source/ExtendCollectionsProvider.d.ts +50 -0
  64. package/es/data-source/index.d.ts +9 -0
  65. package/es/flow/FlowPage.d.ts +2 -1
  66. package/es/flow/admin-shell/AdminLayoutRouteCoordinator.d.ts +8 -40
  67. package/es/flow/admin-shell/BaseLayoutModel.d.ts +89 -0
  68. package/es/flow/admin-shell/BaseLayoutRouteCoordinator.d.ts +74 -0
  69. package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +12 -0
  70. package/es/flow/admin-shell/admin-layout/AdminLayoutModel.d.ts +7 -92
  71. package/es/flow/admin-shell/admin-layout/AppListRender.d.ts +11 -0
  72. package/es/flow/admin-shell/admin-layout/index.d.ts +3 -0
  73. package/es/flow/admin-shell/admin-layout/useApplications.d.ts +3 -2
  74. package/es/flow/admin-shell/useAdminLayoutRoutePage.d.ts +2 -2
  75. package/es/flow/admin-shell/useLayoutRoutePage.d.ts +23 -0
  76. package/es/flow/components/FlowRoute.d.ts +10 -1
  77. package/es/flow/components/filter/index.d.ts +2 -0
  78. package/es/flow/components/filter/useFilterOptions.d.ts +66 -0
  79. package/es/flow/index.d.ts +4 -0
  80. package/es/flow/models/base/PageModel/PageModel.d.ts +3 -1
  81. package/es/flow/models/blocks/assign-form/assignFieldValuesFlow.d.ts +84 -0
  82. package/es/flow/models/blocks/assign-form/index.d.ts +1 -0
  83. package/es/flow/models/blocks/form/FormActionGroupModel.d.ts +1 -0
  84. package/es/flow/models/blocks/form/FormActionModel.d.ts +9 -2
  85. package/es/flow/models/blocks/table/TableBlockModel.d.ts +10 -0
  86. package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.d.ts +1 -1
  87. package/es/flow-compat/passwordUtils.d.ts +1 -1
  88. package/es/index.d.ts +6 -0
  89. package/es/index.mjs +552 -459
  90. package/es/layout-manager/LayoutContentRoute.d.ts +14 -0
  91. package/es/layout-manager/LayoutManager.d.ts +22 -0
  92. package/es/layout-manager/LayoutRoute.d.ts +14 -0
  93. package/es/layout-manager/index.d.ts +13 -0
  94. package/es/layout-manager/types.d.ts +20 -0
  95. package/es/layout-manager/utils.d.ts +14 -0
  96. package/es/nocobase-buildin-plugin/index.d.ts +3 -10
  97. package/es/settings-center/index.d.ts +1 -1
  98. package/es/settings-center/plugin-manager/BulkEnableButton.d.ts +15 -0
  99. package/es/settings-center/plugin-manager/PluginCard.d.ts +15 -0
  100. package/es/settings-center/plugin-manager/PluginDetail.d.ts +16 -0
  101. package/es/settings-center/{PluginManagerPage.d.ts → plugin-manager/index.d.ts} +1 -7
  102. package/es/settings-center/plugin-manager/types.d.ts +34 -0
  103. package/lib/index.js +552 -459
  104. package/package.json +8 -7
  105. package/src/Application.tsx +51 -12
  106. package/src/BaseApplication.tsx +32 -0
  107. package/src/PluginSettingsManager.ts +1 -1
  108. package/src/RouterManager.tsx +17 -1
  109. package/src/__tests__/PluginSettingsManager.test.ts +41 -2
  110. package/src/__tests__/app.test.tsx +17 -1
  111. package/src/__tests__/globalDeps.test.ts +1 -0
  112. package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +45 -2
  113. package/src/__tests__/plugin-manager.test.tsx +177 -0
  114. package/src/__tests__/settings-center.test.tsx +24 -2
  115. package/src/collection-field-interface/CollectionFieldInterface.ts +71 -77
  116. package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +201 -4
  117. package/src/collection-manager/field-configure.ts +548 -0
  118. package/src/collection-manager/field-validation.ts +195 -0
  119. package/src/collection-manager/filter-operators/index.ts +176 -0
  120. package/src/collection-manager/{interfaces/properties → filter-operators}/operators.ts +24 -13
  121. package/src/collection-manager/interfaces/checkbox.ts +2 -9
  122. package/src/collection-manager/interfaces/checkboxGroup.ts +2 -10
  123. package/src/collection-manager/interfaces/collection.ts +2 -15
  124. package/src/collection-manager/interfaces/color.ts +2 -2
  125. package/src/collection-manager/interfaces/createdAt.ts +2 -2
  126. package/src/collection-manager/interfaces/createdBy.ts +1 -12
  127. package/src/collection-manager/interfaces/dateOnly.ts +8 -2
  128. package/src/collection-manager/interfaces/datetime.ts +2 -2
  129. package/src/collection-manager/interfaces/datetimeNoTz.ts +2 -2
  130. package/src/collection-manager/interfaces/email.ts +2 -9
  131. package/src/collection-manager/interfaces/id.ts +1 -2
  132. package/src/collection-manager/interfaces/index.ts +2 -3
  133. package/src/collection-manager/interfaces/input.ts +2 -133
  134. package/src/collection-manager/interfaces/integer.ts +2 -71
  135. package/src/collection-manager/interfaces/json.tsx +17 -11
  136. package/src/collection-manager/interfaces/m2m.tsx +0 -21
  137. package/src/collection-manager/interfaces/m2o.tsx +0 -22
  138. package/src/collection-manager/interfaces/markdown.ts +2 -51
  139. package/src/collection-manager/interfaces/multipleSelect.ts +2 -14
  140. package/src/collection-manager/interfaces/nanoid.ts +2 -2
  141. package/src/collection-manager/interfaces/number.ts +2 -85
  142. package/src/collection-manager/interfaces/o2m.tsx +1 -22
  143. package/src/collection-manager/interfaces/obo.tsx +145 -0
  144. package/src/collection-manager/interfaces/oho.tsx +145 -0
  145. package/src/collection-manager/interfaces/password.ts +2 -44
  146. package/src/collection-manager/interfaces/percent.ts +2 -74
  147. package/src/collection-manager/interfaces/phone.ts +2 -2
  148. package/src/collection-manager/interfaces/properties/index.ts +0 -133
  149. package/src/collection-manager/interfaces/radioGroup.ts +2 -2
  150. package/src/collection-manager/interfaces/richText.ts +2 -51
  151. package/src/collection-manager/interfaces/select.ts +2 -14
  152. package/src/collection-manager/interfaces/snowflake-id.ts +2 -2
  153. package/src/collection-manager/interfaces/tableoid.ts +1 -2
  154. package/src/collection-manager/interfaces/textarea.ts +2 -51
  155. package/src/collection-manager/interfaces/time.ts +2 -2
  156. package/src/collection-manager/interfaces/types.ts +4 -12
  157. package/src/collection-manager/interfaces/unixTimestamp.tsx +2 -2
  158. package/src/collection-manager/interfaces/updatedAt.ts +2 -2
  159. package/src/collection-manager/interfaces/updatedBy.ts +1 -12
  160. package/src/collection-manager/interfaces/url.ts +2 -4
  161. package/src/collection-manager/interfaces/uuid.ts +2 -2
  162. package/src/collection-manager/template-fields.ts +109 -0
  163. package/src/components/KeepAlive.tsx +131 -0
  164. package/src/components/README.md +90 -6
  165. package/src/components/README.zh-CN.md +90 -7
  166. package/src/components/RouterBridge.tsx +28 -4
  167. package/src/components/__tests__/KeepAlive.test.tsx +63 -0
  168. package/src/components/__tests__/RouterBridge.test.tsx +27 -0
  169. package/src/components/form/DialogFormLayout.tsx +5 -29
  170. package/src/components/form/VariableInput.tsx +101 -28
  171. package/src/components/form/__tests__/VariableInput.test.ts +85 -0
  172. package/src/components/form/filter/CollectionFilter.tsx +111 -0
  173. package/src/components/form/filter/CollectionFilterItem.tsx +184 -0
  174. package/src/components/form/filter/DateFilterDynamicComponent.tsx +283 -0
  175. package/src/components/form/filter/FilterValueInput.tsx +198 -0
  176. package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +247 -0
  177. package/src/components/form/filter/__tests__/DateFilterDynamicComponent.test.tsx +148 -0
  178. package/src/components/form/filter/__tests__/FilterValueInput.test.tsx +243 -0
  179. package/src/components/form/filter/__tests__/compileFilterGroup.test.ts +146 -0
  180. package/src/components/form/filter/index.ts +13 -0
  181. package/src/components/form/filter/useFilterActionProps.ts +203 -0
  182. package/src/components/form/index.tsx +1 -0
  183. package/src/data-source/ExtendCollectionsProvider.tsx +144 -0
  184. package/src/data-source/__tests__/ExtendCollectionsProvider.test.tsx +264 -0
  185. package/src/data-source/index.ts +10 -0
  186. package/src/flow/FlowPage.tsx +35 -7
  187. package/src/flow/__tests__/FlowPage.test.tsx +79 -0
  188. package/src/flow/__tests__/FlowRoute.test.tsx +529 -2
  189. package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +191 -0
  190. package/src/flow/actions/__tests__/openView.subModelKey.test.tsx +33 -0
  191. package/src/flow/actions/aclCheck.tsx +4 -0
  192. package/src/flow/actions/aclCheckRefresh.tsx +4 -0
  193. package/src/flow/actions/dateTimeFormat.tsx +12 -8
  194. package/src/flow/actions/linkageRules.tsx +122 -0
  195. package/src/flow/actions/openView.tsx +28 -4
  196. package/src/flow/admin-shell/AdminLayoutRouteCoordinator.ts +11 -329
  197. package/src/flow/admin-shell/BaseLayoutModel.tsx +455 -0
  198. package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +502 -0
  199. package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +547 -3
  200. package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +35 -7
  201. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +160 -0
  202. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +0 -12
  203. package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +28 -201
  204. package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +11 -2
  205. package/src/flow/admin-shell/admin-layout/AppListRender.tsx +139 -0
  206. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +1 -26
  207. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +149 -27
  208. package/src/flow/admin-shell/admin-layout/index.ts +3 -0
  209. package/src/flow/admin-shell/admin-layout/useApplications.tsx +34 -1
  210. package/src/flow/admin-shell/useAdminLayoutRoutePage.ts +10 -26
  211. package/src/flow/admin-shell/useLayoutRoutePage.ts +61 -0
  212. package/src/flow/components/AdminLayout.tsx +4 -154
  213. package/src/flow/components/FlowRoute.tsx +105 -15
  214. package/src/flow/components/filter/index.ts +3 -0
  215. package/src/flow/components/filter/useFilterOptions.ts +102 -0
  216. package/src/flow/index.ts +4 -0
  217. package/src/flow/models/actions/UpdateRecordActionModel.tsx +14 -95
  218. package/src/flow/models/actions/UpdateRecordActionUtils.ts +4 -7
  219. package/src/flow/models/actions/__tests__/AssignFormRefill.test.ts +26 -1
  220. package/src/flow/models/base/ActionModel.tsx +8 -1
  221. package/src/flow/models/base/PageModel/PageModel.tsx +51 -18
  222. package/src/flow/models/base/PageModel/RootPageModel.tsx +6 -13
  223. package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +102 -1
  224. package/src/flow/models/base/RouteModel.tsx +1 -1
  225. package/src/flow/models/blocks/assign-form/AssignFormItemModel.tsx +63 -2
  226. package/src/flow/models/blocks/assign-form/assignFieldValuesFlow.tsx +206 -0
  227. package/src/flow/models/blocks/assign-form/index.ts +1 -0
  228. package/src/flow/models/blocks/form/FormActionGroupModel.tsx +14 -0
  229. package/src/flow/models/blocks/form/FormActionModel.tsx +30 -3
  230. package/src/flow/models/blocks/form/FormItemModel.tsx +8 -1
  231. package/src/flow/models/blocks/form/__tests__/FormActionGroupModel.test.ts +46 -0
  232. package/src/flow/models/blocks/form/__tests__/submitHandler.test.ts +71 -0
  233. package/src/flow/models/blocks/form/submitHandler.ts +8 -1
  234. package/src/flow/models/blocks/form/submitValues.ts +4 -1
  235. package/src/flow/models/blocks/table/TableBlockModel.tsx +118 -16
  236. package/src/flow/models/blocks/table/__tests__/TableBlockModel.rowSelection.test.tsx +114 -0
  237. package/src/flow/models/fields/AssociationFieldModel/SubFormFieldModel.tsx +7 -1
  238. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +1 -1
  239. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +6 -5
  240. package/src/flow/models/fields/ClickableFieldModel.tsx +9 -1
  241. package/src/flow/models/fields/CollectionSelectorFieldModel.tsx +8 -2
  242. package/src/flow/models/fields/DisplayEnumFieldModel.tsx +8 -2
  243. package/src/flow/models/fields/DisplayTimeFieldModel.tsx +1 -1
  244. package/src/flow/models/fields/TimeFieldModel.tsx +1 -1
  245. package/src/flow/models/fields/__tests__/TimeFieldModel.test.tsx +61 -0
  246. package/src/flow/models/fields/mobile-components/MobileDatePicker.tsx +19 -3
  247. package/src/flow/models/fields/mobile-components/__tests__/MobileDatePicker.test.tsx +94 -0
  248. package/src/flow/models/topbar/TopbarActionModel.tsx +1 -1
  249. package/src/flow/utils/__tests__/dateTimeFormat.test.ts +91 -0
  250. package/src/index.ts +6 -0
  251. package/src/layout-manager/LayoutContentRoute.tsx +90 -0
  252. package/src/layout-manager/LayoutManager.tsx +185 -0
  253. package/src/layout-manager/LayoutRoute.tsx +138 -0
  254. package/src/layout-manager/__tests__/LayoutManager.test.tsx +335 -0
  255. package/src/layout-manager/__tests__/LayoutRoute.test.tsx +473 -0
  256. package/src/layout-manager/index.ts +14 -0
  257. package/src/layout-manager/types.ts +22 -0
  258. package/src/layout-manager/utils.ts +37 -0
  259. package/src/nocobase-buildin-plugin/index.tsx +69 -67
  260. package/src/nocobase-buildin-plugin/plugins/LocalePlugin.ts +1 -0
  261. package/src/settings-center/index.ts +1 -1
  262. package/src/settings-center/plugin-manager/BulkEnableButton.tsx +111 -0
  263. package/src/settings-center/plugin-manager/PluginCard.tsx +270 -0
  264. package/src/settings-center/plugin-manager/PluginDetail.tsx +195 -0
  265. package/src/settings-center/plugin-manager/index.tsx +254 -0
  266. package/src/settings-center/plugin-manager/types.ts +35 -0
  267. package/src/settings-center/utils.tsx +8 -1
  268. package/src/theme/__tests__/globalStyles.test.ts +24 -0
  269. package/src/theme/globalStyles.ts +10 -0
  270. package/src/utils/globalDeps.ts +2 -0
  271. package/es/collection-manager/interfaces/linkTo.d.ts +0 -90
  272. package/es/collection-manager/interfaces/o2o.d.ts +0 -621
  273. package/es/collection-manager/interfaces/properties/operators.d.ts +0 -294
  274. package/es/collection-manager/interfaces/subTable.d.ts +0 -172
  275. package/src/collection-manager/interfaces/linkTo.ts +0 -120
  276. package/src/collection-manager/interfaces/o2o.tsx +0 -561
  277. package/src/collection-manager/interfaces/subTable.ts +0 -218
  278. package/src/settings-center/PluginManagerPage.tsx +0 -162
@@ -0,0 +1,455 @@
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 { define, observable } from '@formily/reactive';
11
+ import { parsePathnameToViewParams, type FlowEngine, FlowModel, type ViewParam } from '@nocobase/flow-engine';
12
+ import {
13
+ BaseLayoutRouteCoordinator,
14
+ type BaseLayoutRouteCoordinatorOptions,
15
+ type RoutePageMeta,
16
+ } from './BaseLayoutRouteCoordinator';
17
+ import type { LayoutDefinition } from '../../layout-manager/types';
18
+ import { isLayoutContentRouteName } from '../../layout-manager/utils';
19
+
20
+ export type BaseLayoutStructure = {
21
+ subModels?: Record<string, FlowModel[]>;
22
+ };
23
+
24
+ export type GetLayoutModelOptions<TModel extends FlowModel = BaseLayoutModel> = {
25
+ required?: boolean;
26
+ create?: boolean;
27
+ props?: any;
28
+ use?: new (...args: any[]) => TModel;
29
+ };
30
+
31
+ const DEFAULT_LAYOUT_DEFINITION: LayoutDefinition = {
32
+ routeName: 'admin',
33
+ routePath: '/admin',
34
+ rootRouteName: 'admin',
35
+ uid: 'admin-layout-model',
36
+ layoutModelClass: 'AdminLayoutModel',
37
+ rootPageModelClass: 'RootPageModel',
38
+ childPageModelClass: 'ChildPageModel',
39
+ authCheck: true,
40
+ };
41
+
42
+ export type LayoutRouteMatch =
43
+ | {
44
+ type: 'root';
45
+ pathname: string;
46
+ basePathname: string;
47
+ relativePath: string;
48
+ }
49
+ | {
50
+ type: 'page';
51
+ pathname: string;
52
+ basePathname: string;
53
+ relativePath: string;
54
+ pageUid: string;
55
+ tabUid?: string;
56
+ viewStack: ViewParam[];
57
+ }
58
+ | {
59
+ type: 'notFound';
60
+ pathname: string;
61
+ basePathname: string;
62
+ relativePath: string;
63
+ };
64
+
65
+ export interface LayoutRouteLike {
66
+ id?: string;
67
+ name?: string;
68
+ pathname?: string;
69
+ params?: Record<string, string | undefined>;
70
+ layoutRouteName?: string;
71
+ layoutBasePathname?: string;
72
+ }
73
+
74
+ const normalizeBasePathname = (basePathname?: string) =>
75
+ `/${(basePathname || '/admin').replace(/^\/+/, '').replace(/\/+$/, '')}`;
76
+
77
+ const normalizePathname = (pathname?: string) => {
78
+ if (!pathname || pathname === '/') {
79
+ return '/';
80
+ }
81
+ return `/${pathname.replace(/^\/+/, '').replace(/\/+$/, '')}`;
82
+ };
83
+
84
+ const getRelativePath = (pathname: string, basePath: string) => {
85
+ const normalizedPathname = normalizePathname(pathname);
86
+ const normalizedBasePath = normalizeBasePathname(basePath);
87
+
88
+ if (normalizedPathname === normalizedBasePath) {
89
+ return '';
90
+ }
91
+
92
+ if (normalizedPathname.startsWith(`${normalizedBasePath}/`)) {
93
+ return normalizedPathname.slice(normalizedBasePath.length + 1);
94
+ }
95
+
96
+ return null;
97
+ };
98
+
99
+ const getDefaultBasePathnameFromRoutePath = (routePath?: string) => {
100
+ if (routePath?.startsWith('/')) {
101
+ return normalizeBasePathname(routePath);
102
+ }
103
+ return '';
104
+ };
105
+
106
+ const isKnownViewParamName = (segment: string) => ['tab', 'filterbytk', 'sourceid'].includes(segment);
107
+
108
+ const isStandardLayoutRelativePath = (relativePath: string) => {
109
+ if (!relativePath) {
110
+ return true;
111
+ }
112
+
113
+ const segments = relativePath.split('/').filter(Boolean);
114
+ if (!segments.length) {
115
+ return true;
116
+ }
117
+
118
+ let i = 1;
119
+ while (i < segments.length) {
120
+ const segment = segments[i];
121
+ if (segment === 'view') {
122
+ if (!segments[i + 1]) {
123
+ return false;
124
+ }
125
+ i += 2;
126
+ continue;
127
+ }
128
+
129
+ if (!isKnownViewParamName(segment) || !segments[i + 1]) {
130
+ return false;
131
+ }
132
+ i += 2;
133
+ }
134
+
135
+ return true;
136
+ };
137
+
138
+ /**
139
+ * 通用 Layout 运行时模型。
140
+ *
141
+ * 该模型封装页面路由桥接、弹窗路由、page tab 路由、布局容器和移动端状态,
142
+ * Admin、Embed 等具体 Layout 只需要继承并实现各自的渲染和专属业务能力。
143
+ */
144
+ export class BaseLayoutModel<
145
+ TStructure extends BaseLayoutStructure = BaseLayoutStructure,
146
+ > extends FlowModel<TStructure> {
147
+ isMobileLayout = false;
148
+ currentLayoutRoute: LayoutRouteMatch | null = null;
149
+ protected routeCoordinator?: BaseLayoutRouteCoordinator;
150
+ private activePageUid = '';
151
+ private layoutContentElement: HTMLElement | null = null;
152
+ private readonly routePageMetaMap = new Map<string, RoutePageMeta>();
153
+ private contextBindingsActive = false;
154
+
155
+ constructor(options: any) {
156
+ super(options);
157
+ define(this, {
158
+ isMobileLayout: observable.ref,
159
+ currentLayoutRoute: observable.ref,
160
+ });
161
+ }
162
+
163
+ registerRoutePage(pageUid: string, meta: RoutePageMeta) {
164
+ this.routePageMetaMap.set(pageUid, meta);
165
+ const routeModel = this.getCoordinator().registerPage(pageUid, meta);
166
+ this.getCoordinator().syncRoute(this.getCurrentCoordinatorRouteLike());
167
+ return routeModel;
168
+ }
169
+
170
+ updateRoutePage(pageUid: string, meta: Partial<RoutePageMeta>) {
171
+ const prev = this.routePageMetaMap.get(pageUid) || { active: false };
172
+ const next = {
173
+ ...prev,
174
+ ...meta,
175
+ active: typeof meta.active === 'boolean' ? meta.active : prev.active,
176
+ };
177
+ this.routePageMetaMap.set(pageUid, next);
178
+ this.getCoordinator().syncPageMeta(pageUid, meta);
179
+ }
180
+
181
+ unregisterRoutePage(pageUid: string) {
182
+ this.routePageMetaMap.delete(pageUid);
183
+ if (this.activePageUid === pageUid) {
184
+ this.activePageUid = '';
185
+ }
186
+ this.getCoordinator().unregisterPage(pageUid);
187
+ }
188
+
189
+ setLayoutContentElement(element: HTMLElement | null) {
190
+ this.layoutContentElement = element;
191
+ this.getCoordinator().setLayoutContentElement(element);
192
+ }
193
+
194
+ setIsMobileLayout(isMobileLayout: boolean) {
195
+ this.isMobileLayout = !!isMobileLayout;
196
+ }
197
+
198
+ getCurrentRouteByPageUid(pageUid: string) {
199
+ return this.flowEngine.context.routeRepository?.getRouteBySchemaUid?.(pageUid) || {};
200
+ }
201
+
202
+ get layout(): LayoutDefinition {
203
+ return (
204
+ (this.props.layout as LayoutDefinition) || {
205
+ ...DEFAULT_LAYOUT_DEFINITION,
206
+ uid: this.uid || DEFAULT_LAYOUT_DEFINITION.uid,
207
+ }
208
+ );
209
+ }
210
+
211
+ getCoordinator() {
212
+ if (!this.routeCoordinator) {
213
+ this.routeCoordinator = this.createRouteCoordinator();
214
+ }
215
+ return this.routeCoordinator;
216
+ }
217
+
218
+ protected createRouteCoordinator() {
219
+ return new BaseLayoutRouteCoordinator(this.flowEngine, this.getRouteCoordinatorOptions());
220
+ }
221
+
222
+ protected getRouteCoordinatorOptions(): BaseLayoutRouteCoordinatorOptions {
223
+ return {
224
+ layout: this.layout,
225
+ layoutContext: this.context,
226
+ };
227
+ }
228
+
229
+ protected getPageUidFromRoute(route: any) {
230
+ if (route?.pageUid) {
231
+ return route.pageUid;
232
+ }
233
+ if (route?.layoutRoute?.type === 'page') {
234
+ return route.layoutRoute.pageUid;
235
+ }
236
+ return route?.params?.name || '';
237
+ }
238
+
239
+ isLayoutContentRoute(routeLike: LayoutRouteLike) {
240
+ if (routeLike?.layoutRouteName) {
241
+ return routeLike.layoutRouteName === this.layout.routeName;
242
+ }
243
+
244
+ const routeName = routeLike?.name || routeLike?.id;
245
+ return isLayoutContentRouteName(this.layout.routeName, routeName);
246
+ }
247
+
248
+ resolveLayoutRoute(routeLike: LayoutRouteLike): LayoutRouteMatch {
249
+ const pathname = normalizePathname(routeLike?.pathname);
250
+ const rawBasePathname = routeLike.layoutBasePathname || getDefaultBasePathnameFromRoutePath(this.layout.routePath);
251
+ const basePathname = rawBasePathname ? normalizeBasePathname(rawBasePathname) : '';
252
+
253
+ if (!basePathname) {
254
+ return {
255
+ type: 'notFound',
256
+ pathname,
257
+ basePathname: '',
258
+ relativePath: '',
259
+ };
260
+ }
261
+
262
+ const relativePath = getRelativePath(pathname, basePathname);
263
+
264
+ if (relativePath === null) {
265
+ return {
266
+ type: 'notFound',
267
+ pathname,
268
+ basePathname,
269
+ relativePath: '',
270
+ };
271
+ }
272
+
273
+ if (!relativePath) {
274
+ return {
275
+ type: 'root',
276
+ pathname,
277
+ basePathname,
278
+ relativePath,
279
+ };
280
+ }
281
+
282
+ if (!isStandardLayoutRelativePath(relativePath)) {
283
+ return {
284
+ type: 'notFound',
285
+ pathname,
286
+ basePathname,
287
+ relativePath,
288
+ };
289
+ }
290
+
291
+ const viewStack = parsePathnameToViewParams(pathname, { basePath: basePathname });
292
+ const pageUid = viewStack[0]?.viewUid;
293
+
294
+ if (!pageUid) {
295
+ return {
296
+ type: 'notFound',
297
+ pathname,
298
+ basePathname,
299
+ relativePath,
300
+ };
301
+ }
302
+
303
+ return {
304
+ type: 'page',
305
+ pathname,
306
+ basePathname,
307
+ relativePath,
308
+ pageUid,
309
+ tabUid: viewStack[0]?.tabUid,
310
+ viewStack,
311
+ };
312
+ }
313
+
314
+ getPageUidFromLayoutRoute(match: LayoutRouteMatch | null | undefined) {
315
+ return match?.type === 'page' ? match.pageUid : '';
316
+ }
317
+
318
+ syncLayoutRoute(routeLike: LayoutRouteLike) {
319
+ if (!this.isLayoutContentRoute(routeLike)) {
320
+ this.clearLayoutRoute();
321
+ return null;
322
+ }
323
+
324
+ const layoutRoute = this.resolveLayoutRoute(routeLike);
325
+ this.currentLayoutRoute = layoutRoute;
326
+ this.activePageUid = this.getPageUidFromLayoutRoute(layoutRoute);
327
+ this.getCoordinator().syncRoute({
328
+ ...routeLike,
329
+ layoutRouteName: this.layout.routeName,
330
+ pageUid: this.activePageUid,
331
+ pathname: layoutRoute.pathname,
332
+ layoutBasePathname: layoutRoute.basePathname,
333
+ layoutRoute,
334
+ });
335
+
336
+ return layoutRoute;
337
+ }
338
+
339
+ clearLayoutRoute(routeLike?: LayoutRouteLike) {
340
+ const pathname = routeLike?.pathname ? normalizePathname(routeLike.pathname) : '';
341
+ if (pathname && this.currentLayoutRoute?.pathname && pathname !== this.currentLayoutRoute.pathname) {
342
+ return;
343
+ }
344
+
345
+ this.currentLayoutRoute = null;
346
+ this.activePageUid = '';
347
+ this.routeCoordinator?.syncRoute({});
348
+ }
349
+
350
+ protected onMount(): void {
351
+ super.onMount();
352
+ this.setupContextBindings();
353
+ }
354
+
355
+ protected onUnmount(): void {
356
+ this.teardownRuntime();
357
+ super.onUnmount();
358
+ }
359
+
360
+ private setupContextBindings() {
361
+ this.contextBindingsActive = true;
362
+ this.context.defineProperty('layoutContext', {
363
+ get: () => (this.contextBindingsActive ? this.context : undefined),
364
+ cache: false,
365
+ });
366
+ this.context.defineProperty('currentRoute', {
367
+ get: () => (this.contextBindingsActive ? this.getCurrentRouteByActivePage() : {}),
368
+ // 切页后需要立即读取当前激活页面的路由,不能复用首次访问时的缓存值。
369
+ cache: false,
370
+ });
371
+ this.context.defineProperty('layoutContentElement', {
372
+ get: () => (this.contextBindingsActive ? this.layoutContentElement : null),
373
+ // 布局容器 ref 会在挂载和卸载时变化,这里必须实时读取。
374
+ cache: false,
375
+ });
376
+ this.context.defineProperty('isMobileLayout', {
377
+ get: () => (this.contextBindingsActive ? this.isMobileLayout : false),
378
+ observable: true,
379
+ cache: false,
380
+ });
381
+ this.context.defineProperty('layout', {
382
+ get: () => (this.contextBindingsActive ? this.layout : undefined),
383
+ cache: false,
384
+ });
385
+ this.context.defineProperty('layoutRoute', {
386
+ get: () => (this.contextBindingsActive ? this.currentLayoutRoute : undefined),
387
+ observable: true,
388
+ cache: false,
389
+ });
390
+ }
391
+
392
+ private teardownRuntime() {
393
+ this.contextBindingsActive = false;
394
+ this.routeCoordinator?.destroy();
395
+ this.routeCoordinator = undefined;
396
+ this.routePageMetaMap.clear();
397
+ this.activePageUid = '';
398
+ this.currentLayoutRoute = null;
399
+ this.layoutContentElement = null;
400
+ }
401
+
402
+ private getCurrentRouteByActivePage() {
403
+ if (!this.activePageUid) {
404
+ return {};
405
+ }
406
+ return this.getCurrentRouteByPageUid(this.activePageUid);
407
+ }
408
+
409
+ private getCurrentCoordinatorRouteLike() {
410
+ if (this.currentLayoutRoute?.type === 'page') {
411
+ return {
412
+ layoutRouteName: this.layout.routeName,
413
+ pageUid: this.currentLayoutRoute.pageUid,
414
+ pathname: this.currentLayoutRoute.pathname,
415
+ layoutBasePathname: this.currentLayoutRoute.basePathname,
416
+ layoutRoute: this.currentLayoutRoute,
417
+ };
418
+ }
419
+ return {};
420
+ }
421
+ }
422
+
423
+ /**
424
+ * 按固定 UID 获取或创建 Layout host model。
425
+ */
426
+ export function getLayoutModel<TModel extends FlowModel = BaseLayoutModel>(
427
+ flowEngine: FlowEngine,
428
+ uid: string,
429
+ options: GetLayoutModelOptions<TModel> = {},
430
+ ) {
431
+ const { required = false, create = false, props, use } = options;
432
+ let model = flowEngine.getModel<TModel>(uid);
433
+
434
+ if (!model && create) {
435
+ const ModelClass = use as new (...args: any[]) => TModel;
436
+ if (!ModelClass) {
437
+ throw new Error(`[NocoBase] Cannot create layout model ${uid} without model class.`);
438
+ }
439
+ model = flowEngine.createModel<TModel>({
440
+ uid,
441
+ use: ModelClass,
442
+ props,
443
+ });
444
+ }
445
+
446
+ if (model && props) {
447
+ model.setProps(props);
448
+ }
449
+
450
+ if (!model && required) {
451
+ throw new Error(`[NocoBase] FlowRoute requires layout model ${uid}. Please render FlowRoute under Layout.`);
452
+ }
453
+
454
+ return model;
455
+ }