@nocobase/flow-engine 2.0.59 → 2.0.61

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 (39) hide show
  1. package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +76 -31
  2. package/lib/flowContext.d.ts +6 -1
  3. package/lib/flowContext.js +35 -6
  4. package/lib/flowEngine.d.ts +4 -3
  5. package/lib/flowEngine.js +67 -36
  6. package/lib/models/flowModel.js +45 -13
  7. package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +4 -3
  8. package/lib/runjs-context/contexts/JSBlockRunJSContext.js +4 -15
  9. package/lib/runjs-context/contexts/JSColumnRunJSContext.js +5 -2
  10. package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.js +5 -8
  11. package/lib/runjs-context/contexts/JSFieldRunJSContext.js +4 -3
  12. package/lib/runjs-context/contexts/JSItemRunJSContext.js +4 -3
  13. package/lib/runjs-context/contexts/base.js +464 -29
  14. package/lib/runjs-context/contexts/elementDoc.d.ts +11 -0
  15. package/lib/runjs-context/contexts/elementDoc.js +152 -0
  16. package/lib/utils/loadedPageCache.d.ts +21 -0
  17. package/lib/utils/loadedPageCache.js +125 -0
  18. package/package.json +4 -4
  19. package/src/__tests__/flowContext.test.ts +23 -0
  20. package/src/__tests__/flowEngine.moveModel.test.ts +81 -1
  21. package/src/__tests__/runjsContext.test.ts +18 -0
  22. package/src/__tests__/runjsContextImplementations.test.ts +9 -2
  23. package/src/__tests__/runjsLocales.test.ts +6 -5
  24. package/src/__tests__/viewScopedFlowEngine.test.ts +133 -0
  25. package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +79 -37
  26. package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +148 -1
  27. package/src/flowContext.ts +40 -6
  28. package/src/flowEngine.ts +69 -34
  29. package/src/models/__tests__/flowModel.test.ts +13 -0
  30. package/src/models/flowModel.tsx +62 -29
  31. package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +4 -3
  32. package/src/runjs-context/contexts/JSBlockRunJSContext.ts +4 -15
  33. package/src/runjs-context/contexts/JSColumnRunJSContext.ts +4 -2
  34. package/src/runjs-context/contexts/JSEditableFieldRunJSContext.ts +5 -9
  35. package/src/runjs-context/contexts/JSFieldRunJSContext.ts +4 -3
  36. package/src/runjs-context/contexts/JSItemRunJSContext.ts +4 -3
  37. package/src/runjs-context/contexts/base.ts +467 -31
  38. package/src/runjs-context/contexts/elementDoc.ts +130 -0
  39. package/src/utils/loadedPageCache.ts +117 -0
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,
@@ -105,6 +106,8 @@ export class FlowEngine {
105
106
  */
106
107
  private _savingModels = new Map<string, Promise<any>>();
107
108
 
109
+ private _loadedPageCache = createLoadedPageCache();
110
+
108
111
  /**
109
112
  * Flow engine context object.
110
113
  * @private
@@ -938,7 +941,8 @@ export class FlowEngine {
938
941
  async loadModel<T extends FlowModel = FlowModel>(options): Promise<T | null> {
939
942
  if (!this.ensureModelRepository()) return;
940
943
  const refresh = !!options?.refresh;
941
- if (!refresh) {
944
+ const bypassLoadedPageCache = this._loadedPageCache.shouldBypass(options, () => this.context.flowSettingsEnabled);
945
+ if (!refresh && !bypassLoadedPageCache) {
942
946
  const model = this.findModelByParentId(options.parentId, options.subKey);
943
947
  if (model) {
944
948
  return model as T;
@@ -949,14 +953,24 @@ export class FlowEngine {
949
953
  }
950
954
  }
951
955
  const data = await this._modelRepository.findOne(options);
952
- if (!data?.uid) return null;
953
- if (refresh) {
956
+ if (!data?.uid) {
957
+ if (bypassLoadedPageCache) {
958
+ this._loadedPageCache.clear(options);
959
+ }
960
+ return null;
961
+ }
962
+ if (refresh || bypassLoadedPageCache) {
954
963
  const existing = this.getModel(data.uid);
955
964
  if (existing) {
956
965
  this.removeModelWithSubModels(existing.uid);
957
966
  }
958
967
  }
959
- return this.createModel<T>(data as any);
968
+ const model = this.createModel<T>(data as any);
969
+ if (bypassLoadedPageCache) {
970
+ this._loadedPageCache.mountModelToParent(model, true);
971
+ this._loadedPageCache.clear(options);
972
+ }
973
+ return model;
960
974
  }
961
975
 
962
976
  /**
@@ -996,22 +1010,31 @@ export class FlowEngine {
996
1010
  ): Promise<T | null> {
997
1011
  if (!this.ensureModelRepository()) return;
998
1012
  const { uid, parentId, subKey } = options;
999
- if (uid && this._modelInstances.has(uid)) {
1013
+ const bypassLoadedPageCache = this._loadedPageCache.shouldBypass(options, () => this.context.flowSettingsEnabled);
1014
+ if (uid && !bypassLoadedPageCache && this._modelInstances.has(uid)) {
1000
1015
  return this._modelInstances.get(uid) as T;
1001
1016
  }
1002
- const m = this.findModelByParentId<T>(parentId, subKey);
1003
- if (m) {
1004
- return m;
1005
- }
1017
+ if (!bypassLoadedPageCache) {
1018
+ const m = this.findModelByParentId<T>(parentId, subKey);
1019
+ if (m) {
1020
+ return m;
1021
+ }
1006
1022
 
1007
- const hydrated = this.hydrateModelFromPreviousEngines<T>(options, extra);
1008
- if (hydrated) {
1009
- return hydrated;
1023
+ const hydrated = this.hydrateModelFromPreviousEngines<T>(options, extra);
1024
+ if (hydrated) {
1025
+ return hydrated;
1026
+ }
1010
1027
  }
1011
1028
 
1012
1029
  const data = await this._modelRepository.findOne(options);
1013
1030
  let model: T | null = null;
1014
1031
  if (data?.uid) {
1032
+ if (bypassLoadedPageCache) {
1033
+ const existing = this.getModel(data.uid);
1034
+ if (existing) {
1035
+ this.removeModelWithSubModels(existing.uid);
1036
+ }
1037
+ }
1015
1038
  model = this.createModel<T>(data as any, extra);
1016
1039
  } else {
1017
1040
  model = this.createModel<T>(options, extra);
@@ -1019,18 +1042,9 @@ export class FlowEngine {
1019
1042
  await model.save();
1020
1043
  }
1021
1044
  }
1022
- if (model.parent) {
1023
- const subModel = model.parent.findSubModel(model.subKey, (m) => {
1024
- return m.uid === model.uid;
1025
- });
1026
- if (subModel) {
1027
- return model;
1028
- }
1029
- if (model.subType === 'array') {
1030
- model.parent.addSubModel(model.subKey, model);
1031
- } else {
1032
- model.parent.setSubModel(model.subKey, model);
1033
- }
1045
+ this._loadedPageCache.mountModelToParent(model, bypassLoadedPageCache);
1046
+ if (bypassLoadedPageCache) {
1047
+ this._loadedPageCache.clear(options);
1034
1048
  }
1035
1049
  return model;
1036
1050
  }
@@ -1050,6 +1064,7 @@ export class FlowEngine {
1050
1064
  if (!this.ensureModelRepository()) return;
1051
1065
 
1052
1066
  const modelUid = model.uid;
1067
+ const dirtyLoadedPageKey = this._loadedPageCache.getDirtyKeyForModel(model);
1053
1068
 
1054
1069
  // 如果这个 model 正在保存中,返回现有的保存 Promise
1055
1070
  if (this._savingModels.has(modelUid)) {
@@ -1063,6 +1078,7 @@ export class FlowEngine {
1063
1078
 
1064
1079
  try {
1065
1080
  const result = await savePromise;
1081
+ this._loadedPageCache.markDirty(dirtyLoadedPageKey);
1066
1082
  return result;
1067
1083
  } finally {
1068
1084
  // 无论成功还是失败,都要清除保存状态
@@ -1099,11 +1115,16 @@ export class FlowEngine {
1099
1115
  * @returns {Promise<boolean>} Whether destroyed successfully
1100
1116
  */
1101
1117
  async destroyModel(uid: string) {
1102
- if (this.ensureModelRepository()) {
1118
+ const modelInstance = this._modelInstances.get(uid) as FlowModel;
1119
+ const dirtyLoadedPageKey = this._loadedPageCache.getDirtyKeyForModel(modelInstance);
1120
+ const hasModelRepository = this.ensureModelRepository();
1121
+ if (hasModelRepository) {
1103
1122
  await this._modelRepository.destroy(uid);
1104
1123
  }
1105
1124
 
1106
- const modelInstance = this._modelInstances.get(uid) as FlowModel;
1125
+ if (hasModelRepository) {
1126
+ this._loadedPageCache.markDirty(dirtyLoadedPageKey);
1127
+ }
1107
1128
  const parent = modelInstance?.parent;
1108
1129
  const result = this.removeModel(uid);
1109
1130
  parent && parent.emitter.emit('onSubModelDestroyed', modelInstance);
@@ -1207,17 +1228,25 @@ export class FlowEngine {
1207
1228
 
1208
1229
  /**
1209
1230
  * Move a model instance within its parent model.
1210
- * @param {any} sourceId Source model UID
1211
- * @param {any} targetId Target model UID
1231
+ * @param {string | number} sourceId Source model UID
1232
+ * @param {string | number} targetId Target model UID
1212
1233
  * @returns {Promise<void>} No return value
1213
1234
  */
1214
- async moveModel(sourceId: any, targetId: any, options?: PersistOptions): Promise<void> {
1215
- const sourceModel = this.getModel(sourceId);
1216
- const targetModel = this.getModel(targetId);
1235
+ async moveModel(sourceId: string | number, targetId: string | number, options?: PersistOptions): Promise<void> {
1236
+ const sourceUid = String(sourceId);
1237
+ const targetUid = String(targetId);
1238
+ if (!sourceUid || !targetUid || sourceUid === targetUid) {
1239
+ return;
1240
+ }
1241
+
1242
+ const sourceModel = this.getModel(sourceUid);
1243
+ const targetModel = this.getModel(targetUid);
1217
1244
  if (!sourceModel || !targetModel) {
1218
1245
  console.warn(`FlowEngine: Cannot move model. Source or target model not found.`);
1219
1246
  return;
1220
1247
  }
1248
+ let position: 'before' | 'after' = 'after';
1249
+ const dirtyLoadedPageKey = this._loadedPageCache.getDirtyKeyForModel(sourceModel);
1221
1250
  const move = (sourceModel: FlowModel, targetModel: FlowModel) => {
1222
1251
  if (!sourceModel.parent || !targetModel.parent || sourceModel.parent !== targetModel.parent) {
1223
1252
  console.error('FlowModel.moveTo: Both models must have the same parent to perform move operation.');
@@ -1247,6 +1276,8 @@ export class FlowEngine {
1247
1276
  return false;
1248
1277
  }
1249
1278
 
1279
+ position = currentIndex < targetIndex ? 'after' : 'before';
1280
+
1250
1281
  // 使用splice直接移动数组元素(O(n)比排序O(n log n)更快)
1251
1282
  const [movedModel] = subModelsCopy.splice(currentIndex, 1);
1252
1283
  subModelsCopy.splice(targetIndex, 0, movedModel);
@@ -1261,10 +1292,14 @@ export class FlowEngine {
1261
1292
 
1262
1293
  return true;
1263
1294
  };
1264
- move(sourceModel, targetModel);
1295
+ const moved = move(sourceModel, targetModel);
1296
+ if (!moved) {
1297
+ return;
1298
+ }
1299
+
1265
1300
  if (options?.persist !== false && this.ensureModelRepository()) {
1266
- const position = sourceModel.sortIndex - targetModel.sortIndex > 0 ? 'after' : 'before';
1267
- await this._modelRepository.move(sourceId, targetId, position);
1301
+ await this._modelRepository.move(sourceUid, targetUid, position);
1302
+ this._loadedPageCache.markDirty(dirtyLoadedPageKey);
1268
1303
  }
1269
1304
  // 触发事件以通知其他部分模型已移动
1270
1305
  sourceModel.parent.emitter.emit('onSubModelMoved', { source: sourceModel, target: targetModel });
@@ -313,6 +313,19 @@ describe('FlowModel', () => {
313
313
 
314
314
  model.emitter.off('onStepParamsChanged', listener);
315
315
  });
316
+
317
+ test('should not emit onStepParamsChanged when params are unchanged', () => {
318
+ const listener = vi.fn();
319
+ model.emitter.on('onStepParamsChanged', listener);
320
+
321
+ model.setStepParams('testFlow', 'step1', { param1: 'value1' });
322
+ model.setStepParams('testFlow', { step1: { param1: 'value1' } });
323
+ model.setStepParams({ testFlow: { step1: { param1: 'value1' } } });
324
+
325
+ expect(listener).not.toHaveBeenCalled();
326
+
327
+ model.emitter.off('onStepParamsChanged', listener);
328
+ });
316
329
  });
317
330
  });
318
331
 
@@ -58,6 +58,25 @@ const classEventRegistries = new WeakMap<typeof FlowModel, ModelEventRegistry>()
58
58
  // 使用WeakMap存储每个类的meta
59
59
  const modelMetas = new WeakMap<typeof FlowModel, FlowModelMeta>();
60
60
 
61
+ type SortableModelLike = {
62
+ sortIndex?: number | null;
63
+ };
64
+
65
+ function getStableSortIndex(item: SortableModelLike, fallbackIndex: number) {
66
+ return typeof item?.sortIndex === 'number' && Number.isFinite(item.sortIndex) ? item.sortIndex : fallbackIndex + 1;
67
+ }
68
+
69
+ function sortByStableSortIndex<T extends SortableModelLike>(items: T[]) {
70
+ return items
71
+ .map((item, index) => ({
72
+ item,
73
+ index,
74
+ sortIndex: getStableSortIndex(item, index),
75
+ }))
76
+ .sort((a, b) => a.sortIndex - b.sortIndex || a.index - b.index)
77
+ .map(({ item }) => item);
78
+ }
79
+
61
80
  // 使用WeakMap存储每个类的 GlobalFlowRegistry
62
81
  const modelGlobalRegistries = new WeakMap<typeof FlowModel, GlobalFlowRegistry>();
63
82
 
@@ -178,7 +197,7 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
178
197
  };
179
198
  this.stepParams = options.stepParams || {};
180
199
  this.subModels = {};
181
- this.sortIndex = options.sortIndex || 0;
200
+ this.sortIndex = getStableSortIndex({ sortIndex: options.sortIndex }, -1);
182
201
  this._options = options;
183
202
  this._title = '';
184
203
  this._extraTitle = '';
@@ -447,11 +466,9 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
447
466
 
448
467
  Object.entries(mergedSubModels || {}).forEach(([key, value]) => {
449
468
  if (Array.isArray(value)) {
450
- value
451
- .sort((a, b) => (a.sortIndex || 0) - (b.sortIndex || 0))
452
- .forEach((item) => {
453
- this.addSubModel(key, item);
454
- });
469
+ sortByStableSortIndex(value).forEach((item) => {
470
+ this.addSubModel(key, item);
471
+ });
455
472
  } else {
456
473
  this.setSubModel(key, value);
457
474
  }
@@ -712,26 +729,43 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
712
729
  stepKeyOrStepsParams?: string | Record<string, ParamObject>,
713
730
  params?: ParamObject,
714
731
  ): void {
732
+ let hasChanged = false;
733
+
715
734
  if (typeof flowKeyOrAllParams === 'string') {
716
735
  const flowKey = flowKeyOrAllParams;
717
736
  if (typeof stepKeyOrStepsParams === 'string' && params !== undefined) {
718
- if (!this.stepParams[flowKey]) {
719
- this.stepParams[flowKey] = {};
737
+ const currentStepParams = this.stepParams[flowKey]?.[stepKeyOrStepsParams] || {};
738
+ const nextStepParams = { ...currentStepParams, ...params };
739
+ if (!_.isEqual(currentStepParams, nextStepParams)) {
740
+ if (!this.stepParams[flowKey]) {
741
+ this.stepParams[flowKey] = {};
742
+ }
743
+ this.stepParams[flowKey][stepKeyOrStepsParams] = nextStepParams;
744
+ hasChanged = true;
720
745
  }
721
- this.stepParams[flowKey][stepKeyOrStepsParams] = {
722
- ...this.stepParams[flowKey][stepKeyOrStepsParams],
723
- ...params,
724
- };
725
746
  } else if (typeof stepKeyOrStepsParams === 'object' && stepKeyOrStepsParams !== null) {
726
- this.stepParams[flowKey] = { ...(this.stepParams[flowKey] || {}), ...stepKeyOrStepsParams };
747
+ const currentFlowParams = this.stepParams[flowKey] || {};
748
+ const nextFlowParams = { ...currentFlowParams, ...stepKeyOrStepsParams };
749
+ if (!_.isEqual(currentFlowParams, nextFlowParams)) {
750
+ this.stepParams[flowKey] = nextFlowParams;
751
+ hasChanged = true;
752
+ }
727
753
  }
728
754
  } else if (typeof flowKeyOrAllParams === 'object' && flowKeyOrAllParams !== null) {
729
755
  for (const fk in flowKeyOrAllParams) {
730
756
  if (Object.prototype.hasOwnProperty.call(flowKeyOrAllParams, fk)) {
731
- this.stepParams[fk] = { ...(this.stepParams[fk] || {}), ...flowKeyOrAllParams[fk] };
757
+ const currentFlowParams = this.stepParams[fk] || {};
758
+ const nextFlowParams = { ...currentFlowParams, ...flowKeyOrAllParams[fk] };
759
+ if (!_.isEqual(currentFlowParams, nextFlowParams)) {
760
+ this.stepParams[fk] = nextFlowParams;
761
+ hasChanged = true;
762
+ }
732
763
  }
733
764
  }
734
765
  }
766
+ if (!hasChanged) {
767
+ return;
768
+ }
735
769
  // 发起配置修改事件
736
770
  this.emitter.emit('onStepParamsChanged');
737
771
  }
@@ -1140,7 +1174,10 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
1140
1174
  if (!Array.isArray(subModels[subKey])) {
1141
1175
  subModels[subKey] = observable.shallow([]);
1142
1176
  }
1143
- const maxSortIndex = Math.max(...(subModels[subKey] as FlowModel[]).map((item) => item.sortIndex || 0), 0);
1177
+ const maxSortIndex = Math.max(
1178
+ ...(subModels[subKey] as FlowModel[]).map((item, index) => getStableSortIndex(item, index)),
1179
+ 0,
1180
+ );
1144
1181
  model.sortIndex = maxSortIndex + 1;
1145
1182
  subModels[subKey].push(model);
1146
1183
  actualParent.emitter.emit('onSubModelAdded', model);
@@ -1198,14 +1235,12 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
1198
1235
 
1199
1236
  const results: ArrayElementType<Structure['subModels'][K]>[] = [];
1200
1237
 
1201
- _.castArray(model)
1202
- .sort((a, b) => (a.sortIndex || 0) - (b.sortIndex || 0))
1203
- .forEach((item, index) => {
1204
- const result = (callback as (model: any, index: number) => boolean)(item, index);
1205
- if (result) {
1206
- results.push(item);
1207
- }
1208
- });
1238
+ sortByStableSortIndex(_.castArray(model)).forEach((item, index) => {
1239
+ const result = (callback as (model: any, index: number) => boolean)(item, index);
1240
+ if (result) {
1241
+ results.push(item);
1242
+ }
1243
+ });
1209
1244
 
1210
1245
  return results;
1211
1246
  }
@@ -1222,12 +1257,10 @@ export class FlowModel<Structure extends DefaultStructure = DefaultStructure> {
1222
1257
 
1223
1258
  const results: R[] = [];
1224
1259
 
1225
- _.castArray(model)
1226
- .sort((a, b) => (a.sortIndex || 0) - (b.sortIndex || 0))
1227
- .forEach((item, index) => {
1228
- const result = (callback as (model: any, index: number) => R)(item, index);
1229
- results.push(result);
1230
- });
1260
+ sortByStableSortIndex(_.castArray(model)).forEach((item, index) => {
1261
+ const result = (callback as (model: any, index: number) => R)(item, index);
1262
+ results.push(result);
1263
+ });
1231
1264
 
1232
1265
  return results;
1233
1266
  }
@@ -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: {