拖拽Area停靠中心区域

This commit is contained in:
zqm
2025-11-17 10:59:46 +08:00
parent b4b2e75fef
commit 8e472d4497
4 changed files with 525 additions and 26 deletions

View File

@@ -99,14 +99,52 @@
<!-- 内容区域 -->
<div class="vs-content">
<!-- 这里是内容区域使用slot接收子组件并监听Panel的maximize事件 -->
<slot @maximize="onPanelMaximize"></slot>
<!-- 这里是内容区域优先显示slot内容如果没有slot内容则显示接收到的外部内容 -->
<template v-if="$slots.default">
<slot @maximize="onPanelMaximize"></slot>
</template>
<!-- 直接显示接收到的外部TabPage内容不需要额外包装 -->
<template v-else-if="receivedContent.length > 0">
<TabPage
v-for="(item, index) in receivedContent"
:key="`received-tab-${index}`"
:id="item.tabPage.id"
:title="item.tabPage.title"
:panels="item.tabPage.panels"
:tabPosition="'bottom'"
@tabDragStart="() => {}"
@tabDragMove="() => {}"
@tabDragEnd="() => {}"
>
<!-- 在TabPage内渲染其包含的Panels -->
<Panel
v-for="panel in item.tabPage.panels"
:key="`received-panel-${index}-${panel.id}`"
:id="panel.id"
:title="panel.title"
:collapsed="panel.collapsed"
:toolbarExpanded="panel.toolbarExpanded"
:maximized="panel.maximized"
@toggleCollapse="() => {}"
@maximize="onPanelMaximize"
@close="() => {}"
@toggleToolbar="() => {}"
@dragStart="() => {}"
@dragMove="() => {}"
@dragEnd="() => {}"
@dragover="handleDragOver"
@dragleave="handleDragLeave"
/>
</TabPage>
</template>
</div>
</div>
</template>
<script setup>
import { defineProps, computed, defineEmits, ref, onMounted, watch } from 'vue'
import { defineProps, computed, defineEmits, ref, onMounted, watch, defineExpose } from 'vue'
import TabPage from './TabPage.vue'
import Panel from './Panel.vue'
const props = defineProps({
id: { type: String, required: true },
@@ -137,6 +175,9 @@ const originalPosition = ref({
// 保存最大化前的位置和大小,用于还原
const maximizedFromPosition = ref(null)
// 存储接收到的外部Area内容
const receivedContent = ref([])
// 拖拽相关状态
const isDragging = ref(false)
const dragStartPos = ref({ x: 0, y: 0 })
@@ -527,6 +568,61 @@ onMounted(() => {
})
}
})
// 添加Area内容的方法用于主区域的Area接收外部Area内容
const appendAreaContent = (sourceArea) => {
console.log(`[Area] ${props.id} 接收到内容移动请求:`, sourceArea)
if (!sourceArea) {
console.warn('[Area] 源Area为空无法接收内容')
return false
}
try {
// 清空之前的内容确保只接收一个新的TabPage
receivedContent.value = []
// 处理源Area的tabPages只接收第一个TabPage用户要求只能接收一个TabPage
if (sourceArea.tabPages && sourceArea.tabPages.length > 0) {
const tabPage = sourceArea.tabPages[0] // 只接收第一个TabPage
if (tabPage) {
// 确保所有Panel都处于最大化状态显示还原按钮
const maximizedPanels = (tabPage.panels || []).map(panel => ({
...panel,
maximized: true // 设置为最大化状态
}))
receivedContent.value.push({
id: `received-${tabPage.id || Date.now() + Math.random()}`,
title: tabPage.title || '标签页',
tabPage: {
...tabPage,
panels: maximizedPanels // 使用最大化状态的Panel
},
panels: maximizedPanels
})
console.log(`[Area] 成功接收tabPage: ${tabPage.title}并设置Panel为最大化状态`)
}
} else {
console.warn('[Area] 源Area中没有tabPages数据')
}
console.log(`[Area] ${props.id} 当前接收内容数量: ${receivedContent.value.length}`)
return true
} catch (error) {
console.error('[Area] 接收外部内容时出错:', error)
return false
}
}
// 暴露方法给父组件调用
defineExpose({
appendAreaContent,
id: props.id,
title: props.title,
isMaximized: isMaximized.value
})
</script>
<style scoped>
@@ -784,4 +880,55 @@ onMounted(() => {
align-items: stretch;
justify-content: stretch;
}
/* 接收到的外部内容样式 */
.received-content {
width: 100%;
height: 100%;
overflow: auto;
background: #f8f9ff;
border: 1px solid #e0e6f0;
border-radius: 4px;
}
.received-item {
background: white;
border: 1px solid #d0d7e2;
border-radius: 6px;
margin: 8px;
padding: 12px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
transition: box-shadow 0.2s ease;
}
.received-item:hover {
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
.received-title {
font-size: 13px;
font-weight: 600;
color: #2c3e7a;
margin-bottom: 8px;
padding-bottom: 6px;
border-bottom: 1px solid #e8edf7;
}
.received-body {
min-height: 60px;
}
/* TabPage和Panel容器样式 */
.tab-page-container,
.area-container {
width: 100%;
height: 100%;
border: 1px dashed #c7d2ea;
border-radius: 4px;
padding: 12px;
background: #fafbff;
color: #6b7aa9;
text-align: center;
font-style: italic;
}
</style>