@tmagic/editor 1.3.0-alpha.2 → 1.3.0-alpha.21

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 (167) hide show
  1. package/dist/style.css +91 -120
  2. package/dist/tmagic-editor.js +4609 -3257
  3. package/dist/tmagic-editor.js.map +1 -1
  4. package/dist/tmagic-editor.umd.cjs +5708 -4337
  5. package/dist/tmagic-editor.umd.cjs.map +1 -1
  6. package/package.json +21 -22
  7. package/src/Editor.vue +29 -7
  8. package/src/components/CodeBlockEditor.vue +170 -0
  9. package/src/components/CodeParams.vue +60 -0
  10. package/src/components/ContentMenu.vue +30 -11
  11. package/src/components/Icon.vue +5 -1
  12. package/src/components/Resizer.vue +45 -0
  13. package/src/components/ScrollBar.vue +5 -1
  14. package/src/components/ScrollViewer.vue +5 -1
  15. package/src/components/SearchInput.vue +4 -0
  16. package/src/components/{Layout.vue → SplitView.vue} +8 -4
  17. package/src/components/ToolButton.vue +5 -1
  18. package/src/editorProps.ts +35 -4
  19. package/src/fields/Code.vue +28 -20
  20. package/src/fields/CodeLink.vue +15 -10
  21. package/src/fields/CodeSelect.vue +19 -10
  22. package/src/fields/CodeSelectCol.vue +67 -83
  23. package/src/fields/DataSourceFieldSelect.vue +47 -0
  24. package/src/fields/DataSourceFields.vue +24 -21
  25. package/src/fields/DataSourceInput.vue +318 -0
  26. package/src/fields/DataSourceMethodSelect.vue +136 -0
  27. package/src/fields/DataSourceMethods.vue +103 -0
  28. package/src/fields/DataSourceSelect.vue +68 -0
  29. package/src/fields/EventSelect.vue +62 -22
  30. package/src/fields/KeyValue.vue +13 -11
  31. package/src/fields/UISelect.vue +59 -15
  32. package/src/hooks/index.ts +21 -0
  33. package/src/hooks/use-code-block-edit.ts +84 -0
  34. package/src/hooks/use-data-source-method.ts +100 -0
  35. package/src/{utils/stage.ts → hooks/use-stage.ts} +4 -7
  36. package/src/icons/AppManageIcon.vue +5 -1
  37. package/src/icons/CenterIcon.vue +13 -0
  38. package/src/icons/CodeIcon.vue +5 -3
  39. package/src/index.ts +24 -3
  40. package/src/initService.ts +75 -16
  41. package/src/layouts/AddPageBox.vue +5 -1
  42. package/src/layouts/CodeEditor.vue +66 -11
  43. package/src/layouts/Framework.vue +32 -28
  44. package/src/layouts/NavMenu.vue +5 -1
  45. package/src/layouts/PropsPanel.vue +14 -4
  46. package/src/layouts/sidebar/ComponentListPanel.vue +11 -10
  47. package/src/layouts/sidebar/LayerMenu.vue +13 -25
  48. package/src/layouts/sidebar/LayerNode.vue +40 -0
  49. package/src/layouts/sidebar/LayerPanel.vue +71 -28
  50. package/src/layouts/sidebar/Sidebar.vue +13 -17
  51. package/src/layouts/sidebar/code-block/CodeBlockList.vue +84 -118
  52. package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +63 -0
  53. package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +39 -34
  54. package/src/layouts/sidebar/data-source/DataSourceList.vue +145 -0
  55. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +17 -65
  56. package/src/layouts/workspace/Breadcrumb.vue +5 -1
  57. package/src/layouts/workspace/PageBar.vue +5 -1
  58. package/src/layouts/workspace/PageBarScrollContainer.vue +5 -1
  59. package/src/layouts/workspace/Stage.vue +30 -8
  60. package/src/layouts/workspace/ViewerMenu.vue +15 -36
  61. package/src/layouts/workspace/Workspace.vue +9 -134
  62. package/src/services/codeBlock.ts +20 -72
  63. package/src/services/dataSource.ts +17 -2
  64. package/src/services/dep.ts +23 -20
  65. package/src/services/editor.ts +3 -3
  66. package/src/services/keybinding.ts +185 -0
  67. package/src/services/props.ts +2 -2
  68. package/src/services/storage.ts +3 -2
  69. package/src/theme/code-block.scss +5 -117
  70. package/src/theme/code-editor.scss +27 -2
  71. package/src/theme/content-menu.scss +1 -1
  72. package/src/theme/data-source-input.scss +18 -0
  73. package/src/theme/data-source-methods.scss +13 -0
  74. package/src/theme/event.scss +15 -12
  75. package/src/theme/framework.scss +0 -3
  76. package/src/theme/layer-panel.scss +6 -0
  77. package/src/theme/layout.scss +4 -0
  78. package/src/theme/resizer.scss +25 -10
  79. package/src/theme/stage.scss +4 -0
  80. package/src/theme/theme.scss +2 -0
  81. package/src/type.ts +105 -7
  82. package/src/utils/config.ts +1 -1
  83. package/src/utils/content-menu.ts +92 -0
  84. package/src/utils/data-source/formConfigs/base.ts +35 -30
  85. package/src/utils/data-source/index.ts +75 -6
  86. package/src/utils/dep.ts +39 -7
  87. package/src/utils/index.ts +1 -1
  88. package/src/utils/keybinding-config.ts +120 -0
  89. package/src/utils/props.ts +298 -155
  90. package/src/utils/undo-redo.ts +1 -1
  91. package/types/Editor.vue.d.ts +37 -4
  92. package/types/components/CodeBlockEditor.vue.d.ts +23 -0
  93. package/types/components/CodeParams.vue.d.ts +26 -0
  94. package/types/components/ContentMenu.vue.d.ts +8 -3
  95. package/types/components/Icon.vue.d.ts +2 -2
  96. package/types/{layouts → components}/Resizer.vue.d.ts +2 -2
  97. package/types/components/ScrollBar.vue.d.ts +1 -1
  98. package/types/components/ScrollViewer.vue.d.ts +8 -5
  99. package/types/components/SearchInput.vue.d.ts +1 -1
  100. package/types/components/{Layout.vue.d.ts → SplitView.vue.d.ts} +9 -6
  101. package/types/components/ToolButton.vue.d.ts +10 -7
  102. package/types/editorProps.d.ts +23 -4
  103. package/types/fields/Code.vue.d.ts +23 -18
  104. package/types/fields/CodeLink.vue.d.ts +13 -22
  105. package/types/fields/CodeSelect.vue.d.ts +12 -20
  106. package/types/fields/CodeSelectCol.vue.d.ts +14 -15
  107. package/types/fields/DataSourceFieldSelect.vue.d.ts +29 -0
  108. package/types/fields/DataSourceFields.vue.d.ts +13 -21
  109. package/types/fields/DataSourceInput.vue.d.ts +34 -0
  110. package/types/fields/DataSourceMethodSelect.vue.d.ts +29 -0
  111. package/types/fields/DataSourceMethods.vue.d.ts +32 -0
  112. package/types/fields/DataSourceSelect.vue.d.ts +40 -0
  113. package/types/fields/EventSelect.vue.d.ts +3 -14
  114. package/types/fields/KeyValue.vue.d.ts +15 -27
  115. package/types/fields/UISelect.vue.d.ts +3 -12
  116. package/types/hooks/index.d.ts +3 -0
  117. package/types/hooks/use-code-block-edit.d.ts +125 -0
  118. package/types/hooks/use-data-source-method.d.ts +123 -0
  119. package/types/icons/AppManageIcon.vue.d.ts +1 -1
  120. package/types/icons/CenterIcon.vue.d.ts +2 -0
  121. package/types/icons/CodeIcon.vue.d.ts +1 -1
  122. package/types/index.d.ts +12 -2
  123. package/types/initService.d.ts +2 -2
  124. package/types/layouts/AddPageBox.vue.d.ts +1 -1
  125. package/types/layouts/CodeEditor.vue.d.ts +18 -10
  126. package/types/layouts/Framework.vue.d.ts +11 -32
  127. package/types/layouts/NavMenu.vue.d.ts +7 -4
  128. package/types/layouts/PropsPanel.vue.d.ts +276 -4
  129. package/types/layouts/sidebar/ComponentListPanel.vue.d.ts +4 -4
  130. package/types/layouts/sidebar/LayerMenu.vue.d.ts +2 -2
  131. package/types/layouts/sidebar/LayerNode.vue.d.ts +16 -0
  132. package/types/layouts/sidebar/LayerPanel.vue.d.ts +5 -5
  133. package/types/layouts/sidebar/Sidebar.vue.d.ts +16 -16
  134. package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +7 -12
  135. package/types/layouts/sidebar/code-block/CodeBlockListPanel.vue.d.ts +28 -0
  136. package/types/layouts/sidebar/data-source/DataSourceConfigPanel.vue.d.ts +4 -2
  137. package/types/layouts/sidebar/data-source/DataSourceList.vue.d.ts +14 -0
  138. package/types/layouts/sidebar/data-source/DataSourceListPanel.vue.d.ts +1 -11
  139. package/types/layouts/workspace/Breadcrumb.vue.d.ts +1 -1
  140. package/types/layouts/workspace/PageBar.vue.d.ts +5 -5
  141. package/types/layouts/workspace/PageBarScrollContainer.vue.d.ts +2 -2
  142. package/types/layouts/workspace/Stage.vue.d.ts +2 -2
  143. package/types/layouts/workspace/ViewerMenu.vue.d.ts +7 -4
  144. package/types/layouts/workspace/Workspace.vue.d.ts +9 -9
  145. package/types/services/codeBlock.d.ts +6 -37
  146. package/types/services/dataSource.d.ts +7 -1
  147. package/types/services/dep.d.ts +12 -21
  148. package/types/services/editor.d.ts +1 -1
  149. package/types/services/keybinding.d.ts +20 -0
  150. package/types/services/props.d.ts +10 -115
  151. package/types/type.d.ts +97 -7
  152. package/types/utils/config.d.ts +1 -1
  153. package/types/utils/content-menu.d.ts +7 -0
  154. package/types/utils/data-source/formConfigs/base.d.ts +2 -2
  155. package/types/utils/data-source/index.d.ts +7 -1
  156. package/types/utils/dep.d.ts +4 -2
  157. package/types/utils/index.d.ts +1 -1
  158. package/types/utils/keybinding-config.d.ts +3 -0
  159. package/types/utils/props.d.ts +13 -102
  160. package/src/components/CodeDraftEditor.vue +0 -144
  161. package/src/components/FunctionEditor.vue +0 -190
  162. package/src/layouts/Resizer.vue +0 -32
  163. package/src/layouts/sidebar/code-block/CodeBlockEditor.vue +0 -96
  164. package/types/components/CodeDraftEditor.vue.d.ts +0 -58
  165. package/types/components/FunctionEditor.vue.d.ts +0 -192
  166. package/types/layouts/sidebar/code-block/CodeBlockEditor.vue.d.ts +0 -25
  167. /package/types/{utils/stage.d.ts → hooks/use-stage.d.ts} +0 -0
@@ -1,35 +1,36 @@
1
1
  <template>
2
- <TMagicDrawer
3
- v-model="visible"
2
+ <MFormDrawer
3
+ ref="fomDrawer"
4
+ label-width="80px"
5
+ :close-on-press-escape="false"
4
6
  :title="title"
5
- :close-on-press-escape="true"
6
- :append-to-body="true"
7
- :show-close="true"
8
- :close-on-click-modal="true"
9
- :size="size"
10
- >
11
- <MForm ref="form" :config="dataSourceConfig" :init-values="initValues" @change="changeHandler"></MForm>
12
-
13
- <template #footer>
14
- <div>
15
- <TMagicButton type="primary" @click="submitHandler">确定</TMagicButton>
16
- <TMagicButton @click="hide">关闭</TMagicButton>
17
- </div>
18
- </template>
19
- </TMagicDrawer>
7
+ :width="size"
8
+ :config="dataSourceConfig"
9
+ :values="initValues"
10
+ :disabled="disabled"
11
+ @change="changeHandler"
12
+ @submit="submitHandler"
13
+ @error="errorHandler"
14
+ ></MFormDrawer>
20
15
  </template>
21
16
 
22
17
  <script setup lang="ts">
23
18
  import { computed, inject, ref, watchEffect } from 'vue';
19
+ import { cloneDeep, mergeWith } from 'lodash-es';
24
20
 
25
- import { TMagicButton, TMagicDrawer, tMagicMessage } from '@tmagic/design';
26
- import { MForm } from '@tmagic/form';
21
+ import { tMagicMessage } from '@tmagic/design';
22
+ import { MFormDrawer } from '@tmagic/form';
27
23
 
28
24
  import type { Services } from '@editor/type';
29
25
 
26
+ defineOptions({
27
+ name: 'MEditorDataSourceConfigPanel',
28
+ });
29
+
30
30
  const props = defineProps<{
31
31
  title?: string;
32
32
  values: any;
33
+ disabled: boolean;
33
34
  }>();
34
35
 
35
36
  const type = ref('base');
@@ -42,7 +43,7 @@ const size = computed(() => globalThis.document.body.clientWidth - (services?.ui
42
43
 
43
44
  const dataSourceConfig = computed(() => services?.dataSourceService.getFormConfig(type.value) || []);
44
45
 
45
- const form = ref<InstanceType<typeof MForm>>();
46
+ const fomDrawer = ref<InstanceType<typeof MFormDrawer>>();
46
47
 
47
48
  const initValues = ref({});
48
49
 
@@ -56,29 +57,33 @@ const changeHandler = (value: Record<string, any>) => {
56
57
  return;
57
58
  }
58
59
  type.value = value.type || 'base';
59
- initValues.value = value;
60
- };
61
60
 
62
- const submitHandler = async () => {
63
- try {
64
- const values = await form.value?.submitForm();
65
- emit('submit', values);
66
- } catch (error: any) {
67
- tMagicMessage.error(error.message);
68
- }
61
+ initValues.value = mergeWith(
62
+ cloneDeep(value),
63
+ services?.dataSourceService.getFormValue(type.value) || {},
64
+ (objValue, srcValue) => {
65
+ if (Array.isArray(srcValue)) {
66
+ return srcValue;
67
+ }
68
+ },
69
+ );
69
70
  };
70
71
 
71
- const visible = ref(false);
72
+ const submitHandler = (values: any) => {
73
+ emit('submit', values);
74
+ };
72
75
 
73
- const hide = () => {
74
- visible.value = false;
76
+ const errorHandler = (error: any) => {
77
+ tMagicMessage.error(error.message);
75
78
  };
76
79
 
77
80
  defineExpose({
78
81
  show() {
79
- visible.value = true;
82
+ fomDrawer.value?.show();
80
83
  },
81
84
 
82
- hide,
85
+ hide() {
86
+ fomDrawer.value?.hide();
87
+ },
83
88
  });
84
89
  </script>
@@ -0,0 +1,145 @@
1
+ <template>
2
+ <TMagicTree
3
+ ref="tree"
4
+ class="magic-editor-layer-tree"
5
+ node-key="id"
6
+ empty-text="暂无代码块"
7
+ default-expand-all
8
+ :expand-on-click-node="false"
9
+ :data="list"
10
+ :highlight-current="true"
11
+ @node-click="clickHandler"
12
+ >
13
+ <template #default="{ data }">
14
+ <div :id="data.id" class="list-container">
15
+ <div class="list-item">
16
+ <Icon v-if="data.type === 'code'" class="codeIcon" :icon="Coin"></Icon>
17
+ <Icon v-if="data.type === 'node'" class="compIcon" :icon="Aim"></Icon>
18
+ <span class="name" :class="{ code: data.type === 'ds', hook: data.type === 'key' }">{{
19
+ data.type === 'key' ? data.name : `${data.name}(${data.id})`
20
+ }}</span>
21
+ <!-- 右侧工具栏 -->
22
+ <div class="right-tool" v-if="data.type === 'ds'">
23
+ <TMagicTooltip effect="dark" :content="editable ? '编辑' : '查看'" placement="bottom">
24
+ <Icon :icon="editable ? Edit : View" class="edit-icon" @click.stop="editHandler(`${data.id}`)"></Icon>
25
+ </TMagicTooltip>
26
+ <TMagicTooltip v-if="editable" effect="dark" content="删除" placement="bottom">
27
+ <Icon :icon="Close" class="edit-icon" @click.stop="removeHandler(`${data.id}`)"></Icon>
28
+ </TMagicTooltip>
29
+ <slot name="data-source-panel-tool" :id="data.id" :data="data.codeBlockContent"></slot>
30
+ </div>
31
+ </div>
32
+ </div>
33
+ </template>
34
+ </TMagicTree>
35
+ </template>
36
+
37
+ <script lang="ts" setup>
38
+ import { computed, inject, ref } from 'vue';
39
+ import { Aim, Close, Coin, Edit, View } from '@element-plus/icons-vue';
40
+
41
+ import { tMagicMessageBox, TMagicTooltip, TMagicTree } from '@tmagic/design';
42
+ import { Dep, Id } from '@tmagic/schema';
43
+
44
+ import Icon from '@editor/components/Icon.vue';
45
+ import { DepTargetType, Services } from '@editor/type';
46
+
47
+ defineOptions({
48
+ name: 'MEditorDataSourceList',
49
+ });
50
+
51
+ const emit = defineEmits<{
52
+ edit: [id: string];
53
+ remove: [id: string];
54
+ }>();
55
+
56
+ const { depService, editorService, dataSourceService } = inject<Services>('services') || {};
57
+
58
+ const editable = computed(() => dataSourceService?.get('editable') ?? true);
59
+
60
+ const dataSources = computed(() => dataSourceService?.get('dataSources') || []);
61
+
62
+ const dsDep = computed(() => depService?.getTargets(DepTargetType.DATA_SOURCE) || {});
63
+ const dsMethodDep = computed(() => depService?.getTargets(DepTargetType.DATA_SOURCE_METHOD) || {});
64
+ const dsCondDep = computed(() => depService?.getTargets(DepTargetType.DATA_SOURCE_COND) || {});
65
+
66
+ const getKeyTreeConfig = (dep: Dep[string], type?: string) =>
67
+ dep.keys.map((key) => ({ name: key, id: key, type: 'key', isMethod: type === 'method', isCond: type === 'cond' }));
68
+
69
+ const getNodeTreeConfig = (id: string, dep: Dep[string], type?: string) => ({
70
+ name: dep.name,
71
+ type: 'node',
72
+ id,
73
+ children: getKeyTreeConfig(dep, type),
74
+ });
75
+
76
+ const list = computed(() =>
77
+ dataSources.value.map((ds) => {
78
+ const dsDeps = dsDep.value[ds.id].deps;
79
+ const dsMethodDeps = dsMethodDep.value[ds.id].deps;
80
+ const dsCondDeps = dsCondDep.value[ds.id].deps;
81
+
82
+ const children: any[] = [];
83
+
84
+ const mergeChildren = (deps: Dep, type?: string) => {
85
+ Object.entries(deps).forEach(([id, dep]) => {
86
+ const nodeItem = children.find((item) => item.id === id);
87
+ if (nodeItem) {
88
+ nodeItem.children = nodeItem.children.concat(getKeyTreeConfig(dep, type));
89
+ } else {
90
+ children.push(getNodeTreeConfig(id, dep, type));
91
+ }
92
+ });
93
+ };
94
+
95
+ mergeChildren(dsDeps);
96
+ mergeChildren(dsMethodDeps, 'method');
97
+ mergeChildren(dsCondDeps, 'cond');
98
+
99
+ return {
100
+ id: ds.id,
101
+ name: ds.title,
102
+ type: 'ds',
103
+ children,
104
+ };
105
+ }),
106
+ );
107
+
108
+ const editHandler = (id: string) => {
109
+ emit('edit', id);
110
+ };
111
+
112
+ const removeHandler = async (id: string) => {
113
+ await tMagicMessageBox.confirm('确定删除?', '提示', {
114
+ confirmButtonText: '确定',
115
+ cancelButtonText: '取消',
116
+ type: 'warning',
117
+ });
118
+
119
+ emit('remove', id);
120
+ };
121
+
122
+ // 选中组件
123
+ const selectComp = (compId: Id) => {
124
+ const stage = editorService?.get('stage');
125
+ editorService?.select(compId);
126
+ stage?.select(compId);
127
+ };
128
+
129
+ const clickHandler = (data: any, node: any) => {
130
+ if (data.type === 'node') {
131
+ selectComp(data.id);
132
+ } else if (data.type === 'key') {
133
+ selectComp(node.parent.data.id);
134
+ }
135
+ };
136
+
137
+ const tree = ref<InstanceType<typeof TMagicTree>>();
138
+
139
+ defineExpose({
140
+ filter(val: string) {
141
+ debugger;
142
+ tree.value?.filter(val);
143
+ },
144
+ });
145
+ </script>
@@ -2,45 +2,15 @@
2
2
  <TMagicScrollbar class="data-source-list-panel m-editor-dep-list-panel">
3
3
  <div class="search-wrapper">
4
4
  <SearchInput @search="filterTextChangeHandler"></SearchInput>
5
- <TMagicButton type="primary" size="small" @click="addHandler">新增</TMagicButton>
5
+ <TMagicButton v-if="editable" type="primary" size="small" @click="addHandler">新增</TMagicButton>
6
6
  </div>
7
7
 
8
8
  <!-- 数据源列表 -->
9
- <TMagicTree
10
- ref="tree"
11
- class="magic-editor-layer-tree"
12
- node-key="id"
13
- empty-text="暂无代码块"
14
- default-expand-all
15
- :expand-on-click-node="false"
16
- :data="list"
17
- :highlight-current="true"
18
- >
19
- <template #default="{ data }">
20
- <div :id="data.id" class="list-container">
21
- <div class="list-item">
22
- <Icon v-if="data.type === 'code'" class="codeIcon" :icon="Coin"></Icon>
23
- <Icon v-if="data.type === 'node'" class="compIcon" :icon="Aim"></Icon>
24
- <span class="name" :class="{ code: data.type === 'code', hook: data.type === 'key' }"
25
- >{{ data.name }}({{ data.id }})</span
26
- >
27
- <!-- 右侧工具栏 -->
28
- <div class="right-tool" v-if="data.type === 'code'">
29
- <TMagicTooltip effect="dark" content="编辑" placement="bottom">
30
- <Icon class="edit-icon" :icon="Edit" @click.stop="editHandler(`${data.id}`)"></Icon>
31
- </TMagicTooltip>
32
- <TMagicTooltip effect="dark" content="删除" placement="bottom">
33
- <Icon :icon="Close" class="edit-icon" @click.stop="removeHandler(`${data.id}`)"></Icon>
34
- </TMagicTooltip>
35
- <slot name="data-source-panel-tool" :id="data.id" :data="data.codeBlockContent"></slot>
36
- </div>
37
- </div>
38
- </div>
39
- </template>
40
- </TMagicTree>
9
+ <DataSourceList @edit="editHandler" @remove="removeHandler"></DataSourceList>
41
10
 
42
11
  <DataSourceConfigPanel
43
12
  ref="editDialog"
13
+ :disabled="!editable"
44
14
  :values="dataSourceValues"
45
15
  :title="typeof dataSourceValues.id !== 'undefined' ? `编辑${dataSourceValues.title}` : '新增'"
46
16
  @submit="submitDataSourceHandler"
@@ -50,38 +20,28 @@
50
20
 
51
21
  <script setup lang="ts" name="MEditorDataSourceListPanel">
52
22
  import { computed, inject, ref } from 'vue';
53
- import { Aim, Close, Coin, Edit } from '@element-plus/icons-vue';
54
23
 
55
- import { TMagicButton, tMagicMessageBox, TMagicScrollbar, TMagicTooltip, TMagicTree } from '@tmagic/design';
56
- import { DataSourceSchema } from '@tmagic/schema';
24
+ import { TMagicButton, TMagicScrollbar } from '@tmagic/design';
25
+ import type { DataSourceSchema } from '@tmagic/schema';
57
26
 
58
- import Icon from '@editor/components/Icon.vue';
59
27
  import SearchInput from '@editor/components/SearchInput.vue';
60
28
  import type { Services } from '@editor/type';
61
29
 
62
30
  import DataSourceConfigPanel from './DataSourceConfigPanel.vue';
31
+ import DataSourceList from './DataSourceList.vue';
63
32
 
64
- const services = inject<Partial<Services>>('services', {});
65
- const { dataSourceService, depService } = inject<Services>('services') || {};
66
-
67
- const list = computed(() =>
68
- Object.values(depService?.targets['data-source'] || {}).map((target) => ({
69
- id: target.id,
70
- name: target.name,
71
- type: 'code',
72
- children: Object.entries(target.deps).map(([id, dep]) => ({
73
- name: dep.name,
74
- type: 'node',
75
- id,
76
- children: dep.keys.map((key) => ({ name: key, id: key, type: 'key' })),
77
- })),
78
- })),
79
- );
33
+ defineOptions({
34
+ name: 'MEditorDataSourceListPanel',
35
+ });
36
+
37
+ const { dataSourceService } = inject<Services>('services') || {};
80
38
 
81
39
  const editDialog = ref<InstanceType<typeof DataSourceConfigPanel>>();
82
40
 
83
41
  const dataSourceValues = ref<Record<string, any>>({});
84
42
 
43
+ const editable = computed(() => dataSourceService?.get('editable') ?? true);
44
+
85
45
  const addHandler = () => {
86
46
  if (!editDialog.value) return;
87
47
 
@@ -91,7 +51,7 @@ const addHandler = () => {
91
51
  };
92
52
 
93
53
  const editHandler = (id: string) => {
94
- if (!editDialog.value || !services) return;
54
+ if (!editDialog.value) return;
95
55
 
96
56
  dataSourceValues.value = {
97
57
  ...dataSourceService?.getDataSourceById(id),
@@ -100,19 +60,11 @@ const editHandler = (id: string) => {
100
60
  editDialog.value.show();
101
61
  };
102
62
 
103
- const removeHandler = async (id: string) => {
104
- await tMagicMessageBox.confirm('确定删除?', '提示', {
105
- confirmButtonText: '确定',
106
- cancelButtonText: '取消',
107
- type: 'warning',
108
- });
109
-
63
+ const removeHandler = (id: string) => {
110
64
  dataSourceService?.remove(id);
111
65
  };
112
66
 
113
67
  const submitDataSourceHandler = (value: DataSourceSchema) => {
114
- if (!services) return;
115
-
116
68
  if (value.id) {
117
69
  dataSourceService?.update(value);
118
70
  } else {
@@ -122,9 +74,9 @@ const submitDataSourceHandler = (value: DataSourceSchema) => {
122
74
  editDialog.value?.hide();
123
75
  };
124
76
 
125
- const tree = ref<InstanceType<typeof TMagicTree>>();
77
+ const dataSourceList = ref<InstanceType<typeof DataSourceList>>();
126
78
 
127
79
  const filterTextChangeHandler = (val: string) => {
128
- tree.value?.filter(val);
80
+ dataSourceList.value?.filter(val);
129
81
  };
130
82
  </script>
@@ -7,7 +7,7 @@
7
7
  </div>
8
8
  </template>
9
9
 
10
- <script setup lang="ts" name="MEditorBreadcrumb">
10
+ <script setup lang="ts">
11
11
  import { computed, inject } from 'vue';
12
12
 
13
13
  import { TMagicButton } from '@tmagic/design';
@@ -16,6 +16,10 @@ import { getNodePath } from '@tmagic/utils';
16
16
 
17
17
  import type { Services } from '@editor/type';
18
18
 
19
+ defineOptions({
20
+ name: 'MEditorBreadcrumb',
21
+ });
22
+
19
23
  const services = inject<Services>('services');
20
24
  const editorService = services?.editorService;
21
25
 
@@ -46,7 +46,7 @@
46
46
  </PageBarScrollContainer>
47
47
  </template>
48
48
 
49
- <script lang="ts" setup name="MEditorPageBar">
49
+ <script lang="ts" setup>
50
50
  import { computed, inject } from 'vue';
51
51
  import { CaretBottom, Delete, DocumentCopy } from '@element-plus/icons-vue';
52
52
 
@@ -58,6 +58,10 @@ import type { Services } from '@editor/type';
58
58
 
59
59
  import PageBarScrollContainer from './PageBarScrollContainer.vue';
60
60
 
61
+ defineOptions({
62
+ name: 'MEditorPageBar',
63
+ });
64
+
61
65
  const services = inject<Services>('services');
62
66
  const editorService = services?.editorService;
63
67
 
@@ -26,7 +26,7 @@
26
26
  </div>
27
27
  </template>
28
28
 
29
- <script setup lang="ts" name="MEditorPageBarScrollContainer">
29
+ <script setup lang="ts">
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
 
@@ -36,6 +36,10 @@ import Icon from '@editor/components/Icon.vue';
36
36
  import type { Services } from '@editor/type';
37
37
  import { generatePageNameByApp } from '@editor/utils/editor';
38
38
 
39
+ defineOptions({
40
+ name: 'MEditorPageBarScrollContainer',
41
+ });
42
+
39
43
  const services = inject<Services>('services');
40
44
  const editorService = services?.editorService;
41
45
  const uiService = services?.uiService;
@@ -2,6 +2,7 @@
2
2
  <ScrollViewer
3
3
  class="m-editor-stage"
4
4
  ref="stageWrap"
5
+ tabindex="-1"
5
6
  :width="stageRect?.width"
6
7
  :height="stageRect?.height"
7
8
  :wrap-width="stageContainerRect?.width"
@@ -11,6 +12,7 @@
11
12
  width: 60,
12
13
  height: 50,
13
14
  }"
15
+ @click="stageWrap?.container?.focus()"
14
16
  >
15
17
  <div
16
18
  class="m-editor-stage-container"
@@ -26,7 +28,7 @@
26
28
  </ScrollViewer>
27
29
  </template>
28
30
 
29
- <script lang="ts" setup name="MEditorStage">
31
+ <script lang="ts" setup>
30
32
  import { computed, inject, markRaw, nextTick, onMounted, onUnmounted, ref, toRaw, watch, watchEffect } from 'vue';
31
33
  import { cloneDeep } from 'lodash-es';
32
34
 
@@ -34,11 +36,16 @@ import type { MContainer } from '@tmagic/schema';
34
36
  import StageCore, { calcValueByFontsize, getOffset, Runtime } from '@tmagic/stage';
35
37
 
36
38
  import ScrollViewer from '@editor/components/ScrollViewer.vue';
39
+ import { useStage } from '@editor/hooks/use-stage';
37
40
  import { Layout, MenuButton, MenuComponent, Services, StageOptions } from '@editor/type';
38
- import { useStage } from '@editor/utils/stage';
41
+ import { getConfig } from '@editor/utils/config';
39
42
 
40
43
  import ViewerMenu from './ViewerMenu.vue';
41
44
 
45
+ defineOptions({
46
+ name: 'MEditorStage',
47
+ });
48
+
42
49
  defineProps<{
43
50
  stageContentMenu: (MenuButton | MenuComponent)[];
44
51
  }>();
@@ -70,6 +77,10 @@ watchEffect(() => {
70
77
 
71
78
  stage = useStage(stageOptions);
72
79
 
80
+ stage.on('select', () => {
81
+ stageWrap.value?.container?.focus();
82
+ });
83
+
73
84
  services?.editorService.set('stage', markRaw(stage));
74
85
 
75
86
  stage?.mount(stageContainer.value);
@@ -116,13 +127,17 @@ const resizeObserver = new ResizeObserver((entries) => {
116
127
  });
117
128
 
118
129
  onMounted(() => {
119
- stageWrap.value?.container && resizeObserver.observe(stageWrap.value.container);
130
+ if (stageWrap.value?.container) {
131
+ resizeObserver.observe(stageWrap.value.container);
132
+ services?.keybindingService.registeEl('stage', stageWrap.value.container);
133
+ }
120
134
  });
121
135
 
122
136
  onUnmounted(() => {
123
137
  stage?.destroy();
124
138
  resizeObserver.disconnect();
125
139
  services?.editorService.set('stage', null);
140
+ services?.keybindingService.unregisteEl('stage');
126
141
  });
127
142
 
128
143
  const contextmenuHandler = (e: MouseEvent) => {
@@ -132,9 +147,8 @@ const contextmenuHandler = (e: MouseEvent) => {
132
147
 
133
148
  const dragoverHandler = (e: DragEvent) => {
134
149
  e.preventDefault();
135
- if (e.dataTransfer) {
136
- e.dataTransfer.dropEffect = 'move';
137
- }
150
+ if (!e.dataTransfer) return;
151
+ e.dataTransfer.dropEffect = 'move';
138
152
  };
139
153
 
140
154
  const dropHandler = async (e: DragEvent) => {
@@ -149,8 +163,16 @@ const dropHandler = async (e: DragEvent) => {
149
163
  }
150
164
 
151
165
  if (e.dataTransfer && parent && stageContainer.value && stage) {
152
- // eslint-disable-next-line no-eval
153
- const config = eval(`(${e.dataTransfer.getData('data')})`);
166
+ const parseDSL = getConfig('parseDSL');
167
+
168
+ const data = e.dataTransfer.getData('text/json');
169
+
170
+ if (!data) return;
171
+
172
+ const config = parseDSL(`(${data})`);
173
+
174
+ if (!config) return;
175
+
154
176
  const layout = await services?.editorService.getLayout(parent);
155
177
 
156
178
  const containerRect = stageContainer.value.getBoundingClientRect();
@@ -2,18 +2,24 @@
2
2
  <content-menu :menu-data="menuData" ref="menu"></content-menu>
3
3
  </template>
4
4
 
5
- <script lang="ts" setup name="MEditorViewerMenu">
5
+ <script lang="ts" setup>
6
6
  import { computed, inject, markRaw, ref, watch } from 'vue';
7
- import { Bottom, CopyDocument, Delete, DocumentCopy, Top } from '@element-plus/icons-vue';
7
+ import { Bottom, Top } from '@element-plus/icons-vue';
8
8
 
9
9
  import { NodeType } from '@tmagic/schema';
10
10
  import { isPage } from '@tmagic/utils';
11
11
 
12
12
  import ContentMenu from '@editor/components/ContentMenu.vue';
13
+ import CenterIcon from '@editor/icons/CenterIcon.vue';
13
14
  import storageService from '@editor/services/storage';
14
15
  import { LayerOffset, Layout, MenuButton, MenuComponent, Services } from '@editor/type';
16
+ import { useCopyMenu, useDeleteMenu, useMoveToMenu, usePasteMenu } from '@editor/utils/content-menu';
15
17
  import { COPY_STORAGE_KEY } from '@editor/utils/editor';
16
18
 
19
+ defineOptions({
20
+ name: 'MEditorViewerMenu',
21
+ });
22
+
17
23
  const props = withDefaults(
18
24
  defineProps<{ isMultiSelect?: boolean; stageContentMenu: (MenuButton | MenuComponent)[] }>(),
19
25
  { isMultiSelect: false },
@@ -28,42 +34,20 @@ const canCenter = ref(false);
28
34
  const node = computed(() => editorService?.get('node'));
29
35
  const nodes = computed(() => editorService?.get('nodes'));
30
36
  const parent = computed(() => editorService?.get('parent'));
31
- const stage = computed(() => editorService?.get('stage'));
32
37
 
33
38
  const menuData = computed<(MenuButton | MenuComponent)[]>(() => [
34
39
  {
35
40
  type: 'button',
36
41
  text: '水平居中',
42
+ icon: markRaw(CenterIcon),
37
43
  display: () => canCenter.value,
38
44
  handler: () => {
39
45
  if (!nodes.value) return;
40
46
  editorService?.alignCenter(nodes.value);
41
47
  },
42
48
  },
43
- {
44
- type: 'button',
45
- text: '复制',
46
- icon: markRaw(CopyDocument),
47
- handler: () => {
48
- nodes.value && editorService?.copy(nodes.value);
49
- canPaste.value = true;
50
- },
51
- },
52
- {
53
- type: 'button',
54
- text: '粘贴',
55
- icon: markRaw(DocumentCopy),
56
- display: () => canPaste.value,
57
- handler: () => {
58
- const rect = menu.value?.$el.getBoundingClientRect();
59
- const parentRect = stage.value?.container?.getBoundingClientRect();
60
- const initialLeft = (rect?.left || 0) - (parentRect?.left || 0);
61
- const initialTop = (rect?.top || 0) - (parentRect?.top || 0);
62
-
63
- if (!nodes.value || nodes.value.length === 0) return;
64
- editorService?.paste({ left: initialLeft, top: initialTop });
65
- },
66
- },
49
+ useCopyMenu(),
50
+ usePasteMenu(menu),
67
51
  {
68
52
  type: 'divider',
69
53
  direction: 'horizontal',
@@ -93,6 +77,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() => [
93
77
  {
94
78
  type: 'button',
95
79
  text: '置顶',
80
+ icon: markRaw(Top),
96
81
  display: () => !isPage(node.value) && !props.isMultiSelect,
97
82
  handler: () => {
98
83
  editorService?.moveLayer(LayerOffset.TOP);
@@ -101,25 +86,19 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() => [
101
86
  {
102
87
  type: 'button',
103
88
  text: '置底',
89
+ icon: markRaw(Bottom),
104
90
  display: () => !isPage(node.value) && !props.isMultiSelect,
105
91
  handler: () => {
106
92
  editorService?.moveLayer(LayerOffset.BOTTOM);
107
93
  },
108
94
  },
95
+ useMoveToMenu(services),
109
96
  {
110
97
  type: 'divider',
111
98
  direction: 'horizontal',
112
99
  display: () => !isPage(node.value) && !props.isMultiSelect,
113
100
  },
114
- {
115
- type: 'button',
116
- text: '删除',
117
- icon: Delete,
118
- display: () => !isPage(node.value),
119
- handler: () => {
120
- nodes.value && editorService?.remove(nodes.value);
121
- },
122
- },
101
+ useDeleteMenu(),
123
102
  {
124
103
  type: 'divider',
125
104
  direction: 'horizontal',