123 lines
3.2 KiB
Vue
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>
|