@tmagic/editor 1.4.5 → 1.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/tmagic-editor.js +2293 -1984
  2. package/dist/tmagic-editor.umd.cjs +2294 -1982
  3. package/package.json +19 -14
  4. package/src/components/TreeNode.vue +3 -1
  5. package/src/editorProps.ts +0 -3
  6. package/src/fields/DataSourceInput.vue +7 -1
  7. package/src/fields/EventSelect.vue +90 -16
  8. package/src/hooks/use-code-block-edit.ts +0 -2
  9. package/src/hooks/use-stage.ts +1 -1
  10. package/src/index.ts +1 -0
  11. package/src/initService.ts +24 -18
  12. package/src/layouts/sidebar/code-block/CodeBlockList.vue +34 -16
  13. package/src/layouts/sidebar/data-source/DataSourceList.vue +17 -5
  14. package/src/services/codeBlock.ts +80 -9
  15. package/src/services/dataSource.ts +60 -4
  16. package/src/services/dep.ts +77 -5
  17. package/src/services/editor.ts +23 -14
  18. package/src/services/props.ts +19 -8
  19. package/src/services/stageOverlay.ts +1 -0
  20. package/src/type.ts +1 -0
  21. package/src/utils/data-source/index.ts +20 -13
  22. package/src/utils/editor.ts +2 -0
  23. package/src/utils/idle-task.ts +72 -0
  24. package/src/utils/operator.ts +6 -6
  25. package/types/editorProps.d.ts +0 -3
  26. package/types/index.d.ts +1 -0
  27. package/types/layouts/PropsPanel.vue.d.ts +33 -33
  28. package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +1 -1
  29. package/types/layouts/sidebar/data-source/DataSourceList.vue.d.ts +1 -1
  30. package/types/services/codeBlock.d.ts +23 -2
  31. package/types/services/dataSource.d.ts +13 -1
  32. package/types/services/dep.d.ts +12 -2
  33. package/types/services/editor.d.ts +3 -2
  34. package/types/services/props.d.ts +2 -1
  35. package/types/type.d.ts +1 -0
  36. package/types/utils/editor.d.ts +2 -0
  37. package/types/utils/idle-task.d.ts +14 -0
  38. package/types/utils/operator.d.ts +2 -2
@@ -1,14 +1,18 @@
1
1
  import { reactive } from 'vue';
2
- import { cloneDeep } from 'lodash-es';
2
+ import { cloneDeep, get } from 'lodash-es';
3
3
  import { Writable } from 'type-fest';
4
4
 
5
5
  import type { EventOption } from '@tmagic/core';
6
+ import { Target, type TargetOptions, Watcher } from '@tmagic/dep';
6
7
  import type { FormConfig } from '@tmagic/form';
7
- import type { DataSourceSchema } from '@tmagic/schema';
8
+ import type { DataSourceSchema, Id, MNode } from '@tmagic/schema';
8
9
  import { guid, toLine } from '@tmagic/utils';
9
10
 
11
+ import editorService from '@editor/services/editor';
12
+ import storageService, { Protocol } from '@editor/services/storage';
10
13
  import type { DatasourceTypeOption, SyncHookPlugin } from '@editor/type';
11
14
  import { getFormConfig, getFormValue } from '@editor/utils/data-source';
15
+ import { COPY_DS_STORAGE_KEY } from '@editor/utils/editor';
12
16
 
13
17
  import BaseService from './BaseService';
14
18
 
@@ -102,7 +106,7 @@ class DataSource extends BaseService {
102
106
  public add(config: DataSourceSchema) {
103
107
  const newConfig = {
104
108
  ...config,
105
- id: this.createId(),
109
+ id: config.id && !this.getDataSourceById(config.id) ? config.id : this.createId(),
106
110
  };
107
111
 
108
112
  this.get('dataSources').push(newConfig);
@@ -116,7 +120,6 @@ class DataSource extends BaseService {
116
120
  const dataSources = this.get('dataSources');
117
121
 
118
122
  const index = dataSources.findIndex((ds) => ds.id === config.id);
119
-
120
123
  dataSources[index] = cloneDeep(config);
121
124
 
122
125
  this.emit('update', config);
@@ -153,6 +156,59 @@ class DataSource extends BaseService {
153
156
  public usePlugin(options: SyncHookPlugin<SyncMethodName, DataSource>): void {
154
157
  super.usePlugin(options);
155
158
  }
159
+
160
+ /**
161
+ * 复制时会带上组件关联的数据源
162
+ * @param config 组件节点配置
163
+ * @returns
164
+ */
165
+ public copyWithRelated(config: MNode | MNode[], collectorOptions?: TargetOptions): void {
166
+ const copyNodes: MNode[] = Array.isArray(config) ? config : [config];
167
+ const copyData: DataSourceSchema[] = [];
168
+
169
+ if (collectorOptions && typeof collectorOptions.isTarget === 'function') {
170
+ const customTarget = new Target({
171
+ ...collectorOptions,
172
+ });
173
+
174
+ const coperWatcher = new Watcher();
175
+
176
+ coperWatcher.addTarget(customTarget);
177
+
178
+ coperWatcher.collect(copyNodes, {}, true, collectorOptions.type);
179
+
180
+ Object.keys(customTarget.deps).forEach((nodeId: Id) => {
181
+ const node = editorService.getNodeById(nodeId);
182
+ if (!node) return;
183
+ customTarget!.deps[nodeId].keys.forEach((key) => {
184
+ const [relateDsId] = get(node, key);
185
+ const isExist = copyData.find((dsItem) => dsItem.id === relateDsId);
186
+ if (!isExist) {
187
+ const relateDs = this.getDataSourceById(relateDsId);
188
+ if (relateDs) {
189
+ copyData.push(relateDs);
190
+ }
191
+ }
192
+ });
193
+ });
194
+ }
195
+ storageService.setItem(COPY_DS_STORAGE_KEY, copyData, {
196
+ protocol: Protocol.OBJECT,
197
+ });
198
+ }
199
+ /**
200
+ * 粘贴数据源
201
+ * @returns
202
+ */
203
+ public paste() {
204
+ const dataSource: DataSourceSchema[] = storageService.getItem(COPY_DS_STORAGE_KEY);
205
+ dataSource.forEach((item: DataSourceSchema) => {
206
+ // 不覆盖同样id的数据源
207
+ if (!this.getDataSourceById(item.id)) {
208
+ this.add(item);
209
+ }
210
+ });
211
+ }
156
212
  }
157
213
 
158
214
  export type DataSourceService = DataSource;
@@ -17,18 +17,35 @@
17
17
  */
18
18
  import { reactive } from 'vue';
19
19
 
20
- import { DepTargetType, type Target, Watcher } from '@tmagic/dep';
20
+ import { type DepExtendedData, DepTargetType, type Target, type TargetNode, Watcher } from '@tmagic/dep';
21
21
  import type { Id, MNode } from '@tmagic/schema';
22
+ import { isPage } from '@tmagic/utils';
23
+
24
+ import { IdleTask } from '@editor/utils/idle-task';
22
25
 
23
26
  import BaseService from './BaseService';
24
27
 
28
+ export interface DepEvents {
29
+ 'add-target': [target: Target];
30
+ 'remove-target': [id: string | number];
31
+ collected: [nodes: MNode[], deep: boolean];
32
+ }
33
+
34
+ const idleTask = new IdleTask<{ node: TargetNode; deep: boolean; target: Target }>();
35
+
25
36
  class Dep extends BaseService {
26
37
  private watcher = new Watcher({ initialTargets: reactive({}) });
27
38
 
28
39
  public removeTargets(type: string = DepTargetType.DEFAULT) {
29
40
  this.watcher.removeTargets(type);
30
41
 
31
- this.emit('remove-target');
42
+ const targets = this.watcher.getTargets(type);
43
+
44
+ if (!targets) return;
45
+
46
+ for (const target of Object.values(targets)) {
47
+ this.emit('remove-target', target.id);
48
+ }
32
49
  }
33
50
 
34
51
  public getTargets(type: string = DepTargetType.DEFAULT) {
@@ -46,18 +63,55 @@ class Dep extends BaseService {
46
63
 
47
64
  public removeTarget(id: Id, type: string = DepTargetType.DEFAULT) {
48
65
  this.watcher.removeTarget(id, type);
49
- this.emit('remove-target');
66
+ this.emit('remove-target', id);
50
67
  }
51
68
 
52
69
  public clearTargets() {
53
70
  this.watcher.clearTargets();
54
71
  }
55
72
 
56
- public collect(nodes: MNode[], deep = false, type?: DepTargetType) {
57
- this.watcher.collect(nodes, deep, type);
73
+ public collect(nodes: MNode[], depExtendedData: DepExtendedData = {}, deep = false, type?: DepTargetType) {
74
+ this.watcher.collectByCallback(nodes, type, ({ node, target }) => {
75
+ this.collectNode(node, target, depExtendedData, deep);
76
+ });
77
+
58
78
  this.emit('collected', nodes, deep);
59
79
  }
60
80
 
81
+ public collectIdle(nodes: MNode[], depExtendedData: DepExtendedData = {}, deep = false, type?: DepTargetType) {
82
+ this.watcher.collectByCallback(nodes, type, ({ node, target }) => {
83
+ idleTask.enqueueTask(
84
+ ({ node, deep, target }) => {
85
+ this.collectNode(node, target, depExtendedData, deep);
86
+ },
87
+ {
88
+ node,
89
+ deep,
90
+ target,
91
+ },
92
+ );
93
+ });
94
+
95
+ idleTask.once('finish', () => {
96
+ this.emit('collected', nodes, deep);
97
+ });
98
+ }
99
+
100
+ collectNode(node: MNode, target: Target, depExtendedData: DepExtendedData = {}, deep = false) {
101
+ // 先删除原有依赖,重新收集
102
+ if (isPage(node)) {
103
+ Object.entries(target.deps).forEach(([depKey, dep]) => {
104
+ if (dep.data?.pageId && dep.data.pageId === depExtendedData.pageId) {
105
+ delete target.deps[depKey];
106
+ }
107
+ });
108
+ } else {
109
+ this.watcher.removeTargetDep(target, node);
110
+ }
111
+
112
+ this.watcher.collectItem(node, target, depExtendedData, deep);
113
+ }
114
+
61
115
  public clear(nodes?: MNode[]) {
62
116
  return this.watcher.clear(nodes);
63
117
  }
@@ -73,6 +127,24 @@ class Dep extends BaseService {
73
127
  public hasSpecifiedTypeTarget(type: string = DepTargetType.DEFAULT): boolean {
74
128
  return this.watcher.hasSpecifiedTypeTarget(type);
75
129
  }
130
+
131
+ public on<Name extends keyof DepEvents, Param extends DepEvents[Name]>(
132
+ eventName: Name,
133
+ listener: (...args: Param) => void | Promise<void>,
134
+ ) {
135
+ return super.on(eventName, listener as any);
136
+ }
137
+
138
+ public once<Name extends keyof DepEvents, Param extends DepEvents[Name]>(
139
+ eventName: Name,
140
+ listener: (...args: Param) => void | Promise<void>,
141
+ ) {
142
+ return super.once(eventName, listener as any);
143
+ }
144
+
145
+ public emit<Name extends keyof DepEvents, Param extends DepEvents[Name]>(eventName: Name, ...args: Param) {
146
+ return super.emit(eventName, ...args);
147
+ }
76
148
  }
77
149
 
78
150
  export type DepService = Dep;
@@ -20,14 +20,13 @@ import { reactive, toRaw } from 'vue';
20
20
  import { cloneDeep, get, isObject, mergeWith, uniq } from 'lodash-es';
21
21
  import { Writable } from 'type-fest';
22
22
 
23
- import { DepTargetType } from '@tmagic/dep';
23
+ import { Target, type TargetOptions, Watcher } from '@tmagic/dep';
24
24
  import type { Id, MApp, MComponent, MContainer, MNode, MPage, MPageFragment } from '@tmagic/schema';
25
25
  import { NodeType } from '@tmagic/schema';
26
26
  import { calcValueByFontsize, getNodePath, isNumber, isPage, isPageFragment, isPop } from '@tmagic/utils';
27
27
 
28
28
  import BaseService from '@editor/services//BaseService';
29
29
  import propsService from '@editor/services//props';
30
- import depService from '@editor/services/dep';
31
30
  import historyService from '@editor/services/history';
32
31
  import storageService, { Protocol } from '@editor/services/storage';
33
32
  import type {
@@ -661,16 +660,20 @@ class Editor extends BaseService {
661
660
  * @param config 组件节点配置
662
661
  * @returns
663
662
  */
664
- public copyWithRelated(config: MNode | MNode[]): void {
663
+ public copyWithRelated(config: MNode | MNode[], collectorOptions?: TargetOptions): void {
665
664
  const copyNodes: MNode[] = Array.isArray(config) ? config : [config];
666
- // 关联的组件也一并复制
667
- depService.clearByType(DepTargetType.RELATED_COMP_WHEN_COPY);
668
- depService.collect(copyNodes, true, DepTargetType.RELATED_COMP_WHEN_COPY);
669
- const customTarget = depService.getTarget(
670
- DepTargetType.RELATED_COMP_WHEN_COPY,
671
- DepTargetType.RELATED_COMP_WHEN_COPY,
672
- );
673
- if (customTarget) {
665
+
666
+ // 初始化复制组件相关的依赖收集器
667
+ if (collectorOptions && typeof collectorOptions.isTarget === 'function') {
668
+ const customTarget = new Target({
669
+ ...collectorOptions,
670
+ });
671
+
672
+ const coperWatcher = new Watcher();
673
+
674
+ coperWatcher.addTarget(customTarget);
675
+
676
+ coperWatcher.collect(copyNodes, {}, true, collectorOptions.type);
674
677
  Object.keys(customTarget.deps).forEach((nodeId: Id) => {
675
678
  const node = this.getNodeById(nodeId);
676
679
  if (!node) return;
@@ -686,6 +689,7 @@ class Editor extends BaseService {
686
689
  });
687
690
  });
688
691
  }
692
+
689
693
  storageService.setItem(COPY_STORAGE_KEY, copyNodes, {
690
694
  protocol: Protocol.OBJECT,
691
695
  });
@@ -696,7 +700,7 @@ class Editor extends BaseService {
696
700
  * @param position 粘贴的坐标
697
701
  * @returns 添加后的组件节点配置
698
702
  */
699
- public async paste(position: PastePosition = {}): Promise<MNode | MNode[] | void> {
703
+ public async paste(position: PastePosition = {}, collectorOptions?: TargetOptions): Promise<MNode | MNode[] | void> {
700
704
  const config: MNode[] = storageService.getItem(COPY_STORAGE_KEY);
701
705
  if (!Array.isArray(config)) return;
702
706
 
@@ -711,13 +715,18 @@ class Editor extends BaseService {
711
715
  }
712
716
  }
713
717
  const pasteConfigs = await this.doPaste(config, position);
714
- propsService.replaceRelateId(config, pasteConfigs);
718
+
719
+ if (collectorOptions && typeof collectorOptions.isTarget === 'function') {
720
+ propsService.replaceRelateId(config, pasteConfigs, collectorOptions);
721
+ }
722
+
715
723
  return this.add(pasteConfigs, parent);
716
724
  }
717
725
 
718
726
  public async doPaste(config: MNode[], position: PastePosition = {}): Promise<MNode[]> {
719
727
  propsService.clearRelateId();
720
- const pasteConfigs = beforePaste(position, cloneDeep(config));
728
+ const doc = this.get('stage')?.renderer.contentWindow?.document;
729
+ const pasteConfigs = beforePaste(position, cloneDeep(config), doc);
721
730
  return pasteConfigs;
722
731
  }
723
732
 
@@ -20,12 +20,11 @@ import { reactive } from 'vue';
20
20
  import { cloneDeep, mergeWith } from 'lodash-es';
21
21
  import { Writable } from 'type-fest';
22
22
 
23
- import { DepTargetType } from '@tmagic/dep';
23
+ import { Target, type TargetOptions, Watcher } from '@tmagic/dep';
24
24
  import type { FormConfig } from '@tmagic/form';
25
25
  import type { Id, MComponent, MNode } from '@tmagic/schema';
26
- import { getValueByKeyPath, guid, setValueByKeyPath, toLine } from '@tmagic/utils';
26
+ import { getNodePath, getValueByKeyPath, guid, setValueByKeyPath, toLine } from '@tmagic/utils';
27
27
 
28
- import depService from '@editor/services/dep';
29
28
  import editorService from '@editor/services/editor';
30
29
  import type { AsyncHookPlugin, PropsState, SyncHookPlugin } from '@editor/type';
31
30
  import { fillConfig } from '@editor/utils/props';
@@ -196,16 +195,24 @@ class Props extends BaseService {
196
195
  * @param originConfigs 原组件配置
197
196
  * @param targetConfigs 待替换的组件配置
198
197
  */
199
- public replaceRelateId(originConfigs: MNode[], targetConfigs: MNode[]) {
198
+ public replaceRelateId(originConfigs: MNode[], targetConfigs: MNode[], collectorOptions: TargetOptions) {
200
199
  const relateIdMap = this.getRelateIdMap();
200
+
201
201
  if (Object.keys(relateIdMap).length === 0) return;
202
202
 
203
- const target = depService.getTarget(DepTargetType.RELATED_COMP_WHEN_COPY, DepTargetType.RELATED_COMP_WHEN_COPY);
204
- if (!target) return;
203
+ const target = new Target({
204
+ ...collectorOptions,
205
+ });
206
+
207
+ const coperWatcher = new Watcher();
208
+
209
+ coperWatcher.addTarget(target);
210
+ coperWatcher.collect(originConfigs, {}, true, collectorOptions.type);
205
211
 
206
212
  originConfigs.forEach((config: MNode) => {
207
213
  const newId = relateIdMap[config.id];
208
- const targetConfig = targetConfigs.find((targetConfig) => targetConfig.id === newId);
214
+ const path = getNodePath(newId, targetConfigs);
215
+ const targetConfig = path[path.length - 1];
209
216
 
210
217
  if (!targetConfig) return;
211
218
 
@@ -213,9 +220,13 @@ class Props extends BaseService {
213
220
  const relateOriginId = getValueByKeyPath(fullKey, config);
214
221
  const relateTargetId = relateIdMap[relateOriginId];
215
222
  if (!relateTargetId) return;
216
-
217
223
  setValueByKeyPath(fullKey, relateTargetId, targetConfig);
218
224
  });
225
+
226
+ // 递归items
227
+ if (config.items && Array.isArray(config.items)) {
228
+ this.replaceRelateId(config.items, targetConfigs, collectorOptions);
229
+ }
219
230
  });
220
231
  }
221
232
 
@@ -90,6 +90,7 @@ class StageOverlay extends BaseService {
90
90
  public createStage(stageOptions: StageOptions = {}) {
91
91
  return useStage({
92
92
  ...stageOptions,
93
+ zoom: 1,
93
94
  runtimeUrl: '',
94
95
  autoScrollIntoView: false,
95
96
  render: async (stage: StageCore) => {
package/src/type.ts CHANGED
@@ -150,6 +150,7 @@ export interface StageOptions {
150
150
  renderType?: RenderType;
151
151
  guidesOptions?: Partial<GuidesOptions>;
152
152
  disabledMultiSelect?: boolean;
153
+ zoom?: number;
153
154
  }
154
155
 
155
156
  export interface StoreState {
@@ -1,5 +1,6 @@
1
1
  import { CascaderOption, FormConfig, FormState } from '@tmagic/form';
2
2
  import { DataSchema, DataSourceFieldType, DataSourceSchema } from '@tmagic/schema';
3
+ import { isNumber } from '@tmagic/utils';
3
4
 
4
5
  import BaseFormConfig from './formConfigs/base';
5
6
  import HttpFormConfig from './formConfigs/http';
@@ -165,20 +166,26 @@ export const getDisplayField = (dataSources: DataSourceSchema[], key: string) =>
165
166
  let dsText = '';
166
167
  let ds: DataSourceSchema | undefined;
167
168
  let fields: DataSchema[] | undefined;
168
-
169
169
  // 将模块解析成数据源对应的值
170
- match[1].split('.').forEach((item, index) => {
171
- if (index === 0) {
172
- ds = dataSources.find((ds) => ds.id === item);
173
- dsText += ds?.title || item;
174
- fields = ds?.fields;
175
- return;
176
- }
177
-
178
- const field = fields?.find((field) => field.name === item);
179
- fields = field?.fields;
180
- dsText += `.${field?.title || item}`;
181
- });
170
+ match[1]
171
+ .replaceAll(/\[(\d+)\]/g, '.$1')
172
+ .split('.')
173
+ .forEach((item, index) => {
174
+ if (index === 0) {
175
+ ds = dataSources.find((ds) => ds.id === item);
176
+ dsText += ds?.title || item;
177
+ fields = ds?.fields;
178
+ return;
179
+ }
180
+
181
+ if (isNumber(item)) {
182
+ dsText += `[${item}]`;
183
+ } else {
184
+ const field = fields?.find((field) => field.name === item);
185
+ fields = field?.fields;
186
+ dsText += `.${field?.title || item}`;
187
+ }
188
+ });
182
189
 
183
190
  displayState.push({
184
191
  type: 'var',
@@ -25,6 +25,8 @@ import { calcValueByFontsize, getNodePath, isNumber, isPage, isPageFragment, isP
25
25
 
26
26
  import { Layout } from '@editor/type';
27
27
  export const COPY_STORAGE_KEY = '$MagicEditorCopyData';
28
+ export const COPY_CODE_STORAGE_KEY = '$MagicEditorCopyCode';
29
+ export const COPY_DS_STORAGE_KEY = '$MagicEditorCopyDataSource';
28
30
 
29
31
  /**
30
32
  * 获取所有页面配置
@@ -0,0 +1,72 @@
1
+ import { EventEmitter } from 'events';
2
+
3
+ export interface IdleTaskEvents {
4
+ finish: [];
5
+ }
6
+
7
+ globalThis.requestIdleCallback =
8
+ globalThis.requestIdleCallback ||
9
+ function (cb) {
10
+ const start = Date.now();
11
+ return setTimeout(() => {
12
+ cb({
13
+ didTimeout: false,
14
+ timeRemaining() {
15
+ return Math.max(0, 50 - (Date.now() - start));
16
+ },
17
+ });
18
+ }, 1);
19
+ };
20
+
21
+ export class IdleTask<T = any> extends EventEmitter {
22
+ private taskList: {
23
+ handler: (data: T) => void;
24
+ data: T;
25
+ }[] = [];
26
+
27
+ private taskHandle: number | null = null;
28
+
29
+ public enqueueTask(taskHandler: (data: T) => void, taskData: T) {
30
+ this.taskList.push({
31
+ handler: taskHandler,
32
+ data: taskData,
33
+ });
34
+
35
+ if (!this.taskHandle) {
36
+ this.taskHandle = globalThis.requestIdleCallback(this.runTaskQueue.bind(this), { timeout: 10000 });
37
+ }
38
+ }
39
+
40
+ public on<Name extends keyof IdleTaskEvents, Param extends IdleTaskEvents[Name]>(
41
+ eventName: Name,
42
+ listener: (...args: Param) => void | Promise<void>,
43
+ ) {
44
+ return super.on(eventName, listener as any);
45
+ }
46
+
47
+ public once<Name extends keyof IdleTaskEvents, Param extends IdleTaskEvents[Name]>(
48
+ eventName: Name,
49
+ listener: (...args: Param) => void | Promise<void>,
50
+ ) {
51
+ return super.once(eventName, listener as any);
52
+ }
53
+
54
+ public emit<Name extends keyof IdleTaskEvents, Param extends IdleTaskEvents[Name]>(eventName: Name, ...args: Param) {
55
+ return super.emit(eventName, ...args);
56
+ }
57
+
58
+ private runTaskQueue(deadline: IdleDeadline) {
59
+ while ((deadline.timeRemaining() > 15 || deadline.didTimeout) && this.taskList.length) {
60
+ const task = this.taskList.shift();
61
+ task!.handler(task!.data);
62
+ }
63
+
64
+ if (this.taskList.length) {
65
+ this.taskHandle = globalThis.requestIdleCallback(this.runTaskQueue.bind(this), { timeout: 10000 });
66
+ } else {
67
+ this.taskHandle = 0;
68
+
69
+ this.emit('finish');
70
+ }
71
+ }
72
+ }
@@ -2,7 +2,7 @@ import { toRaw } from 'vue';
2
2
  import { isEmpty } from 'lodash-es';
3
3
 
4
4
  import { Id, MContainer, MNode, NodeType } from '@tmagic/schema';
5
- import { isPage, isPageFragment } from '@tmagic/utils';
5
+ import { calcValueByFontsize, isPage, isPageFragment } from '@tmagic/utils';
6
6
 
7
7
  import editorService from '@editor/services/editor';
8
8
  import propsService from '@editor/services/props';
@@ -15,7 +15,7 @@ import { generatePageNameByApp, getInitPositionStyle } from '@editor/utils/edito
15
15
  * @param config 待粘贴的元素配置(复制时保存的那份配置)
16
16
  * @returns
17
17
  */
18
- export const beforePaste = (position: PastePosition, config: MNode[]): MNode[] => {
18
+ export const beforePaste = (position: PastePosition, config: MNode[], doc?: Document): MNode[] => {
19
19
  if (!config[0]?.style) return config;
20
20
  const curNode = editorService.get('node');
21
21
  // 将数组中第一个元素的坐标作为参照点
@@ -29,7 +29,7 @@ export const beforePaste = (position: PastePosition, config: MNode[]): MNode[] =
29
29
  if (!isEmpty(pastePosition) && curNode?.items) {
30
30
  // 如果没有传入粘贴坐标则可能为键盘操作,不再转换
31
31
  // 如果粘贴时选中了容器,则将元素粘贴到容器内,坐标需要转换为相对于容器的坐标
32
- pastePosition = getPositionInContainer(pastePosition, curNode.id);
32
+ pastePosition = getPositionInContainer(pastePosition, curNode.id, doc);
33
33
  }
34
34
 
35
35
  // 将所有待粘贴元素坐标相对于多选第一个元素坐标重新计算,以保证多选粘贴后元素间距不变
@@ -71,12 +71,12 @@ export const beforePaste = (position: PastePosition, config: MNode[]): MNode[] =
71
71
  * @param id 元素id
72
72
  * @returns PastePosition 转换后的坐标
73
73
  */
74
- export const getPositionInContainer = (position: PastePosition = {}, id: Id) => {
74
+ export const getPositionInContainer = (position: PastePosition = {}, id: Id, doc?: Document) => {
75
75
  let { left = 0, top = 0 } = position;
76
76
  const parentEl = editorService.get('stage')?.renderer?.contentWindow?.document.getElementById(`${id}`);
77
77
  const parentElRect = parentEl?.getBoundingClientRect();
78
- left = left - (parentElRect?.left || 0);
79
- top = top - (parentElRect?.top || 0);
78
+ left = left - calcValueByFontsize(doc, parentElRect?.left || 0);
79
+ top = top - calcValueByFontsize(doc, parentElRect?.top || 0);
80
80
  return {
81
81
  left,
82
82
  top,
@@ -1,5 +1,4 @@
1
1
  import type { EventOption } from '@tmagic/core';
2
- import type { CustomTargetOptions } from '@tmagic/dep';
3
2
  import type { FormConfig, FormState } from '@tmagic/form';
4
3
  import type { DataSourceSchema, Id, MApp, MNode } from '@tmagic/schema';
5
4
  import StageCore, { ContainerHighlightType, type CustomizeMoveableOptionsCallbackConfig, type GuidesOptions, type MoveableOptions, RenderType, type UpdateDragEl } from '@tmagic/stage';
@@ -60,8 +59,6 @@ export interface EditorProps {
60
59
  };
61
60
  /** 禁用鼠标左键按下时就开始拖拽,需要先选中再可以拖拽 */
62
61
  disabledDragStart?: boolean;
63
- /** 自定义依赖收集器,复制组件时会将关联依赖一并复制 */
64
- collectorOptions?: CustomTargetOptions;
65
62
  /** 标尺配置 */
66
63
  guidesOptions?: Partial<GuidesOptions>;
67
64
  /** 禁止多选 */
package/types/index.d.ts CHANGED
@@ -43,6 +43,7 @@ export { default as LayoutContainer } from './components/SplitView.vue';
43
43
  export { default as SplitView } from './components/SplitView.vue';
44
44
  export { default as Resizer } from './components/Resizer.vue';
45
45
  export { default as CodeBlockEditor } from './components/CodeBlockEditor.vue';
46
+ export { default as FloatingBox } from './components/FloatingBox.vue';
46
47
  export { default as PageFragmentSelect } from './fields/PageFragmentSelect.vue';
47
48
  declare const _default: {
48
49
  install: (app: App, opt?: Partial<InstallOptions>) => void;