@tmagic/editor 1.7.14-beta.2 → 1.8.0-beta.0

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.
@@ -74,7 +74,7 @@ var FormPanel_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defi
74
74
  createVNode(unref(TMagicScrollbar), null, {
75
75
  default: withCtx(() => [createVNode(unref(MForm), {
76
76
  ref: "configForm",
77
- class: normalizeClass(propsPanelSize.value),
77
+ class: normalizeClass([propsPanelSize.value, "m-editor-props-form-panel-form"]),
78
78
  "popper-class": `m-editor-props-panel-popper ${propsPanelSize.value}`,
79
79
  "label-width": __props.labelWidth,
80
80
  "label-position": __props.labelPosition,
@@ -12,6 +12,23 @@ import { NodeType } from "@tmagic/core";
12
12
  import { reactive, toRaw } from "vue";
13
13
  import { cloneDeep, isObject, mergeWith, uniq } from "lodash-es";
14
14
  //#region packages/editor/src/services/editor.ts
15
+ /**
16
+ * 经过 BaseService 的插件 / 中间件包装后,源方法的最后一个形参可能被注入为 dispatch 函数
17
+ * 当 options 形参位置被注入为函数(或为 null)时,将其归一为空对象,避免后续逻辑误读
18
+ */
19
+ var safeOptions = (options) => {
20
+ const empty = {};
21
+ if (!options || typeof options === "function") return empty;
22
+ return options;
23
+ };
24
+ /**
25
+ * 经过 BaseService 的插件 / 中间件包装后,源方法的形参可能被注入为 dispatch 函数
26
+ * 当 parent 形参位置被注入为函数(或为空值)时,归一为 null,由调用方继续走默认 parent 逻辑
27
+ */
28
+ var safeParent = (parent) => {
29
+ if (!parent || typeof parent === "function") return null;
30
+ return parent;
31
+ };
15
32
  var Editor = class extends BaseService_default {
16
33
  state = reactive({
17
34
  root: null,
@@ -104,6 +121,19 @@ var Editor = class extends BaseService_default {
104
121
  return parent;
105
122
  }
106
123
  /**
124
+ * 判断给定节点是否位于非当前页面(即选中该节点将会引起当前页面切换)
125
+ * @param node 节点
126
+ * @returns true 表示该节点位于非当前页面
127
+ */
128
+ isOnDifferentPage(node) {
129
+ const currentPageId = this.get("page")?.id;
130
+ if (currentPageId === void 0 || currentPageId === null) return false;
131
+ if (isPage(node) || isPageFragment(node)) return `${node.id}` !== `${currentPageId}`;
132
+ const nodePage = this.getNodeInfo(node.id, false).page;
133
+ if (!nodePage) return false;
134
+ return `${nodePage.id}` !== `${currentPageId}`;
135
+ }
136
+ /**
107
137
  * 只有容器拥有布局
108
138
  */
109
139
  async getLayout(parent, node) {
@@ -222,9 +252,14 @@ var Editor = class extends BaseService_default {
222
252
  * 向指点容器添加组件节点
223
253
  * @param addConfig 将要添加的组件节点配置
224
254
  * @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
255
+ * @param options 可选配置
256
+ * @param options.doNotSelect 添加后是否不更新当前选中节点(默认 false,添加后会选中新增的节点)
257
+ * @param options.doNotSwitchPage 添加后是否不切换当前页面(默认 false;新增页面 / 跨页新增时为 true 会跳过会引发页面切换的选中操作)
225
258
  * @returns 添加后的节点
226
259
  */
227
- async add(addNode, parent) {
260
+ async add(addNode, parent, options) {
261
+ const safeParentNode = safeParent(parent);
262
+ const { doNotSelect = false, doNotSwitchPage = false } = safeOptions(options);
228
263
  this.captureSelectionBeforeOp();
229
264
  const stage = this.get("stage");
230
265
  const addNodes = [];
@@ -236,19 +271,24 @@ var Editor = class extends BaseService_default {
236
271
  const newNodes = await Promise.all(addNodes.map((node) => {
237
272
  const root = this.get("root");
238
273
  if ((isPage(node) || isPageFragment(node)) && root) return this.doAdd(node, root);
239
- const parentNode = parent && typeof parent !== "function" ? parent : getAddParent(node);
274
+ const parentNode = safeParentNode ?? getAddParent(node);
240
275
  if (!parentNode) throw new Error("未找到父元素");
241
276
  return this.doAdd(node, parentNode);
242
277
  }));
243
278
  if (newNodes.length > 1) {
244
- const newNodeIds = newNodes.map((node) => node.id);
245
- stage?.multiSelect(newNodeIds);
246
- await this.multiSelect(newNodeIds);
279
+ const wouldSwitchPage = newNodes.some((n) => this.isOnDifferentPage(n));
280
+ if (!doNotSelect && !(doNotSwitchPage && wouldSwitchPage)) {
281
+ const newNodeIds = newNodes.map((node) => node.id);
282
+ stage?.multiSelect(newNodeIds);
283
+ await this.multiSelect(newNodeIds);
284
+ }
247
285
  } else {
248
- await this.select(newNodes[0]);
286
+ const wouldSwitchPage = this.isOnDifferentPage(newNodes[0]);
287
+ const skipSelect = doNotSelect || doNotSwitchPage && wouldSwitchPage;
288
+ if (!skipSelect) await this.select(newNodes[0]);
249
289
  if (isPage(newNodes[0])) this.state.pageLength += 1;
250
290
  else if (isPageFragment(newNodes[0])) this.state.pageFragmentLength += 1;
251
- else stage?.select(newNodes[0].id);
291
+ else if (!skipSelect) stage?.select(newNodes[0].id);
252
292
  }
253
293
  if (!(isPage(newNodes[0]) || isPageFragment(newNodes[0]))) {
254
294
  const pageForOp = this.getNodeInfo(newNodes[0].id, false).page;
@@ -267,7 +307,8 @@ var Editor = class extends BaseService_default {
267
307
  this.emit("add", newNodes);
268
308
  return Array.isArray(addNode) ? newNodes : newNodes[0];
269
309
  }
270
- async doRemove(node) {
310
+ async doRemove(node, options) {
311
+ const { doNotSelect = false, doNotSwitchPage = false } = safeOptions(options);
271
312
  const root = this.get("root");
272
313
  if (!root) throw new Error("root不能为空");
273
314
  const { parent, node: curNode } = this.getNodeInfo(node.id, false);
@@ -281,6 +322,17 @@ var Editor = class extends BaseService_default {
281
322
  parentId: parent.id,
282
323
  root: cloneDeep(root)
283
324
  });
325
+ const selectedNodes = this.get("nodes");
326
+ const removedSelectedIndex = selectedNodes.findIndex((n) => `${n.id}` === `${node.id}`);
327
+ if (removedSelectedIndex !== -1) {
328
+ const nextSelected = [...selectedNodes];
329
+ nextSelected.splice(removedSelectedIndex, 1);
330
+ this.set("nodes", nextSelected);
331
+ }
332
+ if (isPage(node) || isPageFragment(node)) {
333
+ const currentPage = this.get("page");
334
+ if (currentPage && `${currentPage.id}` === `${node.id}`) this.set("page", null);
335
+ }
284
336
  const selectDefault = async (pages) => {
285
337
  if (pages[0]) {
286
338
  await this.select(pages[0]);
@@ -293,13 +345,15 @@ var Editor = class extends BaseService_default {
293
345
  const rootItems = root.items || [];
294
346
  if (isPage(node)) {
295
347
  this.state.pageLength -= 1;
296
- await selectDefault(rootItems);
348
+ if (!doNotSelect && !doNotSwitchPage) await selectDefault(rootItems);
297
349
  } else if (isPageFragment(node)) {
298
350
  this.state.pageFragmentLength -= 1;
299
- await selectDefault(rootItems);
351
+ if (!doNotSelect && !doNotSwitchPage) await selectDefault(rootItems);
300
352
  } else {
301
- await this.select(parent);
302
- stage?.select(parent.id);
353
+ if (!doNotSelect) {
354
+ await this.select(parent);
355
+ stage?.select(parent.id);
356
+ }
303
357
  this.addModifiedNodeId(parent.id);
304
358
  }
305
359
  if (!rootItems.length) {
@@ -309,9 +363,13 @@ var Editor = class extends BaseService_default {
309
363
  }
310
364
  /**
311
365
  * 删除组件
312
- * @param {Object} node
366
+ * @param {Object} node 要删除的节点或节点集合
367
+ * @param options 可选配置
368
+ * @param options.doNotSelect 删除后是否不更新当前选中节点(默认 false,删除后会选中父节点或首个页面)
369
+ * @param options.doNotSwitchPage 删除后是否不切换当前页面(默认 false;删除页面 / 页面片段时为 true 会跳过自动切换到首个剩余页面)
313
370
  */
314
- async remove(nodeOrNodeList) {
371
+ async remove(nodeOrNodeList, options) {
372
+ const { doNotSelect = false, doNotSwitchPage = false } = safeOptions(options);
315
373
  this.captureSelectionBeforeOp();
316
374
  const nodes = Array.isArray(nodeOrNodeList) ? nodeOrNodeList : [nodeOrNodeList];
317
375
  const removedItems = [];
@@ -331,11 +389,14 @@ var Editor = class extends BaseService_default {
331
389
  });
332
390
  }
333
391
  }
334
- await Promise.all(nodes.map((node) => this.doRemove(node)));
392
+ await Promise.all(nodes.map((node) => this.doRemove(node, {
393
+ doNotSelect,
394
+ doNotSwitchPage
395
+ })));
335
396
  if (removedItems.length > 0 && pageForOp) this.pushOpHistory("remove", { removedItems }, pageForOp);
336
397
  this.emit("remove", nodes);
337
398
  }
338
- async doUpdate(config, { changeRecords = [], selectedAfterUpdate = true } = {}) {
399
+ async doUpdate(config, { changeRecords = [] } = {}) {
339
400
  const root = this.get("root");
340
401
  if (!root) throw new Error("root为空");
341
402
  if (!config?.id) throw new Error("没有配置或者配置缺少id值");
@@ -362,13 +423,16 @@ var Editor = class extends BaseService_default {
362
423
  const layout = await this.getLayout(node);
363
424
  if (Array.isArray(newConfig.items) && newLayout !== layout) newConfig = setChildrenLayout(newConfig, newLayout);
364
425
  parentNodeItems[index] = newConfig;
365
- if (selectedAfterUpdate) {
366
- const nodes = this.get("nodes");
367
- const targetIndex = nodes.findIndex((nodeItem) => `${nodeItem.id}` === `${newConfig.id}`);
368
- nodes.splice(targetIndex, 1, newConfig);
369
- this.set("nodes", [...nodes]);
426
+ const selectedNodes = this.get("nodes");
427
+ const targetIndex = selectedNodes.findIndex((nodeItem) => `${nodeItem.id}` === `${newConfig.id}`);
428
+ if (targetIndex !== -1) {
429
+ selectedNodes.splice(targetIndex, 1, newConfig);
430
+ this.set("nodes", [...selectedNodes]);
431
+ }
432
+ if (isPage(newConfig) || isPageFragment(newConfig)) {
433
+ const currentPage = this.get("page");
434
+ if (currentPage && `${currentPage.id}` === `${newConfig.id}`) this.set("page", newConfig);
370
435
  }
371
- if (isPage(newConfig) || isPageFragment(newConfig)) this.set("page", newConfig);
372
436
  this.addModifiedNodeId(newConfig.id);
373
437
  return {
374
438
  oldNode: node,
@@ -407,9 +471,13 @@ var Editor = class extends BaseService_default {
407
471
  * 将id为id1的组件移动到id为id2的组件位置上,例如:[1,2,3,4] -> sort(1,3) -> [2,1,3,4]
408
472
  * @param id1 组件ID
409
473
  * @param id2 组件ID
474
+ * @param options 可选配置
475
+ * @param options.doNotSelect 排序后是否不更新当前选中节点(默认 false)
476
+ * @param options.doNotSwitchPage 排序后是否不切换当前页面(排序只发生在同一父节点内,方法内为空操作;保留以与其它 DSL 操作 API 一致)
410
477
  * @returns void
411
478
  */
412
- async sort(id1, id2) {
479
+ async sort(id1, id2, options) {
480
+ const { doNotSelect = false } = safeOptions(options);
413
481
  this.captureSelectionBeforeOp();
414
482
  const root = this.get("root");
415
483
  if (!root) throw new Error("root为空");
@@ -422,7 +490,7 @@ var Editor = class extends BaseService_default {
422
490
  const index1 = parent.items.findIndex((node) => `${node.id}` === `${id1}`);
423
491
  parent.items.splice(index2, 0, ...parent.items.splice(index1, 1));
424
492
  await this.update(parent);
425
- await this.select(node);
493
+ if (!doNotSelect) await this.select(node);
426
494
  this.get("stage")?.update({
427
495
  config: cloneDeep(node),
428
496
  parentId: parent.id,
@@ -450,9 +518,14 @@ var Editor = class extends BaseService_default {
450
518
  /**
451
519
  * 从localStorage中获取节点,然后添加到当前容器中
452
520
  * @param position 粘贴的坐标
521
+ * @param collectorOptions 可选的依赖收集器配置
522
+ * @param options 可选配置
523
+ * @param options.doNotSelect 粘贴后是否不更新当前选中节点(默认 false)
524
+ * @param options.doNotSwitchPage 粘贴后是否不切换当前页面(默认 false;跨页粘贴时为 true 会跳过页面切换)
453
525
  * @returns 添加后的组件节点配置
454
526
  */
455
- async paste(position = {}, collectorOptions) {
527
+ async paste(position = {}, collectorOptions, options) {
528
+ const { doNotSelect = false, doNotSwitchPage = false } = safeOptions(options);
456
529
  const config = storage_default.getItem(COPY_STORAGE_KEY);
457
530
  if (!Array.isArray(config)) return;
458
531
  const node = this.get("node");
@@ -463,7 +536,10 @@ var Editor = class extends BaseService_default {
463
536
  }
464
537
  const pasteConfigs = await this.doPaste(config, position);
465
538
  if (collectorOptions && typeof collectorOptions.isTarget === "function") props_default.replaceRelateId(config, pasteConfigs, collectorOptions);
466
- return this.add(pasteConfigs, parent);
539
+ return this.add(pasteConfigs, parent, {
540
+ doNotSelect,
541
+ doNotSwitchPage
542
+ });
467
543
  }
468
544
  async doPaste(config, position = {}) {
469
545
  props_default.clearRelateId();
@@ -484,14 +560,18 @@ var Editor = class extends BaseService_default {
484
560
  /**
485
561
  * 将指点节点设置居中
486
562
  * @param config 组件节点配置
563
+ * @param options 可选配置
564
+ * @param options.doNotSelect 居中后是否不更新当前选中节点(默认 false)
565
+ * @param options.doNotSwitchPage 居中后是否不切换当前页面(居中只更新节点 style,方法内为空操作;保留以与其它 DSL 操作 API 一致)
487
566
  * @returns 当前组件节点配置
488
567
  */
489
- async alignCenter(config) {
568
+ async alignCenter(config, options) {
569
+ const { doNotSelect = false } = safeOptions(options);
490
570
  const nodes = Array.isArray(config) ? config : [config];
491
571
  const stage = this.get("stage");
492
572
  const newNodes = await Promise.all(nodes.map((node) => this.doAlignCenter(node)));
493
573
  const newNode = await this.update(newNodes);
494
- if (newNodes.length > 1) await stage?.multiSelect(newNodes.map((node) => node.id));
574
+ if (!doNotSelect) if (newNodes.length > 1) await stage?.multiSelect(newNodes.map((node) => node.id));
495
575
  else await stage?.select(newNodes[0].id);
496
576
  return newNode;
497
577
  }
@@ -536,8 +616,12 @@ var Editor = class extends BaseService_default {
536
616
  * 移动到指定容器中
537
617
  * @param config 需要移动的节点
538
618
  * @param targetId 容器ID
619
+ * @param options 可选配置
620
+ * @param options.doNotSelect 移动后是否不更新当前选中节点(默认 false)
621
+ * @param options.doNotSwitchPage 移动后是否不切换当前页面(默认 false;目标容器位于其它页面时为 true 会跳过自动选中以避免页面切换)
539
622
  */
540
- async moveToContainer(config, targetId) {
623
+ async moveToContainer(config, targetId, options) {
624
+ const { doNotSelect = false, doNotSwitchPage = false } = safeOptions(options);
541
625
  this.captureSelectionBeforeOp();
542
626
  const root = this.get("root");
543
627
  const { node, parent, page: pageForOp } = this.getNodeInfo(config.id, false);
@@ -559,15 +643,19 @@ var Editor = class extends BaseService_default {
559
643
  });
560
644
  newConfig.style = getInitPositionStyle(newConfig.style, layout);
561
645
  target.items.push(newConfig);
562
- await stage.select(targetId);
646
+ const targetWouldSwitchPage = this.isOnDifferentPage(target);
647
+ const skipSelect = doNotSelect || doNotSwitchPage && targetWouldSwitchPage;
648
+ if (!skipSelect) await stage.select(targetId);
563
649
  const targetParent = this.getParentById(target.id);
564
650
  await stage.update({
565
651
  config: cloneDeep(target),
566
652
  parentId: targetParent?.id,
567
653
  root: cloneDeep(root)
568
654
  });
569
- await this.select(newConfig);
570
- stage.select(newConfig.id);
655
+ if (!skipSelect) {
656
+ await this.select(newConfig);
657
+ stage.select(newConfig.id);
658
+ }
571
659
  this.addModifiedNodeId(target.id);
572
660
  this.addModifiedNodeId(parent.id);
573
661
  this.pushOpHistory("update", { updatedItems: [{
package/dist/es/style.css CHANGED
@@ -1048,20 +1048,6 @@ fieldset.m-fieldset .m-form-tip {
1048
1048
  .m-editor-props-panel .m-editor-props-property-panel.show-style-panel .m-editor-props-panel-src-icon {
1049
1049
  right: calc(15px + var(--props-style-panel-width));
1050
1050
  }
1051
- .m-editor-props-panel .m-editor-props-property-panel .tmagic-design-form {
1052
- padding-right: 10px;
1053
- padding-left: 10px;
1054
- }
1055
- .m-editor-props-panel .m-editor-props-property-panel .tmagic-design-form > .m-container-tab > .tmagic-design-tabs > .el-tabs__content {
1056
- padding-top: 55px;
1057
- }
1058
- .m-editor-props-panel .m-editor-props-property-panel .tmagic-design-form > .m-container-tab > .tmagic-design-tabs > .el-tabs__header.is-top {
1059
- position: absolute;
1060
- top: 0;
1061
- width: 100%;
1062
- background: #fff;
1063
- z-index: 3;
1064
- }
1065
1051
  .m-editor-props-panel .m-editor-props-style-panel {
1066
1052
  position: absolute;
1067
1053
  width: var(--props-style-panel-width);
@@ -1133,6 +1119,21 @@ fieldset.m-fieldset .m-form-tip {
1133
1119
  font-size: 12px;
1134
1120
  }
1135
1121
 
1122
+ .m-editor-props-form-panel-form {
1123
+ padding-right: 10px;
1124
+ padding-left: 10px;
1125
+ }
1126
+ .m-editor-props-form-panel-form > .m-container-tab > .tmagic-design-tabs > .el-tabs__content {
1127
+ padding-top: 55px;
1128
+ }
1129
+ .m-editor-props-form-panel-form > .m-container-tab > .tmagic-design-tabs > .el-tabs__header.is-top {
1130
+ position: absolute;
1131
+ top: 0;
1132
+ width: 100%;
1133
+ background: #fff;
1134
+ z-index: 3;
1135
+ }
1136
+
1136
1137
  .m-editor-props-panel-popper.small span,
1137
1138
  .m-editor-props-panel-popper.small a,
1138
1139
  .m-editor-props-panel-popper.small p {
package/dist/style.css CHANGED
@@ -1048,20 +1048,6 @@ fieldset.m-fieldset .m-form-tip {
1048
1048
  .m-editor-props-panel .m-editor-props-property-panel.show-style-panel .m-editor-props-panel-src-icon {
1049
1049
  right: calc(15px + var(--props-style-panel-width));
1050
1050
  }
1051
- .m-editor-props-panel .m-editor-props-property-panel .tmagic-design-form {
1052
- padding-right: 10px;
1053
- padding-left: 10px;
1054
- }
1055
- .m-editor-props-panel .m-editor-props-property-panel .tmagic-design-form > .m-container-tab > .tmagic-design-tabs > .el-tabs__content {
1056
- padding-top: 55px;
1057
- }
1058
- .m-editor-props-panel .m-editor-props-property-panel .tmagic-design-form > .m-container-tab > .tmagic-design-tabs > .el-tabs__header.is-top {
1059
- position: absolute;
1060
- top: 0;
1061
- width: 100%;
1062
- background: #fff;
1063
- z-index: 3;
1064
- }
1065
1051
  .m-editor-props-panel .m-editor-props-style-panel {
1066
1052
  position: absolute;
1067
1053
  width: var(--props-style-panel-width);
@@ -1133,6 +1119,21 @@ fieldset.m-fieldset .m-form-tip {
1133
1119
  font-size: 12px;
1134
1120
  }
1135
1121
 
1122
+ .m-editor-props-form-panel-form {
1123
+ padding-right: 10px;
1124
+ padding-left: 10px;
1125
+ }
1126
+ .m-editor-props-form-panel-form > .m-container-tab > .tmagic-design-tabs > .el-tabs__content {
1127
+ padding-top: 55px;
1128
+ }
1129
+ .m-editor-props-form-panel-form > .m-container-tab > .tmagic-design-tabs > .el-tabs__header.is-top {
1130
+ position: absolute;
1131
+ top: 0;
1132
+ width: 100%;
1133
+ background: #fff;
1134
+ z-index: 3;
1135
+ }
1136
+
1136
1137
  .m-editor-props-panel-popper.small span,
1137
1138
  .m-editor-props-panel-popper.small a,
1138
1139
  .m-editor-props-panel-popper.small p {