@tmagic/editor 1.2.0-beta.9 → 1.2.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.
Files changed (98) hide show
  1. package/dist/style.css +59 -35
  2. package/dist/{tmagic-editor.mjs → tmagic-editor.js} +1473 -1154
  3. package/dist/tmagic-editor.js.map +1 -0
  4. package/dist/{tmagic-editor.umd.js → tmagic-editor.umd.cjs} +1483 -1159
  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 +145 -0
  9. package/src/components/ContentMenu.vue +1 -1
  10. package/src/components/FunctionEditor.vue +145 -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 +89 -102
  19. package/src/fields/UISelect.vue +1 -1
  20. package/src/index.ts +1 -0
  21. package/src/layouts/AddPageBox.vue +1 -1
  22. package/src/layouts/CodeEditor.vue +2 -3
  23. package/src/layouts/Framework.vue +2 -2
  24. package/src/layouts/NavMenu.vue +2 -2
  25. package/src/layouts/PropsPanel.vue +13 -6
  26. package/src/layouts/Resizer.vue +1 -1
  27. package/src/layouts/sidebar/ComponentListPanel.vue +1 -1
  28. package/src/layouts/sidebar/LayerMenu.vue +9 -7
  29. package/src/layouts/sidebar/LayerPanel.vue +127 -174
  30. package/src/layouts/sidebar/Sidebar.vue +110 -26
  31. package/src/layouts/sidebar/code-block/CodeBlockEditor.vue +27 -83
  32. package/src/layouts/sidebar/code-block/CodeBlockList.vue +90 -122
  33. package/src/layouts/workspace/Breadcrumb.vue +1 -1
  34. package/src/layouts/workspace/PageBar.vue +1 -1
  35. package/src/layouts/workspace/PageBarScrollContainer.vue +1 -1
  36. package/src/layouts/workspace/Stage.vue +26 -9
  37. package/src/layouts/workspace/ViewerMenu.vue +11 -9
  38. package/src/layouts/workspace/Workspace.vue +7 -3
  39. package/src/services/BaseService.ts +10 -1
  40. package/src/services/codeBlock.ts +124 -125
  41. package/src/services/componentList.ts +6 -1
  42. package/src/services/editor.ts +54 -18
  43. package/src/services/events.ts +6 -1
  44. package/src/services/history.ts +7 -16
  45. package/src/services/props.ts +7 -2
  46. package/src/services/storage.ts +1 -0
  47. package/src/services/ui.ts +12 -0
  48. package/src/theme/code-block.scss +43 -24
  49. package/src/theme/component-list-panel.scss +8 -5
  50. package/src/type.ts +77 -50
  51. package/src/utils/editor.ts +9 -1
  52. package/src/utils/index.ts +1 -0
  53. package/src/utils/props.ts +2 -2
  54. package/src/utils/scroll-viewer.ts +18 -2
  55. package/src/utils/stage.ts +4 -3
  56. package/types/Editor.vue.d.ts +47 -27
  57. package/types/components/CodeDraftEditor.vue.d.ts +56 -0
  58. package/types/components/ContentMenu.vue.d.ts +11 -87
  59. package/types/components/FunctionEditor.vue.d.ts +43 -0
  60. package/types/components/Icon.vue.d.ts +4 -50
  61. package/types/{layouts → components}/Layout.vue.d.ts +12 -1
  62. package/types/components/ScrollBar.vue.d.ts +7 -65
  63. package/types/components/ScrollViewer.vue.d.ts +45 -1
  64. package/types/components/ToolButton.vue.d.ts +12 -78
  65. package/types/fields/Code.vue.d.ts +11 -77
  66. package/types/fields/CodeLink.vue.d.ts +12 -82
  67. package/types/fields/CodeSelect.vue.d.ts +13 -77
  68. package/types/fields/UISelect.vue.d.ts +7 -65
  69. package/types/index.d.ts +1 -0
  70. package/types/layouts/AddPageBox.vue.d.ts +1 -43
  71. package/types/layouts/CodeEditor.vue.d.ts +22 -130
  72. package/types/layouts/NavMenu.vue.d.ts +9 -69
  73. package/types/layouts/sidebar/LayerMenu.vue.d.ts +16 -48
  74. package/types/layouts/sidebar/LayerPanel.vue.d.ts +24 -7
  75. package/types/layouts/sidebar/Sidebar.vue.d.ts +10 -5
  76. package/types/layouts/sidebar/code-block/CodeBlockEditor.vue.d.ts +1 -1
  77. package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +6 -5
  78. package/types/layouts/workspace/Breadcrumb.vue.d.ts +1 -43
  79. package/types/layouts/workspace/Stage.vue.d.ts +15 -43
  80. package/types/layouts/workspace/ViewerMenu.vue.d.ts +11 -66
  81. package/types/layouts/workspace/Workspace.vue.d.ts +23 -5
  82. package/types/services/BaseService.d.ts +2 -1
  83. package/types/services/codeBlock.d.ts +57 -36
  84. package/types/services/componentList.d.ts +1 -0
  85. package/types/services/editor.d.ts +5 -6
  86. package/types/services/events.d.ts +1 -0
  87. package/types/services/history.d.ts +5 -15
  88. package/types/services/props.d.ts +3 -2
  89. package/types/services/ui.d.ts +2 -1
  90. package/types/type.d.ts +65 -48
  91. package/types/utils/editor.d.ts +1 -0
  92. package/types/utils/index.d.ts +1 -0
  93. package/types/utils/props.d.ts +1 -1
  94. package/types/utils/scroll-viewer.d.ts +5 -0
  95. package/dist/tmagic-editor.mjs.map +0 -1
  96. package/dist/tmagic-editor.umd.js.map +0 -1
  97. package/src/layouts/sidebar/TabPane.vue +0 -118
  98. package/types/layouts/sidebar/TabPane.vue.d.ts +0 -82
@@ -1,13 +1,14 @@
1
1
  <template>
2
2
  <TMagicDialog
3
- v-model="isShowCodeBlockEditor"
3
+ :model-value="true"
4
4
  class="code-editor-dialog"
5
5
  :title="currentTitle"
6
6
  :fullscreen="true"
7
- :before-close="saveAndClose"
7
+ :close-on-press-escape="false"
8
8
  :append-to-body="true"
9
+ :show-close="false"
9
10
  >
10
- <layout v-model:left="left" :min-left="45" class="code-editor-layout">
11
+ <Layout v-model:left="left" :min-left="45" class="code-editor-layout">
11
12
  <!-- 左侧列表 -->
12
13
  <template #left v-if="mode === CodeEditorMode.LIST">
13
14
  <TMagicTree
@@ -40,62 +41,37 @@
40
41
  ]"
41
42
  >
42
43
  <slot name="code-block-edit-panel-header" :id="id"></slot>
43
- <TMagicCard shadow="never">
44
- <template #header>
45
- <div class="code-name-wrapper">
46
- <div class="code-name-label">代码块名称</div>
47
- <TMagicInput
48
- v-if="codeConfig"
49
- class="code-name-input"
50
- v-model="codeConfig.name"
51
- :disabled="!editable"
52
- />
53
- </div>
54
- </template>
55
- <div class="m-editor-wrapper">
56
- <magic-code-editor
57
- v-if="codeConfig"
58
- ref="codeEditor"
59
- class="m-editor-container"
60
- :init-values="`${codeConfig.content}`"
61
- @save="saveCode"
62
- :options="{
63
- tabSize: 2,
64
- fontSize: 16,
65
- formatOnPaste: true,
66
- readOnly: !editable,
67
- }"
68
- ></magic-code-editor>
69
- <div class="m-editor-content-bottom" v-if="editable">
70
- <TMagicButton type="primary" class="button" @click="saveCode">保存</TMagicButton>
71
- <TMagicButton type="primary" class="button" @click="saveAndClose">关闭</TMagicButton>
72
- </div>
73
- <div class="m-editor-content-bottom" v-else>
74
- <TMagicButton type="primary" class="button" @click="saveAndClose">关闭</TMagicButton>
75
- </div>
76
- </div>
77
- </TMagicCard>
44
+ <FunctionEditor
45
+ v-if="codeConfig"
46
+ :id="id"
47
+ :name="codeConfig.name"
48
+ :content="codeConfig.content"
49
+ :editable="!!editable"
50
+ :autoSaveDraft="mode === CodeEditorMode.EDITOR"
51
+ :codeOptions="codeOptions"
52
+ ></FunctionEditor>
78
53
  </div>
79
54
  </template>
80
- </layout>
55
+ </Layout>
81
56
  </TMagicDialog>
82
57
  </template>
83
58
 
84
- <script lang="ts" setup>
59
+ <script lang="ts" setup name="MEditorCodeBlockEditor">
85
60
  import { computed, inject, reactive, ref, watchEffect } from 'vue';
86
- import { forIn, isEmpty } from 'lodash-es';
87
- import type * as monaco from 'monaco-editor';
61
+ import { cloneDeep, forIn, isEmpty } from 'lodash-es';
88
62
 
89
- import { TMagicButton, TMagicCard, TMagicDialog, TMagicInput, tMagicMessage, TMagicTree } from '@tmagic/design';
63
+ import { TMagicDialog, TMagicTree } from '@tmagic/design';
64
+ import { CodeBlockContent } from '@tmagic/schema';
90
65
 
91
- import type { CodeBlockContent, CodeDslList, ListState, Services } from '../../../type';
66
+ import FunctionEditor from '../../../components/FunctionEditor.vue';
67
+ import Layout from '../../../components/Layout.vue';
68
+ import type { CodeDslItem, ListState, Services } from '../../../type';
92
69
  import { CodeEditorMode } from '../../../type';
93
- import MagicCodeEditor from '../../CodeEditor.vue';
94
- import Layout from '../../Layout.vue';
70
+ import { serializeConfig } from '../../../utils/editor';
95
71
 
96
72
  const services = inject<Services>('services');
73
+ const codeOptions = inject('codeOptions', {});
97
74
 
98
- const codeEditor = ref<InstanceType<typeof MagicCodeEditor>>();
99
75
  const left = ref(200);
100
76
  const currentTitle = ref('');
101
77
  // 编辑器当前需展示的代码块内容
@@ -105,10 +81,6 @@ const state = reactive<ListState>({
105
81
  codeList: [],
106
82
  });
107
83
 
108
- // 是否展示代码编辑区
109
- const isShowCodeBlockEditor = computed(
110
- () => (codeConfig.value && services?.codeBlockService.getCodeEditorShowStatus()) || false,
111
- );
112
84
  const mode = computed(() => services?.codeBlockService.getMode());
113
85
  const id = computed(() => services?.codeBlockService.getId() || '');
114
86
  const editable = computed(() => services?.codeBlockService.getEditStatus());
@@ -116,7 +88,9 @@ const editable = computed(() => services?.codeBlockService.getEditStatus());
116
88
  const selectedIds = computed(() => services?.codeBlockService.getCombineIds() || []);
117
89
 
118
90
  watchEffect(async () => {
119
- codeConfig.value = (await services?.codeBlockService.getCodeContentById(id.value)) || null;
91
+ codeConfig.value = cloneDeep(await services?.codeBlockService.getCodeContentById(id.value)) || null;
92
+ if (!codeConfig.value) return;
93
+ codeConfig.value.content = serializeConfig(codeConfig.value.content);
120
94
  });
121
95
 
122
96
  watchEffect(async () => {
@@ -132,37 +106,7 @@ watchEffect(async () => {
132
106
  currentTitle.value = state.codeList[0]?.name || '';
133
107
  });
134
108
 
135
- // 保存代码
136
- const saveCode = async (): Promise<boolean> => {
137
- if (!codeEditor.value || !codeConfig.value || !editable.value) return true;
138
-
139
- try {
140
- // 代码内容
141
- const codeContent = (codeEditor.value.getEditor() as monaco.editor.IStandaloneCodeEditor)?.getValue();
142
- /* eslint no-eval: "off" */
143
- codeConfig.value.content = eval(codeContent);
144
- } catch (e: any) {
145
- tMagicMessage.error(e.stack);
146
- return false;
147
- }
148
- // 存入dsl
149
- await services?.codeBlockService.setCodeDslById(id.value, {
150
- name: codeConfig.value.name,
151
- content: codeConfig.value.content,
152
- });
153
- tMagicMessage.success('代码保存成功');
154
- return true;
155
- };
156
-
157
- // 保存并关闭
158
- const saveAndClose = async () => {
159
- const saveRes = await saveCode();
160
- if (saveRes) {
161
- await services?.codeBlockService.setCodeEditorShowStatus(false);
162
- }
163
- };
164
-
165
- const selectHandler = (data: CodeDslList) => {
109
+ const selectHandler = (data: CodeDslItem) => {
166
110
  services?.codeBlockService.setId(data.id);
167
111
  currentTitle.value = data.name;
168
112
  };
@@ -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">
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,68 @@
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';
94
+ import { TMagicButton, TMagicInput, tMagicMessage, TMagicScrollbar, TMagicTooltip, TMagicTree } from '@tmagic/design';
95
+ import { CodeBlockContent, Id } from '@tmagic/schema';
103
96
  import StageCore from '@tmagic/stage';
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, CodeEditorMode, 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;
113
106
  }>();
114
107
 
115
108
  const services = inject<Services>('services');
116
109
 
117
110
  // 代码块列表
118
- const state = reactive<ListRelationState>({
111
+ const state = reactive<ListState>({
119
112
  codeList: [],
120
- bindComps: {},
121
113
  });
122
114
 
123
115
  const editable = computed(() => services?.codeBlockService.getEditStatus());
124
116
 
117
+ // 是否展示代码编辑区
118
+ const isShowCodeBlockEditor = computed(() => services?.codeBlockService.getCodeEditorShowStatus() || false);
119
+ // 获取绑定关系
120
+ const codeCombineInfo = ref<CodeRelation | null>(null);
121
+
125
122
  // 根据代码块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),
123
+ const getBindCompsByCodeId = (codeId: Id) => {
124
+ if (!codeCombineInfo.value || !codeCombineInfo.value[codeId]) return [];
125
+ const bindCompsId = Object.keys(codeCombineInfo.value[codeId]);
126
+ return bindCompsId.map((compId) => ({
127
+ compId,
128
+ compName: getCompName(compId),
137
129
  }));
138
- state.bindComps[codeId] = compsInfo;
139
130
  };
140
131
 
141
132
  // 初始化代码块列表
142
133
  const initList = async () => {
143
- const codeDsl = (await services?.codeBlockService.getCodeDsl()) || null;
144
- if (!codeDsl) return;
134
+ const codeDsl = cloneDeep(await services?.codeBlockService.getCodeDsl()) || null;
135
+ codeCombineInfo.value = cloneDeep(services?.codeBlockService.getCombineInfo()) || null;
136
+ if (!codeDsl || !codeCombineInfo.value) return;
145
137
  state.codeList = [];
146
- forIn(codeDsl, (value: CodeBlockContent, codeId: string) => {
147
- getBindCompsByCodeId(codeId, value);
138
+ forIn(codeDsl, (value: CodeBlockContent, codeId: Id) => {
148
139
  state.codeList.push({
149
140
  id: codeId,
150
141
  name: value.name,
151
142
  codeBlockContent: value,
152
143
  showRelation: true,
144
+ combineInfo: getBindCompsByCodeId(codeId),
153
145
  });
154
146
  });
155
147
  };
156
148
 
157
149
  watch(
158
- () => services?.codeBlockService.getCodeDsl(),
150
+ [() => services?.codeBlockService.getCodeDslSync(), () => services?.codeBlockService.refreshCombineInfo()],
159
151
  () => {
160
152
  initList();
161
153
  },
@@ -165,21 +157,6 @@ watch(
165
157
  },
166
158
  );
167
159
 
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
160
  // 新增代码块
184
161
  const createCodeBlock = async () => {
185
162
  const { codeBlockService } = services || {};
@@ -189,7 +166,8 @@ const createCodeBlock = async () => {
189
166
  }
190
167
  const codeConfig: CodeBlockContent = {
191
168
  name: '代码块',
192
- content: `() => {\n // place your code here\n}`,
169
+ content: `({app, params}) => {\n // place your code here\n}`,
170
+ params: [],
193
171
  };
194
172
  await codeBlockService.setMode(CodeEditorMode.EDITOR);
195
173
  const id = await codeBlockService.getUniqueId();
@@ -198,14 +176,15 @@ const createCodeBlock = async () => {
198
176
  };
199
177
 
200
178
  // 编辑代码块
201
- const editCode = async (key: string) => {
179
+ const editCode = async (key: Id) => {
202
180
  await services?.codeBlockService.setMode(CodeEditorMode.EDITOR);
203
181
  services?.codeBlockService.setCodeEditorContent(true, key);
204
182
  };
205
183
 
206
184
  // 删除代码块
207
- const deleteCode = (key: string) => {
208
- const existBinds = !!(state.bindComps[key]?.length > 0);
185
+ const deleteCode = (key: Id) => {
186
+ const currentCode = state.codeList.find((codeItem: CodeDslItem) => codeItem.id === key);
187
+ const existBinds = !isEmpty(currentCode?.combineInfo);
209
188
  const undeleteableList = services?.codeBlockService.getUndeletableList() || [];
210
189
  if (!existBinds && !undeleteableList.includes(key)) {
211
190
  // 无绑定关系,且不在不可删除列表中
@@ -222,7 +201,7 @@ const deleteCode = (key: string) => {
222
201
  const filterText = ref('');
223
202
  const tree = ref();
224
203
 
225
- const filterNode = (value: string, data: CodeDslList): boolean => {
204
+ const filterNode = (value: string, data: CodeDslItem): boolean => {
226
205
  if (!value) {
227
206
  return true;
228
207
  }
@@ -234,7 +213,7 @@ const filterTextChangeHandler = (val: string) => {
234
213
  };
235
214
 
236
215
  // 展示/隐藏组件绑定关系
237
- const toggleCombineRelation = (data: CodeDslList) => {
216
+ const toggleCombineRelation = (data: CodeDslItem) => {
238
217
  const { id } = data;
239
218
  const currentCode = state.codeList.find((item) => item.id === id);
240
219
  if (!currentCode) return;
@@ -247,17 +226,6 @@ const getCompName = (compId: Id): string => {
247
226
  return node?.name || String(compId);
248
227
  };
249
228
 
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
229
  // 选中组件
262
230
  const selectComp = (compId: Id) => {
263
231
  const stage = services?.editorService.get<StageCore | null>('stage');
@@ -7,7 +7,7 @@
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';
@@ -46,7 +46,7 @@
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
 
@@ -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
 
@@ -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
33
  import type { MApp, MContainer, MNode, MPage } 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, StageRect } 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
 
@@ -54,7 +62,7 @@ const zoom = computed(() => services?.uiService.get<number>('zoom') || 1);
54
62
  const node = computed(() => services?.editorService.get<MNode>('node'));
55
63
 
56
64
  watchEffect(() => {
57
- if (stage) return;
65
+ if (stage || !page.value) return;
58
66
 
59
67
  if (!stageContainer.value) return;
60
68
  if (!(stageOptions?.runtimeUrl || stageOptions?.render) || !root.value) return;
@@ -88,6 +96,15 @@ watch(root, (root) => {
88
96
  }
89
97
  });
90
98
 
99
+ watch(page, (page) => {
100
+ if (runtime && page) {
101
+ runtime.updatePageId?.(page.id);
102
+ nextTick(() => {
103
+ stage?.select(page.id);
104
+ });
105
+ }
106
+ });
107
+
91
108
  const resizeObserver = new ResizeObserver((entries) => {
92
109
  for (const { contentRect } of entries) {
93
110
  services?.uiService.set('stageContainerRect', {
@@ -2,9 +2,9 @@
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
9
  import { MNode, NodeType } from '@tmagic/schema';
10
10
  import StageCore from '@tmagic/stage';
@@ -15,7 +15,10 @@ import storageService from '../../services/storage';
15
15
  import { LayerOffset, Layout, MenuButton, MenuComponent, Services } from '../../type';
16
16
  import { COPY_STORAGE_KEY } from '../../utils/editor';
17
17
 
18
- const props = withDefaults(defineProps<{ isMultiSelect?: boolean }>(), { isMultiSelect: false });
18
+ const props = withDefaults(
19
+ defineProps<{ isMultiSelect?: boolean; stageContentMenu: (MenuButton | MenuComponent)[] }>(),
20
+ { isMultiSelect: false },
21
+ );
19
22
 
20
23
  const services = inject<Services>('services');
21
24
  const editorService = services?.editorService;
@@ -28,9 +31,7 @@ const nodes = computed(() => editorService?.get<MNode[]>('nodes'));
28
31
  const parent = computed(() => editorService?.get('parent'));
29
32
  const stage = computed(() => editorService?.get<StageCore>('stage'));
30
33
 
31
- const stageContentMenu = inject<(MenuButton | MenuComponent)[]>('stageContentMenu', []);
32
-
33
- const menuData = reactive<(MenuButton | MenuComponent)[]>([
34
+ const menuData = computed<(MenuButton | MenuComponent)[]>(() => [
34
35
  {
35
36
  type: 'button',
36
37
  text: '水平居中',
@@ -43,7 +44,7 @@ const menuData = reactive<(MenuButton | MenuComponent)[]>([
43
44
  {
44
45
  type: 'button',
45
46
  text: '复制',
46
- icon: markRaw(DocumentCopy),
47
+ icon: markRaw(CopyDocument),
47
48
  handler: () => {
48
49
  nodes.value && editorService?.copy(nodes.value);
49
50
  canPaste.value = true;
@@ -52,6 +53,7 @@ const menuData = reactive<(MenuButton | MenuComponent)[]>([
52
53
  {
53
54
  type: 'button',
54
55
  text: '粘贴',
56
+ icon: markRaw(DocumentCopy),
55
57
  display: () => canPaste.value,
56
58
  handler: () => {
57
59
  const rect = menu.value?.$el.getBoundingClientRect();
@@ -130,7 +132,7 @@ const menuData = reactive<(MenuButton | MenuComponent)[]>([
130
132
  editorService?.get<StageCore>('stage').clearGuides();
131
133
  },
132
134
  },
133
- ...stageContentMenu,
135
+ ...props.stageContentMenu,
134
136
  ]);
135
137
 
136
138
  watch(