设置停靠指示器显示在最顶层:将DockIndicator组件的z-index值从999提高到9999并在父组件中添加相同设置
This commit is contained in:
399
AutoRobot/Windows/Robot/Web/src/DockLayout/DockIndicator.vue
Normal file
399
AutoRobot/Windows/Robot/Web/src/DockLayout/DockIndicator.vue
Normal file
@@ -0,0 +1,399 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="visible" class="dock-indicator" :style="indicatorStyle">
|
||||||
|
<!-- 上侧停靠指示器 -->
|
||||||
|
<div v-if="activeZones.top" class="dock-zone dock-zone-top" @dragenter="onZoneEnter('top')" @dragleave="onZoneLeave('top')" @dragover.prevent>
|
||||||
|
<div class="dock-zone-indicator">
|
||||||
|
<svg width="40" height="20" viewBox="0 0 40 20" aria-hidden="true">
|
||||||
|
<rect x="0" y="0" width="40" height="20" fill="rgba(79, 114, 179, 0.3)" />
|
||||||
|
<rect x="2" y="2" width="36" height="16" fill="rgba(79, 114, 179, 0.5)" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 左侧停靠指示器 -->
|
||||||
|
<div v-if="activeZones.left" class="dock-zone dock-zone-left" @dragenter="onZoneEnter('left')" @dragleave="onZoneLeave('left')" @dragover.prevent>
|
||||||
|
<div class="dock-zone-indicator">
|
||||||
|
<svg width="20" height="40" viewBox="0 0 20 40" aria-hidden="true">
|
||||||
|
<rect x="0" y="0" width="20" height="40" fill="rgba(79, 114, 179, 0.3)" />
|
||||||
|
<rect x="2" y="2" width="16" height="36" fill="rgba(79, 114, 179, 0.5)" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧停靠指示器 -->
|
||||||
|
<div v-if="activeZones.right" class="dock-zone dock-zone-right" @dragenter="onZoneEnter('right')" @dragleave="onZoneLeave('right')" @dragover.prevent>
|
||||||
|
<div class="dock-zone-indicator">
|
||||||
|
<svg width="20" height="40" viewBox="0 0 20 40" aria-hidden="true">
|
||||||
|
<rect x="0" y="0" width="20" height="40" fill="rgba(79, 114, 179, 0.3)" />
|
||||||
|
<rect x="2" y="2" width="16" height="36" fill="rgba(79, 114, 179, 0.5)" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 下侧停靠指示器 -->
|
||||||
|
<div v-if="activeZones.bottom" class="dock-zone dock-zone-bottom" @dragenter="onZoneEnter('bottom')" @dragleave="onZoneLeave('bottom')" @dragover.prevent>
|
||||||
|
<div class="dock-zone-indicator">
|
||||||
|
<svg width="40" height="20" viewBox="0 0 40 20" aria-hidden="true">
|
||||||
|
<rect x="0" y="0" width="40" height="20" fill="rgba(79, 114, 179, 0.3)" />
|
||||||
|
<rect x="2" y="2" width="36" height="16" fill="rgba(79, 114, 179, 0.5)" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 中央停靠指示器 -->
|
||||||
|
<div v-if="activeZones.center" class="dock-zone dock-zone-center" @dragenter="onZoneEnter('center')" @dragleave="onZoneLeave('center')" @dragover.prevent>
|
||||||
|
<div class="dock-zone-indicator">
|
||||||
|
<svg width="60" height="60" viewBox="0 0 60 60" aria-hidden="true">
|
||||||
|
<rect x="0" y="0" width="60" height="60" fill="rgba(79, 114, 179, 0.3)" rx="5" />
|
||||||
|
<rect x="5" y="5" width="50" height="50" fill="rgba(79, 114, 179, 0.5)" rx="3" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 1. 定义可复用组件(symbol):封装所有渐变和路径(只写一次) -->
|
||||||
|
<svg width="0" height="0" viewBox="0 0 40 40" aria-hidden="true">
|
||||||
|
<defs>
|
||||||
|
<!-- 渐变定义(共用,只写一次) -->
|
||||||
|
<linearGradient
|
||||||
|
id="lightGradient"
|
||||||
|
x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
|
<stop offset="0%" stop-color="#DCE3F5" />
|
||||||
|
<stop offset="100%" stop-color="#B7BED1" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
id="Area"
|
||||||
|
x1="50%" y1="0%" x2="50%" y2="100%">
|
||||||
|
<stop offset="0%" stop-color="#F0E2BC" />
|
||||||
|
<stop offset="100%" stop-color="#B09556" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- 封装所有图形为 symbol(id 用于后续调用) -->
|
||||||
|
<symbol id="shared-border" viewBox="0 0 40 40">
|
||||||
|
<path
|
||||||
|
fill="#61697E"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M0 0 L40 0 L40 40 L0 40 Z M1 1 L39 1 L39 39 L1 39 Z" />
|
||||||
|
<path
|
||||||
|
fill="#A1A9C4"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M1 1 L39 1 L39 39 L1 39 Z M4 5 L5 4 L35 4 L36 5 L36 35 L35 36 L5 36 L4 35 Z" />
|
||||||
|
<path
|
||||||
|
fill="#7E869C"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M4 5 L5 4 L35 4 L36 5 L36 35 L35 36 L5 36 L4 35 Z M6 7 L7 6 L33 6 L34 7 L34 33 L33 34 L7 34 L6 33 Z" />
|
||||||
|
<path
|
||||||
|
fill="url(#lightGradient)"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M6 7 L7 6 L33 6 L34 7 L34 33 L33 34 L7 34 L6 33 Z" />
|
||||||
|
</symbol>
|
||||||
|
<symbol id="shared-icon" viewBox="0 0 40 40">
|
||||||
|
<use xlink:href="#shared-border" />
|
||||||
|
<path
|
||||||
|
fill="#4C5E83"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M16 30 L20 26 L23 30 Z" />
|
||||||
|
</symbol>
|
||||||
|
</svg>
|
||||||
|
<!-- 2. 第一个 SVG:不旋转,直接调用共用组件 -->
|
||||||
|
<svg width="41" height="41" viewBox="0 0 40 40" aria-hidden="true">
|
||||||
|
<use xlink:href="#shared-icon" />
|
||||||
|
<path
|
||||||
|
fill="#4C5E83"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M8 8 L32 8 L32 20 L31 21 L9 21 L8 20 Z" />
|
||||||
|
<path
|
||||||
|
fill="url(#Area)"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M10 13 L30 13 L30 19 L10 19 Z" />
|
||||||
|
</svg>
|
||||||
|
<!-- 3. 第二个 SVG:旋转90度,调用同一共用组件 -->
|
||||||
|
<svg width="41" height="41" viewBox="0 0 40 40" aria-hidden="true">
|
||||||
|
<use xlink:href="#shared-icon" transform="rotate(90 20 20)" />
|
||||||
|
<path
|
||||||
|
fill="#4C5E83"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M19 8 L32 8 L32 31 L31 32 L20 32 L19 31 Z" />
|
||||||
|
<path
|
||||||
|
fill="url(#Area)"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M21 14 L30 14 L30 30 L21 30 Z" />
|
||||||
|
</svg>
|
||||||
|
<!-- 4. 第三个 SVG:旋转180度,调用同一共用组件 -->
|
||||||
|
<svg width="41" height="41" viewBox="0 0 40 40" aria-hidden="true">
|
||||||
|
<use xlink:href="#shared-icon" transform="rotate(180 20 20)" />
|
||||||
|
<path
|
||||||
|
fill="#4C5E83"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M8 19 L32 19 L32 31 L31 32 L9 32 L8 31 Z" />
|
||||||
|
<path
|
||||||
|
fill="url(#Area)"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M10 24 L30 24 L30 30 L10 30 Z" />
|
||||||
|
</svg>
|
||||||
|
<!-- 5. 第四个 SVG:旋转270度,调用同一共用组件 -->
|
||||||
|
<svg width="41" height="41" viewBox="0 0 40 40" aria-hidden="true">
|
||||||
|
<use xlink:href="#shared-icon" transform="rotate(270 20 20)" />
|
||||||
|
<path
|
||||||
|
fill="#4C5E83"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M8 8 L21 8 L21 31 L19 32 L9 32 L8 31 Z" />
|
||||||
|
<path
|
||||||
|
fill="url(#Area)"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M10 14 L19 14 L19 30 L10 30 Z" />
|
||||||
|
</svg>
|
||||||
|
<!-- 6. 第五个 SVG:正中的按钮,调用同一共用组件 -->
|
||||||
|
<svg width="41" height="41" viewBox="0 0 40 40" aria-hidden="true">
|
||||||
|
<use xlink:href="#shared-border" transform="rotate(270 20 20)" />
|
||||||
|
<path
|
||||||
|
fill="#4C5E83"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M8 8 L32 8 L32 31 L31 32 L9 32 L8 31 Z" />
|
||||||
|
<path
|
||||||
|
fill="url(#Area)"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M10 14 L30 14 L30 30 L10 30 Z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, watch } from 'vue'
|
||||||
|
|
||||||
|
// Props定义
|
||||||
|
const props = defineProps({
|
||||||
|
// 是否可见
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 目标区域的位置和大小信息
|
||||||
|
targetRect: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
width: 0,
|
||||||
|
height: 0
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 鼠标位置
|
||||||
|
mousePosition: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 停靠区域的激活阈值
|
||||||
|
threshold: {
|
||||||
|
type: Number,
|
||||||
|
default: 30
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 定义事件
|
||||||
|
const emit = defineEmits(['zone-enter', 'zone-leave', 'zone-active'])
|
||||||
|
|
||||||
|
// 状态管理
|
||||||
|
const activeZones = ref({
|
||||||
|
top: false,
|
||||||
|
left: false,
|
||||||
|
right: false,
|
||||||
|
bottom: false,
|
||||||
|
center: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const selectedZone = ref(null)
|
||||||
|
|
||||||
|
// 计算指示器的样式
|
||||||
|
const indicatorStyle = computed(() => {
|
||||||
|
return {
|
||||||
|
position: 'absolute',
|
||||||
|
left: `${props.targetRect.left}px`,
|
||||||
|
top: `${props.targetRect.top}px`,
|
||||||
|
width: `${props.targetRect.width}px`,
|
||||||
|
height: `${props.targetRect.height}px`,
|
||||||
|
pointerEvents: props.visible ? 'auto' : 'none',
|
||||||
|
zIndex: 9999
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听鼠标位置,更新活动区域
|
||||||
|
watch([() => props.mousePosition, () => props.targetRect, () => props.threshold], () => {
|
||||||
|
if (!props.visible) return
|
||||||
|
|
||||||
|
const { x, y } = props.mousePosition
|
||||||
|
const { left, top, width, height } = props.targetRect
|
||||||
|
|
||||||
|
// 重置所有区域
|
||||||
|
activeZones.value = {
|
||||||
|
top: false,
|
||||||
|
left: false,
|
||||||
|
right: false,
|
||||||
|
bottom: false,
|
||||||
|
center: false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查鼠标是否在目标区域内
|
||||||
|
if (x >= left && x <= left + width && y >= top && y <= top + height) {
|
||||||
|
// 检查各个停靠区域
|
||||||
|
if (y <= top + props.threshold) {
|
||||||
|
activeZones.value.top = true
|
||||||
|
} else if (y >= top + height - props.threshold) {
|
||||||
|
activeZones.value.bottom = true
|
||||||
|
} else if (x <= left + props.threshold) {
|
||||||
|
activeZones.value.left = true
|
||||||
|
} else if (x >= left + width - props.threshold) {
|
||||||
|
activeZones.value.right = true
|
||||||
|
} else {
|
||||||
|
// 如果不在边缘,检查是否在中心区域
|
||||||
|
const centerX = left + width / 2
|
||||||
|
const centerY = top + height / 2
|
||||||
|
const centerRadius = Math.min(width, height) / 4
|
||||||
|
|
||||||
|
if (Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2)) <= centerRadius) {
|
||||||
|
activeZones.value.center = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, { immediate: true, deep: true })
|
||||||
|
|
||||||
|
// 当进入某个停靠区域
|
||||||
|
const onZoneEnter = (zone) => {
|
||||||
|
selectedZone.value = zone
|
||||||
|
emit('zone-enter', zone)
|
||||||
|
emit('zone-active', zone)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当离开某个停靠区域
|
||||||
|
const onZoneLeave = (zone) => {
|
||||||
|
if (selectedZone.value === zone) {
|
||||||
|
selectedZone.value = null
|
||||||
|
emit('zone-leave', zone)
|
||||||
|
emit('zone-active', null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暴露方法给父组件
|
||||||
|
defineExpose({
|
||||||
|
activeZones,
|
||||||
|
selectedZone,
|
||||||
|
reset: () => {
|
||||||
|
selectedZone.value = null
|
||||||
|
activeZones.value = {
|
||||||
|
top: false,
|
||||||
|
left: false,
|
||||||
|
right: false,
|
||||||
|
bottom: false,
|
||||||
|
center: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.dock-indicator {
|
||||||
|
position: absolute;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dock-zone {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dock-zone:hover {
|
||||||
|
background-color: rgba(79, 114, 179, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 上侧停靠区域 */
|
||||||
|
.dock-zone-top {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 左侧停靠区域 */
|
||||||
|
.dock-zone-left {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 30px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 右侧停靠区域 */
|
||||||
|
.dock-zone-right {
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 30px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 下侧停靠区域 */
|
||||||
|
.dock-zone-bottom {
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 中央停靠区域 */
|
||||||
|
.dock-zone-center {
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dock-zone-indicator {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
animation: pulse 1.5s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0% {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 活动状态的样式 */
|
||||||
|
.dock-zone.active {
|
||||||
|
background-color: rgba(79, 114, 179, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dock-zone.active .dock-zone-indicator svg rect {
|
||||||
|
fill: rgba(79, 114, 179, 0.8);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,11 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dock-layout" ref="dockLayoutRef" style="display: flex; flex-direction: column; position: relative;">
|
<div class="dock-layout" ref="dockLayoutRef" style="display: flex; flex-direction: column; position: relative;">
|
||||||
|
<!-- 停靠指示器组件 - 设置高z-index确保显示在最顶层 -->
|
||||||
|
<DockIndicator
|
||||||
|
:visible="showDockIndicator"
|
||||||
|
:target-rect="targetAreaRect"
|
||||||
|
:mouse-position="currentMousePosition"
|
||||||
|
@zone-active="onDockZoneActive"
|
||||||
|
style="z-index: 9999;"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- 主区域 -->
|
<!-- 主区域 -->
|
||||||
<Area
|
<Area
|
||||||
:WindowState="windowState"
|
:WindowState="windowState"
|
||||||
:showTitleBar="false"
|
:showTitleBar="false"
|
||||||
title="主区域"
|
title="主区域"
|
||||||
:style="{ position: 'relative', width: '100%', height: '100%', zIndex: 1 }"
|
:style="{ position: 'relative', width: '100%', height: '100%', zIndex: 1 }"
|
||||||
|
@dragover="handleMainAreaDragOver"
|
||||||
|
@dragleave="handleMainAreaDragLeave"
|
||||||
>
|
>
|
||||||
</Area>
|
</Area>
|
||||||
<!-- 浮动区域直接渲染,不使用额外的div包装 -->
|
<!-- 浮动区域直接渲染,不使用额外的div包装 -->
|
||||||
@@ -64,6 +75,8 @@
|
|||||||
@dragStart="onPanelDragStart(area.id, $event)"
|
@dragStart="onPanelDragStart(area.id, $event)"
|
||||||
@dragMove="onPanelDragMove(area.id, $event)"
|
@dragMove="onPanelDragMove(area.id, $event)"
|
||||||
@dragEnd="onPanelDragEnd"
|
@dragEnd="onPanelDragEnd"
|
||||||
|
@dragover="handleAreaDragOver"
|
||||||
|
@dragleave="handleAreaDragLeave"
|
||||||
/>
|
/>
|
||||||
</TabPage>
|
</TabPage>
|
||||||
</Area>
|
</Area>
|
||||||
@@ -75,6 +88,7 @@ import { ref, defineExpose, nextTick, watch } from 'vue'
|
|||||||
import Area from './Area.vue';
|
import Area from './Area.vue';
|
||||||
import Panel from './Panel.vue';
|
import Panel from './Panel.vue';
|
||||||
import TabPage from './TabPage.vue';
|
import TabPage from './TabPage.vue';
|
||||||
|
import DockIndicator from './DockIndicator.vue';
|
||||||
|
|
||||||
// 主区域状态
|
// 主区域状态
|
||||||
const windowState = ref('最大化')
|
const windowState = ref('最大化')
|
||||||
@@ -88,6 +102,12 @@ const dockLayoutRef = ref(null)
|
|||||||
// 区域ID计数器
|
// 区域ID计数器
|
||||||
let areaIdCounter = 1
|
let areaIdCounter = 1
|
||||||
|
|
||||||
|
// 停靠指示器相关状态
|
||||||
|
const showDockIndicator = ref(false)
|
||||||
|
const currentMousePosition = ref({ x: 0, y: 0 })
|
||||||
|
const targetAreaRect = ref({ left: 0, top: 0, width: 0, height: 0 })
|
||||||
|
const activeDockZone = ref(null)
|
||||||
|
|
||||||
// Panel拖拽相关状态
|
// Panel拖拽相关状态
|
||||||
const panelDragState = ref({
|
const panelDragState = ref({
|
||||||
isDragging: false,
|
isDragging: false,
|
||||||
@@ -273,6 +293,12 @@ const onPanelDragStart = (areaId, event) => {
|
|||||||
y: area.y
|
y: area.y
|
||||||
}
|
}
|
||||||
console.log('Panel拖拽开始,移动Area:', areaId)
|
console.log('Panel拖拽开始,移动Area:', areaId)
|
||||||
|
|
||||||
|
// 初始化鼠标位置跟踪
|
||||||
|
currentMousePosition.value = {
|
||||||
|
x: event.clientX,
|
||||||
|
y: event.clientY
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,6 +328,12 @@ const onPanelDragMove = (areaId, event) => {
|
|||||||
area.x = newLeft
|
area.x = newLeft
|
||||||
area.y = newTop
|
area.y = newTop
|
||||||
|
|
||||||
|
// 更新鼠标位置
|
||||||
|
currentMousePosition.value = {
|
||||||
|
x: event.clientX,
|
||||||
|
y: event.clientY
|
||||||
|
}
|
||||||
|
|
||||||
// 调试信息
|
// 调试信息
|
||||||
console.log('Panel拖拽移动,Area新位置:', { x: newLeft, y: newTop })
|
console.log('Panel拖拽移动,Area新位置:', { x: newLeft, y: newTop })
|
||||||
}
|
}
|
||||||
@@ -313,6 +345,16 @@ const onPanelDragEnd = () => {
|
|||||||
console.log('Panel拖拽结束')
|
console.log('Panel拖拽结束')
|
||||||
panelDragState.value.isDragging = false
|
panelDragState.value.isDragging = false
|
||||||
panelDragState.value.currentAreaId = null
|
panelDragState.value.currentAreaId = null
|
||||||
|
|
||||||
|
// 隐藏停靠指示器
|
||||||
|
showDockIndicator.value = false
|
||||||
|
activeDockZone.value = null
|
||||||
|
|
||||||
|
// 如果有活动的停靠区域,可以在这里处理停靠逻辑
|
||||||
|
if (activeDockZone.value) {
|
||||||
|
console.log('停靠到区域:', activeDockZone.value)
|
||||||
|
// 这里可以实现具体的停靠逻辑
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TabPage拖拽开始
|
// TabPage拖拽开始
|
||||||
@@ -395,6 +437,98 @@ watch(floatingAreas, (newAreas) => {
|
|||||||
});
|
});
|
||||||
}, { deep: true });
|
}, { deep: true });
|
||||||
|
|
||||||
|
// 处理主区域的dragover事件
|
||||||
|
const handleMainAreaDragOver = (event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
if (panelDragState.value.isDragging || tabDragState.value.isDragging) {
|
||||||
|
// 获取主区域的位置和大小
|
||||||
|
const mainAreaElement = event.currentTarget
|
||||||
|
const rect = mainAreaElement.getBoundingClientRect()
|
||||||
|
|
||||||
|
// 更新目标区域信息并显示停靠指示器
|
||||||
|
targetAreaRect.value = {
|
||||||
|
left: rect.left,
|
||||||
|
top: rect.top,
|
||||||
|
width: rect.width,
|
||||||
|
height: rect.height
|
||||||
|
}
|
||||||
|
|
||||||
|
showDockIndicator.value = true
|
||||||
|
|
||||||
|
// 更新鼠标位置
|
||||||
|
currentMousePosition.value = {
|
||||||
|
x: event.clientX,
|
||||||
|
y: event.clientY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理主区域的dragleave事件
|
||||||
|
const handleMainAreaDragLeave = () => {
|
||||||
|
// 检查鼠标是否真的离开了区域(可能只是进入了子元素)
|
||||||
|
setTimeout(() => {
|
||||||
|
const activeElement = document.activeElement
|
||||||
|
const dockLayout = dockLayoutRef.value
|
||||||
|
|
||||||
|
// 如果活动元素不是dockLayout的后代,隐藏指示器
|
||||||
|
if (!dockLayout || (activeElement && !dockLayout.contains(activeElement))) {
|
||||||
|
showDockIndicator.value = false
|
||||||
|
activeDockZone.value = null
|
||||||
|
}
|
||||||
|
}, 50)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理浮动区域的dragover事件
|
||||||
|
const handleAreaDragOver = (event, areaId) => {
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
if (panelDragState.value.isDragging || tabDragState.value.isDragging) {
|
||||||
|
// 避免自身停靠到自身
|
||||||
|
if (areaId !== panelDragState.value.currentAreaId && areaId !== tabDragState.value.currentAreaId) {
|
||||||
|
// 获取目标区域的位置和大小
|
||||||
|
const areaElement = event.currentTarget
|
||||||
|
const rect = areaElement.getBoundingClientRect()
|
||||||
|
|
||||||
|
// 更新目标区域信息并显示停靠指示器
|
||||||
|
targetAreaRect.value = {
|
||||||
|
left: rect.left,
|
||||||
|
top: rect.top,
|
||||||
|
width: rect.width,
|
||||||
|
height: rect.height
|
||||||
|
}
|
||||||
|
|
||||||
|
showDockIndicator.value = true
|
||||||
|
|
||||||
|
// 更新鼠标位置
|
||||||
|
currentMousePosition.value = {
|
||||||
|
x: event.clientX,
|
||||||
|
y: event.clientY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理浮动区域的dragleave事件
|
||||||
|
const handleAreaDragLeave = () => {
|
||||||
|
// 延迟检查,避免快速移动时的闪烁
|
||||||
|
setTimeout(() => {
|
||||||
|
const activeElement = document.activeElement
|
||||||
|
const dockLayout = dockLayoutRef.value
|
||||||
|
|
||||||
|
// 如果活动元素不是dockLayout的后代,隐藏指示器
|
||||||
|
if (!dockLayout || (activeElement && !dockLayout.contains(activeElement))) {
|
||||||
|
showDockIndicator.value = false
|
||||||
|
activeDockZone.value = null
|
||||||
|
}
|
||||||
|
}, 50)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理停靠区域激活事件
|
||||||
|
const onDockZoneActive = (zone) => {
|
||||||
|
activeDockZone.value = zone
|
||||||
|
}
|
||||||
|
|
||||||
// 处理Panel最大化同步事件
|
// 处理Panel最大化同步事件
|
||||||
const onPanelMaximizeSync = ({ areaId, maximized }) => {
|
const onPanelMaximizeSync = ({ areaId, maximized }) => {
|
||||||
// 查找对应的Area
|
// 查找对应的Area
|
||||||
|
|||||||
Reference in New Issue
Block a user