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