修复停靠指示器定位问题:调整所有指示器与容器边缘保持5像素距离
This commit is contained in:
@@ -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,36 +50,60 @@
|
|||||||
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"
|
||||||
fill-rule="evenodd"
|
fill-rule="evenodd"
|
||||||
clip-rule="evenodd"
|
clip-rule="evenodd"
|
||||||
d="M8 8 L32 8 L32 20 L31 21 L9 21 L8 20 Z" />
|
d="M8 8 L32 8 L32 20 L31 21 L9 21 L8 20 Z" />
|
||||||
<path
|
<path
|
||||||
fill="url(#Area)"
|
fill="url(#Area)"
|
||||||
fill-rule="evenodd"
|
fill-rule="evenodd"
|
||||||
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"
|
||||||
fill-rule="evenodd"
|
fill-rule="evenodd"
|
||||||
clip-rule="evenodd"
|
clip-rule="evenodd"
|
||||||
d="M19 8 L32 8 L32 31 L31 32 L20 32 L19 31 Z" />
|
d="M19 8 L32 8 L32 31 L31 32 L20 32 L19 31 Z" />
|
||||||
<path
|
<path
|
||||||
fill="url(#Area)"
|
fill="url(#Area)"
|
||||||
fill-rule="evenodd"
|
fill-rule="evenodd"
|
||||||
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>
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user