269 lines
7.1 KiB
Vue
269 lines
7.1 KiB
Vue
<template>
|
||
<div class="dock-layout" ref="dockLayoutRef">
|
||
<!-- 浮动区域列表 -->
|
||
<Area
|
||
v-for="area in floatingAreas"
|
||
:key="area.id"
|
||
:id="area.id"
|
||
:title="area.title"
|
||
v-model:WindowState="area.WindowState"
|
||
:showTitleBar="false"
|
||
:width="area.width"
|
||
:height="area.height"
|
||
:left="area.WindowState !== '最大化' ? area.x : undefined"
|
||
:top="area.WindowState !== '最大化' ? area.y : undefined"
|
||
:style="area.WindowState !== '最大化' ? {
|
||
position: 'absolute',
|
||
zIndex: 10
|
||
} : {
|
||
zIndex: 100
|
||
}"
|
||
@close="onCloseFloatingArea(area.id)"
|
||
@update:position="onUpdatePosition(area.id, $event)"
|
||
>
|
||
<!-- 每个Area内渲染其包含的Panels -->
|
||
<Panel
|
||
v-for="panel in area.panels"
|
||
:key="panel.id"
|
||
:id="panel.id"
|
||
:title="panel.title"
|
||
:collapsed="panel.collapsed"
|
||
:toolbarExpanded="panel.toolbarExpanded"
|
||
@toggleCollapse="onToggleCollapse"
|
||
@maximize="onMaximize"
|
||
@close="onClosePanel(area.id, panel.id)"
|
||
@toggleToolbar="onToggleToolbar"
|
||
@dragStart="onPanelDragStart(area.id, $event)"
|
||
@dragMove="onPanelDragMove(area.id, $event)"
|
||
@dragEnd="onPanelDragEnd"
|
||
/>
|
||
</Area>
|
||
|
||
<!-- 主区域 -->
|
||
<Area
|
||
:WindowState="windowState"
|
||
:showTitleBar="false"
|
||
title="主区域"
|
||
:style="{ position: 'relative', zIndex: 1 }"
|
||
/>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, defineExpose, nextTick } from 'vue'
|
||
import Area from './Area.vue';
|
||
import Panel from './Panel.vue';
|
||
|
||
// 主区域状态
|
||
const windowState = ref('最大化')
|
||
|
||
// 浮动区域列表 - 每个area包含panels数组
|
||
const floatingAreas = ref([])
|
||
|
||
// 容器引用
|
||
const dockLayoutRef = ref(null)
|
||
|
||
// 区域ID计数器
|
||
let areaIdCounter = 1
|
||
|
||
// Panel拖拽相关状态
|
||
const panelDragState = ref({
|
||
isDragging: false,
|
||
currentAreaId: null,
|
||
startClientPos: { x: 0, y: 0 },
|
||
startAreaPos: { x: 0, y: 0 }
|
||
})
|
||
|
||
// 添加新的浮动面板
|
||
const addFloatingPanel = () => {
|
||
// 获取父容器尺寸以计算居中位置
|
||
let x = 50 + (areaIdCounter - 2) * 20
|
||
let y = 50 + (areaIdCounter - 2) * 20
|
||
|
||
// 如果容器已渲染,计算居中位置
|
||
if (dockLayoutRef.value) {
|
||
const containerRect = dockLayoutRef.value.getBoundingClientRect()
|
||
const width = 300
|
||
const height = 250
|
||
x = Math.floor((containerRect.width - width) / 2)
|
||
y = Math.floor((containerRect.height - height) / 2)
|
||
}
|
||
|
||
// 创建新的Area组件配置
|
||
const newArea = {
|
||
id: `floating-area-${areaIdCounter++}`,
|
||
title: `浮动区域 ${areaIdCounter - 1}`,
|
||
x: x,
|
||
y: y,
|
||
width: 300,
|
||
height: 250,
|
||
WindowState: '正常',
|
||
showTitleBar: true,
|
||
// 添加一个Panel,填充满父容器
|
||
panels: [
|
||
{
|
||
id: `panel-${areaIdCounter - 1}-1`,
|
||
title: `面板 1`,
|
||
x: 0,
|
||
y: 0,
|
||
width: 100,
|
||
height: 100,
|
||
collapsed: false,
|
||
toolbarExpanded: false
|
||
}
|
||
]
|
||
}
|
||
floatingAreas.value.push(newArea)
|
||
}
|
||
|
||
// 更新区域位置
|
||
const onUpdatePosition = (id, position) => {
|
||
const area = floatingAreas.value.find(a => a.id === id)
|
||
if (area) {
|
||
area.x = position.left
|
||
area.y = position.top
|
||
}
|
||
}
|
||
|
||
// 切换折叠状态
|
||
const onToggleCollapse = (id) => {
|
||
const area = floatingAreas.value.find(a => a.id === id)
|
||
if (area) {
|
||
area.collapsed = !area.collapsed
|
||
}
|
||
}
|
||
|
||
// 最大化/还原
|
||
const onMaximize = (panelId) => {
|
||
// 查找包含该面板的区域
|
||
for (const area of floatingAreas.value) {
|
||
if (area.panels && area.panels.length === 1 && area.panels[0].id === panelId) {
|
||
// 当区域只包含一个Panel时,切换Area的最大化状态并同时最大化Panel
|
||
if (area.WindowState === '最大化' || area.WindowState === 'maximized') {
|
||
area.WindowState = '正常'
|
||
} else {
|
||
area.WindowState = '最大化'
|
||
}
|
||
console.log('Panel最大化按钮触发,切换Area状态:', area.WindowState)
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
// 关闭浮动区域
|
||
const onCloseFloatingArea = (id) => {
|
||
const index = floatingAreas.value.findIndex(a => a.id === id)
|
||
if (index !== -1) {
|
||
floatingAreas.value.splice(index, 1)
|
||
}
|
||
}
|
||
|
||
// 关闭面板
|
||
const onClosePanel = (areaId, panelId) => {
|
||
const area = floatingAreas.value.find(a => a.id === areaId)
|
||
if (area && area.panels) {
|
||
const panelIndex = area.panels.findIndex(p => p.id === panelId)
|
||
if (panelIndex !== -1) {
|
||
area.panels.splice(panelIndex, 1)
|
||
|
||
// 如果区域内没有面板了,可以考虑关闭整个区域
|
||
if (area.panels.length === 0) {
|
||
onCloseFloatingArea(areaId)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 切换工具栏
|
||
const onToggleToolbar = (id) => {
|
||
const area = floatingAreas.value.find(a => a.id === id)
|
||
if (area) {
|
||
area.toolbarExpanded = !area.toolbarExpanded
|
||
}
|
||
}
|
||
|
||
// Panel拖拽开始
|
||
const onPanelDragStart = (areaId, event) => {
|
||
const area = floatingAreas.value.find(a => a.id === areaId)
|
||
// 只有当Area中只有一个Panel时才允许通过Panel标题栏移动Area
|
||
if (area && area.panels.length === 1) {
|
||
panelDragState.value.isDragging = true
|
||
panelDragState.value.currentAreaId = areaId
|
||
panelDragState.value.startClientPos = {
|
||
x: event.clientX,
|
||
y: event.clientY
|
||
}
|
||
panelDragState.value.startAreaPos = {
|
||
x: area.x,
|
||
y: area.y
|
||
}
|
||
console.log('Panel拖拽开始,移动Area:', areaId)
|
||
}
|
||
}
|
||
|
||
// Panel拖拽移动
|
||
const onPanelDragMove = (areaId, event) => {
|
||
if (panelDragState.value.isDragging && panelDragState.value.currentAreaId === areaId) {
|
||
const area = floatingAreas.value.find(a => a.id === areaId)
|
||
if (area) {
|
||
// 计算移动距离
|
||
const deltaX = event.clientX - panelDragState.value.startClientPos.x
|
||
const deltaY = event.clientY - panelDragState.value.startClientPos.y
|
||
|
||
// 计算新位置
|
||
let newLeft = panelDragState.value.startAreaPos.x + deltaX
|
||
let newTop = panelDragState.value.startAreaPos.y + deltaY
|
||
|
||
// 确保不超出父容器边界
|
||
if (dockLayoutRef.value) {
|
||
const parentRect = dockLayoutRef.value.getBoundingClientRect()
|
||
|
||
// 严格边界检查
|
||
newLeft = Math.max(0, Math.min(newLeft, parentRect.width - area.width))
|
||
newTop = Math.max(0, Math.min(newTop, parentRect.height - area.height))
|
||
}
|
||
|
||
// 更新位置
|
||
area.x = newLeft
|
||
area.y = newTop
|
||
|
||
// 调试信息
|
||
console.log('Panel拖拽移动,Area新位置:', { x: newLeft, y: newTop })
|
||
}
|
||
}
|
||
}
|
||
|
||
// Panel拖拽结束
|
||
const onPanelDragEnd = () => {
|
||
console.log('Panel拖拽结束')
|
||
panelDragState.value.isDragging = false
|
||
panelDragState.value.currentAreaId = null
|
||
}
|
||
|
||
// 暴露方法给父组件
|
||
defineExpose({
|
||
addFloatingPanel
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.dock-layout {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 100%;
|
||
overflow: visible;
|
||
}
|
||
|
||
/* 浮动区域样式已直接应用到Area组件 */
|
||
|
||
/* 添加浮动区域按钮样式 */
|
||
.add-floating-btn {
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
user-select: none;
|
||
}
|
||
|
||
.add-floating-btn:active {
|
||
transform: scale(0.98);
|
||
}
|
||
</style> |