@tmagic/editor 1.0.0-beta.8 → 1.0.0-rc.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.
- package/README.md +1 -0
- package/dist/style.css +94 -45
- package/dist/tmagic-editor.es.js +1622 -2069
- package/dist/tmagic-editor.es.js.map +1 -1
- package/dist/tmagic-editor.umd.js +1621 -2071
- package/dist/tmagic-editor.umd.js.map +1 -1
- package/dist/types/src/Editor.vue.d.ts +50 -2
- package/dist/types/src/fields/Code.vue.d.ts +4 -4
- package/dist/types/src/fields/CodeLink.vue.d.ts +1 -3
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/layouts/CodeEditor.vue.d.ts +13 -4
- package/dist/types/src/layouts/Framework.vue.d.ts +15 -3
- package/dist/types/src/layouts/PropsPanel.vue.d.ts +4 -5
- package/dist/types/src/layouts/sidebar/ComponentListPanel.vue.d.ts +2 -1
- package/dist/types/src/layouts/sidebar/Sidebar.vue.d.ts +2 -3
- package/dist/types/src/layouts/sidebar/TabPane.vue.d.ts +14 -0
- package/dist/types/src/layouts/workspace/Workspace.vue.d.ts +5 -31
- package/dist/types/src/services/BaseService.d.ts +5 -2
- package/dist/types/src/services/editor.d.ts +16 -6
- package/dist/types/src/services/history.d.ts +2 -18
- package/dist/types/src/services/props.d.ts +8 -2
- package/dist/types/src/services/ui.d.ts +2 -0
- package/dist/types/src/type.d.ts +25 -9
- package/dist/types/src/utils/editor.d.ts +5 -10
- package/dist/types/src/utils/polyfills.d.ts +0 -0
- package/dist/types/src/utils/props.d.ts +8 -1
- package/package.json +35 -12
- package/src/Editor.vue +44 -9
- package/src/components/ContentMenu.vue +140 -0
- package/src/components/ScrollViewer.vue +1 -1
- package/src/components/ToolButton.vue +72 -31
- package/src/fields/UISelect.vue +14 -6
- package/src/index.ts +2 -0
- package/src/layouts/AddPageBox.vue +3 -1
- package/src/layouts/CodeEditor.vue +20 -13
- package/src/layouts/Framework.vue +20 -7
- package/src/layouts/PropsPanel.vue +5 -4
- package/src/layouts/sidebar/ComponentListPanel.vue +25 -3
- package/src/layouts/sidebar/LayerMenu.vue +93 -95
- package/src/layouts/sidebar/LayerPanel.vue +80 -59
- package/src/layouts/sidebar/Sidebar.vue +13 -52
- package/src/layouts/sidebar/TabPane.vue +68 -0
- package/src/layouts/workspace/PageBar.vue +145 -17
- package/src/layouts/workspace/Stage.vue +65 -91
- package/src/layouts/workspace/ViewerMenu.vue +122 -68
- package/src/layouts/workspace/Workspace.vue +125 -42
- package/src/services/BaseService.ts +71 -23
- package/src/services/editor.ts +231 -69
- package/src/services/history.ts +7 -0
- package/src/services/props.ts +64 -9
- package/src/services/ui.ts +35 -5
- package/src/theme/common/var.scss +3 -2
- package/src/theme/component-list-panel.scss +2 -0
- package/src/theme/content-menu.scss +33 -26
- package/src/theme/layer-panel.scss +10 -0
- package/src/theme/nav-menu.scss +6 -2
- package/src/theme/page-bar.scss +42 -2
- package/src/theme/props-panel.scss +19 -17
- package/src/theme/workspace.scss +4 -0
- package/src/type.ts +27 -9
- package/src/utils/editor.ts +37 -56
- package/src/utils/polyfills.ts +8 -0
- package/src/utils/props.ts +50 -8
- package/LICENSE +0 -5432
- package/dist/types/src/components/ToolButton.vue.d.ts +0 -35
- package/dist/types/src/fields/UISelect.vue.d.ts +0 -32
- package/dist/types/src/layouts/sidebar/LayerMenu.vue.d.ts +0 -31
- package/dist/types/src/layouts/sidebar/LayerPanel.vue.d.ts +0 -973
- package/dist/types/src/layouts/workspace/PageBar.vue.d.ts +0 -11
- package/dist/types/src/layouts/workspace/Stage.vue.d.ts +0 -192
- package/dist/types/src/layouts/workspace/ViewerMenu.vue.d.ts +0 -18
- package/tsconfig.json +0 -9
- package/vite.config.ts +0 -30
|
@@ -1,46 +1,33 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<div>
|
|
4
|
-
<div class="magic-editor-content-menu-item" @click="() => center()" v-if="canCenter">水平居中</div>
|
|
5
|
-
<div class="magic-editor-content-menu-item" @click="() => copy()">复制</div>
|
|
6
|
-
<div class="magic-editor-content-menu-item" @click="paste" v-if="canPaste">粘贴</div>
|
|
7
|
-
<template v-if="canMoveZPos">
|
|
8
|
-
<div class="separation"></div>
|
|
9
|
-
<div class="magic-editor-content-menu-item" @click="topItem">上移一层</div>
|
|
10
|
-
<div class="magic-editor-content-menu-item" @click="bottomItem">下移一层</div>
|
|
11
|
-
<div class="magic-editor-content-menu-item" @click="top">置顶</div>
|
|
12
|
-
<div class="magic-editor-content-menu-item" @click="bottom">置底</div>
|
|
13
|
-
</template>
|
|
14
|
-
<template v-if="canDelete">
|
|
15
|
-
<div class="separation"></div>
|
|
16
|
-
<div class="magic-editor-content-menu-item" @click="() => remove()">删除</div>
|
|
17
|
-
</template>
|
|
18
|
-
<div class="separation"></div>
|
|
19
|
-
<div class="magic-editor-content-menu-item" @click="clearGuides">清空参考线</div>
|
|
20
|
-
</div>
|
|
21
|
-
</div>
|
|
2
|
+
<content-menu :menu-data="menuData" ref="menu"></content-menu>
|
|
22
3
|
</template>
|
|
23
4
|
|
|
24
5
|
<script lang="ts">
|
|
25
|
-
import { computed, defineComponent, inject, onMounted, ref, watch } from 'vue';
|
|
6
|
+
import { computed, defineComponent, inject, markRaw, onMounted, reactive, ref, watch } from 'vue';
|
|
7
|
+
import { Bottom, Delete, DocumentCopy, Top } from '@element-plus/icons';
|
|
26
8
|
|
|
9
|
+
import { NodeType } from '@tmagic/schema';
|
|
27
10
|
import type StageCore from '@tmagic/stage';
|
|
28
11
|
|
|
29
|
-
import
|
|
12
|
+
import ContentMenu from '@editor/components/ContentMenu.vue';
|
|
13
|
+
import { LayerOffset, Layout, MenuItem, Services } from '@editor/type';
|
|
30
14
|
import { COPY_STORAGE_KEY } from '@editor/utils/editor';
|
|
31
15
|
|
|
32
16
|
export default defineComponent({
|
|
33
|
-
|
|
17
|
+
components: { ContentMenu },
|
|
34
18
|
|
|
35
19
|
setup() {
|
|
36
20
|
const services = inject<Services>('services');
|
|
37
21
|
const editorService = services?.editorService;
|
|
38
|
-
const menu = ref<
|
|
22
|
+
const menu = ref<InstanceType<typeof ContentMenu>>();
|
|
39
23
|
const canPaste = ref(false);
|
|
40
24
|
const canCenter = ref(false);
|
|
41
25
|
|
|
42
26
|
const node = computed(() => editorService?.get('node'));
|
|
43
27
|
const parent = computed(() => editorService?.get('parent'));
|
|
28
|
+
const isPage = computed(() => node.value?.type === NodeType.PAGE);
|
|
29
|
+
|
|
30
|
+
const stageContentMenu = inject<MenuItem[]>('stageContentMenu', []);
|
|
44
31
|
|
|
45
32
|
onMounted(() => {
|
|
46
33
|
const data = globalThis.localStorage.getItem(COPY_STORAGE_KEY);
|
|
@@ -53,56 +40,123 @@ export default defineComponent({
|
|
|
53
40
|
if (!parent.value || !editorService) return (canCenter.value = false);
|
|
54
41
|
const layout = await editorService.getLayout(parent.value);
|
|
55
42
|
canCenter.value =
|
|
56
|
-
[Layout.ABSOLUTE, Layout.FIXED].includes(layout) &&
|
|
43
|
+
[Layout.ABSOLUTE, Layout.FIXED].includes(layout) &&
|
|
44
|
+
![NodeType.ROOT, NodeType.PAGE, 'pop'].includes(`${node.value?.type}`);
|
|
57
45
|
},
|
|
58
46
|
{ immediate: true },
|
|
59
47
|
);
|
|
60
48
|
|
|
61
49
|
return {
|
|
62
50
|
menu,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
51
|
+
menuData: reactive<MenuItem[]>([
|
|
52
|
+
{
|
|
53
|
+
type: 'button',
|
|
54
|
+
text: '水平居中',
|
|
55
|
+
display: () => canCenter.value,
|
|
56
|
+
handler: () => {
|
|
57
|
+
node.value && editorService?.alignCenter(node.value);
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
type: 'button',
|
|
62
|
+
text: '复制',
|
|
63
|
+
icon: markRaw(DocumentCopy),
|
|
64
|
+
handler: () => {
|
|
65
|
+
node.value && editorService?.copy(node.value);
|
|
66
|
+
canPaste.value = true;
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
type: 'button',
|
|
71
|
+
text: '粘贴',
|
|
72
|
+
display: () => canPaste.value,
|
|
73
|
+
handler: () => {
|
|
74
|
+
const stage = editorService?.get<StageCore>('stage');
|
|
75
|
+
|
|
76
|
+
const rect = menu.value?.$el.getBoundingClientRect();
|
|
77
|
+
const parentRect = stage?.container?.getBoundingClientRect();
|
|
78
|
+
let left = (rect?.left || 0) - (parentRect?.left || 0);
|
|
79
|
+
let top = (rect?.top || 0) - (parentRect?.top || 0);
|
|
80
|
+
|
|
81
|
+
if (node.value?.items && stage) {
|
|
82
|
+
const parentEl = stage.renderer.contentWindow?.document.getElementById(`${node.value.id}`);
|
|
83
|
+
const parentElRect = parentEl?.getBoundingClientRect();
|
|
84
|
+
left = left - (parentElRect?.left || 0);
|
|
85
|
+
top = top - (parentElRect?.top || 0);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
editorService?.paste({ left, top });
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
type: 'divider',
|
|
93
|
+
direction: 'horizontal',
|
|
94
|
+
display: () => !isPage.value,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
type: 'button',
|
|
98
|
+
text: '上移一层',
|
|
99
|
+
icon: markRaw(Top),
|
|
100
|
+
display: () => !isPage.value,
|
|
101
|
+
handler: () => {
|
|
102
|
+
editorService?.moveLayer(1);
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
type: 'button',
|
|
107
|
+
text: '下移一层',
|
|
108
|
+
icon: markRaw(Bottom),
|
|
109
|
+
display: () => !isPage.value,
|
|
110
|
+
handler: () => {
|
|
111
|
+
editorService?.moveLayer(-1);
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
type: 'button',
|
|
116
|
+
text: '置顶',
|
|
117
|
+
display: () => !isPage.value,
|
|
118
|
+
handler: () => {
|
|
119
|
+
editorService?.moveLayer(LayerOffset.TOP);
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
type: 'button',
|
|
124
|
+
text: '置底',
|
|
125
|
+
display: () => !isPage.value,
|
|
126
|
+
handler: () => {
|
|
127
|
+
editorService?.moveLayer(LayerOffset.BOTTOM);
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
type: 'divider',
|
|
132
|
+
direction: 'horizontal',
|
|
133
|
+
display: () => !isPage.value,
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
type: 'button',
|
|
137
|
+
text: '删除',
|
|
138
|
+
icon: Delete,
|
|
139
|
+
display: () => !isPage.value,
|
|
140
|
+
handler: () => {
|
|
141
|
+
node.value && editorService?.remove(node.value);
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
type: 'divider',
|
|
146
|
+
direction: 'horizontal',
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
type: 'button',
|
|
150
|
+
text: '清空参考线',
|
|
151
|
+
handler: () => {
|
|
152
|
+
editorService?.get<StageCore>('stage').clearGuides();
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
...stageContentMenu,
|
|
156
|
+
]),
|
|
157
|
+
|
|
158
|
+
show(e: MouseEvent) {
|
|
159
|
+
menu.value?.show(e);
|
|
106
160
|
},
|
|
107
161
|
};
|
|
108
162
|
},
|
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="m-editor-workspace">
|
|
3
|
-
<magic-stage
|
|
4
|
-
:key="page?.id"
|
|
5
|
-
:runtime-url="runtimeUrl"
|
|
6
|
-
:render="render"
|
|
7
|
-
:moveable-options="moveableOptions"
|
|
8
|
-
:can-select="canSelect"
|
|
9
|
-
@select="selectHandler"
|
|
10
|
-
@update="updateNodeHandler"
|
|
11
|
-
@sort="sortNodeHandler"
|
|
12
|
-
></magic-stage>
|
|
2
|
+
<div class="m-editor-workspace" tabindex="1" ref="workspace">
|
|
3
|
+
<magic-stage :key="page?.id"></magic-stage>
|
|
13
4
|
|
|
14
5
|
<slot name="workspace-content"></slot>
|
|
15
6
|
|
|
@@ -21,11 +12,11 @@
|
|
|
21
12
|
</template>
|
|
22
13
|
|
|
23
14
|
<script lang="ts">
|
|
24
|
-
import { computed, defineComponent, inject,
|
|
15
|
+
import { computed, defineComponent, inject, onMounted, onUnmounted, ref } from 'vue';
|
|
16
|
+
import KeyController from 'keycon';
|
|
25
17
|
|
|
26
|
-
import type {
|
|
27
|
-
import
|
|
28
|
-
import StageCore from '@tmagic/stage';
|
|
18
|
+
import type { MNode, MPage } from '@tmagic/schema';
|
|
19
|
+
import { isPage } from '@tmagic/utils';
|
|
29
20
|
|
|
30
21
|
import type { Services } from '@editor/type';
|
|
31
22
|
|
|
@@ -40,39 +31,131 @@ export default defineComponent({
|
|
|
40
31
|
MagicStage,
|
|
41
32
|
},
|
|
42
33
|
|
|
43
|
-
props: {
|
|
44
|
-
runtimeUrl: String,
|
|
45
|
-
|
|
46
|
-
render: {
|
|
47
|
-
type: Function as PropType<() => HTMLDivElement>,
|
|
48
|
-
},
|
|
49
|
-
|
|
50
|
-
moveableOptions: {
|
|
51
|
-
type: [Object, Function] as PropType<MoveableOptions | ((core?: StageCore) => MoveableOptions)>,
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
canSelect: {
|
|
55
|
-
type: Function as PropType<(el: HTMLElement) => boolean | Promise<boolean>>,
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
|
|
59
34
|
setup() {
|
|
60
35
|
const services = inject<Services>('services');
|
|
36
|
+
const workspace = ref<HTMLDivElement>();
|
|
37
|
+
const node = computed(() => services?.editorService.get<MNode>('node'));
|
|
38
|
+
let keycon: KeyController;
|
|
61
39
|
|
|
62
|
-
|
|
63
|
-
|
|
40
|
+
const mouseenterHandler = () => {
|
|
41
|
+
workspace.value?.focus();
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const mouseleaveHandler = () => {
|
|
45
|
+
workspace.value?.blur();
|
|
46
|
+
};
|
|
64
47
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
48
|
+
onMounted(() => {
|
|
49
|
+
workspace.value?.addEventListener('mouseenter', mouseenterHandler);
|
|
50
|
+
workspace.value?.addEventListener('mouseleave', mouseleaveHandler);
|
|
51
|
+
|
|
52
|
+
keycon = new KeyController(workspace.value);
|
|
53
|
+
|
|
54
|
+
const isMac = /mac os x/.test(navigator.userAgent.toLowerCase());
|
|
55
|
+
|
|
56
|
+
const ctrl = isMac ? 'meta' : 'ctrl';
|
|
57
|
+
|
|
58
|
+
keycon
|
|
59
|
+
.keyup('delete', (e) => {
|
|
60
|
+
e.inputEvent.preventDefault();
|
|
61
|
+
if (!node.value || isPage(node.value)) return;
|
|
62
|
+
services?.editorService.remove(node.value);
|
|
63
|
+
})
|
|
64
|
+
.keyup('backspace', (e) => {
|
|
65
|
+
e.inputEvent.preventDefault();
|
|
66
|
+
if (!node.value || isPage(node.value)) return;
|
|
67
|
+
services?.editorService.remove(node.value);
|
|
68
|
+
})
|
|
69
|
+
.keydown([ctrl, 'c'], (e) => {
|
|
70
|
+
e.inputEvent.preventDefault();
|
|
71
|
+
node.value && services?.editorService.copy(node.value);
|
|
72
|
+
})
|
|
73
|
+
.keydown([ctrl, 'v'], (e) => {
|
|
74
|
+
e.inputEvent.preventDefault();
|
|
75
|
+
node.value && services?.editorService.paste();
|
|
76
|
+
})
|
|
77
|
+
.keydown([ctrl, 'x'], (e) => {
|
|
78
|
+
e.inputEvent.preventDefault();
|
|
79
|
+
if (!node.value || isPage(node.value)) return;
|
|
80
|
+
services?.editorService.copy(node.value);
|
|
81
|
+
services?.editorService.remove(node.value);
|
|
82
|
+
})
|
|
83
|
+
.keydown([ctrl, 'z'], (e) => {
|
|
84
|
+
e.inputEvent.preventDefault();
|
|
85
|
+
services?.editorService.undo();
|
|
86
|
+
})
|
|
87
|
+
.keydown([ctrl, 'shift', 'z'], (e) => {
|
|
88
|
+
e.inputEvent.preventDefault();
|
|
89
|
+
services?.editorService.redo();
|
|
90
|
+
})
|
|
91
|
+
.keydown('up', (e) => {
|
|
92
|
+
e.inputEvent.preventDefault();
|
|
93
|
+
services?.editorService.move(0, -1);
|
|
94
|
+
})
|
|
95
|
+
.keydown('down', (e) => {
|
|
96
|
+
e.inputEvent.preventDefault();
|
|
97
|
+
services?.editorService.move(0, 1);
|
|
98
|
+
})
|
|
99
|
+
.keydown('left', (e) => {
|
|
100
|
+
e.inputEvent.preventDefault();
|
|
101
|
+
services?.editorService.move(-1, 0);
|
|
102
|
+
})
|
|
103
|
+
.keydown('right', (e) => {
|
|
104
|
+
e.inputEvent.preventDefault();
|
|
105
|
+
services?.editorService.move(1, 0);
|
|
106
|
+
})
|
|
107
|
+
.keydown([ctrl, 'up'], (e) => {
|
|
108
|
+
e.inputEvent.preventDefault();
|
|
109
|
+
services?.editorService.move(0, -10);
|
|
110
|
+
})
|
|
111
|
+
.keydown([ctrl, 'down'], (e) => {
|
|
112
|
+
e.inputEvent.preventDefault();
|
|
113
|
+
services?.editorService.move(0, 10);
|
|
114
|
+
})
|
|
115
|
+
.keydown([ctrl, 'left'], (e) => {
|
|
116
|
+
e.inputEvent.preventDefault();
|
|
117
|
+
services?.editorService.move(-10, 0);
|
|
118
|
+
})
|
|
119
|
+
.keydown([ctrl, 'right'], (e) => {
|
|
120
|
+
e.inputEvent.preventDefault();
|
|
121
|
+
services?.editorService.move(10, 0);
|
|
122
|
+
})
|
|
123
|
+
.keydown('tab', (e) => {
|
|
124
|
+
e.inputEvent.preventDefault();
|
|
125
|
+
services?.editorService.selectNextNode();
|
|
126
|
+
})
|
|
127
|
+
.keydown([ctrl, 'tab'], (e) => {
|
|
128
|
+
e.inputEvent.preventDefault();
|
|
129
|
+
services?.editorService.selectNextPage();
|
|
130
|
+
})
|
|
131
|
+
.keydown([ctrl, '='], (e) => {
|
|
132
|
+
e.inputEvent.preventDefault();
|
|
133
|
+
services?.uiService.zoom(0.1);
|
|
134
|
+
})
|
|
135
|
+
.keydown([ctrl, 'numpadplus'], (e) => {
|
|
136
|
+
e.inputEvent.preventDefault();
|
|
137
|
+
services?.uiService.zoom(0.1);
|
|
138
|
+
})
|
|
139
|
+
.keydown([ctrl, '-'], (e) => {
|
|
140
|
+
e.inputEvent.preventDefault();
|
|
141
|
+
services?.uiService.zoom(-0.1);
|
|
142
|
+
})
|
|
143
|
+
.keydown([ctrl, 'numpad-'], (e) => {
|
|
144
|
+
e.inputEvent.preventDefault();
|
|
145
|
+
services?.uiService.zoom(-0.1);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
onUnmounted(() => {
|
|
150
|
+
workspace.value?.removeEventListener('mouseenter', mouseenterHandler);
|
|
151
|
+
workspace.value?.removeEventListener('mouseleave', mouseleaveHandler);
|
|
152
|
+
keycon.destroy();
|
|
153
|
+
});
|
|
68
154
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
},
|
|
155
|
+
return {
|
|
156
|
+
workspace,
|
|
72
157
|
|
|
73
|
-
|
|
74
|
-
services?.editorService.sort(ev.src, ev.dist);
|
|
75
|
-
},
|
|
158
|
+
page: computed(() => services?.editorService.get<MPage>('page')),
|
|
76
159
|
};
|
|
77
160
|
},
|
|
78
161
|
});
|
|
@@ -24,6 +24,46 @@ const methodName = (prefix: string, name: string) => `${prefix}${name[0].toUpper
|
|
|
24
24
|
|
|
25
25
|
const isError = (error: any): boolean => Object.prototype.toString.call(error) === '[object Error]';
|
|
26
26
|
|
|
27
|
+
const doAction = async (
|
|
28
|
+
args: any[],
|
|
29
|
+
scope: any,
|
|
30
|
+
sourceMethod: any,
|
|
31
|
+
beforeMethodName: string,
|
|
32
|
+
afterMethodName: string,
|
|
33
|
+
fn: (args: any[], next?: Function | undefined) => Promise<void>,
|
|
34
|
+
) => {
|
|
35
|
+
try {
|
|
36
|
+
let beforeArgs = args;
|
|
37
|
+
|
|
38
|
+
for (const beforeMethod of scope.pluginOptionsList[beforeMethodName]) {
|
|
39
|
+
let beforeReturnValue = (await beforeMethod(...beforeArgs)) || [];
|
|
40
|
+
|
|
41
|
+
if (isError(beforeReturnValue)) throw beforeReturnValue;
|
|
42
|
+
|
|
43
|
+
if (!Array.isArray(beforeReturnValue)) {
|
|
44
|
+
beforeReturnValue = [beforeReturnValue];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
beforeArgs = beforeArgs.map((v: any, index: number) => {
|
|
48
|
+
if (typeof beforeReturnValue[index] === 'undefined') return v;
|
|
49
|
+
return beforeReturnValue[index];
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let returnValue: any = await fn(beforeArgs, sourceMethod.bind(scope));
|
|
54
|
+
|
|
55
|
+
for (const afterMethod of scope.pluginOptionsList[afterMethodName]) {
|
|
56
|
+
returnValue = await afterMethod(returnValue, ...beforeArgs);
|
|
57
|
+
|
|
58
|
+
if (isError(returnValue)) throw returnValue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return returnValue;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
27
67
|
/**
|
|
28
68
|
* 提供两种方式对Class进行扩展
|
|
29
69
|
* 方法1:
|
|
@@ -51,7 +91,7 @@ const isError = (error: any): boolean => Object.prototype.toString.call(error) =
|
|
|
51
91
|
* editorService.afterAdd();
|
|
52
92
|
* }
|
|
53
93
|
*
|
|
54
|
-
* 调用时的参数会透传到before方法的参数中, 然后before的return 会作为原方法的参数和after的参数,after
|
|
94
|
+
* 调用时的参数会透传到before方法的参数中, 然后before的return 会作为原方法的参数和after的参数,after第一个参数则是原方法的return值;
|
|
55
95
|
* 如需终止后续方法调用可以return new Error();
|
|
56
96
|
*
|
|
57
97
|
* 方法2:
|
|
@@ -74,8 +114,10 @@ const isError = (error: any): boolean => Object.prototype.toString.call(error) =
|
|
|
74
114
|
export default class extends EventEmitter {
|
|
75
115
|
private pluginOptionsList: Record<string, Function[]> = {};
|
|
76
116
|
private middleware: Record<string, Function[]> = {};
|
|
117
|
+
private taskList: (() => Promise<void>)[] = [];
|
|
118
|
+
private doingTask = false;
|
|
77
119
|
|
|
78
|
-
constructor(methods: string[]) {
|
|
120
|
+
constructor(methods: string[] = [], serialMethods: string[] = []) {
|
|
79
121
|
super();
|
|
80
122
|
|
|
81
123
|
methods.forEach((propertyName: string) => {
|
|
@@ -93,32 +135,28 @@ export default class extends EventEmitter {
|
|
|
93
135
|
const fn = compose(this.middleware[propertyName]);
|
|
94
136
|
Object.defineProperty(scope, propertyName, {
|
|
95
137
|
value: async (...args: any[]) => {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
for (const beforeMethod of this.pluginOptionsList[beforeMethodName]) {
|
|
99
|
-
let beforeReturnValue = (await beforeMethod(...beforeArgs)) || [];
|
|
100
|
-
|
|
101
|
-
if (isError(beforeReturnValue)) throw beforeReturnValue;
|
|
102
|
-
|
|
103
|
-
if (!Array.isArray(beforeReturnValue)) {
|
|
104
|
-
beforeReturnValue = [beforeReturnValue];
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
beforeArgs = beforeArgs.map((v: any, index: number) => {
|
|
108
|
-
if (typeof beforeReturnValue[index] === 'undefined') return v;
|
|
109
|
-
return beforeReturnValue[index];
|
|
110
|
-
});
|
|
138
|
+
if (!serialMethods.includes(propertyName)) {
|
|
139
|
+
return doAction(args, scope, sourceMethod, beforeMethodName, afterMethodName, fn);
|
|
111
140
|
}
|
|
112
141
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
142
|
+
// 由于async await,所以会出现函数执行到await时让出线程,导致执行顺序出错,例如调用了select(1) -> update -> select(2),这个时候就有可能出现update了2;
|
|
143
|
+
// 这里保证函数调用严格按顺序执行;
|
|
144
|
+
const promise = new Promise<any>((resolve, reject) => {
|
|
145
|
+
this.taskList.push(async () => {
|
|
146
|
+
try {
|
|
147
|
+
const value = await doAction(args, scope, sourceMethod, beforeMethodName, afterMethodName, fn);
|
|
148
|
+
resolve(value);
|
|
149
|
+
} catch (e) {
|
|
150
|
+
reject(e);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
});
|
|
117
154
|
|
|
118
|
-
|
|
155
|
+
if (!this.doingTask) {
|
|
156
|
+
this.doTask();
|
|
119
157
|
}
|
|
120
158
|
|
|
121
|
-
return
|
|
159
|
+
return promise;
|
|
122
160
|
},
|
|
123
161
|
});
|
|
124
162
|
});
|
|
@@ -135,4 +173,14 @@ export default class extends EventEmitter {
|
|
|
135
173
|
if (typeof method === 'function') this.pluginOptionsList[methodName].push(method);
|
|
136
174
|
});
|
|
137
175
|
}
|
|
176
|
+
|
|
177
|
+
private async doTask() {
|
|
178
|
+
this.doingTask = true;
|
|
179
|
+
let task = this.taskList.shift();
|
|
180
|
+
while (task) {
|
|
181
|
+
await task();
|
|
182
|
+
task = this.taskList.shift();
|
|
183
|
+
}
|
|
184
|
+
this.doingTask = false;
|
|
185
|
+
}
|
|
138
186
|
}
|