@nocobase/client-v2 2.2.0-beta.9 → 3.0.0-alpha.1

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 (208) hide show
  1. package/es/APIClient.d.ts +1 -3
  2. package/es/BaseApplication.d.ts +1 -0
  3. package/es/acl/ACLProvider.d.ts +10 -0
  4. package/es/authRedirect.d.ts +5 -0
  5. package/es/collection-field-interface/CollectionFieldInterface.d.ts +1 -0
  6. package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +1 -0
  7. package/es/components/AttachmentUpload.d.ts +51 -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 +2 -0
  18. package/es/flow/actions/index.d.ts +1 -1
  19. package/es/flow/actions/linkageRules.d.ts +3 -0
  20. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +1 -0
  21. package/es/flow/admin-shell/admin-layout/NocoBaseLogo.d.ts +10 -0
  22. package/es/flow/components/FieldAssignExactDatePicker.d.ts +1 -0
  23. package/es/flow/components/FieldAssignValueInput.d.ts +9 -0
  24. package/es/flow/components/FlowRoute.d.ts +2 -2
  25. package/es/flow/components/RunJSValueEditor.d.ts +1 -0
  26. package/es/flow/components/filter/FilterGroup.d.ts +1 -0
  27. package/es/flow/components/filter/VariableFilterItem.d.ts +6 -1
  28. package/es/flow/index.d.ts +1 -0
  29. package/es/flow/internal/registerDeviceTypeContext.d.ts +10 -0
  30. package/es/flow/models/base/ActionModelCore.d.ts +4 -2
  31. package/es/flow/models/base/BlockGridModel.d.ts +3 -0
  32. package/es/flow/models/base/PageModel/PageModel.d.ts +12 -0
  33. package/es/flow/models/base/PageModel/PageModelTabBar.d.ts +22 -0
  34. package/es/flow/models/base/PageModel/PageTabModel.d.ts +12 -0
  35. package/es/flow/models/blocks/form/QuickEditFormModel.d.ts +17 -2
  36. package/es/flow/models/blocks/table/TableColumnModel.d.ts +1 -0
  37. package/es/flow/models/fields/DateTimeFieldModel/dateLimit.d.ts +15 -7
  38. package/es/flow-compat/fieldValidationConstants.d.ts +1 -1
  39. package/es/flow-compat/routeTypes.d.ts +1 -0
  40. package/es/index.d.ts +8 -1
  41. package/es/index.mjs +190 -141
  42. package/es/nocobase-buildin-plugin/currentUserAuthStatus.d.ts +11 -0
  43. package/es/nocobase-buildin-plugin/index.d.ts +9 -0
  44. package/es/settings-app/SettingsApplication.d.ts +18 -0
  45. package/es/settings-app/SettingsBrand.d.ts +16 -0
  46. package/es/settings-app/SettingsBuildInPlugin.d.ts +9 -0
  47. package/es/settings-app/SettingsDocumentRedirect.d.ts +10 -0
  48. package/es/settings-app/SettingsGroupNav.d.ts +17 -0
  49. package/es/settings-app/SettingsPluginSettingsManager.d.ts +14 -0
  50. package/es/settings-app/SettingsRouterManager.d.ts +15 -0
  51. package/es/settings-app/SettingsSearch.d.ts +17 -0
  52. package/es/settings-app/SettingsShell.d.ts +10 -0
  53. package/es/settings-app/isSettingsApp.d.ts +18 -0
  54. package/es/settings-app/runtimePaths.d.ts +18 -0
  55. package/es/settings-app/settingsDocumentPath.d.ts +14 -0
  56. package/es/settings-app/settingsTheme.d.ts +87 -0
  57. package/es/settings-app/useSettingsThemeConfig.d.ts +22 -0
  58. package/es/settings-center/AdminSettingsLayout.d.ts +2 -2
  59. package/es/settings-center/groups.d.ts +66 -0
  60. package/es/settings-center/useSettingsGroups.d.ts +36 -0
  61. package/es/settings-center/useSettingsSearch.d.ts +35 -0
  62. package/es/settings-center/utils.d.ts +24 -0
  63. package/es/theme/index.d.ts +1 -0
  64. package/es/theme/itemActive.d.ts +24 -0
  65. package/es/theme/type.d.ts +9 -0
  66. package/es/utils/getRouteRuntimeVersion.d.ts +23 -0
  67. package/es/utils/index.d.ts +1 -0
  68. package/lib/index.js +195 -146
  69. package/lib/locale/languageCodes.js +3 -1
  70. package/package.json +9 -7
  71. package/src/APIClient.ts +1 -10
  72. package/src/Application.tsx +4 -0
  73. package/src/BaseApplication.tsx +11 -6
  74. package/src/__tests__/app.test.tsx +73 -0
  75. package/src/__tests__/authRedirect.test.ts +137 -0
  76. package/src/__tests__/browserChecker.test.ts +185 -6
  77. package/src/__tests__/getRouteRuntimeVersion.test.ts +68 -0
  78. package/src/__tests__/mockSettingsApplication.ts +29 -0
  79. package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +79 -10
  80. package/src/__tests__/plugin-manager.test.tsx +7 -6
  81. package/src/__tests__/settings-application.test.ts +164 -0
  82. package/src/__tests__/settings-auth-routes.test.ts +71 -0
  83. package/src/__tests__/settings-center.test.tsx +38 -37
  84. package/src/__tests__/settings-layout-root.test.tsx +260 -0
  85. package/src/__tests__/settings-runtime-paths.test.ts +99 -0
  86. package/src/__tests__/settings-shell.test.tsx +200 -0
  87. package/src/acl/ACLProvider.tsx +21 -0
  88. package/src/authRedirect.ts +72 -3
  89. package/src/collection-field-interface/CollectionFieldInterface.ts +1 -0
  90. package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +1 -0
  91. package/src/collection-manager/field-validation.ts +1 -1
  92. package/src/components/AttachmentUpload.tsx +275 -0
  93. package/src/components/README.md +7 -1
  94. package/src/components/README.zh-CN.md +6 -1
  95. package/src/components/category-tabs/SortableCategoryTabs.tsx +210 -0
  96. package/src/components/category-tabs/index.ts +10 -0
  97. package/src/components/form/JsonTextArea.tsx +4 -0
  98. package/src/components/form/ScanInput/CodeScanner.tsx +79 -35
  99. package/src/components/form/ScanInput/__tests__/CodeScanner.test.tsx +101 -0
  100. package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +284 -11
  101. package/src/components/form/ScanInput/useCodeScanner.ts +332 -24
  102. package/src/components/form/TypedVariableInput.tsx +452 -101
  103. package/src/components/form/__tests__/TypedVariableInput.test.tsx +364 -12
  104. package/src/components/form/filter/CollectionFilter.tsx +4 -0
  105. package/src/components/form/filter/CollectionFilterItem.tsx +32 -7
  106. package/src/components/form/filter/CollectionFilterPanel.tsx +4 -0
  107. package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +38 -0
  108. package/src/components/form/filter/__tests__/useFilterActionProps.test.tsx +95 -0
  109. package/src/components/form/filter/index.ts +2 -0
  110. package/src/components/form/filter/useFilterActionProps.ts +37 -6
  111. package/src/components/index.ts +2 -0
  112. package/src/flow/FlowPage.tsx +9 -1
  113. package/src/flow/__tests__/FlowPage.test.tsx +50 -3
  114. package/src/flow/__tests__/FlowRoute.test.tsx +128 -10
  115. package/src/flow/__tests__/PluginFlowEngine.test.ts +58 -0
  116. package/src/flow/actions/__tests__/actionLinkageRules.forkProps.test.ts +314 -0
  117. package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +102 -0
  118. package/src/flow/actions/__tests__/linkageRules.tab.test.ts +171 -0
  119. package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +7 -3
  120. package/src/flow/actions/index.ts +2 -0
  121. package/src/flow/actions/linkageRules.tsx +111 -16
  122. package/src/flow/actions/linkageRulesRefresh.tsx +2 -6
  123. package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +1 -57
  124. package/src/flow/admin-shell/admin-layout/NocoBaseLogo.tsx +77 -0
  125. package/src/flow/admin-shell/admin-layout/TopbarActionsBar.tsx +37 -14
  126. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +253 -6
  127. package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +77 -13
  128. package/src/flow/common/Markdown/style.ts +4 -0
  129. package/src/flow/components/BlockItemCard.tsx +2 -2
  130. package/src/flow/components/FieldAssignExactDatePicker.tsx +25 -11
  131. package/src/flow/components/FieldAssignValueInput.tsx +91 -20
  132. package/src/flow/components/FlowRoute.tsx +78 -32
  133. package/src/flow/components/RunJSValueEditor.tsx +9 -1
  134. package/src/flow/components/__tests__/FieldAssignValueInput.context.test.tsx +216 -0
  135. package/src/flow/components/filter/FilterGroup.tsx +33 -5
  136. package/src/flow/components/filter/VariableFilterItem.tsx +158 -10
  137. package/src/flow/components/filter/__tests__/FilterGroup.test.tsx +45 -0
  138. package/src/flow/components/filter/__tests__/VariableFilterItem.leftMetaTree.test.tsx +9 -0
  139. package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +70 -1
  140. package/src/flow/index.ts +3 -0
  141. package/src/flow/internal/registerDeviceTypeContext.ts +39 -0
  142. package/src/flow/models/base/ActionModelCore.tsx +11 -2
  143. package/src/flow/models/base/BlockGridModel.tsx +26 -0
  144. package/src/flow/models/base/CollectionBlockModel.tsx +6 -2
  145. package/src/flow/models/base/PageModel/PageModel.tsx +387 -53
  146. package/src/flow/models/base/PageModel/PageModelTabBar.tsx +114 -0
  147. package/src/flow/models/base/PageModel/PageTabModel.tsx +249 -18
  148. package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +790 -10
  149. package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.module-isolation.test.tsx +130 -0
  150. package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.test.tsx +118 -0
  151. package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +1075 -7
  152. package/src/flow/models/base/__tests__/ActionModelCore.render.test.tsx +49 -0
  153. package/src/flow/models/base/__tests__/BlockGridModel.selectSceneActivation.test.ts +124 -0
  154. package/src/flow/models/base/__tests__/CollectionBlockModel.addAppends.test.ts +41 -0
  155. package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +28 -3
  156. package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +27 -12
  157. package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +36 -0
  158. package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +66 -4
  159. package/src/flow/models/blocks/form/QuickEditFormModel.tsx +189 -36
  160. package/src/flow/models/blocks/form/__tests__/QuickEditFormModel.quickEdit.test.ts +350 -2
  161. package/src/flow/models/blocks/js-block/JSBlock.tsx +1 -1
  162. package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +2 -1
  163. package/src/flow/models/blocks/table/TableBlockModel.tsx +4 -2
  164. package/src/flow/models/blocks/table/TableColumnModel.tsx +30 -1
  165. package/src/flow/models/blocks/table/__tests__/TableBlockModel.quickEditRefresh.test.ts +12 -0
  166. package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +33 -0
  167. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +2 -1
  168. package/src/flow/models/fields/DateTimeFieldModel/__tests__/DateTimeNoTzFieldModel.dateLimit.test.tsx +149 -0
  169. package/src/flow/models/fields/DateTimeFieldModel/dateLimit.ts +149 -113
  170. package/src/flow/models/fields/JsonFieldModel.tsx +31 -8
  171. package/src/flow/models/fields/VariableFieldFormModel.tsx +3 -3
  172. package/src/flow/models/fields/__tests__/JsonFieldModel.test.ts +82 -0
  173. package/src/flow/models/fields/__tests__/VariableFieldFormModel.test.tsx +51 -0
  174. package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +20 -10
  175. package/src/flow/models/fields/mobile-components/MobileSelect.tsx +24 -12
  176. package/src/flow/models/topbar/TopbarActionModel.tsx +52 -180
  177. package/src/flow/utils/dataScopeFormValueClear.ts +4 -3
  178. package/src/flow-compat/fieldValidationConstants.ts +1 -1
  179. package/src/flow-compat/routeTypes.ts +1 -0
  180. package/src/index.ts +14 -1
  181. package/src/locale/languageCodes.ts +3 -1
  182. package/src/nocobase-buildin-plugin/currentUserAuthStatus.ts +58 -0
  183. package/src/nocobase-buildin-plugin/index.tsx +110 -70
  184. package/src/settings-app/SettingsApplication.ts +31 -0
  185. package/src/settings-app/SettingsBrand.tsx +108 -0
  186. package/src/settings-app/SettingsBuildInPlugin.ts +10 -0
  187. package/src/settings-app/SettingsDocumentRedirect.tsx +26 -0
  188. package/src/settings-app/SettingsGroupNav.tsx +124 -0
  189. package/src/settings-app/SettingsPluginSettingsManager.ts +38 -0
  190. package/src/settings-app/SettingsRouterManager.ts +92 -0
  191. package/src/settings-app/SettingsSearch.tsx +238 -0
  192. package/src/settings-app/SettingsShell.tsx +167 -0
  193. package/src/settings-app/isSettingsApp.ts +21 -0
  194. package/src/settings-app/runtimePaths.ts +104 -0
  195. package/src/settings-app/settingsDocumentPath.ts +66 -0
  196. package/src/settings-app/settingsTheme.ts +301 -0
  197. package/src/settings-app/useSettingsThemeConfig.ts +100 -0
  198. package/src/settings-center/AdminSettingsLayout.tsx +122 -141
  199. package/src/settings-center/SystemSettingsPage.tsx +4 -170
  200. package/src/settings-center/groups.ts +108 -0
  201. package/src/settings-center/useSettingsGroups.ts +105 -0
  202. package/src/settings-center/useSettingsSearch.ts +173 -0
  203. package/src/settings-center/utils.tsx +50 -1
  204. package/src/theme/index.tsx +1 -0
  205. package/src/theme/itemActive.ts +31 -0
  206. package/src/theme/type.ts +10 -0
  207. package/src/utils/getRouteRuntimeVersion.ts +185 -0
  208. package/src/utils/index.tsx +1 -0
@@ -68,6 +68,12 @@ interface LinkageRule {
68
68
  }[];
69
69
  }
70
70
 
71
+ export function updateLinkageRules<T>(rules: T[], updater: (nextRules: T[]) => void): T[] {
72
+ const nextRules = _.cloneDeep(rules);
73
+ updater(nextRules);
74
+ return nextRules;
75
+ }
76
+
71
77
  const previewValueForLog = (value: any) => {
72
78
  if (value == null) return value;
73
79
  const t = typeof value;
@@ -731,6 +737,41 @@ export const linkageSetMenuItemProps = defineAction({
731
737
  },
732
738
  });
733
739
 
740
+ export const linkageSetTabProps = defineAction({
741
+ name: 'linkageSetTabProps',
742
+ title: tExpr('Set tab state'),
743
+ scene: ActionScene.TAB_LINKAGE_RULES,
744
+ sort: 100,
745
+ uiSchema: {
746
+ value: {
747
+ type: 'string',
748
+ 'x-component': (props) => {
749
+ const { value, onChange } = props;
750
+ // eslint-disable-next-line react-hooks/rules-of-hooks
751
+ const ctx = useFlowContext();
752
+ const t = ctx.model.translate.bind(ctx.model);
753
+
754
+ return (
755
+ <Select
756
+ value={value}
757
+ onChange={onChange}
758
+ placeholder={t('Please select state')}
759
+ style={{ width: '100%' }}
760
+ options={[
761
+ { label: t('Visible'), value: 'visible' },
762
+ { label: t('Hidden'), value: 'hidden' },
763
+ ]}
764
+ allowClear
765
+ />
766
+ );
767
+ },
768
+ },
769
+ },
770
+ handler(ctx, { value, setProps }) {
771
+ setProps(ctx.model, { hiddenModel: value === 'hidden' });
772
+ },
773
+ });
774
+
734
775
  export const linkageSetFieldProps = defineAction({
735
776
  name: 'linkageSetFieldProps',
736
777
  title: tExpr('Set field state'),
@@ -1339,6 +1380,7 @@ export const linkageRunjs = defineAction({
1339
1380
  ActionScene.FIELD_LINKAGE_RULES,
1340
1381
  ActionScene.ACTION_LINKAGE_RULES,
1341
1382
  ActionScene.MENU_LINKAGE_RULES,
1383
+ ActionScene.TAB_LINKAGE_RULES,
1342
1384
  ActionScene.DETAILS_FIELD_LINKAGE_RULES,
1343
1385
  ActionScene.SUB_FORM_FIELD_LINKAGE_RULES,
1344
1386
  ],
@@ -1464,13 +1506,26 @@ async function resolveLinkageRulesParamsPreservingRunJsScripts(ctx: FlowContext,
1464
1506
  }
1465
1507
 
1466
1508
  const LinkageRulesUI = observer(
1467
- (props: { readonly value: LinkageRule[]; supportedActions: string[]; title?: string }) => {
1468
- const { value: rules, supportedActions } = props;
1509
+ (props: {
1510
+ readonly value: LinkageRule[];
1511
+ onChange?: (value: LinkageRule[]) => void;
1512
+ supportedActions: string[];
1513
+ title?: string;
1514
+ }) => {
1515
+ const { value: rules = [], onChange, supportedActions } = props;
1469
1516
  const ctx = useFlowContext();
1470
1517
  const flowEngine = useFlowEngine();
1471
1518
  const t = ctx.model.translate.bind(ctx.model);
1472
1519
  const assignPriorityTip = t('Assignment takes precedence over form field assignment');
1473
1520
 
1521
+ const replaceRules = (updater: (nextRules: LinkageRule[]) => void) => {
1522
+ if (onChange) {
1523
+ onChange(updateLinkageRules(rules, updater));
1524
+ } else {
1525
+ updater(rules);
1526
+ }
1527
+ };
1528
+
1474
1529
  // 创建新规则的默认值
1475
1530
  const createNewRule = (): LinkageRule => ({
1476
1531
  key: uid(),
@@ -1487,7 +1542,7 @@ const LinkageRulesUI = observer(
1487
1542
 
1488
1543
  // 删除规则
1489
1544
  const handleDeleteRule = (index: number) => {
1490
- rules.splice(index, 1);
1545
+ replaceRules((nextRules) => nextRules.splice(index, 1));
1491
1546
  };
1492
1547
 
1493
1548
  // 上移规则
@@ -1526,7 +1581,9 @@ const LinkageRulesUI = observer(
1526
1581
 
1527
1582
  // 切换规则启用状态
1528
1583
  const handleToggleEnable = (index: number, enable: boolean) => {
1529
- rules[index].enable = enable;
1584
+ replaceRules((nextRules) => {
1585
+ nextRules[index].enable = enable;
1586
+ });
1530
1587
  };
1531
1588
 
1532
1589
  // 获取可用的动作类型
@@ -2169,17 +2226,30 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
2169
2226
  }
2170
2227
  : props;
2171
2228
 
2172
- // 存储原始值,用于恢复
2173
- if (!model.__originalProps) {
2174
- model.__originalProps = {
2175
- hiddenModel: model.hidden,
2176
- hiddenText: undefined,
2177
- disabled: undefined,
2178
- required: undefined,
2179
- hidden: undefined,
2180
- ...model.props,
2181
- };
2182
- }
2229
+ // 只记录联动实际控制的属性,避免之后恢复状态时把标题、路由等无关的新配置回滚到旧快照。
2230
+ const originalProps = model.__originalProps || (model.__originalProps = {});
2231
+ const rememberOriginalProp = (key: string, value: unknown) => {
2232
+ if (!Object.prototype.hasOwnProperty.call(originalProps, key)) {
2233
+ originalProps[key] = value;
2234
+ }
2235
+ };
2236
+ Object.keys(normalizedProps || {}).forEach((key) => {
2237
+ if (key === 'hiddenModel') {
2238
+ rememberOriginalProp(
2239
+ key,
2240
+ Object.prototype.hasOwnProperty.call(model.props || {}, key) ? model.props?.[key] : model.hidden,
2241
+ );
2242
+ return;
2243
+ }
2244
+
2245
+ rememberOriginalProp(key, model.props?.[key]);
2246
+ if (key === 'hiddenText' && normalizedProps[key]) {
2247
+ rememberOriginalProp('title', model.props?.title);
2248
+ }
2249
+ if (key === 'required') {
2250
+ rememberOriginalProp('rules', model.props?.rules);
2251
+ }
2252
+ });
2183
2253
 
2184
2254
  // 临时存起来,遍历完所有规则后,再统一处理
2185
2255
  patchPropsByModel.set(model, {
@@ -2237,7 +2307,6 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
2237
2307
  const newProps = { ...model.__originalProps, ...patchProps };
2238
2308
  const prevHidden = !!model.hidden;
2239
2309
  const nextHidden = !!newProps.hiddenModel;
2240
-
2241
2310
  model.setProps(_.omit(newProps, ['hiddenModel', 'value', 'hiddenText', LINKAGE_ASSIGN_MODE_PROP]));
2242
2311
  syncFieldOptionsToForks(model, patchProps);
2243
2312
  if (typeof model.setHidden === 'function') {
@@ -2463,6 +2532,32 @@ export const menuLinkageRules = defineAction({
2463
2532
  },
2464
2533
  });
2465
2534
 
2535
+ export const tabLinkageRules = defineAction({
2536
+ name: 'tabLinkageRules',
2537
+ title: tExpr('Tab linkage rules'),
2538
+ uiMode: 'embed',
2539
+ uiSchema(ctx) {
2540
+ return {
2541
+ value: {
2542
+ type: 'array',
2543
+ 'x-component': LinkageRulesUI,
2544
+ 'x-component-props': {
2545
+ supportedActions: getSupportedActions(ctx, ActionScene.TAB_LINKAGE_RULES),
2546
+ title: tExpr('Tab linkage rules'),
2547
+ },
2548
+ },
2549
+ };
2550
+ },
2551
+ defaultParams: {
2552
+ value: [],
2553
+ },
2554
+ useRawParams: true,
2555
+ handler: async (ctx, params) => {
2556
+ const resolved = await resolveLinkageRulesParamsPreservingRunJsScripts(ctx, params);
2557
+ return commonLinkageRulesHandler(ctx, resolved);
2558
+ },
2559
+ });
2560
+
2466
2561
  export const fieldLinkageRules = defineAction({
2467
2562
  name: 'fieldLinkageRules',
2468
2563
  title: tExpr('Field linkage rules'),
@@ -35,12 +35,8 @@ export const linkageRulesRefresh = defineAction({
35
35
  // Prefer running on the current model; fallback to blockModel when the current model doesn't own the flow.
36
36
  if (!hasFlow) return;
37
37
 
38
- // In runtime, only skip master when there are mounted forks that can handle the same flow.
38
+ // Skip master when unmounted forks can handle the same flow.
39
39
  // Otherwise master is likely the rendered model and still needs refresh.
40
- // In design mode, always refresh master so the currently edited model state stays in sync.
41
- const flowSettingsEnabled = Boolean(
42
- (ctx as any)?.flowSettingsEnabled || (model as any)?.context?.flowSettingsEnabled,
43
- );
44
40
  const hasForkWithFlow =
45
41
  !model?.isFork &&
46
42
  !!model?.forks?.size &&
@@ -49,7 +45,7 @@ export const linkageRulesRefresh = defineAction({
49
45
  return !!fork?.getFlow?.(flowKey);
50
46
  });
51
47
  const isMasterMounted = Boolean((model as any)?.context?.ref?.current);
52
- if (hasForkWithFlow && !isMasterMounted && !flowSettingsEnabled) {
48
+ if (hasForkWithFlow && !isMasterMounted) {
53
49
  return;
54
50
  }
55
51
 
@@ -18,7 +18,6 @@ import { theme as antdTheme, ConfigProvider, Grid, Popover } from 'antd';
18
18
  import { createStyles, createGlobalStyle } from 'antd-style';
19
19
  import React, { FC, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
20
20
  import ReactDOM from 'react-dom';
21
- import { useTranslation } from 'react-i18next';
22
21
  import { useLocation } from 'react-router-dom';
23
22
  import {
24
23
  AdminLayoutMenuModelRenderer,
@@ -35,7 +34,6 @@ import { ResetThemeTokenAndKeepAlgorithm } from './ResetThemeTokenAndKeepAlgorit
35
34
  import { PinnedPluginListLite } from './PinnedPluginListLite';
36
35
  import { useApplications } from './useApplications';
37
36
  import { useGlobalTheme, type CustomToken } from '../../../theme';
38
- import { useSystemSettings } from '../../system-settings';
39
37
  import { NocoBaseDesktopRouteType, type NocoBaseDesktopRoute } from '../../../flow-compat';
40
38
  import {
41
39
  FLOW_SETTINGS_PREFERENCE_CHANGE_EVENT,
@@ -44,60 +42,7 @@ import {
44
42
  } from './flowSettingsPreference';
45
43
  import { joinAdminLayoutRoutePath, type AdminLayoutRoutePathLike } from './resolveAdminRouteRuntimeTarget';
46
44
  import { useAppListRender } from './AppListRender';
47
-
48
- const className1 = css`
49
- height: var(--nb-header-height);
50
- margin-right: 4px;
51
- display: inline-flex;
52
- flex-shrink: 0;
53
- color: #fff;
54
- padding: 0;
55
- align-items: center;
56
- `;
57
- const className1WithFixedWidth = css`
58
- ${className1}
59
- width: 168px;
60
- `;
61
- const className1WithAutoWidth = css`
62
- ${className1}
63
- width: auto;
64
- min-width: 168px;
65
- `;
66
- const className2 = css`
67
- object-fit: contain;
68
- width: 100%;
69
- height: 100%;
70
- `;
71
- const className3 = css`
72
- width: 100%;
73
- height: 100%;
74
- font-weight: 500;
75
- text-align: center;
76
- overflow: hidden;
77
- text-overflow: ellipsis;
78
- white-space: nowrap;
79
- `;
80
-
81
- const NocoBaseLogo = observer(() => {
82
- const { token } = antdTheme.useToken();
83
- const customToken = token as CustomToken;
84
- const result = useSystemSettings();
85
- const { t } = useTranslation('lm-collections');
86
- const fontSizeStyle = useMemo(() => ({ fontSize: customToken.fontSizeHeading3 }), [customToken.fontSizeHeading3]);
87
-
88
- const hasLogo = result?.data?.data?.logo?.url;
89
- const logo = hasLogo ? (
90
- <img className={className2} src={result?.data?.data?.logo?.url} />
91
- ) : (
92
- <span style={fontSizeStyle} className={className3}>
93
- {t(result?.data?.data?.title)}
94
- </span>
95
- );
96
-
97
- return (
98
- <div className={hasLogo ? className1WithFixedWidth : className1WithAutoWidth}>{result?.loading ? null : logo}</div>
99
- );
100
- });
45
+ import { NocoBaseLogo } from './NocoBaseLogo';
101
46
 
102
47
  const resetStyle = css`
103
48
  .ant-layout-sider-children {
@@ -712,7 +657,6 @@ export const AdminLayoutComponent = observer((props: any) => {
712
657
  <MobileMenuControlContext.Provider value={{ closeMobileMenu }}>
713
658
  <DndProvider collisionDetection={menuCollisionDetection} onDragEnd={handleMenuDragEnd}>
714
659
  <ProLayout
715
- key={`admin-layout-menu-${menuRouteRefreshVersion}`}
716
660
  {...props}
717
661
  contentStyle={contentStyle}
718
662
  siderWidth={customToken.siderWidth || 200}
@@ -0,0 +1,77 @@
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 { css } from '@emotion/css';
11
+ import { observer } from '@nocobase/flow-engine';
12
+ import { theme as antdTheme } from 'antd';
13
+ import React, { useMemo } from 'react';
14
+ import { useTranslation } from 'react-i18next';
15
+ import { useSystemSettings } from '../../system-settings';
16
+ import type { CustomToken } from '../../../theme';
17
+
18
+ const logoContainerClassName = css`
19
+ height: var(--nb-header-height);
20
+ margin-right: 4px;
21
+ display: inline-flex;
22
+ flex-shrink: 0;
23
+ padding: 0;
24
+ align-items: center;
25
+ `;
26
+
27
+ const fixedWidthClassName = css`
28
+ ${logoContainerClassName}
29
+ width: 168px;
30
+ `;
31
+
32
+ const autoWidthClassName = css`
33
+ ${logoContainerClassName}
34
+ width: auto;
35
+ min-width: 168px;
36
+ `;
37
+
38
+ const logoImageClassName = css`
39
+ object-fit: contain;
40
+ width: 100%;
41
+ height: 100%;
42
+ `;
43
+
44
+ const titleClassName = css`
45
+ width: 100%;
46
+ height: 100%;
47
+ font-weight: 500;
48
+ text-align: center;
49
+ overflow: hidden;
50
+ text-overflow: ellipsis;
51
+ white-space: nowrap;
52
+ `;
53
+
54
+ export const NocoBaseLogo = observer(() => {
55
+ const { token } = antdTheme.useToken();
56
+ const customToken = token as CustomToken;
57
+ const result = useSystemSettings();
58
+ const { t } = useTranslation('lm-collections');
59
+ const title = t(result?.data?.data?.title || 'NocoBase');
60
+ const logoUrl = result?.data?.data?.logo?.url;
61
+ const titleStyle = useMemo<React.CSSProperties>(
62
+ () => ({ color: customToken.colorTextHeaderMenu, fontSize: customToken.fontSizeHeading3 }),
63
+ [customToken.colorTextHeaderMenu, customToken.fontSizeHeading3],
64
+ );
65
+
66
+ const logo = logoUrl ? (
67
+ <img alt={title} className={logoImageClassName} src={logoUrl} />
68
+ ) : (
69
+ <span style={titleStyle} className={titleClassName}>
70
+ {title}
71
+ </span>
72
+ );
73
+
74
+ return <div className={logoUrl ? fixedWidthClassName : autoWidthClassName}>{result?.loading ? null : logo}</div>;
75
+ });
76
+
77
+ NocoBaseLogo.displayName = 'NocoBaseLogo';
@@ -24,7 +24,9 @@ const topbarActionsBarClassName = css`
24
24
  align-items: center;
25
25
  color: var(--nb-topbar-action-color);
26
26
 
27
- .nb-topbar-actions-list {
27
+ .nb-topbar-actions-list,
28
+ .nb-topbar-utility-actions-list,
29
+ .nb-topbar-plugin-settings-action {
28
30
  display: inline-flex;
29
31
  align-items: center;
30
32
  height: 100%;
@@ -127,12 +129,18 @@ const getTopbarActionId = (action: TopbarActionModel) => {
127
129
  return action?.actionId || action?.uid || '';
128
130
  };
129
131
 
132
+ const PLUGIN_SETTINGS_ACTION_ID = 'plugin-settings';
133
+
130
134
  const TopbarActionsContent = React.memo((props: { actions: TopbarActionModel[]; onActionClick?: () => void }) => {
131
135
  const { allow } = useAclSnippets();
132
136
  const { token } = theme.useToken();
133
137
  const customToken = token as CustomToken;
134
138
  const actions = useMemo(() => getVisibleTopbarActions(props.actions, allow), [allow, props.actions]);
135
- const mainActions = actions.filter((action) => getTopbarActionId(action) !== USER_CENTER_ACTION_ID);
139
+ const mainActions = actions.filter((action) => {
140
+ const actionId = getTopbarActionId(action);
141
+ return actionId !== PLUGIN_SETTINGS_ACTION_ID && actionId !== USER_CENTER_ACTION_ID;
142
+ });
143
+ const pluginSettingsAction = actions.find((action) => getTopbarActionId(action) === PLUGIN_SETTINGS_ACTION_ID);
136
144
  const userCenterAction = actions.find((action) => getTopbarActionId(action) === USER_CENTER_ACTION_ID);
137
145
 
138
146
  return (
@@ -153,18 +161,33 @@ const TopbarActionsContent = React.memo((props: { actions: TopbarActionModel[];
153
161
  <ConfigProvider theme={dividerTheme}>
154
162
  <Divider type="vertical" />
155
163
  </ConfigProvider>
156
- <HelpLite />
157
- {userCenterAction ? (
158
- <ErrorBoundary
159
- key={userCenterAction.uid}
160
- FallbackComponent={TopbarActionErrorFallback}
161
- onError={(error) => {
162
- console.error('[NocoBase] Topbar action render failed.', error);
163
- }}
164
- >
165
- <FlowModelRenderer model={userCenterAction} />
166
- </ErrorBoundary>
167
- ) : null}
164
+ <div className="nb-topbar-utility-actions-list">
165
+ {pluginSettingsAction ? (
166
+ <span className="nb-topbar-plugin-settings-action" onClick={props.onActionClick}>
167
+ <ErrorBoundary
168
+ key={pluginSettingsAction.uid}
169
+ FallbackComponent={TopbarActionErrorFallback}
170
+ onError={(error) => {
171
+ console.error('[NocoBase] Topbar action render failed.', error);
172
+ }}
173
+ >
174
+ <FlowModelRenderer model={pluginSettingsAction} />
175
+ </ErrorBoundary>
176
+ </span>
177
+ ) : null}
178
+ <HelpLite />
179
+ {userCenterAction ? (
180
+ <ErrorBoundary
181
+ key={userCenterAction.uid}
182
+ FallbackComponent={TopbarActionErrorFallback}
183
+ onError={(error) => {
184
+ console.error('[NocoBase] Topbar action render failed.', error);
185
+ }}
186
+ >
187
+ <FlowModelRenderer model={userCenterAction} />
188
+ </ErrorBoundary>
189
+ ) : null}
190
+ </div>
168
191
  </div>
169
192
  );
170
193
  });