@tmagic/editor 1.4.4 → 1.4.6
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 +2 -2
- package/dist/tmagic-editor.js +2393 -2242
- package/dist/tmagic-editor.umd.cjs +2403 -2248
- package/package.json +27 -27
- package/src/editorProps.ts +5 -1
- package/src/fields/DataSourceFieldSelect.vue +3 -41
- package/src/fields/EventSelect.vue +30 -9
- package/src/hooks/use-code-block-edit.ts +0 -2
- package/src/hooks/use-stage.ts +1 -1
- package/src/index.ts +1 -0
- package/src/initService.ts +12 -2
- package/src/layouts/workspace/viewer/Stage.vue +7 -6
- package/src/services/codeBlock.ts +77 -9
- package/src/services/dataSource.ts +53 -4
- package/src/services/editor.ts +2 -2
- package/src/services/props.ts +10 -5
- package/src/services/stageOverlay.ts +1 -0
- package/src/type.ts +1 -0
- package/src/utils/content-menu.ts +7 -3
- package/src/utils/data-source/index.ts +39 -3
- package/src/utils/editor.ts +16 -7
- package/types/components/CodeBlockEditor.vue.d.ts +21 -34
- package/types/components/CodeParams.vue.d.ts +2 -2
- package/types/components/ContentMenu.vue.d.ts +1 -1
- package/types/components/FloatingBox.vue.d.ts +41 -38
- package/types/components/ScrollViewer.vue.d.ts +1 -1
- package/types/components/ToolButton.vue.d.ts +4 -4
- package/types/components/Tree.vue.d.ts +2 -2
- package/types/editorProps.d.ts +5 -1
- package/types/fields/CodeLink.vue.d.ts +2 -2
- package/types/fields/DataSourceFields.vue.d.ts +32 -78
- package/types/fields/DataSourceInput.vue.d.ts +2 -2
- package/types/fields/DataSourceMethods.vue.d.ts +2 -2
- package/types/fields/DataSourceMocks.vue.d.ts +31 -76
- package/types/fields/KeyValue.vue.d.ts +2 -2
- package/types/fields/UISelect.vue.d.ts +2 -2
- package/types/hooks/use-code-block-edit.d.ts +18 -6
- package/types/hooks/use-data-source-edit.d.ts +18 -6
- package/types/hooks/use-data-source-method.d.ts +18 -6
- package/types/index.d.ts +1 -0
- package/types/layouts/CodeEditor.vue.d.ts +1 -1
- package/types/layouts/PropsPanel.vue.d.ts +14 -14
- package/types/layouts/sidebar/data-source/DataSourceConfigPanel.vue.d.ts +20 -30
- package/types/layouts/sidebar/layer/LayerMenu.vue.d.ts +6 -6
- package/types/layouts/workspace/viewer/Stage.vue.d.ts +4 -4
- package/types/layouts/workspace/viewer/ViewerMenu.vue.d.ts +6 -6
- package/types/services/codeBlock.d.ts +22 -2
- package/types/services/dataSource.d.ts +13 -1
- package/types/services/ui.d.ts +1 -1
- package/types/type.d.ts +1 -0
- package/types/utils/data-source/index.d.ts +3 -2
- package/types/utils/editor.d.ts +2 -0
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.4.
|
|
2
|
+
"version": "1.4.6",
|
|
3
3
|
"name": "@tmagic/editor",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": [
|
|
@@ -40,16 +40,7 @@
|
|
|
40
40
|
"typescript"
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@babel/core": "^7.18.0",
|
|
44
43
|
"@element-plus/icons-vue": "^2.3.1",
|
|
45
|
-
"@tmagic/core": "1.4.4",
|
|
46
|
-
"@tmagic/dep": "1.4.4",
|
|
47
|
-
"@tmagic/design": "1.4.4",
|
|
48
|
-
"@tmagic/form": "1.4.4",
|
|
49
|
-
"@tmagic/schema": "1.4.4",
|
|
50
|
-
"@tmagic/stage": "1.4.4",
|
|
51
|
-
"@tmagic/table": "1.4.4",
|
|
52
|
-
"@tmagic/utils": "1.4.4",
|
|
53
44
|
"buffer": "^6.0.3",
|
|
54
45
|
"color": "^3.1.3",
|
|
55
46
|
"emmet-monaco-es": "^5.3.0",
|
|
@@ -57,37 +48,46 @@
|
|
|
57
48
|
"gesto": "^1.19.1",
|
|
58
49
|
"keycon": "^1.4.0",
|
|
59
50
|
"lodash-es": "^4.17.21",
|
|
60
|
-
"
|
|
61
|
-
"moveable": "^0.51.1",
|
|
51
|
+
"moveable": "^0.53.0",
|
|
62
52
|
"serialize-javascript": "^6.0.0",
|
|
63
|
-
"
|
|
64
|
-
|
|
65
|
-
"peerDependencies": {
|
|
66
|
-
"@tmagic/design": "1.4.4",
|
|
67
|
-
"@tmagic/form": "1.4.4",
|
|
68
|
-
"monaco-editor": "^0.47.0",
|
|
69
|
-
"vue": "^3.4.21"
|
|
53
|
+
"@tmagic/dep": "1.4.6",
|
|
54
|
+
"@tmagic/table": "1.4.6"
|
|
70
55
|
},
|
|
71
56
|
"devDependencies": {
|
|
72
57
|
"@types/events": "^3.0.0",
|
|
73
58
|
"@types/lodash-es": "^4.17.4",
|
|
74
59
|
"@types/node": "^18.19.0",
|
|
75
60
|
"@types/serialize-javascript": "^5.0.1",
|
|
76
|
-
"@vitejs/plugin-vue": "^
|
|
77
|
-
"@vue/compiler-sfc": "^3.4.
|
|
78
|
-
"@vue/test-utils": "^2.4.
|
|
61
|
+
"@vitejs/plugin-vue": "^5.0.4",
|
|
62
|
+
"@vue/compiler-sfc": "^3.4.27",
|
|
63
|
+
"@vue/test-utils": "^2.4.6",
|
|
79
64
|
"rimraf": "^3.0.2",
|
|
80
|
-
"sass": "^1.
|
|
65
|
+
"sass": "^1.77.0",
|
|
81
66
|
"tsc-alias": "^1.8.5",
|
|
82
67
|
"type-fest": "^4.10.3",
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
|
|
68
|
+
"vite": "^5.2.11",
|
|
69
|
+
"vue-tsc": "^2.0.16"
|
|
70
|
+
},
|
|
71
|
+
"peerDependencies": {
|
|
72
|
+
"monaco-editor": "^0.48.0",
|
|
73
|
+
"vue": "^3.4.27",
|
|
74
|
+
"typescript": "*",
|
|
75
|
+
"@tmagic/form": "1.4.6",
|
|
76
|
+
"@tmagic/schema": "1.4.6",
|
|
77
|
+
"@tmagic/design": "1.4.6",
|
|
78
|
+
"@tmagic/core": "1.4.6",
|
|
79
|
+
"@tmagic/utils": "1.4.6",
|
|
80
|
+
"@tmagic/stage": "1.4.6"
|
|
81
|
+
},
|
|
82
|
+
"peerDependenciesMeta": {
|
|
83
|
+
"typescript": {
|
|
84
|
+
"optional": true
|
|
85
|
+
}
|
|
86
86
|
},
|
|
87
87
|
"scripts": {
|
|
88
88
|
"build": "npm run build:type && vite build",
|
|
89
89
|
"build:type": "npm run clear:type && vue-tsc --declaration --emitDeclarationOnly --project tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
|
90
90
|
"clear:type": "rimraf ./types",
|
|
91
|
-
"type
|
|
91
|
+
"check:type": "vue-tsc --noEmit --project tsconfig.build.json"
|
|
92
92
|
}
|
|
93
93
|
}
|
package/src/editorProps.ts
CHANGED
|
@@ -70,8 +70,12 @@ export interface EditorProps {
|
|
|
70
70
|
codeOptions?: { [key: string]: any };
|
|
71
71
|
/** 禁用鼠标左键按下时就开始拖拽,需要先选中再可以拖拽 */
|
|
72
72
|
disabledDragStart?: boolean;
|
|
73
|
-
/**
|
|
73
|
+
/** 自定义依赖收集器,复制组件时会将关联组件一并复制 */
|
|
74
74
|
collectorOptions?: CustomTargetOptions;
|
|
75
|
+
/** 自定义依赖收集器,复制组件时会将关联代码块一并复制 */
|
|
76
|
+
collectorOptionsForCode?: CustomTargetOptions;
|
|
77
|
+
/** 自定义依赖收集器,复制组件时会将关联数据源一并复制 */
|
|
78
|
+
collectorOptionsForDataSource?: CustomTargetOptions;
|
|
75
79
|
/** 标尺配置 */
|
|
76
80
|
guidesOptions?: Partial<GuidesOptions>;
|
|
77
81
|
/** 禁止多选 */
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="m-fields-data-source-field-select">
|
|
3
3
|
<component
|
|
4
|
-
style="width: 100%"
|
|
5
4
|
:is="tagName"
|
|
6
5
|
:config="showDataSourceFieldSelect || !config.fieldConfig ? cascaderConfig : config.fieldConfig"
|
|
7
6
|
:model="model"
|
|
@@ -39,14 +38,14 @@ import { computed, inject, ref, resolveComponent, watch } from 'vue';
|
|
|
39
38
|
import { Coin, Edit, View } from '@element-plus/icons-vue';
|
|
40
39
|
|
|
41
40
|
import { TMagicButton } from '@tmagic/design';
|
|
42
|
-
import type { CascaderConfig,
|
|
41
|
+
import type { CascaderConfig, FieldProps, FormState } from '@tmagic/form';
|
|
43
42
|
import { filterFunction, MCascader } from '@tmagic/form';
|
|
44
|
-
import type { DataSchema, DataSourceFieldType } from '@tmagic/schema';
|
|
45
43
|
import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX } from '@tmagic/utils';
|
|
46
44
|
|
|
47
45
|
import MIcon from '@editor/components/Icon.vue';
|
|
48
46
|
import type { DataSourceFieldSelectConfig, EventBus, Services } from '@editor/type';
|
|
49
47
|
import { SideItemKey } from '@editor/type';
|
|
48
|
+
import { getCascaderOptionsFromFields } from '@editor/utils';
|
|
50
49
|
|
|
51
50
|
defineOptions({
|
|
52
51
|
name: 'MFieldsDataSourceFieldSelect',
|
|
@@ -77,43 +76,6 @@ const selectedDataSourceId = computed(() => {
|
|
|
77
76
|
|
|
78
77
|
const dataSources = computed(() => services?.dataSourceService.get('dataSources'));
|
|
79
78
|
|
|
80
|
-
const getOptionChildren = (
|
|
81
|
-
fields: DataSchema[] = [],
|
|
82
|
-
dataSourceFieldType: DataSourceFieldType[] = ['any'],
|
|
83
|
-
): CascaderOption[] => {
|
|
84
|
-
const child: CascaderOption[] = [];
|
|
85
|
-
fields.forEach((field) => {
|
|
86
|
-
if (!dataSourceFieldType.length) {
|
|
87
|
-
dataSourceFieldType.push('any');
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const children = getOptionChildren(field.fields, dataSourceFieldType);
|
|
91
|
-
|
|
92
|
-
const item = {
|
|
93
|
-
label: field.title || field.name,
|
|
94
|
-
value: field.name,
|
|
95
|
-
children,
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const fieldType = field.type || 'any';
|
|
99
|
-
if (dataSourceFieldType.includes('any') || dataSourceFieldType.includes(fieldType)) {
|
|
100
|
-
child.push(item);
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (!dataSourceFieldType.includes(fieldType) && !['array', 'object', 'any'].includes(fieldType)) {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (!children.length && ['object', 'array', 'any'].includes(field.type || '')) {
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
child.push(item);
|
|
113
|
-
});
|
|
114
|
-
return child;
|
|
115
|
-
};
|
|
116
|
-
|
|
117
79
|
const cascaderConfig = computed<CascaderConfig>(() => {
|
|
118
80
|
const valueIsKey = props.config.value === 'key';
|
|
119
81
|
|
|
@@ -126,7 +88,7 @@ const cascaderConfig = computed<CascaderConfig>(() => {
|
|
|
126
88
|
dataSources.value?.map((ds) => ({
|
|
127
89
|
label: ds.title || ds.id,
|
|
128
90
|
value: valueIsKey ? ds.id : `${DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX}${ds.id}`,
|
|
129
|
-
children:
|
|
91
|
+
children: getCascaderOptionsFromFields(ds.fields, props.config.dataSourceFieldType),
|
|
130
92
|
})) || [];
|
|
131
93
|
return options.filter((option) => option.children.length);
|
|
132
94
|
},
|
|
@@ -55,11 +55,13 @@ import { has } from 'lodash-es';
|
|
|
55
55
|
|
|
56
56
|
import type { EventOption } from '@tmagic/core';
|
|
57
57
|
import { TMagicButton } from '@tmagic/design';
|
|
58
|
-
import type { FieldProps, FormState, PanelConfig } from '@tmagic/form';
|
|
58
|
+
import type { CascaderOption, FieldProps, FormState, PanelConfig } from '@tmagic/form';
|
|
59
59
|
import { MContainer, MPanel } from '@tmagic/form';
|
|
60
60
|
import { ActionType } from '@tmagic/schema';
|
|
61
|
+
import { DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX } from '@tmagic/utils';
|
|
61
62
|
|
|
62
63
|
import type { CodeSelectColConfig, DataSourceMethodSelectConfig, EventSelectConfig, Services } from '@editor/type';
|
|
64
|
+
import { getCascaderOptionsFromFields } from '@editor/utils';
|
|
63
65
|
|
|
64
66
|
defineOptions({
|
|
65
67
|
name: 'MFieldsEventSelect',
|
|
@@ -78,26 +80,45 @@ const codeBlockService = services?.codeBlockService;
|
|
|
78
80
|
|
|
79
81
|
// 事件名称下拉框表单配置
|
|
80
82
|
const eventNameConfig = computed(() => {
|
|
83
|
+
const fieldType = props.config.src === 'component' ? 'select' : 'cascader';
|
|
81
84
|
const defaultEventNameConfig = {
|
|
82
85
|
name: 'name',
|
|
83
86
|
text: '事件',
|
|
84
|
-
type:
|
|
87
|
+
type: fieldType,
|
|
85
88
|
labelWidth: '40px',
|
|
89
|
+
checkStrictly: true,
|
|
90
|
+
valueSeparator: '.',
|
|
86
91
|
options: (mForm: FormState, { formValue }: any) => {
|
|
87
|
-
let events: EventOption[] = [];
|
|
92
|
+
let events: EventOption[] | CascaderOption[] = [];
|
|
88
93
|
|
|
89
94
|
if (!eventsService || !dataSourceService) return events;
|
|
90
95
|
|
|
91
96
|
if (props.config.src === 'component') {
|
|
92
97
|
events = eventsService.getEvent(formValue.type);
|
|
93
|
-
|
|
94
|
-
|
|
98
|
+
return events.map((option) => ({
|
|
99
|
+
text: option.label,
|
|
100
|
+
value: option.value,
|
|
101
|
+
}));
|
|
95
102
|
}
|
|
103
|
+
if (props.config.src === 'datasource') {
|
|
104
|
+
// 从数据源类型中获取到相关事件
|
|
105
|
+
events = dataSourceService.getFormEvent(formValue.type);
|
|
106
|
+
// 从数据源类型和实例中分别获取数据以追加数据变化的事件
|
|
107
|
+
const dataSource = dataSourceService.getDataSourceById(formValue.id);
|
|
108
|
+
const fields = dataSource?.fields || [];
|
|
109
|
+
if (fields.length > 0) {
|
|
110
|
+
return [
|
|
111
|
+
...events,
|
|
112
|
+
{
|
|
113
|
+
label: '数据变化',
|
|
114
|
+
value: DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX,
|
|
115
|
+
children: getCascaderOptionsFromFields(fields),
|
|
116
|
+
},
|
|
117
|
+
];
|
|
118
|
+
}
|
|
96
119
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
value: option.value,
|
|
100
|
-
}));
|
|
120
|
+
return events;
|
|
121
|
+
}
|
|
101
122
|
},
|
|
102
123
|
};
|
|
103
124
|
return { ...defaultEventNameConfig, ...props.config.eventNameConfig };
|
package/src/hooks/use-stage.ts
CHANGED
|
@@ -24,7 +24,7 @@ export const useStage = (stageOptions: StageOptions) => {
|
|
|
24
24
|
const stage = new StageCore({
|
|
25
25
|
render: stageOptions.render,
|
|
26
26
|
runtimeUrl: stageOptions.runtimeUrl,
|
|
27
|
-
zoom: zoom.value,
|
|
27
|
+
zoom: stageOptions.zoom ?? zoom.value,
|
|
28
28
|
autoScrollIntoView: stageOptions.autoScrollIntoView,
|
|
29
29
|
isContainer: stageOptions.isContainer,
|
|
30
30
|
containerHighlightClassName: stageOptions.containerHighlightClassName,
|
package/src/index.ts
CHANGED
|
@@ -82,6 +82,7 @@ export { default as LayoutContainer } from './components/SplitView.vue';
|
|
|
82
82
|
export { default as SplitView } from './components/SplitView.vue';
|
|
83
83
|
export { default as Resizer } from './components/Resizer.vue';
|
|
84
84
|
export { default as CodeBlockEditor } from './components/CodeBlockEditor.vue';
|
|
85
|
+
export { default as FloatingBox } from './components/FloatingBox.vue';
|
|
85
86
|
export { default as PageFragmentSelect } from './fields/PageFragmentSelect.vue';
|
|
86
87
|
|
|
87
88
|
const defaultInstallOpt: InstallOptions = {
|
package/src/initService.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
createDataSourceCondTarget,
|
|
8
8
|
createDataSourceMethodTarget,
|
|
9
9
|
createDataSourceTarget,
|
|
10
|
-
|
|
10
|
+
createRelatedTargetForCopy,
|
|
11
11
|
DepTargetType,
|
|
12
12
|
Target,
|
|
13
13
|
} from '@tmagic/dep';
|
|
@@ -412,7 +412,17 @@ export const initServiceEvents = (
|
|
|
412
412
|
|
|
413
413
|
// 初始化复制组件相关的依赖收集器
|
|
414
414
|
if (props.collectorOptions && !depService.hasTarget(DepTargetType.RELATED_COMP_WHEN_COPY)) {
|
|
415
|
-
depService.addTarget(
|
|
415
|
+
depService.addTarget(createRelatedTargetForCopy(props.collectorOptions, DepTargetType.RELATED_COMP_WHEN_COPY));
|
|
416
|
+
}
|
|
417
|
+
if (props.collectorOptionsForCode && !depService.hasTarget(DepTargetType.RELATED_CODE_WHEN_COPY)) {
|
|
418
|
+
depService.addTarget(
|
|
419
|
+
createRelatedTargetForCopy(props.collectorOptionsForCode, DepTargetType.RELATED_CODE_WHEN_COPY),
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
if (props.collectorOptionsForDataSource && !depService.hasTarget(DepTargetType.RELATED_DS_WHEN_COPY)) {
|
|
423
|
+
depService.addTarget(
|
|
424
|
+
createRelatedTargetForCopy(props.collectorOptionsForDataSource, DepTargetType.RELATED_DS_WHEN_COPY),
|
|
425
|
+
);
|
|
416
426
|
}
|
|
417
427
|
|
|
418
428
|
onBeforeUnmount(() => {
|
|
@@ -45,7 +45,8 @@ import { computed, inject, markRaw, nextTick, onBeforeUnmount, onMounted, ref, t
|
|
|
45
45
|
import { cloneDeep } from 'lodash-es';
|
|
46
46
|
|
|
47
47
|
import type { MApp, MContainer } from '@tmagic/schema';
|
|
48
|
-
import StageCore, {
|
|
48
|
+
import StageCore, { getOffset, Runtime } from '@tmagic/stage';
|
|
49
|
+
import { calcValueByFontsize } from '@tmagic/utils';
|
|
49
50
|
|
|
50
51
|
import ScrollViewer from '@editor/components/ScrollViewer.vue';
|
|
51
52
|
import { useStage } from '@editor/hooks/use-stage';
|
|
@@ -223,18 +224,18 @@ const dropHandler = async (e: DragEvent) => {
|
|
|
223
224
|
top = e.clientY - containerRect.top + scrollTop;
|
|
224
225
|
left = e.clientX - containerRect.left + scrollLeft;
|
|
225
226
|
|
|
226
|
-
if (parentEl
|
|
227
|
+
if (parentEl) {
|
|
227
228
|
const { left: parentLeft, top: parentTop } = getOffset(parentEl);
|
|
228
|
-
left = left -
|
|
229
|
-
top = top -
|
|
229
|
+
left = left - parentLeft * zoom.value;
|
|
230
|
+
top = top - parentTop * zoom.value;
|
|
230
231
|
}
|
|
231
232
|
}
|
|
232
233
|
|
|
233
234
|
config.data.style = {
|
|
234
235
|
...style,
|
|
235
236
|
position,
|
|
236
|
-
top: top / zoom.value,
|
|
237
|
-
left: left / zoom.value,
|
|
237
|
+
top: calcValueByFontsize(doc, top / zoom.value),
|
|
238
|
+
left: calcValueByFontsize(doc, left / zoom.value),
|
|
238
239
|
};
|
|
239
240
|
|
|
240
241
|
config.data.inputEvent = e;
|
|
@@ -17,21 +17,26 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { reactive } from 'vue';
|
|
20
|
-
import { keys, pick } from 'lodash-es';
|
|
20
|
+
import { cloneDeep, get, keys, pick } from 'lodash-es';
|
|
21
21
|
import type { Writable } from 'type-fest';
|
|
22
22
|
|
|
23
|
+
import { DepTargetType } from '@tmagic/dep';
|
|
23
24
|
import type { ColumnConfig } from '@tmagic/form';
|
|
24
|
-
import type { CodeBlockContent, CodeBlockDSL, Id } from '@tmagic/schema';
|
|
25
|
+
import type { CodeBlockContent, CodeBlockDSL, Id, MNode } from '@tmagic/schema';
|
|
25
26
|
|
|
27
|
+
import depService from '@editor/services/dep';
|
|
28
|
+
import editorService from '@editor/services/editor';
|
|
29
|
+
import storageService, { Protocol } from '@editor/services/storage';
|
|
26
30
|
import type { AsyncHookPlugin, CodeState } from '@editor/type';
|
|
27
31
|
import { CODE_DRAFT_STORAGE_KEY } from '@editor/type';
|
|
28
32
|
import { getConfig } from '@editor/utils/config';
|
|
33
|
+
import { COPY_CODE_STORAGE_KEY } from '@editor/utils/editor';
|
|
29
34
|
|
|
30
35
|
import BaseService from './BaseService';
|
|
31
36
|
|
|
32
37
|
const canUsePluginMethods = {
|
|
33
38
|
async: ['setCodeDslById', 'setEditStatus', 'setCombineIds', 'setUndeleteableList', 'deleteCodeDslByIds'] as const,
|
|
34
|
-
sync: [],
|
|
39
|
+
sync: ['setCodeDslByIdSync'],
|
|
35
40
|
};
|
|
36
41
|
|
|
37
42
|
type AsyncMethodName = Writable<(typeof canUsePluginMethods)['async']>;
|
|
@@ -46,7 +51,10 @@ class CodeBlock extends BaseService {
|
|
|
46
51
|
});
|
|
47
52
|
|
|
48
53
|
constructor() {
|
|
49
|
-
super(
|
|
54
|
+
super([
|
|
55
|
+
...canUsePluginMethods.async.map((methodName) => ({ name: methodName, isAsync: true })),
|
|
56
|
+
...canUsePluginMethods.sync.map((methodName) => ({ name: methodName, isAsync: false })),
|
|
57
|
+
]);
|
|
50
58
|
}
|
|
51
59
|
|
|
52
60
|
/**
|
|
@@ -88,20 +96,32 @@ class CodeBlock extends BaseService {
|
|
|
88
96
|
* @returns {void}
|
|
89
97
|
*/
|
|
90
98
|
public async setCodeDslById(id: Id, codeConfig: Partial<CodeBlockContent>): Promise<void> {
|
|
99
|
+
this.setCodeDslByIdSync(id, codeConfig, true);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 为了兼容历史原因
|
|
104
|
+
* 设置代码块ID和代码内容到源dsl
|
|
105
|
+
* @param {Id} id 代码块id
|
|
106
|
+
* @param {CodeBlockContent} codeConfig 代码块内容配置信息
|
|
107
|
+
* @param {boolean} force 是否强制写入,默认true
|
|
108
|
+
* @returns {void}
|
|
109
|
+
*/
|
|
110
|
+
public setCodeDslByIdSync(id: Id, codeConfig: Partial<CodeBlockContent>, force = true): void {
|
|
91
111
|
const codeDsl = this.getCodeDsl();
|
|
92
112
|
|
|
93
113
|
if (!codeDsl) {
|
|
94
114
|
throw new Error('dsl中没有codeBlocks');
|
|
95
115
|
}
|
|
116
|
+
if (codeDsl[id] && !force) return;
|
|
96
117
|
|
|
97
|
-
const codeConfigProcessed = codeConfig;
|
|
98
|
-
if (
|
|
118
|
+
const codeConfigProcessed = cloneDeep(codeConfig);
|
|
119
|
+
if (codeConfigProcessed.content) {
|
|
99
120
|
// 在保存的时候转换代码内容
|
|
100
121
|
const parseDSL = getConfig('parseDSL');
|
|
101
|
-
if (typeof
|
|
102
|
-
|
|
122
|
+
if (typeof codeConfigProcessed.content === 'string') {
|
|
123
|
+
codeConfigProcessed.content = parseDSL<(...args: any[]) => any>(codeConfigProcessed.content);
|
|
103
124
|
}
|
|
104
|
-
codeConfigProcessed.content = codeConfig.content;
|
|
105
125
|
}
|
|
106
126
|
|
|
107
127
|
const existContent = codeDsl[id] || {};
|
|
@@ -233,6 +253,54 @@ class CodeBlock extends BaseService {
|
|
|
233
253
|
return await this.getUniqueId();
|
|
234
254
|
}
|
|
235
255
|
|
|
256
|
+
/**
|
|
257
|
+
* 复制时会带上组件关联的代码块
|
|
258
|
+
* @param config 组件节点配置
|
|
259
|
+
* @returns
|
|
260
|
+
*/
|
|
261
|
+
public copyWithRelated(config: MNode | MNode[]): void {
|
|
262
|
+
const copyNodes: MNode[] = Array.isArray(config) ? config : [config];
|
|
263
|
+
// 关联的代码块也一并复制
|
|
264
|
+
depService.clearByType(DepTargetType.RELATED_CODE_WHEN_COPY);
|
|
265
|
+
depService.collect(copyNodes, true, DepTargetType.RELATED_CODE_WHEN_COPY);
|
|
266
|
+
const customTarget = depService.getTarget(
|
|
267
|
+
DepTargetType.RELATED_CODE_WHEN_COPY,
|
|
268
|
+
DepTargetType.RELATED_CODE_WHEN_COPY,
|
|
269
|
+
);
|
|
270
|
+
const copyData: CodeBlockDSL = {};
|
|
271
|
+
if (customTarget) {
|
|
272
|
+
Object.keys(customTarget.deps).forEach((nodeId: Id) => {
|
|
273
|
+
const node = editorService.getNodeById(nodeId);
|
|
274
|
+
if (!node) return;
|
|
275
|
+
customTarget!.deps[nodeId].keys.forEach((key) => {
|
|
276
|
+
const relateCodeId = get(node, key);
|
|
277
|
+
const isExist = Object.keys(copyData).find((codeId: Id) => codeId === relateCodeId);
|
|
278
|
+
if (!isExist) {
|
|
279
|
+
const relateCode = this.getCodeContentById(relateCodeId);
|
|
280
|
+
if (relateCode) {
|
|
281
|
+
copyData[relateCodeId] = relateCode;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
storageService.setItem(COPY_CODE_STORAGE_KEY, copyData, {
|
|
288
|
+
protocol: Protocol.OBJECT,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* 粘贴代码块
|
|
294
|
+
* @returns
|
|
295
|
+
*/
|
|
296
|
+
public paste() {
|
|
297
|
+
const codeDsl: CodeBlockDSL = storageService.getItem(COPY_CODE_STORAGE_KEY);
|
|
298
|
+
Object.keys(codeDsl).forEach((codeId: Id) => {
|
|
299
|
+
// 不覆盖同样id的代码块
|
|
300
|
+
this.setCodeDslByIdSync(codeId, codeDsl[codeId], false);
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
236
304
|
public resetState() {
|
|
237
305
|
this.state.codeDsl = null;
|
|
238
306
|
this.state.editable = true;
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { reactive } from 'vue';
|
|
2
|
-
import { cloneDeep } from 'lodash-es';
|
|
2
|
+
import { cloneDeep, get } from 'lodash-es';
|
|
3
3
|
import { Writable } from 'type-fest';
|
|
4
4
|
|
|
5
5
|
import type { EventOption } from '@tmagic/core';
|
|
6
|
+
import { DepTargetType } from '@tmagic/dep';
|
|
6
7
|
import type { FormConfig } from '@tmagic/form';
|
|
7
|
-
import type { DataSourceSchema } from '@tmagic/schema';
|
|
8
|
+
import type { DataSourceSchema, Id, MNode } from '@tmagic/schema';
|
|
8
9
|
import { guid, toLine } from '@tmagic/utils';
|
|
9
10
|
|
|
11
|
+
import depService from '@editor/services/dep';
|
|
12
|
+
import editorService from '@editor/services/editor';
|
|
13
|
+
import storageService, { Protocol } from '@editor/services/storage';
|
|
10
14
|
import type { DatasourceTypeOption, SyncHookPlugin } from '@editor/type';
|
|
11
15
|
import { getFormConfig, getFormValue } from '@editor/utils/data-source';
|
|
16
|
+
import { COPY_DS_STORAGE_KEY } from '@editor/utils/editor';
|
|
12
17
|
|
|
13
18
|
import BaseService from './BaseService';
|
|
14
19
|
|
|
@@ -102,7 +107,7 @@ class DataSource extends BaseService {
|
|
|
102
107
|
public add(config: DataSourceSchema) {
|
|
103
108
|
const newConfig = {
|
|
104
109
|
...config,
|
|
105
|
-
id: this.createId(),
|
|
110
|
+
id: config.id && !this.getDataSourceById(config.id) ? config.id : this.createId(),
|
|
106
111
|
};
|
|
107
112
|
|
|
108
113
|
this.get('dataSources').push(newConfig);
|
|
@@ -116,7 +121,6 @@ class DataSource extends BaseService {
|
|
|
116
121
|
const dataSources = this.get('dataSources');
|
|
117
122
|
|
|
118
123
|
const index = dataSources.findIndex((ds) => ds.id === config.id);
|
|
119
|
-
|
|
120
124
|
dataSources[index] = cloneDeep(config);
|
|
121
125
|
|
|
122
126
|
this.emit('update', config);
|
|
@@ -153,6 +157,51 @@ class DataSource extends BaseService {
|
|
|
153
157
|
public usePlugin(options: SyncHookPlugin<SyncMethodName, DataSource>): void {
|
|
154
158
|
super.usePlugin(options);
|
|
155
159
|
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* 复制时会带上组件关联的数据源
|
|
163
|
+
* @param config 组件节点配置
|
|
164
|
+
* @returns
|
|
165
|
+
*/
|
|
166
|
+
public copyWithRelated(config: MNode | MNode[]): void {
|
|
167
|
+
const copyNodes: MNode[] = Array.isArray(config) ? config : [config];
|
|
168
|
+
depService.clearByType(DepTargetType.RELATED_DS_WHEN_COPY);
|
|
169
|
+
depService.collect(copyNodes, true, DepTargetType.RELATED_DS_WHEN_COPY);
|
|
170
|
+
const customTarget = depService.getTarget(DepTargetType.RELATED_DS_WHEN_COPY, DepTargetType.RELATED_DS_WHEN_COPY);
|
|
171
|
+
const copyData: DataSourceSchema[] = [];
|
|
172
|
+
if (customTarget) {
|
|
173
|
+
Object.keys(customTarget.deps).forEach((nodeId: Id) => {
|
|
174
|
+
const node = editorService.getNodeById(nodeId);
|
|
175
|
+
if (!node) return;
|
|
176
|
+
customTarget!.deps[nodeId].keys.forEach((key) => {
|
|
177
|
+
const [relateDsId] = get(node, key);
|
|
178
|
+
const isExist = copyData.find((dsItem) => dsItem.id === relateDsId);
|
|
179
|
+
if (!isExist) {
|
|
180
|
+
const relateDs = this.getDataSourceById(relateDsId);
|
|
181
|
+
if (relateDs) {
|
|
182
|
+
copyData.push(relateDs);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
storageService.setItem(COPY_DS_STORAGE_KEY, copyData, {
|
|
189
|
+
protocol: Protocol.OBJECT,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* 粘贴数据源
|
|
194
|
+
* @returns
|
|
195
|
+
*/
|
|
196
|
+
public paste() {
|
|
197
|
+
const dataSource: DataSourceSchema[] = storageService.getItem(COPY_DS_STORAGE_KEY);
|
|
198
|
+
dataSource.forEach((item: DataSourceSchema) => {
|
|
199
|
+
// 不覆盖同样id的数据源
|
|
200
|
+
if (!this.getDataSourceById(item.id)) {
|
|
201
|
+
this.add(item);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
}
|
|
156
205
|
}
|
|
157
206
|
|
|
158
207
|
export type DataSourceService = DataSource;
|
package/src/services/editor.ts
CHANGED
|
@@ -23,7 +23,7 @@ import { Writable } from 'type-fest';
|
|
|
23
23
|
import { DepTargetType } from '@tmagic/dep';
|
|
24
24
|
import type { Id, MApp, MComponent, MContainer, MNode, MPage, MPageFragment } from '@tmagic/schema';
|
|
25
25
|
import { NodeType } from '@tmagic/schema';
|
|
26
|
-
import { getNodePath, isNumber, isPage, isPageFragment, isPop } from '@tmagic/utils';
|
|
26
|
+
import { calcValueByFontsize, getNodePath, isNumber, isPage, isPageFragment, isPop } from '@tmagic/utils';
|
|
27
27
|
|
|
28
28
|
import BaseService from '@editor/services//BaseService';
|
|
29
29
|
import propsService from '@editor/services//props';
|
|
@@ -741,7 +741,7 @@ class Editor extends BaseService {
|
|
|
741
741
|
const el = doc.getElementById(`${node.id}`);
|
|
742
742
|
const parentEl = layout === Layout.FIXED ? doc.body : el?.offsetParent;
|
|
743
743
|
if (parentEl && el) {
|
|
744
|
-
node.style.left = (parentEl.clientWidth - el.clientWidth) / 2;
|
|
744
|
+
node.style.left = calcValueByFontsize(doc, (parentEl.clientWidth - el.clientWidth) / 2);
|
|
745
745
|
node.style.right = '';
|
|
746
746
|
}
|
|
747
747
|
} else if (parent.style && isNumber(parent.style?.width) && isNumber(node.style?.width)) {
|
package/src/services/props.ts
CHANGED
|
@@ -23,7 +23,7 @@ import { Writable } from 'type-fest';
|
|
|
23
23
|
import { DepTargetType } from '@tmagic/dep';
|
|
24
24
|
import type { FormConfig } from '@tmagic/form';
|
|
25
25
|
import type { Id, MComponent, MNode } from '@tmagic/schema';
|
|
26
|
-
import { getValueByKeyPath, guid, setValueByKeyPath, toLine } from '@tmagic/utils';
|
|
26
|
+
import { getNodePath, getValueByKeyPath, guid, setValueByKeyPath, toLine } from '@tmagic/utils';
|
|
27
27
|
|
|
28
28
|
import depService from '@editor/services/dep';
|
|
29
29
|
import editorService from '@editor/services/editor';
|
|
@@ -199,13 +199,14 @@ class Props extends BaseService {
|
|
|
199
199
|
public replaceRelateId(originConfigs: MNode[], targetConfigs: MNode[]) {
|
|
200
200
|
const relateIdMap = this.getRelateIdMap();
|
|
201
201
|
if (Object.keys(relateIdMap).length === 0) return;
|
|
202
|
-
|
|
202
|
+
depService.clearByType(DepTargetType.RELATED_COMP_WHEN_COPY);
|
|
203
|
+
depService.collect(originConfigs, true, DepTargetType.RELATED_COMP_WHEN_COPY);
|
|
203
204
|
const target = depService.getTarget(DepTargetType.RELATED_COMP_WHEN_COPY, DepTargetType.RELATED_COMP_WHEN_COPY);
|
|
204
205
|
if (!target) return;
|
|
205
|
-
|
|
206
206
|
originConfigs.forEach((config: MNode) => {
|
|
207
207
|
const newId = relateIdMap[config.id];
|
|
208
|
-
const
|
|
208
|
+
const path = getNodePath(newId, targetConfigs);
|
|
209
|
+
const targetConfig = path[path.length - 1];
|
|
209
210
|
|
|
210
211
|
if (!targetConfig) return;
|
|
211
212
|
|
|
@@ -213,9 +214,13 @@ class Props extends BaseService {
|
|
|
213
214
|
const relateOriginId = getValueByKeyPath(fullKey, config);
|
|
214
215
|
const relateTargetId = relateIdMap[relateOriginId];
|
|
215
216
|
if (!relateTargetId) return;
|
|
216
|
-
|
|
217
217
|
setValueByKeyPath(fullKey, relateTargetId, targetConfig);
|
|
218
218
|
});
|
|
219
|
+
|
|
220
|
+
// 递归items
|
|
221
|
+
if (config.items && Array.isArray(config.items)) {
|
|
222
|
+
this.replaceRelateId(config.items, targetConfigs);
|
|
223
|
+
}
|
|
219
224
|
});
|
|
220
225
|
}
|
|
221
226
|
|
package/src/type.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { computed, markRaw, Ref } from 'vue';
|
|
|
2
2
|
import { CopyDocument, Delete, DocumentCopy } from '@element-plus/icons-vue';
|
|
3
3
|
|
|
4
4
|
import { Id, MContainer, NodeType } from '@tmagic/schema';
|
|
5
|
-
import { isPage, isPageFragment } from '@tmagic/utils';
|
|
5
|
+
import { calcValueByFontsize, isPage, isPageFragment } from '@tmagic/utils';
|
|
6
6
|
|
|
7
7
|
import ContentMenu from '@editor/components/ContentMenu.vue';
|
|
8
8
|
import type { MenuButton, Services } from '@editor/type';
|
|
@@ -46,8 +46,12 @@ export const usePasteMenu = (menu?: Ref<InstanceType<typeof ContentMenu> | undef
|
|
|
46
46
|
const stage = services?.editorService?.get('stage');
|
|
47
47
|
const rect = menu.value.$el.getBoundingClientRect();
|
|
48
48
|
const parentRect = stage?.container?.getBoundingClientRect();
|
|
49
|
-
const initialLeft =
|
|
50
|
-
|
|
49
|
+
const initialLeft =
|
|
50
|
+
calcValueByFontsize(stage?.renderer.getDocument(), (rect.left || 0) - (parentRect?.left || 0)) /
|
|
51
|
+
services.uiService.get('zoom');
|
|
52
|
+
const initialTop =
|
|
53
|
+
calcValueByFontsize(stage?.renderer.getDocument(), (rect.top || 0) - (parentRect?.top || 0)) /
|
|
54
|
+
services.uiService.get('zoom');
|
|
51
55
|
services?.editorService?.paste({ left: initialLeft, top: initialTop });
|
|
52
56
|
} else {
|
|
53
57
|
services?.editorService?.paste();
|