@tmagic/editor 1.2.0-beta.9 → 1.2.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 (103) hide show
  1. package/dist/style.css +62 -34
  2. package/dist/{tmagic-editor.mjs → tmagic-editor.js} +1641 -1266
  3. package/dist/tmagic-editor.js.map +1 -0
  4. package/dist/{tmagic-editor.umd.js → tmagic-editor.umd.cjs} +1649 -1270
  5. package/dist/tmagic-editor.umd.cjs.map +1 -0
  6. package/package.json +16 -14
  7. package/src/Editor.vue +66 -40
  8. package/src/components/CodeDraftEditor.vue +137 -0
  9. package/src/components/ContentMenu.vue +1 -1
  10. package/src/components/FunctionEditor.vue +190 -0
  11. package/src/components/Icon.vue +1 -1
  12. package/src/{layouts → components}/Layout.vue +54 -15
  13. package/src/components/ScrollBar.vue +1 -1
  14. package/src/components/ScrollViewer.vue +10 -1
  15. package/src/components/ToolButton.vue +1 -1
  16. package/src/fields/Code.vue +1 -1
  17. package/src/fields/CodeLink.vue +1 -1
  18. package/src/fields/CodeSelect.vue +102 -99
  19. package/src/fields/UISelect.vue +3 -3
  20. package/src/index.ts +2 -1
  21. package/src/layouts/AddPageBox.vue +5 -2
  22. package/src/layouts/CodeEditor.vue +2 -3
  23. package/src/layouts/Framework.vue +12 -10
  24. package/src/layouts/NavMenu.vue +11 -8
  25. package/src/layouts/PropsPanel.vue +12 -9
  26. package/src/layouts/Resizer.vue +1 -1
  27. package/src/layouts/sidebar/ComponentListPanel.vue +2 -3
  28. package/src/layouts/sidebar/LayerMenu.vue +9 -7
  29. package/src/layouts/sidebar/LayerPanel.vue +130 -178
  30. package/src/layouts/sidebar/Sidebar.vue +110 -26
  31. package/src/layouts/sidebar/code-block/CodeBlockEditor.vue +33 -118
  32. package/src/layouts/sidebar/code-block/CodeBlockList.vue +93 -126
  33. package/src/layouts/workspace/Breadcrumb.vue +6 -7
  34. package/src/layouts/workspace/PageBar.vue +3 -3
  35. package/src/layouts/workspace/PageBarScrollContainer.vue +5 -3
  36. package/src/layouts/workspace/Stage.vue +36 -18
  37. package/src/layouts/workspace/ViewerMenu.vue +16 -15
  38. package/src/layouts/workspace/Workspace.vue +9 -6
  39. package/src/services/BaseService.ts +11 -2
  40. package/src/services/codeBlock.ts +136 -146
  41. package/src/services/componentList.ts +7 -2
  42. package/src/services/editor.ts +161 -85
  43. package/src/services/events.ts +7 -2
  44. package/src/services/history.ts +8 -17
  45. package/src/services/props.ts +8 -3
  46. package/src/services/storage.ts +1 -0
  47. package/src/services/ui.ts +20 -10
  48. package/src/theme/code-block.scss +46 -23
  49. package/src/theme/component-list-panel.scss +8 -5
  50. package/src/type.ts +84 -63
  51. package/src/utils/config.ts +1 -1
  52. package/src/utils/editor.ts +17 -3
  53. package/src/utils/index.ts +2 -1
  54. package/src/utils/logger.ts +1 -1
  55. package/src/utils/operator.ts +12 -12
  56. package/src/utils/props.ts +3 -3
  57. package/src/utils/scroll-viewer.ts +18 -2
  58. package/src/utils/stage.ts +10 -9
  59. package/src/utils/undo-redo.ts +1 -1
  60. package/types/Editor.vue.d.ts +48 -28
  61. package/types/components/CodeDraftEditor.vue.d.ts +55 -0
  62. package/types/components/ContentMenu.vue.d.ts +11 -87
  63. package/types/components/FunctionEditor.vue.d.ts +60 -0
  64. package/types/components/Icon.vue.d.ts +4 -50
  65. package/types/{layouts → components}/Layout.vue.d.ts +12 -1
  66. package/types/components/ScrollBar.vue.d.ts +7 -65
  67. package/types/components/ScrollViewer.vue.d.ts +45 -1
  68. package/types/components/ToolButton.vue.d.ts +12 -78
  69. package/types/fields/Code.vue.d.ts +11 -77
  70. package/types/fields/CodeLink.vue.d.ts +12 -82
  71. package/types/fields/CodeSelect.vue.d.ts +13 -77
  72. package/types/fields/UISelect.vue.d.ts +7 -65
  73. package/types/index.d.ts +1 -0
  74. package/types/layouts/AddPageBox.vue.d.ts +1 -43
  75. package/types/layouts/CodeEditor.vue.d.ts +22 -130
  76. package/types/layouts/NavMenu.vue.d.ts +9 -69
  77. package/types/layouts/sidebar/LayerMenu.vue.d.ts +16 -48
  78. package/types/layouts/sidebar/LayerPanel.vue.d.ts +24 -7
  79. package/types/layouts/sidebar/Sidebar.vue.d.ts +10 -5
  80. package/types/layouts/sidebar/code-block/CodeBlockEditor.vue.d.ts +24 -6
  81. package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +11 -5
  82. package/types/layouts/workspace/Breadcrumb.vue.d.ts +1 -43
  83. package/types/layouts/workspace/Stage.vue.d.ts +15 -43
  84. package/types/layouts/workspace/ViewerMenu.vue.d.ts +11 -66
  85. package/types/layouts/workspace/Workspace.vue.d.ts +25 -8
  86. package/types/services/BaseService.d.ts +2 -1
  87. package/types/services/codeBlock.d.ts +56 -47
  88. package/types/services/componentList.d.ts +1 -0
  89. package/types/services/editor.d.ts +10 -11
  90. package/types/services/events.d.ts +1 -0
  91. package/types/services/history.d.ts +5 -15
  92. package/types/services/props.d.ts +3 -2
  93. package/types/services/ui.d.ts +24 -24
  94. package/types/type.d.ts +70 -59
  95. package/types/utils/editor.d.ts +1 -0
  96. package/types/utils/index.d.ts +1 -0
  97. package/types/utils/operator.d.ts +1 -1
  98. package/types/utils/props.d.ts +1 -1
  99. package/types/utils/scroll-viewer.d.ts +5 -0
  100. package/dist/tmagic-editor.mjs.map +0 -1
  101. package/dist/tmagic-editor.umd.js.map +0 -1
  102. package/src/layouts/sidebar/TabPane.vue +0 -118
  103. package/types/layouts/sidebar/TabPane.vue.d.ts +0 -82
@@ -17,75 +17,68 @@
17
17
  </slot>
18
18
 
19
19
  <!-- 代码块列表 -->
20
- <TMagicTree
21
- v-if="!isEmpty(state.codeList)"
22
- ref="tree"
23
- node-key="id"
24
- empty-text="暂无代码块"
25
- :data="state.codeList"
26
- :highlight-current="true"
27
- :filter-node-method="filterNode"
28
- @node-click="toggleCombineRelation"
29
- >
30
- <template #default="{ data }">
31
- <div :id="data.id" class="list-container">
32
- <div class="list-item">
33
- <div class="code-name">{{ data.name }}({{ data.id }})</div>
34
- <!-- 右侧工具栏 -->
35
- <div class="right-tool">
36
- <TMagicTooltip effect="dark" :content="editable ? '编辑' : '查看'" placement="bottom">
37
- <Icon :icon="editable ? Edit : View" class="edit-icon" @click.stop="editCode(`${data.id}`)"></Icon>
38
- </TMagicTooltip>
39
- <TMagicTooltip
40
- effect="dark"
41
- content="查看绑定关系"
42
- placement="bottom"
43
- v-if="state.bindComps[data.id] && state.bindComps[data.id].length > 0"
44
- >
45
- <Icon :icon="Link" class="edit-icon" @click.stop="toggleCombineRelation(data)"></Icon>
46
- </TMagicTooltip>
47
- <TMagicTooltip effect="dark" content="删除" placement="bottom" v-if="editable">
48
- <Icon :icon="Close" class="edit-icon" @click.stop="deleteCode(`${data.id}`)"></Icon>
49
- </TMagicTooltip>
50
- <slot name="code-block-panel-tool" :id="data.id" :data="data.codeBlockContent"></slot>
20
+ <TMagicScrollbar>
21
+ <TMagicTree
22
+ v-if="!isEmpty(state.codeList)"
23
+ ref="tree"
24
+ node-key="id"
25
+ empty-text="暂无代码块"
26
+ :data="state.codeList"
27
+ :highlight-current="true"
28
+ :filter-node-method="filterNode"
29
+ @node-click="toggleCombineRelation"
30
+ >
31
+ <template #default="{ data }">
32
+ <div :id="data.id" class="list-container">
33
+ <div class="list-item">
34
+ <span class="code-name">{{ data.name }}({{ data.id }})</span>
35
+ <!-- 右侧工具栏 -->
36
+ <div class="right-tool">
37
+ <TMagicTooltip effect="dark" :content="editable ? '编辑' : '查看'" placement="bottom">
38
+ <Icon :icon="editable ? Edit : View" class="edit-icon" @click.stop="editCode(`${data.id}`)"></Icon>
39
+ </TMagicTooltip>
40
+ <TMagicTooltip
41
+ effect="dark"
42
+ content="查看绑定关系"
43
+ placement="bottom"
44
+ v-if="data.combineInfo && data.combineInfo.length > 0"
45
+ >
46
+ <Icon :icon="Link" class="edit-icon" @click.stop="toggleCombineRelation(data)"></Icon>
47
+ </TMagicTooltip>
48
+ <TMagicTooltip effect="dark" content="删除" placement="bottom" v-if="editable">
49
+ <Icon :icon="Close" class="edit-icon" @click.stop="deleteCode(`${data.id}`)"></Icon>
50
+ </TMagicTooltip>
51
+ <slot name="code-block-panel-tool" :id="data.id" :data="data.codeBlockContent"></slot>
52
+ </div>
51
53
  </div>
52
- </div>
53
- <!-- 展示代码块下绑定的组件 -->
54
- <div
55
- class="code-comp-map-wrapper"
56
- v-if="data.showRelation && state.bindComps[data.id] && state.bindComps[data.id].length > 0"
57
- >
58
- <svg class="arrow-left" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-029747aa="">
59
- <path
60
- fill="currentColor"
61
- d="M609.408 149.376 277.76 489.6a32 32 0 0 0 0 44.672l331.648 340.352a29.12 29.12 0 0 0 41.728 0 30.592 30.592 0 0 0 0-42.752L339.264 511.936l311.872-319.872a30.592 30.592 0 0 0 0-42.688 29.12 29.12 0 0 0-41.728 0z"
62
- ></path>
63
- </svg>
64
- <!-- todo 功能暂时隐藏 -->
65
- <!-- <TMagicButton
66
- v-for="(comp, index) in state.bindComps[data.id]"
67
- :key="index"
68
- class="code-comp"
69
- size="small"
70
- :plain="true"
71
- >{{ comp.name }}<Icon :icon="Close" class="comp-delete-icon" @click.stop="unbind(comp.id, data.id)"></Icon
72
- ></TMagicButton> -->
73
- <TMagicButton
74
- v-for="(comp, index) in state.bindComps[data.id]"
75
- :key="index"
76
- class="code-comp"
77
- size="small"
78
- :plain="true"
79
- @click.stop="selectComp(comp.id)"
80
- >{{ comp.name }}</TMagicButton
54
+ <!-- 展示代码块下绑定的组件 -->
55
+ <div
56
+ class="code-comp-map-wrapper"
57
+ v-if="data.showRelation && data.combineInfo && data.combineInfo.length > 0"
81
58
  >
59
+ <svg class="arrow-left" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-029747aa="">
60
+ <path
61
+ fill="currentColor"
62
+ d="M609.408 149.376 277.76 489.6a32 32 0 0 0 0 44.672l331.648 340.352a29.12 29.12 0 0 0 41.728 0 30.592 30.592 0 0 0 0-42.752L339.264 511.936l311.872-319.872a30.592 30.592 0 0 0 0-42.688 29.12 29.12 0 0 0-41.728 0z"
63
+ ></path>
64
+ </svg>
65
+ <TMagicButton
66
+ v-for="(comp, index) in data.combineInfo"
67
+ :key="index"
68
+ class="code-comp"
69
+ size="small"
70
+ :plain="true"
71
+ @click.stop="selectComp(comp.compId)"
72
+ >{{ comp.compName }}</TMagicButton
73
+ >
74
+ </div>
82
75
  </div>
83
- </div>
84
- </template>
85
- </TMagicTree>
76
+ </template>
77
+ </TMagicTree>
78
+ </TMagicScrollbar>
86
79
 
87
80
  <!-- 代码块编辑区 -->
88
- <code-block-editor>
81
+ <code-block-editor v-if="isShowCodeBlockEditor" :paramsColConfig="paramsColConfig">
89
82
  <template #code-block-edit-panel-header="{ id }">
90
83
  <slot name="code-block-edit-panel-header" :id="id"></slot>
91
84
  </template>
@@ -93,69 +86,69 @@
93
86
  </div>
94
87
  </template>
95
88
 
96
- <script lang="ts" setup>
89
+ <script lang="ts" setup name="MEditorCodeBlockList">
97
90
  import { computed, inject, reactive, ref, watch } from 'vue';
98
91
  import { Close, Edit, Link, View } from '@element-plus/icons-vue';
99
- import { forIn, isEmpty } from 'lodash-es';
92
+ import { cloneDeep, forIn, isEmpty } from 'lodash-es';
100
93
 
101
- import { TMagicButton, TMagicInput, tMagicMessage, TMagicTooltip, TMagicTree } from '@tmagic/design';
102
- import { Id } from '@tmagic/schema';
103
- import StageCore from '@tmagic/stage';
94
+ import { TMagicButton, TMagicInput, tMagicMessage, TMagicScrollbar, TMagicTooltip, TMagicTree } from '@tmagic/design';
95
+ import { ColumnConfig } from '@tmagic/form';
96
+ import { CodeBlockContent, Id } from '@tmagic/schema';
104
97
 
105
98
  import Icon from '../../../components/Icon.vue';
106
- import type { CodeBlockContent, CodeRelation, Services } from '../../../type';
107
- import { CodeDeleteErrorType, CodeDslList, CodeEditorMode, ListRelationState } from '../../../type';
99
+ import type { CodeRelation, Services } from '../../../type';
100
+ import { CodeDeleteErrorType, CodeDslItem, ListState } from '../../../type';
108
101
 
109
102
  import codeBlockEditor from './CodeBlockEditor.vue';
110
103
 
111
104
  const props = defineProps<{
112
- customError?: (id: string, errorType: CodeDeleteErrorType) => any;
105
+ customError?: (id: Id, errorType: CodeDeleteErrorType) => any;
106
+ paramsColConfig?: ColumnConfig;
113
107
  }>();
114
108
 
115
109
  const services = inject<Services>('services');
116
110
 
117
111
  // 代码块列表
118
- const state = reactive<ListRelationState>({
112
+ const state = reactive<ListState>({
119
113
  codeList: [],
120
- bindComps: {},
121
114
  });
122
115
 
123
116
  const editable = computed(() => services?.codeBlockService.getEditStatus());
124
117
 
118
+ // 是否展示代码编辑区
119
+ const isShowCodeBlockEditor = computed(() => services?.codeBlockService.getCodeEditorShowStatus() || false);
120
+ // 获取绑定关系
121
+ const codeCombineInfo = ref<CodeRelation | null>(null);
122
+
125
123
  // 根据代码块ID获取其绑定的组件信息
126
- const getBindCompsByCodeId = (codeId: string, codeBlockContent: CodeBlockContent) => {
127
- if (isEmpty(codeBlockContent) || isEmpty(codeBlockContent.comps)) {
128
- state.bindComps[codeId] = [];
129
- return;
130
- }
131
- const compsField = codeBlockContent.comps as CodeRelation;
132
- const bindCompIds = Object.keys(compsField);
133
- const bindCompsFiltered = bindCompIds.filter((compId) => !isEmpty(compsField[compId]));
134
- const compsInfo = bindCompsFiltered.map((compId) => ({
135
- id: compId,
136
- name: getCompName(compId),
124
+ const getBindCompsByCodeId = (codeId: Id) => {
125
+ if (!codeCombineInfo.value || !codeCombineInfo.value[codeId]) return [];
126
+ const bindCompsId = Object.keys(codeCombineInfo.value[codeId]);
127
+ return bindCompsId.map((compId) => ({
128
+ compId,
129
+ compName: getCompName(compId),
137
130
  }));
138
- state.bindComps[codeId] = compsInfo;
139
131
  };
140
132
 
141
133
  // 初始化代码块列表
142
134
  const initList = async () => {
143
- const codeDsl = (await services?.codeBlockService.getCodeDsl()) || null;
144
- if (!codeDsl) return;
135
+ const codeDsl = cloneDeep(await services?.codeBlockService.getCodeDsl()) || null;
136
+ codeCombineInfo.value = cloneDeep(services?.codeBlockService.getCombineInfo()) || null;
137
+ if (!codeDsl || !codeCombineInfo.value) return;
145
138
  state.codeList = [];
146
- forIn(codeDsl, (value: CodeBlockContent, codeId: string) => {
147
- getBindCompsByCodeId(codeId, value);
139
+ forIn(codeDsl, (value: CodeBlockContent, codeId: Id) => {
148
140
  state.codeList.push({
149
141
  id: codeId,
150
142
  name: value.name,
151
143
  codeBlockContent: value,
152
144
  showRelation: true,
145
+ combineInfo: getBindCompsByCodeId(codeId),
153
146
  });
154
147
  });
155
148
  };
156
149
 
157
150
  watch(
158
- () => services?.codeBlockService.getCodeDsl(),
151
+ [() => services?.codeBlockService.getCodeDslSync(), () => services?.codeBlockService.refreshCombineInfo()],
159
152
  () => {
160
153
  initList();
161
154
  },
@@ -165,21 +158,6 @@ watch(
165
158
  },
166
159
  );
167
160
 
168
- // 监听组件名称修改,更新到代码块列表
169
- watch(
170
- () => services?.editorService.get('node'),
171
- (curNode) => {
172
- if (!curNode?.id) return;
173
- forIn(state.bindComps, (bindCompInfo) => {
174
- bindCompInfo.forEach((comp) => {
175
- if (comp.id === curNode.id) {
176
- comp.name = curNode.name;
177
- }
178
- });
179
- });
180
- },
181
- );
182
-
183
161
  // 新增代码块
184
162
  const createCodeBlock = async () => {
185
163
  const { codeBlockService } = services || {};
@@ -189,23 +167,23 @@ const createCodeBlock = async () => {
189
167
  }
190
168
  const codeConfig: CodeBlockContent = {
191
169
  name: '代码块',
192
- content: `() => {\n // place your code here\n}`,
170
+ content: `({app, params}) => {\n // place your code here\n}`,
171
+ params: [],
193
172
  };
194
- await codeBlockService.setMode(CodeEditorMode.EDITOR);
195
173
  const id = await codeBlockService.getUniqueId();
196
174
  await codeBlockService.setCodeDslById(id, codeConfig);
197
175
  codeBlockService.setCodeEditorContent(true, id);
198
176
  };
199
177
 
200
178
  // 编辑代码块
201
- const editCode = async (key: string) => {
202
- await services?.codeBlockService.setMode(CodeEditorMode.EDITOR);
179
+ const editCode = async (key: Id) => {
203
180
  services?.codeBlockService.setCodeEditorContent(true, key);
204
181
  };
205
182
 
206
183
  // 删除代码块
207
- const deleteCode = (key: string) => {
208
- const existBinds = !!(state.bindComps[key]?.length > 0);
184
+ const deleteCode = (key: Id) => {
185
+ const currentCode = state.codeList.find((codeItem: CodeDslItem) => codeItem.id === key);
186
+ const existBinds = !isEmpty(currentCode?.combineInfo);
209
187
  const undeleteableList = services?.codeBlockService.getUndeletableList() || [];
210
188
  if (!existBinds && !undeleteableList.includes(key)) {
211
189
  // 无绑定关系,且不在不可删除列表中
@@ -222,7 +200,7 @@ const deleteCode = (key: string) => {
222
200
  const filterText = ref('');
223
201
  const tree = ref();
224
202
 
225
- const filterNode = (value: string, data: CodeDslList): boolean => {
203
+ const filterNode = (value: string, data: CodeDslItem): boolean => {
226
204
  if (!value) {
227
205
  return true;
228
206
  }
@@ -234,7 +212,7 @@ const filterTextChangeHandler = (val: string) => {
234
212
  };
235
213
 
236
214
  // 展示/隐藏组件绑定关系
237
- const toggleCombineRelation = (data: CodeDslList) => {
215
+ const toggleCombineRelation = (data: CodeDslItem) => {
238
216
  const { id } = data;
239
217
  const currentCode = state.codeList.find((item) => item.id === id);
240
218
  if (!currentCode) return;
@@ -247,20 +225,9 @@ const getCompName = (compId: Id): string => {
247
225
  return node?.name || String(compId);
248
226
  };
249
227
 
250
- // todo 功能暂时隐藏
251
- // 解除绑定
252
- // const unbind = async (compId: Id, codeId: string) => {
253
- // const res = await services?.codeBlockService.unbind(compId, codeId, codeHooks);
254
- // if (res) {
255
- // ElMessage.success('绑定关系解除成功');
256
- // } else {
257
- // ElMessage.error('绑定关系解除失败');
258
- // }
259
- // };
260
-
261
228
  // 选中组件
262
229
  const selectComp = (compId: Id) => {
263
- const stage = services?.editorService.get<StageCore | null>('stage');
230
+ const stage = services?.editorService.get('stage');
264
231
  services?.editorService.select(compId);
265
232
  stage?.select(compId);
266
233
  };
@@ -7,12 +7,11 @@
7
7
  </div>
8
8
  </template>
9
9
 
10
- <script setup lang="ts">
10
+ <script setup lang="ts" name="MEditorBreadcrumb">
11
11
  import { computed, inject } from 'vue';
12
12
 
13
13
  import { TMagicButton } from '@tmagic/design';
14
- import type { MApp, MNode } from '@tmagic/schema';
15
- import type StageCore from '@tmagic/stage';
14
+ import type { MNode } from '@tmagic/schema';
16
15
  import { getNodePath } from '@tmagic/utils';
17
16
 
18
17
  import type { Services } from '../../type';
@@ -20,13 +19,13 @@ import type { Services } from '../../type';
20
19
  const services = inject<Services>('services');
21
20
  const editorService = services?.editorService;
22
21
 
23
- const node = computed(() => editorService?.get<MNode>('node'));
24
- const nodes = computed(() => editorService?.get<MNode[]>('nodes') || []);
25
- const root = computed(() => editorService?.get<MApp>('root'));
22
+ const node = computed(() => editorService?.get('node'));
23
+ const nodes = computed(() => editorService?.get('nodes') || []);
24
+ const root = computed(() => editorService?.get('root'));
26
25
  const path = computed(() => getNodePath(node.value?.id || '', root.value?.items || []));
27
26
 
28
27
  const select = async (node: MNode) => {
29
28
  await editorService?.select(node);
30
- editorService?.get<StageCore>('stage')?.select(node.id);
29
+ editorService?.get('stage')?.select(node.id);
31
30
  };
32
31
  </script>
@@ -46,12 +46,12 @@
46
46
  </PageBarScrollContainer>
47
47
  </template>
48
48
 
49
- <script lang="ts" setup>
49
+ <script lang="ts" setup name="MEditorPageBar">
50
50
  import { computed, inject } from 'vue';
51
51
  import { CaretBottom, Delete, DocumentCopy } from '@element-plus/icons-vue';
52
52
 
53
53
  import { TMagicIcon, TMagicPopover, TMagicTooltip } from '@tmagic/design';
54
- import type { MApp, MPage } from '@tmagic/schema';
54
+ import type { MPage } from '@tmagic/schema';
55
55
 
56
56
  import ToolButton from '../../components/ToolButton.vue';
57
57
  import type { Services } from '../../type';
@@ -61,7 +61,7 @@ import PageBarScrollContainer from './PageBarScrollContainer.vue';
61
61
  const services = inject<Services>('services');
62
62
  const editorService = services?.editorService;
63
63
 
64
- const root = computed(() => editorService?.get<MApp>('root'));
64
+ const root = computed(() => editorService?.get('root'));
65
65
 
66
66
  const page = computed(() => editorService?.get('page'));
67
67
 
@@ -26,7 +26,7 @@
26
26
  </div>
27
27
  </template>
28
28
 
29
- <script setup lang="ts">
29
+ <script setup lang="ts" name="MEditorPageBarScrollContainer">
30
30
  import { computed, inject, nextTick, onMounted, onUnmounted, ref, toRaw, watch } from 'vue';
31
31
  import { ArrowLeftBold, ArrowRightBold, Plus } from '@element-plus/icons-vue';
32
32
 
@@ -100,7 +100,7 @@ const scroll = (type: 'left' | 'right' | 'start' | 'end') => {
100
100
  itemsContainer.value.style.transform = `translate(${translateLeft}px, 0px)`;
101
101
  };
102
102
 
103
- const pageLength = computed(() => editorService?.get<number>('pageLength'));
103
+ const pageLength = computed(() => editorService?.get('pageLength'));
104
104
 
105
105
  watch(pageLength, (length = 0, preLength = 0) => {
106
106
  setTimeout(() => {
@@ -115,9 +115,11 @@ watch(pageLength, (length = 0, preLength = 0) => {
115
115
 
116
116
  const addPage = () => {
117
117
  if (!editorService) return;
118
+ const root = toRaw(editorService.get('root'));
119
+ if (!root) throw new Error('root 不能为空');
118
120
  const pageConfig = {
119
121
  type: NodeType.PAGE,
120
- name: generatePageNameByApp(toRaw(editorService.get('root'))),
122
+ name: generatePageNameByApp(root),
121
123
  };
122
124
  editorService.add(pageConfig);
123
125
  };
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <scroll-viewer
2
+ <ScrollViewer
3
3
  class="m-editor-stage"
4
4
  ref="stageWrap"
5
5
  :width="stageRect?.width"
@@ -7,6 +7,10 @@
7
7
  :wrap-width="stageContainerRect?.width"
8
8
  :wrap-height="stageContainerRect?.height"
9
9
  :zoom="zoom"
10
+ :correction-scroll-size="{
11
+ width: 60,
12
+ height: 50,
13
+ }"
10
14
  >
11
15
  <div
12
16
  class="m-editor-stage-container"
@@ -16,25 +20,29 @@
16
20
  @drop="dropHandler"
17
21
  @dragover="dragoverHandler"
18
22
  ></div>
19
- <teleport to="body">
20
- <viewer-menu ref="menu" :is-multi-select="isMultiSelect"></viewer-menu>
21
- </teleport>
22
- </scroll-viewer>
23
+ <Teleport to="body">
24
+ <ViewerMenu ref="menu" :is-multi-select="isMultiSelect" :stage-content-menu="stageContentMenu"></ViewerMenu>
25
+ </Teleport>
26
+ </ScrollViewer>
23
27
  </template>
24
28
 
25
- <script lang="ts" setup>
26
- import { computed, inject, markRaw, onMounted, onUnmounted, ref, toRaw, watch, watchEffect } from 'vue';
29
+ <script lang="ts" setup name="MEditorStage">
30
+ import { computed, inject, markRaw, nextTick, onMounted, onUnmounted, ref, toRaw, watch, watchEffect } from 'vue';
27
31
  import { cloneDeep } from 'lodash-es';
28
32
 
29
- import type { MApp, MContainer, MNode, MPage } from '@tmagic/schema';
33
+ import type { MContainer } from '@tmagic/schema';
30
34
  import StageCore, { calcValueByFontsize, getOffset, Runtime } from '@tmagic/stage';
31
35
 
32
36
  import ScrollViewer from '../../components/ScrollViewer.vue';
33
- import { Layout, Services, StageOptions, StageRect } from '../../type';
37
+ import { Layout, MenuButton, MenuComponent, Services, StageOptions } from '../../type';
34
38
  import { useStage } from '../../utils/stage';
35
39
 
36
40
  import ViewerMenu from './ViewerMenu.vue';
37
41
 
42
+ defineProps<{
43
+ stageContentMenu: (MenuButton | MenuComponent)[];
44
+ }>();
45
+
38
46
  let stage: StageCore | null = null;
39
47
  let runtime: Runtime | null = null;
40
48
 
@@ -45,16 +53,17 @@ const stageWrap = ref<InstanceType<typeof ScrollViewer>>();
45
53
  const stageContainer = ref<HTMLDivElement>();
46
54
  const menu = ref<InstanceType<typeof ViewerMenu>>();
47
55
 
48
- const isMultiSelect = computed(() => services?.editorService.get('nodes')?.length > 1);
49
- const stageRect = computed(() => services?.uiService.get<StageRect>('stageRect'));
50
- const stageContainerRect = computed(() => services?.uiService.get<StageRect>('stageContainerRect'));
51
- const root = computed(() => services?.editorService.get<MApp>('root'));
52
- const page = computed(() => services?.editorService.get<MPage>('page'));
53
- const zoom = computed(() => services?.uiService.get<number>('zoom') || 1);
54
- const node = computed(() => services?.editorService.get<MNode>('node'));
56
+ const nodes = computed(() => services?.editorService.get('nodes') || []);
57
+ const isMultiSelect = computed(() => nodes.value.length > 1);
58
+ const stageRect = computed(() => services?.uiService.get('stageRect'));
59
+ const stageContainerRect = computed(() => services?.uiService.get('stageContainerRect'));
60
+ const root = computed(() => services?.editorService.get('root'));
61
+ const page = computed(() => services?.editorService.get('page'));
62
+ const zoom = computed(() => services?.uiService.get('zoom') || 1);
63
+ const node = computed(() => services?.editorService.get('node'));
55
64
 
56
65
  watchEffect(() => {
57
- if (stage) return;
66
+ if (stage || !page.value) return;
58
67
 
59
68
  if (!stageContainer.value) return;
60
69
  if (!(stageOptions?.runtimeUrl || stageOptions?.render) || !root.value) return;
@@ -88,6 +97,15 @@ watch(root, (root) => {
88
97
  }
89
98
  });
90
99
 
100
+ watch(page, (page) => {
101
+ if (runtime && page) {
102
+ runtime.updatePageId?.(page.id);
103
+ nextTick(() => {
104
+ stage?.select(page.id);
105
+ });
106
+ }
107
+ });
108
+
91
109
  const resizeObserver = new ResizeObserver((entries) => {
92
110
  for (const { contentRect } of entries) {
93
111
  services?.uiService.set('stageContainerRect', {
@@ -125,7 +143,7 @@ const dropHandler = async (e: DragEvent) => {
125
143
  const doc = stage?.renderer.contentWindow?.document;
126
144
  const parentEl: HTMLElement | null | undefined = doc?.querySelector(`.${stageOptions?.containerHighlightClassName}`);
127
145
 
128
- let parent: MContainer | undefined = page.value;
146
+ let parent: MContainer | undefined | null = page.value;
129
147
  if (parentEl) {
130
148
  parent = services?.editorService.getNodeById(parentEl.id, false) as MContainer;
131
149
  }
@@ -2,12 +2,11 @@
2
2
  <content-menu :menu-data="menuData" ref="menu"></content-menu>
3
3
  </template>
4
4
 
5
- <script lang="ts" setup>
6
- import { computed, inject, markRaw, reactive, ref, watch } from 'vue';
7
- import { Bottom, Delete, DocumentCopy, Top } from '@element-plus/icons-vue';
5
+ <script lang="ts" setup name="MEditorViewerMenu">
6
+ import { computed, inject, markRaw, ref, watch } from 'vue';
7
+ import { Bottom, CopyDocument, Delete, DocumentCopy, Top } from '@element-plus/icons-vue';
8
8
 
9
- import { MNode, NodeType } from '@tmagic/schema';
10
- import StageCore from '@tmagic/stage';
9
+ import { NodeType } from '@tmagic/schema';
11
10
  import { isPage } from '@tmagic/utils';
12
11
 
13
12
  import ContentMenu from '../../components/ContentMenu.vue';
@@ -15,7 +14,10 @@ import storageService from '../../services/storage';
15
14
  import { LayerOffset, Layout, MenuButton, MenuComponent, Services } from '../../type';
16
15
  import { COPY_STORAGE_KEY } from '../../utils/editor';
17
16
 
18
- const props = withDefaults(defineProps<{ isMultiSelect?: boolean }>(), { isMultiSelect: false });
17
+ const props = withDefaults(
18
+ defineProps<{ isMultiSelect?: boolean; stageContentMenu: (MenuButton | MenuComponent)[] }>(),
19
+ { isMultiSelect: false },
20
+ );
19
21
 
20
22
  const services = inject<Services>('services');
21
23
  const editorService = services?.editorService;
@@ -23,14 +25,12 @@ const menu = ref<InstanceType<typeof ContentMenu>>();
23
25
  const canPaste = ref(false);
24
26
  const canCenter = ref(false);
25
27
 
26
- const node = computed(() => editorService?.get<MNode>('node'));
27
- const nodes = computed(() => editorService?.get<MNode[]>('nodes'));
28
+ const node = computed(() => editorService?.get('node'));
29
+ const nodes = computed(() => editorService?.get('nodes'));
28
30
  const parent = computed(() => editorService?.get('parent'));
29
- const stage = computed(() => editorService?.get<StageCore>('stage'));
30
-
31
- const stageContentMenu = inject<(MenuButton | MenuComponent)[]>('stageContentMenu', []);
31
+ const stage = computed(() => editorService?.get('stage'));
32
32
 
33
- const menuData = reactive<(MenuButton | MenuComponent)[]>([
33
+ const menuData = computed<(MenuButton | MenuComponent)[]>(() => [
34
34
  {
35
35
  type: 'button',
36
36
  text: '水平居中',
@@ -43,7 +43,7 @@ const menuData = reactive<(MenuButton | MenuComponent)[]>([
43
43
  {
44
44
  type: 'button',
45
45
  text: '复制',
46
- icon: markRaw(DocumentCopy),
46
+ icon: markRaw(CopyDocument),
47
47
  handler: () => {
48
48
  nodes.value && editorService?.copy(nodes.value);
49
49
  canPaste.value = true;
@@ -52,6 +52,7 @@ const menuData = reactive<(MenuButton | MenuComponent)[]>([
52
52
  {
53
53
  type: 'button',
54
54
  text: '粘贴',
55
+ icon: markRaw(DocumentCopy),
55
56
  display: () => canPaste.value,
56
57
  handler: () => {
57
58
  const rect = menu.value?.$el.getBoundingClientRect();
@@ -127,10 +128,10 @@ const menuData = reactive<(MenuButton | MenuComponent)[]>([
127
128
  type: 'button',
128
129
  text: '清空参考线',
129
130
  handler: () => {
130
- editorService?.get<StageCore>('stage').clearGuides();
131
+ editorService?.get('stage')?.clearGuides();
131
132
  },
132
133
  },
133
- ...stageContentMenu,
134
+ ...props.stageContentMenu,
134
135
  ]);
135
136
 
136
137
  watch(
@@ -3,7 +3,7 @@
3
3
  <Breadcrumb></Breadcrumb>
4
4
 
5
5
  <slot name="stage">
6
- <MagicStage :key="page?.id"></MagicStage>
6
+ <MagicStage v-if="page" :stage-content-menu="stageContentMenu"></MagicStage>
7
7
  </slot>
8
8
 
9
9
  <slot name="workspace-content"></slot>
@@ -15,23 +15,26 @@
15
15
  </div>
16
16
  </template>
17
17
 
18
- <script lang="ts" setup>
18
+ <script lang="ts" setup name="MEditorWorkspace">
19
19
  import { computed, inject, onMounted, onUnmounted, ref } from 'vue';
20
20
  import KeyController from 'keycon';
21
21
 
22
- import type { MNode, MPage } from '@tmagic/schema';
23
22
  import { isPage } from '@tmagic/utils';
24
23
 
25
- import type { Services } from '../../type';
24
+ import type { MenuButton, MenuComponent, Services } from '../../type';
26
25
 
27
26
  import Breadcrumb from './Breadcrumb.vue';
28
27
  import PageBar from './PageBar.vue';
29
28
  import MagicStage from './Stage.vue';
30
29
 
30
+ defineProps<{
31
+ stageContentMenu: (MenuButton | MenuComponent)[];
32
+ }>();
33
+
31
34
  const services = inject<Services>('services');
32
35
  const workspace = ref<HTMLDivElement>();
33
- const nodes = computed(() => services?.editorService.get<MNode[]>('nodes'));
34
- const page = computed(() => services?.editorService.get<MPage>('page'));
36
+ const nodes = computed(() => services?.editorService.get('nodes'));
37
+ const page = computed(() => services?.editorService.get('page'));
35
38
 
36
39
  const mouseenterHandler = () => {
37
40
  workspace.value?.focus();