修复停靠指示器定位问题:调整所有指示器与容器边缘保持5像素距离

This commit is contained in:
zqm
2025-11-13 10:50:22 +08:00
parent 9b4746dcbe
commit 29c4e75819
2 changed files with 239 additions and 40 deletions

View File

@@ -1,6 +1,5 @@
<template> <template>
<div v-if="visible" class="dock-indicator" :style="indicatorStyle"> <div v-if="visible" class="dock-indicator" :style="indicatorStyle">
<!-- 停靠指示器已移除 -->
<!-- 1. 定义可复用组件symbol封装所有渐变和路径只写一次 --> <!-- 1. 定义可复用组件symbol封装所有渐变和路径只写一次 -->
<svg width="0" height="0" viewBox="0 0 40 40" aria-hidden="true"> <svg width="0" height="0" viewBox="0 0 40 40" aria-hidden="true">
<defs> <defs>
@@ -51,8 +50,16 @@
d="M16 30 L20 26 L23 30 Z" /> d="M16 30 L20 26 L23 30 Z" />
</symbol> </symbol>
</svg> </svg>
<!-- 2. 第一个 SVG不旋转直接调用共用组件 -->
<svg width="41" height="41" viewBox="0 0 40 40" aria-hidden="true"> <!-- 上指示根据activeDockZone状态显示和高亮 -->
<svg
width="41"
height="41"
viewBox="0 0 40 40"
aria-hidden="true"
class="indicator-top"
:class="{ 'active': activeDockZone === 'top' }"
>
<use xlink:href="#shared-icon" /> <use xlink:href="#shared-icon" />
<path <path
fill="#4C5E83" fill="#4C5E83"
@@ -65,8 +72,16 @@
clip-rule="evenodd" clip-rule="evenodd"
d="M10 13 L30 13 L30 19 L10 19 Z" /> d="M10 13 L30 13 L30 19 L10 19 Z" />
</svg> </svg>
<!-- 3. 第二个 SVG旋转90度调用同一共用组件 -->
<svg width="41" height="41" viewBox="0 0 40 40" aria-hidden="true"> <!-- 右指示根据activeDockZone状态显示和高亮 -->
<svg
width="41"
height="41"
viewBox="0 0 40 40"
aria-hidden="true"
class="indicator-right"
:class="{ 'active': activeDockZone === 'right' }"
>
<use xlink:href="#shared-icon" transform="rotate(90 20 20)" /> <use xlink:href="#shared-icon" transform="rotate(90 20 20)" />
<path <path
fill="#4C5E83" fill="#4C5E83"
@@ -79,8 +94,16 @@
clip-rule="evenodd" clip-rule="evenodd"
d="M21 14 L30 14 L30 30 L21 30 Z" /> d="M21 14 L30 14 L30 30 L21 30 Z" />
</svg> </svg>
<!-- 4. 第三个 SVG旋转180度调用同一共用组件 -->
<svg width="41" height="41" viewBox="0 0 40 40" aria-hidden="true"> <!-- 下指示根据activeDockZone状态显示和高亮 -->
<svg
width="41"
height="41"
viewBox="0 0 40 40"
aria-hidden="true"
class="indicator-bottom"
:class="{ 'active': activeDockZone === 'bottom' }"
>
<use xlink:href="#shared-icon" transform="rotate(180 20 20)" /> <use xlink:href="#shared-icon" transform="rotate(180 20 20)" />
<path <path
fill="#4C5E83" fill="#4C5E83"
@@ -93,8 +116,16 @@
clip-rule="evenodd" clip-rule="evenodd"
d="M10 24 L30 24 L30 30 L10 30 Z" /> d="M10 24 L30 24 L30 30 L10 30 Z" />
</svg> </svg>
<!-- 5. 第四个 SVG旋转270度调用同一共用组件 -->
<svg width="41" height="41" viewBox="0 0 40 40" aria-hidden="true"> <!-- 左指示根据activeDockZone状态显示和高亮 -->
<svg
width="41"
height="41"
viewBox="0 0 40 40"
aria-hidden="true"
class="indicator-left"
:class="{ 'active': activeDockZone === 'left' }"
>
<use xlink:href="#shared-icon" transform="rotate(270 20 20)" /> <use xlink:href="#shared-icon" transform="rotate(270 20 20)" />
<path <path
fill="#4C5E83" fill="#4C5E83"
@@ -107,9 +138,17 @@
clip-rule="evenodd" clip-rule="evenodd"
d="M10 14 L19 14 L19 30 L10 30 Z" /> d="M10 14 L19 14 L19 30 L10 30 Z" />
</svg> </svg>
<!-- 6. 第五个 SVG正中的按钮调用同一共用组件 -->
<svg width="41" height="41" viewBox="0 0 40 40" aria-hidden="true"> <!-- 中心指示器根据activeDockZone状态显示和高亮 -->
<use xlink:href="#shared-border" transform="rotate(270 20 20)" /> <svg
width="41"
height="41"
viewBox="0 0 40 40"
aria-hidden="true"
class="indicator-center"
:class="{ 'active': activeDockZone === 'center' }"
>
<use xlink:href="#shared-border" />
<path <path
fill="#4C5E83" fill="#4C5E83"
fill-rule="evenodd" fill-rule="evenodd"
@@ -125,7 +164,7 @@
</template> </template>
<script setup> <script setup>
import { computed } from 'vue' import { computed, watch, ref } from 'vue'
// Props定义 // Props定义
const props = defineProps({ const props = defineProps({
@@ -143,6 +182,14 @@ const props = defineProps({
width: 0, width: 0,
height: 0 height: 0
}) })
},
// 鼠标位置
mousePosition: {
type: Object,
default: () => ({
x: 0,
y: 0
})
} }
}) })
@@ -158,6 +205,53 @@ const indicatorStyle = computed(() => {
zIndex: 9999 zIndex: 9999
} }
}) })
// 计算活动的停靠区域
const activeDockZone = computed(() => {
if (!props.visible) return null
// 计算各个区域的位置和大小
const { left, top, width, height } = props.targetRect
const { x, y } = props.mousePosition
// 计算相对位置
const relativeX = x - left
const relativeY = y - top
// 定义区域的阈值25%
const threshold = 0.25
// 判断是否在顶部区域
if (relativeY < height * threshold) {
return 'top'
}
// 判断是否在底部区域
if (relativeY > height * (1 - threshold)) {
return 'bottom'
}
// 判断是否在左侧区域
if (relativeX < width * threshold) {
return 'left'
}
// 判断是否在右侧区域
if (relativeX > width * (1 - threshold)) {
return 'right'
}
// 默认在中心区域
return 'center'
})
// 定义事件
const emit = defineEmits(['zone-active'])
// 监听activeDockZone变化触发事件
watch(activeDockZone, (newZone) => {
emit('zone-active', newZone)
})
</script> </script>
<style scoped> <style scoped>
@@ -165,4 +259,65 @@ const indicatorStyle = computed(() => {
position: absolute; position: absolute;
box-sizing: border-box; box-sizing: border-box;
} }
/* 上指示器定位在目标区域的顶端中间上边缘距dock-layout上边缘5像素 */
.indicator-top {
position: absolute;
top: 5px;
left: 50%;
transform: translateX(-50%);
opacity: 0.7; /* 默认半透明 */
transition: opacity 0.2s;
}
/* 右指示器定位在目标区域的右侧中间右边缘距dock-layout右边缘5像素 */
.indicator-right {
position: absolute;
top: 50%;
right: 5px;
transform: translateY(-50%);
opacity: 0.7; /* 默认半透明 */
transition: opacity 0.2s;
}
/* 下指示器定位在目标区域的底部中间下边缘距dock-layout下边缘5像素 */
.indicator-bottom {
position: absolute;
bottom: 5px;
left: 50%;
transform: translateX(-50%);
opacity: 0.7; /* 默认半透明 */
transition: opacity 0.2s;
}
/* 左指示器定位在目标区域的左侧中间左边缘距dock-layout左边缘5像素 */
.indicator-left {
position: absolute;
top: 50%;
left: 5px;
transform: translateY(-50%);
opacity: 0.7; /* 默认半透明 */
transition: opacity 0.2s;
}
/* 中心指示器:定位在目标区域的中心 */
.indicator-center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
opacity: 0.7; /* 默认半透明 */
transition: opacity 0.2s;
}
/* 活动状态样式 */
.indicator-top.active,
.indicator-right.active,
.indicator-bottom.active,
.indicator-left.active,
.indicator-center.active {
opacity: 1; /* 完全不透明 */
transform: scale(1.1); /* 稍微放大 */
transition: all 0.2s;
}
</style> </style>

View File

@@ -9,7 +9,7 @@
style="z-index: 9999;" style="z-index: 9999;"
/> />
<!-- 主区域 --> <!-- 主区域 - 添加ref引用 -->
<Area <Area
:WindowState="windowState" :WindowState="windowState"
:showTitleBar="false" :showTitleBar="false"
@@ -98,7 +98,6 @@ const floatingAreas = ref([])
// 容器引用 // 容器引用
const dockLayoutRef = ref(null) const dockLayoutRef = ref(null)
// 区域ID计数器 // 区域ID计数器
let areaIdCounter = 1 let areaIdCounter = 1
@@ -299,6 +298,20 @@ const onPanelDragStart = (areaId, event) => {
x: event.clientX, x: event.clientX,
y: event.clientY y: event.clientY
} }
// 拖拽开始时就显示指示器
showDockIndicator.value = true
// 使用dock-layout作为默认目标区域
if (dockLayoutRef.value) {
const rect = dockLayoutRef.value.getBoundingClientRect()
targetAreaRect.value = {
left: 0, // 使用相对于容器的位置(左上角)
top: 0,
width: rect.width,
height: rect.height
}
}
} }
} }
@@ -373,6 +386,26 @@ const onTabDragStart = (areaId, event) => {
y: area.y y: area.y
} }
console.log('TabPage拖拽开始移动Area:', areaId) console.log('TabPage拖拽开始移动Area:', areaId)
// 初始化鼠标位置跟踪
currentMousePosition.value = {
x: event.clientX,
y: event.clientY
}
// 拖拽开始时就显示指示器
showDockIndicator.value = true
// 使用dock-layout作为默认目标区域
if (dockLayoutRef.value) {
const rect = dockLayoutRef.value.getBoundingClientRect()
targetAreaRect.value = {
left: 0, // 使用相对于容器的位置(左上角)
top: 0,
width: rect.width,
height: rect.height
}
}
} }
} }
@@ -402,6 +435,12 @@ const onTabDragMove = (areaId, event) => {
area.x = newLeft area.x = newLeft
area.y = newTop area.y = newTop
// 更新鼠标位置
currentMousePosition.value = {
x: event.clientX,
y: event.clientY
}
// 调试信息 // 调试信息
console.log('TabPage拖拽移动Area新位置:', { x: newLeft, y: newTop }) console.log('TabPage拖拽移动Area新位置:', { x: newLeft, y: newTop })
} }
@@ -442,14 +481,19 @@ const handleMainAreaDragOver = (event) => {
event.preventDefault() event.preventDefault()
if (panelDragState.value.isDragging || tabDragState.value.isDragging) { if (panelDragState.value.isDragging || tabDragState.value.isDragging) {
// 获取主区域的位置和大小 // 使用dock-layout作为基准获取位置和大小
const mainAreaElement = event.currentTarget let rect
const rect = mainAreaElement.getBoundingClientRect() if (dockLayoutRef.value) {
rect = dockLayoutRef.value.getBoundingClientRect()
} else {
// 回退到使用事件目标
rect = event.currentTarget.getBoundingClientRect()
}
// 更新目标区域信息并显示停靠指示器 // 更新目标区域信息并显示停靠指示器
targetAreaRect.value = { targetAreaRect.value = {
left: rect.left, left: 0, // 使用相对于容器的位置(左上角)
top: rect.top, top: 0,
width: rect.width, width: rect.width,
height: rect.height height: rect.height
} }