@nocobase/client-v2 2.2.0-beta.8 → 2.2.0

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 (271) hide show
  1. package/es/APIClient.d.ts +13 -3
  2. package/es/BaseApplication.d.ts +3 -0
  3. package/es/RouteRepository.d.ts +8 -0
  4. package/es/RouterManager.d.ts +15 -0
  5. package/es/authRedirect.d.ts +1 -0
  6. package/es/collection-field-interface/CollectionFieldInterface.d.ts +1 -0
  7. package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +1 -0
  8. package/es/components/category-tabs/SortableCategoryTabs.d.ts +55 -0
  9. package/es/components/category-tabs/index.d.ts +9 -0
  10. package/es/components/form/ScanInput/useCodeScanner.d.ts +3 -2
  11. package/es/components/form/TypedVariableInput.d.ts +31 -5
  12. package/es/components/form/filter/CollectionFilter.d.ts +2 -0
  13. package/es/components/form/filter/CollectionFilterItem.d.ts +11 -1
  14. package/es/components/form/filter/CollectionFilterPanel.d.ts +2 -0
  15. package/es/components/form/filter/index.d.ts +2 -0
  16. package/es/components/form/filter/useFilterActionProps.d.ts +2 -0
  17. package/es/components/index.d.ts +1 -0
  18. package/es/entry-actions/EntryActionManager.d.ts +24 -0
  19. package/es/entry-actions/index.d.ts +9 -0
  20. package/es/flow/actions/index.d.ts +1 -1
  21. package/es/flow/actions/linkageRules.d.ts +27 -0
  22. package/es/flow/admin-shell/BaseLayoutModel.d.ts +6 -0
  23. package/es/flow/admin-shell/BaseLayoutRouteCoordinator.d.ts +7 -0
  24. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +1 -0
  25. package/es/flow/admin-shell/admin-layout/AppListRender.d.ts +2 -1
  26. package/es/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.d.ts +24 -0
  27. package/es/flow/admin-shell/admin-layout/index.d.ts +1 -0
  28. package/es/flow/admin-shell/admin-layout/useApplications.d.ts +7 -2
  29. package/es/flow/components/FieldAssignExactDatePicker.d.ts +1 -0
  30. package/es/flow/components/FieldAssignValueInput.d.ts +7 -24
  31. package/es/flow/components/FlowRoute.d.ts +2 -2
  32. package/es/flow/components/RunJSValueEditor.d.ts +1 -0
  33. package/es/flow/components/field-value-variable/DateVariableEditor.d.ts +24 -0
  34. package/es/flow/components/field-value-variable/FieldValueVariableInput.d.ts +31 -0
  35. package/es/flow/components/field-value-variable/dateValue.d.ts +36 -0
  36. package/es/flow/components/field-value-variable/index.d.ts +11 -0
  37. package/es/flow/components/filter/FilterGroup.d.ts +1 -0
  38. package/es/flow/components/filter/VariableFilterItem.d.ts +6 -1
  39. package/es/flow/components/placeholders/BlockPlaceholder.d.ts +1 -0
  40. package/es/flow/index.d.ts +1 -0
  41. package/es/flow/internal/registerDeviceTypeContext.d.ts +10 -0
  42. package/es/flow/internal/utils/operatorSchemaHelper.d.ts +2 -2
  43. package/es/flow/models/actions/UpdateRecordActionUtils.d.ts +8 -2
  44. package/es/flow/models/base/ActionModelCore.d.ts +6 -2
  45. package/es/flow/models/base/BlockGridModel.d.ts +3 -0
  46. package/es/flow/models/base/CollectionBlockModel.d.ts +3 -0
  47. package/es/flow/models/base/PageModel/PageModel.d.ts +12 -0
  48. package/es/flow/models/base/PageModel/PageModelTabBar.d.ts +22 -0
  49. package/es/flow/models/base/PageModel/PageTabModel.d.ts +12 -0
  50. package/es/flow/models/blocks/details/DetailsBlockModel.d.ts +3 -0
  51. package/es/flow/models/blocks/filter-form/FilterFormGridModel.d.ts +1 -0
  52. package/es/flow/models/blocks/filter-form/FilterFormSubmitActionModel.d.ts +3 -1
  53. package/es/flow/models/blocks/form/FormBlockModel.d.ts +7 -0
  54. package/es/flow/models/blocks/form/FormGridModel.d.ts +6 -0
  55. package/es/flow/models/blocks/form/QuickEditFormModel.d.ts +17 -2
  56. package/es/flow/models/blocks/form/value-runtime/rules.d.ts +1 -0
  57. package/es/flow/models/blocks/table/JSColumnModel.d.ts +2 -0
  58. package/es/flow/models/blocks/table/TableBlockModel.d.ts +1 -0
  59. package/es/flow/models/blocks/table/TableColumnModel.d.ts +12 -0
  60. package/es/flow/models/blocks/table/utils.d.ts +1 -0
  61. package/es/flow/models/fields/AssociationFieldModel/RecordPickerFieldModel.d.ts +2 -1
  62. package/es/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.d.ts +0 -17
  63. package/es/flow/models/fields/AssociationFieldModel/recordSelectShared.d.ts +41 -0
  64. package/es/flow/models/fields/DateTimeFieldModel/dateLimit.d.ts +15 -7
  65. package/es/flow/routeTransientInputArgs.d.ts +14 -0
  66. package/es/flow-compat/fieldValidationConstants.d.ts +1 -1
  67. package/es/flow-compat/passwordUtils.d.ts +1 -1
  68. package/es/flow-compat/routeTypes.d.ts +1 -0
  69. package/es/index.d.ts +7 -0
  70. package/es/index.mjs +301 -177
  71. package/es/ui-operation/index.d.ts +15 -0
  72. package/es/ui-operation/ui-operation-codec.d.ts +10 -0
  73. package/es/utils/getRouteRuntimeVersion.d.ts +23 -0
  74. package/es/utils/index.d.ts +1 -0
  75. package/es/utils/markdownSanitize.d.ts +11 -0
  76. package/lib/index.js +313 -189
  77. package/lib/locale/languageCodes.js +3 -1
  78. package/package.json +9 -7
  79. package/src/APIClient.ts +90 -7
  80. package/src/Application.tsx +7 -1
  81. package/src/BaseApplication.tsx +13 -6
  82. package/src/RouteRepository.ts +25 -0
  83. package/src/RouterManager.tsx +142 -1
  84. package/src/__tests__/APIClient.test.tsx +58 -0
  85. package/src/__tests__/RouteRepository.test.ts +23 -0
  86. package/src/__tests__/RouterManager.test.ts +125 -0
  87. package/src/__tests__/app.test.tsx +73 -0
  88. package/src/__tests__/authRedirect.test.ts +17 -0
  89. package/src/__tests__/browserChecker.test.ts +61 -0
  90. package/src/__tests__/getRouteRuntimeVersion.test.ts +68 -0
  91. package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +35 -2
  92. package/src/__tests__/settings-center.test.tsx +270 -6
  93. package/src/acl/ACLProvider.tsx +2 -0
  94. package/src/acl/__tests__/ACLProvider.test.tsx +92 -0
  95. package/src/authRedirect.ts +38 -0
  96. package/src/collection-field-interface/CollectionFieldInterface.ts +1 -0
  97. package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +1 -0
  98. package/src/collection-manager/field-validation.ts +1 -1
  99. package/src/components/README.md +7 -1
  100. package/src/components/README.zh-CN.md +6 -1
  101. package/src/components/category-tabs/SortableCategoryTabs.tsx +210 -0
  102. package/src/components/category-tabs/index.ts +10 -0
  103. package/src/components/form/JsonTextArea.tsx +4 -0
  104. package/src/components/form/ScanInput/CodeScanner.tsx +79 -35
  105. package/src/components/form/ScanInput/__tests__/CodeScanner.test.tsx +101 -0
  106. package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +284 -11
  107. package/src/components/form/ScanInput/useCodeScanner.ts +332 -24
  108. package/src/components/form/TypedVariableInput.tsx +452 -101
  109. package/src/components/form/__tests__/TypedVariableInput.test.tsx +364 -12
  110. package/src/components/form/filter/CollectionFilter.tsx +4 -0
  111. package/src/components/form/filter/CollectionFilterItem.tsx +36 -9
  112. package/src/components/form/filter/CollectionFilterPanel.tsx +4 -0
  113. package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +55 -0
  114. package/src/components/form/filter/__tests__/useFilterActionProps.test.tsx +95 -0
  115. package/src/components/form/filter/index.ts +2 -0
  116. package/src/components/form/filter/useFilterActionProps.ts +37 -6
  117. package/src/components/form/table/Table.tsx +81 -6
  118. package/src/components/form/table/__tests__/Table.columnWidth.test.tsx +433 -0
  119. package/src/components/index.ts +1 -0
  120. package/src/entry-actions/EntryActionManager.ts +76 -0
  121. package/src/entry-actions/index.ts +10 -0
  122. package/src/flow/FlowPage.tsx +38 -3
  123. package/src/flow/__tests__/FlowPage.test.tsx +201 -27
  124. package/src/flow/__tests__/FlowRoute.test.tsx +675 -12
  125. package/src/flow/__tests__/PluginFlowEngine.test.ts +58 -0
  126. package/src/flow/actions/__tests__/actionLinkageRules.forkProps.test.ts +314 -0
  127. package/src/flow/actions/__tests__/dataScopeFilter.test.ts +62 -0
  128. package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +102 -0
  129. package/src/flow/actions/__tests__/linkageRules.actionStates.test.ts +33 -0
  130. package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +72 -0
  131. package/src/flow/actions/__tests__/linkageRules.tab.test.ts +171 -0
  132. package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +7 -3
  133. package/src/flow/actions/__tests__/openView.defineProps.route.test.tsx +80 -20
  134. package/src/flow/actions/dataScopeFilter.ts +10 -1
  135. package/src/flow/actions/dateTimeFormat.tsx +2 -2
  136. package/src/flow/actions/index.ts +2 -0
  137. package/src/flow/actions/linkageRules.tsx +144 -26
  138. package/src/flow/actions/linkageRulesRefresh.tsx +2 -6
  139. package/src/flow/actions/openView.tsx +21 -1
  140. package/src/flow/admin-shell/BaseLayoutModel.tsx +124 -0
  141. package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +184 -42
  142. package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +1016 -119
  143. package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +41 -5
  144. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.test.tsx +177 -1
  145. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +20 -0
  146. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +78 -59
  147. package/src/flow/admin-shell/admin-layout/AppListRender.tsx +13 -2
  148. package/src/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.tsx +289 -0
  149. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +253 -6
  150. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +228 -0
  151. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +436 -2
  152. package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +48 -0
  153. package/src/flow/admin-shell/admin-layout/index.ts +1 -0
  154. package/src/flow/admin-shell/admin-layout/useApplications.tsx +81 -12
  155. package/src/flow/common/Markdown/Display.tsx +14 -3
  156. package/src/flow/common/Markdown/Edit.tsx +19 -7
  157. package/src/flow/common/Markdown/style.ts +4 -0
  158. package/src/flow/components/BlockItemCard.tsx +2 -2
  159. package/src/flow/components/FieldAssignExactDatePicker.tsx +25 -11
  160. package/src/flow/components/FieldAssignValueInput.tsx +72 -584
  161. package/src/flow/components/FlowRoute.tsx +204 -46
  162. package/src/flow/components/RunJSValueEditor.tsx +9 -1
  163. package/src/flow/components/__tests__/FieldAssignValueInput.context.test.tsx +216 -0
  164. package/src/flow/components/field-value-variable/DateVariableEditor.tsx +181 -0
  165. package/src/flow/components/field-value-variable/FieldValueVariableInput.tsx +326 -0
  166. package/src/flow/components/field-value-variable/__tests__/FieldValueVariableInput.test.tsx +380 -0
  167. package/src/flow/components/field-value-variable/dateValue.ts +223 -0
  168. package/src/flow/components/field-value-variable/index.ts +12 -0
  169. package/src/flow/components/filter/FilterGroup.tsx +33 -5
  170. package/src/flow/components/filter/VariableFilterItem.tsx +170 -13
  171. package/src/flow/components/filter/__tests__/FilterGroup.test.tsx +45 -0
  172. package/src/flow/components/filter/__tests__/VariableFilterItem.leftMetaTree.test.tsx +9 -0
  173. package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +195 -1
  174. package/src/flow/components/placeholders/BlockPlaceholder.tsx +70 -23
  175. package/src/flow/components/placeholders/__tests__/BlockPlaceholder.test.tsx +57 -7
  176. package/src/flow/index.ts +3 -0
  177. package/src/flow/internal/components/Markdown/util.ts +2 -1
  178. package/src/flow/internal/registerDeviceTypeContext.ts +39 -0
  179. package/src/flow/internal/utils/operatorSchemaHelper.ts +3 -2
  180. package/src/flow/models/actions/UpdateRecordActionModel.tsx +18 -1
  181. package/src/flow/models/actions/UpdateRecordActionUtils.ts +18 -6
  182. package/src/flow/models/actions/__tests__/UpdateRecordActionModel.test.ts +76 -4
  183. package/src/flow/models/base/ActionModel.tsx +10 -8
  184. package/src/flow/models/base/ActionModelCore.tsx +16 -2
  185. package/src/flow/models/base/BlockGridModel.tsx +26 -0
  186. package/src/flow/models/base/CollectionBlockModel.tsx +38 -3
  187. package/src/flow/models/base/PageModel/PageModel.tsx +387 -53
  188. package/src/flow/models/base/PageModel/PageModelTabBar.tsx +114 -0
  189. package/src/flow/models/base/PageModel/PageTabModel.tsx +249 -18
  190. package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +790 -10
  191. package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.module-isolation.test.tsx +130 -0
  192. package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.test.tsx +118 -0
  193. package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +1075 -7
  194. package/src/flow/models/base/__tests__/ActionModelCore.render.test.tsx +49 -0
  195. package/src/flow/models/base/__tests__/BlockGridModel.selectSceneActivation.test.ts +124 -0
  196. package/src/flow/models/base/__tests__/CollectionBlockModel.addAppends.test.ts +41 -0
  197. package/src/flow/models/base/__tests__/CollectionBlockModel.initialBeforeRenderRefresh.test.ts +125 -9
  198. package/src/flow/models/blocks/assign-form/AssignFormItemModel.tsx +28 -53
  199. package/src/flow/models/blocks/details/DetailsBlockModel.tsx +8 -3
  200. package/src/flow/models/blocks/details/__tests__/DetailsBlockModel.initialPaginationChangeRefresh.test.ts +32 -1
  201. package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +28 -3
  202. package/src/flow/models/blocks/filter-form/FilterFormGridModel.tsx +36 -5
  203. package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +155 -25
  204. package/src/flow/models/blocks/filter-form/FilterFormSubmitActionModel.tsx +57 -4
  205. package/src/flow/models/blocks/filter-form/__tests__/FilterFormGridModel.onModelCreated.test.ts +174 -1
  206. package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +395 -1
  207. package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.getFilterValue.test.ts +72 -0
  208. package/src/flow/models/blocks/filter-form/__tests__/FilterFormSubmitActionModel.lifecycle.test.ts +90 -0
  209. package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +66 -4
  210. package/src/flow/models/blocks/filter-manager/FilterManager.ts +5 -0
  211. package/src/flow/models/blocks/filter-manager/__tests__/FilterManager.test.ts +40 -0
  212. package/src/flow/models/blocks/form/FormBlockModel.tsx +47 -0
  213. package/src/flow/models/blocks/form/FormGridModel.tsx +4 -0
  214. package/src/flow/models/blocks/form/QuickEditFormModel.tsx +189 -36
  215. package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +8 -2
  216. package/src/flow/models/blocks/form/__tests__/QuickEditFormModel.quickEdit.test.ts +350 -2
  217. package/src/flow/models/blocks/form/__tests__/runJsFormSubmit.test.ts +131 -0
  218. package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +435 -0
  219. package/src/flow/models/blocks/form/value-runtime/rules.ts +67 -14
  220. package/src/flow/models/blocks/form/value-runtime/runtime.ts +43 -19
  221. package/src/flow/models/blocks/js-block/JSBlock.tsx +1 -1
  222. package/src/flow/models/blocks/table/JSColumnModel.tsx +10 -1
  223. package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +2 -1
  224. package/src/flow/models/blocks/table/TableBlockModel.tsx +29 -3
  225. package/src/flow/models/blocks/table/TableColumnModel.tsx +54 -11
  226. package/src/flow/models/blocks/table/__tests__/JSColumnModel.test.tsx +52 -5
  227. package/src/flow/models/blocks/table/__tests__/TableBlockModel.filterReset.test.ts +78 -0
  228. package/src/flow/models/blocks/table/__tests__/TableBlockModel.quickEditRefresh.test.ts +12 -0
  229. package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +84 -0
  230. package/src/flow/models/blocks/table/utils.ts +7 -0
  231. package/src/flow/models/fields/AssociationFieldModel/CascadeSelectFieldModel.tsx +33 -1
  232. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +10 -1
  233. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/__tests__/popupContext.test.ts +120 -0
  234. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/actions/PopupSubTableEditActionModel.tsx +10 -2
  235. package/src/flow/models/fields/AssociationFieldModel/RecordPickerFieldModel.tsx +24 -1
  236. package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +54 -149
  237. package/src/flow/models/fields/AssociationFieldModel/__tests__/RecordPickerFieldModel.itemContext.test.ts +167 -3
  238. package/src/flow/models/fields/AssociationFieldModel/recordSelectShared.tsx +156 -0
  239. package/src/flow/models/fields/DateTimeFieldModel/__tests__/DateTimeNoTzFieldModel.dateLimit.test.tsx +149 -0
  240. package/src/flow/models/fields/DateTimeFieldModel/dateLimit.ts +149 -113
  241. package/src/flow/models/fields/DisplayNumberFieldModel.tsx +1 -1
  242. package/src/flow/models/fields/JsonFieldModel.tsx +31 -8
  243. package/src/flow/models/fields/NumberFieldModel.tsx +1 -1
  244. package/src/flow/models/fields/TextareaFieldModel.tsx +42 -19
  245. package/src/flow/models/fields/VariableFieldFormModel.tsx +3 -3
  246. package/src/flow/models/fields/__tests__/DisplayNumberFieldModel.test.ts +33 -0
  247. package/src/flow/models/fields/__tests__/JsonFieldModel.test.ts +82 -0
  248. package/src/flow/models/fields/__tests__/NumberFieldModel.test.tsx +47 -0
  249. package/src/flow/models/fields/__tests__/TextareaFieldModel.test.tsx +32 -1
  250. package/src/flow/models/fields/__tests__/VariableFieldFormModel.test.tsx +51 -0
  251. package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +26 -10
  252. package/src/flow/models/fields/mobile-components/MobileSelect.tsx +51 -14
  253. package/src/flow/models/fields/mobile-components/__tests__/MobileSelect.test.tsx +139 -13
  254. package/src/flow/models/topbar/TopbarActionModel.tsx +78 -3
  255. package/src/flow/routeTransientInputArgs.ts +27 -0
  256. package/src/flow/utils/__tests__/dateTimeFormat.test.ts +42 -0
  257. package/src/flow/utils/dataScopeFormValueClear.ts +4 -3
  258. package/src/flow-compat/fieldValidationConstants.ts +1 -1
  259. package/src/flow-compat/routeTypes.ts +1 -0
  260. package/src/index.ts +7 -0
  261. package/src/layout-manager/LayoutContentRoute.tsx +2 -1
  262. package/src/layout-manager/LayoutRoute.tsx +2 -1
  263. package/src/layout-manager/__tests__/LayoutRoute.test.tsx +87 -1
  264. package/src/locale/languageCodes.ts +3 -1
  265. package/src/nocobase-buildin-plugin/index.tsx +19 -1
  266. package/src/settings-center/AdminSettingsLayout.tsx +23 -2
  267. package/src/ui-operation/index.ts +33 -0
  268. package/src/ui-operation/ui-operation-codec.ts +54 -0
  269. package/src/utils/getRouteRuntimeVersion.ts +185 -0
  270. package/src/utils/index.tsx +1 -0
  271. package/src/utils/markdownSanitize.ts +88 -0
@@ -15,8 +15,10 @@ import {
15
15
  DndProvider,
16
16
  DragHandler,
17
17
  Droppable,
18
+ isCtxDateExpression,
18
19
  isRunJSValue,
19
20
  normalizeRunJSValue,
21
+ parseCtxDateExpression,
20
22
  tExpr,
21
23
  FlowModelRenderer,
22
24
  FlowSettingsButton,
@@ -44,6 +46,15 @@ import { normalizeFilterValueByOperator } from './valueNormalization';
44
46
 
45
47
  const RELATION_FIELD_TYPES = ['belongsTo', 'hasOne', 'hasMany', 'belongsToMany', 'belongsToArray'];
46
48
  const NUMERIC_FIELD_TYPES = ['integer', 'float', 'double', 'decimal'];
49
+ const DATE_FILTER_OPERATORS = new Set([
50
+ '$dateOn',
51
+ '$dateNotOn',
52
+ '$dateBefore',
53
+ '$dateAfter',
54
+ '$dateNotBefore',
55
+ '$dateNotAfter',
56
+ '$dateBetween',
57
+ ]);
47
58
 
48
59
  function getFilterFormFieldMetaType(field: CollectionField) {
49
60
  if (RELATION_FIELD_TYPES.includes(field.type)) {
@@ -66,6 +77,14 @@ function getFilterFormFieldMetaType(field: CollectionField) {
66
77
  }
67
78
  }
68
79
 
80
+ function parseDateFilterDefaultValue(operator: string | undefined, rawValue: unknown) {
81
+ if (!operator || !DATE_FILTER_OPERATORS.has(operator) || !isCtxDateExpression(rawValue)) {
82
+ return undefined;
83
+ }
84
+
85
+ return parseCtxDateExpression(rawValue);
86
+ }
87
+
69
88
  function shouldShowFilterFormFieldMeta(field: CollectionField) {
70
89
  return Boolean(field?.interface);
71
90
  }
@@ -431,6 +450,7 @@ export class FilterFormBlockModel extends FilterBlockModel<{
431
450
 
432
451
  private canApplyFormDefaultValue(name: string, current: any, force?: boolean) {
433
452
  if (force) return true;
453
+ if (this.userEditedFieldNames.has(name)) return false;
434
454
  if (isEmptyValue(current)) return true;
435
455
  if (!this.lastDefaultValueByFieldName.has(name)) return false;
436
456
  return isEqual(current, this.lastDefaultValueByFieldName.get(name));
@@ -488,7 +508,7 @@ export class FilterFormBlockModel extends FilterBlockModel<{
488
508
  const rules = (params?.value || []) as any[];
489
509
  if (!Array.isArray(rules) || rules.length === 0) return appliedValues;
490
510
 
491
- const resolveValue = async (raw: any) => {
511
+ const resolveValue = async (raw: any, operator?: string) => {
492
512
  // RunJS support
493
513
  if (isRunJSValue(raw)) {
494
514
  const { code, version } = normalizeRunJSValue(raw);
@@ -496,6 +516,11 @@ export class FilterFormBlockModel extends FilterBlockModel<{
496
516
  return ret?.success ? ret.value : undefined;
497
517
  }
498
518
 
519
+ const parsedDateFilterValue = parseDateFilterDefaultValue(operator, raw);
520
+ if (typeof parsedDateFilterValue !== 'undefined') {
521
+ return parsedDateFilterValue;
522
+ }
523
+
499
524
  return await (this.context as any).resolveJsonTemplate?.(raw);
500
525
  };
501
526
 
@@ -519,11 +544,11 @@ export class FilterFormBlockModel extends FilterBlockModel<{
519
544
 
520
545
  const current = (form as any).getFieldValue?.(name);
521
546
 
522
- const resolved = await resolveValue(rule.value);
547
+ const operator = getDefaultOperator(itemModel as any);
548
+ const resolved = await resolveValue(rule.value, operator);
523
549
  if (options?.refreshSeq && options.refreshSeq !== this.defaultValuesRefreshSeq) return appliedValues;
524
550
  if (typeof resolved === 'undefined') continue;
525
551
 
526
- const operator = getDefaultOperator(itemModel as any);
527
552
  const normalized = normalizeFilterValueByOperator(operator, resolved);
528
553
  const mode = this.normalizeFieldValueMode(rule.mode);
529
554
  if (mode === 'default' && !this.canApplyFormDefaultValue(String(name), current, force)) continue;
@@ -59,11 +59,42 @@ export class FilterFormGridModel extends GridModel {
59
59
 
60
60
  readonly loading = observable.ref(false);
61
61
 
62
- private getAssociationFilterTargetKey(field: any) {
63
- const filterTargetKey = field?.targetCollection?.filterTargetKey;
62
+ private isRecordSelectFilterField(subModel: FilterFormItemModel) {
63
+ const fieldModel = subModel.subModels?.field;
64
+ if (!fieldModel) {
65
+ return true;
66
+ }
67
+
68
+ const RecordSelectModel = this.flowEngine.getModelClass('FilterFormRecordSelectFieldModel');
69
+ if (!RecordSelectModel) {
70
+ return fieldModel.constructor?.name === 'FilterFormRecordSelectFieldModel';
71
+ }
72
+
73
+ return fieldModel instanceof RecordSelectModel;
74
+ }
75
+
76
+ private getAssociationFilterTargetKey(field: any, subModel: FilterFormItemModel) {
77
+ const normalizeKey = (key: any) => {
78
+ if (Array.isArray(key)) {
79
+ return key[0];
80
+ }
81
+ return key;
82
+ };
83
+
84
+ const fieldModelValueKey = normalizeKey(subModel.subModels?.field?.props?.fieldNames?.value);
85
+ if (fieldModelValueKey) {
86
+ return fieldModelValueKey;
87
+ }
88
+
89
+ const filterTargetKey = normalizeKey(field?.targetCollection?.filterTargetKey);
90
+ const fieldTargetKey = normalizeKey(field?.targetKey);
91
+
92
+ if (filterTargetKey && filterTargetKey !== 'id') {
93
+ return filterTargetKey;
94
+ }
64
95
 
65
- if (Array.isArray(filterTargetKey)) {
66
- return filterTargetKey[0] || 'id';
96
+ if (!this.isRecordSelectFilterField(subModel) && fieldTargetKey) {
97
+ return fieldTargetKey;
67
98
  }
68
99
 
69
100
  return filterTargetKey || 'id';
@@ -370,7 +401,7 @@ export class FilterFormGridModel extends GridModel {
370
401
  if (field?.target) {
371
402
  return {
372
403
  targetId: model.uid,
373
- filterPaths: [`${fieldPath}.${this.getAssociationFilterTargetKey(field)}`],
404
+ filterPaths: [`${fieldPath}.${this.getAssociationFilterTargetKey(field, subModel)}`],
374
405
  };
375
406
  }
376
407
  }
@@ -31,6 +31,12 @@ import {
31
31
  import { FilterFormFieldModel } from './fields';
32
32
  import { normalizeFilterValueByOperator } from './valueNormalization';
33
33
 
34
+ type CollectionWithManager = Collection & {
35
+ collectionManager?: {
36
+ getCollection?: (name: string) => Collection | undefined;
37
+ };
38
+ };
39
+
34
40
  const getAssociationTargetCollection = (field: any, collection?: Collection, model?: CollectionBlockModel) => {
35
41
  if (field?.targetCollection) {
36
42
  return field.targetCollection;
@@ -42,6 +48,10 @@ const getAssociationTargetCollection = (field: any, collection?: Collection, mod
42
48
  if (collection?.dataSource?.getCollection) {
43
49
  return collection.dataSource.getCollection(targetName);
44
50
  }
51
+ const collectionManager = (collection as CollectionWithManager | undefined)?.collectionManager;
52
+ if (collectionManager?.getCollection) {
53
+ return collectionManager.getCollection(targetName);
54
+ }
45
55
  const dataSourceKey = collection?.dataSourceKey;
46
56
  if (dataSourceKey && model?.context?.dataSourceManager?.getCollection) {
47
57
  return model.context.dataSourceManager.getCollection(dataSourceKey, targetName);
@@ -62,9 +72,25 @@ function isRecord(value: unknown): value is Record<string, unknown> {
62
72
  return !!value && typeof value === 'object' && !Array.isArray(value);
63
73
  }
64
74
 
75
+ interface CollectionFieldWithAssociationMarker {
76
+ isAssociationField?: () => boolean;
77
+ target?: unknown;
78
+ }
79
+
80
+ function hasAssociationMarker(value: unknown): value is CollectionFieldWithAssociationMarker {
81
+ return isRecord(value) && (typeof value.isAssociationField === 'function' || 'target' in value);
82
+ }
83
+
84
+ const isAssociationCollectionField = (collectionField: unknown) => {
85
+ if (!hasAssociationMarker(collectionField)) {
86
+ return false;
87
+ }
88
+ return collectionField.isAssociationField ? collectionField.isAssociationField() : Boolean(collectionField.target);
89
+ };
90
+
65
91
  const normalizeAssociationDefaultFilterValue = (value: any, fieldModel: any) => {
66
92
  const collectionField = fieldModel?.context?.collectionField;
67
- if (!collectionField?.isAssociationField?.()) {
93
+ if (!isAssociationCollectionField(collectionField)) {
68
94
  return value;
69
95
  }
70
96
 
@@ -152,6 +178,74 @@ const buildVirtualFilterCollectionField = (ctx: FlowModelContext, filterField: a
152
178
  return result;
153
179
  };
154
180
 
181
+ const getFilterableChildren = (field: any) => {
182
+ const children = field?.filterable?.children;
183
+ return Array.isArray(children) ? children : [];
184
+ };
185
+
186
+ const hasAssociationChildren = (field: any, collection?: Collection, model?: CollectionBlockModel) => {
187
+ return (
188
+ isAssociationCollectionField(field) &&
189
+ (getFilterableChildren(field).length > 0 || !!getAssociationTargetCollection(field, collection, model))
190
+ );
191
+ };
192
+
193
+ const inferFilterableChildInterface = (child: any) => {
194
+ if (child?.interface) {
195
+ return child.interface;
196
+ }
197
+
198
+ const schema = child?.schema || child?.uiSchema;
199
+ const component = schema?.['x-component'];
200
+ if (component === 'Select') {
201
+ return 'select';
202
+ }
203
+ if (component === 'Checkbox.Group') {
204
+ return 'checkboxGroup';
205
+ }
206
+ if (component === 'Checkbox') {
207
+ return 'checkbox';
208
+ }
209
+
210
+ const type = child?.type || schema?.type;
211
+ if (type === 'number' || type === 'integer') {
212
+ return 'number';
213
+ }
214
+ return 'input';
215
+ };
216
+
217
+ const translateFilterableChildTitle = (title: unknown, t: (value: string) => string) => {
218
+ return typeof title === 'string' ? t(title) : title;
219
+ };
220
+
221
+ const buildFilterableChildField = (child: any, t: (value: string) => string) => {
222
+ const name = child?.name || child?.value;
223
+ if (!name) {
224
+ return;
225
+ }
226
+
227
+ const schema = child?.schema || child?.uiSchema || {};
228
+ const rawTitle = child?.title || child?.label || schema?.title || name;
229
+ const title = translateFilterableChildTitle(rawTitle, t);
230
+ const filterable = child?.filterable || (child?.operators ? { operators: child.operators } : undefined);
231
+
232
+ return {
233
+ ...child,
234
+ name,
235
+ title,
236
+ type: child?.type || schema?.type || 'string',
237
+ interface: inferFilterableChildInterface(child),
238
+ uiSchema: {
239
+ ...schema,
240
+ title: translateFilterableChildTitle(schema?.title || title, t),
241
+ },
242
+ filterable,
243
+ __filterFormVirtual: true,
244
+ isAssociationField: () => false,
245
+ getComponentProps: () => schema?.['x-component-props'] || {},
246
+ };
247
+ };
248
+
155
249
  const buildFilterFormFieldItem = ({
156
250
  model,
157
251
  collection,
@@ -171,12 +265,7 @@ const buildFilterFormFieldItem = ({
171
265
  if (!binding) {
172
266
  return;
173
267
  }
174
- const isAssociation =
175
- typeof field?.isAssociationField === 'function' ? field.isAssociationField() : Boolean(field?.target);
176
- const fieldModel =
177
- isAssociation && ctxWithFlags.engine?.getModelClass?.('FilterFormRecordSelectFieldModel')
178
- ? 'FilterFormRecordSelectFieldModel'
179
- : binding.modelName;
268
+ const fieldModel = binding.modelName;
180
269
  const label = field.title || field.name;
181
270
  const displayLabel = labelPrefix ? `${labelPrefix} / ${label}` : label;
182
271
  return {
@@ -196,7 +285,10 @@ const buildFilterFormFieldItem = ({
196
285
  },
197
286
  filterFormItemSettings: {
198
287
  init: {
199
- filterField: _.pick(field, ['name', 'title', 'interface', 'type']),
288
+ filterField: {
289
+ ..._.pick(field, ['name', 'title', 'interface', 'type', 'filterable', 'uiSchema', '__filterFormVirtual']),
290
+ title: displayLabel,
291
+ },
200
292
  defaultTargetUid: model.uid,
201
293
  },
202
294
  },
@@ -230,7 +322,8 @@ const buildAssociationFieldMenuItem = ({
230
322
  depth: number;
231
323
  }) => {
232
324
  const targetCollection = getAssociationTargetCollection(field, collection, model);
233
- if (!targetCollection) {
325
+ const filterableChildren = getFilterableChildren(field);
326
+ if (!targetCollection && !filterableChildren.length) {
234
327
  return;
235
328
  }
236
329
  if (depth > MAX_ASSOCIATION_DEPTH) {
@@ -247,10 +340,35 @@ const buildAssociationFieldMenuItem = ({
247
340
  const targetFields = getTargetFilterableFields(field, collection, model);
248
341
  const fieldItems: any[] = [];
249
342
  const associationItems: any[] = [];
343
+ const addedFieldPaths = new Set<string>();
344
+
345
+ filterableChildren.forEach((child: any) => {
346
+ const virtualChildField = buildFilterableChildField(child, t);
347
+ if (!virtualChildField) {
348
+ return;
349
+ }
350
+
351
+ const childFieldPath = `${fieldPath}.${virtualChildField.name}`;
352
+ const childItem = buildFilterFormFieldItem({
353
+ model,
354
+ collection,
355
+ ctxWithFlags,
356
+ field: virtualChildField,
357
+ fieldPath: childFieldPath,
358
+ labelPrefix: label,
359
+ });
360
+ if (childItem) {
361
+ addedFieldPaths.add(childFieldPath);
362
+ fieldItems.push(childItem);
363
+ }
364
+ });
250
365
 
251
366
  targetFields.forEach((targetField: any) => {
252
367
  const targetFieldPath = `${fieldPath}.${targetField.name}`;
253
- if (targetField?.targetCollection) {
368
+ if (addedFieldPaths.has(targetFieldPath)) {
369
+ return;
370
+ }
371
+ if (hasAssociationChildren(targetField, collection, model)) {
254
372
  const associationItem = buildAssociationFieldMenuItem({
255
373
  model,
256
374
  collection,
@@ -343,7 +461,7 @@ const getModelFieldGroups = async (model: CollectionBlockModel) => {
343
461
  baseItems.push(baseItem);
344
462
  }
345
463
 
346
- if (field?.targetCollection) {
464
+ if (hasAssociationChildren(field, collection, model)) {
347
465
  const associationItem = buildAssociationFieldMenuItem({
348
466
  model,
349
467
  collection,
@@ -474,6 +592,14 @@ export class FilterFormItemModel extends FilterableItemModel<{
474
592
  title: initFilterField?.title || initFilterField?.name,
475
593
  },
476
594
  });
595
+ if (initFilterField?.__filterFormVirtual) {
596
+ const virtualField = buildVirtualFilterCollectionField(this.context, initFilterField);
597
+ if (virtualField) {
598
+ this.context.defineProperty('collectionField', {
599
+ value: virtualField,
600
+ });
601
+ }
602
+ }
477
603
  }
478
604
  }
479
605
 
@@ -507,10 +633,7 @@ export class FilterFormItemModel extends FilterableItemModel<{
507
633
  const formValue = this.context.form?.getFieldValue(this.props.name);
508
634
  const modelValue = fieldModel.getFilterValue ? fieldModel.getFilterValue() : formValue;
509
635
  const collectionField = (fieldModel as any)?.context?.collectionField;
510
- const isAssociationField =
511
- typeof collectionField?.isAssociationField === 'function'
512
- ? collectionField.isAssociationField()
513
- : !!collectionField?.target;
636
+ const isAssociationField = isAssociationCollectionField(collectionField);
514
637
  const shouldUseFormValue =
515
638
  isAssociationField &&
516
639
  !this.mounted &&
@@ -547,11 +670,7 @@ export class FilterFormItemModel extends FilterableItemModel<{
547
670
  return value;
548
671
  }
549
672
  const collectionField = (fieldModel as any)?.context?.collectionField;
550
- const isAssociation =
551
- typeof collectionField?.isAssociationField === 'function'
552
- ? collectionField.isAssociationField()
553
- : !!collectionField?.target;
554
- if (!isAssociation) {
673
+ if (!isAssociationCollectionField(collectionField)) {
555
674
  return value;
556
675
  }
557
676
  const normalizedFieldNames = normalizeAssociationFieldNames(
@@ -684,7 +803,8 @@ FilterFormItemModel.registerFlow({
684
803
  // @ts-ignore
685
804
  (fieldSettingsInitParams?.dataSourceKey && fieldSettingsInitParams?.collectionName)
686
805
  );
687
- if (!hasCollectionContext && !collectionField && normalizedFilterField) {
806
+ const shouldUseVirtualFilterField = !!normalizedFilterField?.__filterFormVirtual;
807
+ if ((shouldUseVirtualFilterField || (!hasCollectionContext && !collectionField)) && normalizedFilterField) {
688
808
  const virtualField = buildVirtualFilterCollectionField(ctx, normalizedFilterField);
689
809
  if (virtualField) {
690
810
  ctx.model.context.defineProperty('collectionField', {
@@ -779,10 +899,13 @@ FilterFormItemModel.registerFlow({
779
899
  type: 'select',
780
900
  key: 'use',
781
901
  props: {
782
- options: classes.map((model) => ({
783
- label: t(model.modelName),
784
- value: model.modelName,
785
- })),
902
+ options: classes.map((model) => {
903
+ const ModelClass = ctx.engine.getModelClass(model.modelName);
904
+ return {
905
+ label: t(ModelClass?.meta?.label || model.modelName),
906
+ value: model.modelName,
907
+ };
908
+ }),
786
909
  },
787
910
  };
788
911
  },
@@ -798,6 +921,13 @@ FilterFormItemModel.registerFlow({
798
921
  },
799
922
  defaultOperator: {
800
923
  use: 'defaultOperator',
924
+ hideInSettings(ctx) {
925
+ const collectionField =
926
+ ctx.collectionField ||
927
+ ctx.model?.context?.collectionField ||
928
+ ctx.model?.subModels?.field?.context?.collectionField;
929
+ return isAssociationCollectionField(collectionField);
930
+ },
801
931
  },
802
932
  operatorComponentProps: {
803
933
  use: 'operatorComponentProps',
@@ -8,23 +8,76 @@
8
8
  */
9
9
 
10
10
  import type { ButtonProps } from 'antd/es/button';
11
+ import { reaction } from '@formily/reactive';
11
12
  import { tExpr } from '@nocobase/flow-engine';
12
13
  import { FilterFormActionModel } from './FilterFormActionModel';
13
14
 
15
+ type FilterFormBlockState = {
16
+ autoTriggerFilter: boolean;
17
+ };
18
+
19
+ const mountedSubmitActions = new WeakSet<object>();
20
+ const visibleSubmitActionsByBlock = new WeakMap<FilterFormBlockState, Set<object>>();
21
+ const hiddenReactionDisposers = new WeakMap<object, () => void>();
22
+
23
+ const syncAutoTriggerFilter = (blockModel: FilterFormBlockState, lifecycleAction: object & { hidden?: boolean }) => {
24
+ let visibleSubmitActions = visibleSubmitActionsByBlock.get(blockModel);
25
+
26
+ if (mountedSubmitActions.has(lifecycleAction) && !lifecycleAction.hidden) {
27
+ if (!visibleSubmitActions) {
28
+ visibleSubmitActions = new Set<object>();
29
+ visibleSubmitActionsByBlock.set(blockModel, visibleSubmitActions);
30
+ }
31
+ visibleSubmitActions.add(lifecycleAction);
32
+ } else {
33
+ visibleSubmitActions?.delete(lifecycleAction);
34
+ }
35
+
36
+ blockModel.autoTriggerFilter = Boolean(!visibleSubmitActions?.size);
37
+ if (!visibleSubmitActions?.size) {
38
+ visibleSubmitActionsByBlock.delete(blockModel);
39
+ }
40
+ };
41
+
14
42
  export class FilterFormSubmitActionModel extends FilterFormActionModel {
15
43
  defaultProps: ButtonProps = {
16
44
  title: tExpr('Filter'),
17
45
  type: 'primary',
18
46
  };
19
47
 
20
- onInit(options): void {
21
- super.onInit(options);
22
- this.context.blockModel.autoTriggerFilter = false;
48
+ private getLifecycleAction(): object & { hidden?: boolean } {
49
+ return this.context.model || this;
50
+ }
51
+
52
+ private syncAutoTriggerFilter(): void {
53
+ const blockModel = this.context.blockModel as FilterFormBlockState;
54
+ const lifecycleAction = this.getLifecycleAction();
55
+ syncAutoTriggerFilter(blockModel, lifecycleAction);
56
+ }
57
+
58
+ onMount(): void {
59
+ super.onMount();
60
+ const blockModel = this.context.blockModel as FilterFormBlockState;
61
+ const lifecycleAction = this.getLifecycleAction();
62
+ mountedSubmitActions.add(lifecycleAction);
63
+ hiddenReactionDisposers.get(lifecycleAction)?.();
64
+ hiddenReactionDisposers.set(
65
+ lifecycleAction,
66
+ reaction(
67
+ () => lifecycleAction.hidden,
68
+ () => syncAutoTriggerFilter(blockModel, lifecycleAction),
69
+ ),
70
+ );
71
+ this.syncAutoTriggerFilter();
23
72
  }
24
73
 
25
74
  onUnmount(): void {
26
75
  super.onUnmount();
27
- this.context.blockModel.autoTriggerFilter = true;
76
+ const lifecycleAction = this.getLifecycleAction();
77
+ hiddenReactionDisposers.get(lifecycleAction)?.();
78
+ hiddenReactionDisposers.delete(lifecycleAction);
79
+ mountedSubmitActions.delete(lifecycleAction);
80
+ this.syncAutoTriggerFilter();
28
81
  }
29
82
  }
30
83