@tmagic/editor 1.3.16 → 1.4.0-beta.1

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 (116) hide show
  1. package/dist/style.css +11 -3
  2. package/dist/tmagic-editor.js +847 -523
  3. package/dist/tmagic-editor.umd.cjs +862 -538
  4. package/package.json +23 -21
  5. package/src/components/CodeBlockEditor.vue +50 -24
  6. package/src/components/FloatingBox.vue +53 -12
  7. package/src/fields/Code.vue +12 -10
  8. package/src/fields/CodeLink.vue +9 -9
  9. package/src/fields/CodeSelect.vue +7 -5
  10. package/src/fields/CodeSelectCol.vue +1 -1
  11. package/src/fields/DataSourceFieldSelect.vue +118 -27
  12. package/src/fields/DataSourceFields.vue +10 -3
  13. package/src/fields/DataSourceInput.vue +7 -7
  14. package/src/fields/DataSourceMethodSelect.vue +1 -1
  15. package/src/fields/DataSourceMethods.vue +3 -3
  16. package/src/fields/DataSourceMocks.vue +3 -3
  17. package/src/fields/DataSourceSelect.vue +6 -18
  18. package/src/fields/EventSelect.vue +1 -1
  19. package/src/fields/KeyValue.vue +8 -8
  20. package/src/fields/PageFragmentSelect.vue +1 -1
  21. package/src/fields/UISelect.vue +5 -5
  22. package/src/hooks/use-code-block-edit.ts +0 -1
  23. package/src/index.ts +2 -2
  24. package/src/layouts/NavMenu.vue +21 -2
  25. package/src/layouts/sidebar/Sidebar.vue +5 -0
  26. package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +0 -1
  27. package/src/services/BaseService.ts +5 -2
  28. package/src/services/codeBlock.ts +14 -8
  29. package/src/services/dataSource.ts +39 -26
  30. package/src/services/editor.ts +46 -25
  31. package/src/services/events.ts +4 -4
  32. package/src/services/props.ts +44 -35
  33. package/src/services/stageOverlay.ts +14 -7
  34. package/src/services/storage.ts +14 -8
  35. package/src/services/ui.ts +20 -19
  36. package/src/theme/code-editor.scss +6 -0
  37. package/src/theme/floating-box.scss +2 -0
  38. package/src/theme/sidebar.scss +6 -3
  39. package/src/type.ts +81 -37
  40. package/src/utils/operator.ts +37 -39
  41. package/src/utils/props.ts +174 -37
  42. package/types/components/CodeBlockEditor.vue.d.ts +4 -4
  43. package/types/components/CodeParams.vue.d.ts +5 -5
  44. package/types/components/ContentMenu.vue.d.ts +12 -12
  45. package/types/components/FloatingBox.vue.d.ts +14 -12
  46. package/types/components/Icon.vue.d.ts +3 -3
  47. package/types/components/Resizer.vue.d.ts +3 -3
  48. package/types/components/ScrollBar.vue.d.ts +3 -3
  49. package/types/components/ScrollViewer.vue.d.ts +12 -12
  50. package/types/components/SearchInput.vue.d.ts +1 -1
  51. package/types/components/SplitView.vue.d.ts +11 -11
  52. package/types/components/ToolButton.vue.d.ts +13 -13
  53. package/types/components/Tree.vue.d.ts +24 -14
  54. package/types/components/TreeNode.vue.d.ts +22 -12
  55. package/types/fields/Code.vue.d.ts +14 -14
  56. package/types/fields/CodeLink.vue.d.ts +6 -10
  57. package/types/fields/CodeSelect.vue.d.ts +14 -14
  58. package/types/fields/CodeSelectCol.vue.d.ts +11 -11
  59. package/types/fields/DataSourceFieldSelect.vue.d.ts +11 -11
  60. package/types/fields/DataSourceFields.vue.d.ts +11 -11
  61. package/types/fields/DataSourceInput.vue.d.ts +14 -18
  62. package/types/fields/DataSourceMethodSelect.vue.d.ts +11 -11
  63. package/types/fields/DataSourceMethods.vue.d.ts +11 -11
  64. package/types/fields/DataSourceMocks.vue.d.ts +11 -11
  65. package/types/fields/DataSourceSelect.vue.d.ts +13 -26
  66. package/types/fields/EventSelect.vue.d.ts +3 -3
  67. package/types/fields/KeyValue.vue.d.ts +14 -18
  68. package/types/fields/PageFragmentSelect.vue.d.ts +11 -11
  69. package/types/fields/UISelect.vue.d.ts +8 -4
  70. package/types/hooks/use-code-block-edit.d.ts +51 -89
  71. package/types/hooks/use-data-source-method.d.ts +51 -89
  72. package/types/hooks/use-node-status.d.ts +6 -1
  73. package/types/icons/AppManageIcon.vue.d.ts +1 -1
  74. package/types/icons/CenterIcon.vue.d.ts +1 -1
  75. package/types/icons/CodeIcon.vue.d.ts +1 -1
  76. package/types/icons/FolderMinusIcon.vue.d.ts +1 -1
  77. package/types/icons/PinIcon.vue.d.ts +1 -1
  78. package/types/icons/PinnedIcon.vue.d.ts +1 -1
  79. package/types/layouts/AddPageBox.vue.d.ts +3 -3
  80. package/types/layouts/CodeEditor.vue.d.ts +12 -12
  81. package/types/layouts/Framework.vue.d.ts +9 -9
  82. package/types/layouts/NavMenu.vue.d.ts +11 -11
  83. package/types/layouts/PropsPanel.vue.d.ts +193 -243
  84. package/types/layouts/page-bar/AddButton.vue.d.ts +3 -3
  85. package/types/layouts/page-bar/PageBar.vue.d.ts +8 -8
  86. package/types/layouts/page-bar/PageBarScrollContainer.vue.d.ts +8 -8
  87. package/types/layouts/page-bar/SwitchTypeButton.vue.d.ts +3 -3
  88. package/types/layouts/sidebar/ComponentListPanel.vue.d.ts +1 -1
  89. package/types/layouts/sidebar/Sidebar.vue.d.ts +12 -12
  90. package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +10 -10
  91. package/types/layouts/sidebar/code-block/CodeBlockListPanel.vue.d.ts +9 -9
  92. package/types/layouts/sidebar/data-source/DataSourceConfigPanel.vue.d.ts +3 -3
  93. package/types/layouts/sidebar/data-source/DataSourceList.vue.d.ts +3 -3
  94. package/types/layouts/sidebar/data-source/DataSourceListPanel.vue.d.ts +9 -9
  95. package/types/layouts/sidebar/layer/LayerMenu.vue.d.ts +11 -11
  96. package/types/layouts/sidebar/layer/LayerNodeTool.vue.d.ts +3 -3
  97. package/types/layouts/sidebar/layer/LayerPanel.vue.d.ts +9 -9
  98. package/types/layouts/sidebar/layer/use-click.d.ts +42 -76
  99. package/types/layouts/sidebar/layer/use-node-status.d.ts +6 -1
  100. package/types/layouts/workspace/Breadcrumb.vue.d.ts +1 -1
  101. package/types/layouts/workspace/Workspace.vue.d.ts +12 -12
  102. package/types/layouts/workspace/viewer/NodeListMenu.vue.d.ts +1 -1
  103. package/types/layouts/workspace/viewer/Stage.vue.d.ts +11 -11
  104. package/types/layouts/workspace/viewer/StageOverlay.vue.d.ts +1 -1
  105. package/types/layouts/workspace/viewer/ViewerMenu.vue.d.ts +12 -12
  106. package/types/services/BaseService.d.ts +5 -2
  107. package/types/services/codeBlock.d.ts +8 -0
  108. package/types/services/dataSource.d.ts +9 -2
  109. package/types/services/editor.d.ts +8 -1
  110. package/types/services/props.d.ts +15 -22
  111. package/types/services/stageOverlay.d.ts +8 -1
  112. package/types/services/storage.d.ts +8 -0
  113. package/types/services/ui.d.ts +15 -9
  114. package/types/type.d.ts +50 -32
  115. package/types/utils/operator.d.ts +1 -1
  116. package/types/utils/props.d.ts +2 -13
@@ -18,6 +18,7 @@
18
18
 
19
19
  import { reactive } from 'vue';
20
20
  import { cloneDeep, mergeWith } from 'lodash-es';
21
+ import { Writable } from 'type-fest';
21
22
 
22
23
  import { DepTargetType } from '@tmagic/dep';
23
24
  import type { FormConfig } from '@tmagic/form';
@@ -26,11 +27,26 @@ import { getValueByKeyPath, guid, setValueByKeyPath, toLine } from '@tmagic/util
26
27
 
27
28
  import depService from '@editor/services/dep';
28
29
  import editorService from '@editor/services/editor';
29
- import type { PropsState } from '@editor/type';
30
+ import type { AsyncHookPlugin, PropsState, SyncHookPlugin } from '@editor/type';
30
31
  import { fillConfig } from '@editor/utils/props';
31
32
 
32
33
  import BaseService from './BaseService';
33
34
 
35
+ const canUsePluginMethods = {
36
+ async: [
37
+ 'setPropsConfig',
38
+ 'getPropsConfig',
39
+ 'setPropsValue',
40
+ 'getPropsValue',
41
+ 'fillConfig',
42
+ 'getDefaultPropsValue',
43
+ ] as const,
44
+ sync: ['createId', 'setNewItemId'] as const,
45
+ };
46
+
47
+ type AsyncMethodName = Writable<(typeof canUsePluginMethods)['async']>;
48
+ type SyncMethodName = Writable<(typeof canUsePluginMethods)['sync']>;
49
+
34
50
  class Props extends BaseService {
35
51
  private state = reactive<PropsState>({
36
52
  propsConfigMap: {},
@@ -40,14 +56,8 @@ class Props extends BaseService {
40
56
 
41
57
  constructor() {
42
58
  super([
43
- { name: 'setPropsConfig', isAsync: true },
44
- { name: 'getPropsConfig', isAsync: true },
45
- { name: 'setPropsValue', isAsync: true },
46
- { name: 'getPropsValue', isAsync: true },
47
- { name: 'createId', isAsync: false },
48
- { name: 'setNewItemId', isAsync: true },
49
- { name: 'fillConfig', isAsync: true },
50
- { name: 'getDefaultPropsValue', isAsync: true },
59
+ ...canUsePluginMethods.async.map((methodName) => ({ name: methodName, isAsync: true })),
60
+ ...canUsePluginMethods.sync.map((methodName) => ({ name: methodName, isAsync: false })),
51
61
  ]);
52
62
  }
53
63
 
@@ -58,17 +68,12 @@ class Props extends BaseService {
58
68
  this.emit('props-configs-change');
59
69
  }
60
70
 
61
- public async fillConfig(config: FormConfig) {
62
- return fillConfig(config);
71
+ public async fillConfig(config: FormConfig, labelWidth?: string) {
72
+ return fillConfig(config, typeof labelWidth !== 'function' ? labelWidth : '80px');
63
73
  }
64
74
 
65
- /**
66
- * 为指定类型组件设置组件属性表单配置
67
- * @param type 组件类型
68
- * @param config 组件属性表单配置
69
- */
70
75
  public async setPropsConfig(type: string, config: FormConfig) {
71
- this.state.propsConfigMap[type] = await this.fillConfig(Array.isArray(config) ? config : [config]);
76
+ this.state.propsConfigMap[toLine(type)] = await this.fillConfig(Array.isArray(config) ? config : [config]);
72
77
  }
73
78
 
74
79
  /**
@@ -81,7 +86,7 @@ class Props extends BaseService {
81
86
  return await this.getPropsConfig('button');
82
87
  }
83
88
 
84
- return cloneDeep(this.state.propsConfigMap[type] || (await this.fillConfig([])));
89
+ return cloneDeep(this.state.propsConfigMap[toLine(type)] || (await this.fillConfig([])));
85
90
  }
86
91
 
87
92
  public setPropsValues(values: Record<string, Partial<MNode>>) {
@@ -96,7 +101,7 @@ class Props extends BaseService {
96
101
  * @param value 组件初始值
97
102
  */
98
103
  public async setPropsValue(type: string, value: Partial<MNode>) {
99
- this.state.propsValueMap[type] = value;
104
+ this.state.propsValueMap[toLine(type)] = value;
100
105
  }
101
106
 
102
107
  /**
@@ -104,7 +109,9 @@ class Props extends BaseService {
104
109
  * @param type 组件类型
105
110
  * @returns 组件初始值
106
111
  */
107
- public async getPropsValue(type: string, { inputEvent, ...defaultValue }: Record<string, any> = {}) {
112
+ public async getPropsValue(componentType: string, { inputEvent, ...defaultValue }: Record<string, any> = {}) {
113
+ const type = toLine(componentType);
114
+
108
115
  if (type === 'area') {
109
116
  const value = (await this.getPropsValue('button')) as MComponent;
110
117
  value.className = 'action-area';
@@ -115,16 +122,14 @@ class Props extends BaseService {
115
122
  return value;
116
123
  }
117
124
 
118
- const [id, defaultPropsValue, data] = await Promise.all([
119
- this.createId(type),
120
- this.getDefaultPropsValue(type),
121
- this.setNewItemId(
122
- cloneDeep({
123
- type,
124
- ...defaultValue,
125
- } as any),
126
- ),
127
- ]);
125
+ const id = this.createId(type);
126
+ const defaultPropsValue = this.getDefaultPropsValue(type);
127
+ const data = this.setNewItemId(
128
+ cloneDeep({
129
+ type,
130
+ ...defaultValue,
131
+ } as any),
132
+ );
128
133
 
129
134
  return {
130
135
  id,
@@ -133,7 +138,7 @@ class Props extends BaseService {
133
138
  };
134
139
  }
135
140
 
136
- public async createId(type: string | number): Promise<string> {
141
+ public createId(type: string | number): string {
137
142
  return `${type}_${guid()}`;
138
143
  }
139
144
 
@@ -144,16 +149,16 @@ class Props extends BaseService {
144
149
  * @param {Boolean} force 是否强制设置新的ID
145
150
  */
146
151
  /* eslint no-param-reassign: ["error", { "props": false }] */
147
- public async setNewItemId(config: MNode, force = true) {
152
+ public setNewItemId(config: MNode, force = true) {
148
153
  if (force || editorService.getNodeById(config.id)) {
149
- const newId = await this.createId(config.type || 'component');
154
+ const newId = this.createId(config.type || 'component');
150
155
  this.setRelateId(config.id, newId);
151
156
  config.id = newId;
152
157
  }
153
158
 
154
159
  if (config.items && Array.isArray(config.items)) {
155
160
  for (const item of config.items) {
156
- await this.setNewItemId(item);
161
+ this.setNewItemId(item);
157
162
  }
158
163
  }
159
164
 
@@ -165,7 +170,7 @@ class Props extends BaseService {
165
170
  * @param type 组件类型
166
171
  * @returns Object
167
172
  */
168
- public async getDefaultPropsValue(type: string) {
173
+ public getDefaultPropsValue(type: string) {
169
174
  return ['page', 'container'].includes(type)
170
175
  ? {
171
176
  type,
@@ -227,6 +232,10 @@ class Props extends BaseService {
227
232
  this.removeAllPlugins();
228
233
  }
229
234
 
235
+ public usePlugin(options: AsyncHookPlugin<AsyncMethodName, Props> & SyncHookPlugin<SyncMethodName, Props>): void {
236
+ super.usePlugin(options);
237
+ }
238
+
230
239
  /**
231
240
  * 获取setNewItemId前后映射关系
232
241
  * @param oldId 原组件ID
@@ -1,11 +1,19 @@
1
1
  import { reactive } from 'vue';
2
+ import type { Writable } from 'type-fest';
2
3
 
3
4
  import StageCore from '@tmagic/stage';
4
5
 
5
6
  import { useStage } from '@editor/hooks/use-stage';
6
7
  import BaseService from '@editor/services//BaseService';
7
8
  import editorService from '@editor/services//editor';
8
- import type { StageOptions, StageOverlayState } from '@editor/type';
9
+ import type { StageOptions, StageOverlayState, SyncHookPlugin } from '@editor/type';
10
+
11
+ const canUsePluginMethods = {
12
+ async: [],
13
+ sync: ['openOverlay', 'closeOverlay', 'updateOverlay', 'createStage'] as const,
14
+ };
15
+
16
+ type SyncMethodName = Writable<(typeof canUsePluginMethods)['sync']>;
9
17
 
10
18
  class StageOverlay extends BaseService {
11
19
  private state: StageOverlayState = reactive({
@@ -20,12 +28,7 @@ class StageOverlay extends BaseService {
20
28
  });
21
29
 
22
30
  constructor() {
23
- super([
24
- { name: 'openOverlay', isAsync: false },
25
- { name: 'closeOverlay', isAsync: false },
26
- { name: 'updateOverlay', isAsync: false },
27
- { name: 'createStage', isAsync: false },
28
- ]);
31
+ super(canUsePluginMethods.sync.map((methodName) => ({ name: methodName, isAsync: false })));
29
32
 
30
33
  this.get('wrapDiv').classList.add('tmagic-editor-sub-stage-wrap');
31
34
  }
@@ -111,6 +114,10 @@ class StageOverlay extends BaseService {
111
114
  });
112
115
  }
113
116
 
117
+ public usePlugin(options: SyncHookPlugin<SyncMethodName, StageOverlay>): void {
118
+ super.usePlugin(options);
119
+ }
120
+
114
121
  private createContentEl() {
115
122
  const sourceEl = this.get('sourceEl');
116
123
  if (!sourceEl) return;
@@ -1,5 +1,7 @@
1
1
  import serialize from 'serialize-javascript';
2
+ import type { Writable } from 'type-fest';
2
3
 
4
+ import type { SyncHookPlugin } from '@editor/type';
3
5
  import { getConfig } from '@editor/utils/config';
4
6
 
5
7
  import BaseService from './BaseService';
@@ -17,6 +19,13 @@ export enum Protocol {
17
19
  BOOLEAN = 'boolean',
18
20
  }
19
21
 
22
+ const canUsePluginMethods = {
23
+ async: [],
24
+ sync: ['getStorage', 'getNamespace', 'clear', 'getItem', 'removeItem', 'setItem'] as const,
25
+ };
26
+
27
+ type SyncMethodName = Writable<(typeof canUsePluginMethods)['sync']>;
28
+
20
29
  /**
21
30
  * 数据存储服务
22
31
  */
@@ -25,14 +34,7 @@ export class WebStorage extends BaseService {
25
34
  private namespace = 'tmagic';
26
35
 
27
36
  constructor() {
28
- super([
29
- { name: 'getStorage', isAsync: false },
30
- { name: 'getNamespace', isAsync: false },
31
- { name: 'clear', isAsync: false },
32
- { name: 'getItem', isAsync: false },
33
- { name: 'removeItem', isAsync: false },
34
- { name: 'setItem', isAsync: false },
35
- ]);
37
+ super(canUsePluginMethods.sync.map((methodName) => ({ name: methodName, isAsync: false })));
36
38
  }
37
39
 
38
40
  /**
@@ -123,6 +125,10 @@ export class WebStorage extends BaseService {
123
125
  this.removeAllPlugins();
124
126
  }
125
127
 
128
+ public usePlugin(options: SyncHookPlugin<SyncMethodName, WebStorage>): void {
129
+ super.usePlugin(options);
130
+ }
131
+
126
132
  private getValueAndProtocol(value: string | null) {
127
133
  let protocol = '';
128
134
 
@@ -17,11 +17,12 @@
17
17
  */
18
18
 
19
19
  import { reactive } from 'vue';
20
+ import type { Writable } from 'type-fest';
20
21
 
21
22
  import { convertToNumber } from '@tmagic/utils';
22
23
 
23
24
  import editorService from '@editor/services/editor';
24
- import type { StageRect, UiState } from '@editor/type';
25
+ import type { AsyncHookPlugin, StageRect, UiState } from '@editor/type';
25
26
 
26
27
  import BaseService from './BaseService';
27
28
 
@@ -46,16 +47,25 @@ const state = reactive<UiState>({
46
47
  showRule: true,
47
48
  propsPanelSize: 'small',
48
49
  showAddPageButton: true,
49
- floatBox: new Map(),
50
50
  hideSlideBar: false,
51
+ navMenuRect: {
52
+ left: 0,
53
+ top: 0,
54
+ width: 0,
55
+ height: 0,
56
+ },
51
57
  });
52
58
 
59
+ const canUsePluginMethods = {
60
+ async: ['zoom', 'calcZoom'] as const,
61
+ sync: [] as const,
62
+ };
63
+
64
+ type AsyncMethodName = Writable<(typeof canUsePluginMethods)['async']>;
65
+
53
66
  class Ui extends BaseService {
54
67
  constructor() {
55
- super([
56
- { name: 'zoom', isAsync: true },
57
- { name: 'calcZoom', isAsync: true },
58
- ]);
68
+ super(canUsePluginMethods.async.map((methodName) => ({ name: methodName, isAsync: true })));
59
69
  }
60
70
 
61
71
  public set<K extends keyof UiState, T extends UiState[K]>(name: K, value: T) {
@@ -105,19 +115,6 @@ class Ui extends BaseService {
105
115
  return Math.min((width - 60) / stageWidth || 1, (height - 80) / stageHeight || 1);
106
116
  }
107
117
 
108
- public async setFloatBox(keys: string[]) {
109
- const map = state.floatBox;
110
- for (const key of keys) {
111
- if (map.get(key)) continue;
112
- map.set(key, {
113
- status: false,
114
- zIndex: 99,
115
- top: 0,
116
- left: 0,
117
- });
118
- }
119
- }
120
-
121
118
  public resetState() {
122
119
  this.set('showSrc', false);
123
120
  this.set('uiSelectMode', false);
@@ -134,6 +131,10 @@ class Ui extends BaseService {
134
131
  this.removeAllPlugins();
135
132
  }
136
133
 
134
+ public usePlugin(options: AsyncHookPlugin<AsyncMethodName, Ui>): void {
135
+ super.usePlugin(options);
136
+ }
137
+
137
138
  private async setStageRect(value: StageRect) {
138
139
  state.stageRect = {
139
140
  ...state.stageRect,
@@ -1,3 +1,9 @@
1
+ .m-container-vs-code {
2
+ .el-form-item {
3
+ margin-bottom: 0;
4
+ }
5
+ }
6
+
1
7
  .magic-code-editor {
2
8
  width: 100%;
3
9
  }
@@ -5,6 +5,8 @@
5
5
  border: 1px solid $--border-color;
6
6
  display: flex;
7
7
  flex-direction: column;
8
+ max-height: 100%;
9
+ max-width: 100%;
8
10
 
9
11
  .m-editor-float-box-title {
10
12
  text-align: center;
@@ -72,12 +72,15 @@
72
72
  }
73
73
 
74
74
  .m-editor-slide-list-box {
75
- min-width: 270px;
75
+ display: flex;
76
+ min-width: 240px;
76
77
  min-height: 500px;
77
- max-height: 1024px;
78
78
  > div {
79
79
  &:first-child {
80
- width: 100%;
80
+ min-width: 240px;
81
81
  }
82
82
  }
83
+ .m-form-box {
84
+ border-left: 1px solid #e0e0e0;
85
+ }
83
86
  }
package/src/type.ts CHANGED
@@ -17,9 +17,20 @@
17
17
  */
18
18
 
19
19
  import type { Component } from 'vue';
20
+ import type { PascalCasedProperties } from 'type-fest';
20
21
 
21
- import type { ColumnConfig, FilterFunction, FormConfig, FormItem } from '@tmagic/form';
22
- import type { CodeBlockContent, CodeBlockDSL, Id, MApp, MContainer, MNode, MPage, MPageFragment } from '@tmagic/schema';
22
+ import type { ChildConfig, ColumnConfig, FilterFunction, FormConfig, FormItem, Input } from '@tmagic/form';
23
+ import type {
24
+ CodeBlockContent,
25
+ CodeBlockDSL,
26
+ DataSourceFieldType,
27
+ Id,
28
+ MApp,
29
+ MContainer,
30
+ MNode,
31
+ MPage,
32
+ MPageFragment,
33
+ } from '@tmagic/schema';
23
34
  import type StageCore from '@tmagic/stage';
24
35
  import type {
25
36
  ContainerHighlightType,
@@ -225,20 +236,16 @@ export interface UiState {
225
236
  propsPanelSize: 'large' | 'default' | 'small';
226
237
  /** 是否显示新增页面按钮 */
227
238
  showAddPageButton: boolean;
228
-
229
- /** slide 拖拽悬浮窗 state */
230
- floatBox: Map<
231
- string,
232
- {
233
- status: boolean;
234
- zIndex: number;
235
- top: number;
236
- left: number;
237
- }
238
- >;
239
-
240
239
  /** 是否隐藏侧边栏 */
241
240
  hideSlideBar: boolean;
241
+
242
+ // navMenu 的宽高
243
+ navMenuRect: {
244
+ left: number;
245
+ top: number;
246
+ width: number;
247
+ height: number;
248
+ };
242
249
  }
243
250
 
244
251
  export interface EditorNodeInfo {
@@ -595,42 +602,44 @@ export interface KeyBindingCacheItem {
595
602
  bound: boolean;
596
603
  }
597
604
 
598
- export interface CodeSelectColConfig {
605
+ export interface CodeSelectColConfig extends FormItem {
599
606
  type: 'code-select-col';
600
- name: string;
601
- text: string;
602
- labelWidth?: number | string;
603
- disabled?: boolean | FilterFunction;
607
+ /** 是否可以编辑代码块,disable表示的是是否可以选择代码块 */
604
608
  notEditable?: boolean | FilterFunction;
605
- display?: boolean | FilterFunction;
606
609
  }
607
610
 
608
- export interface PageFragmentSelectConfig {
611
+ export interface PageFragmentSelectConfig extends FormItem {
609
612
  type: 'page-fragment-select';
610
- name: string;
611
- text: string;
612
- labelWidth?: number | string;
613
- disabled?: boolean | FilterFunction;
614
- display?: boolean | FilterFunction;
615
613
  }
616
614
 
617
- export interface DataSourceMethodSelectConfig {
615
+ export interface DataSourceSelect extends FormItem, Input {
616
+ type: 'data-source-select';
617
+ /** 数据源类型: base、http... */
618
+ dataSourceType?: string;
619
+ /** 是否要编译成数据源的data。
620
+ * id: 不编译,就是要数据源id;
621
+ * value: 要编译(数据源data)
622
+ * */
623
+ value?: 'id' | 'value';
624
+ }
625
+
626
+ export interface DataSourceMethodSelectConfig extends FormItem {
618
627
  type: 'data-source-method-select';
619
- name: string;
620
- text: string;
621
- labelWidth?: number | string;
622
- disabled?: boolean | FilterFunction;
628
+ /** 是否可以编辑数据源,disable表示的是是否可以选择数据源 */
623
629
  notEditable?: boolean | FilterFunction;
624
- display?: boolean | FilterFunction;
625
630
  }
626
631
 
627
- export interface DataSourceFieldSelectConfig {
632
+ export interface DataSourceFieldSelectConfig extends FormItem {
628
633
  type: 'data-source-field-select';
629
- name: string;
634
+ /** 是否要编译成数据源的data。
635
+ * key: 不编译,就是要数据源id和field name;
636
+ * value: 要编译(数据源data[`${filed}`])
637
+ * */
630
638
  value?: 'key' | 'value';
631
- labelWidth?: number | string;
632
- disabled?: boolean | FilterFunction;
633
- display?: boolean | FilterFunction;
639
+ /** 是否严格的遵守父子节点不互相关联 */
640
+ checkStrictly?: boolean;
641
+ dataSourceFieldType?: DataSourceFieldType[];
642
+ fieldConfig?: ChildConfig;
634
643
  }
635
644
 
636
645
  /** 可新增的数据源类型选项 */
@@ -670,3 +679,38 @@ export interface TreeNodeData {
670
679
  items?: TreeNodeData[];
671
680
  [key: string]: any;
672
681
  }
682
+
683
+ export type AsyncBeforeHook<Value extends Array<string>, C extends Record<Value[number], (...args: any) => any>> = {
684
+ [K in Value[number]]?: (...args: Parameters<C[K]>) => Promise<Parameters<C[K]>> | Parameters<C[K]>;
685
+ };
686
+
687
+ export type AsyncAfterHook<Value extends Array<string>, C extends Record<Value[number], (...args: any) => any>> = {
688
+ [K in Value[number]]?: (
689
+ result: Awaited<ReturnType<C[K]>>,
690
+ ...args: Parameters<C[K]>
691
+ ) => ReturnType<C[K]> | Awaited<ReturnType<C[K]>>;
692
+ };
693
+
694
+ export type SyncBeforeHook<Value extends Array<string>, C extends Record<Value[number], (...args: any) => any>> = {
695
+ [K in Value[number]]?: (...args: Parameters<C[K]>) => Parameters<C[K]>;
696
+ };
697
+
698
+ export type SyncAfterHook<Value extends Array<string>, C extends Record<Value[number], (...args: any) => any>> = {
699
+ [K in Value[number]]?: (result: ReturnType<C[K]>, ...args: Parameters<C[K]>) => ReturnType<C[K]>;
700
+ };
701
+
702
+ export type AddPrefixToObject<T, P extends string> = {
703
+ [K in keyof T as K extends string ? `${P}${K}` : never]: T[K];
704
+ };
705
+
706
+ export type AsyncHookPlugin<
707
+ T extends Array<string>,
708
+ C extends Record<T[number], (...args: any) => any>,
709
+ > = AddPrefixToObject<PascalCasedProperties<AsyncBeforeHook<T, C>>, 'before'> &
710
+ AddPrefixToObject<PascalCasedProperties<AsyncAfterHook<T, C>>, 'after'>;
711
+
712
+ export type SyncHookPlugin<
713
+ T extends Array<string>,
714
+ C extends Record<T[number], (...args: any) => any>,
715
+ > = AddPrefixToObject<PascalCasedProperties<SyncBeforeHook<T, C>>, 'before'> &
716
+ AddPrefixToObject<PascalCasedProperties<SyncAfterHook<T, C>>, 'after'>;
@@ -15,55 +15,53 @@ import { generatePageNameByApp, getInitPositionStyle } from '@editor/utils/edito
15
15
  * @param config 待粘贴的元素配置(复制时保存的那份配置)
16
16
  * @returns
17
17
  */
18
- export const beforePaste = async (position: PastePosition, config: MNode[]): Promise<MNode[]> => {
18
+ export const beforePaste = (position: PastePosition, config: MNode[]): MNode[] => {
19
19
  if (!config[0]?.style) return config;
20
20
  const curNode = editorService.get('node');
21
21
  // 将数组中第一个元素的坐标作为参照点
22
22
  const { left: referenceLeft, top: referenceTop } = config[0].style;
23
23
  // 坐标校准后的粘贴数据
24
- const pasteConfigs: MNode[] = await Promise.all(
25
- config.map(async (configItem: MNode): Promise<MNode> => {
26
- // 解构 position 对象,从 position 删除 offsetX、offsetY字段
27
- const { offsetX = 0, offsetY = 0, ...positionClone } = position;
28
- let pastePosition = positionClone;
24
+ const pasteConfigs: MNode[] = config.map((configItem: MNode): MNode => {
25
+ // 解构 position 对象,从 position 删除 offsetX、offsetY字段
26
+ const { offsetX = 0, offsetY = 0, ...positionClone } = position;
27
+ let pastePosition = positionClone;
29
28
 
30
- if (!isEmpty(pastePosition) && curNode?.items) {
31
- // 如果没有传入粘贴坐标则可能为键盘操作,不再转换
32
- // 如果粘贴时选中了容器,则将元素粘贴到容器内,坐标需要转换为相对于容器的坐标
33
- pastePosition = getPositionInContainer(pastePosition, curNode.id);
34
- }
29
+ if (!isEmpty(pastePosition) && curNode?.items) {
30
+ // 如果没有传入粘贴坐标则可能为键盘操作,不再转换
31
+ // 如果粘贴时选中了容器,则将元素粘贴到容器内,坐标需要转换为相对于容器的坐标
32
+ pastePosition = getPositionInContainer(pastePosition, curNode.id);
33
+ }
35
34
 
36
- // 将所有待粘贴元素坐标相对于多选第一个元素坐标重新计算,以保证多选粘贴后元素间距不变
37
- if (pastePosition.left && configItem.style?.left) {
38
- pastePosition.left = configItem.style.left - referenceLeft + pastePosition.left;
39
- }
40
- if (pastePosition.top && configItem.style?.top) {
41
- pastePosition.top = configItem.style?.top - referenceTop + pastePosition.top;
42
- }
43
- const pasteConfig = await propsService.setNewItemId(configItem, false);
35
+ // 将所有待粘贴元素坐标相对于多选第一个元素坐标重新计算,以保证多选粘贴后元素间距不变
36
+ if (pastePosition.left && configItem.style?.left) {
37
+ pastePosition.left = configItem.style.left - referenceLeft + pastePosition.left;
38
+ }
39
+ if (pastePosition.top && configItem.style?.top) {
40
+ pastePosition.top = configItem.style?.top - referenceTop + pastePosition.top;
41
+ }
42
+ const pasteConfig = propsService.setNewItemId(configItem, false);
44
43
 
45
- if (pasteConfig.style) {
46
- const { left, top } = pasteConfig.style;
47
- // 判断能转换为数字时,做粘贴偏移量计算
48
- if (typeof left === 'number' || (!!left && !isNaN(Number(left)))) {
49
- pasteConfig.style.left = Number(left) + offsetX;
50
- }
51
- if (typeof top === 'number' || (!!top && !isNaN(Number(top)))) {
52
- pasteConfig.style.top = Number(top) + offsetY;
53
- }
54
-
55
- pasteConfig.style = {
56
- ...pasteConfig.style,
57
- ...pastePosition,
58
- };
44
+ if (pasteConfig.style) {
45
+ const { left, top } = pasteConfig.style;
46
+ // 判断能转换为数字时,做粘贴偏移量计算
47
+ if (typeof left === 'number' || (!!left && !isNaN(Number(left)))) {
48
+ pasteConfig.style.left = Number(left) + offsetX;
59
49
  }
60
- const root = editorService.get('root');
61
- if ((isPage(pasteConfig) || isPageFragment(pasteConfig)) && root) {
62
- pasteConfig.name = generatePageNameByApp(root, isPage(pasteConfig) ? NodeType.PAGE : NodeType.PAGE_FRAGMENT);
50
+ if (typeof top === 'number' || (!!top && !isNaN(Number(top)))) {
51
+ pasteConfig.style.top = Number(top) + offsetY;
63
52
  }
64
- return pasteConfig as MNode;
65
- }),
66
- );
53
+
54
+ pasteConfig.style = {
55
+ ...pasteConfig.style,
56
+ ...pastePosition,
57
+ };
58
+ }
59
+ const root = editorService.get('root');
60
+ if ((isPage(pasteConfig) || isPageFragment(pasteConfig)) && root) {
61
+ pasteConfig.name = generatePageNameByApp(root, isPage(pasteConfig) ? NodeType.PAGE : NodeType.PAGE_FRAGMENT);
62
+ }
63
+ return pasteConfig as MNode;
64
+ });
67
65
  return pasteConfigs;
68
66
  };
69
67