@tmagic/editor 1.1.6 → 1.2.0-beta.10

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 (82) hide show
  1. package/dist/style.css +191 -8
  2. package/dist/tmagic-editor.mjs +2104 -1027
  3. package/dist/tmagic-editor.mjs.map +1 -1
  4. package/dist/tmagic-editor.umd.js +2136 -1053
  5. package/dist/tmagic-editor.umd.js.map +1 -1
  6. package/package.json +11 -10
  7. package/src/Editor.vue +27 -12
  8. package/src/components/ContentMenu.vue +4 -4
  9. package/src/components/Icon.vue +7 -5
  10. package/src/{layouts → components}/Layout.vue +15 -5
  11. package/src/components/ScrollBar.vue +1 -1
  12. package/src/components/ScrollViewer.vue +10 -1
  13. package/src/components/ToolButton.vue +30 -15
  14. package/src/fields/Code.vue +5 -2
  15. package/src/fields/CodeLink.vue +20 -12
  16. package/src/fields/CodeSelect.vue +131 -0
  17. package/src/fields/UISelect.vue +9 -8
  18. package/src/index.ts +7 -2
  19. package/src/layouts/AddPageBox.vue +13 -20
  20. package/src/layouts/CodeEditor.vue +106 -120
  21. package/src/layouts/Framework.vue +62 -40
  22. package/src/layouts/NavMenu.vue +1 -1
  23. package/src/layouts/PropsPanel.vue +5 -10
  24. package/src/layouts/Resizer.vue +1 -1
  25. package/src/layouts/sidebar/ComponentListPanel.vue +15 -13
  26. package/src/layouts/sidebar/LayerMenu.vue +89 -92
  27. package/src/layouts/sidebar/LayerPanel.vue +273 -178
  28. package/src/layouts/sidebar/Sidebar.vue +129 -37
  29. package/src/layouts/sidebar/code-block/CodeBlockEditor.vue +169 -0
  30. package/src/layouts/sidebar/code-block/CodeBlockList.vue +267 -0
  31. package/src/layouts/workspace/Breadcrumb.vue +32 -0
  32. package/src/layouts/workspace/PageBar.vue +12 -11
  33. package/src/layouts/workspace/PageBarScrollContainer.vue +1 -1
  34. package/src/layouts/workspace/Stage.vue +15 -7
  35. package/src/layouts/workspace/ViewerMenu.vue +8 -7
  36. package/src/layouts/workspace/Workspace.vue +10 -3
  37. package/src/services/codeBlock.ts +415 -0
  38. package/src/services/editor.ts +35 -5
  39. package/src/theme/breadcrumb.scss +6 -0
  40. package/src/theme/code-block.scss +184 -0
  41. package/src/theme/component-list-panel.scss +3 -7
  42. package/src/theme/index.scss +1 -13
  43. package/src/theme/layer-panel.scss +7 -2
  44. package/src/theme/layout.scss +5 -0
  45. package/src/theme/theme.scss +16 -0
  46. package/src/type.ts +88 -1
  47. package/src/utils/props.ts +9 -3
  48. package/src/utils/scroll-viewer.ts +18 -2
  49. package/src/utils/stage.ts +7 -0
  50. package/types/Editor.vue.d.ts +2 -2
  51. package/types/components/ContentMenu.vue.d.ts +1 -3
  52. package/types/components/Icon.vue.d.ts +1 -3
  53. package/types/{layouts → components}/Layout.vue.d.ts +0 -0
  54. package/types/components/ScrollBar.vue.d.ts +1 -3
  55. package/types/components/ScrollViewer.vue.d.ts +45 -1
  56. package/types/components/ToolButton.vue.d.ts +1 -3
  57. package/types/fields/Code.vue.d.ts +9 -3
  58. package/types/fields/CodeLink.vue.d.ts +5 -3
  59. package/types/fields/CodeSelect.vue.d.ts +94 -0
  60. package/types/fields/UISelect.vue.d.ts +1 -3
  61. package/types/index.d.ts +4 -0
  62. package/types/layouts/AddPageBox.vue.d.ts +43 -3
  63. package/types/layouts/CodeEditor.vue.d.ts +158 -45
  64. package/types/layouts/NavMenu.vue.d.ts +1 -3
  65. package/types/layouts/sidebar/LayerMenu.vue.d.ts +31 -63
  66. package/types/layouts/sidebar/LayerPanel.vue.d.ts +38 -1074
  67. package/types/layouts/sidebar/Sidebar.vue.d.ts +119 -17
  68. package/types/layouts/sidebar/code-block/CodeBlockEditor.vue.d.ts +50 -0
  69. package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +73 -0
  70. package/types/layouts/workspace/Breadcrumb.vue.d.ts +44 -0
  71. package/types/layouts/workspace/Stage.vue.d.ts +23 -7
  72. package/types/layouts/workspace/ViewerMenu.vue.d.ts +6 -3
  73. package/types/layouts/workspace/Workspace.vue.d.ts +23 -5
  74. package/types/services/codeBlock.d.ts +155 -0
  75. package/types/services/editor.d.ts +12 -1
  76. package/types/services/props.d.ts +12 -0
  77. package/types/services/ui.d.ts +1 -1
  78. package/types/type.d.ts +78 -1
  79. package/types/utils/props.d.ts +12 -0
  80. package/types/utils/scroll-viewer.d.ts +5 -0
  81. package/src/layouts/sidebar/TabPane.vue +0 -99
  82. package/types/layouts/sidebar/TabPane.vue.d.ts +0 -14
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.1.6",
2
+ "version": "1.2.0-beta.10",
3
3
  "name": "@tmagic/editor",
4
4
  "sideEffects": [
5
5
  "dist/*",
@@ -45,14 +45,14 @@
45
45
  "dependencies": {
46
46
  "@babel/core": "^7.18.0",
47
47
  "@element-plus/icons-vue": "^2.0.9",
48
- "@tmagic/core": "1.1.6",
49
- "@tmagic/form": "1.1.6",
50
- "@tmagic/schema": "1.1.6",
51
- "@tmagic/stage": "1.1.6",
52
- "@tmagic/utils": "1.1.6",
48
+ "@tmagic/core": "1.2.0-beta.10",
49
+ "@tmagic/design": "1.2.0-beta.10",
50
+ "@tmagic/form": "1.2.0-beta.10",
51
+ "@tmagic/schema": "1.2.0-beta.10",
52
+ "@tmagic/stage": "1.2.0-beta.10",
53
+ "@tmagic/utils": "1.2.0-beta.10",
53
54
  "buffer": "^6.0.3",
54
55
  "color": "^3.1.3",
55
- "element-plus": "^2.2.17",
56
56
  "events": "^3.3.0",
57
57
  "gesto": "^1.7.0",
58
58
  "keycon": "^1.1.2",
@@ -62,8 +62,8 @@
62
62
  "vue": "^3.2.37"
63
63
  },
64
64
  "peerDependencies": {
65
- "@tmagic/form": "1.1.6",
66
- "element-plus": "^2.2.17",
65
+ "@tmagic/design": "1.2.0-beta.10",
66
+ "@tmagic/form": "1.2.0-beta.10",
67
67
  "monaco-editor": "^0.34.0",
68
68
  "vue": "^3.2.37"
69
69
  },
@@ -79,6 +79,7 @@
79
79
  "sass": "^1.35.1",
80
80
  "typescript": "^4.7.4",
81
81
  "vite": "^3.1.3",
82
- "vue-tsc": "^0.39.4"
82
+ "vite-plugin-vue-setup-extend": "^0.4.0",
83
+ "vue-tsc": "^1.0.8"
83
84
  }
84
85
  }
package/src/Editor.vue CHANGED
@@ -1,12 +1,12 @@
1
1
  <template>
2
- <framework :code-options="codeOptions">
2
+ <Framework :code-options="codeOptions">
3
3
  <template #nav>
4
- <slot name="nav" :editorService="editorService"><nav-menu :data="menu"></nav-menu></slot>
4
+ <slot name="nav" :editorService="editorService"><NavMenu :data="menu"></NavMenu></slot>
5
5
  </template>
6
6
 
7
7
  <template #sidebar>
8
8
  <slot name="sidebar" :editorService="editorService">
9
- <sidebar :data="sidebar">
9
+ <Sidebar :data="sidebar" :layer-content-menu="layerContentMenu">
10
10
  <template #layer-panel-header>
11
11
  <slot name="layer-panel-header"></slot>
12
12
  </template>
@@ -22,33 +22,45 @@
22
22
  <template #component-list-item="{ component }">
23
23
  <slot name="component-list-item" :component="component"></slot>
24
24
  </template>
25
- </sidebar>
25
+
26
+ <template #code-block-panel-header>
27
+ <slot name="code-block-panel-header"></slot>
28
+ </template>
29
+
30
+ <template #code-block-panel-tool="{ id, data }">
31
+ <slot name="code-block-panel-tool" :id="id" :data="data"></slot>
32
+ </template>
33
+
34
+ <template #code-block-edit-panel-header="{ id }">
35
+ <slot name="code-block-edit-panel-header" :id="id"></slot>
36
+ </template>
37
+ </Sidebar>
26
38
  </slot>
27
39
  </template>
28
40
 
29
41
  <template #workspace>
30
42
  <slot name="workspace" :editorService="editorService">
31
- <workspace>
43
+ <Workspace :stage-content-menu="stageContentMenu">
32
44
  <template #stage><slot name="stage"></slot></template>
33
45
  <template #workspace-content><slot name="workspace-content" :editorService="editorService"></slot></template>
34
46
  <template #page-bar-title="{ page }"><slot name="page-bar-title" :page="page"></slot></template>
35
47
  <template #page-bar-popover="{ page }"><slot name="page-bar-popover" :page="page"></slot></template>
36
- </workspace>
48
+ </Workspace>
37
49
  </slot>
38
50
  </template>
39
51
 
40
52
  <template #props-panel>
41
53
  <slot name="props-panel">
42
- <props-panel @mounted="(instance: any) => $emit('props-panel-mounted', instance)">
54
+ <PropsPanel @mounted="(instance: any) => $emit('props-panel-mounted', instance)">
43
55
  <template #props-panel-header>
44
56
  <slot name="props-panel-header"></slot>
45
57
  </template>
46
- </props-panel>
58
+ </PropsPanel>
47
59
  </slot>
48
60
  </template>
49
61
 
50
62
  <template #empty><slot name="empty" :editorService="editorService"></slot></template>
51
- </framework>
63
+ </Framework>
52
64
  </template>
53
65
 
54
66
  <script lang="ts">
@@ -65,6 +77,7 @@ import NavMenu from './layouts/NavMenu.vue';
65
77
  import PropsPanel from './layouts/PropsPanel.vue';
66
78
  import Sidebar from './layouts/sidebar/Sidebar.vue';
67
79
  import Workspace from './layouts/workspace/Workspace.vue';
80
+ import codeBlockService from './services/codeBlock';
68
81
  import componentListService from './services/componentList';
69
82
  import editorService from './services/editor';
70
83
  import eventsService from './services/events';
@@ -216,7 +229,9 @@ export default defineComponent({
216
229
  // 初始值变化,重新设置节点信息
217
230
  watch(
218
231
  () => props.modelValue,
219
- (modelValue) => editorService.set('root', modelValue),
232
+ (modelValue) => {
233
+ editorService.set('root', modelValue);
234
+ },
220
235
  {
221
236
  immediate: true,
222
237
  },
@@ -288,6 +303,7 @@ export default defineComponent({
288
303
  uiService.destroy();
289
304
  componentListService.destroy();
290
305
  storageService.destroy();
306
+ codeBlockService.destroy();
291
307
  });
292
308
 
293
309
  const services: Services = {
@@ -298,12 +314,11 @@ export default defineComponent({
298
314
  editorService,
299
315
  uiService,
300
316
  storageService,
317
+ codeBlockService,
301
318
  };
302
319
 
303
320
  provide('services', services);
304
321
 
305
- provide('layerContentMenu', props.layerContentMenu);
306
- provide('stageContentMenu', props.stageContentMenu);
307
322
  provide(
308
323
  'stageOptions',
309
324
  reactive({
@@ -1,14 +1,14 @@
1
1
  <template>
2
- <div v-if="menuData.length && visible" class="magic-editor-content-menu" ref="menu" :style="menuStyle">
2
+ <div v-if="menuData.length" v-show="visible" class="magic-editor-content-menu" ref="menu" :style="menuStyle">
3
3
  <div>
4
- <tool-button
4
+ <ToolButton
5
5
  v-for="(item, index) in menuData"
6
6
  event-type="mouseup"
7
7
  :data="item"
8
8
  :key="index"
9
9
  @mouseup="hide"
10
10
  @mouseenter="showSubMenu(item)"
11
- ></tool-button>
11
+ ></ToolButton>
12
12
  </div>
13
13
  <teleport to="body">
14
14
  <content-menu
@@ -22,7 +22,7 @@
22
22
  </div>
23
23
  </template>
24
24
 
25
- <script lang="ts" setup>
25
+ <script lang="ts" setup name="MEditorContentMenu">
26
26
  import { nextTick, onMounted, onUnmounted, ref } from 'vue';
27
27
 
28
28
  import { MenuButton, MenuComponent } from '../type';
@@ -1,16 +1,18 @@
1
1
  <template>
2
- <el-icon v-if="!icon" class="magic-editor-icon"><Edit></Edit></el-icon>
3
- <el-icon v-else-if="typeof icon === 'string' && icon.startsWith('http')" class="magic-editor-icon"
2
+ <TMagicIcon v-if="!icon" class="magic-editor-icon"><Edit></Edit></TMagicIcon>
3
+ <TMagicIcon v-else-if="typeof icon === 'string' && icon.startsWith('http')" class="magic-editor-icon"
4
4
  ><img :src="icon"
5
- /></el-icon>
5
+ /></TMagicIcon>
6
6
  <i v-else-if="typeof icon === 'string'" class="magic-editor-icon" :class="icon"></i>
7
- <el-icon v-else class="magic-editor-icon"><component :is="toRaw(icon)"></component></el-icon>
7
+ <TMagicIcon v-else class="magic-editor-icon"><component :is="toRaw(icon)"></component></TMagicIcon>
8
8
  </template>
9
9
 
10
- <script lang="ts" setup>
10
+ <script lang="ts" setup name="MEditorIcon">
11
11
  import { toRaw } from 'vue';
12
12
  import { Edit } from '@element-plus/icons-vue';
13
13
 
14
+ import { TMagicIcon } from '@tmagic/design';
15
+
14
16
  defineProps<{
15
17
  icon?: any;
16
18
  }>();
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div ref="el">
2
+ <div ref="el" class="m-editor-layout">
3
3
  <template v-if="typeof props.left !== 'undefined'">
4
4
  <div class="m-editor-layout-left" :class="leftClass" :style="`width: ${left}px`">
5
5
  <slot name="left"></slot>
@@ -20,10 +20,10 @@
20
20
  </div>
21
21
  </template>
22
22
 
23
- <script lang="ts" setup>
23
+ <script lang="ts" setup name="MEditorLayout">
24
24
  import { onMounted, onUnmounted, ref } from 'vue';
25
25
 
26
- import Resizer from './Resizer.vue';
26
+ import Resizer from '../layouts/Resizer.vue';
27
27
 
28
28
  const emit = defineEmits(['update:left', 'change', 'update:right']);
29
29
 
@@ -74,8 +74,13 @@ const center = ref(0);
74
74
 
75
75
  const changeLeft = (deltaX: number) => {
76
76
  if (typeof props.left === 'undefined') return;
77
- const left = Math.max(props.left + deltaX, props.minLeft) || 0;
77
+ let left = Math.max(props.left + deltaX, props.minLeft) || 0;
78
78
  emit('update:left', left);
79
+
80
+ if (clientWidth - left - (props.right || 0) <= 0) {
81
+ left = props.left;
82
+ }
83
+
79
84
  center.value = clientWidth - left - (props.right || 0);
80
85
 
81
86
  emit('change', {
@@ -87,8 +92,13 @@ const changeLeft = (deltaX: number) => {
87
92
 
88
93
  const changeRight = (deltaX: number) => {
89
94
  if (typeof props.right === 'undefined') return;
90
- const right = Math.max(props.right - deltaX, props.minRight) || 0;
95
+ let right = Math.max(props.right - deltaX, props.minRight) || 0;
91
96
  emit('update:right', right);
97
+
98
+ if (clientWidth - (props.left || 0) - right <= 0) {
99
+ right = props.right;
100
+ }
101
+
92
102
  center.value = clientWidth - (props.left || 0) - right;
93
103
 
94
104
  emit('change', {
@@ -4,7 +4,7 @@
4
4
  </div>
5
5
  </template>
6
6
 
7
- <script lang="ts" setup>
7
+ <script lang="ts" setup name="MEditorScrollBar">
8
8
  import { computed, onMounted, onUnmounted, ref } from 'vue';
9
9
  import Gesto from 'gesto';
10
10
 
@@ -22,7 +22,7 @@
22
22
  </div>
23
23
  </template>
24
24
 
25
- <script lang="ts" setup>
25
+ <script lang="ts" setup name="MEditorScrollViewer">
26
26
  import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
27
27
 
28
28
  import { ScrollViewerEvent } from '../type';
@@ -37,6 +37,10 @@ const props = withDefaults(
37
37
  wrapWidth?: number;
38
38
  wrapHeight?: number;
39
39
  zoom?: number;
40
+ correctionScrollSize?: {
41
+ width: number;
42
+ height: number;
43
+ };
40
44
  }>(),
41
45
  {
42
46
  width: 0,
@@ -44,6 +48,10 @@ const props = withDefaults(
44
48
  wrapWidth: 0,
45
49
  wrapHeight: 0,
46
50
  zoom: 1,
51
+ correctionScrollSize: () => ({
52
+ width: 0,
53
+ height: 0,
54
+ }),
47
55
  },
48
56
  );
49
57
 
@@ -69,6 +77,7 @@ onMounted(() => {
69
77
  container: container.value,
70
78
  target: el.value,
71
79
  zoom: props.zoom,
80
+ correctionScrollSize: props.correctionScrollSize,
72
81
  });
73
82
 
74
83
  scrollViewer.on('scroll', (data: ScrollViewerEvent) => {
@@ -7,41 +7,56 @@
7
7
  @mousedown="mousedownHandler(data, $event)"
8
8
  @mouseup="mouseupHandler(data, $event)"
9
9
  >
10
- <el-divider v-if="data.type === 'divider'" :direction="data.direction || 'vertical'"></el-divider>
10
+ <TMagicDivider v-if="data.type === 'divider'" :direction="data.direction || 'vertical'"></TMagicDivider>
11
11
  <div v-else-if="data.type === 'text'" class="menu-item-text">{{ data.text }}</div>
12
12
 
13
13
  <template v-else-if="data.type === 'button'">
14
- <el-tooltip v-if="data.tooltip" effect="dark" placement="bottom-start" :content="data.tooltip">
15
- <el-button size="small" text :disabled="disabled"
16
- ><MIcon v-if="data.icon" :icon="data.icon"></MIcon><span>{{ data.text }}</span></el-button
14
+ <TMagicTooltip v-if="data.tooltip" effect="dark" placement="bottom-start" :content="data.tooltip">
15
+ <TMagicButton size="small" text :disabled="disabled"
16
+ ><MIcon v-if="data.icon" :icon="data.icon"></MIcon><span>{{ data.text }}</span></TMagicButton
17
17
  >
18
- </el-tooltip>
19
- <el-button v-else size="small" text :disabled="disabled"
20
- ><MIcon v-if="data.icon" :icon="data.icon"></MIcon><span>{{ data.text }}</span></el-button
18
+ </TMagicTooltip>
19
+ <TMagicButton v-else size="small" text :disabled="disabled"
20
+ ><MIcon v-if="data.icon" :icon="data.icon"></MIcon><span>{{ data.text }}</span></TMagicButton
21
21
  >
22
22
  </template>
23
23
 
24
- <el-dropdown v-else-if="data.type === 'dropdown'" trigger="click" :disabled="disabled" @command="dropdownHandler">
24
+ <TMagicDropdown
25
+ v-else-if="data.type === 'dropdown'"
26
+ trigger="click"
27
+ :disabled="disabled"
28
+ @command="dropdownHandler"
29
+ >
25
30
  <span class="el-dropdown-link menubar-menu-button">
26
- {{ data.text }}<el-icon class="el-icon--right"><ArrowDown></ArrowDown></el-icon>
31
+ {{ data.text }}<TMagicIcon class="el-icon--right"><ArrowDown></ArrowDown></TMagicIcon>
27
32
  </span>
28
33
  <template #dropdown>
29
- <el-dropdown-menu v-if="data.items && data.items.length">
30
- <el-dropdown-item v-for="(subItem, index) in data.items" :key="index" :command="{ data, subItem }">{{
34
+ <TMagicDropdownMenu v-if="data.items && data.items.length">
35
+ <TMagicDropdownItem v-for="(subItem, index) in data.items" :key="index" :command="{ data, subItem }">{{
31
36
  subItem.text
32
- }}</el-dropdown-item>
33
- </el-dropdown-menu>
37
+ }}</TMagicDropdownItem>
38
+ </TMagicDropdownMenu>
34
39
  </template>
35
- </el-dropdown>
40
+ </TMagicDropdown>
36
41
 
37
42
  <component v-else-if="data.type === 'component'" v-bind="data.props || {}" :is="data.component"></component>
38
43
  </div>
39
44
  </template>
40
45
 
41
- <script lang="ts" setup>
46
+ <script lang="ts" setup name="MEditorToolButton">
42
47
  import { computed, inject } from 'vue';
43
48
  import { ArrowDown } from '@element-plus/icons-vue';
44
49
 
50
+ import {
51
+ TMagicButton,
52
+ TMagicDivider,
53
+ TMagicDropdown,
54
+ TMagicDropdownItem,
55
+ TMagicDropdownMenu,
56
+ TMagicIcon,
57
+ TMagicTooltip,
58
+ } from '@tmagic/design';
59
+
45
60
  import MIcon from '../components/Icon.vue';
46
61
  import type { MenuButton, MenuComponent, Services } from '../type';
47
62
 
@@ -3,11 +3,12 @@
3
3
  :style="`height: ${height}`"
4
4
  :init-values="model[name]"
5
5
  :language="language"
6
+ :options="config.options"
6
7
  @save="save"
7
8
  ></magic-code-editor>
8
9
  </template>
9
10
 
10
- <script lang="ts" setup>
11
+ <script lang="ts" setup name="MEditorCode">
11
12
  import { computed } from 'vue';
12
13
 
13
14
  const emit = defineEmits(['change']);
@@ -17,12 +18,14 @@ const props = defineProps<{
17
18
  name: string;
18
19
  config: {
19
20
  language?: string;
21
+ options?: Object;
22
+ height?: string;
20
23
  };
21
24
  prop: string;
22
25
  }>();
23
26
 
24
27
  const language = computed(() => props.config.language || 'javascript');
25
- const height = computed(() => `${document.body.clientHeight - 168}px`);
28
+ const height = computed(() => props.config.height || `${document.body.clientHeight - 168}px`);
26
29
 
27
30
  const save = (v: string) => {
28
31
  props.model[props.name] = v;
@@ -2,7 +2,7 @@
2
2
  <m-fields-link :config="formConfig" :model="modelValue" name="form" @change="changeHandler"></m-fields-link>
3
3
  </template>
4
4
 
5
- <script lang="ts" setup>
5
+ <script lang="ts" setup name="MEditorCodeLink">
6
6
  import { computed, reactive, watch } from 'vue';
7
7
  import serialize from 'serialize-javascript';
8
8
 
@@ -12,6 +12,7 @@ const props = defineProps<{
12
12
  name: string;
13
13
  text?: string;
14
14
  formTitle?: string;
15
+ codeOptions?: Object;
15
16
  };
16
17
  model: any;
17
18
  name: string;
@@ -20,17 +21,24 @@ const props = defineProps<{
20
21
 
21
22
  const emit = defineEmits(['change']);
22
23
 
23
- const formConfig = computed(() => ({
24
- ...props.config,
25
- text: '',
26
- type: 'link',
27
- form: [
28
- {
29
- name: props.name,
30
- type: 'vs-code',
31
- },
32
- ],
33
- }));
24
+ const formConfig = computed(() => {
25
+ const { codeOptions, ...config } = props.config;
26
+ return {
27
+ ...config,
28
+ text: '',
29
+ type: 'link',
30
+ form: [
31
+ {
32
+ name: props.name,
33
+ type: 'vs-code',
34
+ options: {
35
+ tabSize: 2,
36
+ ...(codeOptions || {}),
37
+ },
38
+ },
39
+ ],
40
+ };
41
+ });
34
42
 
35
43
  const modelValue = reactive<{ form: Record<string, string> }>({
36
44
  form: {
@@ -0,0 +1,131 @@
1
+ <template>
2
+ <div class="m-fields-code-select" :key="fieldKey">
3
+ <TMagicCard shadow="never">
4
+ <template #header>
5
+ <m-fields-select
6
+ :config="selectConfig"
7
+ :model="model"
8
+ :prop="prop"
9
+ :name="name"
10
+ :size="size"
11
+ @change="changeHandler"
12
+ ></m-fields-select>
13
+ </template>
14
+ <div class="tool-bar">
15
+ <TMagicTooltip class="tool-item" effect="dark" content="查看代码块" placement="top">
16
+ <svg
17
+ @click="viewHandler"
18
+ preserveAspectRatio="xMidYMid meet"
19
+ viewBox="0 0 24 24"
20
+ width="15px"
21
+ height="15px"
22
+ data-v-65a7fb6c=""
23
+ >
24
+ <path
25
+ fill="currentColor"
26
+ d="m23 12l-7.071 7.071l-1.414-1.414L20.172 12l-5.657-5.657l1.414-1.414L23 12zM3.828 12l5.657 5.657l-1.414 1.414L1 12l7.071-7.071l1.414 1.414L3.828 12z"
27
+ ></path>
28
+ </svg>
29
+ </TMagicTooltip>
30
+ </div>
31
+ </TMagicCard>
32
+ </div>
33
+ </template>
34
+
35
+ <script lang="ts" setup name="MEditorCodeSelect">
36
+ import { computed, defineEmits, defineProps, inject, ref, watchEffect } from 'vue';
37
+ import { map, xor } from 'lodash-es';
38
+
39
+ import { TMagicCard, tMagicMessage, TMagicTooltip } from '@tmagic/design';
40
+ import { FormState, SelectConfig } from '@tmagic/form';
41
+
42
+ import type { Services } from '../type';
43
+ import { CodeEditorMode, CodeSelectOp } from '../type';
44
+ const services = inject<Services>('services');
45
+ const form = inject<FormState>('mForm');
46
+ const emit = defineEmits(['change']);
47
+
48
+ const props = defineProps<{
49
+ config: {
50
+ selectConfig?: SelectConfig;
51
+ };
52
+ model: any;
53
+ prop: string;
54
+ name: string;
55
+ size: string;
56
+ }>();
57
+
58
+ const selectConfig = computed(() => {
59
+ const defaultConfig = {
60
+ multiple: true,
61
+ options: async () => {
62
+ const codeDsl = await services?.codeBlockService.getCodeDsl();
63
+ if (codeDsl) {
64
+ return map(codeDsl, (value, key) => ({
65
+ text: `${value.name}(${key})`,
66
+ label: `${value.name}(${key})`,
67
+ value: key,
68
+ }));
69
+ }
70
+ return [];
71
+ },
72
+ };
73
+ return {
74
+ ...defaultConfig,
75
+ ...props.config.selectConfig,
76
+ };
77
+ });
78
+ const fieldKey = ref('');
79
+ const multiple = ref(true);
80
+ const lastTagSnapshot = ref<string[]>([]);
81
+
82
+ watchEffect(async () => {
83
+ if (!props.model[props.name]) return;
84
+ const combineNames = await Promise.all(
85
+ props.model[props.name].map(async (id: string) => {
86
+ const { name = '' } = (await services?.codeBlockService.getCodeContentById(id)) || {};
87
+ return name;
88
+ }),
89
+ );
90
+ fieldKey.value = combineNames.join('-');
91
+ });
92
+
93
+ const changeHandler = async (value: any) => {
94
+ let codeIds = value;
95
+ if (typeof value === 'string') {
96
+ multiple.value = false;
97
+ codeIds = value ? [value] : [];
98
+ }
99
+ await setCombineRelation(codeIds);
100
+ emit('change', value);
101
+ };
102
+
103
+ // 同步绑定关系
104
+ const setCombineRelation = async (codeIds: string[]) => {
105
+ // 组件id
106
+ const { id = '' } = services?.editorService.get('node') || {};
107
+
108
+ // 兼容单选
109
+ let opFlag = CodeSelectOp.CHANGE;
110
+ let diffValues = codeIds;
111
+ if (multiple.value) {
112
+ // initValues为表单初始值,当表单内容发生变化时,initValues也会更新,可以理解为上一次表单内容的快照
113
+ lastTagSnapshot.value = form?.initValues[props.name] || [];
114
+ opFlag = codeIds.length < lastTagSnapshot.value.length ? CodeSelectOp.DELETE : CodeSelectOp.ADD;
115
+ diffValues = xor(codeIds, lastTagSnapshot.value) as string[];
116
+ }
117
+ // 记录绑定关系
118
+ await services?.codeBlockService.setCombineRelation(id, diffValues, opFlag, props.prop);
119
+ };
120
+
121
+ const viewHandler = async () => {
122
+ if (props.model[props.name].length === 0) {
123
+ tMagicMessage.error('请先绑定代码块');
124
+ return;
125
+ }
126
+ // 记录当前已被绑定的代码块,为查看弹窗的展示内容
127
+ await services?.codeBlockService.setCombineIds(props.model[props.name]);
128
+ await services?.codeBlockService.setMode(CodeEditorMode.LIST);
129
+ services?.codeBlockService.setCodeEditorContent(true, props.model[props.name][0]);
130
+ };
131
+ </script>
@@ -1,21 +1,22 @@
1
1
  <template>
2
2
  <div class="m-fields-ui-select" v-if="uiSelectMode" @click="cancelHandler">
3
- <el-button type="danger" :icon="Delete" text style="padding: 0">取消</el-button>
3
+ <TMagicButton type="danger" :icon="Delete" text style="padding: 0">取消</TMagicButton>
4
4
  </div>
5
5
  <div class="m-fields-ui-select" v-else @click="startSelect" style="display: flex">
6
- <el-tooltip v-if="val" content="清除">
7
- <el-button style="padding: 0" type="danger" :icon="Close" text @click.stop="deleteHandler"></el-button>
8
- </el-tooltip>
9
- <el-tooltip :content="val ? toName + '_' + val : '点击此处选择'">
10
- <el-button text style="padding: 0; margin: 0">{{ val ? toName + '_' + val : '点击此处选择' }}</el-button>
11
- </el-tooltip>
6
+ <TMagicTooltip v-if="val" content="清除">
7
+ <TMagicButton style="padding: 0" type="danger" :icon="Close" text @click.stop="deleteHandler"></TMagicButton>
8
+ </TMagicTooltip>
9
+ <TMagicTooltip :content="val ? toName + '_' + val : '点击此处选择'">
10
+ <TMagicButton text style="padding: 0; margin: 0">{{ val ? toName + '_' + val : '点击此处选择' }}</TMagicButton>
11
+ </TMagicTooltip>
12
12
  </div>
13
13
  </template>
14
14
 
15
- <script lang="ts" setup>
15
+ <script lang="ts" setup name="MEditorUISelect">
16
16
  import { computed, inject, ref } from 'vue';
17
17
  import { Close, Delete } from '@element-plus/icons-vue';
18
18
 
19
+ import { TMagicButton, TMagicTooltip } from '@tmagic/design';
19
20
  import { FormState } from '@tmagic/form';
20
21
 
21
22
  import { Services } from '../type';
package/src/index.ts CHANGED
@@ -19,6 +19,7 @@ import { App } from 'vue';
19
19
 
20
20
  import Code from './fields/Code.vue';
21
21
  import CodeLink from './fields/CodeLink.vue';
22
+ import CodeSelect from './fields/CodeSelect.vue';
22
23
  import uiSelect from './fields/UISelect.vue';
23
24
  import CodeEditor from './layouts/CodeEditor.vue';
24
25
  import { setConfig } from './utils/config';
@@ -38,12 +39,16 @@ export { default as historyService } from './services/history';
38
39
  export { default as storageService } from './services/storage';
39
40
  export { default as eventsService } from './services/events';
40
41
  export { default as uiService } from './services/ui';
42
+ export { default as codeBlockService } from './services/codeBlock';
41
43
  export { default as ComponentListPanel } from './layouts/sidebar/ComponentListPanel.vue';
42
44
  export { default as LayerPanel } from './layouts/sidebar/LayerPanel.vue';
45
+ export { default as CodeSelect } from './fields/CodeSelect.vue';
46
+ export { default as CodeBlockList } from './layouts/sidebar/code-block/CodeBlockList.vue';
43
47
  export { default as PropsPanel } from './layouts/PropsPanel.vue';
44
48
  export { default as ToolButton } from './components/ToolButton.vue';
45
49
  export { default as ContentMenu } from './components/ContentMenu.vue';
46
50
  export { default as Icon } from './components/Icon.vue';
51
+ export { default as Layout } from './components/Layout.vue';
47
52
 
48
53
  const defaultInstallOpt: InstallOptions = {
49
54
  // @todo, 自定义图片上传方法等编辑器依赖的外部选项
@@ -56,11 +61,11 @@ export default {
56
61
  // eslint-disable-next-line no-param-reassign
57
62
  app.config.globalProperties.$TMAGIC_EDITOR = option;
58
63
  setConfig(option);
59
-
60
64
  app.component(Editor.name, Editor);
61
65
  app.component('m-fields-ui-select', uiSelect);
62
66
  app.component('m-fields-code-link', CodeLink);
63
67
  app.component('m-fields-vs-code', Code);
64
- app.component(CodeEditor.name, CodeEditor);
68
+ app.component('magic-code-editor', CodeEditor);
69
+ app.component('m-fields-code-select', CodeSelect);
65
70
  },
66
71
  };