@tmagic/editor 1.8.0-beta.5 → 1.8.0-beta.7

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 (46) hide show
  1. package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +4 -1
  2. package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +8 -2
  3. package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +25 -8
  4. package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +6 -4
  5. package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +25 -12
  6. package/dist/es/fields/StyleSetter/components/Border.vue_vue_type_script_setup_true_lang.js +27 -5
  7. package/dist/es/index.js +2 -2
  8. package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +10 -14
  9. package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +12 -4
  10. package/dist/es/layouts/history-list/composables.js +28 -77
  11. package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +2 -2
  12. package/dist/es/layouts/sidebar/data-source/DataSourceConfigPanel.vue_vue_type_script_setup_true_lang.js +16 -3
  13. package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +23 -1
  14. package/dist/es/services/codeBlock.js +30 -19
  15. package/dist/es/services/dataSource.js +29 -21
  16. package/dist/es/services/editor.js +52 -33
  17. package/dist/es/services/history.js +10 -15
  18. package/dist/es/style.css +4 -1
  19. package/dist/es/utils/data-source/index.js +2 -0
  20. package/dist/es/utils/editor.js +68 -48
  21. package/dist/es/utils/history.js +42 -33
  22. package/dist/style.css +4 -1
  23. package/dist/tmagic-editor.umd.cjs +412 -295
  24. package/package.json +7 -7
  25. package/src/fields/CodeSelectCol.vue +7 -1
  26. package/src/fields/DataSourceFieldSelect/FieldSelect.vue +16 -2
  27. package/src/fields/DataSourceFields.vue +37 -8
  28. package/src/fields/DataSourceMethodSelect.vue +9 -4
  29. package/src/fields/DataSourceMethods.vue +42 -21
  30. package/src/fields/StyleSetter/components/Border.vue +15 -6
  31. package/src/layouts/history-list/HistoryListPanel.vue +12 -25
  32. package/src/layouts/history-list/InitialRow.vue +3 -0
  33. package/src/layouts/history-list/composables.ts +34 -94
  34. package/src/layouts/sidebar/Sidebar.vue +2 -2
  35. package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +31 -2
  36. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +26 -1
  37. package/src/services/codeBlock.ts +28 -22
  38. package/src/services/dataSource.ts +29 -24
  39. package/src/services/editor.ts +41 -37
  40. package/src/services/history.ts +19 -25
  41. package/src/theme/style-setter/border.scss +4 -1
  42. package/src/type.ts +38 -26
  43. package/src/utils/data-source/index.ts +2 -0
  44. package/src/utils/editor.ts +125 -59
  45. package/src/utils/history.ts +48 -45
  46. package/types/index.d.ts +109 -112
@@ -5112,46 +5112,29 @@
5112
5112
  };
5113
5113
  /**
5114
5114
  * 把单个「按 id 分栈」的历史栈(代码块 / 数据源)拆成若干 group:
5115
- * - 把"新增/删除"独立成组(语义上属于一次性事件,不应与 update 合并);
5116
- * - 连续 'update' 合并到同一组,组内 steps 顺序就是发生顺序。
5115
+ * 每条操作记录独立成组,不做相邻 update 合并(与页面历史的合并策略不同)。
5117
5116
  *
5118
5117
  * 代码块与数据源除 `kind` 外结构完全一致,统一由本方法处理;`kind` 决定返回的具体分组类型。
5119
5118
  */
5120
5119
  var mergeStackSteps = (kind, id, list, cursor) => {
5121
- const groups = [];
5122
- let current = null;
5123
5120
  const currentIndex = cursor - 1;
5124
- list.forEach((step, index) => {
5125
- const { opType } = step;
5121
+ return list.map((step, index) => {
5126
5122
  const applied = index < cursor;
5127
5123
  const isCurrent = index === currentIndex;
5128
- if (opType === "update" && current?.opType === "update") {
5129
- current.steps.push({
5124
+ return {
5125
+ kind,
5126
+ id,
5127
+ opType: step.opType,
5128
+ steps: [{
5130
5129
  step,
5131
5130
  index,
5132
5131
  applied,
5133
5132
  isCurrent
5134
- });
5135
- current.applied = applied;
5136
- if (isCurrent) current.isCurrent = true;
5137
- } else {
5138
- current = {
5139
- kind,
5140
- id,
5141
- opType,
5142
- steps: [{
5143
- step,
5144
- index,
5145
- applied,
5146
- isCurrent
5147
- }],
5148
- applied,
5149
- isCurrent
5150
- };
5151
- groups.push(current);
5152
- }
5133
+ }],
5134
+ applied,
5135
+ isCurrent
5136
+ };
5153
5137
  });
5154
- return groups;
5155
5138
  };
5156
5139
  /**
5157
5140
  * 把页面栈拆成若干 group:
@@ -5232,22 +5215,45 @@
5232
5215
  return items?.length ? `${items.length} 个节点` : void 0;
5233
5216
  }
5234
5217
  };
5235
- /** 把 `Record<Id, UndoRedo>` 整体序列化为 `Record<Id, SerializedUndoRedo>`。 */
5218
+ /**
5219
+ * 把 `Record<Id, UndoRedo>` 整体序列化为 `Record<Id, SerializedUndoRedo>`。
5220
+ *
5221
+ * 序列化(深克隆)的同一趟里,只把每条 step 中可能含函数的 `diff` 用 serialize-javascript 序列化成字符串,
5222
+ * 其余字段(uuid / opType / timestamp / `modifiedNodeIds` Map 等)原样保留,交给 IndexedDB 结构化克隆。
5223
+ * 这样既能写入函数,又避免序列化整份快照的开销;读取时再由 {@link parseStacksStepDiff} 还原 diff。
5224
+ * 不含 `diff` 的元素(如通用栈)原样透传。
5225
+ */
5236
5226
  var serializeStacks = (stacks) => {
5237
5227
  const result = {};
5238
5228
  Object.entries(stacks).forEach(([id, undoRedo]) => {
5239
- if (undoRedo) result[id] = undoRedo.serialize();
5229
+ if (!undoRedo) return;
5230
+ const serialized = undoRedo.serialize();
5231
+ result[id] = {
5232
+ ...serialized,
5233
+ elementList: serialized.elementList.map((step) => step.diff === void 0 ? step : Object.assign({}, step, { diff: (0, serialize_javascript.default)(step.diff) }))
5234
+ };
5240
5235
  });
5241
5236
  return result;
5242
5237
  };
5243
5238
  /**
5244
5239
  * 把 `Record<Id, SerializedUndoRedo>` 整体还原为 `Record<Id, UndoRedo>`。
5245
5240
  * 还原时把每个栈的游标定位到最近一条已保存(`saved === true`)记录之后。
5241
+ *
5242
+ * 与 {@link serializeStacks} 相反:当传入 `parse`(parseDSL)时,把每条 step 中以字符串形式存储的 `diff`
5243
+ * 解析回真实对象(含函数);不含 `diff` 的元素(如通用栈)原样透传。
5246
5244
  */
5247
- var deserializeStacks = (stacks = {}) => {
5245
+ var deserializeStacks = (stacks = {}, parse) => {
5248
5246
  const result = {};
5249
5247
  Object.entries(stacks).forEach(([id, serialized]) => {
5250
- if (serialized) result[id] = UndoRedo.fromSerialized(serialized, { isSavedStep: (element) => element.saved === true });
5248
+ if (!serialized) return;
5249
+ const elementList = parse ? serialized.elementList.map((step) => {
5250
+ const { diff } = step;
5251
+ return typeof diff === "string" ? Object.assign({}, step, { diff: parse(`(${diff})`) }) : step;
5252
+ }) : serialized.elementList;
5253
+ result[id] = UndoRedo.fromSerialized({
5254
+ ...serialized,
5255
+ elementList
5256
+ }, { isSavedStep: (element) => element.saved === true });
5251
5257
  });
5252
5258
  return result;
5253
5259
  };
@@ -5265,6 +5271,8 @@
5265
5271
  var undoFloor = (undoRedo) => {
5266
5272
  return undoRedo.getElementList()[0]?.opType === "initial" ? 1 : 0;
5267
5273
  };
5274
+ /** 将单次 push 产生的 history uuid(或 null)转为 *AndGetHistoryId 返回用的 uuid 列表。 */
5275
+ var getLastPushedHistoryIds = (historyId) => historyId ? [historyId] : [];
5268
5276
  //#endregion
5269
5277
  //#region packages/editor/src/utils/indexed-db.ts
5270
5278
  /**
@@ -5356,7 +5364,7 @@
5356
5364
  var DEFAULT_DB_NAME = "tmagic-editor";
5357
5365
  var DEFAULT_STORE_NAME = "history";
5358
5366
  var DEFAULT_KEY = "default";
5359
- var PERSIST_VERSION = 1;
5367
+ var PERSIST_VERSION = 2;
5360
5368
  var History = class extends BaseService {
5361
5369
  state = (0, vue.reactive)({
5362
5370
  pageSteps: {},
@@ -5679,7 +5687,7 @@
5679
5687
  dataSourceState: serializeStacks(this.state.dataSourceState),
5680
5688
  savedAt: Date.now()
5681
5689
  };
5682
- await idbSet(this.resolveDbName(dbName, appId), storeName, key, (0, serialize_javascript.default)(snapshot));
5690
+ await idbSet(this.resolveDbName(dbName, appId), storeName, key, snapshot);
5683
5691
  this.emit("save-to-indexed-db", snapshot);
5684
5692
  return snapshot;
5685
5693
  }
@@ -5693,13 +5701,12 @@
5693
5701
  */
5694
5702
  async restoreFromIndexedDB(options = {}) {
5695
5703
  const { dbName = DEFAULT_DB_NAME, storeName = DEFAULT_STORE_NAME, key = DEFAULT_KEY, appId } = options;
5696
- const raw = await idbGet(this.resolveDbName(dbName, appId), storeName, key);
5697
- if (!raw) return null;
5698
- const snapshot = typeof raw === "string" ? getEditorConfig("parseDSL")(`(${raw})`) : raw;
5704
+ const snapshot = await idbGet(this.resolveDbName(dbName, appId), storeName, key);
5699
5705
  if (!snapshot) return null;
5700
- this.state.pageSteps = deserializeStacks(snapshot.pageSteps);
5701
- this.state.codeBlockState = deserializeStacks(snapshot.codeBlockState);
5702
- this.state.dataSourceState = deserializeStacks(snapshot.dataSourceState);
5706
+ const parseDSL = getEditorConfig("parseDSL");
5707
+ this.state.pageSteps = deserializeStacks(snapshot.pageSteps, parseDSL);
5708
+ this.state.codeBlockState = deserializeStacks(snapshot.codeBlockState, parseDSL);
5709
+ this.state.dataSourceState = deserializeStacks(snapshot.dataSourceState, parseDSL);
5703
5710
  this.state.pageId = snapshot.pageId;
5704
5711
  this.setCanUndoRedo();
5705
5712
  this.emit("restore-from-indexed-db", snapshot);
@@ -5739,11 +5746,8 @@
5739
5746
  return mergePageSteps(targetPageId, list, undoRedo.getCursor()).filter((group) => group.opType !== "initial");
5740
5747
  }
5741
5748
  /**
5742
- * 取出全部代码块的历史栈,按 codeBlockId 分组。
5743
- * 同一栈内相邻、同 opType 且作用于同一 id 的多步会被合并为一个 group:
5744
- * - 这正是"代码块/数据源各自按 id 分栈"的天然表现,再叠加"连续修改同目标的相邻步骤合并展示"。
5745
- * - 合并后 group 暴露子步骤数组,UI 可展开查看每一步的 changeRecords。
5746
- * - applied 字段:组内最后一步是否处于已应用段。
5749
+ * 取出全部代码块的历史栈,按 codeBlockId 分桶展示。
5750
+ * 同一栈内每条操作记录独立成组,不做相邻 update 合并。
5747
5751
  */
5748
5752
  getCodeBlockHistoryGroups() {
5749
5753
  const groups = [];
@@ -5840,7 +5844,7 @@
5840
5844
  return null;
5841
5845
  }
5842
5846
  /**
5843
- * 取出全部数据源的历史栈,按 dataSourceId 分组。同上。
5847
+ * 取出全部数据源的历史栈,按 dataSourceId 分桶展示。同上,每条操作独立成组。
5844
5848
  */
5845
5849
  getDataSourceHistoryGroups() {
5846
5850
  const groups = [];
@@ -6073,12 +6077,26 @@
6073
6077
  }
6074
6078
  return (Math.min(parentHeight, wrapperHeightDeal) - height) / 2;
6075
6079
  };
6080
+ var removeConflictPosition = (style, primary, secondary) => {
6081
+ const isInvalid = (value) => value === "" || value === 0 || value === void 0 || value === null;
6082
+ if (!(primary in style) || !(secondary in style)) return;
6083
+ const primaryValue = style[primary];
6084
+ const secondaryValue = style[secondary];
6085
+ const primaryInvalid = isInvalid(primaryValue);
6086
+ const secondaryInvalid = isInvalid(secondaryValue);
6087
+ if (primaryInvalid && !secondaryInvalid) delete style[primary];
6088
+ else if (secondaryInvalid && !primaryInvalid) delete style[secondary];
6089
+ else if (primaryInvalid && secondaryInvalid) if (secondaryValue === 0 && primaryValue !== 0) delete style[primary];
6090
+ else delete style[secondary];
6091
+ };
6076
6092
  var getInitPositionStyle = (style = {}, layout) => {
6077
6093
  if (layout === Layout.ABSOLUTE) {
6078
6094
  const newStyle = {
6079
6095
  ...style,
6080
6096
  position: "absolute"
6081
6097
  };
6098
+ removeConflictPosition(newStyle, "left", "right");
6099
+ removeConflictPosition(newStyle, "top", "bottom");
6082
6100
  if (typeof newStyle.left === "undefined" && typeof newStyle.right === "undefined") newStyle.left = 0;
6083
6101
  return newStyle;
6084
6102
  }
@@ -6103,58 +6121,63 @@
6103
6121
  }
6104
6122
  return node;
6105
6123
  };
6106
- var change2Fixed = (node, root) => {
6107
- const style = { ...node.style || {} };
6108
- const path = (0, _tmagic_utils.getNodePath)(node.id, root.items);
6109
- const offset = {
6110
- left: 0,
6111
- top: 0
6112
- };
6113
- if (!node.style?.right && (0, _tmagic_utils.isNumber)(node.style?.left || 0)) for (const value of path) {
6114
- if (value.style?.right || !(0, _tmagic_utils.isNumber)(value.style?.left || 0)) {
6115
- offset.left = 0;
6116
- break;
6117
- }
6118
- offset.left = offset.left + Number(value.style?.left || 0);
6124
+ var hasPositionValue = (style, key) => typeof style?.[key] !== "undefined" && style?.[key] !== "";
6125
+ /**
6126
+ * 沿节点路径累加(或抵消)某一方向上的定位偏移量
6127
+ * 当路径上的祖先使用了反方向定位(例如计算 left 时祖先用了 right)或非数值定位时,
6128
+ * 无法简单地通过求和换算坐标,此时返回 0 表示放弃补偿、保持原值
6129
+ * @param path 从根到目标节点的路径(含节点自身)
6130
+ * @param dir 需要累加的定位方向
6131
+ * @param opposite 反方向定位属性
6132
+ * @param sign change2Fixed 1(累加祖先偏移),Fixed2Other -1(抵消祖先偏移)
6133
+ * @param init 偏移量初始值
6134
+ */
6135
+ var accumulatePositionOffset = (path, dir, opposite, sign, init = 0) => {
6136
+ let offset = init;
6137
+ for (const value of path) {
6138
+ if (hasPositionValue(value.style, opposite) || !(0, _tmagic_utils.isNumber)(value.style?.[dir] || 0)) return 0;
6139
+ offset = offset + sign * Number(value.style?.[dir] || 0);
6119
6140
  }
6120
- if (!node.style?.bottom && (0, _tmagic_utils.isNumber)(node.style?.top || 0)) for (const value of path) {
6121
- if (value.style?.bottom || !(0, _tmagic_utils.isNumber)(value.style?.top || 0)) {
6122
- offset.top = 0;
6123
- break;
6124
- }
6125
- offset.top = offset.top + Number(value.style?.top || 0);
6141
+ return offset;
6142
+ };
6143
+ var change2Fixed = (node, path) => {
6144
+ const style = { ...node.style || {} };
6145
+ if (!hasPositionValue(node.style, "right") && (0, _tmagic_utils.isNumber)(node.style?.left || 0)) {
6146
+ const left = accumulatePositionOffset(path, "left", "right", 1);
6147
+ if (left) style.left = left;
6148
+ } else if (hasPositionValue(node.style, "right") && (0, _tmagic_utils.isNumber)(node.style?.right || 0)) {
6149
+ const right = accumulatePositionOffset(path, "right", "left", 1);
6150
+ if (right) style.right = right;
6151
+ }
6152
+ if (!hasPositionValue(node.style, "bottom") && (0, _tmagic_utils.isNumber)(node.style?.top || 0)) {
6153
+ const top = accumulatePositionOffset(path, "top", "bottom", 1);
6154
+ if (top) style.top = top;
6155
+ } else if (hasPositionValue(node.style, "bottom") && (0, _tmagic_utils.isNumber)(node.style?.bottom || 0)) {
6156
+ const bottom = accumulatePositionOffset(path, "bottom", "top", 1);
6157
+ if (bottom) style.bottom = bottom;
6126
6158
  }
6127
- if (offset.left) style.left = offset.left;
6128
- if (offset.top) style.top = offset.top;
6129
6159
  return style;
6130
6160
  };
6131
- var Fixed2Other = async (node, root, getLayout) => {
6132
- const path = (0, _tmagic_utils.getNodePath)(node.id, root.items);
6161
+ var Fixed2Other = async (node, path, getLayout) => {
6133
6162
  const cur = path.pop();
6134
6163
  const offset = {
6135
- left: cur?.style?.left || 0,
6136
- top: cur?.style?.top || 0
6164
+ left: 0,
6165
+ top: 0,
6166
+ right: 0,
6167
+ bottom: 0
6137
6168
  };
6138
- if (!node.style?.right && (0, _tmagic_utils.isNumber)(node.style?.left || 0)) for (const value of path) {
6139
- if (value.style?.right || !(0, _tmagic_utils.isNumber)(value.style?.left || 0)) {
6140
- offset.left = 0;
6141
- break;
6142
- }
6143
- offset.left = offset.left - Number(value.style?.left || 0);
6144
- }
6145
- if (!node.style?.bottom && (0, _tmagic_utils.isNumber)(node.style?.top || 0)) for (const value of path) {
6146
- if (value.style?.bottom || !(0, _tmagic_utils.isNumber)(value.style?.top || 0)) {
6147
- offset.top = 0;
6148
- break;
6149
- }
6150
- offset.top = offset.top - Number(value.style?.top || 0);
6151
- }
6169
+ if (!hasPositionValue(node.style, "right") && (0, _tmagic_utils.isNumber)(node.style?.left || 0)) offset.left = accumulatePositionOffset(path, "left", "right", -1, Number(cur?.style?.left || 0));
6170
+ else if (hasPositionValue(node.style, "right") && (0, _tmagic_utils.isNumber)(node.style?.right || 0)) offset.right = accumulatePositionOffset(path, "right", "left", -1, Number(cur?.style?.right || 0));
6171
+ if (!hasPositionValue(node.style, "bottom") && (0, _tmagic_utils.isNumber)(node.style?.top || 0)) offset.top = accumulatePositionOffset(path, "top", "bottom", -1, Number(cur?.style?.top || 0));
6172
+ else if (hasPositionValue(node.style, "bottom") && (0, _tmagic_utils.isNumber)(node.style?.bottom || 0)) offset.bottom = accumulatePositionOffset(path, "bottom", "top", -1, Number(cur?.style?.bottom || 0));
6152
6173
  const style = node.style || {};
6153
6174
  const parent = path.pop();
6154
6175
  if (!parent) return getRelativeStyle(style);
6155
6176
  if (await getLayout(parent) !== Layout.RELATIVE) {
6156
6177
  if (offset.left) style.left = offset.left;
6157
6178
  if (offset.top) style.top = offset.top;
6179
+ if (offset.right) style.right = offset.right;
6180
+ if (offset.bottom) style.bottom = offset.bottom;
6158
6181
  return {
6159
6182
  ...style,
6160
6183
  position: "absolute"
@@ -6186,11 +6209,11 @@
6186
6209
  };
6187
6210
  var fixNodePosition = (config, parent, stage) => {
6188
6211
  if (config.style?.position !== "absolute") return config.style;
6189
- return {
6190
- ...config.style || {},
6191
- top: getMiddleTop(config, parent, stage),
6192
- left: fixNodeLeft(config, parent, stage?.renderer?.contentWindow?.document)
6193
- };
6212
+ const style = { ...config.style || {} };
6213
+ const baseStyle = config.style || {};
6214
+ if (!("right" in baseStyle)) style.left = fixNodeLeft(config, parent, stage?.renderer?.contentWindow?.document);
6215
+ if (!("top" in baseStyle) && !("bottom" in baseStyle)) style.top = getMiddleTop(config, parent, stage);
6216
+ return style;
6194
6217
  };
6195
6218
  var serializeConfig = (config) => (0, serialize_javascript.default)(config, {
6196
6219
  space: 2,
@@ -6267,13 +6290,14 @@
6267
6290
  var resolveSelectedNode = (config, getNodeInfoFn, rootId) => {
6268
6291
  const id = typeof config === "string" || typeof config === "number" ? config : config.id;
6269
6292
  if (!id) throw new Error("没有ID,无法选中");
6270
- const { node, parent, page } = getNodeInfoFn(id);
6293
+ const { node, parent, page, path } = getNodeInfoFn(id);
6271
6294
  if (!node) throw new Error("获取不到组件信息");
6272
6295
  if (node.id === rootId) throw new Error("不能选根节点");
6273
6296
  return {
6274
6297
  node,
6275
6298
  parent,
6276
- page
6299
+ page,
6300
+ path
6277
6301
  };
6278
6302
  };
6279
6303
  /**
@@ -6285,11 +6309,11 @@
6285
6309
  * @param getLayoutFn 获取父节点布局方式的回调函数
6286
6310
  * @returns 处理后的节点配置(深拷贝)
6287
6311
  */
6288
- var toggleFixedPosition = async (dist, src, root, getLayoutFn) => {
6312
+ var toggleFixedPosition = async (dist, src, path, getLayoutFn) => {
6289
6313
  const newConfig = cloneDeep$1(dist);
6290
6314
  if (!(0, _tmagic_utils.isPop)(src) && newConfig.style?.position) {
6291
- if ((0, _tmagic_stage.isFixed)(newConfig.style) && !(0, _tmagic_stage.isFixed)(src.style || {})) newConfig.style = change2Fixed(newConfig, root);
6292
- else if (!(0, _tmagic_stage.isFixed)(newConfig.style) && (0, _tmagic_stage.isFixed)(src.style || {})) newConfig.style = await Fixed2Other(newConfig, root, getLayoutFn);
6315
+ if ((0, _tmagic_stage.isFixed)(newConfig.style) && !(0, _tmagic_stage.isFixed)(src.style || {})) newConfig.style = change2Fixed(newConfig, path);
6316
+ else if (!(0, _tmagic_stage.isFixed)(newConfig.style) && (0, _tmagic_stage.isFixed)(src.style || {})) newConfig.style = await Fixed2Other(newConfig, path, getLayoutFn);
6293
6317
  }
6294
6318
  return newConfig;
6295
6319
  };
@@ -6603,7 +6627,7 @@
6603
6627
  this.state.pageFragmentLength = 0;
6604
6628
  this.state.stageLoading = false;
6605
6629
  }
6606
- this.emit("root-change", value, preValue);
6630
+ this.emit("root-change", value, preValue, options);
6607
6631
  }
6608
6632
  }
6609
6633
  /**
@@ -6626,12 +6650,14 @@
6626
6650
  if (!root) return {
6627
6651
  node: null,
6628
6652
  parent: null,
6629
- page: null
6653
+ page: null,
6654
+ path: []
6630
6655
  };
6631
6656
  if (id === root.id) return {
6632
6657
  node: root,
6633
6658
  parent: null,
6634
- page: null
6659
+ page: null,
6660
+ path: []
6635
6661
  };
6636
6662
  const pageIdStr = `${this.get("page")?.id || ""}`;
6637
6663
  const currentPageNode = root.items?.find((item) => `${item.id}` === pageIdStr);
@@ -6951,13 +6977,12 @@
6951
6977
  this.emit("remove", nodes);
6952
6978
  }
6953
6979
  async doUpdate(config, { changeRecords = [], historySource } = {}) {
6954
- const root = this.get("root");
6955
- if (!root) throw new Error("root为空");
6980
+ if (!this.get("root")) throw new Error("root为空");
6956
6981
  if (!config?.id) throw new Error("没有配置或者配置缺少id值");
6957
6982
  const info = this.getNodeInfo(config.id, false);
6958
6983
  if (!info.node) throw new Error(`获取不到id为${config.id}的节点`);
6959
6984
  const node = (0, vue.toRaw)(info.node);
6960
- let newConfig = await toggleFixedPosition((0, vue.toRaw)(config), node, root, this.getLayout);
6985
+ let newConfig = await toggleFixedPosition((0, vue.toRaw)(config), node, info.path, this.getLayout);
6961
6986
  newConfig = mergeWith(cloneDeep$1(node), newConfig, editorNodeMergeCustomizer);
6962
6987
  if (!newConfig.type) throw new Error("配置缺少type值");
6963
6988
  if (newConfig.type === _tmagic_core.NodeType.ROOT) {
@@ -7332,46 +7357,61 @@
7332
7357
  }
7333
7358
  /**
7334
7359
  * 下列 *AndGetHistoryId 方法与对应的普通操作(add / remove / update ...)行为完全一致,
7335
- * 唯一区别是返回值为本次写入历史栈的历史记录 uuid{@link StepValue.uuid}),
7336
- * 而非节点 / 节点数组。可用于精确引用 / 定位该条历史记录(埋点、revert、跨端同步等)。
7360
+ * 返回值在 {@link DslOpWithHistoryIdsResult} 中同时包含原操作结果与本次写入历史栈的 uuid 列表({@link StepValue.uuid}),
7361
+ * 可用于精确引用 / 定位该条历史记录(埋点、revert、跨端同步等)。
7337
7362
  *
7338
- * 当本次操作未写入历史(doNotPushHistory 为 true、或操作无实际变更 / 提前返回)时返回 null。
7363
+ * 当本次操作未写入历史(doNotPushHistory 为 true、或操作无实际变更 / 提前返回)时 historyIds 为 `[]`。
7339
7364
  */
7340
- /** 等价于 {@link add},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
7365
+ /** 等价于 {@link add},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
7341
7366
  async addAndGetHistoryId(addNode, parent, options = {}) {
7342
7367
  this.lastPushedHistoryId = null;
7343
- await this.add(addNode, parent, options);
7344
- return this.lastPushedHistoryId;
7368
+ return {
7369
+ result: await this.add(addNode, parent, options),
7370
+ historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
7371
+ };
7345
7372
  }
7346
- /** 等价于 {@link remove},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
7373
+ /** 等价于 {@link remove},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
7347
7374
  async removeAndGetHistoryId(nodeOrNodeList, options = {}) {
7348
7375
  this.lastPushedHistoryId = null;
7349
7376
  await this.remove(nodeOrNodeList, options);
7350
- return this.lastPushedHistoryId;
7377
+ return {
7378
+ result: void 0,
7379
+ historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
7380
+ };
7351
7381
  }
7352
- /** 等价于 {@link update},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
7382
+ /** 等价于 {@link update},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
7353
7383
  async updateAndGetHistoryId(config, data = {}) {
7354
7384
  this.lastPushedHistoryId = null;
7355
- await this.update(config, data);
7356
- return this.lastPushedHistoryId;
7385
+ return {
7386
+ result: await this.update(config, data),
7387
+ historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
7388
+ };
7357
7389
  }
7358
- /** 等价于 {@link moveLayer},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
7390
+ /** 等价于 {@link moveLayer},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
7359
7391
  async moveLayerAndGetHistoryId(offset, options = {}) {
7360
7392
  this.lastPushedHistoryId = null;
7361
7393
  await this.moveLayer(offset, options);
7362
- return this.lastPushedHistoryId;
7394
+ return {
7395
+ result: void 0,
7396
+ historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
7397
+ };
7363
7398
  }
7364
- /** 等价于 {@link moveToContainer},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
7399
+ /** 等价于 {@link moveToContainer},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
7365
7400
  async moveToContainerAndGetHistoryId(config, targetId, options = {}) {
7366
7401
  this.lastPushedHistoryId = null;
7367
- await this.moveToContainer(config, targetId, options);
7368
- return this.lastPushedHistoryId;
7402
+ return {
7403
+ result: await this.moveToContainer(config, targetId, options),
7404
+ historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
7405
+ };
7369
7406
  }
7370
- /** 等价于 {@link dragTo},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
7407
+ /** 等价于 {@link dragTo},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
7371
7408
  async dragToAndGetHistoryId(config, targetParent, targetIndex, options = {}) {
7372
7409
  this.lastPushedHistoryId = null;
7373
7410
  await this.dragTo(config, targetParent, targetIndex, options);
7374
- return this.lastPushedHistoryId;
7411
+ return {
7412
+ result: void 0,
7413
+ historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
7414
+ };
7375
7415
  }
7376
7416
  /**
7377
7417
  * 撤销当前操作
@@ -7480,17 +7520,19 @@
7480
7520
  return revertedStep;
7481
7521
  }
7482
7522
  /**
7483
- * 通过历史记录 uuid 回滚当前页面的某条历史步骤,语义与 {@link revertPageStep} 完全一致,
7484
- * 仅入参从 index 改为 uuid{@link StepValue.uuid})。uuid 不随栈内步骤增删而变化,
7485
- * 更适合业务侧持有引用后再回滚(埋点、跨端同步等场景)。
7523
+ * 通过历史记录 uuid 回滚当前页面的历史步骤,语义与 {@link revertPageStep} 完全一致,
7524
+ * 仅入参从 index 改为 uuid 列表({@link StepValue.uuid})。按数组顺序依次回滚,
7525
+ * 返回与入参同序的结果列表(某项失败时为 `null`)。
7486
7526
  *
7487
- * @param uuid 目标历史记录的 uuid,通常由 *AndGetHistoryId 方法返回
7488
- * @returns 反向后产生的新 step;找不到对应 uuid / 未应用 / 反向失败时返回 null
7527
+ * @param uuids 目标历史记录的 uuid 列表,通常由 *AndGetHistoryId 方法返回的 `historyIds`
7489
7528
  */
7490
- async revertPageStepById(uuid) {
7491
- const index = history_default.getPageStepIndexByUuid(uuid);
7492
- if (index < 0) return null;
7493
- return this.revertPageStep(index);
7529
+ async revertPageStepById(uuids) {
7530
+ const results = [];
7531
+ for (const uuid of uuids) {
7532
+ const index = history_default.getPageStepIndexByUuid(uuid);
7533
+ results.push(index < 0 ? null : await this.revertPageStep(index));
7534
+ }
7535
+ return results;
7494
7536
  }
7495
7537
  /**
7496
7538
  * 跳转当前页面历史栈到指定游标位置。
@@ -8472,6 +8514,7 @@
8472
8514
  items: [
8473
8515
  {
8474
8516
  title: "数据定义",
8517
+ status: "fields",
8475
8518
  items: [{
8476
8519
  name: "fields",
8477
8520
  type: "data-source-fields",
@@ -8480,6 +8523,7 @@
8480
8523
  },
8481
8524
  {
8482
8525
  title: "方法定义",
8526
+ status: "methods",
8483
8527
  items: [{
8484
8528
  name: "methods",
8485
8529
  type: "data-source-methods",
@@ -10131,12 +10175,13 @@
10131
10175
  var toRowGroup = (group, key, descriptor) => {
10132
10176
  const { describeGroup, describeStep, isStepDiffable, isStepRevertable } = descriptor;
10133
10177
  const timestamp = groupTimestamp(group);
10178
+ const lastStep = group.steps[group.steps.length - 1]?.step;
10134
10179
  return {
10135
10180
  key,
10136
10181
  applied: group.applied,
10137
10182
  isCurrent: Boolean(group.isCurrent),
10138
10183
  opType: group.opType,
10139
- desc: describeGroup(group),
10184
+ desc: describeGroup ? describeGroup(group) : describeStep(lastStep),
10140
10185
  source: groupSource(group),
10141
10186
  time: formatHistoryTime(timestamp),
10142
10187
  timeTitle: formatHistoryFullTime(timestamp),
@@ -10169,28 +10214,36 @@
10169
10214
  var pickLastDescription = (descs) => {
10170
10215
  for (let i = descs.length - 1; i >= 0; i--) if (descs[i]) return descs[i];
10171
10216
  };
10172
- var describePageStep = (step) => {
10217
+ /**
10218
+ * 页面 / 数据源 / 代码块三类历史共用的单步描述核心。
10219
+ * 各类型只在「取展示名」与「实体单位名」上有差异,通过参数注入,文案模板完全一致:
10220
+ * - 新增 / 删除:单实体展示「label」,多实体(仅页面可能出现)退化为「N 个X」;
10221
+ * - 修改:展示「label · propPath」,无 diff 时兜底「X」,多实体退化为「N 个X」。
10222
+ * 操作类型(新增 / 删除 / 修改)已由列表行的 op 徽标单独展示,故描述文案不再重复动词。
10223
+ * 展示 id 统一取 schema.id;调用方显式传入的 historyDescription 永远优先。
10224
+ */
10225
+ var describeStep = (step, getLabel, unit) => {
10173
10226
  if (step.historyDescription) return step.historyDescription;
10174
- const { opType } = step;
10175
10227
  const items = step.diff ?? [];
10176
- if (opType === "add") {
10177
- const count = items.length;
10228
+ const label = (schema) => labelWithId(getLabel(schema), schema?.id);
10229
+ if (step.opType === "add") {
10178
10230
  const node = items[0]?.newSchema;
10179
- return `新增 ${count} 个节点${count === 1 && node ? `(${labelWithId(nameOf(node), node.id)})` : ""}`;
10231
+ return items.length === 1 && node ? label(node) : `${items.length} 个${unit}`;
10180
10232
  }
10181
- if (opType === "remove") {
10182
- const count = items.length;
10233
+ if (step.opType === "remove") {
10183
10234
  const node = items[0]?.oldSchema;
10184
- return `删除 ${count} 个节点${count === 1 && node ? `(${labelWithId(nameOf(node), node.id)})` : ""}`;
10235
+ return items.length === 1 && node ? label(node) : `${items.length} 个${unit}`;
10185
10236
  }
10186
- if (!items.length) return "修改节点";
10237
+ if (!items.length) return unit;
10187
10238
  if (items.length === 1) {
10188
- const { newSchema, changeRecords } = items[0];
10189
- const propPath = changeRecords?.[0]?.propPath;
10190
- return `修改 ${labelWithId(nameOf(newSchema), newSchema?.id)}${propPath ? ` · ${propPath}` : ""}`;
10239
+ const { newSchema, oldSchema, changeRecords } = items[0];
10240
+ const propPath = changeRecords?.map((changeRecord) => changeRecord.propPath).join(",");
10241
+ const target = label(newSchema ?? oldSchema);
10242
+ return propPath ? `${target} · ${propPath}` : target;
10191
10243
  }
10192
- return `修改 ${items.length} 个节点`;
10244
+ return `${items.length} 个${unit}`;
10193
10245
  };
10246
+ var describePageStep = (step) => describeStep(step, (node) => nameOf(node), "节点");
10194
10247
  /**
10195
10248
  * 合并组的展示文案:
10196
10249
  * - 若组内任一步显式提供了 historyDescription:取最后一条非空 historyDescription(最近一次的描述更准确);
@@ -10201,55 +10254,7 @@
10201
10254
  const lastDesc = pickLastDescription(group.steps.map((s) => s.step.historyDescription));
10202
10255
  if (lastDesc) return lastDesc;
10203
10256
  if (group.steps.length === 1) return describePageStep(group.steps[0].step);
10204
- const paths = /* @__PURE__ */ new Set();
10205
- group.steps.forEach((s) => {
10206
- s.step.diff?.[0]?.changeRecords?.forEach((r) => r.propPath && paths.add(r.propPath));
10207
- });
10208
- const pathList = Array.from(paths).slice(0, 3).join(", ");
10209
- const target = labelWithId(group.targetName ?? (group.targetId !== void 0 ? `${group.targetId}` : "节点"), group.targetId);
10210
- return pathList ? `修改 ${target} · ${pathList}${paths.size > 3 ? "…" : ""}` : `修改 ${target}`;
10211
- };
10212
- var describeDataSourceStep = (step) => {
10213
- if (step.historyDescription) return step.historyDescription;
10214
- const { oldSchema, newSchema, changeRecords } = step.diff?.[0] ?? {};
10215
- if (!oldSchema && newSchema) return `创建 ${labelWithId(newSchema.title, newSchema.id ?? step.id)}`;
10216
- if (!newSchema && oldSchema) return `删除 ${labelWithId(oldSchema.title, oldSchema.id ?? step.id)}`;
10217
- const propPath = changeRecords?.[0]?.propPath;
10218
- const title = labelWithId(newSchema?.title || oldSchema?.title, step.id);
10219
- return propPath ? `修改 ${title} · ${propPath}` : `修改 ${title}`;
10220
- };
10221
- var describeDataSourceGroup = (group) => {
10222
- const lastDesc = pickLastDescription(group.steps.map((s) => s.step.historyDescription));
10223
- if (lastDesc) return lastDesc;
10224
- if (group.steps.length === 1) return describeDataSourceStep(group.steps[0].step);
10225
- const paths = /* @__PURE__ */ new Set();
10226
- group.steps.forEach((s) => {
10227
- s.step.diff?.[0]?.changeRecords?.forEach((r) => r.propPath && paths.add(r.propPath));
10228
- });
10229
- const pathList = Array.from(paths).slice(0, 3).join(", ");
10230
- const target = labelWithId(group.steps[group.steps.length - 1].step.diff?.[0]?.newSchema?.title || group.steps[0].step.diff?.[0]?.oldSchema?.title, group.id);
10231
- return pathList ? `修改 ${target} · ${pathList}${paths.size > 3 ? "…" : ""}` : `修改 ${target}`;
10232
- };
10233
- var describeCodeBlockStep = (step) => {
10234
- if (step.historyDescription) return step.historyDescription;
10235
- const { oldSchema: oldContent, newSchema: newContent, changeRecords } = step.diff?.[0] ?? {};
10236
- if (!oldContent && newContent) return `创建 ${labelWithId(newContent.name, newContent.id ?? step.id)}`;
10237
- if (!newContent && oldContent) return `删除 ${labelWithId(oldContent.name, oldContent.id ?? step.id)}`;
10238
- const propPath = changeRecords?.[0]?.propPath;
10239
- const title = labelWithId(newContent?.name || oldContent?.name, step.id);
10240
- return propPath ? `修改 ${title} · ${propPath}` : `修改 ${title}`;
10241
- };
10242
- var describeCodeBlockGroup = (group) => {
10243
- const lastDesc = pickLastDescription(group.steps.map((s) => s.step.historyDescription));
10244
- if (lastDesc) return lastDesc;
10245
- if (group.steps.length === 1) return describeCodeBlockStep(group.steps[0].step);
10246
- const paths = /* @__PURE__ */ new Set();
10247
- group.steps.forEach((s) => {
10248
- s.step.diff?.[0]?.changeRecords?.forEach((r) => r.propPath && paths.add(r.propPath));
10249
- });
10250
- const pathList = Array.from(paths).slice(0, 3).join(", ");
10251
- const target = labelWithId(group.steps[group.steps.length - 1].step.diff?.[0]?.newSchema?.name || group.steps[0].step.diff?.[0]?.oldSchema?.name, group.id);
10252
- return pathList ? `修改 ${target} · ${pathList}${paths.size > 3 ? "…" : ""}` : `修改 ${target}`;
10257
+ return labelWithId(group.targetName ?? (group.targetId !== void 0 ? `${group.targetId}` : "节点"), group.targetId);
10253
10258
  };
10254
10259
  /**
10255
10260
  * 页面 step 是否支持「回滚」(类 git revert):
@@ -10264,21 +10269,11 @@
10264
10269
  return items.every((item) => Boolean(item.changeRecords?.length));
10265
10270
  };
10266
10271
  /**
10267
- * 数据源 step 是否支持「回滚」:
10268
- * - 新增(无 oldSchema)/ 删除(无 newSchema):不依赖 changeRecords,始终可回滚;
10269
- * - 更新(前后 schema 都存在):必须有 changeRecords 才支持局部反向 patch,否则不支持回滚。
10270
- */
10271
- var isDataSourceStepRevertable = (step) => {
10272
- const item = step.diff?.[0];
10273
- if (!item?.oldSchema || !item?.newSchema) return true;
10274
- return Boolean(item.changeRecords?.length);
10275
- };
10276
- /**
10277
- * 代码块 step 是否支持「回滚」:
10272
+ * diff 项历史(数据源 / 代码块)是否支持「回滚」:
10278
10273
  * - 新增(无 oldSchema)/ 删除(无 newSchema):不依赖 changeRecords,始终可回滚;
10279
- * - 更新(前后 content 都存在):必须有 changeRecords 才支持局部反向 patch,否则不支持回滚。
10274
+ * - 更新(前后内容都存在):必须有 changeRecords 才支持局部反向 patch,否则不支持回滚。
10280
10275
  */
10281
- var isCodeBlockStepRevertable = (step) => {
10276
+ var isSingleDiffStepRevertable = (step) => {
10282
10277
  const item = step.diff?.[0];
10283
10278
  if (!item?.oldSchema || !item?.newSchema) return true;
10284
10279
  return Boolean(item.changeRecords?.length);
@@ -10293,7 +10288,7 @@
10293
10288
  class: "m-editor-history-list-item-saved",
10294
10289
  title: "该记录为最近一次保存的状态"
10295
10290
  };
10296
- var _hoisted_5$3 = {
10291
+ var _hoisted_5$4 = {
10297
10292
  key: 1,
10298
10293
  class: "m-editor-history-list-item-actions"
10299
10294
  };
@@ -10453,7 +10448,7 @@
10453
10448
  (0, vue.createElementVNode)("span", { class: (0, vue.normalizeClass)(["m-editor-history-list-item-op", `op-${__props.group.opType}`]) }, (0, vue.toDisplayString)((0, vue.unref)(opLabel)(__props.group.opType)), 3),
10454
10449
  (0, vue.createElementVNode)("span", _hoisted_3$11, (0, vue.toDisplayString)(__props.group.desc), 1),
10455
10450
  headSaved.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_4$8, "已保存")) : (0, vue.createCommentVNode)("v-if", true),
10456
- !merged.value && (headRevertable.value || headDiffable.value || canHeadGoto.value) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_5$3, [
10451
+ !merged.value && (headRevertable.value || headDiffable.value || canHeadGoto.value) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_5$4, [
10457
10452
  headRevertable.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
10458
10453
  key: 0,
10459
10454
  class: "m-editor-history-list-item-revert",
@@ -10546,9 +10541,14 @@
10546
10541
  var _hoisted_2$22 = { class: "m-editor-history-list-item-desc" };
10547
10542
  var _hoisted_3$10 = {
10548
10543
  key: 0,
10544
+ class: "m-editor-history-list-item-saved",
10545
+ title: "该记录为最近一次保存的状态"
10546
+ };
10547
+ var _hoisted_4$7 = {
10548
+ key: 1,
10549
10549
  class: "m-editor-history-list-item-actions"
10550
10550
  };
10551
- var _hoisted_4$7 = ["title"];
10551
+ var _hoisted_5$3 = ["title"];
10552
10552
  var InitialRow_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
10553
10553
  name: "MEditorHistoryListInitialRow",
10554
10554
  __name: "InitialRow",
@@ -10573,6 +10573,8 @@
10573
10573
  */
10574
10574
  const props = __props;
10575
10575
  const desc = (0, vue.computed)(() => props.marker?.historyDescription || "未修改的初始状态");
10576
+ /** 基线(初始状态)是否为最近一次保存点:仅页面栈的 `initial` 基线 step 会被 markSaved 标记。 */
10577
+ const saved = (0, vue.computed)(() => Boolean(props.marker?.saved));
10576
10578
  const time = (0, vue.computed)(() => formatHistoryTime(props.marker?.timestamp));
10577
10579
  const timeTitle = (0, vue.computed)(() => formatHistoryFullTime(props.marker?.timestamp));
10578
10580
  const rowTitle = (0, vue.computed)(() => {
@@ -10598,16 +10600,17 @@
10598
10600
  }, "#0", -1)),
10599
10601
  _cache[1] || (_cache[1] = (0, vue.createElementVNode)("span", { class: "m-editor-history-list-item-op op-initial" }, "初始", -1)),
10600
10602
  (0, vue.createElementVNode)("span", _hoisted_2$22, (0, vue.toDisplayString)(desc.value), 1),
10601
- __props.gotoEnabled && !__props.isCurrent ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_3$10, [(0, vue.createElementVNode)("span", {
10603
+ saved.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_3$10, "已保存")) : (0, vue.createCommentVNode)("v-if", true),
10604
+ __props.gotoEnabled && !__props.isCurrent ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_4$7, [(0, vue.createElementVNode)("span", {
10602
10605
  class: "m-editor-history-list-item-goto",
10603
10606
  title: "回到该记录",
10604
10607
  onClick: (0, vue.withModifiers)(onClick, ["stop"])
10605
10608
  }, "回到")])) : (0, vue.createCommentVNode)("v-if", true),
10606
10609
  time.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
10607
- key: 1,
10610
+ key: 2,
10608
10611
  class: "m-editor-history-list-item-time",
10609
10612
  title: timeTitle.value
10610
- }, (0, vue.toDisplayString)(time.value), 9, _hoisted_4$7)) : (0, vue.createCommentVNode)("v-if", true)
10613
+ }, (0, vue.toDisplayString)(time.value), 9, _hoisted_5$3)) : (0, vue.createCommentVNode)("v-if", true)
10611
10614
  ], 10, _hoisted_1$60);
10612
10615
  };
10613
10616
  }
@@ -11299,8 +11302,8 @@
11299
11302
  /**
11300
11303
  * 历史记录面板:在顶部 NavMenu 上点击图标打开 popover,分三个 tab:
11301
11304
  * - 页面:当前活动页面的历史栈,连续修改同一节点的多步会被合并成一组
11302
- * - 数据源:以 dataSource.id 分组,每组内部相邻的连续 update 自动合并
11303
- * - 代码块:同上,按 codeBlock.id 分组并合并相邻 update
11305
+ * - 数据源:以 dataSource.id 分桶,每条操作记录独立展示
11306
+ * - 代码块:同上,按 codeBlock.id 分桶,每条操作记录独立展示
11304
11307
  *
11305
11308
  * 数据通过 historyService 暴露的聚合 API 读取,UI 仅用于只读展示,
11306
11309
  * 同时支持点击任意一条记录跳转至该状态:
@@ -11355,10 +11358,8 @@
11355
11358
  * 基于 historyService 的 reactive state 派生,活动页切换或标记写入后自动刷新。
11356
11359
  */
11357
11360
  const pageMarker = (0, vue.computed)(() => historyService.getPageMarker());
11358
- /** 数据源 step 仅 update(前后 schema 都存在)时可查看差异。 */
11359
- const isDataSourceStepDiffable = (step) => Boolean(step.diff?.[0]?.oldSchema && step.diff?.[0]?.newSchema);
11360
11361
  /** 代码块 step 仅 update(前后 content 都存在)时可查看差异。 */
11361
- const isCodeBlockStepDiffable = (step) => Boolean(step.diff?.[0]?.oldSchema && step.diff?.[0]?.newSchema);
11362
+ const isStepDiffable = (step) => Boolean(step.diff?.[0]?.oldSchema && step.diff?.[0]?.newSchema);
11362
11363
  /**
11363
11364
  * 数据源 / 代码块两类 bucket 历史的整体渲染配置:把 title / prefix 与各自的描述、
11364
11365
  * 可差异、可回滚判定收敛为单一对象整体注入 BucketTab,组件内部按需读取。
@@ -11366,18 +11367,16 @@
11366
11367
  const dataSourceConfig = {
11367
11368
  title: "数据源",
11368
11369
  prefix: "ds",
11369
- describeGroup: describeDataSourceGroup,
11370
- describeStep: describeDataSourceStep,
11371
- isStepDiffable: isDataSourceStepDiffable,
11372
- isStepRevertable: isDataSourceStepRevertable
11370
+ describeStep: (step) => describeStep(step, (schema) => schema?.title, "数据源"),
11371
+ isStepDiffable,
11372
+ isStepRevertable: isSingleDiffStepRevertable
11373
11373
  };
11374
11374
  const codeBlockConfig = {
11375
11375
  title: "代码块",
11376
11376
  prefix: "cb",
11377
- describeGroup: describeCodeBlockGroup,
11378
- describeStep: describeCodeBlockStep,
11379
- isStepDiffable: isCodeBlockStepDiffable,
11380
- isStepRevertable: isCodeBlockStepRevertable
11377
+ describeStep: (step) => describeStep(step, (content) => content?.name, "代码块"),
11378
+ isStepDiffable,
11379
+ isStepRevertable: isSingleDiffStepRevertable
11381
11380
  };
11382
11381
  /** 把"目标 step 索引"翻译成"目标 cursor"(已应用步骤数量)。 */
11383
11382
  const indexToCursor = (index) => index + 1;
@@ -13644,7 +13643,9 @@
13644
13643
  props: /* @__PURE__ */ (0, vue.mergeModels)({
13645
13644
  title: {},
13646
13645
  values: {},
13647
- disabled: { type: Boolean }
13646
+ disabled: { type: Boolean },
13647
+ editMethodName: {},
13648
+ editFieldPath: {}
13648
13649
  }, {
13649
13650
  "visible": {
13650
13651
  type: Boolean,
@@ -13669,9 +13670,20 @@
13669
13670
  const dataSourceConfig = (0, vue.ref)([]);
13670
13671
  const { height: editorHeight } = useEditorContentHeight();
13671
13672
  const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(uiService, (0, vue.inject)("parentFloating", (0, vue.ref)(null)));
13673
+ /** 供「方法定义」tab 内的字段消费,用于打开数据源详情后自动打开指定方法 */
13674
+ (0, vue.provide)("editingDataSourceMethodName", (0, vue.computed)(() => props.editMethodName));
13675
+ /** 供「数据定义」tab 内的字段消费,用于打开数据源详情后自动打开指定字段 */
13676
+ (0, vue.provide)("editingDataSourceFieldPath", (0, vue.computed)(() => props.editFieldPath || []));
13672
13677
  (0, vue.watchEffect)(() => {
13673
13678
  initValues.value = props.values;
13674
- dataSourceConfig.value = dataSourceService.getFormConfig(initValues.value.type);
13679
+ const config = dataSourceService.getFormConfig(initValues.value.type);
13680
+ let activeTab = "fields";
13681
+ if (props.editMethodName) activeTab = "methods";
13682
+ else if (props.editFieldPath?.length) activeTab = "fields";
13683
+ dataSourceConfig.value = config.map((item) => item.type === "tab" ? {
13684
+ ...item,
13685
+ active: activeTab
13686
+ } : item);
13675
13687
  });
13676
13688
  const submitHandler = (values, data) => {
13677
13689
  emit("submit", values, data);
@@ -13960,7 +13972,13 @@
13960
13972
  const eventBus = (0, vue.inject)("eventBus");
13961
13973
  const { dataSourceService } = useServices();
13962
13974
  const { editDialog, dataSourceValues, dialogTitle, editable, editHandler, submitDataSourceHandler } = useDataSourceEdit(dataSourceService);
13975
+ /** 打开数据源详情时需要直接定位并打开的方法名,为空则正常展示「数据定义」tab */
13976
+ const editMethodName = (0, vue.ref)("");
13977
+ /** 打开数据源详情时需要直接定位并打开的字段路径,为空则不自动打开字段配置 */
13978
+ const editFieldPath = (0, vue.ref)([]);
13963
13979
  const editDialogCloseHandler = () => {
13980
+ editMethodName.value = "";
13981
+ editFieldPath.value = [];
13964
13982
  if (dataSourceListRef.value) for (const [, status] of dataSourceListRef.value.nodeStatusMap.entries()) status.selected = false;
13965
13983
  };
13966
13984
  (0, vue.watch)(dataSourceValues, (dataSourceValues) => {
@@ -13998,6 +14016,18 @@
13998
14016
  dataSourceListRef.value?.filter(val);
13999
14017
  };
14000
14018
  eventBus?.on("edit-data-source", (id) => {
14019
+ editMethodName.value = "";
14020
+ editFieldPath.value = [];
14021
+ editHandler(id);
14022
+ });
14023
+ eventBus?.on("edit-data-source-method", (id, methodName) => {
14024
+ editMethodName.value = methodName;
14025
+ editFieldPath.value = [];
14026
+ editHandler(id);
14027
+ });
14028
+ eventBus?.on("edit-data-source-field", (id, fieldPath) => {
14029
+ editMethodName.value = "";
14030
+ editFieldPath.value = fieldPath;
14001
14031
  editHandler(id);
14002
14032
  });
14003
14033
  eventBus?.on("remove-data-source", (id) => {
@@ -14046,12 +14076,16 @@
14046
14076
  disabled: !(0, vue.unref)(editable),
14047
14077
  values: (0, vue.unref)(dataSourceValues),
14048
14078
  title: (0, vue.unref)(dialogTitle),
14079
+ "edit-method-name": editMethodName.value,
14080
+ "edit-field-path": editFieldPath.value,
14049
14081
  onSubmit: (0, vue.unref)(submitDataSourceHandler),
14050
14082
  onClose: editDialogCloseHandler
14051
14083
  }, null, 8, [
14052
14084
  "disabled",
14053
14085
  "values",
14054
14086
  "title",
14087
+ "edit-method-name",
14088
+ "edit-field-path",
14055
14089
  "onSubmit"
14056
14090
  ]),
14057
14091
  ((0, vue.openBlock)(), (0, vue.createBlock)(vue.Teleport, { to: "body" }, [menuData.value.length ? ((0, vue.openBlock)(), (0, vue.createBlock)(ContentMenu_default, {
@@ -15047,7 +15081,7 @@
15047
15081
  slots: {}
15048
15082
  },
15049
15083
  layer: {
15050
- $key: "layer",
15084
+ $key: SideItemKey.LAYER,
15051
15085
  type: "component",
15052
15086
  icon: _element_plus_icons_vue.List,
15053
15087
  text: "已选组件",
@@ -15065,7 +15099,7 @@
15065
15099
  slots: {}
15066
15100
  },
15067
15101
  [SideItemKey.CODE_BLOCK]: {
15068
- $key: "code-block",
15102
+ $key: SideItemKey.CODE_BLOCK,
15069
15103
  type: "component",
15070
15104
  icon: _element_plus_icons_vue.EditPen,
15071
15105
  text: "代码编辑",
@@ -16436,31 +16470,40 @@
16436
16470
  }
16437
16471
  /**
16438
16472
  * 下列 *AndGetHistoryId 方法与对应的写入方法行为完全一致,
16439
- * 唯一区别是返回值为本次写入历史栈的历史记录 uuid({@link CodeBlockStepValue.uuid}),
16473
+ * 返回值在 {@link DslOpWithHistoryIdsResult} 中同时包含原操作结果与本次写入的历史 uuid 列表,
16440
16474
  * 可用于精确引用 / 定位该条历史记录(埋点、revert、跨端同步等)。
16441
16475
  *
16442
- * 当本次操作未写入历史(doNotPushHistory 为 true、或无对应记录)时:单条写入返回 null,批量删除返回空数组。
16476
+ * 当本次操作未写入历史(doNotPushHistory 为 true、或无对应记录)时 historyIds 为 `[]`。
16443
16477
  */
16444
- /** 等价于 {@link setCodeDslById},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
16478
+ /** 等价于 {@link setCodeDslById},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
16445
16479
  async setCodeDslByIdAndGetHistoryId(id, codeConfig, options = {}) {
16446
16480
  this.lastPushedHistoryId = null;
16447
16481
  await this.setCodeDslById(id, codeConfig, options);
16448
- return this.lastPushedHistoryId;
16482
+ return {
16483
+ result: void 0,
16484
+ historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
16485
+ };
16449
16486
  }
16450
- /** 等价于 {@link setCodeDslByIdSync},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
16487
+ /** 等价于 {@link setCodeDslByIdSync},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
16451
16488
  setCodeDslByIdSyncAndGetHistoryId(id, codeConfig, force = true, options = {}) {
16452
16489
  this.lastPushedHistoryId = null;
16453
16490
  this.setCodeDslByIdSync(id, codeConfig, force, options);
16454
- return this.lastPushedHistoryId;
16491
+ return {
16492
+ result: void 0,
16493
+ historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
16494
+ };
16455
16495
  }
16456
16496
  /**
16457
- * 等价于 {@link deleteCodeDslByIds},但返回本次写入的全部历史记录 uuid(按删除顺序)。
16458
- * 一次删除多个代码块会产生多条历史记录,因此返回数组;未写入任何历史时返回空数组。
16497
+ * 等价于 {@link deleteCodeDslByIds},并额外返回本次写入的全部历史记录 uuid(按删除顺序)。
16498
+ * 一次删除多个代码块会产生多条历史记录;未写入任何历史时 historyIds 为 `[]`。
16459
16499
  */
16460
16500
  async deleteCodeDslByIdsAndGetHistoryId(codeIds, options = {}) {
16461
16501
  this.lastDeletedHistoryIds = [];
16462
16502
  await this.deleteCodeDslByIds(codeIds, options);
16463
- return [...this.lastDeletedHistoryIds];
16503
+ return {
16504
+ result: void 0,
16505
+ historyIds: [...this.lastDeletedHistoryIds]
16506
+ };
16464
16507
  }
16465
16508
  setParamsColConfig(config) {
16466
16509
  this.state.paramsColConfig = config;
@@ -16543,17 +16586,19 @@
16543
16586
  return await this.applyRevertStep(entry.step, description);
16544
16587
  }
16545
16588
  /**
16546
- * 通过历史记录 uuid 回滚某条代码块历史步骤,语义同 {@link revert},
16547
- * 仅无需调用方再传 codeBlockId 与 index:内部会按 uuid({@link CodeBlockStepValue.uuid})
16548
- * 在全部代码块栈中定位对应步骤后再回滚。
16589
+ * 通过历史记录 uuid 回滚代码块历史步骤,语义同 {@link revert},
16590
+ * 仅无需调用方再传 codeBlockId 与 index:内部会按 uuid 在全部代码块栈中定位对应步骤后再回滚。
16591
+ * 按数组顺序依次回滚,返回与入参同序的结果列表(某项失败时为 `null`)。
16549
16592
  *
16550
- * @param uuid 目标历史记录的 uuid,通常由 {@link setCodeDslByIdAndGetHistoryId} 等方法返回
16551
- * @returns 反向后产生的新 step;找不到对应 uuid / 未应用时返回 null
16593
+ * @param uuids 目标历史记录的 uuid 列表,通常由 {@link setCodeDslByIdAndGetHistoryId} 等方法返回的 `historyIds`
16552
16594
  */
16553
- async revertById(uuid) {
16554
- const location = history_default.findCodeBlockStepLocationByUuid(uuid);
16555
- if (!location) return null;
16556
- return await this.revert(location.id, location.index);
16595
+ async revertById(uuids) {
16596
+ const results = [];
16597
+ for (const uuid of uuids) {
16598
+ const location = history_default.findCodeBlockStepLocationByUuid(uuid);
16599
+ results.push(location ? await this.revert(location.id, location.index) : null);
16600
+ }
16601
+ return results;
16557
16602
  }
16558
16603
  /**
16559
16604
  * 生成代码块唯一id
@@ -16883,28 +16928,35 @@
16883
16928
  }
16884
16929
  /**
16885
16930
  * 下列 *AndGetHistoryId 方法与对应的 add / update / remove 行为完全一致,
16886
- * 唯一区别是返回值为本次写入历史栈的历史记录 uuid{@link DataSourceStepValue.uuid}),
16887
- * 而非数据源配置。可用于精确引用 / 定位该条历史记录(埋点、revert、跨端同步等)。
16931
+ * 返回值在 {@link DslOpWithHistoryIdsResult} 中同时包含原操作结果与本次写入历史栈的 uuid 列表({@link DataSourceStepValue.uuid}),
16932
+ * 可用于精确引用 / 定位该条历史记录(埋点、revert、跨端同步等)。
16888
16933
  *
16889
- * 当本次操作未写入历史(doNotPushHistory 为 true、或无对应记录)时返回 null。
16934
+ * 当本次操作未写入历史(doNotPushHistory 为 true、或无对应记录)时 historyIds 为 `[]`。
16890
16935
  */
16891
- /** 等价于 {@link add},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
16936
+ /** 等价于 {@link add},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
16892
16937
  addAndGetHistoryId(config, options = {}) {
16893
16938
  this.lastPushedHistoryId = null;
16894
- this.add(config, options);
16895
- return this.lastPushedHistoryId;
16939
+ return {
16940
+ result: this.add(config, options),
16941
+ historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
16942
+ };
16896
16943
  }
16897
- /** 等价于 {@link update},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
16944
+ /** 等价于 {@link update},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
16898
16945
  updateAndGetHistoryId(config, options = {}) {
16899
16946
  this.lastPushedHistoryId = null;
16900
- this.update(config, options);
16901
- return this.lastPushedHistoryId;
16947
+ return {
16948
+ result: this.update(config, options),
16949
+ historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
16950
+ };
16902
16951
  }
16903
- /** 等价于 {@link remove},但返回本次写入历史记录的 uuid(未入栈时返回 null)。 */
16952
+ /** 等价于 {@link remove},并额外返回本次写入历史记录的 uuid 列表(未入栈时 historyIds 为 `[]`)。 */
16904
16953
  removeAndGetHistoryId(id, options = {}) {
16905
16954
  this.lastPushedHistoryId = null;
16906
16955
  this.remove(id, options);
16907
- return this.lastPushedHistoryId;
16956
+ return {
16957
+ result: void 0,
16958
+ historyIds: getLastPushedHistoryIds(this.lastPushedHistoryId)
16959
+ };
16908
16960
  }
16909
16961
  /**
16910
16962
  * 撤销指定数据源的最近一次变更。
@@ -16980,17 +17032,18 @@
16980
17032
  return this.applyRevertStep(entry.step, description);
16981
17033
  }
16982
17034
  /**
16983
- * 通过历史记录 uuid 回滚某条数据源历史步骤,语义同 {@link revert},
16984
- * 仅无需调用方再传 dataSourceId 与 index:内部会按 uuid({@link DataSourceStepValue.uuid})
16985
- * 在全部数据源栈中定位对应步骤后再回滚。
17035
+ * 通过历史记录 uuid 回滚数据源历史步骤,语义同 {@link revert},
17036
+ * 仅无需调用方再传 dataSourceId 与 index:内部会按 uuid 在全部数据源栈中定位对应步骤后再回滚。
17037
+ * 按数组顺序依次回滚,返回与入参同序的结果列表(某项失败时为 `null`)。
16986
17038
  *
16987
- * @param uuid 目标历史记录的 uuid,通常由 {@link addAndGetHistoryId} 等方法返回
16988
- * @returns 反向后产生的新 step;找不到对应 uuid / 未应用时返回 null
17039
+ * @param uuids 目标历史记录的 uuid 列表,通常由 {@link addAndGetHistoryId} 等方法返回的 `historyIds`
16989
17040
  */
16990
- revertById(uuid) {
16991
- const location = history_default.findDataSourceStepLocationByUuid(uuid);
16992
- if (!location) return null;
16993
- return this.revert(location.id, location.index);
17041
+ revertById(uuids) {
17042
+ return uuids.map((uuid) => {
17043
+ const location = history_default.findDataSourceStepLocationByUuid(uuid);
17044
+ if (!location) return null;
17045
+ return this.revert(location.id, location.index);
17046
+ });
16994
17047
  }
16995
17048
  createId() {
16996
17049
  return `ds_${(0, _tmagic_utils.guid)()}`;
@@ -18630,7 +18683,8 @@
18630
18683
  */
18631
18684
  const isCompareMode = (0, vue.computed)(() => Boolean(props.isCompare && props.lastValues));
18632
18685
  const notEditable = (0, vue.computed)(() => (0, _tmagic_form.filterFunction)(mForm, props.config.notEditable, props));
18633
- const hasCodeBlockSidePanel = (0, vue.computed)(() => (uiService.get("sideBarItems") || []).find((item) => item.$key === SideItemKey.CODE_BLOCK));
18686
+ const codeBlockSidePanel = (0, vue.computed)(() => (uiService.get("sideBarItems") || []).find((item) => item.$key === SideItemKey.CODE_BLOCK));
18687
+ const hasCodeBlockSidePanel = (0, vue.computed)(() => codeBlockSidePanel.value);
18634
18688
  /**
18635
18689
  * 根据代码块id获取代码块参数配置
18636
18690
  * @param codeId 代码块ID
@@ -18685,6 +18739,8 @@
18685
18739
  emit("change", props.model[props.name], eventData);
18686
18740
  };
18687
18741
  const editCode = (id) => {
18742
+ const sideBarItem = codeBlockSidePanel.value;
18743
+ if (sideBarItem) uiService.set("sideBarActiveTabName", sideBarItem.text || sideBarItem.$key || SideItemKey.CODE_BLOCK);
18688
18744
  eventBus?.emit("edit-code", id);
18689
18745
  };
18690
18746
  return (_ctx, _cache) => {
@@ -18817,6 +18873,15 @@
18817
18873
  calcBoxPosition();
18818
18874
  addDialogVisible.value = true;
18819
18875
  };
18876
+ const editField = (row, index) => {
18877
+ fieldValues.value = {
18878
+ ...row,
18879
+ index
18880
+ };
18881
+ fieldTitle.value = `编辑${row.title}`;
18882
+ calcBoxPosition();
18883
+ addDialogVisible.value = true;
18884
+ };
18820
18885
  const fieldChange = ({ index, ...value }, data) => {
18821
18886
  addDialogVisible.value = false;
18822
18887
  if (index > -1) emit("change", value, {
@@ -18868,13 +18933,7 @@
18868
18933
  actions: [{
18869
18934
  text: "编辑",
18870
18935
  handler: (row, index) => {
18871
- fieldValues.value = {
18872
- ...row,
18873
- index
18874
- };
18875
- fieldTitle.value = `编辑${row.title}`;
18876
- calcBoxPosition();
18877
- addDialogVisible.value = true;
18936
+ editField(row, index);
18878
18937
  }
18879
18938
  }, {
18880
18939
  text: "删除",
@@ -19061,6 +19120,20 @@
19061
19120
  const addFromJsonDialogVisible = (0, vue.useModel)(__props, "visible1");
19062
19121
  const { height: editorHeight } = useEditorContentHeight();
19063
19122
  const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(uiService, (0, vue.inject)("parentFloating", (0, vue.ref)(null)));
19123
+ /**
19124
+ * 由 DataSourceConfigPanel 注入:打开数据源详情后需要直接打开的字段路径(字段名数组)。
19125
+ * 当前层消费 path[0],并把剩余路径下发给嵌套字段,实现逐层打开。
19126
+ */
19127
+ const editingFieldPath = (0, vue.inject)("editingDataSourceFieldPath", (0, vue.computed)(() => []));
19128
+ (0, vue.provide)("editingDataSourceFieldPath", (0, vue.computed)(() => editingFieldPath.value.slice(1)));
19129
+ (0, vue.onMounted)(() => {
19130
+ const path = editingFieldPath.value;
19131
+ if (!path.length) return;
19132
+ const fields = props.model[props.name] || [];
19133
+ const index = fields.findIndex((field) => field.name === path[0]);
19134
+ if (index === -1) return;
19135
+ editField(fields[index], index);
19136
+ });
19064
19137
  return (_ctx, _cache) => {
19065
19138
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$36, [
19066
19139
  (0, vue.createVNode)((0, vue.unref)(_tmagic_table.MagicTable), {
@@ -19442,6 +19515,19 @@
19442
19515
  const codeConfig = (0, vue.ref)();
19443
19516
  const codeBlockEditorRef = (0, vue.useTemplateRef)("codeBlockEditor");
19444
19517
  let editIndex = -1;
19518
+ const editMethod = (method, index) => {
19519
+ let codeContent = "({ params, dataSource, app }) => {\n // place your code here\n}";
19520
+ if (method.content) if (typeof method.content !== "string") codeContent = method.content.toString();
19521
+ else codeContent = method.content;
19522
+ codeConfig.value = {
19523
+ ...cloneDeep$1(method),
19524
+ content: codeContent
19525
+ };
19526
+ editIndex = index;
19527
+ (0, vue.nextTick)(() => {
19528
+ codeBlockEditorRef.value?.show();
19529
+ });
19530
+ };
19445
19531
  const methodColumns = [
19446
19532
  {
19447
19533
  label: "名称",
@@ -19466,17 +19552,7 @@
19466
19552
  actions: [{
19467
19553
  text: "编辑",
19468
19554
  handler: (method, index) => {
19469
- let codeContent = "({ params, dataSource, app }) => {\n // place your code here\n}";
19470
- if (method.content) if (typeof method.content !== "string") codeContent = method.content.toString();
19471
- else codeContent = method.content;
19472
- codeConfig.value = {
19473
- ...cloneDeep$1(method),
19474
- content: codeContent
19475
- };
19476
- editIndex = index;
19477
- (0, vue.nextTick)(() => {
19478
- codeBlockEditorRef.value?.show();
19479
- });
19555
+ editMethod(method, index);
19480
19556
  }
19481
19557
  }, {
19482
19558
  text: "删除",
@@ -19524,6 +19600,16 @@
19524
19600
  codeConfig.value = void 0;
19525
19601
  codeBlockEditorRef.value?.hide();
19526
19602
  };
19603
+ /** 由 DataSourceConfigPanel 注入:打开数据源详情后需要直接打开的方法名 */
19604
+ const editingMethodName = (0, vue.inject)("editingDataSourceMethodName", (0, vue.computed)(() => ""));
19605
+ (0, vue.onMounted)(() => {
19606
+ const methodName = editingMethodName.value;
19607
+ if (!methodName) return;
19608
+ const methods = props.model[props.name] || [];
19609
+ const index = methods.findIndex((method) => method.name === methodName);
19610
+ if (index === -1) return;
19611
+ editMethod(methods[index], index);
19612
+ });
19527
19613
  return (_ctx, _cache) => {
19528
19614
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$34, [
19529
19615
  (0, vue.createVNode)((0, vue.unref)(_tmagic_table.MagicTable), {
@@ -19951,7 +20037,7 @@
19951
20037
  const eventBus = (0, vue.inject)("eventBus");
19952
20038
  const emit = __emit;
19953
20039
  const props = __props;
19954
- const hasDataSourceSidePanel = (0, vue.computed)(() => (uiService.get("sideBarItems") || []).find((item) => item.$key === SideItemKey.DATA_SOURCE));
20040
+ const dataSourceSidePanel = (0, vue.computed)(() => (uiService.get("sideBarItems") || []).find((item) => item.$key === SideItemKey.DATA_SOURCE));
19955
20041
  const notEditable = (0, vue.computed)(() => (0, _tmagic_form.filterFunction)(mForm, props.config.notEditable, props));
19956
20042
  /** 对比模式下隐藏查看/编辑操作按钮,仅保留只读展示。 */
19957
20043
  const isCompare = (0, vue.computed)(() => Boolean(mForm?.isCompare));
@@ -20039,9 +20125,11 @@
20039
20125
  emit("change", props.model[props.name], eventData);
20040
20126
  };
20041
20127
  const editCodeHandler = () => {
20042
- const [id] = props.model[props.name];
20128
+ const [id, methodName] = props.model[props.name];
20043
20129
  if (!dataSourceService.getDataSourceById(id)) return;
20044
- eventBus?.emit("edit-data-source", id);
20130
+ const sideBarItem = dataSourceSidePanel.value;
20131
+ if (sideBarItem) uiService.set("sideBarActiveTabName", sideBarItem.text || sideBarItem.$key || SideItemKey.DATA_SOURCE);
20132
+ eventBus?.emit("edit-data-source-method", id, methodName);
20045
20133
  };
20046
20134
  return (_ctx, _cache) => {
20047
20135
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$31, [(0, vue.createElementVNode)("div", _hoisted_2$6, [(0, vue.createVNode)((0, vue.unref)(_tmagic_form.MCascader), {
@@ -20060,7 +20148,7 @@
20060
20148
  "size",
20061
20149
  "disabled",
20062
20150
  "prop"
20063
- ]), __props.model[__props.name] && isCustomMethod.value && hasDataSourceSidePanel.value && !isCompare.value ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
20151
+ ]), __props.model[__props.name] && isCustomMethod.value && dataSourceSidePanel.value && !isCompare.value ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
20064
20152
  key: 0,
20065
20153
  content: notEditable.value ? "查看" : "编辑"
20066
20154
  }, {
@@ -20176,9 +20264,15 @@
20176
20264
  modelValue.value = v;
20177
20265
  emit("change", v);
20178
20266
  };
20179
- const hasDataSourceSidePanel = (0, vue.computed)(() => uiService.get("sideBarItems").find((item) => item.$key === SideItemKey.DATA_SOURCE));
20267
+ const dataSourceSidePanel = (0, vue.computed)(() => uiService.get("sideBarItems").find((item) => item.$key === SideItemKey.DATA_SOURCE));
20268
+ const hasDataSourceSidePanel = (0, vue.computed)(() => dataSourceSidePanel.value);
20180
20269
  const editHandler = (id) => {
20181
- eventBus?.emit("edit-data-source", (0, _tmagic_utils.removeDataSourceFieldPrefix)(id));
20270
+ const sideBarItem = dataSourceSidePanel.value;
20271
+ if (sideBarItem) uiService.set("sideBarActiveTabName", sideBarItem.text || sideBarItem.$key || SideItemKey.DATA_SOURCE);
20272
+ const dataSourceId = (0, _tmagic_utils.removeDataSourceFieldPrefix)(id);
20273
+ const fieldPath = selectFieldsId.value;
20274
+ if (fieldPath.length) eventBus?.emit("edit-data-source-field", dataSourceId, [...fieldPath]);
20275
+ else eventBus?.emit("edit-data-source", dataSourceId);
20182
20276
  };
20183
20277
  return (_ctx, _cache) => {
20184
20278
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$30, [__props.dataSourceId ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicCascader), {
@@ -22397,6 +22491,13 @@
22397
22491
  ] }));
22398
22492
  const selectDirection = (d) => direction.value = d || "";
22399
22493
  const emit = __emit;
22494
+ const props = __props;
22495
+ const hasValue = (value) => value !== void 0 && value !== null && value !== "";
22496
+ const isConfigured = (d) => [
22497
+ "Width",
22498
+ "Color",
22499
+ "Style"
22500
+ ].some((key) => hasValue(props.model?.[`border${d}${key}`]));
22400
22501
  const change = (value, eventData) => {
22401
22502
  eventData.changeRecords?.forEach((record) => {
22402
22503
  emit("change", record.value, { modifyKey: record.propPath });
@@ -22406,25 +22507,40 @@
22406
22507
  return (_ctx, _cache) => {
22407
22508
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$1, [(0, vue.createElementVNode)("div", _hoisted_2, [
22408
22509
  (0, vue.createElementVNode)("div", _hoisted_3, [(0, vue.createElementVNode)("div", {
22409
- class: (0, vue.normalizeClass)(["border-icon border-icon-top", { active: direction.value === "Top" }]),
22510
+ class: (0, vue.normalizeClass)(["border-icon border-icon-top", {
22511
+ active: direction.value === "Top",
22512
+ configured: isConfigured("Top")
22513
+ }]),
22410
22514
  onClick: _cache[0] || (_cache[0] = ($event) => selectDirection("Top"))
22411
22515
  }, null, 2)]),
22412
22516
  (0, vue.createElementVNode)("div", _hoisted_4, [
22413
22517
  (0, vue.createElementVNode)("div", {
22414
- class: (0, vue.normalizeClass)(["border-icon border-icon-left", { active: direction.value === "Left" }]),
22518
+ class: (0, vue.normalizeClass)(["border-icon border-icon-left", {
22519
+ active: direction.value === "Left",
22520
+ configured: isConfigured("Left")
22521
+ }]),
22415
22522
  onClick: _cache[1] || (_cache[1] = ($event) => selectDirection("Left"))
22416
22523
  }, null, 2),
22417
22524
  (0, vue.createElementVNode)("div", {
22418
- class: (0, vue.normalizeClass)(["border-icon", { active: direction.value === "" }]),
22525
+ class: (0, vue.normalizeClass)(["border-icon", {
22526
+ active: direction.value === "",
22527
+ configured: isConfigured("")
22528
+ }]),
22419
22529
  onClick: _cache[2] || (_cache[2] = ($event) => selectDirection())
22420
22530
  }, null, 2),
22421
22531
  (0, vue.createElementVNode)("div", {
22422
- class: (0, vue.normalizeClass)(["border-icon border-icon-right", { active: direction.value === "Right" }]),
22532
+ class: (0, vue.normalizeClass)(["border-icon border-icon-right", {
22533
+ active: direction.value === "Right",
22534
+ configured: isConfigured("Right")
22535
+ }]),
22423
22536
  onClick: _cache[3] || (_cache[3] = ($event) => selectDirection("Right"))
22424
22537
  }, null, 2)
22425
22538
  ]),
22426
22539
  (0, vue.createElementVNode)("div", _hoisted_5, [(0, vue.createElementVNode)("div", {
22427
- class: (0, vue.normalizeClass)(["border-icon border-icon-bottom", { active: direction.value === "Bottom" }]),
22540
+ class: (0, vue.normalizeClass)(["border-icon border-icon-bottom", {
22541
+ active: direction.value === "Bottom",
22542
+ configured: isConfigured("Bottom")
22543
+ }]),
22428
22544
  onClick: _cache[4] || (_cache[4] = ($event) => selectDirection("Bottom"))
22429
22545
  }, null, 2)])
22430
22546
  ]), (0, vue.createElementVNode)("div", _hoisted_6, [(0, vue.createVNode)((0, vue.unref)(_tmagic_form.MContainer), {
@@ -23138,6 +23254,7 @@
23138
23254
  exports.getFormValue = getFormValue;
23139
23255
  exports.getGuideLineFromCache = getGuideLineFromCache;
23140
23256
  exports.getInitPositionStyle = getInitPositionStyle;
23257
+ exports.getLastPushedHistoryIds = getLastPushedHistoryIds;
23141
23258
  exports.getNodeIndex = getNodeIndex;
23142
23259
  exports.getOrCreateStack = getOrCreateStack;
23143
23260
  exports.getPageFragmentList = getPageFragmentList;