@nocobase/flow-engine 2.1.0-beta.42 → 2.1.0-beta.43

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 (35) hide show
  1. package/lib/components/subModel/LazyDropdown.js +17 -9
  2. package/lib/flowContext.d.ts +6 -1
  3. package/lib/flowContext.js +35 -6
  4. package/lib/flowEngine.d.ts +1 -0
  5. package/lib/flowEngine.js +53 -30
  6. package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +4 -3
  7. package/lib/runjs-context/contexts/JSBlockRunJSContext.js +4 -15
  8. package/lib/runjs-context/contexts/JSColumnRunJSContext.js +5 -2
  9. package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.js +5 -8
  10. package/lib/runjs-context/contexts/JSFieldRunJSContext.js +4 -3
  11. package/lib/runjs-context/contexts/JSItemRunJSContext.js +4 -3
  12. package/lib/runjs-context/contexts/base.js +464 -29
  13. package/lib/runjs-context/contexts/elementDoc.d.ts +11 -0
  14. package/lib/runjs-context/contexts/elementDoc.js +152 -0
  15. package/lib/utils/loadedPageCache.d.ts +24 -0
  16. package/lib/utils/loadedPageCache.js +139 -0
  17. package/package.json +4 -4
  18. package/src/__tests__/flowContext.test.ts +23 -0
  19. package/src/__tests__/runjsContext.test.ts +18 -0
  20. package/src/__tests__/runjsContextImplementations.test.ts +9 -2
  21. package/src/__tests__/runjsLocales.test.ts +6 -5
  22. package/src/__tests__/viewScopedFlowEngine.test.ts +133 -0
  23. package/src/components/subModel/LazyDropdown.tsx +16 -7
  24. package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +51 -0
  25. package/src/flowContext.ts +40 -6
  26. package/src/flowEngine.ts +51 -27
  27. package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +4 -3
  28. package/src/runjs-context/contexts/JSBlockRunJSContext.ts +4 -15
  29. package/src/runjs-context/contexts/JSColumnRunJSContext.ts +4 -2
  30. package/src/runjs-context/contexts/JSEditableFieldRunJSContext.ts +5 -9
  31. package/src/runjs-context/contexts/JSFieldRunJSContext.ts +4 -3
  32. package/src/runjs-context/contexts/JSItemRunJSContext.ts +4 -3
  33. package/src/runjs-context/contexts/base.ts +467 -31
  34. package/src/runjs-context/contexts/elementDoc.ts +130 -0
  35. package/src/utils/loadedPageCache.ts +147 -0
@@ -556,6 +556,57 @@ describe('transformItems - searchable flags', () => {
556
556
  await waitFor(() => expect(screen.getByText('Field 1')).toBeInTheDocument());
557
557
  expect(screen.getByRole('textbox')).toHaveValue('');
558
558
  });
559
+
560
+ it('keeps root group search value when hovering a sibling submenu', async () => {
561
+ const engine = new FlowEngine();
562
+ await engine.flowSettings.forceEnable();
563
+ class Parent extends FlowModel {}
564
+ engine.registerModels({ Parent });
565
+ const parent = engine.createModel<FlowModel>({ use: 'Parent' });
566
+
567
+ const items = [
568
+ {
569
+ key: 'fields',
570
+ label: '',
571
+ type: 'group' as const,
572
+ searchable: true,
573
+ searchPlaceholder: 'Search fields',
574
+ children: [
575
+ { key: 'nickname', label: 'Nickname', createModelOptions: { use: 'Parent' } },
576
+ { key: 'email', label: 'Email', createModelOptions: { use: 'Parent' } },
577
+ ],
578
+ },
579
+ {
580
+ key: 'association-fields',
581
+ label: 'Display association fields',
582
+ children: [{ key: 'author', label: 'Author', createModelOptions: { use: 'Parent' } }],
583
+ },
584
+ ];
585
+
586
+ const user = userEvent.setup();
587
+ render(
588
+ <FlowEngineProvider engine={engine}>
589
+ <ConfigProvider>
590
+ <App>
591
+ <AddSubModelButton model={parent} subModelKey="items" items={items as any}>
592
+ Open
593
+ </AddSubModelButton>
594
+ </App>
595
+ </ConfigProvider>
596
+ </FlowEngineProvider>,
597
+ );
598
+
599
+ await user.click(screen.getByText('Open'));
600
+ const searchInput = await screen.findByPlaceholderText('Search fields');
601
+ await user.type(searchInput, 'nick');
602
+ await waitFor(() => expect(screen.queryByText('Email')).not.toBeInTheDocument());
603
+
604
+ await user.hover(screen.getByText('Display association fields'));
605
+
606
+ await waitFor(() => expect(screen.getByText('Author')).toBeInTheDocument());
607
+ expect(searchInput).toHaveValue('nick');
608
+ expect(screen.getByText('Nickname')).toBeInTheDocument();
609
+ });
559
610
  });
560
611
 
561
612
  describe('transformItems - hide', () => {
@@ -400,6 +400,10 @@ export type FlowContextGetApiInfosOptions = {
400
400
  * RunJS 文档版本(默认 v1)。
401
401
  */
402
402
  version?: RunJSVersion;
403
+ /**
404
+ * Include editor completion metadata. Defaults to false so API-doc callers keep the compact public shape.
405
+ */
406
+ includeCompletion?: boolean;
403
407
  };
404
408
 
405
409
  export type FlowContextGetVarInfosOptions = {
@@ -704,10 +708,11 @@ export class FlowContext {
704
708
  * - 输出仅来自 RunJS doc 与 defineProperty/defineMethod 的 info
705
709
  * - 不读取/展开 PropertyMeta(变量结构)
706
710
  * - 不自动展开深层 properties
707
- * - 不返回自动补全字段(例如 completion
711
+ * - 默认不返回自动补全字段(例如 completion),传入 includeCompletion=true 时返回
708
712
  */
709
713
  async getApiInfos(options: FlowContextGetApiInfosOptions = {}): Promise<Record<string, FlowContextApiInfo>> {
710
714
  const version = (options.version as RunJSVersion) || ('v1' as RunJSVersion);
715
+ const includeCompletion = !!options.includeCompletion;
711
716
  const evalCtx = this.createProxy();
712
717
 
713
718
  const isPrivateKey = (key: string) => typeof key === 'string' && key.startsWith('_');
@@ -759,7 +764,14 @@ export class FlowContext {
759
764
  const src = toDocObject(obj);
760
765
  if (!src) return {};
761
766
  const out: any = {};
762
- for (const k of ['description', 'examples', 'ref', 'params', 'returns']) {
767
+ for (const k of [
768
+ 'description',
769
+ 'examples',
770
+ ...(includeCompletion ? ['completion'] : []),
771
+ 'ref',
772
+ 'params',
773
+ 'returns',
774
+ ]) {
763
775
  const v = (src as any)[k];
764
776
  if (typeof v !== 'undefined') out[k] = v;
765
777
  }
@@ -773,7 +785,17 @@ export class FlowContext {
773
785
  const src = toDocObject(obj);
774
786
  if (!src) return {};
775
787
  const out: any = {};
776
- for (const k of ['title', 'type', 'interface', 'description', 'examples', 'ref', 'params', 'returns']) {
788
+ for (const k of [
789
+ 'title',
790
+ 'type',
791
+ 'interface',
792
+ 'description',
793
+ 'examples',
794
+ ...(includeCompletion ? ['completion'] : []),
795
+ 'ref',
796
+ 'params',
797
+ 'returns',
798
+ ]) {
777
799
  const v = (src as any)[k];
778
800
  if (typeof v !== 'undefined') out[k] = v;
779
801
  }
@@ -872,7 +894,7 @@ export class FlowContext {
872
894
  node = { ...node, ...pickPropertyInfo(docObj) };
873
895
  node = { ...node, ...pickPropertyInfo(infoObj) };
874
896
  delete (node as any).properties;
875
- delete (node as any).completion;
897
+ if (!includeCompletion) delete (node as any).completion;
876
898
  if (!Object.keys(node).length) continue;
877
899
  const outKey = mapDocKeyToApiKey(key, docNode);
878
900
  // Avoid exposing ctx.React/ctx.ReactDOM/ctx.antd in api docs when mapping to ctx.libs.*.
@@ -890,7 +912,7 @@ export class FlowContext {
890
912
  node = { ...node, ...pickMethodInfo(docObj) };
891
913
  node = { ...node, ...pickMethodInfo(info) };
892
914
  delete (node as any).properties;
893
- delete (node as any).completion;
915
+ if (!includeCompletion) delete (node as any).completion;
894
916
  if (!Object.keys(node).length) continue;
895
917
  node.type = 'function';
896
918
 
@@ -913,7 +935,7 @@ export class FlowContext {
913
935
  let node: FlowContextApiInfo = {};
914
936
  node = { ...node, ...pickPropertyInfo(childObj) };
915
937
  delete (node as any).properties;
916
- delete (node as any).completion;
938
+ if (!includeCompletion) delete (node as any).completion;
917
939
  if (!node.description || !String(node.description).trim()) continue;
918
940
  out[outKey] = node;
919
941
  }
@@ -3075,6 +3097,17 @@ class BaseFlowEngineContext extends FlowContext {
3075
3097
  const jsCode = await prepareRunJsCode(String(code ?? ''), { preprocessTemplates: shouldPreprocessTemplates });
3076
3098
  return runner.run(jsCode);
3077
3099
  },
3100
+ {
3101
+ description: 'Execute a RunJS code string in the current Flow context.',
3102
+ detail: '(code: string, variables?: Record<string, any>, options?: JSRunnerOptions) => Promise<RunJSResult>',
3103
+ params: [
3104
+ { name: 'code', type: 'string', description: 'RunJS code to execute.' },
3105
+ { name: 'variables', type: 'Record<string, any>', optional: true, description: 'Additional globals.' },
3106
+ { name: 'options', type: 'JSRunnerOptions', optional: true, description: 'Runner options.' },
3107
+ ],
3108
+ returns: { type: 'Promise<{ success: boolean; value?: any; error?: any; timeout?: boolean }>' },
3109
+ completion: { insertText: `await ctx.runjs('return 1')` },
3110
+ },
3078
3111
  );
3079
3112
  }
3080
3113
  }
@@ -3927,6 +3960,7 @@ export type FlowSettingsContext<TModel extends FlowModel = FlowModel> = FlowRunt
3927
3960
 
3928
3961
  export type RunJSDocCompletionDoc = {
3929
3962
  insertText?: string;
3963
+ requires?: Array<'element'>;
3930
3964
  };
3931
3965
 
3932
3966
  export type RunJSDocHiddenDoc = boolean | ((ctx: any) => boolean | Promise<boolean>);
package/src/flowEngine.ts CHANGED
@@ -20,6 +20,7 @@ import { APIResource, FlowResource, MultiRecordResource, SingleRecordResource, S
20
20
  import { Emitter } from './emitter';
21
21
  import ModelOperationScheduler from './scheduler/ModelOperationScheduler';
22
22
  import type { ScheduleOptions, ScheduledCancel } from './scheduler/ModelOperationScheduler';
23
+ import { createLoadedPageCache } from './utils/loadedPageCache';
23
24
  import type {
24
25
  ActionDefinition,
25
26
  ApplyFlowCacheEntry,
@@ -135,6 +136,8 @@ export class FlowEngine {
135
136
  */
136
137
  private _savingModels = new Map<string, Promise<any>>();
137
138
 
139
+ private _loadedPageCache = createLoadedPageCache();
140
+
138
141
  /**
139
142
  * Flow engine context object.
140
143
  * @private
@@ -1339,7 +1342,8 @@ export class FlowEngine {
1339
1342
  async loadModel<T extends FlowModel = FlowModel>(options): Promise<T | null> {
1340
1343
  if (!this.ensureModelRepository()) return;
1341
1344
  const refresh = !!options?.refresh;
1342
- if (!refresh) {
1345
+ const bypassLoadedPageCache = this._loadedPageCache.shouldBypass(options, () => this.context.flowSettingsEnabled);
1346
+ if (!refresh && !bypassLoadedPageCache) {
1343
1347
  const model = this.findModelByParentId(options.parentId, options.subKey);
1344
1348
  if (model) {
1345
1349
  return model as T;
@@ -1350,15 +1354,24 @@ export class FlowEngine {
1350
1354
  }
1351
1355
  }
1352
1356
  const data = await this._modelRepository.findOne(options);
1353
- if (!data?.uid) return null;
1354
- await this.resolveModelTree(data);
1355
- if (refresh) {
1357
+ if (!data?.uid) {
1358
+ if (bypassLoadedPageCache) {
1359
+ this._loadedPageCache.clear(options);
1360
+ }
1361
+ return null;
1362
+ }
1363
+ if (refresh || bypassLoadedPageCache) {
1356
1364
  const existing = this.getModel(data.uid);
1357
1365
  if (existing) {
1358
1366
  this.removeModelWithSubModels(existing.uid);
1359
1367
  }
1360
1368
  }
1361
- return this.createModelAsync<T>(data as any);
1369
+ const model = await this.createModelAsync<T>(data as any);
1370
+ if (bypassLoadedPageCache) {
1371
+ this._loadedPageCache.mountModelToParent(model, true);
1372
+ this._loadedPageCache.clear(options);
1373
+ }
1374
+ return model;
1362
1375
  }
1363
1376
 
1364
1377
  /**
@@ -1398,22 +1411,31 @@ export class FlowEngine {
1398
1411
  ): Promise<T | null> {
1399
1412
  if (!this.ensureModelRepository()) return;
1400
1413
  const { uid, parentId, subKey } = options;
1401
- if (uid && this._modelInstances.has(uid)) {
1414
+ const bypassLoadedPageCache = this._loadedPageCache.shouldBypass(options, () => this.context.flowSettingsEnabled);
1415
+ if (uid && !bypassLoadedPageCache && this._modelInstances.has(uid)) {
1402
1416
  return this._modelInstances.get(uid) as T;
1403
1417
  }
1404
- const m = this.findModelByParentId<T>(parentId, subKey);
1405
- if (m) {
1406
- return m;
1407
- }
1418
+ if (!bypassLoadedPageCache) {
1419
+ const m = this.findModelByParentId<T>(parentId, subKey);
1420
+ if (m) {
1421
+ return m;
1422
+ }
1408
1423
 
1409
- const hydrated = await this.hydrateModelFromPreviousEngines<T>(options, extra);
1410
- if (hydrated) {
1411
- return hydrated;
1424
+ const hydrated = await this.hydrateModelFromPreviousEngines<T>(options, extra);
1425
+ if (hydrated) {
1426
+ return hydrated;
1427
+ }
1412
1428
  }
1413
1429
 
1414
1430
  const data = await this._modelRepository.findOne(options);
1415
1431
  let model: T | null = null;
1416
1432
  if (data?.uid) {
1433
+ if (bypassLoadedPageCache) {
1434
+ const existing = this.getModel(data.uid);
1435
+ if (existing) {
1436
+ this.removeModelWithSubModels(existing.uid);
1437
+ }
1438
+ }
1417
1439
  model = await this.createModelAsync<T>(data as any, extra);
1418
1440
  } else {
1419
1441
  model = await this.createModelAsync<T>(options, extra);
@@ -1421,18 +1443,9 @@ export class FlowEngine {
1421
1443
  await model.save();
1422
1444
  }
1423
1445
  }
1424
- if (model.parent) {
1425
- const subModel = model.parent.findSubModel(model.subKey, (m) => {
1426
- return m.uid === model.uid;
1427
- });
1428
- if (subModel) {
1429
- return model;
1430
- }
1431
- if (model.subType === 'array') {
1432
- model.parent.addSubModel(model.subKey, model);
1433
- } else {
1434
- model.parent.setSubModel(model.subKey, model);
1435
- }
1446
+ this._loadedPageCache.mountModelToParent(model, bypassLoadedPageCache);
1447
+ if (bypassLoadedPageCache) {
1448
+ this._loadedPageCache.clear(options);
1436
1449
  }
1437
1450
  return model;
1438
1451
  }
@@ -1452,6 +1465,9 @@ export class FlowEngine {
1452
1465
  if (!this.ensureModelRepository()) return;
1453
1466
 
1454
1467
  const modelUid = model.uid;
1468
+ const dirtyLoadedPageKey = this._loadedPageCache.getDirtyKeyForModel(model, {
1469
+ force: !!options?.onlyStepParams,
1470
+ });
1455
1471
 
1456
1472
  // 如果这个 model 正在保存中,返回现有的保存 Promise
1457
1473
  if (this._savingModels.has(modelUid)) {
@@ -1465,6 +1481,7 @@ export class FlowEngine {
1465
1481
 
1466
1482
  try {
1467
1483
  const result = await savePromise;
1484
+ this._loadedPageCache.markDirty(dirtyLoadedPageKey);
1468
1485
  return result;
1469
1486
  } finally {
1470
1487
  // 无论成功还是失败,都要清除保存状态
@@ -1501,11 +1518,16 @@ export class FlowEngine {
1501
1518
  * @returns {Promise<boolean>} Whether destroyed successfully
1502
1519
  */
1503
1520
  async destroyModel(uid: string) {
1504
- if (this.ensureModelRepository()) {
1521
+ const modelInstance = this._modelInstances.get(uid) as FlowModel;
1522
+ const dirtyLoadedPageKey = this._loadedPageCache.getDirtyKeyForModel(modelInstance);
1523
+ const hasModelRepository = this.ensureModelRepository();
1524
+ if (hasModelRepository) {
1505
1525
  await this._modelRepository.destroy(uid);
1506
1526
  }
1507
1527
 
1508
- const modelInstance = this._modelInstances.get(uid) as FlowModel;
1528
+ if (hasModelRepository) {
1529
+ this._loadedPageCache.markDirty(dirtyLoadedPageKey);
1530
+ }
1509
1531
  const parent = modelInstance?.parent;
1510
1532
  const result = this.removeModel(uid);
1511
1533
  parent && parent.emitter.emit('onSubModelDestroyed', modelInstance);
@@ -1620,6 +1642,7 @@ export class FlowEngine {
1620
1642
  console.warn(`FlowEngine: Cannot move model. Source or target model not found.`);
1621
1643
  return;
1622
1644
  }
1645
+ const dirtyLoadedPageKey = this._loadedPageCache.getDirtyKeyForModel(sourceModel);
1623
1646
  const move = (sourceModel: FlowModel, targetModel: FlowModel) => {
1624
1647
  if (!sourceModel.parent || !targetModel.parent || sourceModel.parent !== targetModel.parent) {
1625
1648
  console.error('FlowModel.moveTo: Both models must have the same parent to perform move operation.');
@@ -1667,6 +1690,7 @@ export class FlowEngine {
1667
1690
  if (options?.persist !== false && this.ensureModelRepository()) {
1668
1691
  const position = sourceModel.sortIndex - targetModel.sortIndex > 0 ? 'after' : 'before';
1669
1692
  await this._modelRepository.move(sourceId, targetId, position);
1693
+ this._loadedPageCache.markDirty(dirtyLoadedPageKey);
1670
1694
  }
1671
1695
  // 触发事件以通知其他部分模型已移动
1672
1696
  sourceModel.parent.emitter.emit('onSubModelMoved', { source: sourceModel, target: targetModel });
@@ -8,14 +8,15 @@
8
8
  */
9
9
 
10
10
  import { FlowRunJSContext } from '../../flowContext';
11
+ import { createElementPropertyDoc, createZhCNElementPropertyDoc } from './elementDoc';
11
12
 
12
13
  export class FormJSFieldItemRunJSContext extends FlowRunJSContext {}
13
14
 
14
15
  FormJSFieldItemRunJSContext.define({
15
16
  label: 'FormJSFieldItem RunJS context',
16
17
  properties: {
17
- element: `ElementProxy instance providing a safe DOM container for form field rendering.
18
- Supports innerHTML, append, and other DOM manipulation methods.`,
18
+ element: createElementPropertyDoc(`ElementProxy instance providing a safe DOM container for form field rendering.
19
+ Supports innerHTML, append, and other DOM manipulation methods.`),
19
20
  value: `Current field value (read-only in display mode; in controlled scenarios, use setProps to modify).`,
20
21
  record: `Current record data object (read-only).
21
22
  Contains all field values of the parent record.`,
@@ -38,7 +39,7 @@ FormJSFieldItemRunJSContext.define(
38
39
  {
39
40
  label: '表单 JS 字段项 RunJS 上下文',
40
41
  properties: {
41
- element: 'ElementProxy,表单字段容器',
42
+ element: createZhCNElementPropertyDoc('ElementProxy,表单字段容器'),
42
43
  value: '字段值(展示模式为只读;受控场景用 setProps 修改)',
43
44
  record: '当前记录(只读)',
44
45
  formValues: {
@@ -8,21 +8,16 @@
8
8
  */
9
9
 
10
10
  import { FlowRunJSContext } from '../../flowContext';
11
+ import { createElementPropertyDoc, createZhCNElementPropertyDoc } from './elementDoc';
11
12
 
12
13
  export class JSBlockRunJSContext extends FlowRunJSContext {}
13
14
 
14
15
  JSBlockRunJSContext.define({
15
16
  label: 'RunJS context',
16
17
  properties: {
17
- element: {
18
- description: `ElementProxy instance providing a safe DOM container.
18
+ element: createElementPropertyDoc(`ElementProxy instance providing a safe DOM container.
19
19
  Supports innerHTML, append, and other DOM manipulation methods.
20
- Use this to render content in the JS block.`,
21
- detail: 'ElementProxy',
22
- properties: {
23
- innerHTML: 'Set or read the HTML content of the container element.',
24
- },
25
- },
20
+ Use this to render content in the JS block.`),
26
21
  record: `Current record data object (read-only).
27
22
  Available when the JS block is within a data block or detail view context.`,
28
23
  value: 'Current value of the field or component, if available in the current context.',
@@ -44,13 +39,7 @@ JSBlockRunJSContext.define(
44
39
  {
45
40
  label: 'RunJS 上下文',
46
41
  properties: {
47
- element: {
48
- description: 'ElementProxy,安全的 DOM 容器,支持 innerHTML/append 等',
49
- detail: 'ElementProxy',
50
- properties: {
51
- innerHTML: '读取或设置容器的 HTML 内容',
52
- },
53
- },
42
+ element: createZhCNElementPropertyDoc('ElementProxy,安全的 DOM 容器,支持 innerHTML/append 等'),
54
43
  record: '当前记录(只读,用于数据区块/详情等场景)',
55
44
  value: '当前值(若存在)',
56
45
  React: 'React 库',
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import { FlowRunJSContext } from '../../flowContext';
11
+ import { createElementPropertyDoc, createZhCNElementPropertyDoc } from './elementDoc';
11
12
 
12
13
  /**
13
14
  * RunJS context for JSColumnModel (table custom column).
@@ -18,8 +19,9 @@ export class JSColumnRunJSContext extends FlowRunJSContext {}
18
19
  JSColumnRunJSContext.define({
19
20
  label: 'JSColumn RunJS context',
20
21
  properties: {
21
- element:
22
+ element: createElementPropertyDoc(
22
23
  'ElementProxy instance providing a safe DOM container for the current table cell. Supports innerHTML/append and basic DOM APIs.',
24
+ ),
23
25
  record: 'Current row record object (read-only).',
24
26
  recordIndex: 'Index of the current row in the page (0-based).',
25
27
  collection: 'Collection definition metadata (read-only).',
@@ -40,7 +42,7 @@ JSColumnRunJSContext.define(
40
42
  {
41
43
  label: 'JS 列 RunJS 上下文',
42
44
  properties: {
43
- element: 'ElementProxy,表格单元格的安全 DOM 容器,支持 innerHTML/append 等',
45
+ element: createZhCNElementPropertyDoc('ElementProxy,表格单元格的安全 DOM 容器,支持 innerHTML/append 等'),
44
46
  record: '当前行记录对象(只读)',
45
47
  recordIndex: '当前行索引(从 0 开始)',
46
48
  collection: '集合定义元数据(只读)',
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import { FlowRunJSContext } from '../../flowContext';
11
+ import { createElementPropertyDoc, createZhCNElementPropertyDoc } from './elementDoc';
11
12
 
12
13
  /**
13
14
  * RunJS context for JSEditableFieldModel (form editable custom field).
@@ -19,11 +20,9 @@ export class JSEditableFieldRunJSContext extends FlowRunJSContext {}
19
20
  JSEditableFieldRunJSContext.define({
20
21
  label: 'JSEditableField RunJS context',
21
22
  properties: {
22
- element: {
23
- description:
24
- 'ElementProxy instance providing a safe DOM container for field rendering. In editable mode this container is typically a <span> element.',
25
- detail: 'ElementProxy',
26
- },
23
+ element: createElementPropertyDoc(
24
+ 'ElementProxy instance providing a safe DOM container for field rendering. In editable mode this container is typically a <span> element.',
25
+ ),
27
26
  value: {
28
27
  description:
29
28
  'Current field value (read-only snapshot). In editable scenarios, prefer ctx.getValue()/ctx.setValue(v) for two-way binding.',
@@ -69,10 +68,7 @@ JSEditableFieldRunJSContext.define(
69
68
  {
70
69
  label: 'JS 可编辑字段 RunJS 上下文',
71
70
  properties: {
72
- element: {
73
- description: 'ElementProxy,字段渲染的安全容器(通常为 <span> 容器)。',
74
- detail: 'ElementProxy',
75
- },
71
+ element: createZhCNElementPropertyDoc('ElementProxy,字段渲染的安全容器(通常为 <span> 容器)。'),
76
72
  value: {
77
73
  description: '字段当前值(只读快照)。可编辑场景建议使用 ctx.getValue()/ctx.setValue(v) 做双向绑定。',
78
74
  detail: 'any',
@@ -8,14 +8,15 @@
8
8
  */
9
9
 
10
10
  import { FlowRunJSContext } from '../../flowContext';
11
+ import { createElementPropertyDoc, createZhCNElementPropertyDoc } from './elementDoc';
11
12
 
12
13
  export class JSFieldRunJSContext extends FlowRunJSContext {}
13
14
 
14
15
  JSFieldRunJSContext.define({
15
16
  label: 'JSField RunJS context',
16
17
  properties: {
17
- element: `ElementProxy instance providing a safe DOM container for field rendering.
18
- Supports innerHTML, append, and other DOM manipulation methods.`,
18
+ element: createElementPropertyDoc(`ElementProxy instance providing a safe DOM container for field rendering.
19
+ Supports innerHTML, append, and other DOM manipulation methods.`),
19
20
  value: `Current value of the field (read-only).
20
21
  Contains the data value stored in this field.`,
21
22
  record: `Current record data object (read-only).
@@ -34,7 +35,7 @@ JSFieldRunJSContext.define(
34
35
  {
35
36
  label: 'JS 字段 RunJS 上下文',
36
37
  properties: {
37
- element: 'ElementProxy,字段渲染容器,支持 innerHTML/append 等 DOM 操作',
38
+ element: createZhCNElementPropertyDoc('ElementProxy,字段渲染容器,支持 innerHTML/append 等 DOM 操作'),
38
39
  value: '字段当前值(只读)',
39
40
  record: '当前记录对象(只读,包含父记录全部字段值)',
40
41
  collection: '集合定义元数据(只读,描述字段所属集合的 Schema)',
@@ -8,14 +8,15 @@
8
8
  */
9
9
 
10
10
  import { FlowRunJSContext } from '../../flowContext';
11
+ import { createElementPropertyDoc, createZhCNElementPropertyDoc } from './elementDoc';
11
12
 
12
13
  export class JSItemRunJSContext extends FlowRunJSContext {}
13
14
 
14
15
  JSItemRunJSContext.define({
15
16
  label: 'JSItem RunJS context',
16
17
  properties: {
17
- element: `ElementProxy instance providing a safe DOM container for form item rendering.
18
- Supports innerHTML, append, and other DOM manipulation methods.`,
18
+ element: createElementPropertyDoc(`ElementProxy instance providing a safe DOM container for form item rendering.
19
+ Supports innerHTML, append, and other DOM manipulation methods.`),
19
20
  resource: `Current resource instance (read-only).
20
21
  Provides access to the data resource associated with the current form context.`,
21
22
  record: `Current record data object (read-only).
@@ -36,7 +37,7 @@ JSItemRunJSContext.define(
36
37
  {
37
38
  label: 'JS 表单项 RunJS 上下文',
38
39
  properties: {
39
- element: 'ElementProxy,表单项渲染容器,支持 innerHTML/append 等 DOM 操作',
40
+ element: createZhCNElementPropertyDoc('ElementProxy,表单项渲染容器,支持 innerHTML/append 等 DOM 操作'),
40
41
  resource: '当前资源(只读)',
41
42
  record: '当前记录(只读)',
42
43
  formValues: {