Files
JoyD/AutoRobot/Windows/Robot/Web/src/components/PanelArea.vue
2025-10-23 22:09:49 +08:00

163 lines
4.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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