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

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 (338) 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/authRedirect.d.ts +12 -0
  5. package/es/collection-field-interface/CollectionFieldInterface.d.ts +51 -15
  6. package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +82 -3
  7. package/es/collection-manager/field-configure.d.ts +80 -0
  8. package/es/collection-manager/field-validation.d.ts +43 -0
  9. package/es/collection-manager/filter-operators/index.d.ts +46 -0
  10. package/es/collection-manager/filter-operators/operators.d.ts +30 -0
  11. package/es/collection-manager/interfaces/checkbox.d.ts +1 -41
  12. package/es/collection-manager/interfaces/checkboxGroup.d.ts +12 -44
  13. package/es/collection-manager/interfaces/collection.d.ts +12 -51
  14. package/es/collection-manager/interfaces/color.d.ts +1 -16
  15. package/es/collection-manager/interfaces/createdAt.d.ts +1 -44
  16. package/es/collection-manager/interfaces/createdBy.d.ts +0 -4
  17. package/es/collection-manager/interfaces/dateOnly.d.ts +7 -44
  18. package/es/collection-manager/interfaces/datetime.d.ts +1 -44
  19. package/es/collection-manager/interfaces/datetimeNoTz.d.ts +1 -44
  20. package/es/collection-manager/interfaces/email.d.ts +1 -29
  21. package/es/collection-manager/interfaces/id.d.ts +1 -16
  22. package/es/collection-manager/interfaces/index.d.ts +2 -3
  23. package/es/collection-manager/interfaces/input.d.ts +1 -102
  24. package/es/collection-manager/interfaces/integer.d.ts +1 -95
  25. package/es/collection-manager/interfaces/json.d.ts +16 -7
  26. package/es/collection-manager/interfaces/m2m.d.ts +11 -19
  27. package/es/collection-manager/interfaces/m2o.d.ts +11 -19
  28. package/es/collection-manager/interfaces/markdown.d.ts +1 -63
  29. package/es/collection-manager/interfaces/multipleSelect.d.ts +12 -44
  30. package/es/collection-manager/interfaces/nanoid.d.ts +1 -34
  31. package/es/collection-manager/interfaces/number.d.ts +1 -87
  32. package/es/collection-manager/interfaces/o2m.d.ts +12 -24
  33. package/es/collection-manager/interfaces/obo.d.ts +207 -0
  34. package/es/collection-manager/interfaces/oho.d.ts +207 -0
  35. package/es/collection-manager/interfaces/password.d.ts +1 -56
  36. package/es/collection-manager/interfaces/percent.d.ts +1 -84
  37. package/es/collection-manager/interfaces/phone.d.ts +1 -25
  38. package/es/collection-manager/interfaces/properties/index.d.ts +0 -28
  39. package/es/collection-manager/interfaces/radioGroup.d.ts +1 -29
  40. package/es/collection-manager/interfaces/richText.d.ts +1 -63
  41. package/es/collection-manager/interfaces/select.d.ts +12 -44
  42. package/es/collection-manager/interfaces/snowflake-id.d.ts +1 -34
  43. package/es/collection-manager/interfaces/tableoid.d.ts +1 -10
  44. package/es/collection-manager/interfaces/textarea.d.ts +1 -51
  45. package/es/collection-manager/interfaces/time.d.ts +1 -16
  46. package/es/collection-manager/interfaces/types.d.ts +3 -12
  47. package/es/collection-manager/interfaces/unixTimestamp.d.ts +1 -44
  48. package/es/collection-manager/interfaces/updatedAt.d.ts +1 -44
  49. package/es/collection-manager/interfaces/updatedBy.d.ts +0 -4
  50. package/es/collection-manager/interfaces/url.d.ts +1 -20
  51. package/es/collection-manager/interfaces/uuid.d.ts +1 -34
  52. package/es/collection-manager/template-fields.d.ts +53 -0
  53. package/es/components/KeepAlive.d.ts +22 -0
  54. package/es/components/RouterBridge.d.ts +9 -0
  55. package/es/components/form/DialogFormLayout.d.ts +5 -29
  56. package/es/components/form/TypedVariableInput.d.ts +75 -0
  57. package/es/components/form/VariableInput.d.ts +53 -2
  58. package/es/components/form/filter/CollectionFilter.d.ts +49 -0
  59. package/es/components/form/filter/CollectionFilterItem.d.ts +49 -0
  60. package/es/components/form/filter/DateFilterDynamicComponent.d.ts +57 -0
  61. package/es/components/form/filter/FilterValueInput.d.ts +29 -0
  62. package/es/components/form/filter/index.d.ts +11 -0
  63. package/es/components/form/filter/useFilterActionProps.d.ts +96 -0
  64. package/es/components/form/index.d.ts +2 -0
  65. package/es/data-source/ExtendCollectionsProvider.d.ts +50 -0
  66. package/es/data-source/index.d.ts +9 -0
  67. package/es/flow/FlowPage.d.ts +2 -1
  68. package/es/flow/admin-shell/AdminLayoutRouteCoordinator.d.ts +8 -40
  69. package/es/flow/admin-shell/BaseLayoutModel.d.ts +89 -0
  70. package/es/flow/admin-shell/BaseLayoutRouteCoordinator.d.ts +74 -0
  71. package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +12 -0
  72. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +1 -0
  73. package/es/flow/admin-shell/admin-layout/AdminLayoutModel.d.ts +7 -92
  74. package/es/flow/admin-shell/admin-layout/AppListRender.d.ts +11 -0
  75. package/es/flow/admin-shell/admin-layout/index.d.ts +3 -0
  76. package/es/flow/admin-shell/admin-layout/useApplications.d.ts +3 -2
  77. package/es/flow/admin-shell/useAdminLayoutRoutePage.d.ts +2 -2
  78. package/es/flow/admin-shell/useLayoutRoutePage.d.ts +23 -0
  79. package/es/flow/components/ConditionBuilder.d.ts +1 -0
  80. package/es/flow/components/FieldAssignRulesEditor.d.ts +3 -1
  81. package/es/flow/components/FieldAssignValueInput.d.ts +1 -0
  82. package/es/flow/components/FlowRoute.d.ts +10 -1
  83. package/es/flow/components/fieldAssignOptions.d.ts +10 -0
  84. package/es/flow/components/filter/LinkageFilterItem.d.ts +1 -0
  85. package/es/flow/components/filter/VariableFilterItem.d.ts +1 -0
  86. package/es/flow/components/filter/index.d.ts +2 -0
  87. package/es/flow/components/filter/metaTreeAssociationDepth.d.ts +13 -0
  88. package/es/flow/components/filter/useFilterOptions.d.ts +66 -0
  89. package/es/flow/index.d.ts +4 -0
  90. package/es/flow/models/base/AssociationFieldGroupModel.d.ts +2 -33
  91. package/es/flow/models/base/PageModel/PageModel.d.ts +3 -1
  92. package/es/flow/models/blocks/assign-form/assignFieldValuesFlow.d.ts +92 -0
  93. package/es/flow/models/blocks/assign-form/index.d.ts +1 -0
  94. package/es/flow/models/blocks/form/FormActionGroupModel.d.ts +1 -0
  95. package/es/flow/models/blocks/form/FormActionModel.d.ts +9 -2
  96. package/es/flow/models/blocks/table/TableBlockModel.d.ts +10 -0
  97. package/es/flow/models/blocks/table/TableColumnModel.d.ts +2 -0
  98. package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.d.ts +1 -1
  99. package/es/flow-compat/passwordUtils.d.ts +1 -1
  100. package/es/index.d.ts +6 -0
  101. package/es/index.mjs +552 -459
  102. package/es/layout-manager/LayoutContentRoute.d.ts +14 -0
  103. package/es/layout-manager/LayoutManager.d.ts +22 -0
  104. package/es/layout-manager/LayoutRoute.d.ts +14 -0
  105. package/es/layout-manager/index.d.ts +13 -0
  106. package/es/layout-manager/types.d.ts +20 -0
  107. package/es/layout-manager/utils.d.ts +14 -0
  108. package/es/nocobase-buildin-plugin/index.d.ts +3 -10
  109. package/es/settings-center/index.d.ts +1 -1
  110. package/es/settings-center/plugin-manager/BulkEnableButton.d.ts +15 -0
  111. package/es/settings-center/plugin-manager/PluginCard.d.ts +15 -0
  112. package/es/settings-center/plugin-manager/PluginDetail.d.ts +16 -0
  113. package/es/settings-center/{PluginManagerPage.d.ts → plugin-manager/index.d.ts} +1 -7
  114. package/es/settings-center/plugin-manager/types.d.ts +34 -0
  115. package/lib/index.js +552 -459
  116. package/package.json +8 -7
  117. package/src/Application.tsx +51 -12
  118. package/src/BaseApplication.tsx +32 -0
  119. package/src/PluginSettingsManager.ts +1 -1
  120. package/src/RouterManager.tsx +17 -1
  121. package/src/__tests__/PluginSettingsManager.test.ts +41 -2
  122. package/src/__tests__/app.test.tsx +17 -1
  123. package/src/__tests__/globalDeps.test.ts +1 -0
  124. package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +50 -2
  125. package/src/__tests__/plugin-manager.test.tsx +177 -0
  126. package/src/__tests__/settings-center.test.tsx +24 -2
  127. package/src/authRedirect.ts +43 -0
  128. package/src/collection-field-interface/CollectionFieldInterface.ts +71 -77
  129. package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +201 -4
  130. package/src/collection-manager/field-configure.ts +548 -0
  131. package/src/collection-manager/field-validation.ts +195 -0
  132. package/src/collection-manager/filter-operators/index.ts +176 -0
  133. package/src/collection-manager/{interfaces/properties → filter-operators}/operators.ts +24 -13
  134. package/src/collection-manager/interfaces/checkbox.ts +2 -9
  135. package/src/collection-manager/interfaces/checkboxGroup.ts +2 -10
  136. package/src/collection-manager/interfaces/collection.ts +2 -15
  137. package/src/collection-manager/interfaces/color.ts +2 -2
  138. package/src/collection-manager/interfaces/createdAt.ts +2 -2
  139. package/src/collection-manager/interfaces/createdBy.ts +1 -12
  140. package/src/collection-manager/interfaces/dateOnly.ts +8 -2
  141. package/src/collection-manager/interfaces/datetime.ts +2 -2
  142. package/src/collection-manager/interfaces/datetimeNoTz.ts +2 -2
  143. package/src/collection-manager/interfaces/email.ts +2 -9
  144. package/src/collection-manager/interfaces/id.ts +1 -2
  145. package/src/collection-manager/interfaces/index.ts +2 -3
  146. package/src/collection-manager/interfaces/input.ts +2 -133
  147. package/src/collection-manager/interfaces/integer.ts +2 -71
  148. package/src/collection-manager/interfaces/json.tsx +17 -11
  149. package/src/collection-manager/interfaces/m2m.tsx +0 -21
  150. package/src/collection-manager/interfaces/m2o.tsx +0 -22
  151. package/src/collection-manager/interfaces/markdown.ts +2 -51
  152. package/src/collection-manager/interfaces/multipleSelect.ts +2 -14
  153. package/src/collection-manager/interfaces/nanoid.ts +2 -2
  154. package/src/collection-manager/interfaces/number.ts +2 -85
  155. package/src/collection-manager/interfaces/o2m.tsx +1 -22
  156. package/src/collection-manager/interfaces/obo.tsx +145 -0
  157. package/src/collection-manager/interfaces/oho.tsx +145 -0
  158. package/src/collection-manager/interfaces/password.ts +2 -44
  159. package/src/collection-manager/interfaces/percent.ts +2 -74
  160. package/src/collection-manager/interfaces/phone.ts +2 -2
  161. package/src/collection-manager/interfaces/properties/index.ts +0 -133
  162. package/src/collection-manager/interfaces/radioGroup.ts +2 -2
  163. package/src/collection-manager/interfaces/richText.ts +2 -51
  164. package/src/collection-manager/interfaces/select.ts +2 -14
  165. package/src/collection-manager/interfaces/snowflake-id.ts +2 -2
  166. package/src/collection-manager/interfaces/tableoid.ts +1 -2
  167. package/src/collection-manager/interfaces/textarea.ts +2 -51
  168. package/src/collection-manager/interfaces/time.ts +2 -2
  169. package/src/collection-manager/interfaces/types.ts +4 -12
  170. package/src/collection-manager/interfaces/unixTimestamp.tsx +2 -2
  171. package/src/collection-manager/interfaces/updatedAt.ts +2 -2
  172. package/src/collection-manager/interfaces/updatedBy.ts +1 -12
  173. package/src/collection-manager/interfaces/url.ts +2 -4
  174. package/src/collection-manager/interfaces/uuid.ts +2 -2
  175. package/src/collection-manager/template-fields.ts +109 -0
  176. package/src/components/KeepAlive.tsx +131 -0
  177. package/src/components/README.md +138 -6
  178. package/src/components/README.zh-CN.md +138 -7
  179. package/src/components/RouterBridge.tsx +28 -4
  180. package/src/components/__tests__/KeepAlive.test.tsx +63 -0
  181. package/src/components/__tests__/RouterBridge.test.tsx +27 -0
  182. package/src/components/form/DialogFormLayout.tsx +5 -29
  183. package/src/components/form/TypedVariableInput.tsx +441 -0
  184. package/src/components/form/VariableInput.tsx +101 -28
  185. package/src/components/form/__tests__/TypedVariableInput.test.tsx +152 -0
  186. package/src/components/form/__tests__/VariableInput.test.ts +85 -0
  187. package/src/components/form/filter/CollectionFilter.tsx +111 -0
  188. package/src/components/form/filter/CollectionFilterItem.tsx +184 -0
  189. package/src/components/form/filter/DateFilterDynamicComponent.tsx +283 -0
  190. package/src/components/form/filter/FilterValueInput.tsx +198 -0
  191. package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +247 -0
  192. package/src/components/form/filter/__tests__/DateFilterDynamicComponent.test.tsx +148 -0
  193. package/src/components/form/filter/__tests__/FilterValueInput.test.tsx +243 -0
  194. package/src/components/form/filter/__tests__/compileFilterGroup.test.ts +146 -0
  195. package/src/components/form/filter/index.ts +13 -0
  196. package/src/components/form/filter/useFilterActionProps.ts +203 -0
  197. package/src/components/form/index.tsx +2 -0
  198. package/src/data-source/ExtendCollectionsProvider.tsx +144 -0
  199. package/src/data-source/__tests__/ExtendCollectionsProvider.test.tsx +264 -0
  200. package/src/data-source/index.ts +10 -0
  201. package/src/flow/FlowPage.tsx +35 -7
  202. package/src/flow/__tests__/FlowPage.test.tsx +79 -0
  203. package/src/flow/__tests__/FlowRoute.test.tsx +537 -3
  204. package/src/flow/actions/__tests__/linkageRules.hiddenSync.restore.test.ts +100 -0
  205. package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +191 -0
  206. package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +23 -1
  207. package/src/flow/actions/__tests__/openView.subModelKey.test.tsx +33 -0
  208. package/src/flow/actions/aclCheck.tsx +4 -0
  209. package/src/flow/actions/aclCheckRefresh.tsx +4 -0
  210. package/src/flow/actions/dataScope.tsx +8 -1
  211. package/src/flow/actions/dateTimeFormat.tsx +12 -8
  212. package/src/flow/actions/linkageRules.tsx +127 -2
  213. package/src/flow/actions/linkageRulesRefresh.tsx +2 -3
  214. package/src/flow/actions/openView.tsx +28 -4
  215. package/src/flow/actions/setTargetDataScope.tsx +1 -1
  216. package/src/flow/admin-shell/AdminLayoutRouteCoordinator.ts +11 -329
  217. package/src/flow/admin-shell/BaseLayoutModel.tsx +455 -0
  218. package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +502 -0
  219. package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +547 -3
  220. package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +35 -7
  221. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +160 -0
  222. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +15 -13
  223. package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +28 -201
  224. package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +11 -2
  225. package/src/flow/admin-shell/admin-layout/AppListRender.tsx +139 -0
  226. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +76 -27
  227. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +149 -27
  228. package/src/flow/admin-shell/admin-layout/index.ts +3 -0
  229. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.test.ts +11 -0
  230. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.ts +2 -4
  231. package/src/flow/admin-shell/admin-layout/useApplications.tsx +34 -1
  232. package/src/flow/admin-shell/useAdminLayoutRoutePage.ts +10 -26
  233. package/src/flow/admin-shell/useLayoutRoutePage.ts +61 -0
  234. package/src/flow/common/Markdown/Edit.tsx +32 -4
  235. package/src/flow/components/AdminLayout.tsx +4 -154
  236. package/src/flow/components/ConditionBuilder.tsx +12 -2
  237. package/src/flow/components/FieldAssignRulesEditor.tsx +37 -46
  238. package/src/flow/components/FieldAssignValueInput.tsx +8 -4
  239. package/src/flow/components/FlowRoute.tsx +105 -15
  240. package/src/flow/components/__tests__/FieldAssignRulesEditor.test.tsx +112 -1
  241. package/src/flow/components/__tests__/fieldAssignOptions.test.ts +71 -1
  242. package/src/flow/components/code-editor/__tests__/runjsCompletions.test.ts +272 -27
  243. package/src/flow/components/code-editor/runjsCompletions.ts +490 -9
  244. package/src/flow/components/fieldAssignOptions.ts +23 -6
  245. package/src/flow/components/filter/LinkageFilterItem.tsx +22 -9
  246. package/src/flow/components/filter/VariableFilterItem.tsx +27 -9
  247. package/src/flow/components/filter/__tests__/fieldsToOptions.test.ts +48 -0
  248. package/src/flow/components/filter/__tests__/metaTreeAssociationDepth.test.ts +60 -0
  249. package/src/flow/components/filter/fieldsToOptions.ts +9 -4
  250. package/src/flow/components/filter/index.ts +3 -0
  251. package/src/flow/components/filter/metaTreeAssociationDepth.ts +69 -0
  252. package/src/flow/components/filter/useFilterOptions.ts +102 -0
  253. package/src/flow/index.ts +4 -0
  254. package/src/flow/models/actions/FilterActionModel.tsx +15 -3
  255. package/src/flow/models/actions/UpdateRecordActionModel.tsx +14 -95
  256. package/src/flow/models/actions/UpdateRecordActionUtils.ts +4 -7
  257. package/src/flow/models/actions/__tests__/AssignFormRefill.test.ts +116 -1
  258. package/src/flow/models/base/ActionModel.tsx +8 -1
  259. package/src/flow/models/base/AssociationFieldGroupModel.tsx +20 -3
  260. package/src/flow/models/base/GridModel.tsx +1 -1
  261. package/src/flow/models/base/PageModel/ChildPageModel.tsx +5 -1
  262. package/src/flow/models/base/PageModel/PageModel.tsx +51 -18
  263. package/src/flow/models/base/PageModel/RootPageModel.tsx +6 -13
  264. package/src/flow/models/base/PageModel/__tests__/ChildPageModel.test.ts +12 -0
  265. package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +102 -1
  266. package/src/flow/models/base/RouteModel.tsx +1 -1
  267. package/src/flow/models/base/__tests__/AssociationFieldGroupModel.test.ts +86 -0
  268. package/src/flow/models/base/__tests__/GridModel.render.test.tsx +53 -0
  269. package/src/flow/models/blocks/assign-form/AssignFormGridModel.tsx +12 -3
  270. package/src/flow/models/blocks/assign-form/AssignFormItemModel.tsx +77 -6
  271. package/src/flow/models/blocks/assign-form/__tests__/assignFieldValuesFlow.editor.test.tsx +216 -0
  272. package/src/flow/models/blocks/assign-form/assignFieldValuesFlow.tsx +308 -0
  273. package/src/flow/models/blocks/assign-form/index.ts +1 -0
  274. package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +1 -1
  275. package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +6 -1
  276. package/src/flow/models/blocks/filter-manager/flow-actions/connectFields.tsx +48 -6
  277. package/src/flow/models/blocks/form/FormActionGroupModel.tsx +14 -0
  278. package/src/flow/models/blocks/form/FormActionModel.tsx +30 -3
  279. package/src/flow/models/blocks/form/FormAssociationItemModel.tsx +9 -0
  280. package/src/flow/models/blocks/form/FormItemModel.tsx +17 -1
  281. package/src/flow/models/blocks/form/__tests__/FormActionGroupModel.test.ts +46 -0
  282. package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +17 -0
  283. package/src/flow/models/blocks/form/__tests__/FormItemModel.defineChildren.test.ts +27 -2
  284. package/src/flow/models/blocks/form/__tests__/submitHandler.test.ts +71 -0
  285. package/src/flow/models/blocks/form/submitHandler.ts +8 -1
  286. package/src/flow/models/blocks/form/submitValues.ts +4 -1
  287. package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +29 -0
  288. package/src/flow/models/blocks/form/value-runtime/runtime.ts +5 -1
  289. package/src/flow/models/blocks/table/TableBlockModel.tsx +118 -16
  290. package/src/flow/models/blocks/table/TableColumnModel.tsx +15 -5
  291. package/src/flow/models/blocks/table/TableCustomColumnModel.tsx +2 -2
  292. package/src/flow/models/blocks/table/__tests__/TableBlockModel.rowSelection.test.tsx +114 -0
  293. package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +18 -0
  294. package/src/flow/models/fields/AssociationFieldModel/SubFormFieldModel.tsx +7 -1
  295. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.tsx +6 -7
  296. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +1 -1
  297. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +6 -5
  298. package/src/flow/models/fields/ClickableFieldModel.tsx +9 -1
  299. package/src/flow/models/fields/CollectionSelectorFieldModel.tsx +8 -2
  300. package/src/flow/models/fields/DisplayEnumFieldModel.tsx +8 -2
  301. package/src/flow/models/fields/DisplayTimeFieldModel.tsx +1 -1
  302. package/src/flow/models/fields/TimeFieldModel.tsx +1 -1
  303. package/src/flow/models/fields/__tests__/TimeFieldModel.test.tsx +61 -0
  304. package/src/flow/models/fields/mobile-components/MobileDatePicker.tsx +19 -3
  305. package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +5 -3
  306. package/src/flow/models/fields/mobile-components/__tests__/MobileDatePicker.test.tsx +94 -0
  307. package/src/flow/models/fields/mobile-components/__tests__/MobileSelect.test.tsx +70 -0
  308. package/src/flow/models/topbar/TopbarActionModel.tsx +1 -1
  309. package/src/flow/utils/__tests__/dateTimeFormat.test.ts +91 -0
  310. package/src/index.ts +6 -0
  311. package/src/layout-manager/LayoutContentRoute.tsx +90 -0
  312. package/src/layout-manager/LayoutManager.tsx +185 -0
  313. package/src/layout-manager/LayoutRoute.tsx +138 -0
  314. package/src/layout-manager/__tests__/LayoutManager.test.tsx +335 -0
  315. package/src/layout-manager/__tests__/LayoutRoute.test.tsx +473 -0
  316. package/src/layout-manager/index.ts +14 -0
  317. package/src/layout-manager/types.ts +22 -0
  318. package/src/layout-manager/utils.ts +37 -0
  319. package/src/nocobase-buildin-plugin/index.tsx +69 -67
  320. package/src/nocobase-buildin-plugin/plugins/LocalePlugin.ts +1 -0
  321. package/src/settings-center/index.ts +1 -1
  322. package/src/settings-center/plugin-manager/BulkEnableButton.tsx +111 -0
  323. package/src/settings-center/plugin-manager/PluginCard.tsx +270 -0
  324. package/src/settings-center/plugin-manager/PluginDetail.tsx +195 -0
  325. package/src/settings-center/plugin-manager/index.tsx +254 -0
  326. package/src/settings-center/plugin-manager/types.ts +35 -0
  327. package/src/settings-center/utils.tsx +8 -1
  328. package/src/theme/__tests__/globalStyles.test.ts +24 -0
  329. package/src/theme/globalStyles.ts +10 -0
  330. package/src/utils/globalDeps.ts +2 -0
  331. package/es/collection-manager/interfaces/linkTo.d.ts +0 -90
  332. package/es/collection-manager/interfaces/o2o.d.ts +0 -621
  333. package/es/collection-manager/interfaces/properties/operators.d.ts +0 -294
  334. package/es/collection-manager/interfaces/subTable.d.ts +0 -172
  335. package/src/collection-manager/interfaces/linkTo.ts +0 -120
  336. package/src/collection-manager/interfaces/o2o.tsx +0 -561
  337. package/src/collection-manager/interfaces/subTable.ts +0 -218
  338. package/src/settings-center/PluginManagerPage.tsx +0 -162
@@ -7,161 +7,11 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
 
10
- import { useFlowEngine, FlowModelRenderer } from '@nocobase/flow-engine';
11
- import React, { type FC, useEffect, useMemo, useRef, useState } from 'react';
12
- import { getAdminLayoutModel, AdminLayoutModel } from '..';
13
- import { useApp } from '../../hooks/useApp';
14
- import { useLocation, useNavigate, useParams } from 'react-router-dom';
15
- import { NocoBaseDesktopRouteType } from '../../flow-compat';
16
- import {
17
- findFirstV2LandingRoute,
18
- resolveAdminRouteRuntimeTarget,
19
- toRouterNavigationPath,
20
- } from '../admin-shell/admin-layout/resolveAdminRouteRuntimeTarget';
10
+ import React, { type FC } from 'react';
11
+ import { LayoutRoute } from '../../layout-manager/LayoutRoute';
21
12
 
22
- const AdminLayoutEntryGuard: FC<{ children: React.ReactNode }> = ({ children }) => {
23
- const flowEngine = useFlowEngine();
24
- const app = useApp();
25
- const navigate = useNavigate();
26
- const location = useLocation();
27
- const params = useParams();
28
- const [ready, setReady] = useState(false);
29
- const replaceTriggeredRef = useRef(false);
30
- const routeRepository = flowEngine.context.routeRepository;
31
- const isAdminRoot = useMemo(() => {
32
- const pathname = toRouterNavigationPath(location.pathname, app.router.getBasename());
33
- return pathname === '/admin';
34
- }, [app, location.pathname]);
35
-
36
- useEffect(() => {
37
- replaceTriggeredRef.current = false;
38
- }, [location.pathname, location.search, location.hash, params.name]);
39
-
40
- useEffect(() => {
41
- let active = true;
42
-
43
- const run = async () => {
44
- setReady(false);
45
-
46
- if (!isAdminRoot && !params.name) {
47
- if (active) {
48
- setReady(true);
49
- }
50
- return;
51
- }
52
-
53
- if (!routeRepository?.isAccessibleLoaded?.()) {
54
- try {
55
- await routeRepository?.ensureAccessibleLoaded?.();
56
- } catch (_error) {
57
- if (active) {
58
- setReady(true);
59
- }
60
- return;
61
- }
62
- }
63
-
64
- if (!active || replaceTriggeredRef.current) {
65
- return;
66
- }
67
-
68
- if (params.name) {
69
- const currentRoute = routeRepository?.getRouteBySchemaUid?.(params.name);
70
- if (currentRoute?.type === NocoBaseDesktopRouteType.page) {
71
- const target = resolveAdminRouteRuntimeTarget({
72
- app,
73
- route: currentRoute,
74
- location: {
75
- pathname: window.location.pathname,
76
- search: window.location.search,
77
- hash: window.location.hash,
78
- },
79
- preserveLocationState: true,
80
- });
81
-
82
- if (target.navigationMode === 'document' && target.runtimePath) {
83
- replaceTriggeredRef.current = true;
84
- window.location.replace(target.runtimePath);
85
- return;
86
- }
87
- }
88
-
89
- if (active) {
90
- setReady(true);
91
- }
92
- return;
93
- }
94
-
95
- const firstAccessibleRoute = findFirstV2LandingRoute(routeRepository?.listAccessible?.() || []);
96
- if (!firstAccessibleRoute) {
97
- if (active) {
98
- setReady(true);
99
- }
100
- return;
101
- }
102
-
103
- const target = resolveAdminRouteRuntimeTarget({
104
- app,
105
- route: firstAccessibleRoute,
106
- });
107
-
108
- if (!target.runtimePath) {
109
- if (active) {
110
- setReady(true);
111
- }
112
- return;
113
- }
114
-
115
- replaceTriggeredRef.current = true;
116
- if (target.navigationMode === 'document') {
117
- window.location.replace(target.runtimePath);
118
- return;
119
- }
120
-
121
- navigate(toRouterNavigationPath(target.runtimePath, app.router.getBasename()), { replace: true });
122
- };
123
-
124
- void run();
125
-
126
- return () => {
127
- active = false;
128
- };
129
- }, [
130
- app,
131
- flowEngine,
132
- isAdminRoot,
133
- location.hash,
134
- location.pathname,
135
- location.search,
136
- navigate,
137
- params.name,
138
- routeRepository,
139
- ]);
140
-
141
- if (!ready) {
142
- return null;
143
- }
144
-
145
- return <>{children}</>;
146
- };
147
-
148
- const AdminLayout: FC = (props) => {
149
- const flowEngine = useFlowEngine();
150
- const model = getAdminLayoutModel(flowEngine, {
151
- create: true,
152
- props,
153
- use: AdminLayoutModel,
154
- });
155
-
156
- if (!model) {
157
- throw new Error('[NocoBase] Failed to create admin-layout-model.');
158
- }
159
-
160
- return (
161
- <AdminLayoutEntryGuard>
162
- <FlowModelRenderer model={model} />
163
- </AdminLayoutEntryGuard>
164
- );
13
+ const AdminLayout: FC = () => {
14
+ return <LayoutRoute layoutRouteName="admin" />;
165
15
  };
166
16
 
167
17
  export default AdminLayout;
@@ -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: { value: FilterGroupType; onChange: (value: FilterGroupType) => void; extraMetaTree?: MetaTreeNode[] }) => {
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 model={ctx.model} value={itemProps.value} extraMetaTree={props.extraMetaTree} />
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
  />
@@ -24,7 +24,7 @@ import type { CascaderProps, CollapseProps } from 'antd';
24
24
  import React from 'react';
25
25
  import { ConditionBuilder } from './ConditionBuilder';
26
26
  import { FieldAssignValueInput } from './FieldAssignValueInput';
27
- import type { FieldAssignCascaderOption } from './fieldAssignOptions';
27
+ import { buildFieldAssignCascaderOptionsFromCollection, type FieldAssignCascaderOption } from './fieldAssignOptions';
28
28
  import { isRunJSValue, isVariableExpression, type MetaTreeNode } from '@nocobase/flow-engine';
29
29
  import { isToManyAssociationField } from '../internal/utils/modelUtils';
30
30
 
@@ -36,6 +36,7 @@ type CollectionFieldLike = {
36
36
  type?: unknown;
37
37
  interface?: unknown;
38
38
  uiSchema?: unknown;
39
+ target?: unknown;
39
40
  targetKey?: unknown;
40
41
  targetCollectionTitleFieldName?: unknown;
41
42
  targetCollection?: any;
@@ -105,6 +106,7 @@ export interface FieldAssignRulesEditorProps {
105
106
  onSyncAssociationTitleField?: (params: SyncAssociationTitleFieldParams) => Promise<void> | void;
106
107
  /** 在日期字段下启用“日期变量替换 Constant 位”。 */
107
108
  enableDateVariableAsConstant?: boolean;
109
+ maxAssociationFieldDepth?: number;
108
110
  }
109
111
 
110
112
  export const FieldAssignRulesEditor: React.FC<FieldAssignRulesEditorProps> = (props) => {
@@ -123,9 +125,10 @@ export const FieldAssignRulesEditor: React.FC<FieldAssignRulesEditorProps> = (pr
123
125
  isTitleFieldCandidate,
124
126
  onSyncAssociationTitleField,
125
127
  enableDateVariableAsConstant = false,
128
+ maxAssociationFieldDepth = 2,
126
129
  } = props;
127
130
 
128
- const value = Array.isArray(rawValue) ? rawValue : [];
131
+ const value = React.useMemo(() => (Array.isArray(rawValue) ? rawValue : []), [rawValue]);
129
132
  const [cascaderOptions, setCascaderOptions] = React.useState<FieldAssignCascaderOption[]>(() =>
130
133
  Array.isArray(fieldOptions) ? (fieldOptions as FieldAssignCascaderOption[]) : [],
131
134
  );
@@ -171,6 +174,19 @@ export const FieldAssignRulesEditor: React.FC<FieldAssignRulesEditorProps> = (pr
171
174
  }
172
175
  }, [getRuleKey, syncingRuleKey, value]);
173
176
 
177
+ const parseTargetPathToSegments = React.useCallback((targetPath?: string): string[] => {
178
+ const raw = String(targetPath || '');
179
+ if (!raw) return [];
180
+ return raw
181
+ .split('.')
182
+ .map((s) => s.trim())
183
+ .filter(Boolean);
184
+ }, []);
185
+
186
+ const isAssociationFieldLike = React.useCallback((field?: CollectionFieldLike | null) => {
187
+ return !!(field?.isAssociationField?.() || field?.target || field?.targetCollection);
188
+ }, []);
189
+
174
190
  const buildCollectionMetaTreeNodes = React.useCallback(
175
191
  (collection: any, basePaths: string[], visited: Set<any>): MetaTreeNode[] => {
176
192
  if (!collection?.getFields) return [];
@@ -199,7 +215,7 @@ export const FieldAssignRulesEditor: React.FC<FieldAssignRulesEditorProps> = (pr
199
215
  paths: [...basePaths, name],
200
216
  };
201
217
 
202
- const isAssoc = !!f.isAssociationField?.();
218
+ const isAssoc = isAssociationFieldLike(f);
203
219
  const isToMany = isToManyAssociationField(f);
204
220
  if (isAssoc && !isToMany && f.targetCollection) {
205
221
  node.children = async () =>
@@ -211,7 +227,7 @@ export const FieldAssignRulesEditor: React.FC<FieldAssignRulesEditorProps> = (pr
211
227
 
212
228
  return nodes;
213
229
  },
214
- [t],
230
+ [isAssociationFieldLike, t],
215
231
  );
216
232
 
217
233
  const buildItemMetaTree = React.useCallback(
@@ -226,7 +242,7 @@ export const FieldAssignRulesEditor: React.FC<FieldAssignRulesEditorProps> = (pr
226
242
  for (let i = 0; i < segs.length; i++) {
227
243
  const seg = segs[i] as string;
228
244
  const field = current?.getField?.(seg);
229
- if (!field?.isAssociationField?.()) break;
245
+ if (!isAssociationFieldLike(field)) break;
230
246
  const toMany = isToManyAssociationField(field);
231
247
  const nextCollection = field?.targetCollection;
232
248
  if (!nextCollection) break;
@@ -291,7 +307,7 @@ export const FieldAssignRulesEditor: React.FC<FieldAssignRulesEditorProps> = (pr
291
307
 
292
308
  return [buildObjectNode(levels.length - 1, ['item'], 'item', 'Current item')];
293
309
  },
294
- [buildCollectionMetaTreeNodes, rootCollection, t],
310
+ [buildCollectionMetaTreeNodes, isAssociationFieldLike, parseTargetPathToSegments, rootCollection, t],
295
311
  );
296
312
 
297
313
  const patchItem = (index: number, patch: Partial<FieldAssignRuleItem>) => {
@@ -381,15 +397,6 @@ export const FieldAssignRulesEditor: React.FC<FieldAssignRulesEditorProps> = (pr
381
397
  [t],
382
398
  );
383
399
 
384
- const parseTargetPathToSegments = React.useCallback((targetPath?: string): string[] => {
385
- const raw = String(targetPath || '');
386
- if (!raw) return [];
387
- return raw
388
- .split('.')
389
- .map((s) => s.trim())
390
- .filter(Boolean);
391
- }, []);
392
-
393
400
  const getFieldLabel = (targetPath?: string) => {
394
401
  const segs = parseTargetPathToSegments(targetPath);
395
402
  if (!segs.length) return undefined;
@@ -413,7 +420,7 @@ export const FieldAssignRulesEditor: React.FC<FieldAssignRulesEditorProps> = (pr
413
420
  : seg,
414
421
  ),
415
422
  );
416
- if (field?.isAssociationField?.() && field?.targetCollection) {
423
+ if (isAssociationFieldLike(field as CollectionFieldLike) && field?.targetCollection) {
417
424
  collection = field.targetCollection;
418
425
  }
419
426
  }
@@ -435,7 +442,7 @@ export const FieldAssignRulesEditor: React.FC<FieldAssignRulesEditorProps> = (pr
435
442
 
436
443
  const isLast = i === segs.length - 1;
437
444
  if (isLast) {
438
- if (!field?.isAssociationField?.() || !field?.targetCollection) return null;
445
+ if (!isAssociationFieldLike(field) || !field?.targetCollection) return null;
439
446
 
440
447
  const targetCollection = field.targetCollection;
441
448
  const targetFields =
@@ -502,7 +509,7 @@ export const FieldAssignRulesEditor: React.FC<FieldAssignRulesEditorProps> = (pr
502
509
  };
503
510
  }
504
511
 
505
- if (!field?.isAssociationField?.() || !field?.targetCollection) {
512
+ if (!isAssociationFieldLike(field) || !field?.targetCollection) {
506
513
  return null;
507
514
  }
508
515
  collection = field.targetCollection;
@@ -510,7 +517,7 @@ export const FieldAssignRulesEditor: React.FC<FieldAssignRulesEditorProps> = (pr
510
517
 
511
518
  return null;
512
519
  },
513
- [isTitleFieldCandidate, parseTargetPathToSegments, rootCollection, t],
520
+ [isAssociationFieldLike, isTitleFieldCandidate, parseTargetPathToSegments, rootCollection, t],
514
521
  );
515
522
 
516
523
  const resolveTargetCollectionBySegments = React.useCallback(
@@ -519,42 +526,24 @@ export const FieldAssignRulesEditor: React.FC<FieldAssignRulesEditorProps> = (pr
519
526
  let collection = rootCollection;
520
527
  for (const seg of segments) {
521
528
  const field = collection?.getField?.(seg);
522
- if (!field?.isAssociationField?.() || !field?.targetCollection) {
529
+ if (!isAssociationFieldLike(field) || !field?.targetCollection) {
523
530
  return null;
524
531
  }
525
532
  collection = field.targetCollection;
526
533
  }
527
534
  return collection || null;
528
535
  },
529
- [rootCollection],
536
+ [isAssociationFieldLike, rootCollection],
530
537
  );
531
538
 
532
539
  const buildChildrenFromCollection = React.useCallback(
533
- (collection: any): FieldAssignCascaderOption[] => {
534
- const fields = typeof collection?.getFields === 'function' ? collection.getFields() || [] : [];
535
- const out: FieldAssignCascaderOption[] = [];
536
- for (const rawField of fields) {
537
- if (!rawField) continue;
538
- const f = rawField as CollectionFieldLike;
539
- const fieldInterface = typeof f.interface === 'string' ? f.interface : undefined;
540
- if (!fieldInterface) continue;
541
- if (fieldInterface === 'formula') continue;
542
-
543
- const name = String(f.name || '');
544
- if (!name) continue;
545
-
546
- const title = t(typeof f.title === 'string' ? f.title : name);
547
- const isAssoc = !!f.isAssociationField?.();
548
- const hasTarget = !!f.targetCollection;
549
- out.push({
550
- label: title,
551
- value: name,
552
- isLeaf: !(isAssoc && hasTarget),
553
- });
554
- }
555
- return out;
540
+ (collection: any, associationDepth: number): FieldAssignCascaderOption[] => {
541
+ return buildFieldAssignCascaderOptionsFromCollection(collection, t, {
542
+ associationDepth,
543
+ maxAssociationFieldDepth,
544
+ });
556
545
  },
557
- [t],
546
+ [maxAssociationFieldDepth, t],
558
547
  );
559
548
 
560
549
  const loadCascaderData = React.useCallback<NonNullable<CascaderProps<FieldAssignCascaderOption>['loadData']>>(
@@ -573,7 +562,7 @@ export const FieldAssignRulesEditor: React.FC<FieldAssignRulesEditorProps> = (pr
573
562
  return;
574
563
  }
575
564
 
576
- const children = buildChildrenFromCollection(targetCollection);
565
+ const children = buildChildrenFromCollection(targetCollection, segments.length);
577
566
  if (!children.length) {
578
567
  target.isLeaf = true;
579
568
  } else {
@@ -889,6 +878,7 @@ export const FieldAssignRulesEditor: React.FC<FieldAssignRulesEditorProps> = (pr
889
878
  value={item.value}
890
879
  onChange={(v) => patchItem(index, { value: v })}
891
880
  extraMetaTree={extraMetaTree}
881
+ maxAssociationFieldDepth={maxAssociationFieldDepth}
892
882
  {...(getValueInputProps?.(item, index) || {})}
893
883
  enableDateVariableAsConstant={enableDateVariableAsConstant}
894
884
  associationFieldNamesOverride={
@@ -959,6 +949,7 @@ export const FieldAssignRulesEditor: React.FC<FieldAssignRulesEditorProps> = (pr
959
949
  value={item.condition || { logic: '$and', items: [] }}
960
950
  onChange={(condition) => patchItem(index, { condition })}
961
951
  extraMetaTree={extraMetaTree}
952
+ maxAssociationFieldDepth={2}
962
953
  />
963
954
  </div>
964
955
  )}
@@ -40,6 +40,7 @@ import { resolveOperatorComponent } from '../internal/utils/operatorSchemaHelper
40
40
  import { InputFieldModel } from '../models/fields/InputFieldModel';
41
41
  import { normalizeFilterValueByOperator } from '../models/blocks/filter-form/valueNormalization';
42
42
  import { FieldAssignExactDatePicker, type ExactDatePickerMode } from './FieldAssignExactDatePicker';
43
+ import { limitAssociationMetaTree } from './filter/metaTreeAssociationDepth';
43
44
 
44
45
  const DATE_FIELD_INTERFACES = new Set(['date', 'datetime', 'datetimeNoTz', 'createdAt', 'updatedAt', 'unixTimestamp']);
45
46
 
@@ -340,6 +341,7 @@ interface Props {
340
341
  * 默认 false,保持历史行为。
341
342
  */
342
343
  enableDateVariableAsConstant?: boolean;
344
+ maxAssociationFieldDepth?: number;
343
345
  }
344
346
 
345
347
  type ResolvedFieldContext = {
@@ -718,6 +720,7 @@ export const FieldAssignValueInput: React.FC<Props> = ({
718
720
  preferFormItemFieldModel,
719
721
  associationFieldNamesOverride,
720
722
  enableDateVariableAsConstant = false,
723
+ maxAssociationFieldDepth = 2,
721
724
  }) => {
722
725
  const flowCtx = useFlowContext<FlowModelContext>();
723
726
  const normalizeEventValue = React.useCallback((eventOrValue: unknown) => {
@@ -859,7 +862,7 @@ export const FieldAssignValueInput: React.FC<Props> = ({
859
862
  fieldName,
860
863
  collectionField: nested.collectionField || null,
861
864
  };
862
- }, [flowCtx.model, itemModel, resolveNestedAssociationField, targetPath]);
865
+ }, [flowCtx, itemModel, resolveNestedAssociationField, targetPath]);
863
866
 
864
867
  const { collection, dataSource, blockModel, fieldPath, fieldName, collectionField: cf } = resolved;
865
868
  const itemCollectionField = (resolved?.itemModel as any)?.context?.collectionField;
@@ -1232,7 +1235,7 @@ export const FieldAssignValueInput: React.FC<Props> = ({
1232
1235
  );
1233
1236
  };
1234
1237
  return C;
1235
- }, [placeholder, tempRoot, coerceEmptyValueForRenderer, normalizeEventValue]);
1238
+ }, [placeholder, tempRoot, coerceEmptyValueForRenderer, normalizeEventValue, operator]);
1236
1239
 
1237
1240
  const DateVariableConstantEditor = React.useMemo(() => {
1238
1241
  const C: React.FC<any> = (inputProps) => {
@@ -1408,6 +1411,7 @@ export const FieldAssignValueInput: React.FC<Props> = ({
1408
1411
  const extra = extraMetaTreeRef.current;
1409
1412
  const extraTree = Array.isArray(extra) ? extra : [];
1410
1413
  const mergedBase = mergeItemMetaTreeForAssignValue(base as MetaTreeNode[], extraTree as MetaTreeNode[]);
1414
+ const limitedBase = limitAssociationMetaTree(mergedBase, { maxAssociationDepth: maxAssociationFieldDepth });
1411
1415
  return [
1412
1416
  {
1413
1417
  title: tExpr('Constant'),
@@ -1418,10 +1422,10 @@ export const FieldAssignValueInput: React.FC<Props> = ({
1418
1422
  },
1419
1423
  { title: tExpr('Null'), name: 'null', type: 'object', paths: ['null'], render: NullComponent },
1420
1424
  { title: tExpr('RunJS'), name: 'runjs', type: 'object', paths: ['runjs'], render: RunJSComponent },
1421
- ...mergedBase,
1425
+ ...limitedBase,
1422
1426
  ];
1423
1427
  };
1424
- }, [flowCtx, ConstantEditor, NullComponent, RunJSComponent]);
1428
+ }, [flowCtx, ConstantEditor, NullComponent, RunJSComponent, maxAssociationFieldDepth]);
1425
1429
 
1426
1430
  const displayValue = React.useMemo(() => {
1427
1431
  if (!useDateVariableConstant) {
@@ -7,15 +7,18 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
 
10
- import { useFlowEngine } from '@nocobase/flow-engine';
10
+ import { type FlowEngine, useFlowContext, useFlowEngine } from '@nocobase/flow-engine';
11
11
  import React, { useEffect, useMemo, useRef, useState } from 'react';
12
12
  import { deviceType } from 'react-device-detect';
13
- import { useAdminLayoutRoutePage } from '../admin-shell/useAdminLayoutRoutePage';
14
13
  import { useParams } from 'react-router-dom';
15
14
  import { useApp } from '../../hooks/useApp';
16
15
  import { NocoBaseDesktopRouteType } from '../../flow-compat';
17
16
  import { resolveAdminRouteRuntimeTarget } from '../admin-shell/admin-layout/resolveAdminRouteRuntimeTarget';
17
+ import { getAdminLayoutModel, type AdminLayoutModel } from '../admin-shell/admin-layout/AdminLayoutModel';
18
+ import { getLayoutModel, type BaseLayoutModel } from '../admin-shell/BaseLayoutModel';
19
+ import { useLayoutRoutePage } from '../admin-shell/useLayoutRoutePage';
18
20
  import { AppNotFound } from '../../components';
21
+ import { useKeepAlive } from '../../components/KeepAlive';
19
22
 
20
23
  type FlowRouteGuardState = {
21
24
  pending: boolean;
@@ -23,8 +26,66 @@ type FlowRouteGuardState = {
23
26
  notFound: boolean;
24
27
  };
25
28
 
26
- const BridgeFlowRoute = ({ pageUid }: { pageUid: string }) => {
29
+ export type LegacyPageBehavior = 'redirect' | 'notFound' | 'bridge';
30
+
31
+ export type FlowRouteProps = {
32
+ pageUid?: string;
33
+ active?: boolean;
34
+ getLayoutModel?: (flowEngine: FlowEngine) => BaseLayoutModel | undefined;
35
+ legacyPageBehavior?: LegacyPageBehavior;
36
+ };
37
+
38
+ const getDefaultAdminLayoutModel = (flowEngine: FlowEngine) =>
39
+ getAdminLayoutModel<AdminLayoutModel>(flowEngine, { required: true });
40
+
41
+ const getDefaultLayoutModel = (flowEngine: FlowEngine, contextLayout?: any) => {
42
+ const layout = contextLayout || flowEngine.context.layout;
43
+
44
+ if (layout?.uid) {
45
+ return getLayoutModel<BaseLayoutModel>(flowEngine, layout.uid, { required: true });
46
+ }
47
+
48
+ return getDefaultAdminLayoutModel(flowEngine);
49
+ };
50
+
51
+ const getDefaultLegacyPageBehavior = (flowEngine: FlowEngine, contextLayout?: any): LegacyPageBehavior => {
52
+ const layout = contextLayout || flowEngine.context.layout;
53
+
54
+ if (layout?.routeName && layout.routeName !== 'admin') {
55
+ return 'notFound';
56
+ }
57
+
58
+ return 'redirect';
59
+ };
60
+
61
+ const hasFlowModel = async (flowEngine: FlowEngine, pageUid: string) => {
62
+ if (flowEngine.getModel(pageUid)) {
63
+ return true;
64
+ }
65
+
66
+ const modelData = await flowEngine.modelRepository?.findOne({ uid: pageUid }).catch(() => null);
67
+ if (modelData?.uid) {
68
+ return true;
69
+ }
70
+
71
+ const model = await flowEngine.loadModel({ uid: pageUid }).catch(() => null);
72
+ if (model && flowEngine.getModel(pageUid) === model) {
73
+ flowEngine.removeModelWithSubModels(pageUid);
74
+ }
75
+ return !!model;
76
+ };
77
+
78
+ const BridgeFlowRoute = ({
79
+ pageUid,
80
+ active,
81
+ getLayoutModel,
82
+ }: {
83
+ pageUid: string;
84
+ active?: boolean;
85
+ getLayoutModel: (flowEngine: FlowEngine) => BaseLayoutModel | undefined;
86
+ }) => {
27
87
  const flowEngine = useFlowEngine();
88
+ const { active: keepAliveActive } = useKeepAlive();
28
89
  const routeRepository = flowEngine.context.routeRepository;
29
90
  const refreshDesktopRoutes = React.useMemo(
30
91
  () => routeRepository?.refreshAccessible.bind(routeRepository),
@@ -60,11 +121,13 @@ const BridgeFlowRoute = ({ pageUid }: { pageUid: string }) => {
60
121
  });
61
122
  }, [flowEngine]);
62
123
 
63
- useAdminLayoutRoutePage({
124
+ useLayoutRoutePage({
64
125
  flowEngine,
65
126
  pageUid,
127
+ active: active ?? keepAliveActive,
66
128
  refreshDesktopRoutes,
67
129
  layoutContentRef,
130
+ getLayoutModel,
68
131
  });
69
132
 
70
133
  return <div ref={layoutContentRef} />;
@@ -84,12 +147,20 @@ const BridgeFlowRoute = ({ pageUid }: { pageUid: string }) => {
84
147
  * @returns {JSX.Element} 当前动态页面的布局挂载节点
85
148
  * @throws {Error} 当缺少 `route.params.name` 时抛出异常
86
149
  */
87
- const FlowRoute = () => {
150
+ const FlowRoute = (props: FlowRouteProps = {}) => {
88
151
  const flowEngine = useFlowEngine();
152
+ const flowContext = useFlowContext<any>();
153
+ const contextLayout = flowContext?.layout;
154
+ const getLayoutModel = useMemo(
155
+ () => props.getLayoutModel || ((engine: FlowEngine) => getDefaultLayoutModel(engine, contextLayout)),
156
+ [contextLayout, props.getLayoutModel],
157
+ );
158
+ const legacyPageBehavior = props.legacyPageBehavior || getDefaultLegacyPageBehavior(flowEngine, contextLayout);
89
159
  const app = useApp();
90
160
  const routeRepository = flowEngine.context.routeRepository;
91
161
  const params = useParams();
92
- const pageUid = params?.name;
162
+ const pageUid = props.pageUid || params?.name;
163
+ const skipRouteRepositoryCheck = !routeRepository;
93
164
  const [guardState, setGuardState] = useState<FlowRouteGuardState>({
94
165
  pending: true,
95
166
  allowBridge: false,
@@ -99,7 +170,7 @@ const FlowRoute = () => {
99
170
  const requestIdRef = useRef(0);
100
171
 
101
172
  if (!pageUid) {
102
- throw new Error('[NocoBase] FlowRoute requires route.params.name.');
173
+ throw new Error('[NocoBase] FlowRoute requires pageUid or route.params.name.');
103
174
  }
104
175
 
105
176
  useEffect(() => {
@@ -109,7 +180,7 @@ const FlowRoute = () => {
109
180
  const run = async () => {
110
181
  setGuardState({ pending: true, allowBridge: false, notFound: false });
111
182
 
112
- if (!routeRepository?.isAccessibleLoaded?.()) {
183
+ if (!skipRouteRepositoryCheck && !routeRepository?.isAccessibleLoaded?.()) {
113
184
  try {
114
185
  await routeRepository?.ensureAccessibleLoaded?.();
115
186
  } catch (_error) {
@@ -124,8 +195,26 @@ const FlowRoute = () => {
124
195
  return;
125
196
  }
126
197
 
127
- const route = routeRepository?.getRouteBySchemaUid?.(pageUid);
198
+ const route = skipRouteRepositoryCheck ? undefined : routeRepository?.getRouteBySchemaUid?.(pageUid);
199
+ if (!route && legacyPageBehavior === 'notFound') {
200
+ const flowModelExists = await hasFlowModel(flowEngine, pageUid);
201
+ if (active && requestId === requestIdRef.current) {
202
+ setGuardState({ pending: false, allowBridge: flowModelExists, notFound: !flowModelExists });
203
+ }
204
+ return;
205
+ }
206
+
128
207
  if (route?.type === NocoBaseDesktopRouteType.page) {
208
+ if (legacyPageBehavior === 'notFound') {
209
+ setGuardState({ pending: false, allowBridge: false, notFound: true });
210
+ return;
211
+ }
212
+
213
+ if (legacyPageBehavior === 'bridge') {
214
+ setGuardState({ pending: false, allowBridge: true, notFound: false });
215
+ return;
216
+ }
217
+
129
218
  const target = resolveAdminRouteRuntimeTarget({
130
219
  app,
131
220
  route,
@@ -161,22 +250,23 @@ const FlowRoute = () => {
161
250
  return () => {
162
251
  active = false;
163
252
  };
164
- }, [app, pageUid, routeRepository]);
253
+ }, [app, flowEngine, legacyPageBehavior, pageUid, routeRepository, skipRouteRepositoryCheck]);
165
254
 
166
255
  const content = useMemo(() => {
167
256
  if (guardState.pending) {
168
257
  return null;
169
258
  }
170
259
 
260
+ if (guardState.notFound) {
261
+ return <AppNotFound />;
262
+ }
263
+
171
264
  if (!guardState.allowBridge) {
172
- if (guardState.notFound) {
173
- return <AppNotFound />;
174
- }
175
265
  return null;
176
266
  }
177
267
 
178
- return <BridgeFlowRoute pageUid={pageUid} />;
179
- }, [guardState.allowBridge, guardState.notFound, guardState.pending, pageUid]);
268
+ return <BridgeFlowRoute pageUid={pageUid} active={props.active} getLayoutModel={getLayoutModel} />;
269
+ }, [getLayoutModel, guardState.allowBridge, guardState.notFound, guardState.pending, pageUid, props.active]);
180
270
 
181
271
  return content;
182
272
  };