抽取浮动窗口为Panel.vue组件
This commit is contained in:
140
AutoRobot/Windows/Robot/Web/src/DockLayout/Panel.vue
Normal file
140
AutoRobot/Windows/Robot/Web/src/DockLayout/Panel.vue
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
<template>
|
||||||
|
<div class="panel absolute bg-white shadow rounded border overflow-hidden"
|
||||||
|
:style="{ top: y + 'px', left: x + 'px', width: width + 'px', height: height + 'px' }">
|
||||||
|
<div class="flex flex-col h-full">
|
||||||
|
<!-- 标题栏 -->
|
||||||
|
<div class="title-bar h-6 bg-[#435d9c] text-white px-2 flex items-center justify-between select-none">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="text-xs">{{ title }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="title-bar-buttons flex items-center gap-0.5">
|
||||||
|
<button class="button-icon p-[2px] rounded hover:opacity-100 opacity-80"
|
||||||
|
@click="onToggleCollapse"
|
||||||
|
aria-label="折叠/展开">
|
||||||
|
<!-- 向下小三角,使用内联SVG避免样式作用域问题 -->
|
||||||
|
<svg width="11" height="11" viewBox="0 0 11 11" aria-hidden="true">
|
||||||
|
<polygon points="5.5,8 2,3.5 9,3.5" fill="#cbd6ff" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button class="button-icon p-[2px] rounded hover:opacity-100 opacity-80"
|
||||||
|
@click="onMaximize"
|
||||||
|
aria-label="最大化">
|
||||||
|
<!-- 最大化图标:仅外框1px + 两行填充 -->
|
||||||
|
<svg class="icon-square-svg" width="11" height="11" viewBox="0 0 11 11" aria-hidden="true">
|
||||||
|
<rect x="0.5" y="0.5" width="10" height="10" fill="#cbd6ff" stroke="#8ea3d8" stroke-width="1" />
|
||||||
|
<rect x="3" y="3" width="5" height="1" fill="#b8c6ff" />
|
||||||
|
<!-- 下行采用更宽填充以贴近示例图 -->
|
||||||
|
<rect x="1" y="3" width="8.5" height="6.5" fill="#435d9c" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button class="button-icon p-[2px] rounded hover:opacity-100 opacity-80"
|
||||||
|
@click="onClose"
|
||||||
|
aria-label="关闭">
|
||||||
|
<!-- 关闭图标(X),内联SVG确保1px线条 -->
|
||||||
|
<svg width="11" height="11" viewBox="0 0 11 11" aria-hidden="true">
|
||||||
|
<line x1="2" y1="2" x2="9" y2="9" stroke="#e6efff" stroke-width="1" />
|
||||||
|
<line x1="2" y1="9" x2="9" y2="2" stroke="#e6efff" stroke-width="1" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 工具栏:位于标题栏下方,右侧扩展钮 -->
|
||||||
|
<div class="toolbar h-6 bg-[#d5e2f6] text-[#2c3e7a] px-2 flex items-center justify-between border-b border-[#c7d2ea]">
|
||||||
|
<div class="toolbar-left flex items-center gap-2">
|
||||||
|
<span class="text-xs">工具栏</span>
|
||||||
|
<button v-if="toolbarExpanded" class="toolbar-button px-2 py-0.5 text-xs bg-white/60 rounded hover:bg-white">示例按钮</button>
|
||||||
|
</div>
|
||||||
|
<button class="toolbar-toggle px-2 py-0.5 text-xs rounded hover:bg-white/40"
|
||||||
|
@click="onToggleToolbar"
|
||||||
|
aria-label="展开工具栏">
|
||||||
|
<i class="fa-solid" :class="toolbarExpanded ? 'fa-angles-left' : 'fa-angles-right'"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 内容区:可折叠 -->
|
||||||
|
<div class="content-area bg-[#f5f7fb] flex-1" v-show="!collapsed"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defineProps, defineEmits } from 'vue';
|
||||||
|
|
||||||
|
// 定义组件属性
|
||||||
|
const props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
default: 300
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: Number,
|
||||||
|
default: 180
|
||||||
|
},
|
||||||
|
collapsed: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
toolbarExpanded: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 定义事件
|
||||||
|
const emit = defineEmits(['toggleCollapse', 'maximize', 'close', 'toggleToolbar']);
|
||||||
|
|
||||||
|
// 事件处理函数
|
||||||
|
const onToggleCollapse = () => {
|
||||||
|
emit('toggleCollapse', props.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMaximize = () => {
|
||||||
|
emit('maximize', props.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
emit('close', props.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onToggleToolbar = () => {
|
||||||
|
emit('toggleToolbar', props.id);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 面板基础样式 */
|
||||||
|
.panel {
|
||||||
|
border: 1px solid #c7d2ea;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 图标样式优化 */
|
||||||
|
.icon-square-svg {
|
||||||
|
/* 优化SVG渲染,避免1px边框显示过粗的问题 */
|
||||||
|
shape-rendering: crispEdges;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 禁用可能存在的旧伪元素样式 */
|
||||||
|
:deep(.icon-square::before),
|
||||||
|
:deep(.icon-square::after) {
|
||||||
|
content: none !important;
|
||||||
|
display: none !important;
|
||||||
|
border: 0 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -44,57 +44,30 @@
|
|||||||
|
|
||||||
<!-- 预留主区域(暂不放面板) -->
|
<!-- 预留主区域(暂不放面板) -->
|
||||||
<div ref="panelHost" class="flex-1 w-full h-[calc(100%-4rem)] relative bg-gray-100">
|
<div ref="panelHost" class="flex-1 w-full h-[calc(100%-4rem)] relative bg-gray-100">
|
||||||
<!-- 浮动面板渲染区 -->
|
<!-- 浮动面板渲染区(使用抽取的组件) -->
|
||||||
<div
|
<Panel
|
||||||
v-for="panel in floatingPanels"
|
v-for="panel in floatingPanels"
|
||||||
:key="panel.id"
|
:key="panel.id"
|
||||||
class="absolute bg-white shadow rounded border overflow-hidden"
|
:id="panel.id"
|
||||||
:style="{ top: panel.y + 'px', left: panel.x + 'px', width: panel.width + 'px', height: panel.height + 'px' }"
|
:title="panel.title"
|
||||||
>
|
:x="panel.x"
|
||||||
<div class="flex flex-col h-full">
|
:y="panel.y"
|
||||||
<!-- 标题栏:小三角移到右侧,压缩高度与间距 -->
|
:width="panel.width"
|
||||||
<div class="h-6 bg-[#435d9c] text-white px-2 flex items-center justify-between select-none">
|
:height="panel.height"
|
||||||
<div class="flex items-center">
|
:collapsed="panel.collapsed"
|
||||||
<span class="text-xs">{{ panel.title }}</span>
|
:toolbarExpanded="panel.toolbarExpanded"
|
||||||
</div>
|
@toggleCollapse="toggleCollapse"
|
||||||
<div class="flex items-center gap-0.5">
|
@maximize="maximizePanel"
|
||||||
<button class="p-[2px] rounded hover:opacity-100 opacity-80" @click="toggleCollapse(panel.id)" aria-label="折叠/展开">
|
@close="closePanel"
|
||||||
<span class="icon-triangle-down"></span>
|
@toggleToolbar="toggleToolbarExpand"
|
||||||
</button>
|
/>
|
||||||
<button class="p-[2px] rounded hover:opacity-100 opacity-80" @click="maximizePanel(panel.id)" aria-label="最大化">
|
|
||||||
<svg class="icon-square-svg" width="11" height="11" viewBox="0 0 11 11" aria-hidden="true">
|
|
||||||
<!-- 外框与底色(仅1px边框) -->
|
|
||||||
<rect x="0.5" y="0.5" width="10" height="10" fill="#cbd6ff" stroke="#8ea3d8" stroke-width="1" />
|
|
||||||
<!-- 两行填充:上行1px浅蓝,下行标题栏色 -->
|
|
||||||
<rect x="1" y="3" width="8.5" height="6.5" fill="#435d9c" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button class="p-[2px] rounded hover:opacity-100 opacity-80" @click="closePanel(panel.id)" aria-label="关闭">
|
|
||||||
<span class="icon-x"></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 工具栏:位于标题栏下方,右侧扩展钮 -->
|
|
||||||
<div class="h-6 bg-[#d5e2f6] text-[#2c3e7a] px-2 flex items-center justify-between border-b border-[#c7d2ea]">
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<span class="text-xs">工具栏</span>
|
|
||||||
<button v-if="panel.toolbarExpanded" class="px-2 py-0.5 text-xs bg-white/60 rounded hover:bg-white">示例按钮</button>
|
|
||||||
</div>
|
|
||||||
<button class="px-2 py-0.5 text-xs rounded hover:bg-white/40" @click="toggleToolbarExpand(panel.id)" aria-label="展开工具栏">
|
|
||||||
<i class="fa-solid" :class="panel.toolbarExpanded ? 'fa-angles-left' : 'fa-angles-right'"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<!-- 内容区:可折叠 -->
|
|
||||||
<div class="bg-[#f5f7fb] flex-1" v-show="!panel.collapsed"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
import Panel from '../DockLayout/Panel.vue';
|
||||||
// 顶部控制栏按钮的引用
|
// 顶部控制栏按钮的引用
|
||||||
const layoutFileInput = ref(null);
|
const layoutFileInput = ref(null);
|
||||||
const panelHost = ref(null);
|
const panelHost = ref(null);
|
||||||
|
|||||||
Reference in New Issue
Block a user