Files
mengyaconnect/mengyaconnect-frontend/src/components/PanelOverlay.vue
2026-05-16 19:19:39 +08:00

123 lines
3.2 KiB
Vue

<template>
<div class="overlay" @click.self="handleClose">
<div class="overlay-card">
<div class="drag-handle mobile-only"></div>
<header class="overlay-header">
<h2>{{ panelTitle }}</h2>
<button class="icon-btn" type="button" @click="handleClose"></button>
</header>
<ConnectPanel v-if="activePanel === 'connect'" />
<SshPanel v-else-if="activePanel === 'ssh'" />
<CommandsPanel v-else-if="activePanel === 'commands'" />
<ScriptsPanel v-else-if="activePanel === 'scripts'" />
</div>
</div>
</template>
<script setup>
import { computed, nextTick } from "vue";
import { useUI } from "../composables/useUI";
import { useSessions } from "../composables/useSessions";
import ConnectPanel from "./panels/ConnectPanel.vue";
import SshPanel from "./panels/SshPanel.vue";
import CommandsPanel from "./panels/CommandsPanel.vue";
import ScriptsPanel from "./panels/ScriptsPanel.vue";
const { activePanel, closeOverlay } = useUI();
const { sessions, activeId } = useSessions();
const titleMap = { connect: "新建连接", ssh: "SSH 配置", commands: "快捷命令", scripts: "脚本管理" };
const panelTitle = computed(() => titleMap[activePanel.value] ?? "");
function handleClose() {
closeOverlay();
nextTick(() => {
const session = sessions.value.find((s) => s.id === activeId.value);
session?.term?.focus();
});
}
</script>
<style scoped>
.overlay {
position: absolute;
inset: 0;
display: flex;
justify-content: center;
align-items: flex-start;
padding: 16px;
background: rgba(0, 0, 0, 0.65);
z-index: 50;
backdrop-filter: blur(4px);
}
.overlay-card {
width: min(900px, 100%);
max-height: 100%;
background: var(--bg-1);
border-radius: 10px;
border: 1px solid var(--border);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.8);
display: flex;
flex-direction: column;
overflow: hidden;
}
.drag-handle {
display: none;
width: 40px;
height: 4px;
border-radius: 2px;
background: var(--text-3);
margin: 10px auto 0;
flex-shrink: 0;
}
.overlay-header {
padding: 10px 14px;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid var(--border);
flex-shrink: 0;
}
.overlay-header h2 { margin: 0; font-size: 15px; font-weight: 600; color: var(--text-1); }
.icon-btn {
border: none;
background: transparent;
color: var(--text-2);
font-size: 16px;
cursor: pointer;
padding: 2px 6px;
border-radius: 4px;
}
.icon-btn:hover { color: var(--text-1); background: var(--bg-3); }
:deep(.overlay-body) {
padding: 12px 14px 16px;
overflow: auto;
scrollbar-width: thin;
scrollbar-color: var(--border) transparent;
}
@media (max-width: 768px) {
.overlay { align-items: flex-end; padding: 0; background: rgba(0, 0, 0, 0.7); }
.overlay-card {
border-radius: 16px 16px 0 0;
border-bottom: none;
width: 100%;
max-width: 100%;
max-height: min(88vh, var(--vvh, 88vh));
animation: slideUp 0.28s cubic-bezier(0.32, 0.72, 0, 1);
}
.drag-handle { display: block; }
:deep(.overlay-body) { padding: 10px 12px 20px; }
}
@keyframes slideUp {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
</style>