更新DockLayout组件和相关指示器实现
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
class="vs-area select-none"
|
class="vs-area select-none"
|
||||||
:class="{ 'is-maximized': isMaximized, 'is-normal': !isMaximized }"
|
:class="{ 'is-maximized': isMaximized, 'is-normal': !isMaximized }"
|
||||||
:style="areaStyle"
|
:style="areaStyle"
|
||||||
|
:data-area-id="id"
|
||||||
@dragover="handleDragOver"
|
@dragover="handleDragOver"
|
||||||
@dragleave="handleDragLeave"
|
@dragleave="handleDragLeave"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<div
|
<div
|
||||||
v-if="activeDockZone"
|
v-if="activeDockZone"
|
||||||
class="dock-preview-area"
|
class="dock-preview-area"
|
||||||
:style="previewAreaStyle"
|
:style="enhancedPreviewAreaStyle"
|
||||||
></div>
|
></div>
|
||||||
<!-- 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">
|
||||||
@@ -90,6 +90,78 @@
|
|||||||
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>
|
||||||
|
|
||||||
|
<!-- 右指示:根据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"
|
viewBox="0 0 40 40"
|
||||||
aria-hidden="false"
|
aria-hidden="false"
|
||||||
class="dock-top-area"
|
class="dock-top-area"
|
||||||
|
@mouseenter="handleSubAreaMouseEnter('top-area')"
|
||||||
|
@mouseleave="handleSubAreaMouseLeave"
|
||||||
|
@mousemove="handleSubAreaMouseMove('top-area', $event)"
|
||||||
>
|
>
|
||||||
<use xlink:href="#shared-area" />
|
<use xlink:href="#shared-area" />
|
||||||
<path
|
<path
|
||||||
@@ -150,6 +225,9 @@
|
|||||||
viewBox="0 0 40 40"
|
viewBox="0 0 40 40"
|
||||||
aria-hidden="false"
|
aria-hidden="false"
|
||||||
class="dock-right-area"
|
class="dock-right-area"
|
||||||
|
@mouseenter="handleSubAreaMouseEnter('right-area')"
|
||||||
|
@mouseleave="handleSubAreaMouseLeave"
|
||||||
|
@mousemove="handleSubAreaMouseMove('right-area', $event)"
|
||||||
>
|
>
|
||||||
<use xlink:href="#shared-area" />
|
<use xlink:href="#shared-area" />
|
||||||
<path
|
<path
|
||||||
@@ -177,6 +255,9 @@
|
|||||||
viewBox="0 0 40 40"
|
viewBox="0 0 40 40"
|
||||||
aria-hidden="false"
|
aria-hidden="false"
|
||||||
class="dock-bottom-area"
|
class="dock-bottom-area"
|
||||||
|
@mouseenter="handleSubAreaMouseEnter('bottom-area')"
|
||||||
|
@mouseleave="handleSubAreaMouseLeave"
|
||||||
|
@mousemove="handleSubAreaMouseMove('bottom-area', $event)"
|
||||||
>
|
>
|
||||||
<use xlink:href="#shared-area" />
|
<use xlink:href="#shared-area" />
|
||||||
<path
|
<path
|
||||||
@@ -204,6 +285,9 @@
|
|||||||
viewBox="0 0 40 40"
|
viewBox="0 0 40 40"
|
||||||
aria-hidden="false"
|
aria-hidden="false"
|
||||||
class="dock-left-area"
|
class="dock-left-area"
|
||||||
|
@mouseenter="handleSubAreaMouseEnter('left-area')"
|
||||||
|
@mouseleave="handleSubAreaMouseLeave"
|
||||||
|
@mousemove="handleSubAreaMouseMove('left-area', $event)"
|
||||||
>
|
>
|
||||||
<use xlink:href="#shared-area" />
|
<use xlink:href="#shared-area" />
|
||||||
<path
|
<path
|
||||||
@@ -415,6 +499,122 @@ const handleMouseLeave = () => {
|
|||||||
}, 100)
|
}, 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(() => {
|
onUnmounted(() => {
|
||||||
if (mouseLeaveTimer) {
|
if (mouseLeaveTimer) {
|
||||||
@@ -446,7 +646,7 @@ const activeDockZone = computed(() => {
|
|||||||
return hoveredZone.value
|
return hoveredZone.value
|
||||||
})
|
})
|
||||||
|
|
||||||
// 计算半透明区域框的样式
|
// 计算半透明区域框的基础样式
|
||||||
const previewAreaStyle = computed(() => {
|
const previewAreaStyle = computed(() => {
|
||||||
if (!activeDockZone.value) return {};
|
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'])
|
const emit = defineEmits(['zone-active'])
|
||||||
|
|
||||||
|
|||||||
@@ -30,14 +30,21 @@
|
|||||||
| `indicator-center-area` | `center-dock-zone` | 中心停靠区域(半透明背景) |
|
| `indicator-center-area` | `center-dock-zone` | 中心停靠区域(半透明背景) |
|
||||||
|
|
||||||
### 4. 子区域指示器(Sub-Area Indicators)
|
### 4. 子区域指示器(Sub-Area Indicators)
|
||||||
位于中心区域内的四个子区域,用于检测具体停靠区域。
|
位于中心区域内的四个半透明子区域,鼠标悬停后显示半透明的依靠区,以鼠标位置所在的Area.vue内容区作为基准进行停靠检测。
|
||||||
|
|
||||||
| 当前名称 | 建议命名 | 用途说明 |
|
| 当前名称 | 建议命名 | 用途说明 |
|
||||||
|---------|---------|----------|
|
|---------|---------|----------|
|
||||||
| `dock-top-area` | `center-top-area` | 中心区域内的上方区域指示器 |
|
| `dock-top-area` | `center-top-area` | 中心区域内上方半透明区域,鼠标悬停显示停靠依靠区 |
|
||||||
| `dock-right-area` | `center-right-area` | 中心区域内的右侧区域指示器 |
|
| `dock-right-area` | `center-right-area` | 中心区域内右侧半透明区域,鼠标悬停显示停靠依靠区 |
|
||||||
| `dock-bottom-area` | `center-bottom-area` | 中心区域内的下方区域指示器 |
|
| `dock-bottom-area` | `center-bottom-area` | 中心区域内下方半透明区域,鼠标悬停显示停靠依靠区 |
|
||||||
| `dock-left-area` | `center-left-area` | 中心区域内的左侧区域指示器 |
|
| `dock-left-area` | `center-left-area` | 中心区域内左侧半透明区域,鼠标悬停显示停靠依靠区 |
|
||||||
|
|
||||||
|
**✅ 已完成功能实现(v1.2)**:
|
||||||
|
- 子区域鼠标事件处理(hover效果)
|
||||||
|
- 鼠标位置检测功能(detectMouseArea方法)
|
||||||
|
- 半透明依靠区动态显示(computeRelianceAreaStyle方法)
|
||||||
|
- Area组件标识支持(data-area-id属性)
|
||||||
|
- 基于鼠标位置的动态样式计算(enhancedPreviewAreaStyle)
|
||||||
|
|
||||||
### 5. 边缘方向指示器(Edge Direction Indicators)
|
### 5. 边缘方向指示器(Edge Direction Indicators)
|
||||||
围绕中心区域边缘的四个方向指示器,用于精确停靠定位。
|
围绕中心区域边缘的四个方向指示器,用于精确停靠定位。
|
||||||
@@ -114,20 +121,24 @@ DockIndicator
|
|||||||
- **全局顶部指示器**:指代全局顶部边缘指示器
|
- **全局顶部指示器**:指代全局顶部边缘指示器
|
||||||
- **中心区域**:指代center-dock-container
|
- **中心区域**:指代center-dock-container
|
||||||
- **中心停靠区**:指代center-dock-zone
|
- **中心停靠区**:指代center-dock-zone
|
||||||
- **中心各子区域**:分别称为"上方区域"、"右侧区域"、"下方区域"、"左侧区域"
|
- **中心各子区域**:分别称为"上方半透明区域"、"右侧半透明区域"、"下方半透明区域"、"左侧半透明区域"(鼠标悬停时显示停靠依靠区)
|
||||||
- **中心边缘指示器**:分别称为"上边缘指示器"、"右边缘指示器"、"下边缘指示器"、"左边缘指示器"
|
- **中心边缘指示器**:分别称为"上边缘指示器"、"右边缘指示器"、"下边缘指示器"、"左边缘指示器"
|
||||||
- **中心主指示器**:指代中心的中央指示器
|
- **中心主指示器**:指代中心的中央指示器
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**文档版本**:v1.1
|
**文档版本**:v1.2
|
||||||
**创建日期**:2024年
|
**创建日期**:2024年
|
||||||
**最后更新**:2024年
|
**最后更新**:2024年
|
||||||
**适用范围**:DockIndicator.vue 组件
|
**适用范围**:DockIndicator.vue 组件
|
||||||
|
|
||||||
## 更新日志
|
## 更新日志
|
||||||
|
|
||||||
### v1.1 (当前版本)
|
### v1.2 (当前版本)
|
||||||
|
- 添加子区域指示器功能实现记录
|
||||||
|
- 更新文档以反映半透明依靠区功能的完成状态
|
||||||
|
|
||||||
|
### v1.1
|
||||||
- 修正层级结构图,添加完整的外部边缘指示器(右、下、左)
|
- 修正层级结构图,添加完整的外部边缘指示器(右、下、左)
|
||||||
- 在层级结构图中标注当前实际使用的类名
|
- 在层级结构图中标注当前实际使用的类名
|
||||||
- 完善命名体系的一致性
|
- 完善命名体系的一致性
|
||||||
|
|||||||
Reference in New Issue
Block a user