Files
JoyD/AutoRobot/Windows/Robot/Web/src/components/PanelArea.vue

163 lines
4.1 KiB
Vue
Raw Normal View History

<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>