@nocobase/plugin-ui-templates 2.0.0-alpha.57
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.
- package/LICENSE.txt +172 -0
- package/build.config.ts +12 -0
- package/client.js +1 -0
- package/dist/client/collections/flowModelTemplates.d.ts +67 -0
- package/dist/client/components/FlowModelTemplatesPage.d.ts +12 -0
- package/dist/client/components/TemplateSelectOption.d.ts +20 -0
- package/dist/client/constants.d.ts +9 -0
- package/dist/client/hooks/useFlowModelTemplateActions.d.ts +24 -0
- package/dist/client/index.d.ts +13 -0
- package/dist/client/index.js +10 -0
- package/dist/client/locale.d.ts +18 -0
- package/dist/client/menuExtensions.d.ts +9 -0
- package/dist/client/models/ReferenceBlockModel.d.ts +47 -0
- package/dist/client/models/ReferenceFormGridModel.d.ts +38 -0
- package/dist/client/models/SubModelTemplateImporterModel.d.ts +55 -0
- package/dist/client/models/referenceShared.d.ts +23 -0
- package/dist/client/openViewActionExtensions.d.ts +10 -0
- package/dist/client/schemas/flowModelTemplates.d.ts +11 -0
- package/dist/client/subModelMenuExtensions.d.ts +10 -0
- package/dist/client/utils/infiniteSelect.d.ts +28 -0
- package/dist/client/utils/refHost.d.ts +20 -0
- package/dist/client/utils/templateCompatibility.d.ts +91 -0
- package/dist/client.d.ts +9 -0
- package/dist/client.js +42 -0
- package/dist/externalVersion.js +24 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +48 -0
- package/dist/locale/de-DE.json +14 -0
- package/dist/locale/en-US.json +72 -0
- package/dist/locale/es-ES.json +14 -0
- package/dist/locale/fr-FR.json +14 -0
- package/dist/locale/hu-HU.json +14 -0
- package/dist/locale/id-ID.json +14 -0
- package/dist/locale/it-IT.json +14 -0
- package/dist/locale/ja-JP.json +14 -0
- package/dist/locale/ko-KR.json +14 -0
- package/dist/locale/nl-NL.json +14 -0
- package/dist/locale/pt-BR.json +14 -0
- package/dist/locale/ru-RU.json +14 -0
- package/dist/locale/tr-TR.json +14 -0
- package/dist/locale/uk-UA.json +14 -0
- package/dist/locale/vi-VN.json +14 -0
- package/dist/locale/zh-CN.json +71 -0
- package/dist/locale/zh-TW.json +14 -0
- package/dist/server/collections/flowModelTemplateUsages.d.ts +11 -0
- package/dist/server/collections/flowModelTemplateUsages.js +71 -0
- package/dist/server/collections/flowModelTemplates.d.ts +11 -0
- package/dist/server/collections/flowModelTemplates.js +96 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/index.js +42 -0
- package/dist/server/plugin.d.ts +17 -0
- package/dist/server/plugin.js +242 -0
- package/dist/server/resources/flowModelTemplateUsages.d.ts +19 -0
- package/dist/server/resources/flowModelTemplateUsages.js +91 -0
- package/dist/server/resources/flowModelTemplates.d.ts +20 -0
- package/dist/server/resources/flowModelTemplates.js +267 -0
- package/package.json +37 -0
- package/server.js +1 -0
- package/src/client/__tests__/openViewActionExtensions.test.ts +1208 -0
- package/src/client/collections/flowModelTemplates.ts +131 -0
- package/src/client/components/FlowModelTemplatesPage.tsx +78 -0
- package/src/client/components/TemplateSelectOption.tsx +106 -0
- package/src/client/constants.ts +10 -0
- package/src/client/hooks/useFlowModelTemplateActions.tsx +137 -0
- package/src/client/index.ts +54 -0
- package/src/client/locale.ts +40 -0
- package/src/client/menuExtensions.tsx +1033 -0
- package/src/client/models/ReferenceBlockModel.tsx +793 -0
- package/src/client/models/ReferenceFormGridModel.tsx +302 -0
- package/src/client/models/SubModelTemplateImporterModel.tsx +634 -0
- package/src/client/models/__tests__/ReferenceBlockModel.test.tsx +482 -0
- package/src/client/models/__tests__/ReferenceFormGridModel.test.tsx +175 -0
- package/src/client/models/__tests__/SubModelTemplateImporterModel.test.ts +447 -0
- package/src/client/models/referenceShared.tsx +99 -0
- package/src/client/openViewActionExtensions.tsx +981 -0
- package/src/client/schemas/flowModelTemplates.ts +264 -0
- package/src/client/subModelMenuExtensions.ts +103 -0
- package/src/client/utils/infiniteSelect.ts +150 -0
- package/src/client/utils/refHost.ts +44 -0
- package/src/client/utils/templateCompatibility.ts +374 -0
- package/src/client.ts +10 -0
- package/src/index.ts +11 -0
- package/src/locale/de-DE.json +14 -0
- package/src/locale/en-US.json +72 -0
- package/src/locale/es-ES.json +14 -0
- package/src/locale/fr-FR.json +14 -0
- package/src/locale/hu-HU.json +14 -0
- package/src/locale/id-ID.json +14 -0
- package/src/locale/it-IT.json +14 -0
- package/src/locale/ja-JP.json +14 -0
- package/src/locale/ko-KR.json +14 -0
- package/src/locale/nl-NL.json +14 -0
- package/src/locale/pt-BR.json +14 -0
- package/src/locale/ru-RU.json +14 -0
- package/src/locale/tr-TR.json +14 -0
- package/src/locale/uk-UA.json +14 -0
- package/src/locale/vi-VN.json +14 -0
- package/src/locale/zh-CN.json +71 -0
- package/src/locale/zh-TW.json +14 -0
- package/src/server/__tests__/template-usage.test.ts +351 -0
- package/src/server/collections/flowModelTemplateUsages.ts +51 -0
- package/src/server/collections/flowModelTemplates.ts +76 -0
- package/src/server/index.ts +10 -0
- package/src/server/plugin.ts +236 -0
- package/src/server/resources/flowModelTemplateUsages.ts +61 -0
- package/src/server/resources/flowModelTemplates.ts +251 -0
|
@@ -0,0 +1,302 @@
|
|
|
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 _ from 'lodash';
|
|
12
|
+
import { type CreateModelOptions, FlowEngine, FlowModel } from '@nocobase/flow-engine';
|
|
13
|
+
import { FormGridModel } from '@nocobase/client';
|
|
14
|
+
import { REF_HOST_CTX_KEY } from '../constants';
|
|
15
|
+
import { NAMESPACE } from '../locale';
|
|
16
|
+
import {
|
|
17
|
+
ensureBlockScopedEngine,
|
|
18
|
+
ReferenceScopedRenderer,
|
|
19
|
+
renderReferenceTargetPlaceholder,
|
|
20
|
+
unlinkScopedEngine,
|
|
21
|
+
} from './referenceShared';
|
|
22
|
+
|
|
23
|
+
const SETTINGS_FLOW_KEY = 'referenceSettings';
|
|
24
|
+
const SETTINGS_STEP_KEY = 'useTemplate';
|
|
25
|
+
|
|
26
|
+
export { REF_HOST_CTX_KEY };
|
|
27
|
+
|
|
28
|
+
export type ReferenceFormGridTargetSettings = {
|
|
29
|
+
/** 模板 uid(flowModelTemplates.uid) */
|
|
30
|
+
templateUid: string;
|
|
31
|
+
/** 模板名称(用于 UI 展示,可选) */
|
|
32
|
+
templateName?: string;
|
|
33
|
+
/** 模板根区块 uid(flowModels.uid) */
|
|
34
|
+
targetUid: string;
|
|
35
|
+
/** 从模板根上取片段的路径,当前仅支持 'subModels.grid' */
|
|
36
|
+
targetPath?: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
type ReferenceHostInfo = {
|
|
40
|
+
hostUid?: string;
|
|
41
|
+
hostUse?: string;
|
|
42
|
+
ref: {
|
|
43
|
+
templateUid: string;
|
|
44
|
+
templateName?: string;
|
|
45
|
+
targetUid: string;
|
|
46
|
+
targetPath: string;
|
|
47
|
+
mountSubKey: 'grid';
|
|
48
|
+
mode: 'reference';
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export class ReferenceFormGridModel extends FlowModel {
|
|
53
|
+
private _scopedEngine?: FlowEngine;
|
|
54
|
+
private _targetGrid?: FlowModel;
|
|
55
|
+
private _resolvedTargetUid?: string;
|
|
56
|
+
private _invalidTargetUid?: string;
|
|
57
|
+
|
|
58
|
+
constructor(options: any) {
|
|
59
|
+
super(options);
|
|
60
|
+
|
|
61
|
+
const forwardedReadMethods = new Set<string>(['mapSubModels', 'filterSubModels', 'findSubModel', 'hasSubModel']);
|
|
62
|
+
|
|
63
|
+
const proxy = new Proxy(this as any, {
|
|
64
|
+
get(target, prop: string | symbol) {
|
|
65
|
+
const t = target._targetGrid as FlowModel | undefined;
|
|
66
|
+
if (t) {
|
|
67
|
+
if (prop === 'subModels') return t.subModels;
|
|
68
|
+
if (typeof prop === 'string' && forwardedReadMethods.has(prop)) {
|
|
69
|
+
const val = Reflect.get(t as any, prop, t as any);
|
|
70
|
+
return typeof val === 'function' ? val.bind(t) : val;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (prop in target) {
|
|
75
|
+
const val = Reflect.get(target, prop, target);
|
|
76
|
+
if (typeof val === 'function' && prop !== 'constructor') {
|
|
77
|
+
return val.bind(target);
|
|
78
|
+
}
|
|
79
|
+
return val;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (t) {
|
|
83
|
+
const val = Reflect.get(t as any, prop, t as any);
|
|
84
|
+
if (typeof val === 'function' && prop !== 'constructor') {
|
|
85
|
+
return val.bind(t);
|
|
86
|
+
}
|
|
87
|
+
if (val !== undefined) return val;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return undefined;
|
|
91
|
+
},
|
|
92
|
+
has(target, prop: string | symbol) {
|
|
93
|
+
if (prop in target) return true;
|
|
94
|
+
const t = target._targetGrid as FlowModel | undefined;
|
|
95
|
+
return !!t && prop in t;
|
|
96
|
+
},
|
|
97
|
+
ownKeys(target) {
|
|
98
|
+
const keys = new Set(Reflect.ownKeys(target));
|
|
99
|
+
const t = target._targetGrid as FlowModel | undefined;
|
|
100
|
+
if (t) {
|
|
101
|
+
for (const k of Reflect.ownKeys(t)) keys.add(k);
|
|
102
|
+
}
|
|
103
|
+
return Array.from(keys);
|
|
104
|
+
},
|
|
105
|
+
getOwnPropertyDescriptor(target, prop: string | symbol) {
|
|
106
|
+
const desc = Reflect.getOwnPropertyDescriptor(target, prop);
|
|
107
|
+
if (desc) return desc;
|
|
108
|
+
const t = target._targetGrid as FlowModel | undefined;
|
|
109
|
+
if (!t) return undefined;
|
|
110
|
+
return Object.getOwnPropertyDescriptor(t, prop) || undefined;
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return proxy as any;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private _ensureScopedEngine(): FlowEngine {
|
|
118
|
+
this._scopedEngine = ensureBlockScopedEngine(this.flowEngine, this._scopedEngine);
|
|
119
|
+
return this._scopedEngine;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private _getTargetSettings(): ReferenceFormGridTargetSettings | undefined {
|
|
123
|
+
const raw = this.getStepParams(SETTINGS_FLOW_KEY, SETTINGS_STEP_KEY);
|
|
124
|
+
if (!raw || typeof raw !== 'object') return undefined;
|
|
125
|
+
const templateUid = String((raw as any).templateUid || '').trim();
|
|
126
|
+
const targetUid = String((raw as any).targetUid || '').trim();
|
|
127
|
+
if (!templateUid || !targetUid) return undefined;
|
|
128
|
+
const templateName = String((raw as any).templateName || '').trim() || undefined;
|
|
129
|
+
const targetPath = String((raw as any).targetPath || '').trim() || undefined;
|
|
130
|
+
return { templateUid, templateName, targetUid, targetPath };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private _syncHostExtraTitle(settings?: ReferenceFormGridTargetSettings) {
|
|
134
|
+
const host = this.parent as FlowModel | undefined;
|
|
135
|
+
if (!host) return;
|
|
136
|
+
|
|
137
|
+
const master: any = (host as any)?.isFork ? (host as any).master || host : host;
|
|
138
|
+
const targets: FlowModel[] = [host];
|
|
139
|
+
if (master && master !== host && master instanceof FlowModel) targets.push(master);
|
|
140
|
+
master?.forks?.forEach?.((f: any) => f instanceof FlowModel && targets.push(f));
|
|
141
|
+
|
|
142
|
+
// 未配置时清空 extraTitle,回退为原有 title(不修改用户 title)
|
|
143
|
+
if (!settings) {
|
|
144
|
+
targets.forEach((m) => m.setExtraTitle(''));
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const label = host.context.t('Reference template', { ns: [NAMESPACE, 'client'], nsMode: 'fallback' });
|
|
149
|
+
const fieldsOnly = host.context.t('(Fields only)', { ns: [NAMESPACE, 'client'], nsMode: 'fallback' });
|
|
150
|
+
const name = settings.templateName?.trim() || settings.templateUid?.trim() || '';
|
|
151
|
+
const extra = name ? `${label}: ${name} ${fieldsOnly}` : `${label} ${fieldsOnly}`;
|
|
152
|
+
|
|
153
|
+
targets.forEach((m) => m.setExtraTitle(extra.trim()));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
addSubModel<T extends FlowModel>(subKey: string, options: CreateModelOptions | T) {
|
|
157
|
+
if (!this._targetGrid) {
|
|
158
|
+
throw new Error('[block-reference] Target grid is not resolved yet.');
|
|
159
|
+
}
|
|
160
|
+
return this._targetGrid.addSubModel(subKey, options);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
setSubModel(subKey: string, options: CreateModelOptions | FlowModel) {
|
|
164
|
+
if (!this._targetGrid) {
|
|
165
|
+
throw new Error('[block-reference] Target grid is not resolved yet.');
|
|
166
|
+
}
|
|
167
|
+
return this._targetGrid.setSubModel(subKey, options);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
public async onDispatchEventStart(eventName: string): Promise<void> {
|
|
171
|
+
if (eventName !== 'beforeRender') return;
|
|
172
|
+
|
|
173
|
+
const settings = this._getTargetSettings();
|
|
174
|
+
if (!settings) {
|
|
175
|
+
this._syncHostExtraTitle(undefined);
|
|
176
|
+
this._targetGrid = undefined;
|
|
177
|
+
this._resolvedTargetUid = undefined;
|
|
178
|
+
this._invalidTargetUid = undefined;
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
this._syncHostExtraTitle(settings);
|
|
183
|
+
|
|
184
|
+
const targetPath = settings.targetPath?.trim() || 'subModels.grid';
|
|
185
|
+
if (targetPath !== 'subModels.grid') {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`[block-reference] Only 'subModels.grid' is supported for ReferenceFormGridModel (got '${targetPath}').`,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (this._resolvedTargetUid === settings.targetUid && this._targetGrid) {
|
|
192
|
+
// 已成功解析,清理 invalid 标记
|
|
193
|
+
this._invalidTargetUid = undefined;
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const engine = this._ensureScopedEngine();
|
|
198
|
+
const targetUid = settings.targetUid;
|
|
199
|
+
const prevTargetGrid = this._targetGrid;
|
|
200
|
+
const prevResolvedTargetUid = this._resolvedTargetUid;
|
|
201
|
+
const prevInvalidTargetUid = this._invalidTargetUid;
|
|
202
|
+
// 进入解析流程时,先认为是“resolving”,避免渲染层误判为 invalid
|
|
203
|
+
this._invalidTargetUid = undefined;
|
|
204
|
+
|
|
205
|
+
// 在“模板引用”切换的中间态(例如模型树刚替换、上下文尚未稳定)下,
|
|
206
|
+
// 可能出现首次解析不到目标(短暂返回 null/undefined)。这里做一次轻量重试,
|
|
207
|
+
// 避免界面闪现 “Target block is invalid” 占位。
|
|
208
|
+
const tryResolveTargetGrid = async (): Promise<FlowModel | undefined> => {
|
|
209
|
+
const root = await engine.loadModel<FlowModel>({ uid: targetUid });
|
|
210
|
+
if (!root) return undefined;
|
|
211
|
+
|
|
212
|
+
const host = this.parent as FlowModel | undefined;
|
|
213
|
+
const hostInfo: ReferenceHostInfo = {
|
|
214
|
+
hostUid: host?.uid,
|
|
215
|
+
hostUse: host?.use,
|
|
216
|
+
ref: {
|
|
217
|
+
templateUid: settings.templateUid,
|
|
218
|
+
templateName: settings.templateName,
|
|
219
|
+
targetUid,
|
|
220
|
+
targetPath,
|
|
221
|
+
mountSubKey: 'grid',
|
|
222
|
+
mode: 'reference',
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
root.context.defineProperty(REF_HOST_CTX_KEY, { value: hostInfo });
|
|
226
|
+
|
|
227
|
+
const fragment = _.get(root as any, targetPath);
|
|
228
|
+
const gridModel =
|
|
229
|
+
fragment instanceof FlowModel ? fragment : _.castArray(fragment).find((m) => m instanceof FlowModel);
|
|
230
|
+
return gridModel || undefined;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
let gridModel = await tryResolveTargetGrid();
|
|
234
|
+
if (!gridModel) {
|
|
235
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
236
|
+
const latest = this._getTargetSettings();
|
|
237
|
+
if (latest?.targetUid !== targetUid) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
gridModel = await tryResolveTargetGrid();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (!gridModel) {
|
|
244
|
+
this._targetGrid = undefined;
|
|
245
|
+
this._resolvedTargetUid = undefined;
|
|
246
|
+
this._invalidTargetUid = targetUid;
|
|
247
|
+
if (prevTargetGrid || prevResolvedTargetUid || prevInvalidTargetUid !== targetUid) {
|
|
248
|
+
this.rerender();
|
|
249
|
+
}
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
this._targetGrid = gridModel;
|
|
254
|
+
this._resolvedTargetUid = targetUid;
|
|
255
|
+
this._invalidTargetUid = undefined;
|
|
256
|
+
if (prevTargetGrid !== gridModel || prevResolvedTargetUid !== targetUid || prevInvalidTargetUid) {
|
|
257
|
+
this.rerender();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
clearForks() {
|
|
262
|
+
try {
|
|
263
|
+
this._syncHostExtraTitle(undefined);
|
|
264
|
+
unlinkScopedEngine(this._scopedEngine);
|
|
265
|
+
} finally {
|
|
266
|
+
this._scopedEngine = undefined;
|
|
267
|
+
}
|
|
268
|
+
super.clearForks();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async destroy(): Promise<boolean> {
|
|
272
|
+
try {
|
|
273
|
+
unlinkScopedEngine(this._scopedEngine);
|
|
274
|
+
} finally {
|
|
275
|
+
this._scopedEngine = undefined;
|
|
276
|
+
}
|
|
277
|
+
return await super.destroy();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
render() {
|
|
281
|
+
const settings = this._getTargetSettings();
|
|
282
|
+
if (!settings) {
|
|
283
|
+
return renderReferenceTargetPlaceholder(this as any, 'unconfigured');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const target = this._targetGrid;
|
|
287
|
+
if (!target) {
|
|
288
|
+
if (this._invalidTargetUid === settings.targetUid) {
|
|
289
|
+
return renderReferenceTargetPlaceholder(this as any, 'invalid');
|
|
290
|
+
}
|
|
291
|
+
// 目标尚未解析完成:展示 resolving,占位避免闪现 invalid
|
|
292
|
+
return renderReferenceTargetPlaceholder(this as any, 'resolving');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const engine = this._ensureScopedEngine();
|
|
296
|
+
return <ReferenceScopedRenderer engine={engine} model={target} />;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
ReferenceFormGridModel.define({
|
|
301
|
+
hide: true,
|
|
302
|
+
});
|