163 lines
4.1 KiB
Vue
163 lines
4.1 KiB
Vue
<template>
|
||
<div
|
||
v-if="panels.length > 0"
|
||
:class="containerClasses"
|
||
:style="containerStyle"
|
||
class="overflow-hidden"
|
||
>
|
||
<template v-for="(panel, index) in panels" :key="panel.id">
|
||
<DockPanel
|
||
:panel="panel"
|
||
:position="position"
|
||
:index="index"
|
||
@close="closePanel(panel.id)"
|
||
@float="floatPanel(panel.id)"
|
||
@dock="dockPanel"
|
||
@toggleCollapse="toggleCollapse(panel.id)"
|
||
@titleBarDragStart="handlePanelTitleBarDragStart(panel.id, $event)"
|
||
/>
|
||
<!-- 面板间分隔条 - 不显示在最后一个面板旁 -->
|
||
<div
|
||
v-if="index < panels.length - 1"
|
||
:class="separatorClasses"
|
||
@mousedown="startPanelResize(panel.id, index, $event)"
|
||
/>
|
||
</template>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed } from 'vue';
|
||
import DockPanel from './DockPanel.js';
|
||
|
||
// 定义组件属性
|
||
const props = defineProps({
|
||
// 面板位置(left, right, top, bottom)
|
||
position: {
|
||
type: String,
|
||
required: true,
|
||
validator: (value) => ['left', 'right', 'top', 'bottom'].includes(value)
|
||
},
|
||
// 面板列表
|
||
panels: {
|
||
type: Array,
|
||
default: () => []
|
||
},
|
||
// 区域尺寸(宽度或高度)
|
||
size: {
|
||
type: Number,
|
||
required: true
|
||
},
|
||
// 面板尺寸比例
|
||
sizeRatios: {
|
||
type: Array,
|
||
default: () => []
|
||
},
|
||
// store实例
|
||
store: {
|
||
type: Object,
|
||
required: true
|
||
},
|
||
// 绝对定位边界(可选):{ x, y, width, height }
|
||
bounds: {
|
||
type: Object,
|
||
required: false,
|
||
default: null
|
||
}
|
||
});
|
||
|
||
// 定义组件事件
|
||
const emit = defineEmits([
|
||
'closePanel',
|
||
'floatPanel',
|
||
'dockPanel',
|
||
'toggleCollapse',
|
||
'titleBarDragStart',
|
||
'panelResizeStart'
|
||
]);
|
||
|
||
// 根据位置计算容器类名
|
||
const containerClasses = computed(() => {
|
||
const baseClasses = 'overflow-hidden bg-white';
|
||
|
||
const positionClasses = {
|
||
left: 'flex flex-col border-r border-gray-300 panel-area-left',
|
||
right: 'flex flex-col border-l border-gray-300 panel-area-right',
|
||
top: 'flex border-b border-gray-300 panel-area-top',
|
||
bottom: 'flex border-t border-gray-300 panel-area-bottom'
|
||
};
|
||
|
||
const absoluteClass = props.bounds ? ' absolute' : '';
|
||
return `${baseClasses} ${positionClasses[props.position]}${absoluteClass}`;
|
||
});
|
||
|
||
// 根据位置计算容器样式
|
||
const containerStyle = computed(() => {
|
||
// 若提供了绝对定位边界,则使用top/left/width/height
|
||
if (props.bounds) {
|
||
const { x = 0, y = 0, width = 0, height = 0 } = props.bounds;
|
||
return {
|
||
position: 'absolute',
|
||
top: `${y}px`,
|
||
left: `${x}px`,
|
||
width: `${width}px`,
|
||
height: `${height}px`
|
||
};
|
||
}
|
||
|
||
const isVertical = ['left', 'right'].includes(props.position);
|
||
const styleProperty = isVertical ? 'width' : 'height';
|
||
|
||
return {
|
||
[styleProperty]: `${props.size}px`
|
||
};
|
||
});
|
||
|
||
// 根据位置计算分隔条类名
|
||
const separatorClasses = computed(() => {
|
||
const isVertical = ['left', 'right'].includes(props.position);
|
||
|
||
if (isVertical) {
|
||
// 左右面板中的子面板是垂直排列的,应该使用水平分隔条
|
||
return 'h-1 cursor-row-resize z-30 dock-panel-separator dock-panel-separator-horizontal';
|
||
} else {
|
||
// 顶部底部面板中的子面板是水平排列的,应该使用垂直分隔条
|
||
return 'w-1 cursor-col-resize z-30 dock-panel-separator dock-panel-separator-vertical';
|
||
}
|
||
});
|
||
|
||
// 处理关闭面板
|
||
function closePanel(panelId) {
|
||
emit('closePanel', panelId);
|
||
}
|
||
|
||
// 处理浮动面板
|
||
function floatPanel(panelId) {
|
||
emit('floatPanel', panelId);
|
||
}
|
||
|
||
// 处理停靠面板
|
||
function dockPanel(panelId, position) {
|
||
emit('dockPanel', panelId, position);
|
||
}
|
||
|
||
// 处理折叠面板
|
||
function toggleCollapse(panelId) {
|
||
emit('toggleCollapse', panelId);
|
||
}
|
||
|
||
// 处理面板标题栏拖拽开始
|
||
function handlePanelTitleBarDragStart(panelId, event) {
|
||
emit('titleBarDragStart', panelId, event);
|
||
}
|
||
|
||
// 处理面板调整大小开始
|
||
function startPanelResize(panelId, panelIndex, event) {
|
||
emit('panelResizeStart', props.position, panelId, panelIndex, event);
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* PanelArea特有的作用域样式 */
|
||
/* 共享样式已在styles/panel-shared.css中定义 */
|
||
</style> |