中心区依靠多个TabPanel
This commit is contained in:
@@ -125,6 +125,7 @@
|
|||||||
:collapsed="panel.collapsed"
|
:collapsed="panel.collapsed"
|
||||||
:toolbarExpanded="panel.toolbarExpanded"
|
:toolbarExpanded="panel.toolbarExpanded"
|
||||||
:maximized="panel.maximized"
|
:maximized="panel.maximized"
|
||||||
|
:content="panel.content"
|
||||||
@toggleCollapse="() => {}"
|
@toggleCollapse="() => {}"
|
||||||
@maximize="onPanelMaximize"
|
@maximize="onPanelMaximize"
|
||||||
@close="() => {}"
|
@close="() => {}"
|
||||||
@@ -262,7 +263,7 @@ const areaStyle = computed(() => {
|
|||||||
return style
|
return style
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['close', 'update:WindowState', 'update:position', 'dragover', 'dragleave', 'areaDragStart', 'areaDragMove', 'areaDragEnd'])
|
const emit = defineEmits(['close', 'update:WindowState', 'update:position', 'dragover', 'dragleave', 'areaDragStart', 'areaDragMove', 'areaDragEnd', 'area-merged'])
|
||||||
|
|
||||||
// 处理Panel的最大化事件
|
// 处理Panel的最大化事件
|
||||||
const onPanelMaximize = (panelId) => {
|
const onPanelMaximize = (panelId) => {
|
||||||
@@ -569,56 +570,121 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 添加Area内容的方法,用于主区域的Area接收外部Area内容
|
|
||||||
const appendAreaContent = (sourceArea) => {
|
|
||||||
console.log(`[Area] ${props.id} 接收到内容移动请求:`, sourceArea)
|
// 合并Area内容的方法,只保留合并逻辑
|
||||||
|
const mergeAreaContent = (sourceArea) => {
|
||||||
|
console.log(`[Area] ${props.id} 接收到Area合并请求:`, sourceArea)
|
||||||
|
|
||||||
if (!sourceArea) {
|
if (!sourceArea) {
|
||||||
console.warn('[Area] 源Area为空,无法接收内容')
|
console.warn('[Area] 源Area为空,无法合并内容')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 清空之前的内容,确保只接收一个新的TabPage
|
const isEmpty = receivedContent.value.length === 0
|
||||||
receivedContent.value = []
|
|
||||||
|
|
||||||
// 处理源Area的tabPages,只接收第一个TabPage(用户要求只能接收一个TabPage)
|
if (isEmpty) {
|
||||||
if (sourceArea.tabPages && sourceArea.tabPages.length > 0) {
|
// 4.2.1 如果目标Area内容区为空,将源Area内容区的子组件添加到目标Area内容区
|
||||||
const tabPage = sourceArea.tabPages[0] // 只接收第一个TabPage
|
console.log('[Area] 目标Area为空,添加源Area的子组件')
|
||||||
if (tabPage) {
|
|
||||||
// 确保所有Panel都处于最大化状态,显示还原按钮
|
|
||||||
const maximizedPanels = (tabPage.panels || []).map(panel => ({
|
|
||||||
...panel,
|
|
||||||
maximized: true // 设置为最大化状态
|
|
||||||
}))
|
|
||||||
|
|
||||||
receivedContent.value.push({
|
// 处理源Area的所有tabPages
|
||||||
id: `received-${tabPage.id || Date.now() + Math.random()}`,
|
if (sourceArea.tabPages && sourceArea.tabPages.length > 0) {
|
||||||
title: tabPage.title || '标签页',
|
sourceArea.tabPages.forEach((tabPage, tabIndex) => {
|
||||||
tabPage: {
|
const newTabPageId = `merged-tabpage-${Date.now()}-${tabIndex}`
|
||||||
...tabPage,
|
const newPanels = (tabPage.panels || []).map((panel, panelIndex) => {
|
||||||
panels: maximizedPanels // 使用最大化状态的Panel
|
const newPanelId = `merged-panel-${Date.now()}-${tabIndex}-${panelIndex}`
|
||||||
},
|
console.log(`[Area] 添加Panel: ${panel.id} -> ${newPanelId}`)
|
||||||
panels: maximizedPanels
|
return {
|
||||||
|
...panel,
|
||||||
|
id: newPanelId,
|
||||||
|
maximized: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
receivedContent.value.push({
|
||||||
|
id: `received-${newTabPageId}`,
|
||||||
|
title: tabPage.title || `标签页${tabIndex + 1}`,
|
||||||
|
tabPage: {
|
||||||
|
...tabPage,
|
||||||
|
id: newTabPageId,
|
||||||
|
panels: newPanels
|
||||||
|
},
|
||||||
|
panels: newPanels
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(`[Area] 成功添加TabPage: ${tabPage.title} (${newPanels.length} 个Panel)`)
|
||||||
})
|
})
|
||||||
console.log(`[Area] 成功接收tabPage: ${tabPage.title},并设置Panel为最大化状态`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 触发事件通知父组件将源Area保存到隐藏列表
|
||||||
|
emit('area-merged', {
|
||||||
|
sourceArea: sourceArea,
|
||||||
|
targetAreaId: props.id,
|
||||||
|
targetAreaHasContent: false, // 目标Area为空
|
||||||
|
operation: 'add-children',
|
||||||
|
addedTabPages: receivedContent.value
|
||||||
|
})
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
console.warn('[Area] 源Area中没有tabPages数据')
|
// 4.2.2 如果目标Area内容区已包含TabPage,将源Area的每个TabPage添加到目标Area的TabPage中
|
||||||
|
console.log('[Area] 目标Area已有TabPage,合并TabPage标签页')
|
||||||
|
|
||||||
|
// 获取第一个现有的TabPage作为合并目标
|
||||||
|
const existingTabPage = receivedContent.value[0]
|
||||||
|
if (!existingTabPage) {
|
||||||
|
console.error('[Area] 现有TabPage不存在')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理源Area的所有tabPages
|
||||||
|
if (sourceArea.tabPages && sourceArea.tabPages.length > 0) {
|
||||||
|
sourceArea.tabPages.forEach((sourceTabPage, tabIndex) => {
|
||||||
|
if (sourceTabPage && sourceTabPage.panels) {
|
||||||
|
// 为每个Panel创建新的唯一ID避免冲突
|
||||||
|
const newPanels = sourceTabPage.panels.map((panel, panelIndex) => {
|
||||||
|
const newPanelId = `merged-panel-${Date.now()}-${tabIndex}-${panelIndex}`
|
||||||
|
console.log(`[Area] 合并Panel到现有TabPage: ${panel.id} -> ${newPanelId}`)
|
||||||
|
return {
|
||||||
|
...panel,
|
||||||
|
id: newPanelId,
|
||||||
|
maximized: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 将新的Panel添加到现有TabPage
|
||||||
|
existingTabPage.tabPage.panels.push(...newPanels)
|
||||||
|
existingTabPage.panels.push(...newPanels)
|
||||||
|
|
||||||
|
console.log(`[Area] 成功合并 ${newPanels.length} 个Panel到现有TabPage`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 触发事件通知父组件将源Area及其TabPage组件保存到隐藏列表
|
||||||
|
emit('area-merged', {
|
||||||
|
sourceArea: sourceArea,
|
||||||
|
targetAreaId: props.id,
|
||||||
|
targetAreaHasContent: true, // 目标Area已有内容
|
||||||
|
operation: 'merge-tabpages',
|
||||||
|
sourceTabPages: sourceArea.tabPages || []
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(`[Area] 合并完成,现有TabPage共有 ${existingTabPage.tabPage.panels.length} 个Panel`)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[Area] ${props.id} 当前接收内容数量: ${receivedContent.value.length}`)
|
|
||||||
return true
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Area] 接收外部内容时出错:', error)
|
console.error('[Area] 合并Area内容时出错:', error)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 暴露方法给父组件调用
|
// 暴露方法给父组件调用
|
||||||
defineExpose({
|
defineExpose({
|
||||||
appendAreaContent,
|
mergeAreaContent, // 合并Area内容的方法
|
||||||
id: props.id,
|
id: props.id,
|
||||||
title: props.title,
|
title: props.title,
|
||||||
isMaximized: isMaximized.value
|
isMaximized: isMaximized.value
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
:style="{ position: 'relative', width: '100%', height: '100%', zIndex: 1 }"
|
:style="{ position: 'relative', width: '100%', height: '100%', zIndex: 1 }"
|
||||||
@dragover="handleMainAreaDragOver"
|
@dragover="handleMainAreaDragOver"
|
||||||
@dragleave="handleMainAreaDragLeave"
|
@dragleave="handleMainAreaDragLeave"
|
||||||
|
@area-merged="onAreaMerged"
|
||||||
>
|
>
|
||||||
<!-- 这里可以放置主区域的内容 -->
|
<!-- 这里可以放置主区域的内容 -->
|
||||||
</Area>
|
</Area>
|
||||||
@@ -76,6 +77,7 @@
|
|||||||
:collapsed="panel.collapsed"
|
:collapsed="panel.collapsed"
|
||||||
:toolbarExpanded="panel.toolbarExpanded"
|
:toolbarExpanded="panel.toolbarExpanded"
|
||||||
:maximized="panel.maximized"
|
:maximized="panel.maximized"
|
||||||
|
:content="panel.content"
|
||||||
@toggleCollapse="onToggleCollapse"
|
@toggleCollapse="onToggleCollapse"
|
||||||
@maximize="onMaximize"
|
@maximize="onMaximize"
|
||||||
@close="onClosePanel(area.id, panel.id)"
|
@close="onClosePanel(area.id, panel.id)"
|
||||||
@@ -180,6 +182,44 @@ const checkMainContentForAreas = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成随机测试内容用于合并测试
|
||||||
|
* @param {number} panelIndex - 面板索引
|
||||||
|
* @returns {Object} 包含随机内容的对象
|
||||||
|
*/
|
||||||
|
const generateRandomContent = (panelIndex) => {
|
||||||
|
const contentTypes = ['图表', '数据', '配置', '文档', '代码', '日志']
|
||||||
|
const randomType = contentTypes[Math.floor(Math.random() * contentTypes.length)]
|
||||||
|
const colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD']
|
||||||
|
const randomColor = colors[Math.floor(Math.random() * colors.length)]
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: randomType,
|
||||||
|
color: randomColor,
|
||||||
|
index: panelIndex,
|
||||||
|
title: `${randomType}面板 ${panelIndex + 1}`,
|
||||||
|
data: generateSampleData(panelIndex),
|
||||||
|
timestamp: new Date().toLocaleString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成样本数据
|
||||||
|
* @param {number} dataIndex - 数据索引
|
||||||
|
* @returns {Array} 样本数据数组
|
||||||
|
*/
|
||||||
|
const generateSampleData = (dataIndex) => {
|
||||||
|
const data = []
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
data.push({
|
||||||
|
label: `项目 ${String.fromCharCode(65 + i)}`,
|
||||||
|
value: Math.floor(Math.random() * 100) + 10,
|
||||||
|
id: `data-${dataIndex}-${i}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
// 添加新的浮动面板
|
// 添加新的浮动面板
|
||||||
const addFloatingPanel = () => {
|
const addFloatingPanel = () => {
|
||||||
// 获取父容器尺寸以计算居中位置
|
// 获取父容器尺寸以计算居中位置
|
||||||
@@ -217,13 +257,15 @@ const addFloatingPanel = () => {
|
|||||||
panels: [
|
panels: [
|
||||||
{
|
{
|
||||||
id: `panel-${areaIdCounter - 1}-1-1`,
|
id: `panel-${areaIdCounter - 1}-1-1`,
|
||||||
title: `面板 1`,
|
title: `面板 ${areaIdCounter - 1}`,
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 100,
|
height: 100,
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
toolbarExpanded: false
|
toolbarExpanded: false,
|
||||||
|
// 添加随机测试内容以便合并测试
|
||||||
|
content: generateRandomContent(areaIdCounter - 1)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -354,6 +396,41 @@ const addAreaToHiddenList = (area) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理Area合并事件
|
||||||
|
const onAreaMerged = (eventData) => {
|
||||||
|
console.log('处理Area合并事件:', eventData)
|
||||||
|
|
||||||
|
const { sourceArea, targetAreaHasContent } = eventData
|
||||||
|
|
||||||
|
// 获取源Area对象
|
||||||
|
const sourceAreaObj = floatingAreas.value.find(a => a.id === sourceArea.id)
|
||||||
|
if (!sourceAreaObj) {
|
||||||
|
console.warn('找不到源Area:', sourceArea.id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据目标Area内容状态执行不同的隐藏逻辑
|
||||||
|
if (targetAreaHasContent) {
|
||||||
|
// 目标Area已有内容:保存源Area及其TabPage组件到隐藏列表
|
||||||
|
console.log('目标Area已有内容,保存源Area和TabPage组件到隐藏列表')
|
||||||
|
addAreaToHiddenList({
|
||||||
|
...sourceAreaObj,
|
||||||
|
tabPages: [...sourceAreaObj.tabPages] // 深拷贝TabPages
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 目标Area为空:仅保存源Area到隐藏列表
|
||||||
|
console.log('目标Area为空,保存源Area到隐藏列表')
|
||||||
|
addAreaToHiddenList(sourceAreaObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从浮动区域中移除已合并的源Area
|
||||||
|
const sourceIndex = floatingAreas.value.findIndex(a => a.id === sourceArea.id)
|
||||||
|
if (sourceIndex !== -1) {
|
||||||
|
floatingAreas.value.splice(sourceIndex, 1)
|
||||||
|
console.log('源Area已从浮动区域移除:', sourceArea.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Panel拖拽开始
|
// Panel拖拽开始
|
||||||
const onPanelDragStart = (areaId, event) => {
|
const onPanelDragStart = (areaId, event) => {
|
||||||
const area = floatingAreas.value.find(a => a.id === areaId)
|
const area = floatingAreas.value.find(a => a.id === areaId)
|
||||||
@@ -909,7 +986,7 @@ const onPanelMaximizeSync = ({ areaId, maximized }) => {
|
|||||||
* 检查Area是否可以停靠到中心区域
|
* 检查Area是否可以停靠到中心区域
|
||||||
* @param {Object} sourceArea - 源Area对象
|
* @param {Object} sourceArea - 源Area对象
|
||||||
* @param {Object} targetArea - 目标Area对象(主区域)
|
* @param {Object} targetArea - 目标Area对象(主区域)
|
||||||
* @returns {Object} 验证结果 {canDock: boolean, reason: string}
|
* @returns {Object} 验证结果 {canDock: boolean, reason: string, strategy: string}
|
||||||
*/
|
*/
|
||||||
const canDockToCenter = (sourceArea, targetArea) => {
|
const canDockToCenter = (sourceArea, targetArea) => {
|
||||||
// 验证1:检查源Area是否包含TabPage
|
// 验证1:检查源Area是否包含TabPage
|
||||||
@@ -949,69 +1026,248 @@ const canDockToCenter = (sourceArea, targetArea) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 所有验证通过
|
// 5.2. 新增验证:根据目标Area状态确定停靠策略
|
||||||
|
// 检查主区域内容状态
|
||||||
|
checkMainContentForAreas()
|
||||||
|
|
||||||
|
// 如果主区域已有内容,判断停靠策略
|
||||||
|
if (hasAreasInMainContent.value) {
|
||||||
|
// 检查主区域是否已有TabPage
|
||||||
|
const mainAreaElement = mainAreaRef.value?.$el
|
||||||
|
if (mainAreaElement) {
|
||||||
|
const existingTabPages = mainAreaElement.querySelectorAll('.tab-page, [class*="tab"]')
|
||||||
|
const hasExistingTabPage = existingTabPages.length > 0
|
||||||
|
|
||||||
|
if (hasExistingTabPage && sourceArea.tabPages.length === 1) {
|
||||||
|
// 场景5.2:源Area只有一个TabPage,目标Area也有TabPage
|
||||||
|
// 策略:合并TabPage标签页
|
||||||
|
return {
|
||||||
|
canDock: true,
|
||||||
|
reason: '可以停靠 - 采用TabPage标签页合并策略',
|
||||||
|
strategy: 'merge-tabpages'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 其他场景:采用简单Area内容移动策略
|
||||||
|
return {
|
||||||
|
canDock: true,
|
||||||
|
reason: '可以停靠 - 采用简单移动策略',
|
||||||
|
strategy: 'simple-move'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主区域为空的情况:采用简单移动策略
|
||||||
return {
|
return {
|
||||||
canDock: true,
|
canDock: true,
|
||||||
reason: '验证通过,可以停靠到中心区域'
|
reason: '验证通过,可以停靠到中心区域',
|
||||||
|
strategy: 'simple-move'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 第3步:中心停靠逻辑
|
// 3.3 创建handleCenterDocking核心处理函数
|
||||||
/**
|
/**
|
||||||
* 处理中心停靠的核心函数
|
* 处理中心停靠逻辑
|
||||||
* @param {string} areaId - 源Area的ID
|
* @param {string} sourceAreaId - 源Area的ID
|
||||||
* @returns {Object} 处理结果 {success: boolean, message: string}
|
* @returns {Object} 处理结果 {success: boolean, message: string}
|
||||||
*/
|
*/
|
||||||
const handleCenterDocking = (areaId) => {
|
const handleCenterDocking = (sourceAreaId) => {
|
||||||
|
console.log('开始处理中心停靠:', sourceAreaId)
|
||||||
|
|
||||||
|
// 1. 查找源Area
|
||||||
|
const sourceArea = floatingAreas.value.find(a => a.id === sourceAreaId)
|
||||||
|
if (!sourceArea) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `未找到ID为 ${sourceAreaId} 的源Area`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 验证停靠条件
|
||||||
|
const validationResult = canDockToCenter(sourceArea, mainAreaRef.value)
|
||||||
|
if (!validationResult.canDock) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `停靠验证失败: ${validationResult.reason}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('停靠验证通过:', validationResult.reason)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 3.4 查找源Area
|
// 3. 根据策略执行停靠逻辑
|
||||||
const sourceArea = floatingAreas.value.find(a => a.id === areaId)
|
if (validationResult.strategy === 'merge-tabpages') {
|
||||||
if (!sourceArea) {
|
// 5.2. 策略:合并TabPage标签页
|
||||||
return {
|
return handleMergeTabpagesDocking(sourceArea)
|
||||||
success: false,
|
|
||||||
message: '找不到指定的Area'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2.1-2.5 验证是否可以停靠到中心
|
|
||||||
const validation = canDockToCenter(sourceArea, mainAreaRef.value)
|
|
||||||
if (!validation.canDock) {
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
message: `验证失败:${validation.reason}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3.4 实现Area子组件移动到主区域的逻辑
|
|
||||||
// 将Area的内容移动到主区域(通过主区域Ref处理)
|
|
||||||
if (mainAreaRef.value && mainAreaRef.value.appendAreaContent) {
|
|
||||||
mainAreaRef.value.appendAreaContent(sourceArea)
|
|
||||||
} else {
|
} else {
|
||||||
console.warn('主区域Ref不可用,无法移动内容')
|
// 默认策略:简单Area内容移动
|
||||||
|
return handleSimpleAreaDocking(sourceArea)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('停靠处理过程中发生错误:', error)
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `停靠过程中发生错误: ${error.message}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5.2. 新增:处理TabPage标签页合并策略
|
||||||
|
/**
|
||||||
|
* 处理TabPage标签页合并策略
|
||||||
|
* 当源Area只有一个TabPage且目标Area已有TabPage时使用此策略
|
||||||
|
* @param {Object} sourceArea - 源Area对象
|
||||||
|
* @returns {Object} 处理结果 {success: boolean, message: string}
|
||||||
|
*/
|
||||||
|
// 合并TabPage标签页的核心处理函数
|
||||||
|
const handleMergeTabpagesDocking = (sourceArea) => {
|
||||||
|
console.log(`[DockLayout] 开始合并TabPage标签页,源Area:`, sourceArea)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 验证源Area是否有效
|
||||||
|
if (!sourceArea || !sourceArea.tabPages || sourceArea.tabPages.length === 0) {
|
||||||
|
console.warn('[DockLayout] 源Area无效或没有TabPage,无法执行合并操作')
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3.5 实现Area添加到隐藏列表的逻辑
|
// 查找主区域的Area组件
|
||||||
addAreaToHiddenList(sourceArea)
|
const mainArea = mainAreaRef.value
|
||||||
|
if (!mainArea) {
|
||||||
// 3.6 实现Area从浮动区域移除的逻辑
|
console.error('[DockLayout] 找不到主区域引用')
|
||||||
const index = floatingAreas.value.findIndex(a => a.id === areaId)
|
return null
|
||||||
if (index !== -1) {
|
|
||||||
floatingAreas.value.splice(index, 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3.7 更新主区域和浮动区域的响应式状态
|
console.log('[DockLayout] 使用新的mergeAreaContent方法合并TabPage')
|
||||||
checkMainContentForAreas()
|
const success = mainArea.mergeAreaContent(sourceArea)
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
const newTabPageId = `merged-${Date.now()}`
|
||||||
|
const panelsCount = sourceArea.tabPages[0]?.panels?.length || 0
|
||||||
|
console.log(`[DockLayout] 成功合并 ${panelsCount} 个Panel到主区域TabPage: ${newTabPageId}`)
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
newTabPageId: newTabPageId,
|
||||||
|
panelsAdded: panelsCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error('[DockLayout] mergeAreaContent方法执行失败')
|
||||||
|
return null
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[DockLayout] 合并TabPage时发生错误:', error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在主区域查找或创建TabPage
|
||||||
|
* @returns {Object} 查找结果 {success: boolean, message: string, tabPage?: Object}
|
||||||
|
*/
|
||||||
|
const findOrCreateMainAreaTabPage = () => {
|
||||||
|
try {
|
||||||
|
// 检查主区域是否已经接收了内容
|
||||||
|
if (mainAreaRef.value && mainAreaRef.value.receivedContent) {
|
||||||
|
const existingContent = mainAreaRef.value.receivedContent
|
||||||
|
console.log('检查主区域已接收内容,数量:', existingContent.length)
|
||||||
|
|
||||||
|
if (existingContent.length > 0) {
|
||||||
|
// 找到现有的TabPage内容
|
||||||
|
const existingTabPage = existingContent[0] // 只使用第一个TabPage
|
||||||
|
console.log('找到现有的TabPage:', existingTabPage.title)
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: '找到现有的TabPage',
|
||||||
|
tabPage: existingTabPage.tabPage,
|
||||||
|
rawTabPage: existingTabPage // 返回原始TabPage对象以供修改
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果主区域没有内容,现在不再主动创建空TabPage
|
||||||
|
// 因为在合并逻辑中,Area会自动根据需要创建TabPage
|
||||||
|
console.log('主区域没有现有TabPage,将通过合并操作自动创建')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: '成功停靠到中心区域'
|
message: '主区域为空,等待合并操作创建TabPage',
|
||||||
|
tabPage: null,
|
||||||
|
rawTabPage: null
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('中心停靠处理错误:', error)
|
console.error('查找TabPage时发生错误:', error)
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: `停靠处理出错:${error.message}`
|
message: `查找TabPage失败: ${error.message}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3.4 实现简单Area停靠策略(重构为使用合并逻辑)
|
||||||
|
/**
|
||||||
|
* 处理简单Area停靠策略(Area内容合并)
|
||||||
|
* 当主区域为空或不符合TabPage合并条件时使用此策略
|
||||||
|
* @param {Object} sourceArea - 源Area对象
|
||||||
|
* @returns {Object} 处理结果 {success: boolean, message: string}
|
||||||
|
*/
|
||||||
|
const handleSimpleAreaDocking = (sourceArea) => {
|
||||||
|
console.log('执行简单Area停靠策略(使用合并逻辑)')
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 验证源Area结构
|
||||||
|
if (!sourceArea.tabPages || sourceArea.tabPages.length === 0) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: '源Area不包含TabPage,无法进行停靠合并'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('源Area结构验证通过,包含', sourceArea.tabPages.length, '个TabPage')
|
||||||
|
|
||||||
|
// 2. 使用mergeAreaContent方法将源Area合并到主区域
|
||||||
|
if (mainAreaRef.value && typeof mainAreaRef.value.mergeAreaContent === 'function') {
|
||||||
|
console.log('通过mergeAreaContent方法合并源Area到主区域')
|
||||||
|
const result = mainAreaRef.value.mergeAreaContent(sourceArea)
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
console.log('源Area成功合并到主区域,area-merged事件将处理隐藏列表操作')
|
||||||
|
|
||||||
|
// 3. 更新主区域状态(合并完成后由事件处理函数处理隐藏列表)
|
||||||
|
nextTick(() => {
|
||||||
|
checkMainContentForAreas()
|
||||||
|
// 确保主区域最大化显示还原按钮
|
||||||
|
if (mainAreaRef.value) {
|
||||||
|
mainAreaRef.value.WindowState = '最大化'
|
||||||
|
console.log('主区域状态已设置为最大化')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: `成功合并 ${sourceArea.tabPages.length} 个TabPage到主区域`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: 'mergeAreaContent方法执行失败'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('主区域不支持mergeAreaContent方法')
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: '主区域不支持合并操作'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('简单Area停靠策略执行时发生错误:', error)
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `简单Area停靠失败: ${error.message}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,41 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 内容区:可折叠 -->
|
<!-- 内容区:可折叠 -->
|
||||||
<div class="content-area bg-[#f5f7fb] flex-1" v-show="!collapsed"></div>
|
<div class="content-area bg-[#f5f7fb] flex-1 p-4" v-show="!collapsed">
|
||||||
|
<div v-if="content" class="panel-content">
|
||||||
|
<div class="mb-4">
|
||||||
|
<h3 class="text-lg font-bold mb-2" :style="{ color: content.color }">
|
||||||
|
{{ content.title }}
|
||||||
|
</h3>
|
||||||
|
<p class="text-sm text-gray-600 mb-3">
|
||||||
|
类型:{{ content.type }} | 创建时间:{{ content.timestamp }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-3">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in content.data"
|
||||||
|
:key="item.id"
|
||||||
|
class="data-item p-3 border rounded-lg shadow-sm"
|
||||||
|
:style="{
|
||||||
|
borderLeftColor: content.color,
|
||||||
|
borderLeftWidth: '4px'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="font-medium">{{ item.label }}</span>
|
||||||
|
<span class="text-sm font-bold" :style="{ color: content.color }">
|
||||||
|
{{ item.value }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="text-gray-500 text-center">
|
||||||
|
<p>暂无内容</p>
|
||||||
|
<div class="mt-2 text-xs">面板ID: {{ id }} - 标题: {{ title }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -116,6 +150,10 @@ const props = defineProps({
|
|||||||
maximized: {
|
maximized: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -35,10 +35,9 @@
|
|||||||
1. 当一个Panel被拖动时,显示停靠指示器。
|
1. 当一个Panel被拖动时,显示停靠指示器。
|
||||||
2. 当拖动Panel到指示器时,显示停靠区。
|
2. 当拖动Panel到指示器时,显示停靠区。
|
||||||
3. 当主区域内没有其他Area时,隐藏外部边缘指示器、中心区域容器,只显示中心指示器。
|
3. 当主区域内没有其他Area时,隐藏外部边缘指示器、中心区域容器,只显示中心指示器。
|
||||||
4. 当将源Panel拖动到中心指示器时
|
4. 当将源Area拖动到中心指示器时
|
||||||
4.1. 如果源Area只有一个直接子组件(TabPage),且目标Area内容区为空,则将源Area的子组件停靠到中心区域。这个源Area保存到DockLayout的的隐藏列表中。
|
4.1. Area的merge行为只接受一个Area参数.
|
||||||
5. 当将源Area拖动到中心指示器时
|
4.1. 如果目标Area内容区为空,则将源Area内容区的子组件添加到目标Area内容区。这个源Area保存到DockLayout的的隐藏列表中。
|
||||||
5.1. 如果源Area只有一个直接子组件(TabPage),且目标Area内容区为空,则将源Area的子组件停靠到中心区域。这个源Area保存到DockLayout的的隐藏列表中。
|
4.2. 如果目标Area内容区已经包含一个TabPage,则将源Area的TabPage组件的每个标签页添加到目标Area的Tabpage中。这个源Area和源Area的TabPage组件保存到DockLayout的的隐藏列表中。
|
||||||
5.2. 如果源Area只有一个直接子组件(TabPage),且目标Area内容区已经包含一个TabPage,则将源Area的TabPage组件的每个标签页移动并添加到目标Area的Tabpage中。这个源Area保存到DockLayout的的隐藏列表中。
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user