@nocobase/flow-engine 2.0.0-alpha.14 → 2.0.0-alpha.16

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.
@@ -0,0 +1,10 @@
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
+ import React from 'react';
10
+ export declare const FieldSkeleton: React.FC;
@@ -0,0 +1,64 @@
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
+ var __create = Object.create;
11
+ var __defProp = Object.defineProperty;
12
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
13
+ var __getOwnPropNames = Object.getOwnPropertyNames;
14
+ var __getProtoOf = Object.getPrototypeOf;
15
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
16
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
17
+ var __export = (target, all) => {
18
+ for (var name in all)
19
+ __defProp(target, name, { get: all[name], enumerable: true });
20
+ };
21
+ var __copyProps = (to, from, except, desc) => {
22
+ if (from && typeof from === "object" || typeof from === "function") {
23
+ for (let key of __getOwnPropNames(from))
24
+ if (!__hasOwnProp.call(to, key) && key !== except)
25
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
26
+ }
27
+ return to;
28
+ };
29
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
30
+ // If the importer is in node compatibility mode or this is not an ESM
31
+ // file that has been converted to a CommonJS file using a Babel-
32
+ // compatible transform (i.e. "__esModule" has not been set), then set
33
+ // "default" to the CommonJS "module.exports" for node compatibility.
34
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
35
+ mod
36
+ ));
37
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
38
+ var FieldSkeleton_exports = {};
39
+ __export(FieldSkeleton_exports, {
40
+ FieldSkeleton: () => FieldSkeleton
41
+ });
42
+ module.exports = __toCommonJS(FieldSkeleton_exports);
43
+ var import_react = __toESM(require("react"));
44
+ var import_provider = require("../provider");
45
+ const FieldSkeleton = /* @__PURE__ */ __name(() => {
46
+ const flowEngine = (0, import_provider.useFlowEngine)();
47
+ const token = flowEngine.context.themeToken;
48
+ return /* @__PURE__ */ import_react.default.createElement(
49
+ "span",
50
+ {
51
+ style: {
52
+ display: "inline-block",
53
+ width: "100%",
54
+ height: 16,
55
+ backgroundColor: token.colorFillSecondary,
56
+ borderRadius: token.borderRadiusSM
57
+ }
58
+ }
59
+ );
60
+ }, "FieldSkeleton");
61
+ // Annotate the CommonJS export names for ESM import in node:
62
+ 0 && (module.exports = {
63
+ FieldSkeleton
64
+ });
@@ -42,7 +42,6 @@ __export(FlowModelRenderer_exports, {
42
42
  });
43
43
  module.exports = __toCommonJS(FlowModelRenderer_exports);
44
44
  var import_reactive_react = require("@formily/reactive-react");
45
- var import_antd = require("antd");
46
45
  var import_lodash = __toESM(require("lodash"));
47
46
  var import_react = __toESM(require("react"));
48
47
  var import_react_error_boundary = require("react-error-boundary");
@@ -180,7 +179,7 @@ const FlowModelRendererCore = (0, import_reactive_react.observer)(
180
179
  const FlowModelRenderer = (0, import_reactive_react.observer)(
181
180
  ({
182
181
  model,
183
- fallback = /* @__PURE__ */ import_react.default.createElement(import_antd.Skeleton.Button, { size: "small" }),
182
+ fallback = null,
184
183
  showFlowSettings = false,
185
184
  flowSettingsVariant = "dropdown",
186
185
  hideRemoveInSettings = false,
@@ -59,7 +59,7 @@ const openStepSettingsDialog = /* @__PURE__ */ __name(async ({
59
59
  uiModeProps,
60
60
  cleanup
61
61
  }) => {
62
- var _a, _b, _c, _d, _e, _f, _g, _h;
62
+ var _a, _b, _c, _d, _e, _f;
63
63
  const t = (0, import_utils.getT)(model);
64
64
  const message = model.context.message;
65
65
  if (!model) {
@@ -133,11 +133,11 @@ const openStepSettingsDialog = /* @__PURE__ */ __name(async ({
133
133
  destroyOnClose: true,
134
134
  ...(0, import_reactive.toJS)(uiModeProps),
135
135
  // 透传 navigation,便于变量元信息根据真实视图栈推断父级弹窗
136
- inputArgs: {
137
- ...((_e = (0, import_reactive.toJS)(uiModeProps)) == null ? void 0 : _e.inputArgs) || {},
138
- navigation: ((_f = ctx == null ? void 0 : ctx.view) == null ? void 0 : _f.navigation) ?? ((_h = (_g = (0, import_reactive.toJS)(uiModeProps)) == null ? void 0 : _g.inputArgs) == null ? void 0 : _h.navigation),
139
- __isSettingsPopup: true
140
- },
136
+ inputArgs: (0, import_utils.buildSettingsViewInputArgs)(
137
+ model,
138
+ { ...((_e = (0, import_reactive.toJS)(uiModeProps)) == null ? void 0 : _e.inputArgs) || {}, __isSettingsPopup: true },
139
+ { navigationOverride: (_f = ctx == null ? void 0 : ctx.view) == null ? void 0 : _f.navigation }
140
+ ),
141
141
  onClose: /* @__PURE__ */ __name(() => {
142
142
  if (cleanup) {
143
143
  cleanup();
@@ -603,6 +603,8 @@ const _FlowSettings = class _FlowSettings {
603
603
  zIndex: 5e3,
604
604
  // 允许透传其它 props(如 maskClosable、footer 等),但确保 content 由我们接管
605
605
  ...modeProps,
606
+ // 统一构造 settings 弹窗的 inputArgs(集合/记录/父导航/关联)
607
+ inputArgs: (0, import_utils.buildSettingsViewInputArgs)(model, modeProps == null ? void 0 : modeProps.inputArgs),
606
608
  content: /* @__PURE__ */ __name((currentView, viewCtx) => {
607
609
  viewCtx == null ? void 0 : viewCtx.defineMethod("getStepFormValues", (flowKey2, stepKey2) => {
608
610
  var _a2;
@@ -42,6 +42,7 @@ export declare class FlowModel<Structure extends DefaultStructure = DefaultStruc
42
42
  private _options;
43
43
  protected _title: string;
44
44
  isNew: boolean;
45
+ skeleton: any;
45
46
  /**
46
47
  * 所有 fork 实例的引用集合。
47
48
  * 使用 Set 便于在销毁时主动遍历并调用 dispose,避免悬挂引用。
@@ -93,6 +93,7 @@ const _FlowModel = class _FlowModel {
93
93
  __publicField(this, "_title");
94
94
  __publicField(this, "isNew", false);
95
95
  // 标记是否为新建状态
96
+ __publicField(this, "skeleton", null);
96
97
  /**
97
98
  * 所有 fork 实例的引用集合。
98
99
  * 使用 Set 便于在销毁时主动遍历并调用 dispose,避免悬挂引用。
package/lib/provider.d.ts CHANGED
@@ -17,6 +17,8 @@ export declare const FlowEngineProvider: React.FC<FlowEngineProviderProps>;
17
17
  export declare const FlowEngineGlobalsContextProvider: React.FC<{
18
18
  children: React.ReactNode;
19
19
  }>;
20
- export declare const useFlowEngine: () => FlowEngine;
20
+ export declare const useFlowEngine: ({ throwError }?: {
21
+ throwError?: boolean;
22
+ }) => FlowEngine;
21
23
  export declare const useFlowEngineContext: () => FlowEngineContext;
22
24
  export {};
package/lib/provider.js CHANGED
@@ -93,9 +93,9 @@ const FlowEngineGlobalsContextProvider = /* @__PURE__ */ __name(({ children }) =
93
93
  }, [engine, drawer, modal, message, notification, config, popover, token, dialog, embed]);
94
94
  return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, children, contextHolder, popoverContextHolder, pageContextHolder, dialogContextHolder);
95
95
  }, "FlowEngineGlobalsContextProvider");
96
- const useFlowEngine = /* @__PURE__ */ __name(() => {
96
+ const useFlowEngine = /* @__PURE__ */ __name(({ throwError = true } = {}) => {
97
97
  const context = (0, import_react.useContext)(FlowEngineReactContext);
98
- if (!context) {
98
+ if (!context && throwError) {
99
99
  throw new Error(
100
100
  "useFlowEngine must be used within a FlowEngineProvider, and FlowEngineProvider must be supplied with an engine."
101
101
  );
@@ -0,0 +1,19 @@
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
+ import type { FlowModel } from '../models';
10
+ /**
11
+ * 统一构造设置弹窗 openView 的 inputArgs:
12
+ * - collectionName/dataSourceKey/filterByTk:优先从 model.context 推断;否则回退父视图 inputArgs
13
+ * - sourceId:优先从 resource.getSourceId();否则回退父视图 inputArgs
14
+ * - associationName:来自 association.resourceName 或 resource.getResourceName()
15
+ * - navigation:父视图 navigation 或传入的 navigationOverride
16
+ */
17
+ export declare function buildSettingsViewInputArgs(model: FlowModel, extra?: Record<string, any>, options?: {
18
+ navigationOverride?: any;
19
+ }): Record<string, any>;
@@ -0,0 +1,75 @@
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
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
15
+ var __export = (target, all) => {
16
+ for (var name in all)
17
+ __defProp(target, name, { get: all[name], enumerable: true });
18
+ };
19
+ var __copyProps = (to, from, except, desc) => {
20
+ if (from && typeof from === "object" || typeof from === "function") {
21
+ for (let key of __getOwnPropNames(from))
22
+ if (!__hasOwnProp.call(to, key) && key !== except)
23
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
24
+ }
25
+ return to;
26
+ };
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+ var buildSettingsViewInputArgs_exports = {};
29
+ __export(buildSettingsViewInputArgs_exports, {
30
+ buildSettingsViewInputArgs: () => buildSettingsViewInputArgs
31
+ });
32
+ module.exports = __toCommonJS(buildSettingsViewInputArgs_exports);
33
+ var import_variablesParams = require("./variablesParams");
34
+ function buildSettingsViewInputArgs(model, extra, options) {
35
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
36
+ const defaults = {};
37
+ try {
38
+ const ref = (0, import_variablesParams.inferRecordRef)(model.context);
39
+ if (ref == null ? void 0 : ref.collection) {
40
+ defaults.collectionName = ref.collection;
41
+ defaults.dataSourceKey = ref.dataSourceKey || "main";
42
+ defaults.filterByTk = ref.filterByTk;
43
+ }
44
+ const parentArgs = ((_b = (_a = model == null ? void 0 : model.context) == null ? void 0 : _a.view) == null ? void 0 : _b.inputArgs) || {};
45
+ if (!defaults.collectionName && (parentArgs == null ? void 0 : parentArgs.collectionName)) {
46
+ defaults.collectionName = parentArgs.collectionName;
47
+ defaults.dataSourceKey = parentArgs.dataSourceKey || defaults.dataSourceKey || "main";
48
+ }
49
+ if (defaults.filterByTk == null && (parentArgs == null ? void 0 : parentArgs.filterByTk) != null) {
50
+ defaults.filterByTk = parentArgs.filterByTk;
51
+ }
52
+ if (defaults.sourceId == null && (parentArgs == null ? void 0 : parentArgs.sourceId) != null) {
53
+ defaults.sourceId = parentArgs.sourceId;
54
+ }
55
+ const nav = (options == null ? void 0 : options.navigationOverride) ?? ((_d = (_c = model == null ? void 0 : model.context) == null ? void 0 : _c.view) == null ? void 0 : _d.navigation);
56
+ if (nav) {
57
+ defaults.navigation = nav;
58
+ }
59
+ const sid = (_g = (_f = (_e = model == null ? void 0 : model.context) == null ? void 0 : _e.resource) == null ? void 0 : _f.getSourceId) == null ? void 0 : _g.call(_f);
60
+ if (sid !== void 0 && sid !== null && String(sid) !== "") {
61
+ defaults.sourceId = sid;
62
+ }
63
+ const assocName = ((_i = (_h = model == null ? void 0 : model.context) == null ? void 0 : _h.association) == null ? void 0 : _i.resourceName) || ((_l = (_k = (_j = model == null ? void 0 : model.context) == null ? void 0 : _j.resource) == null ? void 0 : _k.getResourceName) == null ? void 0 : _l.call(_k));
64
+ if (typeof assocName === "string" && assocName) {
65
+ defaults.associationName = assocName;
66
+ }
67
+ } catch (_) {
68
+ }
69
+ return { ...defaults, ...extra || {} };
70
+ }
71
+ __name(buildSettingsViewInputArgs, "buildSettingsViewInputArgs");
72
+ // Annotate the CommonJS export names for ESM import in node:
73
+ 0 && (module.exports = {
74
+ buildSettingsViewInputArgs
75
+ });
@@ -19,4 +19,5 @@ export { buildRecordMeta, collectContextParamsForTemplate, createCurrentRecordMe
19
19
  export { extractPropertyPath, formatPathToVariable, isVariableExpression } from './context';
20
20
  export { clearAutoFlowError, getAutoFlowError, setAutoFlowError, type AutoFlowError } from './autoFlowError';
21
21
  export { parsePathnameToViewParams, type ViewParam } from './parsePathnameToViewParams';
22
+ export { buildSettingsViewInputArgs } from './buildSettingsViewInputArgs';
22
23
  export { createSafeDocument, createSafeWindow } from './safeGlobals';
@@ -32,6 +32,7 @@ __export(utils_exports, {
32
32
  FlowExitException: () => import_exceptions.FlowExitException,
33
33
  MENU_KEYS: () => import_constants.MENU_KEYS,
34
34
  buildRecordMeta: () => import_variablesParams.buildRecordMeta,
35
+ buildSettingsViewInputArgs: () => import_buildSettingsViewInputArgs.buildSettingsViewInputArgs,
35
36
  clearAutoFlowError: () => import_autoFlowError.clearAutoFlowError,
36
37
  collectContextParamsForTemplate: () => import_variablesParams.collectContextParamsForTemplate,
37
38
  compileUiSchema: () => import_schema_utils.compileUiSchema,
@@ -75,6 +76,7 @@ var import_variablesParams = require("./variablesParams");
75
76
  var import_context = require("./context");
76
77
  var import_autoFlowError = require("./autoFlowError");
77
78
  var import_parsePathnameToViewParams = require("./parsePathnameToViewParams");
79
+ var import_buildSettingsViewInputArgs = require("./buildSettingsViewInputArgs");
78
80
  var import_safeGlobals = require("./safeGlobals");
79
81
  // Annotate the CommonJS export names for ESM import in node:
80
82
  0 && (module.exports = {
@@ -84,6 +86,7 @@ var import_safeGlobals = require("./safeGlobals");
84
86
  FlowExitException,
85
87
  MENU_KEYS,
86
88
  buildRecordMeta,
89
+ buildSettingsViewInputArgs,
87
90
  clearAutoFlowError,
88
91
  collectContextParamsForTemplate,
89
92
  compileUiSchema,
@@ -265,7 +265,7 @@ function createPopupMeta(ctx) {
265
265
  return out;
266
266
  }, "buildVariablesParams"),
267
267
  properties: /* @__PURE__ */ __name(async () => {
268
- var _a;
268
+ var _a, _b, _c, _d, _e;
269
269
  const props = {};
270
270
  props.uid = { type: "string", title: t("Popup uid") };
271
271
  const base = await (0, import_variablesParams.buildRecordMeta)(
@@ -277,16 +277,30 @@ function createPopupMeta(ctx) {
277
277
  try {
278
278
  const inputArgs = ((_a = ctx.view) == null ? void 0 : _a.inputArgs) || {};
279
279
  const srcId = inputArgs == null ? void 0 : inputArgs.sourceId;
280
- const assoc = inputArgs == null ? void 0 : inputArgs.associationName;
281
- const dsKey = (inputArgs == null ? void 0 : inputArgs.dataSourceKey) || "main";
280
+ let assoc = inputArgs == null ? void 0 : inputArgs.associationName;
281
+ let dsKey = (inputArgs == null ? void 0 : inputArgs.dataSourceKey) || "main";
282
+ if (!assoc || typeof assoc !== "string" || !assoc.includes(".")) {
283
+ const nav = (_b = ctx.view) == null ? void 0 : _b.navigation;
284
+ const stack = Array.isArray(nav == null ? void 0 : nav.viewStack) ? nav.viewStack : [];
285
+ const last = stack == null ? void 0 : stack[stack.length - 1];
286
+ if (last == null ? void 0 : last.viewUid) {
287
+ let model = (_d = (_c = ctx == null ? void 0 : ctx.engine) == null ? void 0 : _c.getModel) == null ? void 0 : _d.call(_c, last.viewUid);
288
+ if (!model) {
289
+ model = await ctx.engine.loadModel({ uid: last.viewUid });
290
+ }
291
+ const p = ((_e = model == null ? void 0 : model.getStepParams) == null ? void 0 : _e.call(model, "popupSettings", "openView")) || {};
292
+ assoc = (p == null ? void 0 : p.associationName) || assoc;
293
+ dsKey = (p == null ? void 0 : p.dataSourceKey) || dsKey;
294
+ }
295
+ }
282
296
  if (srcId != null && srcId !== "" && assoc && typeof assoc === "string") {
283
297
  const parentCollectionName = String(assoc).includes(".") ? String(assoc).split(".")[0] : void 0;
284
298
  if (parentCollectionName) {
285
299
  const parentCollectionAccessor = /* @__PURE__ */ __name(() => {
286
- var _a2, _b, _c, _d;
300
+ var _a2, _b2, _c2, _d2;
287
301
  try {
288
- const ds = (_b = (_a2 = ctx.dataSourceManager) == null ? void 0 : _a2.getDataSource) == null ? void 0 : _b.call(_a2, dsKey);
289
- return ((_d = (_c = ds == null ? void 0 : ds.collectionManager) == null ? void 0 : _c.getCollection) == null ? void 0 : _d.call(_c, parentCollectionName)) || null;
302
+ const ds = (_b2 = (_a2 = ctx.dataSourceManager) == null ? void 0 : _a2.getDataSource) == null ? void 0 : _b2.call(_a2, dsKey);
303
+ return ((_d2 = (_c2 = ds == null ? void 0 : ds.collectionManager) == null ? void 0 : _c2.getCollection) == null ? void 0 : _d2.call(_c2, parentCollectionName)) || null;
290
304
  } catch (_) {
291
305
  return null;
292
306
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/flow-engine",
3
- "version": "2.0.0-alpha.14",
3
+ "version": "2.0.0-alpha.16",
4
4
  "private": false,
5
5
  "description": "A standalone flow engine for NocoBase, managing workflows, models, and actions.",
6
6
  "main": "lib/index.js",
@@ -33,5 +33,5 @@
33
33
  ],
34
34
  "author": "NocoBase Team",
35
35
  "license": "AGPL-3.0",
36
- "gitHead": "6b4785d03d0a551f1fe6815e9bcbc7a9f38901df"
36
+ "gitHead": "cc7ffb72d642e86eb95cc3d5831e77d73b788d6c"
37
37
  }
@@ -0,0 +1,27 @@
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 React from 'react';
11
+ import { useFlowEngine } from '../provider';
12
+
13
+ export const FieldSkeleton: React.FC = () => {
14
+ const flowEngine = useFlowEngine();
15
+ const token = flowEngine.context.themeToken;
16
+ return (
17
+ <span
18
+ style={{
19
+ display: 'inline-block',
20
+ width: '100%',
21
+ height: 16,
22
+ backgroundColor: token.colorFillSecondary,
23
+ borderRadius: token.borderRadiusSM,
24
+ }}
25
+ />
26
+ );
27
+ };
@@ -43,7 +43,6 @@
43
43
  */
44
44
 
45
45
  import { observer } from '@formily/reactive-react';
46
- import { Skeleton, Spin } from 'antd';
47
46
  import _ from 'lodash';
48
47
  import React from 'react';
49
48
  import { ErrorBoundary } from 'react-error-boundary';
@@ -329,7 +328,7 @@ const FlowModelRendererCore: React.FC<{
329
328
  export const FlowModelRenderer: React.FC<FlowModelRendererProps> = observer(
330
329
  ({
331
330
  model,
332
- fallback = <Skeleton.Button size="small" />,
331
+ fallback = null,
333
332
  showFlowSettings = false,
334
333
  flowSettingsVariant = 'dropdown',
335
334
  hideRemoveInSettings = false,
@@ -14,7 +14,14 @@ import { Button, Space } from 'antd';
14
14
  import React, { useEffect } from 'react';
15
15
  import { FlowSettingsContextProvider, useFlowSettingsContext } from '../../../../hooks/useFlowSettingsContext';
16
16
  import { StepSettingsDialogProps } from '../../../../types';
17
- import { compileUiSchema, FlowExitException, getT, resolveDefaultParams, resolveStepUiSchema } from '../../../../utils';
17
+ import {
18
+ compileUiSchema,
19
+ FlowExitException,
20
+ getT,
21
+ resolveDefaultParams,
22
+ resolveStepUiSchema,
23
+ buildSettingsViewInputArgs,
24
+ } from '../../../../utils';
18
25
 
19
26
  const SchemaField = createSchemaField();
20
27
 
@@ -136,11 +143,11 @@ const openStepSettingsDialog = async ({
136
143
  destroyOnClose: true,
137
144
  ...toJS(uiModeProps),
138
145
  // 透传 navigation,便于变量元信息根据真实视图栈推断父级弹窗
139
- inputArgs: {
140
- ...(toJS(uiModeProps)?.inputArgs || {}),
141
- navigation: ctx?.view?.navigation ?? toJS(uiModeProps)?.inputArgs?.navigation,
142
- __isSettingsPopup: true,
143
- },
146
+ inputArgs: buildSettingsViewInputArgs(
147
+ model as any,
148
+ { ...(toJS(uiModeProps)?.inputArgs || {}), __isSettingsPopup: true },
149
+ { navigationOverride: ctx?.view?.navigation },
150
+ ),
144
151
  onClose: () => {
145
152
  if (cleanup) {
146
153
  cleanup();
@@ -29,6 +29,7 @@ import {
29
29
  resolveStepUiSchema,
30
30
  resolveUiMode,
31
31
  setupRuntimeContextSteps,
32
+ buildSettingsViewInputArgs,
32
33
  } from './utils';
33
34
  import { FlowStepContext } from './hooks/useFlowStep';
34
35
 
@@ -764,6 +765,8 @@ export class FlowSettings {
764
765
  zIndex: 5000,
765
766
  // 允许透传其它 props(如 maskClosable、footer 等),但确保 content 由我们接管
766
767
  ...modeProps,
768
+ // 统一构造 settings 弹窗的 inputArgs(集合/记录/父导航/关联)
769
+ inputArgs: buildSettingsViewInputArgs(model as any, (modeProps as any)?.inputArgs),
767
770
  content: (currentView, viewCtx) => {
768
771
  viewCtx?.defineMethod('getStepFormValues', (flowKey: string, stepKey: string) => {
769
772
  return forms.get(keyOf({ flowKey, stepKey }))?.values;
@@ -89,6 +89,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
89
89
  private _options: FlowModelOptions<Structure>;
90
90
  protected _title: string;
91
91
  public isNew = false; // 标记是否为新建状态
92
+ public skeleton = null;
92
93
 
93
94
  /**
94
95
  * 所有 fork 实例的引用集合。
package/src/provider.tsx CHANGED
@@ -84,10 +84,10 @@ export const FlowEngineGlobalsContextProvider: React.FC<{ children: React.ReactN
84
84
  </>
85
85
  );
86
86
  };
87
-
88
- export const useFlowEngine = (): FlowEngine => {
87
+ // 不 throw Error 怎么处理?
88
+ export const useFlowEngine = ({ throwError = true } = {}): FlowEngine => {
89
89
  const context = useContext(FlowEngineReactContext);
90
- if (!context) {
90
+ if (!context && throwError) {
91
91
  // This error should ideally not be hit if FlowEngineProvider is used correctly at the root
92
92
  // and always supplied with an engine.
93
93
  throw new Error(
@@ -0,0 +1,72 @@
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 type { FlowModel } from '../models';
11
+ import { inferRecordRef } from './variablesParams';
12
+
13
+ /**
14
+ * 统一构造设置弹窗 openView 的 inputArgs:
15
+ * - collectionName/dataSourceKey/filterByTk:优先从 model.context 推断;否则回退父视图 inputArgs
16
+ * - sourceId:优先从 resource.getSourceId();否则回退父视图 inputArgs
17
+ * - associationName:来自 association.resourceName 或 resource.getResourceName()
18
+ * - navigation:父视图 navigation 或传入的 navigationOverride
19
+ */
20
+ export function buildSettingsViewInputArgs(
21
+ model: FlowModel,
22
+ extra?: Record<string, any>,
23
+ options?: { navigationOverride?: any },
24
+ ): Record<string, any> {
25
+ const defaults: Record<string, any> = {};
26
+
27
+ try {
28
+ // 1) 当前记录/集合
29
+ const ref = inferRecordRef((model as any).context);
30
+ if (ref?.collection) {
31
+ defaults.collectionName = ref.collection;
32
+ defaults.dataSourceKey = ref.dataSourceKey || 'main';
33
+ defaults.filterByTk = ref.filterByTk;
34
+ }
35
+
36
+ // 2) 回退父视图 inputArgs(如在抽屉/页面中)
37
+ const parentArgs = (model as any)?.context?.view?.inputArgs || {};
38
+ if (!defaults.collectionName && parentArgs?.collectionName) {
39
+ defaults.collectionName = parentArgs.collectionName;
40
+ defaults.dataSourceKey = parentArgs.dataSourceKey || defaults.dataSourceKey || 'main';
41
+ }
42
+ if (defaults.filterByTk == null && parentArgs?.filterByTk != null) {
43
+ defaults.filterByTk = parentArgs.filterByTk;
44
+ }
45
+ if (defaults.sourceId == null && parentArgs?.sourceId != null) {
46
+ defaults.sourceId = parentArgs.sourceId;
47
+ }
48
+
49
+ // 3) 导航(用于推断“上级弹窗”)
50
+ const nav = options?.navigationOverride ?? (model as any)?.context?.view?.navigation;
51
+ if (nav) {
52
+ defaults.navigation = nav;
53
+ }
54
+
55
+ // 4) 直接从资源读取 sourceId(优先级高于父视图)
56
+ const sid = (model as any)?.context?.resource?.getSourceId?.();
57
+ if (sid !== undefined && sid !== null && String(sid) !== '') {
58
+ defaults.sourceId = sid;
59
+ }
60
+
61
+ // 5) 关联名(用于“上级记录”)
62
+ const assocName =
63
+ (model as any)?.context?.association?.resourceName || (model as any)?.context?.resource?.getResourceName?.();
64
+ if (typeof assocName === 'string' && assocName) {
65
+ defaults.associationName = assocName;
66
+ }
67
+ } catch (_) {
68
+ // 忽略推断异常
69
+ }
70
+
71
+ return { ...defaults, ...(extra || {}) };
72
+ }
@@ -57,6 +57,7 @@ export { extractPropertyPath, formatPathToVariable, isVariableExpression } from
57
57
 
58
58
  export { clearAutoFlowError, getAutoFlowError, setAutoFlowError, type AutoFlowError } from './autoFlowError';
59
59
  export { parsePathnameToViewParams, type ViewParam } from './parsePathnameToViewParams';
60
+ export { buildSettingsViewInputArgs } from './buildSettingsViewInputArgs';
60
61
 
61
62
  // 安全全局对象(window/document)
62
63
  export { createSafeDocument, createSafeWindow } from './safeGlobals';
@@ -287,8 +287,25 @@ export function createPopupMeta(ctx: FlowContext): PropertyMetaFactory {
287
287
  try {
288
288
  const inputArgs = (ctx.view as any)?.inputArgs || {};
289
289
  const srcId = inputArgs?.sourceId;
290
- const assoc: string | undefined = inputArgs?.associationName;
291
- const dsKey: string = inputArgs?.dataSourceKey || 'main';
290
+ let assoc: string | undefined = inputArgs?.associationName;
291
+ let dsKey: string = inputArgs?.dataSourceKey || 'main';
292
+
293
+ // 兜底:若 associationName 缺失或不含“.”,尝试从当前视图模型的 openView 参数推断
294
+ if (!assoc || typeof assoc !== 'string' || !assoc.includes('.')) {
295
+ const nav = (ctx.view as any)?.navigation;
296
+ const stack = Array.isArray(nav?.viewStack) ? nav.viewStack : [];
297
+ const last = stack?.[stack.length - 1];
298
+ if (last?.viewUid) {
299
+ let model: any = ctx?.engine?.getModel?.(last.viewUid);
300
+ if (!model) {
301
+ model = await ctx.engine.loadModel({ uid: last.viewUid });
302
+ }
303
+ const p = model?.getStepParams?.('popupSettings', 'openView') || {};
304
+ assoc = p?.associationName || assoc;
305
+ dsKey = p?.dataSourceKey || dsKey;
306
+ }
307
+ }
308
+
292
309
  if (srcId != null && srcId !== '' && assoc && typeof assoc === 'string') {
293
310
  const parentCollectionName = String(assoc).includes('.') ? String(assoc).split('.')[0] : undefined;
294
311
  if (parentCollectionName) {