mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2026-07-13 22:52:31 +08:00
fix(editor): 修复 FloatingBox 拖动时鼠标进入 iframe 区域事件丢失
- 拖拽/缩放开始时插入全屏透明遮罩盖住 iframe,结束时移除,避免事件被 iframe 吞掉 - 修正拖拽标题时 z-index 竞态导致遮罩被浮窗盖住的问题 - 将 body 内边距从 scss 抽取为 bodyStyle 透传,复用方按需自定义
This commit is contained in:
parent
c57ef89715
commit
284be0d276
@ -4,6 +4,7 @@
|
|||||||
v-model:visible="boxVisible"
|
v-model:visible="boxVisible"
|
||||||
v-model:width="width"
|
v-model:width="width"
|
||||||
v-model:height="codeBlockEditorHeight"
|
v-model:height="codeBlockEditorHeight"
|
||||||
|
:body-style="{ padding: '0 16px' }"
|
||||||
:title="content.name ? `${disabled ? '查看' : '编辑'}${content.name}` : '新增代码'"
|
:title="content.name ? `${disabled ? '查看' : '编辑'}${content.name}` : '新增代码'"
|
||||||
:position="boxPosition"
|
:position="boxPosition"
|
||||||
:before-close="beforeClose"
|
:before-close="beforeClose"
|
||||||
|
|||||||
@ -1,6 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<Teleport to="body" v-if="visible">
|
<Teleport to="body" v-if="visible">
|
||||||
<div ref="target" class="m-editor-float-box" :style="{ ...style, zIndex: curZIndex }" @mousedown="nextZIndex">
|
<div
|
||||||
|
ref="target"
|
||||||
|
class="m-editor-float-box"
|
||||||
|
v-bind="$attrs"
|
||||||
|
:style="{ ...style, zIndex: curZIndex }"
|
||||||
|
@mousedown="nextZIndex"
|
||||||
|
>
|
||||||
<div ref="title" class="m-editor-float-box-title">
|
<div ref="title" class="m-editor-float-box-title">
|
||||||
<slot name="title">
|
<slot name="title">
|
||||||
<span>{{ title }}</span>
|
<span>{{ title }}</span>
|
||||||
@ -9,7 +15,7 @@
|
|||||||
<TMagicButton link size="small" @click="closeHandler"><MIcon :icon="Close"></MIcon></TMagicButton>
|
<TMagicButton link size="small" @click="closeHandler"><MIcon :icon="Close"></MIcon></TMagicButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="m-editor-float-box-body" :style="{ height: `${bodyHeight}px` }">
|
<div class="m-editor-float-box-body" :style="{ height: `${bodyHeight}px`, ...bodyStyle }">
|
||||||
<slot name="body"></slot>
|
<slot name="body"></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -17,7 +23,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, nextTick, onBeforeUnmount, provide, ref, useTemplateRef, watch } from 'vue';
|
import { computed, type CSSProperties, nextTick, onBeforeUnmount, provide, ref, useTemplateRef, watch } from 'vue';
|
||||||
import { Close } from '@element-plus/icons-vue';
|
import { Close } from '@element-plus/icons-vue';
|
||||||
import VanillaMoveable from 'moveable';
|
import VanillaMoveable from 'moveable';
|
||||||
|
|
||||||
@ -39,6 +45,7 @@ const props = withDefaults(
|
|||||||
defineProps<{
|
defineProps<{
|
||||||
position?: Position;
|
position?: Position;
|
||||||
title?: string;
|
title?: string;
|
||||||
|
bodyStyle?: CSSProperties;
|
||||||
beforeClose?: (_done: (_cancel?: boolean) => void) => void;
|
beforeClose?: (_done: (_cancel?: boolean) => void) => void;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
@ -84,6 +91,31 @@ const style = computed(() => {
|
|||||||
|
|
||||||
let moveable: VanillaMoveable | null = null;
|
let moveable: VanillaMoveable | null = null;
|
||||||
|
|
||||||
|
// 拖拽/缩放时用于覆盖 iframe 的遮罩,防止鼠标进入 iframe 区域后事件被 iframe 吞掉导致拖拽丢失
|
||||||
|
let dragMask: HTMLDivElement | null = null;
|
||||||
|
|
||||||
|
const showDragMask = () => {
|
||||||
|
if (!dragMask) {
|
||||||
|
dragMask = globalThis.document.createElement('div');
|
||||||
|
dragMask.className = 'm-editor-float-box-drag-mask';
|
||||||
|
}
|
||||||
|
globalThis.document.body.appendChild(dragMask);
|
||||||
|
|
||||||
|
// 拖拽标题时,root 上的 @mousedown="nextZIndex" 会在 dragStart 之后把浮窗 z-index 抬高,
|
||||||
|
// 若此时才读取会拿到旧值导致遮罩被浮窗(及其内部 iframe)盖住,故用 nextTick 在 z-index 稳定后再设置到浮窗之上
|
||||||
|
const setMaskZIndex = () => {
|
||||||
|
if (dragMask) {
|
||||||
|
dragMask.style.zIndex = `${curZIndex.value + 1}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
setMaskZIndex();
|
||||||
|
nextTick(setMaskZIndex);
|
||||||
|
};
|
||||||
|
|
||||||
|
const hideDragMask = () => {
|
||||||
|
dragMask?.parentNode?.removeChild(dragMask);
|
||||||
|
};
|
||||||
|
|
||||||
const initMoveable = () => {
|
const initMoveable = () => {
|
||||||
moveable = new VanillaMoveable(globalThis.document.body, {
|
moveable = new VanillaMoveable(globalThis.document.body, {
|
||||||
className: 'm-editor-floating-box-moveable',
|
className: 'm-editor-floating-box-moveable',
|
||||||
@ -101,6 +133,11 @@ const initMoveable = () => {
|
|||||||
bounds: { left: 0, top: 0, right: 0, bottom: 0, position: 'css' },
|
bounds: { left: 0, top: 0, right: 0, bottom: 0, position: 'css' },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
moveable.on('dragStart', showDragMask);
|
||||||
|
moveable.on('resizeStart', showDragMask);
|
||||||
|
moveable.on('dragEnd', hideDragMask);
|
||||||
|
moveable.on('resizeEnd', hideDragMask);
|
||||||
|
|
||||||
moveable.on('drag', (e) => {
|
moveable.on('drag', (e) => {
|
||||||
e.target.style.transform = e.transform;
|
e.target.style.transform = e.transform;
|
||||||
});
|
});
|
||||||
@ -115,6 +152,7 @@ const initMoveable = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const destroyMoveable = () => {
|
const destroyMoveable = () => {
|
||||||
|
hideDragMask();
|
||||||
moveable?.destroy();
|
moveable?.destroy();
|
||||||
moveable = null;
|
moveable = null;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FloatingBox
|
<FloatingBox
|
||||||
|
:body-style="{ padding: '0 16px' }"
|
||||||
v-model:visible="addDialogVisible"
|
v-model:visible="addDialogVisible"
|
||||||
v-model:width="width"
|
v-model:width="width"
|
||||||
v-model:height="editorHeight"
|
v-model:height="editorHeight"
|
||||||
@ -28,6 +29,7 @@
|
|||||||
</FloatingBox>
|
</FloatingBox>
|
||||||
|
|
||||||
<FloatingBox
|
<FloatingBox
|
||||||
|
:body-style="{ padding: '0 16px' }"
|
||||||
v-model:visible="addFromJsonDialogVisible"
|
v-model:visible="addFromJsonDialogVisible"
|
||||||
v-model:width="width"
|
v-model:width="width"
|
||||||
v-model:height="editorHeight"
|
v-model:height="editorHeight"
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FloatingBox
|
<FloatingBox
|
||||||
|
:body-style="{ padding: '0 16px' }"
|
||||||
v-model:visible="addDialogVisible"
|
v-model:visible="addDialogVisible"
|
||||||
v-model:width="width"
|
v-model:width="width"
|
||||||
v-model:height="editorHeight"
|
v-model:height="editorHeight"
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<FloatingBox
|
<FloatingBox
|
||||||
|
:body-style="{ padding: '0 16px' }"
|
||||||
v-model:visible="boxVisible"
|
v-model:visible="boxVisible"
|
||||||
v-model:width="width"
|
v-model:width="width"
|
||||||
v-model:height="editorHeight"
|
v-model:height="editorHeight"
|
||||||
|
|||||||
@ -25,10 +25,18 @@
|
|||||||
.m-editor-float-box-body {
|
.m-editor-float-box-body {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 0 16px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.m-editor-floating-box-moveable {
|
.m-editor-floating-box-moveable {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.m-editor-float-box-drag-mask {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user