更新DockLayout组件和相关指示器实现

This commit is contained in:
zqm
2025-11-14 10:35:22 +08:00
parent d2f47559d3
commit 05d1dd866a
3 changed files with 243 additions and 10 deletions

View File

@@ -3,6 +3,7 @@
class="vs-area select-none"
:class="{ 'is-maximized': isMaximized, 'is-normal': !isMaximized }"
:style="areaStyle"
:data-area-id="id"
@dragover="handleDragOver"
@dragleave="handleDragLeave"
>

View File

@@ -4,7 +4,7 @@
<div
v-if="activeDockZone"
class="dock-preview-area"
:style="previewAreaStyle"
:style="enhancedPreviewAreaStyle"
></div>
<!-- 1. 定义可复用组件symbol封装所有渐变和路径只写一次 -->
<svg width="0" height="0" viewBox="0 0 40 40" aria-hidden="true">
@@ -91,6 +91,78 @@
d="M10 13 L30 13 L30 19 L10 19 Z" />
</svg>
<!-- 右指示根据activeDockZone状态显示和高亮 -->
<svg
width="41"
height="41"
viewBox="0 0 40 40"
aria-hidden="true"
class="indicator-right"
:class="{ 'active': activeDockZone === 'right' }"
@mouseenter="handleMouseEnter('right')"
@mouseleave="handleMouseLeave"
>
<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>
<!-- 下指示根据activeDockZone状态显示和高亮 -->
<svg
width="41"
height="41"
viewBox="0 0 40 40"
aria-hidden="true"
class="indicator-bottom"
:class="{ 'active': activeDockZone === 'bottom' }"
@mouseenter="handleMouseEnter('bottom')"
@mouseleave="handleMouseLeave"
>
<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>
<!-- 左指示根据activeDockZone状态显示和高亮 -->
<svg
width="41"
height="41"
viewBox="0 0 40 40"
aria-hidden="true"
class="indicator-left"
:class="{ 'active': activeDockZone === 'left' }"
@mouseenter="handleMouseEnter('left')"
@mouseleave="handleMouseLeave"
>
<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>
<!-- 中心区域容器包装中心指示区和中心指示器 -->
@@ -123,6 +195,9 @@
viewBox="0 0 40 40"
aria-hidden="false"
class="dock-top-area"
@mouseenter="handleSubAreaMouseEnter('top-area')"
@mouseleave="handleSubAreaMouseLeave"
@mousemove="handleSubAreaMouseMove('top-area', $event)"
>
<use xlink:href="#shared-area" />
<path
@@ -150,6 +225,9 @@
viewBox="0 0 40 40"
aria-hidden="false"
class="dock-right-area"
@mouseenter="handleSubAreaMouseEnter('right-area')"
@mouseleave="handleSubAreaMouseLeave"
@mousemove="handleSubAreaMouseMove('right-area', $event)"
>
<use xlink:href="#shared-area" />
<path
@@ -177,6 +255,9 @@
viewBox="0 0 40 40"
aria-hidden="false"
class="dock-bottom-area"
@mouseenter="handleSubAreaMouseEnter('bottom-area')"
@mouseleave="handleSubAreaMouseLeave"
@mousemove="handleSubAreaMouseMove('bottom-area', $event)"
>
<use xlink:href="#shared-area" />
<path
@@ -204,6 +285,9 @@
viewBox="0 0 40 40"
aria-hidden="false"
class="dock-left-area"
@mouseenter="handleSubAreaMouseEnter('left-area')"
@mouseleave="handleSubAreaMouseLeave"
@mousemove="handleSubAreaMouseMove('left-area', $event)"
>
<use xlink:href="#shared-area" />
<path
@@ -415,6 +499,122 @@ const handleMouseLeave = () => {
}, 100)
}
// 处理子区域鼠标进入事件
const handleSubAreaMouseEnter = (area) => {
// 清除可能存在的离开定时器
if (mouseLeaveTimer) {
clearTimeout(mouseLeaveTimer)
mouseLeaveTimer = null
}
hoveredZone.value = area
}
// 处理子区域鼠标离开事件
const handleSubAreaMouseLeave = () => {
// 添加短暂延迟,避免快速进出导致的闪烁
mouseLeaveTimer = setTimeout(() => {
hoveredZone.value = null
mouseLeaveTimer = null
}, 100)
}
// 处理子区域鼠标移动事件
const handleSubAreaMouseMove = (area, event) => {
// 更新鼠标位置信息
const rect = event.currentTarget.getBoundingClientRect()
const mouseX = event.clientX - rect.left
const mouseY = event.clientY - rect.top
// 可以在这里添加更复杂的鼠标位置计算逻辑
// 例如根据鼠标在子区域内的位置动态调整依靠区的大小
// 保持当前悬停状态
hoveredZone.value = area
}
// 检测鼠标所在的Area组件
const detectMouseArea = (mouseX, mouseY) => {
try {
// 查找所有包含Area组件的DOM元素
const areaElements = document.querySelectorAll('.vs-area')
for (let element of areaElements) {
const rect = element.getBoundingClientRect()
// 检查鼠标位置是否在当前Area元素内
if (mouseX >= rect.left && mouseX <= rect.right &&
mouseY >= rect.top && mouseY <= rect.bottom) {
return {
element: element,
rect: rect,
id: element.getAttribute('data-area-id') || element.dataset.id || 'unknown'
}
}
}
return null
} catch (error) {
console.warn('检测鼠标区域时出错:', error)
return null
}
}
// 计算基于鼠标位置的半透明依靠区样式
const computeRelianceAreaStyle = (area, zone) => {
if (!area || !props.targetRect) return {}
const { left: targetLeft, top: targetTop, width: targetWidth, height: targetHeight } = props.targetRect
const areaRect = area.rect
// 计算鼠标在Area内容区内的相对位置
const mouseRelativeX = props.mousePosition.x - areaRect.left
const mouseRelativeY = props.mousePosition.y - areaRect.top
// 根据不同区域和鼠标位置计算依靠区
const threshold = 0.3
switch (zone) {
case 'top-area':
return {
position: 'absolute',
left: `${targetLeft}px`,
top: `${targetTop}px`,
width: `${targetWidth}px`,
height: `${targetHeight * threshold}px`
}
case 'bottom-area':
return {
position: 'absolute',
left: `${targetLeft}px`,
top: `${targetTop + targetHeight * (1 - threshold)}px`,
width: `${targetWidth}px`,
height: `${targetHeight * threshold}px`
}
case 'left-area':
return {
position: 'absolute',
left: `${targetLeft}px`,
top: `${targetTop}px`,
width: `${targetWidth * threshold}px`,
height: `${targetHeight}px`
}
case 'right-area':
return {
position: 'absolute',
left: `${targetLeft + targetWidth * (1 - threshold)}px`,
top: `${targetTop}px`,
width: `${targetWidth * threshold}px`,
height: `${targetHeight}px`
}
default:
return {}
}
}
// 清理定时器
onUnmounted(() => {
if (mouseLeaveTimer) {
@@ -446,7 +646,7 @@ const activeDockZone = computed(() => {
return hoveredZone.value
})
// 计算半透明区域框的样式
// 计算半透明区域框的基础样式
const previewAreaStyle = computed(() => {
if (!activeDockZone.value) return {};
@@ -504,6 +704,27 @@ const previewAreaStyle = computed(() => {
}
})
// 增强的半透明区域框样式,支持基于鼠标位置的动态计算
const enhancedPreviewAreaStyle = computed(() => {
if (!activeDockZone.value) return {};
// 如果是子区域指示器,使用增强的计算逻辑
if (activeDockZone.value.includes('-area')) {
// 检测鼠标所在的Area组件
const mouseArea = detectMouseArea(props.mousePosition.x, props.mousePosition.y)
// 使用基于鼠标位置的计算逻辑
const relianceStyle = computeRelianceAreaStyle(mouseArea, activeDockZone.value)
// 合并基础样式和增强样式
const basicStyle = previewAreaStyle.value
return { ...basicStyle, ...relianceStyle }
}
// 对于非子区域指示器,使用原有的计算逻辑
return previewAreaStyle.value
})
// 定义事件
const emit = defineEmits(['zone-active'])

View File

@@ -30,14 +30,21 @@
| `indicator-center-area` | `center-dock-zone` | 中心停靠区域(半透明背景) |
### 4. 子区域指示器Sub-Area Indicators
位于中心区域内的四个子区域,用于检测具体停靠区域
位于中心区域内的四个半透明子区域,鼠标悬停后显示半透明的依靠区以鼠标位置所在的Area.vue内容区作为基准进行停靠检测
| 当前名称 | 建议命名 | 用途说明 |
|---------|---------|----------|
| `dock-top-area` | `center-top-area` | 中心区域内上方区域指示器 |
| `dock-right-area` | `center-right-area` | 中心区域内右侧区域指示器 |
| `dock-bottom-area` | `center-bottom-area` | 中心区域内下方区域指示器 |
| `dock-left-area` | `center-left-area` | 中心区域内左侧区域指示器 |
| `dock-top-area` | `center-top-area` | 中心区域内上方半透明区域,鼠标悬停显示停靠依靠区 |
| `dock-right-area` | `center-right-area` | 中心区域内右侧半透明区域,鼠标悬停显示停靠依靠区 |
| `dock-bottom-area` | `center-bottom-area` | 中心区域内下方半透明区域,鼠标悬停显示停靠依靠区 |
| `dock-left-area` | `center-left-area` | 中心区域内左侧半透明区域,鼠标悬停显示停靠依靠区 |
**✅ 已完成功能实现v1.2**
- 子区域鼠标事件处理hover效果
- 鼠标位置检测功能detectMouseArea方法
- 半透明依靠区动态显示computeRelianceAreaStyle方法
- Area组件标识支持data-area-id属性
- 基于鼠标位置的动态样式计算enhancedPreviewAreaStyle
### 5. 边缘方向指示器Edge Direction Indicators
围绕中心区域边缘的四个方向指示器,用于精确停靠定位。
@@ -114,20 +121,24 @@ DockIndicator
- **全局顶部指示器**:指代全局顶部边缘指示器
- **中心区域**指代center-dock-container
- **中心停靠区**指代center-dock-zone
- **中心各子区域**:分别称为"上方区域"、"右侧区域"、"下方区域"、"左侧区域"
- **中心各子区域**:分别称为"上方半透明区域"、"右侧半透明区域"、"下方半透明区域"、"左侧半透明区域"(鼠标悬停时显示停靠依靠区)
- **中心边缘指示器**:分别称为"上边缘指示器"、"右边缘指示器"、"下边缘指示器"、"左边缘指示器"
- **中心主指示器**:指代中心的中央指示器
---
**文档版本**v1.1
**文档版本**v1.2
**创建日期**2024年
**最后更新**2024年
**适用范围**DockIndicator.vue 组件
## 更新日志
### v1.1 (当前版本)
### v1.2 (当前版本)
- 添加子区域指示器功能实现记录
- 更新文档以反映半透明依靠区功能的完成状态
### v1.1
- 修正层级结构图,添加完整的外部边缘指示器(右、下、左)
- 在层级结构图中标注当前实际使用的类名
- 完善命名体系的一致性