@tmagic/editor 1.7.8 → 1.7.10

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 (47) hide show
  1. package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +3 -1
  2. package/dist/es/components/CodeParams.vue_vue_type_script_setup_true_lang.js +17 -7
  3. package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +38 -9
  4. package/dist/es/fields/DataSourceFieldSelect/Index.vue_vue_type_script_setup_true_lang.js +3 -1
  5. package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +38 -5
  6. package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +3 -2
  7. package/dist/es/fields/StyleSetter/Index.vue_vue_type_script_setup_true_lang.js +5 -0
  8. package/dist/es/fields/StyleSetter/pro/Transform.js +5 -0
  9. package/dist/es/fields/StyleSetter/pro/Transform.vue_vue_type_script_setup_true_lang.js +54 -0
  10. package/dist/es/hooks/use-code-block-edit.js +3 -2
  11. package/dist/es/index.js +3 -3
  12. package/dist/es/initService.js +4 -4
  13. package/dist/es/layouts/workspace/viewer/Stage.vue_vue_type_script_setup_true_lang.js +64 -1
  14. package/dist/es/layouts/workspace/viewer/StageOverlay.vue_vue_type_script_setup_true_lang.js +1 -5
  15. package/dist/es/services/editor.js +191 -174
  16. package/dist/es/services/history.js +1 -9
  17. package/dist/es/type.js +28 -1
  18. package/dist/es/utils/dep/worker.js +1 -1
  19. package/dist/es/utils/editor-history.js +100 -0
  20. package/dist/es/utils/editor.js +178 -4
  21. package/dist/es/utils/props.js +4 -1
  22. package/dist/es/utils/undo-redo.js +7 -4
  23. package/dist/tmagic-editor.umd.cjs +744 -234
  24. package/package.json +8 -8
  25. package/src/Editor.vue +1 -0
  26. package/src/components/CodeBlockEditor.vue +1 -1
  27. package/src/components/CodeParams.vue +23 -7
  28. package/src/editorProps.ts +2 -0
  29. package/src/fields/DataSourceFieldSelect/FieldSelect.vue +39 -6
  30. package/src/fields/DataSourceFieldSelect/Index.vue +1 -0
  31. package/src/fields/DataSourceMethodSelect.vue +54 -13
  32. package/src/fields/DataSourceMethods.vue +9 -5
  33. package/src/fields/StyleSetter/Index.vue +5 -1
  34. package/src/fields/StyleSetter/pro/Transform.vue +54 -0
  35. package/src/fields/StyleSetter/pro/index.ts +1 -0
  36. package/src/hooks/use-code-block-edit.ts +8 -4
  37. package/src/initService.ts +6 -6
  38. package/src/layouts/workspace/viewer/Stage.vue +89 -1
  39. package/src/layouts/workspace/viewer/StageOverlay.vue +1 -6
  40. package/src/services/editor.ts +231 -270
  41. package/src/services/history.ts +1 -9
  42. package/src/type.ts +65 -5
  43. package/src/utils/editor-history.ts +138 -0
  44. package/src/utils/editor.ts +249 -4
  45. package/src/utils/props.ts +4 -0
  46. package/src/utils/undo-redo.ts +7 -6
  47. package/types/index.d.ts +179 -53
@@ -141,6 +141,33 @@
141
141
  DragType["LAYER_TREE"] = "layer-tree";
142
142
  return DragType;
143
143
  }({});
144
+ var canUsePluginMethods = {
145
+ async: [
146
+ "getLayout",
147
+ "highlight",
148
+ "select",
149
+ "multiSelect",
150
+ "doAdd",
151
+ "add",
152
+ "doRemove",
153
+ "remove",
154
+ "doUpdate",
155
+ "update",
156
+ "sort",
157
+ "copy",
158
+ "paste",
159
+ "doPaste",
160
+ "doAlignCenter",
161
+ "alignCenter",
162
+ "moveLayer",
163
+ "moveToContainer",
164
+ "dragTo",
165
+ "undo",
166
+ "redo",
167
+ "move"
168
+ ],
169
+ sync: []
170
+ };
144
171
  //#endregion
145
172
  //#region node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/_freeGlobal.js
146
173
  /** Detect free variable `global` from Node.js. */
@@ -4259,8 +4286,9 @@
4259
4286
  _tmagic_design.tMagicMessage.error("获取代码块内容失败");
4260
4287
  return;
4261
4288
  }
4262
- let codeContent = codeBlock.content;
4263
- if (typeof codeContent !== "string") codeContent = codeContent.toString();
4289
+ let codeContent = "";
4290
+ if (codeBlock.content) if (typeof codeBlock.content !== "string") codeContent = codeBlock.content.toString();
4291
+ else codeContent = codeBlock.content;
4264
4292
  codeConfig.value = {
4265
4293
  ...cloneDeep(codeBlock),
4266
4294
  content: codeContent
@@ -4575,7 +4603,10 @@
4575
4603
  "borderWidth",
4576
4604
  "borderStyle",
4577
4605
  "borderColor"
4578
- ] }]
4606
+ ] }, {
4607
+ name: "transform",
4608
+ defaultValue: () => ({})
4609
+ }]
4579
4610
  }]
4580
4611
  };
4581
4612
  var eventTabConfig = {
@@ -4918,7 +4949,7 @@
4918
4949
  elementList;
4919
4950
  listCursor;
4920
4951
  listMaxSize;
4921
- constructor(listMaxSize = 20) {
4952
+ constructor(listMaxSize = 100) {
4922
4953
  const minListMaxSize = 2;
4923
4954
  this.elementList = [];
4924
4955
  this.listCursor = 0;
@@ -4933,20 +4964,23 @@
4933
4964
  }
4934
4965
  }
4935
4966
  canUndo() {
4936
- return this.listCursor > 1;
4967
+ return this.listCursor > 0;
4937
4968
  }
4969
+ /** 返回被撤销的操作 */
4938
4970
  undo() {
4939
4971
  if (!this.canUndo()) return null;
4940
4972
  this.listCursor -= 1;
4941
- return this.getCurrentElement();
4973
+ return cloneDeep(this.elementList[this.listCursor]);
4942
4974
  }
4943
4975
  canRedo() {
4944
4976
  return this.elementList.length > this.listCursor;
4945
4977
  }
4978
+ /** 返回被重做的操作 */
4946
4979
  redo() {
4947
4980
  if (!this.canRedo()) return null;
4981
+ const element = cloneDeep(this.elementList[this.listCursor]);
4948
4982
  this.listCursor += 1;
4949
- return this.getCurrentElement();
4983
+ return element;
4950
4984
  }
4951
4985
  getCurrentElement() {
4952
4986
  if (this.listCursor < 1) return null;
@@ -4978,15 +5012,7 @@
4978
5012
  changePage(page) {
4979
5013
  if (!page) return;
4980
5014
  this.state.pageId = page.id;
4981
- if (!this.state.pageSteps[this.state.pageId]) {
4982
- const undoRedo = new UndoRedo();
4983
- undoRedo.pushElement({
4984
- data: page,
4985
- modifiedNodeIds: /* @__PURE__ */ new Map(),
4986
- nodeId: page.id
4987
- });
4988
- this.state.pageSteps[this.state.pageId] = undoRedo;
4989
- }
5015
+ if (!this.state.pageSteps[this.state.pageId]) this.state.pageSteps[this.state.pageId] = new UndoRedo();
4990
5016
  this.setCanUndoRedo();
4991
5017
  this.emit("page-change", this.state.pageSteps[this.state.pageId]);
4992
5018
  }
@@ -5417,6 +5443,273 @@
5417
5443
  buildChangeRecords(value, basePath);
5418
5444
  return changeRecords;
5419
5445
  };
5446
+ /**
5447
+ * 根据节点配置或ID解析出选中节点信息,并进行合法性校验
5448
+ * @param config 节点配置或节点ID
5449
+ * @param getNodeInfoFn 获取节点信息的回调函数
5450
+ * @param rootId 根节点ID,用于排除根节点被选中
5451
+ * @returns 选中节点的完整信息(node、parent、page)
5452
+ */
5453
+ var resolveSelectedNode = (config, getNodeInfoFn, rootId) => {
5454
+ const id = typeof config === "string" || typeof config === "number" ? config : config.id;
5455
+ if (!id) throw new Error("没有ID,无法选中");
5456
+ const { node, parent, page } = getNodeInfoFn(id);
5457
+ if (!node) throw new Error("获取不到组件信息");
5458
+ if (node.id === rootId) throw new Error("不能选根节点");
5459
+ return {
5460
+ node,
5461
+ parent,
5462
+ page
5463
+ };
5464
+ };
5465
+ /**
5466
+ * 处理节点在 fixed 定位与其他定位之间的切换
5467
+ * 当节点从非 fixed 变为 fixed 时,根据节点路径累加偏移量;反之则还原偏移量
5468
+ * @param dist 更新后的节点配置(目标状态)
5469
+ * @param src 更新前的节点配置(原始状态)
5470
+ * @param root 根节点配置,用于计算节点路径上的偏移量
5471
+ * @param getLayoutFn 获取父节点布局方式的回调函数
5472
+ * @returns 处理后的节点配置(深拷贝)
5473
+ */
5474
+ var toggleFixedPosition = async (dist, src, root, getLayoutFn) => {
5475
+ const newConfig = cloneDeep(dist);
5476
+ if (!(0, _tmagic_utils.isPop)(src) && newConfig.style?.position) {
5477
+ if ((0, _tmagic_stage.isFixed)(newConfig.style) && !(0, _tmagic_stage.isFixed)(src.style || {})) newConfig.style = change2Fixed(newConfig, root);
5478
+ else if (!(0, _tmagic_stage.isFixed)(newConfig.style) && (0, _tmagic_stage.isFixed)(src.style || {})) newConfig.style = await Fixed2Other(newConfig, root, getLayoutFn);
5479
+ }
5480
+ return newConfig;
5481
+ };
5482
+ /**
5483
+ * 根据键盘移动的偏移量计算节点的新样式
5484
+ * 仅对 absolute 或 fixed 定位的节点生效;优先修改 top/left,若未设置则修改 bottom/right
5485
+ * @param style 节点当前样式
5486
+ * @param left 水平方向偏移量(正值向右,负值向左)
5487
+ * @param top 垂直方向偏移量(正值向下,负值向上)
5488
+ * @returns 计算后的新样式对象,若节点不支持移动则返回 null
5489
+ */
5490
+ var calcMoveStyle = (style, left, top) => {
5491
+ if (!style || !["absolute", "fixed"].includes(style.position)) return null;
5492
+ const newStyle = { ...style };
5493
+ if (top) {
5494
+ if ((0, _tmagic_utils.isNumber)(style.top)) {
5495
+ newStyle.top = Number(style.top) + Number(top);
5496
+ newStyle.bottom = "";
5497
+ } else if ((0, _tmagic_utils.isNumber)(style.bottom)) {
5498
+ newStyle.bottom = Number(style.bottom) - Number(top);
5499
+ newStyle.top = "";
5500
+ }
5501
+ }
5502
+ if (left) {
5503
+ if ((0, _tmagic_utils.isNumber)(style.left)) {
5504
+ newStyle.left = Number(style.left) + Number(left);
5505
+ newStyle.right = "";
5506
+ } else if ((0, _tmagic_utils.isNumber)(style.right)) {
5507
+ newStyle.right = Number(style.right) - Number(left);
5508
+ newStyle.left = "";
5509
+ }
5510
+ }
5511
+ return newStyle;
5512
+ };
5513
+ /**
5514
+ * 计算节点水平居中对齐后的样式
5515
+ * 流式布局(relative)下不做处理;优先通过 DOM 元素实际宽度计算,回退到配置中的 width 值
5516
+ * @param node 需要居中的节点配置
5517
+ * @param parent 父容器节点配置
5518
+ * @param layout 当前布局方式
5519
+ * @param doc 画布 document 对象,用于获取 DOM 元素实际宽度
5520
+ * @returns 计算后的新样式对象,若不支持居中则返回 null
5521
+ */
5522
+ var calcAlignCenterStyle = (node, parent, layout, doc) => {
5523
+ if (layout === Layout.RELATIVE || !node.style) return null;
5524
+ const style = { ...node.style };
5525
+ if (doc) {
5526
+ const el = (0, _tmagic_utils.getElById)()(doc, node.id);
5527
+ const parentEl = layout === Layout.FIXED ? doc.body : el?.offsetParent;
5528
+ if (parentEl && el) {
5529
+ style.left = (0, _tmagic_utils.calcValueByFontsize)(doc, (parentEl.clientWidth - el.clientWidth) / 2);
5530
+ style.right = "";
5531
+ }
5532
+ } else if (parent.style && (0, _tmagic_utils.isNumber)(parent.style?.width) && (0, _tmagic_utils.isNumber)(node.style?.width)) {
5533
+ style.left = (parent.style.width - node.style.width) / 2;
5534
+ style.right = "";
5535
+ }
5536
+ return style;
5537
+ };
5538
+ /**
5539
+ * 计算图层移动后的目标索引
5540
+ * 流式布局与绝对定位布局的移动方向相反:流式布局中"上移"对应索引减小,绝对定位中"上移"对应索引增大
5541
+ * @param currentIndex 节点当前在兄弟列表中的索引
5542
+ * @param offset 移动偏移量,支持数值或 LayerOffset.TOP / LayerOffset.BOTTOM
5543
+ * @param brothersLength 兄弟节点总数
5544
+ * @param isRelative 是否为流式布局
5545
+ * @returns 目标索引位置
5546
+ */
5547
+ var calcLayerTargetIndex = (currentIndex, offset, brothersLength, isRelative) => {
5548
+ if (offset === LayerOffset.TOP) return isRelative ? 0 : brothersLength;
5549
+ if (offset === LayerOffset.BOTTOM) return isRelative ? brothersLength : 0;
5550
+ return currentIndex + (isRelative ? -offset : offset);
5551
+ };
5552
+ /**
5553
+ * 节点配置合并策略:用于 mergeWith 的自定义回调
5554
+ * - undefined 且 source 拥有该 key 时返回空字符串
5555
+ * - 原来是数组而新值是对象时,使用新值
5556
+ * - 新值是数组时,直接替换而非逐元素合并
5557
+ */
5558
+ var editorNodeMergeCustomizer = (objValue, srcValue, key, _object, source) => {
5559
+ if (typeof srcValue === "undefined" && Object.hasOwn(source, key)) return "";
5560
+ if (isObject(srcValue) && Array.isArray(objValue)) return srcValue;
5561
+ if (Array.isArray(srcValue)) return srcValue;
5562
+ };
5563
+ /**
5564
+ * 收集复制节点关联的依赖节点,将关联节点追加到 copyNodes 数组中
5565
+ * @param copyNodes 待复制的节点列表(会被就地修改)
5566
+ * @param collectorOptions 依赖收集器配置
5567
+ * @param getNodeById 根据 ID 获取节点的回调函数
5568
+ */
5569
+ var collectRelatedNodes = (copyNodes, collectorOptions, getNodeById) => {
5570
+ const customTarget = new _tmagic_core.Target({ ...collectorOptions });
5571
+ const coperWatcher = new _tmagic_core.Watcher();
5572
+ coperWatcher.addTarget(customTarget);
5573
+ coperWatcher.collect(copyNodes, {}, true, collectorOptions.type);
5574
+ Object.keys(customTarget.deps).forEach((nodeId) => {
5575
+ const node = getNodeById(nodeId);
5576
+ if (!node) return;
5577
+ customTarget.deps[nodeId].keys.forEach((key) => {
5578
+ const relateNodeId = get(node, key);
5579
+ if (!copyNodes.find((n) => n.id === relateNodeId)) {
5580
+ const relateNode = getNodeById(relateNodeId);
5581
+ if (relateNode) copyNodes.push(relateNode);
5582
+ }
5583
+ });
5584
+ });
5585
+ };
5586
+ /**
5587
+ * 对拖拽的节点进行分类:同父容器内移动 vs 跨容器移动
5588
+ * @param configs 被拖拽的节点列表
5589
+ * @param targetParent 目标父容器
5590
+ * @param getNodeInfo 获取节点信息的回调
5591
+ * @returns 分类结果,包含同容器索引列表和跨容器节点列表
5592
+ */
5593
+ var classifyDragSources = (configs, targetParent, getNodeInfo) => {
5594
+ const sameParentIndices = [];
5595
+ const crossParentConfigs = [];
5596
+ for (const config of configs) {
5597
+ const { parent, node: curNode } = getNodeInfo(config.id, false);
5598
+ if (!parent || !curNode) continue;
5599
+ if ((0, _tmagic_utils.getNodePath)(curNode.id, parent.items).some((node) => `${targetParent.id}` === `${node.id}`)) continue;
5600
+ const index = getNodeIndex(curNode.id, parent);
5601
+ if (`${parent.id}` === `${targetParent.id}`) {
5602
+ if (typeof index !== "number" || index === -1) return {
5603
+ sameParentIndices,
5604
+ crossParentConfigs,
5605
+ aborted: true
5606
+ };
5607
+ sameParentIndices.push(index);
5608
+ } else crossParentConfigs.push({
5609
+ config,
5610
+ parent
5611
+ });
5612
+ }
5613
+ return {
5614
+ sameParentIndices,
5615
+ crossParentConfigs,
5616
+ aborted: false
5617
+ };
5618
+ };
5619
+ //#endregion
5620
+ //#region packages/editor/src/utils/editor-history.ts
5621
+ /**
5622
+ * 应用 add 类型的历史操作
5623
+ * reverse=true(撤销):从父节点中移除已添加的节点
5624
+ * reverse=false(重做):重新添加节点到父节点中
5625
+ */
5626
+ async function applyHistoryAddOp(step, reverse, ctx) {
5627
+ const { root, stage } = ctx;
5628
+ if (reverse) for (const node of step.nodes ?? []) {
5629
+ const parent = ctx.getNodeById(step.parentId, false);
5630
+ if (!parent?.items) continue;
5631
+ const idx = getNodeIndex(node.id, parent);
5632
+ if (typeof idx === "number" && idx !== -1) parent.items.splice(idx, 1);
5633
+ await stage?.remove({
5634
+ id: node.id,
5635
+ parentId: parent.id,
5636
+ root: cloneDeep(root)
5637
+ });
5638
+ }
5639
+ else {
5640
+ const parent = ctx.getNodeById(step.parentId, false);
5641
+ if (parent?.items) for (const node of step.nodes ?? []) {
5642
+ const idx = step.indexMap?.[node.id] ?? parent.items.length;
5643
+ parent.items.splice(idx, 0, cloneDeep(node));
5644
+ await stage?.add({
5645
+ config: cloneDeep(node),
5646
+ parent: cloneDeep(parent),
5647
+ parentId: parent.id,
5648
+ root: cloneDeep(root)
5649
+ });
5650
+ }
5651
+ }
5652
+ }
5653
+ /**
5654
+ * 应用 remove 类型的历史操作
5655
+ * reverse=true(撤销):将已删除的节点按原位置重新插入
5656
+ * reverse=false(重做):再次删除节点
5657
+ */
5658
+ async function applyHistoryRemoveOp(step, reverse, ctx) {
5659
+ const { root, stage } = ctx;
5660
+ if (reverse) {
5661
+ const sorted = [...step.removedItems ?? []].sort((a, b) => a.index - b.index);
5662
+ for (const { node, parentId, index } of sorted) {
5663
+ const parent = ctx.getNodeById(parentId, false);
5664
+ if (!parent?.items) continue;
5665
+ parent.items.splice(index, 0, cloneDeep(node));
5666
+ await stage?.add({
5667
+ config: cloneDeep(node),
5668
+ parent: cloneDeep(parent),
5669
+ parentId,
5670
+ root: cloneDeep(root)
5671
+ });
5672
+ }
5673
+ } else for (const { node, parentId } of step.removedItems ?? []) {
5674
+ const parent = ctx.getNodeById(parentId, false);
5675
+ if (!parent?.items) continue;
5676
+ const idx = getNodeIndex(node.id, parent);
5677
+ if (typeof idx === "number" && idx !== -1) parent.items.splice(idx, 1);
5678
+ await stage?.remove({
5679
+ id: node.id,
5680
+ parentId,
5681
+ root: cloneDeep(root)
5682
+ });
5683
+ }
5684
+ }
5685
+ /**
5686
+ * 应用 update 类型的历史操作
5687
+ * reverse=true(撤销):将节点恢复为 oldNode
5688
+ * reverse=false(重做):将节点更新为 newNode
5689
+ */
5690
+ async function applyHistoryUpdateOp(step, reverse, ctx) {
5691
+ const { root, stage } = ctx;
5692
+ const items = step.updatedItems ?? [];
5693
+ for (const { oldNode, newNode } of items) {
5694
+ const config = reverse ? oldNode : newNode;
5695
+ if (config.type === _tmagic_core.NodeType.ROOT) {
5696
+ ctx.setRoot(cloneDeep(config));
5697
+ continue;
5698
+ }
5699
+ const info = ctx.getNodeInfo(config.id, false);
5700
+ if (!info.parent) continue;
5701
+ const idx = getNodeIndex(config.id, info.parent);
5702
+ if (typeof idx !== "number" || idx === -1) continue;
5703
+ info.parent.items[idx] = cloneDeep(config);
5704
+ if ((0, _tmagic_utils.isPage)(config) || (0, _tmagic_utils.isPageFragment)(config)) ctx.setPage(config);
5705
+ }
5706
+ const curPage = ctx.getPage();
5707
+ if (stage && curPage) await stage.update({
5708
+ config: cloneDeep((0, vue.toRaw)(curPage)),
5709
+ parentId: root.id,
5710
+ root: cloneDeep((0, vue.toRaw)(root))
5711
+ });
5712
+ }
5420
5713
  //#endregion
5421
5714
  //#region packages/editor/src/utils/operator.ts
5422
5715
  /**
@@ -5484,33 +5777,6 @@
5484
5777
  };
5485
5778
  //#endregion
5486
5779
  //#region packages/editor/src/services/editor.ts
5487
- var canUsePluginMethods$5 = {
5488
- async: [
5489
- "getLayout",
5490
- "highlight",
5491
- "select",
5492
- "multiSelect",
5493
- "doAdd",
5494
- "add",
5495
- "doRemove",
5496
- "remove",
5497
- "doUpdate",
5498
- "update",
5499
- "sort",
5500
- "copy",
5501
- "paste",
5502
- "doPaste",
5503
- "doAlignCenter",
5504
- "alignCenter",
5505
- "moveLayer",
5506
- "moveToContainer",
5507
- "dragTo",
5508
- "undo",
5509
- "redo",
5510
- "move"
5511
- ],
5512
- sync: []
5513
- };
5514
5780
  var Editor = class extends BaseService_default {
5515
5781
  state = (0, vue.reactive)({
5516
5782
  root: null,
@@ -5527,8 +5793,9 @@
5527
5793
  disabledMultiSelect: false
5528
5794
  });
5529
5795
  isHistoryStateChange = false;
5796
+ selectionBeforeOp = null;
5530
5797
  constructor() {
5531
- super(canUsePluginMethods$5.async.map((methodName) => ({
5798
+ super(canUsePluginMethods.async.map((methodName) => ({
5532
5799
  name: methodName,
5533
5800
  isAsync: true
5534
5801
  })), [
@@ -5722,6 +5989,7 @@
5722
5989
  * @returns 添加后的节点
5723
5990
  */
5724
5991
  async add(addNode, parent) {
5992
+ this.captureSelectionBeforeOp();
5725
5993
  const stage = this.get("stage");
5726
5994
  const addNodes = [];
5727
5995
  if (!Array.isArray(addNode)) {
@@ -5746,7 +6014,20 @@
5746
6014
  else if ((0, _tmagic_utils.isPageFragment)(newNodes[0])) this.state.pageFragmentLength += 1;
5747
6015
  else stage?.select(newNodes[0].id);
5748
6016
  }
5749
- if (!((0, _tmagic_utils.isPage)(newNodes[0]) || (0, _tmagic_utils.isPageFragment)(newNodes[0]))) this.pushHistoryState();
6017
+ if (!((0, _tmagic_utils.isPage)(newNodes[0]) || (0, _tmagic_utils.isPageFragment)(newNodes[0]))) {
6018
+ const pageForOp = this.getNodeInfo(newNodes[0].id, false).page;
6019
+ this.pushOpHistory("add", {
6020
+ nodes: newNodes.map((n) => cloneDeep((0, vue.toRaw)(n))),
6021
+ parentId: (this.getParentById(newNodes[0].id, false) ?? this.get("root")).id,
6022
+ indexMap: Object.fromEntries(newNodes.map((n) => {
6023
+ const p = this.getParentById(n.id, false);
6024
+ return [n.id, p ? getNodeIndex(n.id, p) : -1];
6025
+ }))
6026
+ }, {
6027
+ name: pageForOp?.name || "",
6028
+ id: pageForOp.id
6029
+ });
6030
+ }
5750
6031
  this.emit("add", newNodes);
5751
6032
  return Array.isArray(addNode) ? newNodes : newNodes[0];
5752
6033
  }
@@ -5795,9 +6076,27 @@
5795
6076
  * @param {Object} node
5796
6077
  */
5797
6078
  async remove(nodeOrNodeList) {
6079
+ this.captureSelectionBeforeOp();
5798
6080
  const nodes = Array.isArray(nodeOrNodeList) ? nodeOrNodeList : [nodeOrNodeList];
6081
+ const removedItems = [];
6082
+ let pageForOp = null;
6083
+ if (!((0, _tmagic_utils.isPage)(nodes[0]) || (0, _tmagic_utils.isPageFragment)(nodes[0]))) for (const n of nodes) {
6084
+ const { parent, node: curNode, page } = this.getNodeInfo(n.id, false);
6085
+ if (parent && curNode) {
6086
+ if (!pageForOp && page) pageForOp = {
6087
+ name: page.name || "",
6088
+ id: page.id
6089
+ };
6090
+ const idx = getNodeIndex(curNode.id, parent);
6091
+ removedItems.push({
6092
+ node: cloneDeep((0, vue.toRaw)(curNode)),
6093
+ parentId: parent.id,
6094
+ index: typeof idx === "number" ? idx : -1
6095
+ });
6096
+ }
6097
+ }
5799
6098
  await Promise.all(nodes.map((node) => this.doRemove(node)));
5800
- if (!((0, _tmagic_utils.isPage)(nodes[0]) || (0, _tmagic_utils.isPageFragment)(nodes[0]))) this.pushHistoryState();
6099
+ if (removedItems.length > 0 && pageForOp) this.pushOpHistory("remove", { removedItems }, pageForOp);
5801
6100
  this.emit("remove", nodes);
5802
6101
  }
5803
6102
  async doUpdate(config, { changeRecords = [] } = {}) {
@@ -5807,12 +6106,8 @@
5807
6106
  const info = this.getNodeInfo(config.id, false);
5808
6107
  if (!info.node) throw new Error(`获取不到id为${config.id}的节点`);
5809
6108
  const node = (0, vue.toRaw)(info.node);
5810
- let newConfig = await this.toggleFixedPosition((0, vue.toRaw)(config), node, root);
5811
- newConfig = mergeWith(cloneDeep(node), newConfig, (objValue, srcValue, key, object, source) => {
5812
- if (typeof srcValue === "undefined" && Object.hasOwn(source, key)) return "";
5813
- if (isObject(srcValue) && Array.isArray(objValue)) return srcValue;
5814
- if (Array.isArray(srcValue)) return srcValue;
5815
- });
6109
+ let newConfig = await toggleFixedPosition((0, vue.toRaw)(config), node, root, this.getLayout);
6110
+ newConfig = mergeWith(cloneDeep(node), newConfig, editorNodeMergeCustomizer);
5816
6111
  if (!newConfig.type) throw new Error("配置缺少type值");
5817
6112
  if (newConfig.type === _tmagic_core.NodeType.ROOT) {
5818
6113
  this.set("root", newConfig);
@@ -5850,9 +6145,23 @@
5850
6145
  * @returns 更新后的节点配置
5851
6146
  */
5852
6147
  async update(config, data = {}) {
6148
+ this.captureSelectionBeforeOp();
5853
6149
  const nodes = Array.isArray(config) ? config : [config];
5854
6150
  const updateData = await Promise.all(nodes.map((node) => this.doUpdate(node, data)));
5855
- if (updateData[0].oldNode?.type !== _tmagic_core.NodeType.ROOT) this.pushHistoryState();
6151
+ if (updateData[0].oldNode?.type !== _tmagic_core.NodeType.ROOT) {
6152
+ const curNodes = this.get("nodes");
6153
+ if (!this.isHistoryStateChange && curNodes.length) {
6154
+ const pageForOp = this.getNodeInfo(nodes[0].id, false).page;
6155
+ this.pushOpHistory("update", { updatedItems: updateData.map((d) => ({
6156
+ oldNode: cloneDeep(d.oldNode),
6157
+ newNode: cloneDeep((0, vue.toRaw)(d.newNode))
6158
+ })) }, {
6159
+ name: pageForOp?.name || "",
6160
+ id: pageForOp.id
6161
+ });
6162
+ }
6163
+ this.isHistoryStateChange = false;
6164
+ }
5856
6165
  this.emit("update", updateData);
5857
6166
  return Array.isArray(config) ? updateData.map((item) => item.newNode) : updateData[0].newNode;
5858
6167
  }
@@ -5863,6 +6172,7 @@
5863
6172
  * @returns void
5864
6173
  */
5865
6174
  async sort(id1, id2) {
6175
+ this.captureSelectionBeforeOp();
5866
6176
  const root = this.get("root");
5867
6177
  if (!root) throw new Error("root为空");
5868
6178
  const node = this.get("node");
@@ -5880,8 +6190,6 @@
5880
6190
  parentId: parent.id,
5881
6191
  root: cloneDeep(root)
5882
6192
  });
5883
- this.addModifiedNodeId(parent.id);
5884
- this.pushHistoryState();
5885
6193
  }
5886
6194
  /**
5887
6195
  * 将组件节点配置存储到localStorage中
@@ -5898,23 +6206,7 @@
5898
6206
  */
5899
6207
  copyWithRelated(config, collectorOptions) {
5900
6208
  const copyNodes = Array.isArray(config) ? config : [config];
5901
- if (collectorOptions && typeof collectorOptions.isTarget === "function") {
5902
- const customTarget = new _tmagic_core.Target({ ...collectorOptions });
5903
- const coperWatcher = new _tmagic_core.Watcher();
5904
- coperWatcher.addTarget(customTarget);
5905
- coperWatcher.collect(copyNodes, {}, true, collectorOptions.type);
5906
- Object.keys(customTarget.deps).forEach((nodeId) => {
5907
- const node = this.getNodeById(nodeId);
5908
- if (!node) return;
5909
- customTarget.deps[nodeId].keys.forEach((key) => {
5910
- const relateNodeId = get(node, key);
5911
- if (!copyNodes.find((node) => node.id === relateNodeId)) {
5912
- const relateNode = this.getNodeById(relateNodeId);
5913
- if (relateNode) copyNodes.push(relateNode);
5914
- }
5915
- });
5916
- });
5917
- }
6209
+ if (collectorOptions && typeof collectorOptions.isTarget === "function") collectRelatedNodes(copyNodes, collectorOptions, (id) => this.getNodeById(id));
5918
6210
  storage_default.setItem(COPY_STORAGE_KEY, copyNodes, { protocol: Protocol.OBJECT });
5919
6211
  }
5920
6212
  /**
@@ -5945,20 +6237,10 @@
5945
6237
  if (!parent) throw new Error("找不到父节点");
5946
6238
  const node = cloneDeep((0, vue.toRaw)(config));
5947
6239
  const layout = await this.getLayout(parent, node);
5948
- if (layout === Layout.RELATIVE) return config;
5949
- if (!node.style) return config;
5950
6240
  const doc = this.get("stage")?.renderer?.contentWindow?.document;
5951
- if (doc) {
5952
- const el = (0, _tmagic_utils.getElById)()(doc, node.id);
5953
- const parentEl = layout === Layout.FIXED ? doc.body : el?.offsetParent;
5954
- if (parentEl && el) {
5955
- node.style.left = (0, _tmagic_utils.calcValueByFontsize)(doc, (parentEl.clientWidth - el.clientWidth) / 2);
5956
- node.style.right = "";
5957
- }
5958
- } else if (parent.style && (0, _tmagic_utils.isNumber)(parent.style?.width) && (0, _tmagic_utils.isNumber)(node.style?.width)) {
5959
- node.style.left = (parent.style.width - node.style.width) / 2;
5960
- node.style.right = "";
5961
- }
6241
+ const newStyle = calcAlignCenterStyle(node, parent, layout, doc);
6242
+ if (!newStyle) return config;
6243
+ node.style = newStyle;
5962
6244
  return node;
5963
6245
  }
5964
6246
  /**
@@ -5980,6 +6262,7 @@
5980
6262
  * @param offset 偏移量
5981
6263
  */
5982
6264
  async moveLayer(offset) {
6265
+ this.captureSelectionBeforeOp();
5983
6266
  const root = this.get("root");
5984
6267
  if (!root) throw new Error("root为空");
5985
6268
  const parent = this.get("parent");
@@ -5989,11 +6272,9 @@
5989
6272
  const brothers = parent.items || [];
5990
6273
  const index = brothers.findIndex((item) => `${item.id}` === `${node?.id}`);
5991
6274
  const isRelative = await this.getLayout(parent, node) === Layout.RELATIVE;
5992
- let offsetIndex;
5993
- if (offset === LayerOffset.TOP) offsetIndex = isRelative ? 0 : brothers.length;
5994
- else if (offset === LayerOffset.BOTTOM) offsetIndex = isRelative ? brothers.length : 0;
5995
- else offsetIndex = index + (isRelative ? -offset : offset);
6275
+ const offsetIndex = calcLayerTargetIndex(index, offset, brothers.length, isRelative);
5996
6276
  if (offsetIndex > 0 && offsetIndex > brothers.length || offsetIndex < 0) return;
6277
+ const oldParent = cloneDeep((0, vue.toRaw)(parent));
5997
6278
  brothers.splice(index, 1);
5998
6279
  brothers.splice(offsetIndex, 0, node);
5999
6280
  const grandparent = this.getParentById(parent.id);
@@ -6003,7 +6284,14 @@
6003
6284
  root: cloneDeep(root)
6004
6285
  });
6005
6286
  this.addModifiedNodeId(parent.id);
6006
- this.pushHistoryState();
6287
+ const pageForOp = this.getNodeInfo(node.id, false).page;
6288
+ this.pushOpHistory("update", { updatedItems: [{
6289
+ oldNode: oldParent,
6290
+ newNode: cloneDeep((0, vue.toRaw)(parent))
6291
+ }] }, {
6292
+ name: pageForOp?.name || "",
6293
+ id: pageForOp.id
6294
+ });
6007
6295
  this.emit("move-layer", offset);
6008
6296
  }
6009
6297
  /**
@@ -6012,11 +6300,14 @@
6012
6300
  * @param targetId 容器ID
6013
6301
  */
6014
6302
  async moveToContainer(config, targetId) {
6303
+ this.captureSelectionBeforeOp();
6015
6304
  const root = this.get("root");
6016
- const { node, parent } = this.getNodeInfo(config.id, false);
6305
+ const { node, parent, page: pageForOp } = this.getNodeInfo(config.id, false);
6017
6306
  const target = this.getNodeById(targetId, false);
6018
6307
  const stage = this.get("stage");
6019
6308
  if (root && node && parent && stage) {
6309
+ const oldSourceParent = cloneDeep((0, vue.toRaw)(parent));
6310
+ const oldTarget = cloneDeep((0, vue.toRaw)(target));
6020
6311
  const index = getNodeIndex(node.id, parent);
6021
6312
  parent.items?.splice(index, 1);
6022
6313
  await stage.remove({
@@ -6041,36 +6332,42 @@
6041
6332
  stage.select(newConfig.id);
6042
6333
  this.addModifiedNodeId(target.id);
6043
6334
  this.addModifiedNodeId(parent.id);
6044
- this.pushHistoryState();
6335
+ this.pushOpHistory("update", { updatedItems: [{
6336
+ oldNode: oldSourceParent,
6337
+ newNode: cloneDeep((0, vue.toRaw)(parent))
6338
+ }, {
6339
+ oldNode: oldTarget,
6340
+ newNode: cloneDeep((0, vue.toRaw)(target))
6341
+ }] }, {
6342
+ name: pageForOp?.name || "",
6343
+ id: pageForOp.id
6344
+ });
6045
6345
  return newConfig;
6046
6346
  }
6047
6347
  }
6048
6348
  async dragTo(config, targetParent, targetIndex) {
6349
+ this.captureSelectionBeforeOp();
6049
6350
  if (!targetParent || !Array.isArray(targetParent.items)) return;
6050
6351
  const configs = Array.isArray(config) ? config : [config];
6051
- const sourceIndicesInTargetParent = [];
6052
- const sourceOutTargetParent = [];
6352
+ const beforeSnapshots = /* @__PURE__ */ new Map();
6353
+ for (const cfg of configs) {
6354
+ const { parent } = this.getNodeInfo(cfg.id, false);
6355
+ if (parent && !beforeSnapshots.has(`${parent.id}`)) beforeSnapshots.set(`${parent.id}`, cloneDeep((0, vue.toRaw)(parent)));
6356
+ }
6357
+ if (!beforeSnapshots.has(`${targetParent.id}`)) beforeSnapshots.set(`${targetParent.id}`, cloneDeep((0, vue.toRaw)(targetParent)));
6053
6358
  const newLayout = await this.getLayout(targetParent);
6054
- forConfigs: for (const config of configs) {
6055
- const { parent, node: curNode } = this.getNodeInfo(config.id, false);
6056
- if (!parent || !curNode) continue;
6057
- const path = (0, _tmagic_utils.getNodePath)(curNode.id, parent.items);
6058
- for (const node of path) if (targetParent.id === node.id) continue forConfigs;
6059
- const index = getNodeIndex(curNode.id, parent);
6060
- if (parent.id === targetParent.id) {
6061
- if (typeof index !== "number" || index === -1) return;
6062
- sourceIndicesInTargetParent.push(index);
6063
- } else {
6064
- if (newLayout !== await this.getLayout(parent)) setLayout(config, newLayout);
6065
- parent.items?.splice(index, 1);
6066
- sourceOutTargetParent.push(config);
6067
- this.addModifiedNodeId(parent.id);
6068
- }
6359
+ const { sameParentIndices, crossParentConfigs, aborted } = classifyDragSources(configs, targetParent, (id, raw) => this.getNodeInfo(id, raw));
6360
+ if (aborted) return;
6361
+ for (const { config: crossConfig, parent } of crossParentConfigs) {
6362
+ if (newLayout !== await this.getLayout(parent)) setLayout(crossConfig, newLayout);
6363
+ const index = getNodeIndex(crossConfig.id, parent);
6364
+ parent.items?.splice(index, 1);
6365
+ this.addModifiedNodeId(parent.id);
6069
6366
  }
6070
- moveItemsInContainer(sourceIndicesInTargetParent, targetParent, targetIndex);
6071
- sourceOutTargetParent.forEach((config, index) => {
6072
- targetParent.items?.splice(targetIndex + index, 0, config);
6073
- this.addModifiedNodeId(config.id);
6367
+ moveItemsInContainer(sameParentIndices, targetParent, targetIndex);
6368
+ crossParentConfigs.forEach(({ config: crossConfig }, index) => {
6369
+ targetParent.items?.splice(targetIndex + index, 0, crossConfig);
6370
+ this.addModifiedNodeId(crossConfig.id);
6074
6371
  });
6075
6372
  const page = this.get("page");
6076
6373
  const root = this.get("root");
@@ -6080,7 +6377,19 @@
6080
6377
  parentId: root.id,
6081
6378
  root: cloneDeep(root)
6082
6379
  });
6083
- this.pushHistoryState();
6380
+ const updatedItems = [];
6381
+ for (const oldNode of beforeSnapshots.values()) {
6382
+ const newNode = this.getNodeById(oldNode.id, false);
6383
+ if (newNode) updatedItems.push({
6384
+ oldNode,
6385
+ newNode: cloneDeep((0, vue.toRaw)(newNode))
6386
+ });
6387
+ }
6388
+ const pageForOp = this.getNodeInfo(configs[0].id, false).page;
6389
+ this.pushOpHistory("update", { updatedItems }, {
6390
+ name: pageForOp?.name || "",
6391
+ id: pageForOp.id
6392
+ });
6084
6393
  this.emit("drag-to", {
6085
6394
  targetIndex,
6086
6395
  configs,
@@ -6089,56 +6398,32 @@
6089
6398
  }
6090
6399
  /**
6091
6400
  * 撤销当前操作
6092
- * @returns 上一次数据
6401
+ * @returns 被撤销的操作
6093
6402
  */
6094
6403
  async undo() {
6095
6404
  const value = history_default.undo();
6096
- await this.changeHistoryState(value);
6405
+ if (value) await this.applyHistoryOp(value, true);
6097
6406
  return value;
6098
6407
  }
6099
6408
  /**
6100
6409
  * 恢复到下一步
6101
- * @returns 下一步数据
6410
+ * @returns 被恢复的操作
6102
6411
  */
6103
6412
  async redo() {
6104
6413
  const value = history_default.redo();
6105
- await this.changeHistoryState(value);
6414
+ if (value) await this.applyHistoryOp(value, false);
6106
6415
  return value;
6107
6416
  }
6108
6417
  async move(left, top) {
6109
6418
  const node = (0, vue.toRaw)(this.get("node"));
6110
6419
  if (!node || (0, _tmagic_utils.isPage)(node)) return;
6111
- const { style, id, type } = node;
6112
- if (!style || !["absolute", "fixed"].includes(style.position)) return;
6113
- const update = (style) => this.update({
6114
- id,
6115
- type,
6116
- style
6420
+ const newStyle = calcMoveStyle(node.style || {}, left, top);
6421
+ if (!newStyle) return;
6422
+ await this.update({
6423
+ id: node.id,
6424
+ type: node.type,
6425
+ style: newStyle
6117
6426
  });
6118
- if (top) {
6119
- if ((0, _tmagic_utils.isNumber)(style.top)) update({
6120
- ...style,
6121
- top: Number(style.top) + Number(top),
6122
- bottom: ""
6123
- });
6124
- else if ((0, _tmagic_utils.isNumber)(style.bottom)) update({
6125
- ...style,
6126
- bottom: Number(style.bottom) - Number(top),
6127
- top: ""
6128
- });
6129
- }
6130
- if (left) {
6131
- if ((0, _tmagic_utils.isNumber)(style.left)) update({
6132
- ...style,
6133
- left: Number(style.left) + Number(left),
6134
- right: ""
6135
- });
6136
- else if ((0, _tmagic_utils.isNumber)(style.right)) update({
6137
- ...style,
6138
- right: Number(style.right) - Number(left),
6139
- left: ""
6140
- });
6141
- }
6142
6427
  }
6143
6428
  resetState() {
6144
6429
  this.set("root", null);
@@ -6174,50 +6459,74 @@
6174
6459
  addModifiedNodeId(id) {
6175
6460
  if (!this.isHistoryStateChange) this.get("modifiedNodeIds").set(id, id);
6176
6461
  }
6177
- pushHistoryState() {
6178
- const curNode = cloneDeep((0, vue.toRaw)(this.get("node")));
6179
- const page = this.get("page");
6180
- if (!this.isHistoryStateChange && curNode && page) history_default.push({
6181
- data: cloneDeep((0, vue.toRaw)(page)),
6182
- modifiedNodeIds: this.get("modifiedNodeIds"),
6183
- nodeId: curNode.id
6184
- });
6462
+ captureSelectionBeforeOp() {
6463
+ if (this.isHistoryStateChange || this.selectionBeforeOp) return;
6464
+ this.selectionBeforeOp = this.get("nodes").map((n) => n.id);
6465
+ }
6466
+ pushOpHistory(opType, extra, pageData) {
6467
+ if (this.isHistoryStateChange) {
6468
+ this.selectionBeforeOp = null;
6469
+ return;
6470
+ }
6471
+ const step = {
6472
+ data: pageData,
6473
+ opType,
6474
+ selectedBefore: this.selectionBeforeOp ?? [],
6475
+ selectedAfter: this.get("nodes").map((n) => n.id),
6476
+ modifiedNodeIds: new Map(this.get("modifiedNodeIds")),
6477
+ ...extra
6478
+ };
6479
+ history_default.push(step);
6480
+ this.selectionBeforeOp = null;
6185
6481
  this.isHistoryStateChange = false;
6186
6482
  }
6187
- async changeHistoryState(value) {
6188
- if (!value) return;
6483
+ /**
6484
+ * 应用历史操作(撤销 / 重做)
6485
+ * @param step 操作记录
6486
+ * @param reverse true = 撤销,false = 重做
6487
+ */
6488
+ async applyHistoryOp(step, reverse) {
6189
6489
  this.isHistoryStateChange = true;
6190
- await this.update(value.data);
6191
- this.set("modifiedNodeIds", value.modifiedNodeIds);
6192
- setTimeout(() => {
6193
- if (!value.nodeId) return;
6194
- this.select(value.nodeId).then(() => {
6195
- this.get("stage")?.select(value.nodeId);
6196
- });
6197
- }, 0);
6198
- this.emit("history-change", value.data);
6199
- }
6200
- async toggleFixedPosition(dist, src, root) {
6201
- const newConfig = cloneDeep(dist);
6202
- if (!(0, _tmagic_utils.isPop)(src) && newConfig.style?.position) {
6203
- if ((0, _tmagic_stage.isFixed)(newConfig.style) && !(0, _tmagic_stage.isFixed)(src.style || {})) newConfig.style = change2Fixed(newConfig, root);
6204
- else if (!(0, _tmagic_stage.isFixed)(newConfig.style) && (0, _tmagic_stage.isFixed)(src.style || {})) newConfig.style = await Fixed2Other(newConfig, root, this.getLayout);
6490
+ const root = this.get("root");
6491
+ const stage = this.get("stage");
6492
+ if (!root) return;
6493
+ const ctx = {
6494
+ root,
6495
+ stage,
6496
+ getNodeById: (id, raw) => this.getNodeById(id, raw),
6497
+ getNodeInfo: (id, raw) => this.getNodeInfo(id, raw),
6498
+ setRoot: (r) => this.set("root", r),
6499
+ setPage: (p) => this.set("page", p),
6500
+ getPage: () => this.get("page")
6501
+ };
6502
+ switch (step.opType) {
6503
+ case "add":
6504
+ await applyHistoryAddOp(step, reverse, ctx);
6505
+ break;
6506
+ case "remove":
6507
+ await applyHistoryRemoveOp(step, reverse, ctx);
6508
+ break;
6509
+ case "update":
6510
+ await applyHistoryUpdateOp(step, reverse, ctx);
6511
+ break;
6205
6512
  }
6206
- return newConfig;
6513
+ this.set("modifiedNodeIds", step.modifiedNodeIds);
6514
+ const page = (0, vue.toRaw)(this.get("page"));
6515
+ if (page) {
6516
+ const selectIds = reverse ? step.selectedBefore : step.selectedAfter;
6517
+ setTimeout(() => {
6518
+ if (!selectIds.length) return;
6519
+ if (selectIds.length > 1) {
6520
+ this.multiSelect(selectIds);
6521
+ stage?.multiSelect(selectIds);
6522
+ } else this.select(selectIds[0]).then(() => stage?.select(selectIds[0])).catch(() => {});
6523
+ }, 0);
6524
+ this.emit("history-change", page);
6525
+ }
6526
+ this.isHistoryStateChange = false;
6207
6527
  }
6208
6528
  selectedConfigExceptionHandler(config) {
6209
- let id;
6210
- if (typeof config === "string" || typeof config === "number") id = config;
6211
- else id = config.id;
6212
- if (!id) throw new Error("没有ID,无法选中");
6213
- const { node, parent, page } = this.getNodeInfo(id);
6214
- if (!node) throw new Error("获取不到组件信息");
6215
- if (node.id === this.state.root?.id) throw new Error("不能选根节点");
6216
- return {
6217
- node,
6218
- parent,
6219
- page
6220
- };
6529
+ return resolveSelectedNode(config, (id) => this.getNodeInfo(id), this.state.root?.id);
6221
6530
  }
6222
6531
  };
6223
6532
  var editor_default = new Editor();
@@ -6275,13 +6584,13 @@
6275
6584
  top: 0
6276
6585
  }
6277
6586
  });
6278
- var canUsePluginMethods$4 = {
6587
+ var canUsePluginMethods$5 = {
6279
6588
  async: ["zoom", "calcZoom"],
6280
6589
  sync: []
6281
6590
  };
6282
6591
  var Ui = class extends BaseService_default {
6283
6592
  constructor() {
6284
- super(canUsePluginMethods$4.async.map((methodName) => ({
6593
+ super(canUsePluginMethods$5.async.map((methodName) => ({
6285
6594
  name: methodName,
6286
6595
  isAsync: true
6287
6596
  })));
@@ -11899,11 +12208,7 @@
11899
12208
  const frameworkRect = (0, vue.computed)(() => uiService.get("frameworkRect"));
11900
12209
  const style = (0, vue.computed)(() => ({ transform: `scale(${zoom.value})` }));
11901
12210
  (0, vue.watch)(stage, (stage) => {
11902
- if (stage) stage.on("dblclick", async (event) => {
11903
- const el = await stage.actionManager?.getElementFromPoint(event) || null;
11904
- stageOverlayService.openOverlay(el);
11905
- });
11906
- else stageOverlayService.closeOverlay();
12211
+ if (!stage) stageOverlayService.closeOverlay();
11907
12212
  });
11908
12213
  (0, vue.watch)(zoom, (zoom) => {
11909
12214
  const stage = stageOverlayService.get("stage");
@@ -12148,7 +12453,7 @@
12148
12453
  const props = __props;
12149
12454
  let stage = null;
12150
12455
  let runtime = null;
12151
- const { editorService, uiService, keybindingService } = useServices();
12456
+ const { editorService, uiService, keybindingService, stageOverlayService } = useServices();
12152
12457
  const stageLoading = (0, vue.computed)(() => editorService.get("stageLoading"));
12153
12458
  const stageWrapRef = (0, vue.useTemplateRef)("stageWrap");
12154
12459
  const stageContainerEl = (0, vue.useTemplateRef)("stageContainer");
@@ -12161,6 +12466,42 @@
12161
12466
  const page = (0, vue.computed)(() => editorService.get("page"));
12162
12467
  const zoom = (0, vue.computed)(() => uiService.get("zoom"));
12163
12468
  const node = (0, vue.computed)(() => editorService.get("node"));
12469
+ /**
12470
+ * 判断元素是否被非页面级的滚动容器裁剪(未完整显示)
12471
+ *
12472
+ * 从元素向上遍历祖先节点,跳过页面/页面片容器,
12473
+ * 检查是否存在设置了 overflow 的滚动容器将该元素裁剪,
12474
+ * 只有元素未被完整显示时才需要打开 overlay 以展示完整内容
12475
+ */
12476
+ const isClippedByScrollContainer = (el) => {
12477
+ const win = el.ownerDocument.defaultView;
12478
+ if (!win) return false;
12479
+ const root = editorService.get("root");
12480
+ const pageIds = new Set(root?.items?.map((item) => `${item.id}`) ?? []);
12481
+ const elId = (0, _tmagic_utils.getIdFromEl)()(el);
12482
+ if (elId && pageIds.has(elId)) return false;
12483
+ let parent = el.parentElement;
12484
+ while (parent && parent !== el.ownerDocument.documentElement) {
12485
+ const parentId = (0, _tmagic_utils.getIdFromEl)()(parent);
12486
+ if (parentId && pageIds.has(parentId)) return false;
12487
+ const { overflowX, overflowY } = win.getComputedStyle(parent);
12488
+ if ([
12489
+ "auto",
12490
+ "scroll",
12491
+ "hidden"
12492
+ ].includes(overflowX) || [
12493
+ "auto",
12494
+ "scroll",
12495
+ "hidden"
12496
+ ].includes(overflowY) || parent.scrollWidth > parent.clientWidth || parent.scrollHeight > parent.clientHeight) {
12497
+ const elRect = el.getBoundingClientRect();
12498
+ const containerRect = parent.getBoundingClientRect();
12499
+ if (elRect.top < containerRect.top || elRect.left < containerRect.left || elRect.bottom > containerRect.bottom || elRect.right > containerRect.right) return true;
12500
+ }
12501
+ parent = parent.parentElement;
12502
+ }
12503
+ return false;
12504
+ };
12164
12505
  (0, vue.watchEffect)(() => {
12165
12506
  if (stage || !page.value) return;
12166
12507
  if (!stageContainerEl.value) return;
@@ -12169,6 +12510,33 @@
12169
12510
  stage.on("select", () => {
12170
12511
  stageWrapRef.value?.container?.focus();
12171
12512
  });
12513
+ stage.on("dblclick", async (event) => {
12514
+ if (props.stageOptions.beforeDblclick) {
12515
+ if (await props.stageOptions.beforeDblclick(event) === false) return;
12516
+ }
12517
+ const el = await stage?.actionManager?.getElementFromPoint(event) || null;
12518
+ if (!el) return;
12519
+ const id = (0, _tmagic_utils.getIdFromEl)()(el);
12520
+ if (id) {
12521
+ const node = editorService.getNodeById(id);
12522
+ if (node?.type === "page-fragment-container" && node.pageFragmentId) {
12523
+ await editorService.select(node.pageFragmentId);
12524
+ return;
12525
+ }
12526
+ }
12527
+ if (!props.disabledStageOverlay && isClippedByScrollContainer(el)) {
12528
+ stageOverlayService.openOverlay(el);
12529
+ return;
12530
+ }
12531
+ const nextEl = await stage?.actionManager?.getNextElementFromPoint(event) || null;
12532
+ if (nextEl) {
12533
+ const nextId = (0, _tmagic_utils.getIdFromEl)()(nextEl);
12534
+ if (nextId) {
12535
+ await editorService.select(nextId);
12536
+ editorService.get("stage")?.select(nextId);
12537
+ }
12538
+ }
12539
+ });
12172
12540
  editorService.set("stage", (0, vue.markRaw)(stage));
12173
12541
  stage.mount(stageContainerEl.value);
12174
12542
  if (!node.value?.id) return;
@@ -12406,7 +12774,7 @@
12406
12774
  var Workspace_default = Workspace_vue_vue_type_script_setup_true_lang_default;
12407
12775
  //#endregion
12408
12776
  //#region packages/editor/src/services/codeBlock.ts
12409
- var canUsePluginMethods$3 = {
12777
+ var canUsePluginMethods$4 = {
12410
12778
  async: [
12411
12779
  "setCodeDslById",
12412
12780
  "setEditStatus",
@@ -12425,10 +12793,10 @@
12425
12793
  paramsColConfig: void 0
12426
12794
  });
12427
12795
  constructor() {
12428
- super([...canUsePluginMethods$3.async.map((methodName) => ({
12796
+ super([...canUsePluginMethods$4.async.map((methodName) => ({
12429
12797
  name: methodName,
12430
12798
  isAsync: true
12431
- })), ...canUsePluginMethods$3.sync.map((methodName) => ({
12799
+ })), ...canUsePluginMethods$4.sync.map((methodName) => ({
12432
12800
  name: methodName,
12433
12801
  isAsync: false
12434
12802
  }))]);
@@ -12673,7 +13041,7 @@
12673
13041
  var componentList_default = new ComponentList();
12674
13042
  //#endregion
12675
13043
  //#region packages/editor/src/services/dataSource.ts
12676
- var canUsePluginMethods$2 = {
13044
+ var canUsePluginMethods$3 = {
12677
13045
  async: [],
12678
13046
  sync: [
12679
13047
  "getFormConfig",
@@ -12701,7 +13069,7 @@
12701
13069
  methods: {}
12702
13070
  });
12703
13071
  constructor() {
12704
- super(canUsePluginMethods$2.sync.map((methodName) => ({
13072
+ super(canUsePluginMethods$3.sync.map((methodName) => ({
12705
13073
  name: methodName,
12706
13074
  isAsync: false
12707
13075
  })));
@@ -12820,7 +13188,7 @@
12820
13188
  var dataSource_default = new DataSource();
12821
13189
  //#endregion
12822
13190
  //#region packages/editor/src/utils/dep/worker.ts?worker&inline
12823
- var jsContent = "(function() {\n //#region packages/schema/src/index.ts\n const NODE_CONDS_KEY = \"displayConds\";\n const NODE_DISABLE_DATA_SOURCE_KEY = \"_tmagic_node_disabled_data_source\";\n const NODE_DISABLE_CODE_BLOCK_KEY = \"_tmagic_node_disabled_code_block\";\n let HookType = /* @__PURE__ */ function(HookType) {\n /** 代码块钩子标识 */\n HookType[\"CODE\"] = \"code\";\n return HookType;\n }({});\n //#endregion\n //#region packages/utils/src/index.ts\n const isObject = (obj) => Object.prototype.toString.call(obj) === \"[object Object]\";\n const getKeysArray = (keys) => `${keys}`.replace(/\\[(\\d+)\\]/g, \".$1\").split(\".\");\n const DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX = \"ds-field::\";\n const dataSourceTemplateRegExp = /\\$\\{([\\s\\S]+?)\\}/g;\n //#endregion\n //#region packages/dep/src/types.ts\n /** 依赖收集的目标类型 */\n let DepTargetType = /* @__PURE__ */ function(DepTargetType) {\n DepTargetType[\"DEFAULT\"] = \"default\";\n /** 代码块 */\n DepTargetType[\"CODE_BLOCK\"] = \"code-block\";\n /** 数据源 */\n DepTargetType[\"DATA_SOURCE\"] = \"data-source\";\n /** 数据源方法 */\n DepTargetType[\"DATA_SOURCE_METHOD\"] = \"data-source-method\";\n /** 数据源条件 */\n DepTargetType[\"DATA_SOURCE_COND\"] = \"data-source-cond\";\n return DepTargetType;\n }({});\n //#endregion\n //#region packages/dep/src/Target.ts\n /**\n * 需要收集依赖的目标\n * 例如:一个代码块可以为一个目标\n */\n var Target = class {\n /**\n * 如何识别目标\n */\n isTarget;\n /**\n * 目标id,不可重复\n * 例如目标是代码块,则为代码块id\n */\n id;\n /**\n * 目标名称,用于显示在依赖列表中\n */\n name;\n /**\n * 不同的目标可以进行分类,例如代码块,数据源可以为两个不同的type\n */\n type = DepTargetType.DEFAULT;\n /**\n * 依赖详情\n * 实例:{ 'node_id': { name: 'node_name', keys: [ created, mounted ] } }\n */\n deps = {};\n /**\n * 是否默认收集,默认为true,当值为false时需要传入type参数给collect方法才会被收集\n */\n isCollectByDefault;\n constructor(options) {\n this.isTarget = options.isTarget;\n this.id = options.id;\n this.name = options.name;\n this.isCollectByDefault = options.isCollectByDefault ?? true;\n if (options.type) this.type = options.type;\n if (options.initialDeps) this.deps = options.initialDeps;\n }\n /**\n * 更新依赖\n * @param option 节点配置\n * @param key 哪个key配置了这个目标的id\n */\n updateDep({ id, name, key, data }) {\n const dep = this.deps[id] || {\n name,\n keys: []\n };\n dep.name = name;\n dep.data = data;\n this.deps[id] = dep;\n if (!dep.keys.includes(key)) dep.keys.push(key);\n }\n /**\n * 删除依赖\n * @param node 哪个节点的依赖需要移除,如果为空,则移除所有依赖\n * @param key 节点下哪个key需要移除,如果为空,则移除改节点下的所有依赖key\n * @returns void\n */\n removeDep(id, key) {\n if (typeof id === \"undefined\") {\n Object.keys(this.deps).forEach((depKey) => {\n delete this.deps[depKey];\n });\n return;\n }\n const dep = this.deps[id];\n if (!dep) return;\n if (key) {\n const index = dep.keys.indexOf(key);\n dep.keys.splice(index, 1);\n if (dep.keys.length === 0) delete this.deps[id];\n } else delete this.deps[id];\n }\n /**\n * 判断指定节点下的指定key是否存在在依赖列表中\n * @param node 哪个节点\n * @param key 哪个key\n * @returns boolean\n */\n hasDep(id, key) {\n return this.deps[id]?.keys.includes(key) ?? false;\n }\n destroy() {\n this.deps = {};\n }\n };\n //#endregion\n //#region packages/dep/src/utils.ts\n const INTEGER_REGEXP = /^\\d+$/;\n const createCodeBlockTarget = (id, codeBlock, initialDeps = {}) => new Target({\n type: DepTargetType.CODE_BLOCK,\n id,\n initialDeps,\n name: codeBlock.name,\n isTarget: (_key, value) => {\n if (id === value) return true;\n if (value?.hookType === HookType.CODE && Array.isArray(value.hookData)) return value.hookData.some((item) => item.codeId === id);\n return false;\n }\n });\n /**\n * ['array'] ['array', '0'] ['array', '0', 'a'] 这种返回false\n * ['array', 'a'] 这种返回true\n * @param keys\n * @param fields\n * @returns boolean\n */\n const isIncludeArrayField = (keys, fields) => {\n let f = fields;\n return keys.some((key, index) => {\n const field = f.find(({ name }) => name === key);\n f = field?.fields || [];\n return field?.type === \"array\" && index < keys.length - 1 && !INTEGER_REGEXP.test(keys[index + 1]);\n });\n };\n /**\n * 判断模板(value)是不是使用数据源Id(dsId),如:`xxx${dsId.field}xxx${dsId.field}`\n * @param value any\n * @param dsId string | number\n * @param hasArray boolean true: 一定要包含有需要迭代的模板; false: 一定要包含普通模板;\n * @returns boolean\n */\n const isDataSourceTemplate = (value, ds, hasArray = false) => {\n const templates = value.match(dataSourceTemplateRegExp) || [];\n if (templates.length <= 0) return false;\n for (const tpl of templates) {\n const keys = getKeysArray(tpl.substring(2, tpl.length - 1));\n const dsId = keys.shift();\n if (!dsId || dsId !== ds.id) continue;\n if (hasArray === isIncludeArrayField(keys, ds.fields)) return true;\n }\n return false;\n };\n /**\n * 指定数据源的字符串模板,如:{ isBindDataSourceField: true, dataSourceId: 'id', template: `xxx${field}xxx`}\n * @param value any\n * @param dsId string | number\n * @returns boolean\n */\n const isSpecificDataSourceTemplate = (value, dsId) => value?.isBindDataSourceField && value.dataSourceId && value.dataSourceId === dsId && typeof value.template === \"string\";\n /**\n * 关联数据源字段,格式为 [前缀+数据源ID, 字段名]\n * 使用data-source-field-select value: 'value' 可以配置出来\n * @param value any[]\n * @param id string | number\n * @returns boolean\n */\n const isUseDataSourceField = (value, id) => {\n if (!Array.isArray(value) || typeof value[0] !== \"string\") return false;\n const [prefixId] = value;\n const prefixIndex = prefixId.indexOf(DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX);\n if (prefixIndex === -1) return false;\n return prefixId.substring(prefixIndex + DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX.length) === id;\n };\n const isDataSourceTarget = (ds, key, value, hasArray = false) => {\n if (!value) return false;\n const valueType = typeof value;\n if (valueType !== \"string\" && valueType !== \"object\") return false;\n if (`${key}`.startsWith(\"displayConds\")) return false;\n if (valueType === \"string\") return isDataSourceTemplate(value, ds, hasArray);\n if (isObject(value) && value.isBindDataSource && value.dataSourceId === ds.id) return true;\n if (isSpecificDataSourceTemplate(value, ds.id)) return true;\n if (isUseDataSourceField(value, ds.id)) {\n const [, ...keys] = value;\n const includeArray = isIncludeArrayField(keys, ds.fields);\n return hasArray ? includeArray : !includeArray;\n }\n return false;\n };\n const isDataSourceCondTarget = (ds, key, value, hasArray = false) => {\n if (!Array.isArray(value) || !ds) return false;\n const [dsId, ...keys] = value;\n if (dsId !== ds.id || !`${key}`.startsWith(\"displayConds\")) return false;\n if (ds.fields?.some((field) => field.name === keys[0])) {\n const includeArray = isIncludeArrayField(keys, ds.fields);\n return hasArray ? includeArray : !includeArray;\n }\n return false;\n };\n const createDataSourceTarget = (ds, initialDeps = {}) => new Target({\n type: DepTargetType.DATA_SOURCE,\n id: ds.id,\n initialDeps,\n isTarget: (key, value) => isDataSourceTarget(ds, key, value)\n });\n const createDataSourceCondTarget = (ds, initialDeps = {}) => new Target({\n type: DepTargetType.DATA_SOURCE_COND,\n id: ds.id,\n initialDeps,\n isTarget: (key, value) => isDataSourceCondTarget(ds, key, value)\n });\n const createDataSourceMethodTarget = (ds, initialDeps = {}) => new Target({\n type: DepTargetType.DATA_SOURCE_METHOD,\n id: ds.id,\n initialDeps,\n isTarget: (_key, value) => {\n if (!Array.isArray(value)) return false;\n const [dsId, methodName] = value;\n if (!methodName || dsId !== ds.id) return false;\n if (ds.methods?.some((method) => method.name === methodName)) return true;\n if (ds.fields?.some((field) => field.name === methodName)) return false;\n return true;\n }\n });\n const traverseTarget = (targetsList, cb, type) => {\n if (type) {\n const targets = targetsList[type];\n if (targets) for (const target of Object.values(targets)) cb(target);\n return;\n }\n for (const targets of Object.values(targetsList)) for (const target of Object.values(targets)) cb(target);\n };\n //#endregion\n //#region packages/dep/src/Watcher.ts\n const DATA_SOURCE_TARGET_TYPES = new Set([\n DepTargetType.DATA_SOURCE,\n DepTargetType.DATA_SOURCE_COND,\n DepTargetType.DATA_SOURCE_METHOD\n ]);\n var Watcher = class {\n targetsList = {};\n childrenProp = \"items\";\n idProp = \"id\";\n nameProp = \"name\";\n constructor(options) {\n if (options?.initialTargets) this.targetsList = options.initialTargets;\n if (options?.childrenProp) this.childrenProp = options.childrenProp;\n }\n getTargetsList() {\n return this.targetsList;\n }\n /**\n * 获取指定类型中的所有target\n * @param type 分类\n * @returns Target[]\n */\n getTargets(type = DepTargetType.DEFAULT) {\n return this.targetsList[type] || {};\n }\n /**\n * 添加新的目标\n * @param target Target\n */\n addTarget(target) {\n const targets = this.getTargets(target.type) || {};\n this.targetsList[target.type] = targets;\n targets[target.id] = target;\n }\n /**\n * 获取指定id的target\n * @param id target id\n * @returns Target\n */\n getTarget(id, type = DepTargetType.DEFAULT) {\n return this.getTargets(type)[id];\n }\n /**\n * 判断是否存在指定id的target\n * @param id target id\n * @returns boolean\n */\n hasTarget(id, type = DepTargetType.DEFAULT) {\n return Boolean(this.getTarget(id, type));\n }\n /**\n * 判断是否存在指定类型的target\n * @param type target type\n * @returns boolean\n */\n hasSpecifiedTypeTarget(type = DepTargetType.DEFAULT) {\n return Object.keys(this.getTargets(type)).length > 0;\n }\n /**\n * 删除指定id的target\n * @param id target id\n */\n removeTarget(id, type = DepTargetType.DEFAULT) {\n const targets = this.getTargets(type);\n if (targets[id]) {\n targets[id].destroy();\n delete targets[id];\n }\n }\n /**\n * 删除指定分类的所有target\n * @param type 分类\n * @returns void\n */\n removeTargets(type = DepTargetType.DEFAULT) {\n const targets = this.targetsList[type];\n if (!targets) return;\n for (const target of Object.values(targets)) target.destroy();\n delete this.targetsList[type];\n }\n /**\n * 删除所有target\n */\n clearTargets() {\n for (const key of Object.keys(this.targetsList)) delete this.targetsList[key];\n }\n /**\n * 收集依赖\n * @param nodes 需要收集的节点\n * @param deep 是否需要收集子节点\n * @param type 强制收集指定类型的依赖\n */\n collect(nodes, depExtendedData = {}, deep = false, type) {\n this.collectByCallback(nodes, type, ({ node, target }) => {\n this.removeTargetDep(target, node);\n this.collectItem(node, target, depExtendedData, deep);\n });\n }\n collectByCallback(nodes, type, cb) {\n traverseTarget(this.targetsList, (target) => {\n if (!type && !target.isCollectByDefault) return;\n for (const node of nodes) cb({\n node,\n target\n });\n }, type);\n }\n /**\n * 清除所有目标的依赖\n * @param nodes 需要清除依赖的节点\n */\n clear(nodes, type) {\n let { targetsList } = this;\n if (type) targetsList = { [type]: this.getTargets(type) };\n const clearedItemsNodeIds = /* @__PURE__ */ new Set();\n traverseTarget(targetsList, (target) => {\n if (nodes) for (const node of nodes) {\n target.removeDep(node[this.idProp]);\n if (Array.isArray(node[this.childrenProp]) && node[this.childrenProp].length && !clearedItemsNodeIds.has(node[this.idProp])) {\n clearedItemsNodeIds.add(node[this.idProp]);\n this.clear(node[this.childrenProp]);\n }\n }\n else target.removeDep();\n });\n }\n /**\n * 清除指定类型的依赖\n * @param type 类型\n * @param nodes 需要清除依赖的节点\n */\n clearByType(type, nodes) {\n this.clear(nodes, type);\n }\n collectItem(node, target, depExtendedData = {}, deep = false) {\n if (node[\"_tmagic_node_disabled_data_source\"] && DATA_SOURCE_TARGET_TYPES.has(target.type)) return;\n if (node[\"_tmagic_node_disabled_code_block\"] && target.type === DepTargetType.CODE_BLOCK) return;\n const collectTarget = (config, prop = \"\") => {\n const doCollect = (key, value) => {\n const keyIsItems = key === this.childrenProp;\n const fullKey = prop ? `${prop}.${key}` : key;\n if (target.isTarget(fullKey, value)) target.updateDep({\n id: node[this.idProp],\n name: `${node[this.nameProp] || node[this.idProp]}`,\n data: depExtendedData,\n key: fullKey\n });\n else if (!keyIsItems && Array.isArray(value)) for (let i = 0, l = value.length; i < l; i++) {\n const item = value[i];\n if (isObject(item)) collectTarget(item, `${fullKey}[${i}]`);\n }\n else if (isObject(value)) collectTarget(value, fullKey);\n if (keyIsItems && deep && Array.isArray(value)) for (const child of value) this.collectItem(child, target, depExtendedData, deep);\n };\n for (const [key, value] of Object.entries(config)) {\n if (typeof value === \"undefined\" || value === \"\") continue;\n doCollect(key, value);\n }\n };\n collectTarget(node);\n }\n removeTargetDep(target, node, key) {\n target.removeDep(node[this.idProp], key);\n if (typeof key === \"undefined\" && Array.isArray(node[this.childrenProp]) && node[this.childrenProp].length) for (const item of node[this.childrenProp]) this.removeTargetDep(target, item, key);\n }\n };\n //#endregion\n //#region packages/editor/src/utils/logger.ts\n const error = (...args) => {};\n //#endregion\n //#region packages/editor/src/utils/dep/worker.ts\n onmessage = (e) => {\n const watcher = new Watcher({ initialTargets: {} });\n const { dsl } = e.data;\n try {\n const mApp = eval(`(${dsl})`);\n if (!mApp) postMessage({});\n watcher.clearTargets();\n if (mApp.codeBlocks) for (const [id, code] of Object.entries(mApp.codeBlocks)) watcher.addTarget(createCodeBlockTarget(id, code));\n if (mApp.dataSources) for (const ds of mApp.dataSources) {\n watcher.addTarget(createDataSourceTarget(ds, {}));\n watcher.addTarget(createDataSourceMethodTarget(ds, {}));\n watcher.addTarget(createDataSourceCondTarget(ds, {}));\n }\n watcher.collectByCallback(mApp.items, void 0, ({ node, target }) => {\n watcher.collectItem(node, target, { pageId: node.id }, true);\n });\n const data = {\n [DepTargetType.DATA_SOURCE]: {},\n [DepTargetType.DATA_SOURCE_METHOD]: {},\n [DepTargetType.DATA_SOURCE_COND]: {},\n [DepTargetType.CODE_BLOCK]: {}\n };\n traverseTarget(watcher.getTargetsList(), (target) => {\n data[target.type][target.id] = target.deps;\n });\n postMessage(data);\n } catch (e) {\n postMessage({});\n }\n };\n //#endregion\n})();\n";
13191
+ var jsContent = "(function() {\n //#region packages/schema/src/index.ts\n const NODE_CONDS_KEY = \"displayConds\";\n const NODE_DISABLE_DATA_SOURCE_KEY = \"_tmagic_node_disabled_data_source\";\n const NODE_DISABLE_CODE_BLOCK_KEY = \"_tmagic_node_disabled_code_block\";\n let HookType = /* @__PURE__ */ function(HookType) {\n /** 代码块钩子标识 */\n HookType[\"CODE\"] = \"code\";\n return HookType;\n }({});\n //#endregion\n //#region packages/utils/src/const.ts\n const DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX = \"ds-field::\";\n //#endregion\n //#region packages/utils/src/index.ts\n const isObject = (obj) => Object.prototype.toString.call(obj) === \"[object Object]\";\n const getKeysArray = (keys) => `${keys}`.replace(/\\[(\\d+)\\]/g, \".$1\").split(\".\");\n const dataSourceTemplateRegExp = /\\$\\{([\\s\\S]+?)\\}/g;\n //#endregion\n //#region packages/dep/src/types.ts\n /** 依赖收集的目标类型 */\n let DepTargetType = /* @__PURE__ */ function(DepTargetType) {\n DepTargetType[\"DEFAULT\"] = \"default\";\n /** 代码块 */\n DepTargetType[\"CODE_BLOCK\"] = \"code-block\";\n /** 数据源 */\n DepTargetType[\"DATA_SOURCE\"] = \"data-source\";\n /** 数据源方法 */\n DepTargetType[\"DATA_SOURCE_METHOD\"] = \"data-source-method\";\n /** 数据源条件 */\n DepTargetType[\"DATA_SOURCE_COND\"] = \"data-source-cond\";\n return DepTargetType;\n }({});\n //#endregion\n //#region packages/dep/src/Target.ts\n /**\n * 需要收集依赖的目标\n * 例如:一个代码块可以为一个目标\n */\n var Target = class {\n /**\n * 如何识别目标\n */\n isTarget;\n /**\n * 目标id,不可重复\n * 例如目标是代码块,则为代码块id\n */\n id;\n /**\n * 目标名称,用于显示在依赖列表中\n */\n name;\n /**\n * 不同的目标可以进行分类,例如代码块,数据源可以为两个不同的type\n */\n type = DepTargetType.DEFAULT;\n /**\n * 依赖详情\n * 实例:{ 'node_id': { name: 'node_name', keys: [ created, mounted ] } }\n */\n deps = {};\n /**\n * 是否默认收集,默认为true,当值为false时需要传入type参数给collect方法才会被收集\n */\n isCollectByDefault;\n constructor(options) {\n this.isTarget = options.isTarget;\n this.id = options.id;\n this.name = options.name;\n this.isCollectByDefault = options.isCollectByDefault ?? true;\n if (options.type) this.type = options.type;\n if (options.initialDeps) this.deps = options.initialDeps;\n }\n /**\n * 更新依赖\n * @param option 节点配置\n * @param key 哪个key配置了这个目标的id\n */\n updateDep({ id, name, key, data }) {\n const dep = this.deps[id] || {\n name,\n keys: []\n };\n dep.name = name;\n dep.data = data;\n this.deps[id] = dep;\n if (!dep.keys.includes(key)) dep.keys.push(key);\n }\n /**\n * 删除依赖\n * @param node 哪个节点的依赖需要移除,如果为空,则移除所有依赖\n * @param key 节点下哪个key需要移除,如果为空,则移除改节点下的所有依赖key\n * @returns void\n */\n removeDep(id, key) {\n if (typeof id === \"undefined\") {\n Object.keys(this.deps).forEach((depKey) => {\n delete this.deps[depKey];\n });\n return;\n }\n const dep = this.deps[id];\n if (!dep) return;\n if (key) {\n const index = dep.keys.indexOf(key);\n dep.keys.splice(index, 1);\n if (dep.keys.length === 0) delete this.deps[id];\n } else delete this.deps[id];\n }\n /**\n * 判断指定节点下的指定key是否存在在依赖列表中\n * @param node 哪个节点\n * @param key 哪个key\n * @returns boolean\n */\n hasDep(id, key) {\n return this.deps[id]?.keys.includes(key) ?? false;\n }\n destroy() {\n this.deps = {};\n }\n };\n //#endregion\n //#region packages/dep/src/utils.ts\n const INTEGER_REGEXP = /^\\d+$/;\n const createCodeBlockTarget = (id, codeBlock, initialDeps = {}) => new Target({\n type: DepTargetType.CODE_BLOCK,\n id,\n initialDeps,\n name: codeBlock.name,\n isTarget: (_key, value) => {\n if (id === value) return true;\n if (value?.hookType === HookType.CODE && Array.isArray(value.hookData)) return value.hookData.some((item) => item.codeId === id);\n return false;\n }\n });\n /**\n * ['array'] ['array', '0'] ['array', '0', 'a'] 这种返回false\n * ['array', 'a'] 这种返回true\n * @param keys\n * @param fields\n * @returns boolean\n */\n const isIncludeArrayField = (keys, fields) => {\n let f = fields;\n return keys.some((key, index) => {\n const field = f.find(({ name }) => name === key);\n f = field?.fields || [];\n return field?.type === \"array\" && index < keys.length - 1 && !INTEGER_REGEXP.test(keys[index + 1]);\n });\n };\n /**\n * 判断模板(value)是不是使用数据源Id(dsId),如:`xxx${dsId.field}xxx${dsId.field}`\n * @param value any\n * @param dsId string | number\n * @param hasArray boolean true: 一定要包含有需要迭代的模板; false: 一定要包含普通模板;\n * @returns boolean\n */\n const isDataSourceTemplate = (value, ds, hasArray = false) => {\n const templates = value.match(dataSourceTemplateRegExp) || [];\n if (templates.length <= 0) return false;\n for (const tpl of templates) {\n const keys = getKeysArray(tpl.substring(2, tpl.length - 1));\n const dsId = keys.shift();\n if (!dsId || dsId !== ds.id) continue;\n if (hasArray === isIncludeArrayField(keys, ds.fields)) return true;\n }\n return false;\n };\n /**\n * 指定数据源的字符串模板,如:{ isBindDataSourceField: true, dataSourceId: 'id', template: `xxx${field}xxx`}\n * @param value any\n * @param dsId string | number\n * @returns boolean\n */\n const isSpecificDataSourceTemplate = (value, dsId) => value?.isBindDataSourceField && value.dataSourceId && value.dataSourceId === dsId && typeof value.template === \"string\";\n /**\n * 关联数据源字段,格式为 [前缀+数据源ID, 字段名]\n * 使用data-source-field-select value: 'value' 可以配置出来\n * @param value any[]\n * @param id string | number\n * @returns boolean\n */\n const isUseDataSourceField = (value, id) => {\n if (!Array.isArray(value) || typeof value[0] !== \"string\") return false;\n const [prefixId] = value;\n const prefixIndex = prefixId.indexOf(DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX);\n if (prefixIndex === -1) return false;\n return prefixId.substring(prefixIndex + DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX.length) === id;\n };\n const isDataSourceTarget = (ds, key, value, hasArray = false) => {\n if (!value) return false;\n const valueType = typeof value;\n if (valueType !== \"string\" && valueType !== \"object\") return false;\n if (`${key}`.startsWith(\"displayConds\")) return false;\n if (valueType === \"string\") return isDataSourceTemplate(value, ds, hasArray);\n if (isObject(value) && value.isBindDataSource && value.dataSourceId === ds.id) return true;\n if (isSpecificDataSourceTemplate(value, ds.id)) return true;\n if (isUseDataSourceField(value, ds.id)) {\n const [, ...keys] = value;\n const includeArray = isIncludeArrayField(keys, ds.fields);\n return hasArray ? includeArray : !includeArray;\n }\n return false;\n };\n const isDataSourceCondTarget = (ds, key, value, hasArray = false) => {\n if (!Array.isArray(value) || !ds) return false;\n const [dsId, ...keys] = value;\n if (dsId !== ds.id || !`${key}`.startsWith(\"displayConds\")) return false;\n if (ds.fields?.some((field) => field.name === keys[0])) {\n const includeArray = isIncludeArrayField(keys, ds.fields);\n return hasArray ? includeArray : !includeArray;\n }\n return false;\n };\n const createDataSourceTarget = (ds, initialDeps = {}) => new Target({\n type: DepTargetType.DATA_SOURCE,\n id: ds.id,\n initialDeps,\n isTarget: (key, value) => isDataSourceTarget(ds, key, value)\n });\n const createDataSourceCondTarget = (ds, initialDeps = {}) => new Target({\n type: DepTargetType.DATA_SOURCE_COND,\n id: ds.id,\n initialDeps,\n isTarget: (key, value) => isDataSourceCondTarget(ds, key, value)\n });\n const createDataSourceMethodTarget = (ds, initialDeps = {}) => new Target({\n type: DepTargetType.DATA_SOURCE_METHOD,\n id: ds.id,\n initialDeps,\n isTarget: (_key, value) => {\n if (!Array.isArray(value)) return false;\n const [dsId, methodName] = value;\n if (!methodName || dsId !== ds.id) return false;\n if (ds.methods?.some((method) => method.name === methodName)) return true;\n if (ds.fields?.some((field) => field.name === methodName)) return false;\n return true;\n }\n });\n const traverseTarget = (targetsList, cb, type) => {\n if (type) {\n const targets = targetsList[type];\n if (targets) for (const target of Object.values(targets)) cb(target);\n return;\n }\n for (const targets of Object.values(targetsList)) for (const target of Object.values(targets)) cb(target);\n };\n //#endregion\n //#region packages/dep/src/Watcher.ts\n const DATA_SOURCE_TARGET_TYPES = new Set([\n DepTargetType.DATA_SOURCE,\n DepTargetType.DATA_SOURCE_COND,\n DepTargetType.DATA_SOURCE_METHOD\n ]);\n var Watcher = class {\n targetsList = {};\n childrenProp = \"items\";\n idProp = \"id\";\n nameProp = \"name\";\n constructor(options) {\n if (options?.initialTargets) this.targetsList = options.initialTargets;\n if (options?.childrenProp) this.childrenProp = options.childrenProp;\n }\n getTargetsList() {\n return this.targetsList;\n }\n /**\n * 获取指定类型中的所有target\n * @param type 分类\n * @returns Target[]\n */\n getTargets(type = DepTargetType.DEFAULT) {\n return this.targetsList[type] || {};\n }\n /**\n * 添加新的目标\n * @param target Target\n */\n addTarget(target) {\n const targets = this.getTargets(target.type) || {};\n this.targetsList[target.type] = targets;\n targets[target.id] = target;\n }\n /**\n * 获取指定id的target\n * @param id target id\n * @returns Target\n */\n getTarget(id, type = DepTargetType.DEFAULT) {\n return this.getTargets(type)[id];\n }\n /**\n * 判断是否存在指定id的target\n * @param id target id\n * @returns boolean\n */\n hasTarget(id, type = DepTargetType.DEFAULT) {\n return Boolean(this.getTarget(id, type));\n }\n /**\n * 判断是否存在指定类型的target\n * @param type target type\n * @returns boolean\n */\n hasSpecifiedTypeTarget(type = DepTargetType.DEFAULT) {\n return Object.keys(this.getTargets(type)).length > 0;\n }\n /**\n * 删除指定id的target\n * @param id target id\n */\n removeTarget(id, type = DepTargetType.DEFAULT) {\n const targets = this.getTargets(type);\n if (targets[id]) {\n targets[id].destroy();\n delete targets[id];\n }\n }\n /**\n * 删除指定分类的所有target\n * @param type 分类\n * @returns void\n */\n removeTargets(type = DepTargetType.DEFAULT) {\n const targets = this.targetsList[type];\n if (!targets) return;\n for (const target of Object.values(targets)) target.destroy();\n delete this.targetsList[type];\n }\n /**\n * 删除所有target\n */\n clearTargets() {\n for (const key of Object.keys(this.targetsList)) delete this.targetsList[key];\n }\n /**\n * 收集依赖\n * @param nodes 需要收集的节点\n * @param deep 是否需要收集子节点\n * @param type 强制收集指定类型的依赖\n */\n collect(nodes, depExtendedData = {}, deep = false, type) {\n this.collectByCallback(nodes, type, ({ node, target }) => {\n this.removeTargetDep(target, node);\n this.collectItem(node, target, depExtendedData, deep);\n });\n }\n collectByCallback(nodes, type, cb) {\n traverseTarget(this.targetsList, (target) => {\n if (!type && !target.isCollectByDefault) return;\n for (const node of nodes) cb({\n node,\n target\n });\n }, type);\n }\n /**\n * 清除所有目标的依赖\n * @param nodes 需要清除依赖的节点\n */\n clear(nodes, type) {\n let { targetsList } = this;\n if (type) targetsList = { [type]: this.getTargets(type) };\n const clearedItemsNodeIds = /* @__PURE__ */ new Set();\n traverseTarget(targetsList, (target) => {\n if (nodes) for (const node of nodes) {\n target.removeDep(node[this.idProp]);\n if (Array.isArray(node[this.childrenProp]) && node[this.childrenProp].length && !clearedItemsNodeIds.has(node[this.idProp])) {\n clearedItemsNodeIds.add(node[this.idProp]);\n this.clear(node[this.childrenProp]);\n }\n }\n else target.removeDep();\n });\n }\n /**\n * 清除指定类型的依赖\n * @param type 类型\n * @param nodes 需要清除依赖的节点\n */\n clearByType(type, nodes) {\n this.clear(nodes, type);\n }\n collectItem(node, target, depExtendedData = {}, deep = false) {\n if (node[\"_tmagic_node_disabled_data_source\"] && DATA_SOURCE_TARGET_TYPES.has(target.type)) return;\n if (node[\"_tmagic_node_disabled_code_block\"] && target.type === DepTargetType.CODE_BLOCK) return;\n const collectTarget = (config, prop = \"\") => {\n const doCollect = (key, value) => {\n const keyIsItems = key === this.childrenProp;\n const fullKey = prop ? `${prop}.${key}` : key;\n if (target.isTarget(fullKey, value)) target.updateDep({\n id: node[this.idProp],\n name: `${node[this.nameProp] || node[this.idProp]}`,\n data: depExtendedData,\n key: fullKey\n });\n else if (!keyIsItems && Array.isArray(value)) for (let i = 0, l = value.length; i < l; i++) {\n const item = value[i];\n if (isObject(item)) collectTarget(item, `${fullKey}[${i}]`);\n }\n else if (isObject(value)) collectTarget(value, fullKey);\n if (keyIsItems && deep && Array.isArray(value)) for (const child of value) this.collectItem(child, target, depExtendedData, deep);\n };\n for (const [key, value] of Object.entries(config)) {\n if (typeof value === \"undefined\" || value === \"\") continue;\n doCollect(key, value);\n }\n };\n collectTarget(node);\n }\n removeTargetDep(target, node, key) {\n target.removeDep(node[this.idProp], key);\n if (typeof key === \"undefined\" && Array.isArray(node[this.childrenProp]) && node[this.childrenProp].length) for (const item of node[this.childrenProp]) this.removeTargetDep(target, item, key);\n }\n };\n //#endregion\n //#region packages/editor/src/utils/logger.ts\n const error = (...args) => {\n console.error(\"magic editor: \", ...args);\n };\n //#endregion\n //#region packages/editor/src/utils/dep/worker.ts\n onmessage = (e) => {\n const watcher = new Watcher({ initialTargets: {} });\n const { dsl } = e.data;\n try {\n const mApp = eval(`(${dsl})`);\n if (!mApp) postMessage({});\n watcher.clearTargets();\n if (mApp.codeBlocks) for (const [id, code] of Object.entries(mApp.codeBlocks)) watcher.addTarget(createCodeBlockTarget(id, code));\n if (mApp.dataSources) for (const ds of mApp.dataSources) {\n watcher.addTarget(createDataSourceTarget(ds, {}));\n watcher.addTarget(createDataSourceMethodTarget(ds, {}));\n watcher.addTarget(createDataSourceCondTarget(ds, {}));\n }\n watcher.collectByCallback(mApp.items, void 0, ({ node, target }) => {\n watcher.collectItem(node, target, { pageId: node.id }, true);\n });\n const data = {\n [DepTargetType.DATA_SOURCE]: {},\n [DepTargetType.DATA_SOURCE_METHOD]: {},\n [DepTargetType.DATA_SOURCE_COND]: {},\n [DepTargetType.CODE_BLOCK]: {}\n };\n traverseTarget(watcher.getTargetsList(), (target) => {\n data[target.type][target.id] = target.deps;\n });\n postMessage(data);\n } catch (e) {\n error(e);\n postMessage({});\n }\n };\n //#endregion\n})();\n";
12824
13192
  var blob = typeof self !== "undefined" && self.Blob && new Blob(["(self.URL || self.webkitURL).revokeObjectURL(self.location.href);", jsContent], { type: "text/javascript;charset=utf-8" });
12825
13193
  function WorkerWrapper(options) {
12826
13194
  let objURL;
@@ -13002,7 +13370,7 @@
13002
13370
  var dep_default = new Dep();
13003
13371
  //#endregion
13004
13372
  //#region packages/editor/src/services/events.ts
13005
- var canUsePluginMethods$1 = {
13373
+ var canUsePluginMethods$2 = {
13006
13374
  async: [],
13007
13375
  sync: [
13008
13376
  "setEvent",
@@ -13015,10 +13383,10 @@
13015
13383
  var methodMap = (0, vue.reactive)({});
13016
13384
  var Events = class extends BaseService_default {
13017
13385
  constructor() {
13018
- super([...canUsePluginMethods$1.async.map((methodName) => ({
13386
+ super([...canUsePluginMethods$2.async.map((methodName) => ({
13019
13387
  name: methodName,
13020
13388
  isAsync: true
13021
- })), ...canUsePluginMethods$1.sync.map((methodName) => ({
13389
+ })), ...canUsePluginMethods$2.sync.map((methodName) => ({
13022
13390
  name: methodName,
13023
13391
  isAsync: false
13024
13392
  }))]);
@@ -13236,7 +13604,7 @@
13236
13604
  var keybinding_default = new Keybinding();
13237
13605
  //#endregion
13238
13606
  //#region packages/editor/src/services/stageOverlay.ts
13239
- var canUsePluginMethods = {
13607
+ var canUsePluginMethods$1 = {
13240
13608
  async: [],
13241
13609
  sync: [
13242
13610
  "openOverlay",
@@ -13257,7 +13625,7 @@
13257
13625
  stageOverlayVisible: false
13258
13626
  });
13259
13627
  constructor() {
13260
- super(canUsePluginMethods.sync.map((methodName) => ({
13628
+ super(canUsePluginMethods$1.sync.map((methodName) => ({
13261
13629
  name: methodName,
13262
13630
  isAsync: false
13263
13631
  })));
@@ -13477,13 +13845,13 @@
13477
13845
  });
13478
13846
  };
13479
13847
  var initServiceEvents = (props, emit, { editorService, codeBlockService, dataSourceService, depService }) => {
13480
- let getTMagicAppPrimise = null;
13848
+ let getTMagicAppPromise = null;
13481
13849
  const getTMagicApp = async () => {
13482
13850
  const { renderer } = await getStage();
13483
13851
  if (!renderer) return;
13484
13852
  if (renderer.runtime) return renderer.runtime.getApp?.();
13485
- if (getTMagicAppPrimise) return getTMagicAppPrimise;
13486
- getTMagicAppPrimise = new Promise((resolve) => {
13853
+ if (getTMagicAppPromise) return getTMagicAppPromise;
13854
+ getTMagicAppPromise = new Promise((resolve) => {
13487
13855
  const timeout = globalThis.setTimeout(() => {
13488
13856
  resolve(void 0);
13489
13857
  }, 1e4);
@@ -13492,7 +13860,7 @@
13492
13860
  resolve(renderer.runtime?.getApp?.());
13493
13861
  });
13494
13862
  });
13495
- return getTMagicAppPrimise;
13863
+ return getTMagicAppPromise;
13496
13864
  };
13497
13865
  const updateStageNodes = (nodes) => {
13498
13866
  for (const node of nodes) updateStageNode(node);
@@ -13850,6 +14218,7 @@
13850
14218
  canSelect: { type: Function },
13851
14219
  isContainer: { type: Function },
13852
14220
  customContentMenu: { type: Function },
14221
+ beforeDblclick: { type: Function },
13853
14222
  extendFormState: { type: Function },
13854
14223
  pageBarSortOptions: {},
13855
14224
  pageFilterFunction: { type: Function }
@@ -13896,7 +14265,8 @@
13896
14265
  disabledDragStart: props.disabledDragStart,
13897
14266
  renderType: props.renderType,
13898
14267
  guidesOptions: props.guidesOptions,
13899
- disabledMultiSelect: props.disabledMultiSelect
14268
+ disabledMultiSelect: props.disabledMultiSelect,
14269
+ beforeDblclick: props.beforeDblclick
13900
14270
  };
13901
14271
  stageOverlay_default.set("stageOptions", stageOptions);
13902
14272
  (0, vue.provide)("services", services);
@@ -14136,13 +14506,23 @@
14136
14506
  labelWidth: "120px",
14137
14507
  name: props.name
14138
14508
  }];
14139
- const codeParamsConfig = (0, vue.computed)(() => getFormConfig(props.paramsConfig.map(({ name, text, extra, ...config }) => ({
14140
- type: "data-source-field-select",
14141
- name,
14142
- text,
14143
- extra,
14144
- fieldConfig: config
14145
- }))));
14509
+ const codeParamsConfig = (0, vue.computed)(() => getFormConfig(props.paramsConfig.map(({ name, text, extra, ...config }) => {
14510
+ let { type } = config;
14511
+ if (typeof type === "function") type = type(void 0, { model: props.model[props.name] });
14512
+ if (type && ["data-source-field-select", "vs-code"].includes(type)) return {
14513
+ ...config,
14514
+ name,
14515
+ text,
14516
+ extra
14517
+ };
14518
+ return {
14519
+ type: "data-source-field-select",
14520
+ name,
14521
+ text,
14522
+ extra,
14523
+ fieldConfig: config
14524
+ };
14525
+ })));
14146
14526
  /**
14147
14527
  * 参数值修改更新
14148
14528
  */
@@ -14998,8 +15378,9 @@
14998
15378
  actions: [{
14999
15379
  text: "编辑",
15000
15380
  handler: (method, index) => {
15001
- let codeContent = method.content || "({ params, dataSource, app }) => {\n // place your code here\n}";
15002
- if (typeof codeContent !== "string") codeContent = codeContent.toString();
15381
+ let codeContent = "({ params, dataSource, app }) => {\n // place your code here\n}";
15382
+ if (method.content) if (typeof method.content !== "string") codeContent = method.content.toString();
15383
+ else codeContent = method.content;
15003
15384
  codeConfig.value = {
15004
15385
  ...cloneDeep(method),
15005
15386
  content: codeContent
@@ -15488,6 +15869,30 @@
15488
15869
  });
15489
15870
  const getParamItemsConfig = ([dataSourceId, methodName] = ["", ""]) => {
15490
15871
  if (!dataSourceId) return [];
15872
+ if (methodName === _tmagic_utils.DATA_SOURCE_SET_DATA_METHOD_NAME) return [{
15873
+ name: "field",
15874
+ text: "字段",
15875
+ type: "data-source-field-select",
15876
+ dataSourceId,
15877
+ checkStrictly: true
15878
+ }, {
15879
+ name: "data",
15880
+ text: "数据",
15881
+ type: (_formState, { model }) => {
15882
+ const fieldType = getFieldType(dataSourceService.getDataSourceById(`${dataSourceId}`), model.field);
15883
+ let type = "vs-code";
15884
+ if (fieldType === "number") type = "number";
15885
+ else if (fieldType === "string") type = "text";
15886
+ else if (fieldType === "boolean") type = "switch";
15887
+ return type;
15888
+ },
15889
+ language: "javascript",
15890
+ options: (0, vue.inject)("codeOptions", {}),
15891
+ autosize: {
15892
+ minRows: 1,
15893
+ maxRows: 10
15894
+ }
15895
+ }];
15491
15896
  const paramStatements = dataSources.value?.find((item) => item.id === dataSourceId)?.methods?.find((item) => item.name === methodName)?.params;
15492
15897
  if (!paramStatements) return [];
15493
15898
  return paramStatements.map((paramState) => ({
@@ -15496,13 +15901,20 @@
15496
15901
  }));
15497
15902
  };
15498
15903
  const paramsConfig = (0, vue.ref)(getParamItemsConfig(props.model[props.name || "dataSourceMethod"]));
15499
- const methodsOptions = (0, vue.computed)(() => dataSources.value?.filter((ds) => ds.methods?.length || dataSourceService.getFormMethod(ds.type).length)?.map((ds) => ({
15904
+ const methodsOptions = (0, vue.computed)(() => dataSources.value?.map((ds) => ({
15500
15905
  label: ds.title || ds.id,
15501
15906
  value: ds.id,
15502
- children: [...dataSourceService?.getFormMethod(ds.type) || [], ...(ds.methods || []).map((method) => ({
15503
- label: method.name,
15504
- value: method.name
15505
- }))]
15907
+ children: [
15908
+ {
15909
+ label: "设置数据",
15910
+ value: _tmagic_utils.DATA_SOURCE_SET_DATA_METHOD_NAME
15911
+ },
15912
+ ...dataSourceService?.getFormMethod(ds.type) || [],
15913
+ ...(ds.methods || []).map((method) => ({
15914
+ label: method.name,
15915
+ value: method.name
15916
+ }))
15917
+ ]
15506
15918
  })) || []);
15507
15919
  const cascaderConfig = (0, vue.computed)(() => ({
15508
15920
  type: "cascader",
@@ -15598,7 +16010,8 @@
15598
16010
  checkStrictly: { type: Boolean },
15599
16011
  size: {},
15600
16012
  dataSourceFieldType: {},
15601
- notEditable: { type: [Boolean, Function] }
16013
+ notEditable: { type: [Boolean, Function] },
16014
+ dataSourceId: {}
15602
16015
  }, {
15603
16016
  "modelValue": { default: [] },
15604
16017
  "modelModifiers": {}
@@ -15612,7 +16025,11 @@
15612
16025
  const { dataSourceService, uiService } = useServices();
15613
16026
  const mForm = (0, vue.inject)("mForm");
15614
16027
  const eventBus = (0, vue.inject)("eventBus");
15615
- const dataSources = (0, vue.computed)(() => dataSourceService.get("dataSources") || []);
16028
+ const allDataSources = (0, vue.computed)(() => dataSourceService.get("dataSources") || []);
16029
+ const dataSources = (0, vue.computed)(() => {
16030
+ if (!props.dataSourceId) return allDataSources.value;
16031
+ return allDataSources.value.filter((ds) => ds.id === props.dataSourceId);
16032
+ });
15616
16033
  const valueIsKey = (0, vue.computed)(() => props.value === "key");
15617
16034
  const notEditable = (0, vue.computed)(() => (0, _tmagic_form.filterFunction)(mForm, props.notEditable, props));
15618
16035
  const dataSourcesOptions = (0, vue.computed)(() => dataSources.value.map((ds) => ({
@@ -15622,7 +16039,10 @@
15622
16039
  const selectDataSourceId = (0, vue.ref)("");
15623
16040
  const selectFieldsId = (0, vue.ref)([]);
15624
16041
  (0, vue.watch)(modelValue, (value) => {
15625
- if (Array.isArray(value)) {
16042
+ if (props.dataSourceId) {
16043
+ selectDataSourceId.value = valueIsKey.value ? props.dataSourceId : `${_tmagic_utils.DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX}${props.dataSourceId}`;
16044
+ selectFieldsId.value = Array.isArray(value) ? value : [];
16045
+ } else if (Array.isArray(value) && value.length) {
15626
16046
  const [dsId, ...fields] = value;
15627
16047
  selectDataSourceId.value = dsId;
15628
16048
  selectFieldsId.value = fields;
@@ -15632,7 +16052,7 @@
15632
16052
  }
15633
16053
  }, { immediate: true });
15634
16054
  const fieldsOptions = (0, vue.computed)(() => {
15635
- const ds = dataSources.value.find((ds) => ds.id === (0, _tmagic_utils.removeDataSourceFieldPrefix)(selectDataSourceId.value));
16055
+ const ds = allDataSources.value.find((ds) => ds.id === (0, _tmagic_utils.removeDataSourceFieldPrefix)(selectDataSourceId.value));
15636
16056
  if (!ds) return [];
15637
16057
  return getCascaderOptionsFromFields(ds.fields, props.dataSourceFieldType);
15638
16058
  });
@@ -15648,8 +16068,13 @@
15648
16068
  emit("change", modelValue.value);
15649
16069
  };
15650
16070
  const fieldChangeHandler = (v = []) => {
15651
- modelValue.value = [selectDataSourceId.value, ...v];
15652
- emit("change", modelValue.value);
16071
+ if (props.dataSourceId) {
16072
+ modelValue.value = v;
16073
+ emit("change", v);
16074
+ } else {
16075
+ modelValue.value = [selectDataSourceId.value, ...v];
16076
+ emit("change", modelValue.value);
16077
+ }
15653
16078
  };
15654
16079
  const onChangeHandler = (v = []) => {
15655
16080
  modelValue.value = v;
@@ -15660,7 +16085,23 @@
15660
16085
  eventBus?.emit("edit-data-source", (0, _tmagic_utils.removeDataSourceFieldPrefix)(id));
15661
16086
  };
15662
16087
  return (_ctx, _cache) => {
15663
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$30, [__props.checkStrictly ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicSelect), {
16088
+ 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), {
16089
+ key: 0,
16090
+ "model-value": selectFieldsId.value,
16091
+ clearable: "",
16092
+ filterable: "",
16093
+ size: __props.size,
16094
+ disabled: __props.disabled,
16095
+ options: fieldsOptions.value,
16096
+ props: { checkStrictly: __props.checkStrictly },
16097
+ onChange: fieldChangeHandler
16098
+ }, null, 8, [
16099
+ "model-value",
16100
+ "size",
16101
+ "disabled",
16102
+ "options",
16103
+ "props"
16104
+ ])) : __props.checkStrictly ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicSelect), {
15664
16105
  "model-value": selectDataSourceId.value,
15665
16106
  clearable: "",
15666
16107
  filterable: "",
@@ -15703,7 +16144,7 @@
15703
16144
  "options",
15704
16145
  "props"
15705
16146
  ])], 64)) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicCascader), {
15706
- key: 1,
16147
+ key: 2,
15707
16148
  clearable: "",
15708
16149
  filterable: "",
15709
16150
  "model-value": modelValue.value,
@@ -15719,7 +16160,7 @@
15719
16160
  "options",
15720
16161
  "props"
15721
16162
  ])), selectDataSourceId.value && hasDataSourceSidePanel.value ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
15722
- key: 2,
16163
+ key: 3,
15723
16164
  content: notEditable.value ? "查看" : "编辑"
15724
16165
  }, {
15725
16166
  default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
@@ -15837,6 +16278,7 @@
15837
16278
  value: __props.config.value,
15838
16279
  checkStrictly: checkStrictly.value,
15839
16280
  dataSourceFieldType: __props.config.dataSourceFieldType,
16281
+ dataSourceId: __props.config.dataSourceId,
15840
16282
  onChange: onChangeHandler
15841
16283
  }, null, 8, [
15842
16284
  "model-value",
@@ -15844,7 +16286,8 @@
15844
16286
  "size",
15845
16287
  "value",
15846
16288
  "checkStrictly",
15847
- "dataSourceFieldType"
16289
+ "dataSourceFieldType",
16290
+ "dataSourceId"
15848
16291
  ])) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)(tagName.value), {
15849
16292
  key: 1,
15850
16293
  config: __props.config.fieldConfig,
@@ -17854,6 +18297,60 @@
17854
18297
  //#region packages/editor/src/fields/StyleSetter/pro/Border.vue
17855
18298
  var Border_default = Border_vue_vue_type_script_setup_true_lang_default;
17856
18299
  //#endregion
18300
+ //#region packages/editor/src/fields/StyleSetter/pro/Transform.vue?vue&type=script&setup=true&lang.ts
18301
+ var Transform_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
18302
+ __name: "Transform",
18303
+ props: {
18304
+ values: {},
18305
+ disabled: { type: Boolean },
18306
+ size: {}
18307
+ },
18308
+ emits: ["change"],
18309
+ setup(__props, { emit: __emit }) {
18310
+ const emit = __emit;
18311
+ const config = (0, _tmagic_form.defineFormItem)({ items: [{
18312
+ name: "transform",
18313
+ items: [{
18314
+ name: "rotate",
18315
+ text: "旋转角度",
18316
+ labelWidth: "68px",
18317
+ type: "data-source-field-select",
18318
+ checkStrictly: false,
18319
+ dataSourceFieldType: ["string", "number"],
18320
+ fieldConfig: { type: "text" }
18321
+ }, {
18322
+ name: "scale",
18323
+ text: "缩放",
18324
+ labelWidth: "68px",
18325
+ type: "data-source-field-select",
18326
+ checkStrictly: false,
18327
+ dataSourceFieldType: ["string", "number"],
18328
+ fieldConfig: { type: "text" }
18329
+ }]
18330
+ }] });
18331
+ const change = (value, eventData) => {
18332
+ emit("change", value, eventData);
18333
+ };
18334
+ return (_ctx, _cache) => {
18335
+ return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_form.MContainer), {
18336
+ config: (0, vue.unref)(config),
18337
+ model: __props.values,
18338
+ size: __props.size,
18339
+ disabled: __props.disabled,
18340
+ onChange: change
18341
+ }, null, 8, [
18342
+ "config",
18343
+ "model",
18344
+ "size",
18345
+ "disabled"
18346
+ ]);
18347
+ };
18348
+ }
18349
+ });
18350
+ //#endregion
18351
+ //#region packages/editor/src/fields/StyleSetter/pro/Transform.vue
18352
+ var Transform_default = Transform_vue_vue_type_script_setup_true_lang_default;
18353
+ //#endregion
17857
18354
  //#region packages/editor/src/fields/StyleSetter/Index.vue?vue&type=script&setup=true&lang.ts
17858
18355
  var Index_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
17859
18356
  name: "MFieldsStyleSetter",
@@ -17895,6 +18392,10 @@
17895
18392
  {
17896
18393
  title: "边框与圆角",
17897
18394
  component: Border_default
18395
+ },
18396
+ {
18397
+ title: "变形",
18398
+ component: Transform_default
17898
18399
  }
17899
18400
  ];
17900
18401
  const collapseValue = (0, vue.shallowRef)(Array(list.length).fill(1).map((x, i) => `${i}`));
@@ -18330,8 +18831,14 @@
18330
18831
  exports.arrayOptions = arrayOptions;
18331
18832
  exports.beforePaste = beforePaste;
18332
18833
  exports.buildChangeRecords = buildChangeRecords;
18834
+ exports.calcAlignCenterStyle = calcAlignCenterStyle;
18835
+ exports.calcLayerTargetIndex = calcLayerTargetIndex;
18836
+ exports.calcMoveStyle = calcMoveStyle;
18837
+ exports.canUsePluginMethods = canUsePluginMethods;
18333
18838
  exports.change2Fixed = change2Fixed;
18839
+ exports.classifyDragSources = classifyDragSources;
18334
18840
  exports.codeBlockService = codeBlock_default;
18841
+ exports.collectRelatedNodes = collectRelatedNodes;
18335
18842
  exports.dataSourceService = dataSource_default;
18336
18843
  exports.debug = debug;
18337
18844
  exports.default = plugin_default;
@@ -18343,6 +18850,7 @@
18343
18850
  }
18344
18851
  });
18345
18852
  exports.displayTabConfig = displayTabConfig;
18853
+ exports.editorNodeMergeCustomizer = editorNodeMergeCustomizer;
18346
18854
  exports.editorService = editor_default;
18347
18855
  exports.eqOptions = eqOptions;
18348
18856
  exports.error = error;
@@ -18383,6 +18891,7 @@
18383
18891
  exports.moveItemsInContainer = moveItemsInContainer;
18384
18892
  exports.numberOptions = numberOptions;
18385
18893
  exports.propsService = props_default;
18894
+ exports.resolveSelectedNode = resolveSelectedNode;
18386
18895
  exports.serializeConfig = serializeConfig;
18387
18896
  exports.setChildrenLayout = setChildrenLayout;
18388
18897
  exports.setEditorConfig = setEditorConfig;
@@ -18396,6 +18905,7 @@
18396
18905
  return _tmagic_table.default;
18397
18906
  }
18398
18907
  });
18908
+ exports.toggleFixedPosition = toggleFixedPosition;
18399
18909
  exports.uiService = ui_default;
18400
18910
  exports.updateStatus = updateStatus;
18401
18911
  exports.useCodeBlockEdit = useCodeBlockEdit;