mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-09-19 21:06:06 +08:00
feat(editor): 支持拖拽添加组件
This commit is contained in:
parent
76b8d2314a
commit
5da8601f36
@ -12,7 +12,14 @@
|
|||||||
<template v-for="(group, index) in list">
|
<template v-for="(group, index) in list">
|
||||||
<el-collapse-item v-if="group.items && group.items.length" :key="index" :name="index">
|
<el-collapse-item v-if="group.items && group.items.length" :key="index" :name="index">
|
||||||
<template #title><i class="el-icon-s-grid"></i>{{ group.title }}</template>
|
<template #title><i class="el-icon-s-grid"></i>{{ group.title }}</template>
|
||||||
<div class="component-item" v-for="item in group.items" :key="item.type" @click="appendComponent(item)">
|
<div
|
||||||
|
class="component-item"
|
||||||
|
v-for="item in group.items"
|
||||||
|
draggable="true"
|
||||||
|
:key="item.type"
|
||||||
|
@click="appendComponent(item)"
|
||||||
|
@dragstart="dragstartHandler(item, $event)"
|
||||||
|
>
|
||||||
<m-icon :icon="item.icon"></m-icon>
|
<m-icon :icon="item.icon"></m-icon>
|
||||||
|
|
||||||
<el-tooltip effect="dark" placement="bottom" :content="item.text">
|
<el-tooltip effect="dark" placement="bottom" :content="item.text">
|
||||||
@ -27,6 +34,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, inject, ref } from 'vue';
|
import { computed, defineComponent, inject, ref } from 'vue';
|
||||||
|
import serialize from 'serialize-javascript';
|
||||||
|
|
||||||
import MIcon from '@editor/components/Icon.vue';
|
import MIcon from '@editor/components/Icon.vue';
|
||||||
import type { ComponentGroup, ComponentItem, Services } from '@editor/type';
|
import type { ComponentGroup, ComponentItem, Services } from '@editor/type';
|
||||||
@ -63,6 +71,20 @@ export default defineComponent({
|
|||||||
...data,
|
...data,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
dragstartHandler({ text, type, data = {} }: ComponentItem, e: DragEvent) {
|
||||||
|
if (e.dataTransfer) {
|
||||||
|
e.dataTransfer.effectAllowed = 'move';
|
||||||
|
e.dataTransfer.setData(
|
||||||
|
'data',
|
||||||
|
serialize({
|
||||||
|
name: text,
|
||||||
|
type,
|
||||||
|
...data,
|
||||||
|
}).replace(/"(\w+)":\s/g, '$1: '),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -9,8 +9,10 @@
|
|||||||
<div
|
<div
|
||||||
class="m-editor-stage-container"
|
class="m-editor-stage-container"
|
||||||
ref="stageContainer"
|
ref="stageContainer"
|
||||||
@contextmenu="contextmenuHandler"
|
|
||||||
:style="`transform: scale(${zoom})`"
|
:style="`transform: scale(${zoom})`"
|
||||||
|
@contextmenu="contextmenuHandler"
|
||||||
|
@drop="dropHandler"
|
||||||
|
@dragover="dragoverHandler"
|
||||||
></div>
|
></div>
|
||||||
<teleport to="body">
|
<teleport to="body">
|
||||||
<viewer-menu ref="menu"></viewer-menu>
|
<viewer-menu ref="menu"></viewer-menu>
|
||||||
@ -39,7 +41,7 @@ import type { MoveableOptions, Runtime, SortEventData, UpdateEventData } from '@
|
|||||||
import StageCore from '@tmagic/stage';
|
import StageCore from '@tmagic/stage';
|
||||||
|
|
||||||
import ScrollViewer from '@editor/components/ScrollViewer.vue';
|
import ScrollViewer from '@editor/components/ScrollViewer.vue';
|
||||||
import type { Services, StageRect } from '@editor/type';
|
import { Layout, Services, StageRect } from '@editor/type';
|
||||||
|
|
||||||
import ViewerMenu from './ViewerMenu.vue';
|
import ViewerMenu from './ViewerMenu.vue';
|
||||||
|
|
||||||
@ -188,6 +190,34 @@ export default defineComponent({
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
menu.value?.show(e);
|
menu.value?.show(e);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
dragoverHandler(e: DragEvent) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.dataTransfer) {
|
||||||
|
e.dataTransfer.dropEffect = 'move';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async dropHandler(e: DragEvent) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.dataTransfer && page.value && stageContainer.value && stage) {
|
||||||
|
// eslint-disable-next-line no-eval
|
||||||
|
const config = eval(`(${e.dataTransfer.getData('data')})`);
|
||||||
|
const layout = await services?.editorService.getLayout(page.value);
|
||||||
|
|
||||||
|
const containerRect = stageContainer.value.getBoundingClientRect();
|
||||||
|
const { scrollTop, scrollLeft } = stage.mask;
|
||||||
|
if (layout === Layout.ABSOLUTE) {
|
||||||
|
config.style = {
|
||||||
|
position: 'absolute',
|
||||||
|
top: e.clientY - containerRect.top + scrollTop,
|
||||||
|
left: e.clientX - containerRect.left + scrollLeft,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
services?.editorService.add(config, page.value);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { reactive } from 'vue';
|
import { reactive } from 'vue';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep, mergeWith } from 'lodash-es';
|
||||||
|
|
||||||
import type { FormConfig } from '@tmagic/form';
|
import type { FormConfig } from '@tmagic/form';
|
||||||
import type { MComponent, MNode } from '@tmagic/schema';
|
import type { MComponent, MNode } from '@tmagic/schema';
|
||||||
@ -100,8 +100,7 @@ class Props extends BaseService {
|
|||||||
|
|
||||||
return cloneDeep({
|
return cloneDeep({
|
||||||
...getDefaultPropsValue(type),
|
...getDefaultPropsValue(type),
|
||||||
...defaultValue,
|
...mergeWith(this.state.propsValueMap[type] || {}, defaultValue),
|
||||||
...(this.state.propsValueMap[type] || {}),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,7 @@
|
|||||||
color: $--font-color;
|
color: $--font-color;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 42px;
|
width: 42px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
i {
|
i {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
@ -144,7 +144,7 @@ const setTop2Middle = (node: MNode, parentNode: MNode, stage: StageCore) => {
|
|||||||
const style = node.style || {};
|
const style = node.style || {};
|
||||||
const height = style.height || 0;
|
const height = style.height || 0;
|
||||||
|
|
||||||
if (!stage || style.top || !parentNode.style || !isNumber(height)) return style;
|
if (!stage || typeof style.top !== 'undefined' || !parentNode.style || !isNumber(height)) return style;
|
||||||
|
|
||||||
const { height: parentHeight } = parentNode.style;
|
const { height: parentHeight } = parentNode.style;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user