update
This commit is contained in:
@@ -0,0 +1,73 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { PropType } from 'vue';
|
||||||
|
import { useLayoutStore, type PaneName } from '../stores/layout.store';
|
||||||
|
|
||||||
|
// --- Props ---
|
||||||
|
const props = defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
paneName: {
|
||||||
|
type: String as PropType<PaneName>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- Setup ---
|
||||||
|
const layoutStore = useLayoutStore();
|
||||||
|
|
||||||
|
// --- Methods ---
|
||||||
|
const closePane = () => {
|
||||||
|
console.log(`[PaneTitleBar] Requesting to close pane: ${props.paneName}`);
|
||||||
|
layoutStore.setPaneVisibility(props.paneName, false);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="pane-title-bar">
|
||||||
|
<span class="title">{{ title }}</span>
|
||||||
|
<button class="close-button" @click="closePane" :title="`关闭 ${title}`">
|
||||||
|
× <!-- 使用 HTML 实体 '×' -->
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.pane-title-bar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 4px 8px; /* 调整内边距使标题栏更紧凑 */
|
||||||
|
background-color: #e9ecef; /* 标题栏背景色,可以根据主题调整 */
|
||||||
|
border-bottom: 1px solid #ced4da; /* 底部边框 */
|
||||||
|
height: 28px; /* 固定标题栏高度 */
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex-shrink: 0; /* 防止标题栏被压缩 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 0.85em; /* 稍小字体 */
|
||||||
|
font-weight: 600;
|
||||||
|
color: #495057; /* 标题颜色 */
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #6c757d; /* 关闭按钮颜色 */
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.2em; /* 稍大图标 */
|
||||||
|
line-height: 1;
|
||||||
|
padding: 0 4px; /* 微调内边距 */
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button:hover {
|
||||||
|
background-color: #dc3545; /* 悬停时背景变红 */
|
||||||
|
color: white; /* 悬停时图标变白 */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -205,11 +205,11 @@ defineExpose({ write });
|
|||||||
overflow: hidden; /* 防止滚动条出现 */
|
overflow: hidden; /* 防止滚动条出现 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 尝试直接给 screen 添加 padding */
|
/* 终端屏幕样式 - 移除上下间距 */
|
||||||
.terminal-container :deep(.xterm-screen) {
|
.terminal-container :deep(.xterm-screen) {
|
||||||
padding: 10px; /* 为终端内容区域添加内边距 */
|
padding: 0; /* 移除内边距,解决上下有间距的问题 */
|
||||||
box-sizing: border-box; /* 确保 padding 包含在尺寸内 */
|
box-sizing: border-box;
|
||||||
/* 覆盖 xterm.css 可能设置的 position: absolute,以便 padding 生效 */
|
/* 覆盖 xterm.css 可能设置的 position: absolute */
|
||||||
position: relative !important;
|
position: relative !important;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, PropType } from 'vue'; // 导入 ref
|
import { ref, computed, PropType } from 'vue'; // 导入 ref 和 computed
|
||||||
|
import { useI18n } from 'vue-i18n'; // 导入 i18n
|
||||||
|
import { storeToRefs } from 'pinia'; // 导入 storeToRefs
|
||||||
import WorkspaceConnectionListComponent from './WorkspaceConnectionList.vue'; // 导入连接列表组件
|
import WorkspaceConnectionListComponent from './WorkspaceConnectionList.vue'; // 导入连接列表组件
|
||||||
import { useSessionStore } from '../stores/session.store'; // 导入 session store
|
import { useSessionStore } from '../stores/session.store'; // 导入 session store
|
||||||
|
import { useLayoutStore, type PaneName } from '../stores/layout.store'; // 导入布局 store 和类型
|
||||||
// 导入会话状态类型
|
// 导入会话状态类型
|
||||||
import type { SessionTabInfoWithStatus } from '../stores/session.store'; // 导入更新后的类型
|
import type { SessionTabInfoWithStatus } from '../stores/session.store'; // 导入更新后的类型
|
||||||
|
|
||||||
|
// --- Setup ---
|
||||||
|
const { t } = useI18n(); // 初始化 i18n
|
||||||
|
const layoutStore = useLayoutStore(); // 初始化布局 store
|
||||||
|
const { paneVisibility } = storeToRefs(layoutStore); // 修正:使用 storeToRefs 获取响应式状态
|
||||||
|
|
||||||
// 定义 Props
|
// 定义 Props
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
sessions: {
|
sessions: {
|
||||||
@@ -31,9 +39,10 @@ const closeSession = (event: MouseEvent, sessionId: string) => {
|
|||||||
emit('close-session', sessionId);
|
emit('close-session', sessionId);
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- 新增:弹出窗口状态和处理 ---
|
// --- 本地状态 ---
|
||||||
const sessionStore = useSessionStore();
|
const sessionStore = useSessionStore(); // Session store 保持不变
|
||||||
const showConnectionListPopup = ref(false);
|
const showConnectionListPopup = ref(false); // 连接列表弹出状态
|
||||||
|
const showLayoutMenu = ref(false); // 新增:布局菜单弹出状态
|
||||||
|
|
||||||
const togglePopup = () => {
|
const togglePopup = () => {
|
||||||
showConnectionListPopup.value = !showConnectionListPopup.value;
|
showConnectionListPopup.value = !showConnectionListPopup.value;
|
||||||
@@ -46,13 +55,42 @@ const handlePopupConnect = (connectionId: number) => {
|
|||||||
sessionStore.handleConnectRequest(connectionId);
|
sessionStore.handleConnectRequest(connectionId);
|
||||||
showConnectionListPopup.value = false; // 关闭弹出窗口
|
showConnectionListPopup.value = false; // 关闭弹出窗口
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// --- 新增:布局菜单处理 ---
|
||||||
|
const toggleLayoutMenu = () => {
|
||||||
|
console.log('Toggling layout menu visibility. Current state:', showLayoutMenu.value); // 添加日志
|
||||||
|
showLayoutMenu.value = !showLayoutMenu.value;
|
||||||
|
console.log('New state:', showLayoutMenu.value); // 添加日志
|
||||||
|
};
|
||||||
|
|
||||||
|
// 定义面板名称到显示文本的映射 (恢复 commandBar)
|
||||||
|
const paneLabels: Record<PaneName, string> = {
|
||||||
|
connections: t('layout.pane.connections'),
|
||||||
|
terminal: t('layout.pane.terminal'),
|
||||||
|
commandBar: t('layout.pane.commandBar'), // 恢复
|
||||||
|
fileManager: t('layout.pane.fileManager'),
|
||||||
|
editor: t('layout.pane.editor'),
|
||||||
|
statusMonitor: t('layout.pane.statusMonitor'),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取所有可控制的面板名称
|
||||||
|
const availablePanes = computed(() => Object.keys(paneVisibility.value) as PaneName[]); // 修正:使用 .value
|
||||||
|
|
||||||
|
// 处理菜单项点击
|
||||||
|
const handleTogglePane = (paneName: PaneName) => {
|
||||||
|
layoutStore.togglePaneVisibility(paneName);
|
||||||
|
// 可以选择点击后关闭菜单,或者保持打开
|
||||||
|
// showLayoutMenu.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="terminal-tab-bar">
|
<div class="terminal-tab-bar">
|
||||||
<ul class="tab-list">
|
<div class="tabs-and-add-button"> <!-- 新容器包裹标签和+按钮 -->
|
||||||
<li
|
<ul class="tab-list">
|
||||||
v-for="session in sessions"
|
<li
|
||||||
|
v-for="session in sessions"
|
||||||
:key="session.sessionId"
|
:key="session.sessionId"
|
||||||
:class="['tab-item', { active: session.sessionId === activeSessionId }]"
|
:class="['tab-item', { active: session.sessionId === activeSessionId }]"
|
||||||
@click="activateSession(session.sessionId)"
|
@click="activateSession(session.sessionId)"
|
||||||
@@ -64,14 +102,30 @@ const handlePopupConnect = (connectionId: number) => {
|
|||||||
<button class="close-tab-button" @click="closeSession($event, session.sessionId)" title="关闭标签页">
|
<button class="close-tab-button" @click="closeSession($event, session.sessionId)" title="关闭标签页">
|
||||||
× <!-- 使用 HTML 实体 '×' -->
|
× <!-- 使用 HTML 实体 '×' -->
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<!-- 新增 "+" 按钮 -->
|
<!-- "+" 按钮紧随标签列表 -->
|
||||||
<button class="add-tab-button" @click="togglePopup" title="新建连接标签页">
|
<button class="add-tab-button" @click="togglePopup" title="新建连接标签页">
|
||||||
<i class="fas fa-plus"></i>
|
<i class="fas fa-plus"></i>
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
<!-- 新增:连接列表弹出窗口 -->
|
<!-- 布局菜单按钮容器(推到最右侧) -->
|
||||||
|
<div class="layout-menu-container">
|
||||||
|
<button class="layout-menu-button" @click="toggleLayoutMenu" title="调整布局">
|
||||||
|
<i class="fas fa-bars"></i> <!-- 使用 Font Awesome bars 图标 -->
|
||||||
|
</button>
|
||||||
|
<!-- 布局菜单下拉列表 (保持不变) -->
|
||||||
|
<div v-if="showLayoutMenu" class="layout-menu-dropdown">
|
||||||
|
<ul>
|
||||||
|
<li v-for="pane in availablePanes" :key="pane" @click="handleTogglePane(pane)">
|
||||||
|
<span class="checkmark">{{ paneVisibility[pane] ? '✓' : '' }}</span>
|
||||||
|
{{ paneLabels[pane] || pane }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 移除多余的结束标签 -->
|
||||||
|
<!-- 连接列表弹出窗口 (保持不变) -->
|
||||||
<div v-if="showConnectionListPopup" class="connection-list-popup" @click.self="togglePopup">
|
<div v-if="showConnectionListPopup" class="connection-list-popup" @click.self="togglePopup">
|
||||||
<div class="popup-content">
|
<div class="popup-content">
|
||||||
<button class="popup-close-button" @click="togglePopup">×</button>
|
<button class="popup-close-button" @click="togglePopup">×</button>
|
||||||
@@ -91,12 +145,23 @@ const handlePopupConnect = (connectionId: number) => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
background-color: #e0e0e0; /* 标签栏背景色 */
|
background-color: #e0e0e0; /* 标签栏背景色 */
|
||||||
border-bottom: 1px solid #bdbdbd;
|
border-bottom: 1px solid #bdbdbd;
|
||||||
overflow-x: auto; /* 如果标签过多则允许水平滚动 */
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
/* padding: 0 0.5rem; */ /* 移除左右内边距,让标签列表和按钮自己控制 */
|
|
||||||
padding-right: 0.5rem; /* 只保留右侧内边距给按钮 */
|
|
||||||
height: 2.5rem; /* 固定标签栏高度 */
|
height: 2.5rem; /* 固定标签栏高度 */
|
||||||
box-sizing: border-box; /* 确保 padding 不会增加总高度 */
|
box-sizing: border-box; /* 确保 padding 不会增加总高度 */
|
||||||
|
/* justify-content: space-between; */ /* 移除,让内容自然靠左 */
|
||||||
|
overflow: hidden; /* 恢复:防止标签过多时破坏布局 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 包裹标签和+按钮的容器 */
|
||||||
|
.tabs-and-add-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow-x: auto; /* 允许标签和+按钮区域滚动 */
|
||||||
|
/* flex-grow: 1; */ /* 移除:让其自然宽度 */
|
||||||
|
/* max-width: calc(100% - 50px); */ /* 移除宽度限制 */
|
||||||
|
flex-shrink: 1; /* 允许在空间不足时收缩 */
|
||||||
|
min-width: 0; /* 允许收缩到0 */
|
||||||
|
height: 100%; /* 确保高度与父元素相同,消除上下间距 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 状态点样式 */
|
/* 状态点样式 */
|
||||||
@@ -117,9 +182,11 @@ const handlePopupConnect = (connectionId: number) => {
|
|||||||
|
|
||||||
.tab-list {
|
.tab-list {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0; /* 确保列表无内边距 */
|
padding: 0;
|
||||||
margin: 0; /* 确保列表无外边距 */
|
margin: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-shrink: 0; /* 防止标签列表被压缩 */
|
||||||
|
height: 100%; /* 确保占满整个高度 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-item {
|
.tab-item {
|
||||||
@@ -133,7 +200,7 @@ const handlePopupConnect = (connectionId: number) => {
|
|||||||
background-color: #f0f0f0; /* 未激活标签背景 */
|
background-color: #f0f0f0; /* 未激活标签背景 */
|
||||||
color: #616161; /* 未激活标签文字颜色 */
|
color: #616161; /* 未激活标签文字颜色 */
|
||||||
transition: background-color 0.2s ease, color 0.2s ease;
|
transition: background-color 0.2s ease, color 0.2s ease;
|
||||||
max-width: 200px; /* 限制标签最大宽度 */
|
/* max-width: 200px; */ /* 移除最大宽度限制 */
|
||||||
position: relative; /* 保持相对定位,以防万一需要定位子元素 */
|
position: relative; /* 保持相对定位,以防万一需要定位子元素 */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,10 +220,11 @@ const handlePopupConnect = (connectionId: number) => {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
/* margin-right: 1.5rem; */ /* 调整右边距,因为关闭按钮现在是 flex item */
|
/* margin-right: 1.5rem; */ /* 移除 */
|
||||||
line-height: normal; /* 默认行高 */
|
line-height: normal; /* 默认行高 */
|
||||||
flex-grow: 1; /* 允许名称伸展 */
|
flex-grow: 1; /* 保持:允许名称伸展 */
|
||||||
margin-left: 4px; /* 在状态点和名称之间添加一点间距 */
|
margin-left: 4px; /* 在状态点和名称之间添加一点间距 */
|
||||||
|
text-align: left; /* 保持文本左对齐 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-tab-button {
|
.close-tab-button {
|
||||||
@@ -169,9 +237,9 @@ const handlePopupConnect = (connectionId: number) => {
|
|||||||
padding: 0 0.3rem;
|
padding: 0 0.3rem;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
margin-left: auto; /* 推到右侧 */
|
margin-left: auto; /* 保持 auto 将按钮推到最右侧 */
|
||||||
|
flex-shrink: 0; /* 防止按钮被压缩 */
|
||||||
/* 移除绝对定位 */
|
/* 移除绝对定位 */
|
||||||
/* position: absolute; */
|
|
||||||
/* top: 50%; */
|
/* top: 50%; */
|
||||||
/* right: 0.5rem; */
|
/* right: 0.5rem; */
|
||||||
/* transform: translateY(-50%); */
|
/* transform: translateY(-50%); */
|
||||||
@@ -194,9 +262,8 @@ const handlePopupConnect = (connectionId: number) => {
|
|||||||
.add-tab-button {
|
.add-tab-button {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
border-left: 1px solid #bdbdbd; /* 左侧分隔线 */
|
border-left: 1px solid #bdbdbd; /* 恢复左侧分隔线 */
|
||||||
padding: 0 0.8rem;
|
padding: 0 0.8rem;
|
||||||
/* margin-left: 0.5rem; */ /* 移除左外边距 */
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
color: #616161;
|
color: #616161;
|
||||||
@@ -213,6 +280,8 @@ const handlePopupConnect = (connectionId: number) => {
|
|||||||
line-height: 1; /* 确保图标垂直居中 */
|
line-height: 1; /* 确保图标垂直居中 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 移除 action-buttons-container 样式 */
|
||||||
|
|
||||||
/* 弹出窗口样式 */
|
/* 弹出窗口样式 */
|
||||||
.connection-list-popup {
|
.connection-list-popup {
|
||||||
position: fixed; /* 固定定位,覆盖整个屏幕 */
|
position: fixed; /* 固定定位,覆盖整个屏幕 */
|
||||||
@@ -277,4 +346,77 @@ const handlePopupConnect = (connectionId: number) => {
|
|||||||
padding: 0; /* 保持移除内边距 */
|
padding: 0; /* 保持移除内边距 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 新增:布局菜单样式 */
|
||||||
|
.layout-menu-container {
|
||||||
|
position: relative; /* 用于定位下拉菜单 */
|
||||||
|
display: flex; /* 确保按钮垂直居中 */
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
margin-left: auto; /* 保持:将布局按钮推到最右侧 */
|
||||||
|
border-left: 1px solid #bdbdbd; /* 确保布局按钮左侧有分隔线 */
|
||||||
|
flex-shrink: 0; /* 保持:防止被压缩 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-menu-button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
/* border-left: 1px solid #bdbdbd; */ /* 移除按钮左侧分隔线 */
|
||||||
|
padding: 0 0.8rem;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.1em;
|
||||||
|
color: #616161;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.layout-menu-button:hover {
|
||||||
|
background-color: #d0d0d0;
|
||||||
|
}
|
||||||
|
.layout-menu-button i {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-menu-dropdown {
|
||||||
|
position: absolute; /* 恢复绝对定位 */
|
||||||
|
top: 100%; /* 定位在按钮下方 */
|
||||||
|
right: 0; /* 对齐到右侧 */
|
||||||
|
background-color: lightblue; /* 临时调试背景色 */
|
||||||
|
min-height: 20px; /* 临时调试最小高度 */
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
box-shadow: 0 2px 5px rgba(0,0,0,0.15);
|
||||||
|
z-index: 9999; /* 保持高 z-index */
|
||||||
|
min-width: 150px; /* 最小宽度 */
|
||||||
|
padding: 5px 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-menu-dropdown ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-menu-dropdown li {
|
||||||
|
padding: 8px 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-menu-dropdown li:hover {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-menu-dropdown .checkmark {
|
||||||
|
display: inline-block;
|
||||||
|
width: 20px; /* 固定宽度以便对齐 */
|
||||||
|
text-align: center;
|
||||||
|
margin-right: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #28a745; /* 勾选标记颜色 */
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -545,5 +545,15 @@
|
|||||||
},
|
},
|
||||||
"commandInputBar": {
|
"commandInputBar": {
|
||||||
"placeholder": "Enter command and press Enter to send..."
|
"placeholder": "Enter command and press Enter to send..."
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"pane": {
|
||||||
|
"connections": "Connections",
|
||||||
|
"terminal": "Terminal",
|
||||||
|
"commandBar": "Command Bar",
|
||||||
|
"fileManager": "File Manager",
|
||||||
|
"editor": "Editor",
|
||||||
|
"statusMonitor": "Status Monitor"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -548,5 +548,15 @@
|
|||||||
},
|
},
|
||||||
"commandInputBar": {
|
"commandInputBar": {
|
||||||
"placeholder": "在此输入命令后按 Enter 发送到终端..."
|
"placeholder": "在此输入命令后按 Enter 发送到终端..."
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"pane": {
|
||||||
|
"connections": "连接列表",
|
||||||
|
"terminal": "终端",
|
||||||
|
"commandBar": "命令栏",
|
||||||
|
"fileManager": "文件管理器",
|
||||||
|
"editor": "编辑器",
|
||||||
|
"statusMonitor": "状态监视器"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
// 定义面板名称的类型,方便管理和引用 (恢复 commandBar)
|
||||||
|
export type PaneName = 'connections' | 'terminal' | 'commandBar' | 'fileManager' | 'editor' | 'statusMonitor';
|
||||||
|
|
||||||
|
// 定义 Store
|
||||||
|
export const useLayoutStore = defineStore('layout', () => {
|
||||||
|
// 使用 ref 创建响应式状态,存储每个面板的可见性 (恢复 commandBar)
|
||||||
|
const paneVisibility = ref<Record<PaneName, boolean>>({
|
||||||
|
connections: true,
|
||||||
|
terminal: true,
|
||||||
|
commandBar: true, // 恢复
|
||||||
|
fileManager: true,
|
||||||
|
editor: true,
|
||||||
|
statusMonitor: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Action: 切换指定面板的可见性
|
||||||
|
function togglePaneVisibility(paneName: PaneName) {
|
||||||
|
if (paneVisibility.value[paneName] !== undefined) {
|
||||||
|
paneVisibility.value[paneName] = !paneVisibility.value[paneName];
|
||||||
|
console.log(`[Layout Store] Toggled visibility for ${paneName}: ${paneVisibility.value[paneName]}`);
|
||||||
|
} else {
|
||||||
|
console.warn(`[Layout Store] Attempted to toggle visibility for unknown pane: ${paneName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action: 设置指定面板的可见性
|
||||||
|
function setPaneVisibility(paneName: PaneName, isVisible: boolean) {
|
||||||
|
if (paneVisibility.value[paneName] !== undefined) {
|
||||||
|
paneVisibility.value[paneName] = isVisible;
|
||||||
|
console.log(`[Layout Store] Set visibility for ${paneName} to: ${isVisible}`);
|
||||||
|
} else {
|
||||||
|
console.warn(`[Layout Store] Attempted to set visibility for unknown pane: ${paneName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
paneVisibility,
|
||||||
|
togglePaneVisibility,
|
||||||
|
setPaneVisibility,
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -10,9 +10,11 @@ import AddConnectionFormComponent from '../components/AddConnectionForm.vue';
|
|||||||
import TerminalTabBar from '../components/TerminalTabBar.vue';
|
import TerminalTabBar from '../components/TerminalTabBar.vue';
|
||||||
import CommandInputBar from '../components/CommandInputBar.vue';
|
import CommandInputBar from '../components/CommandInputBar.vue';
|
||||||
import FileEditorContainer from '../components/FileEditorContainer.vue'; // 导入编辑器容器
|
import FileEditorContainer from '../components/FileEditorContainer.vue'; // 导入编辑器容器
|
||||||
|
import PaneTitleBar from '../components/PaneTitleBar.vue'; // 导入标题栏组件
|
||||||
import { useSessionStore, type SessionTabInfoWithStatus, type SshTerminalInstance } from '../stores/session.store'; // 导入 SshTerminalInstance
|
import { useSessionStore, type SessionTabInfoWithStatus, type SshTerminalInstance } from '../stores/session.store'; // 导入 SshTerminalInstance
|
||||||
import { useSettingsStore } from '../stores/settings.store'; // 导入设置 Store
|
import { useSettingsStore } from '../stores/settings.store'; // 导入设置 Store
|
||||||
import { useFileEditorStore } from '../stores/fileEditor.store'; // 导入文件编辑器 Store
|
import { useFileEditorStore } from '../stores/fileEditor.store'; // 导入文件编辑器 Store
|
||||||
|
import { useLayoutStore } from '../stores/layout.store'; // 导入布局 Store
|
||||||
import type { ConnectionInfo } from '../stores/connections.store';
|
import type { ConnectionInfo } from '../stores/connections.store';
|
||||||
// 导入 splitpanes 组件
|
// 导入 splitpanes 组件
|
||||||
import { Splitpanes, Pane } from 'splitpanes';
|
import { Splitpanes, Pane } from 'splitpanes';
|
||||||
@@ -24,11 +26,13 @@ const { t } = useI18n();
|
|||||||
const sessionStore = useSessionStore();
|
const sessionStore = useSessionStore();
|
||||||
const settingsStore = useSettingsStore(); // 初始化设置 Store
|
const settingsStore = useSettingsStore(); // 初始化设置 Store
|
||||||
const fileEditorStore = useFileEditorStore(); // 初始化文件编辑器 Store (用于共享模式)
|
const fileEditorStore = useFileEditorStore(); // 初始化文件编辑器 Store (用于共享模式)
|
||||||
|
const layoutStore = useLayoutStore(); // 初始化布局 Store
|
||||||
|
|
||||||
// --- 从 Store 获取响应式状态和 Getters ---
|
// --- 从 Store 获取响应式状态和 Getters ---
|
||||||
const { sessionTabsWithStatus, activeSessionId, activeSession } = storeToRefs(sessionStore);
|
const { sessionTabsWithStatus, activeSessionId, activeSession } = storeToRefs(sessionStore);
|
||||||
const { shareFileEditorTabsBoolean } = storeToRefs(settingsStore); // 获取共享设置
|
const { shareFileEditorTabsBoolean } = storeToRefs(settingsStore); // 获取共享设置
|
||||||
const { orderedTabs: globalEditorTabs, activeTabId: globalActiveEditorTabId } = storeToRefs(fileEditorStore); // 获取全局编辑器状态
|
const { orderedTabs: globalEditorTabs, activeTabId: globalActiveEditorTabId } = storeToRefs(fileEditorStore); // 获取全局编辑器状态
|
||||||
|
const { paneVisibility } = storeToRefs(layoutStore); // 获取布局可见性状态
|
||||||
|
|
||||||
// --- 计算属性 (用于动态绑定编辑器 Props) ---
|
// --- 计算属性 (用于动态绑定编辑器 Props) ---
|
||||||
// **再次修正:** 确保计算属性在共享模式下严格只依赖全局状态
|
// **再次修正:** 确保计算属性在共享模式下严格只依赖全局状态
|
||||||
@@ -185,8 +189,10 @@ onBeforeUnmount(() => {
|
|||||||
<splitpanes class="default-theme" :horizontal="false" style="height: 100%">
|
<splitpanes class="default-theme" :horizontal="false" style="height: 100%">
|
||||||
|
|
||||||
<!-- 1. 左侧边栏 Pane (连接列表) -->
|
<!-- 1. 左侧边栏 Pane (连接列表) -->
|
||||||
<pane size="15" min-size="10" class="sidebar-pane"> <!-- 调整大小 -->
|
<pane v-if="paneVisibility.connections" size="15" min-size="10" class="sidebar-pane pane-with-title"> <!-- 使用 v-if 控制, 添加 class -->
|
||||||
|
<PaneTitleBar :title="t('layout.pane.connections')" pane-name="connections" />
|
||||||
<WorkspaceConnectionListComponent
|
<WorkspaceConnectionListComponent
|
||||||
|
class="pane-content"
|
||||||
@connect-request="(id) => { console.log(`[WorkspaceView] Received 'connect-request' event for ID: ${id}`); sessionStore.handleConnectRequest(id); }"
|
@connect-request="(id) => { console.log(`[WorkspaceView] Received 'connect-request' event for ID: ${id}`); sessionStore.handleConnectRequest(id); }"
|
||||||
@open-new-session="(id) => { console.log(`[WorkspaceView] Received 'open-new-session' event for ID: ${id}`); sessionStore.handleOpenNewSession(id); }"
|
@open-new-session="(id) => { console.log(`[WorkspaceView] Received 'open-new-session' event for ID: ${id}`); sessionStore.handleOpenNewSession(id); }"
|
||||||
@request-add-connection="() => { console.log('[WorkspaceView] Received \'request-add-connection\' event'); handleRequestAddConnection(); }"
|
@request-add-connection="() => { console.log('[WorkspaceView] Received \'request-add-connection\' event'); handleRequestAddConnection(); }"
|
||||||
@@ -194,15 +200,17 @@ onBeforeUnmount(() => {
|
|||||||
/>
|
/>
|
||||||
</pane>
|
</pane>
|
||||||
|
|
||||||
<!-- 2. 中间区域 Pane (终端/命令栏/文件管理器) -->
|
<!-- 2. 中间区域 Pane (终端/命令栏/文件管理器) - 这个 Pane 本身通常保持可见,内部 Pane 才切换 -->
|
||||||
<pane size="50" min-size="30" class="middle-pane"> <!-- 调整大小 -->
|
<pane size="50" min-size="30" class="middle-pane">
|
||||||
<!-- 上下分割 (终端 | 命令栏 | 文件管理器) -->
|
<!-- 上下分割 (终端 | 命令栏 | 文件管理器) -->
|
||||||
<splitpanes :horizontal="true" style="height: 100%" :dbl-click-splitter="false">
|
<splitpanes :horizontal="true" style="height: 100%" :dbl-click-splitter="false">
|
||||||
<!-- 上方 Pane (终端) -->
|
<!-- 上方 Pane (终端) -->
|
||||||
<pane size="55" min-size="20" class="terminal-pane"> <!-- 调整大小 -->
|
<pane v-if="paneVisibility.terminal" size="55" min-size="20" class="terminal-pane pane-with-title"> <!-- 使用 v-if 控制, 添加 class -->
|
||||||
<div
|
<PaneTitleBar :title="t('layout.pane.terminal')" pane-name="terminal" />
|
||||||
v-for="tabInfo in sessionTabsWithStatus"
|
<div class="pane-content terminal-content-wrapper"> <!-- 添加包裹 div -->
|
||||||
:key="tabInfo.sessionId"
|
<div
|
||||||
|
v-for="tabInfo in sessionTabsWithStatus"
|
||||||
|
:key="tabInfo.sessionId"
|
||||||
v-show="tabInfo.sessionId === activeSessionId"
|
v-show="tabInfo.sessionId === activeSessionId"
|
||||||
class="terminal-session-wrapper"
|
class="terminal-session-wrapper"
|
||||||
>
|
>
|
||||||
@@ -214,26 +222,32 @@ onBeforeUnmount(() => {
|
|||||||
@data="sessionStore.sessions.get(tabInfo.sessionId)?.terminalManager.handleTerminalData"
|
@data="sessionStore.sessions.get(tabInfo.sessionId)?.terminalManager.handleTerminalData"
|
||||||
@resize="(dims) => { console.log(`[工作区视图 ${tabInfo.sessionId}] 收到 resize 事件:`, dims); sessionStore.sessions.get(tabInfo.sessionId)?.terminalManager.handleTerminalResize(dims); }"
|
@resize="(dims) => { console.log(`[工作区视图 ${tabInfo.sessionId}] 收到 resize 事件:`, dims); sessionStore.sessions.get(tabInfo.sessionId)?.terminalManager.handleTerminalResize(dims); }"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!activeSessionId" class="terminal-placeholder">
|
<div v-if="!activeSessionId" class="terminal-placeholder">
|
||||||
<h2>{{ t('workspace.selectConnectionPrompt') }}</h2>
|
<h2>{{ t('workspace.selectConnectionPrompt') }}</h2>
|
||||||
<p>{{ t('workspace.selectConnectionHint') }}</p>
|
<p>{{ t('workspace.selectConnectionHint') }}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</pane> <!-- End Terminal Pane -->
|
</pane> <!-- End Terminal Pane -->
|
||||||
|
|
||||||
<!-- 中间 Pane (命令栏) -->
|
<!-- 中间 Pane (命令栏) - 移除标题栏,但保留 v-if -->
|
||||||
<pane size="5" min-size="5" class="command-bar-pane">
|
<pane v-if="paneVisibility.commandBar" size="5" min-size="5" class="command-bar-pane">
|
||||||
<CommandInputBar
|
<CommandInputBar
|
||||||
v-if="activeSessionId"
|
v-if="activeSessionId"
|
||||||
@send-command="handleSendCommand"
|
@send-command="handleSendCommand"
|
||||||
/>
|
/>
|
||||||
</pane> <!-- End Command Bar Pane -->
|
</pane> <!-- End Command Bar Pane -->
|
||||||
|
|
||||||
<!-- 下方 Pane (文件管理器) -->
|
<!-- 下方 Pane (文件管理器区域 - 包含新的水平分割) -->
|
||||||
<pane size="40" min-size="15" class="file-manager-pane"> <!-- 调整大小 -->
|
<pane v-if="paneVisibility.fileManager" size="40" min-size="15" class="file-manager-area-pane pane-with-title"> <!-- 使用 v-if 控制, 添加 class -->
|
||||||
<div
|
<PaneTitleBar :title="t('layout.pane.fileManager')" pane-name="fileManager" />
|
||||||
v-for="tabInfo in sessionTabsWithStatus"
|
<!-- 新增:内部水平分割,允许未来添加列 -->
|
||||||
:key="tabInfo.sessionId + '-fm-wrapper'"
|
<splitpanes :horizontal="false" style="height: 100%" :dbl-click-splitter="false" class="pane-content"> <!-- 添加 class -->
|
||||||
|
<!-- 初始的文件管理器 Pane -->
|
||||||
|
<pane class="file-manager-pane"> <!-- 这个内部 pane 不需要 title bar -->
|
||||||
|
<div
|
||||||
|
v-for="tabInfo in sessionTabsWithStatus"
|
||||||
|
:key="tabInfo.sessionId + '-fm-wrapper'"
|
||||||
v-show="tabInfo.sessionId === activeSessionId"
|
v-show="tabInfo.sessionId === activeSessionId"
|
||||||
class="file-manager-wrapper"
|
class="file-manager-wrapper"
|
||||||
>
|
>
|
||||||
@@ -250,15 +264,20 @@ onBeforeUnmount(() => {
|
|||||||
isSftpReady: sessionStore.sessions.get(tabInfo.sessionId)!.wsManager.isSftpReady
|
isSftpReady: sessionStore.sessions.get(tabInfo.sessionId)!.wsManager.isSftpReady
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!activeSessionId" class="pane-placeholder">{{ t('fileManager.noActiveSession') }}</div>
|
<div v-if="!activeSessionId" class="pane-placeholder">{{ t('fileManager.noActiveSession') }}</div>
|
||||||
</pane> <!-- End File Manager Pane -->
|
</pane> <!-- End Inner File Manager Pane -->
|
||||||
</splitpanes> <!-- End Middle Area Splitpanes -->
|
<!-- 这里可以将来添加其他 Pane -->
|
||||||
|
</splitpanes> <!-- End Inner Horizontal Splitpanes -->
|
||||||
|
</pane> <!-- End File Manager Area Pane -->
|
||||||
|
</splitpanes> <!-- End Middle Area Vertical Splitpanes -->
|
||||||
</pane> <!-- End Middle Pane -->
|
</pane> <!-- End Middle Pane -->
|
||||||
|
|
||||||
<!-- 3. 右侧区域 1 Pane (文件编辑器) -->
|
<!-- 3. 右侧区域 1 Pane (文件编辑器) -->
|
||||||
<pane size="20" min-size="15" class="file-editor-pane"> <!-- 新增编辑器窗格 -->
|
<pane v-if="paneVisibility.editor" size="20" min-size="15" class="file-editor-pane pane-with-title"> <!-- 使用 v-if 控制, 添加 class -->
|
||||||
|
<PaneTitleBar :title="t('layout.pane.editor')" pane-name="editor" />
|
||||||
<FileEditorContainer
|
<FileEditorContainer
|
||||||
|
class="pane-content"
|
||||||
:tabs="editorTabs"
|
:tabs="editorTabs"
|
||||||
:active-tab-id="activeEditorTabId"
|
:active-tab-id="activeEditorTabId"
|
||||||
:session-id="activeSessionId"
|
:session-id="activeSessionId"
|
||||||
@@ -270,10 +289,12 @@ onBeforeUnmount(() => {
|
|||||||
</pane>
|
</pane>
|
||||||
|
|
||||||
<!-- 4. 右侧区域 2 Pane (状态监视器) -->
|
<!-- 4. 右侧区域 2 Pane (状态监视器) -->
|
||||||
<pane size="15" min-size="10" class="sidebar-pane status-monitor-pane"> <!-- 调整大小 -->
|
<pane v-if="paneVisibility.statusMonitor" size="15" min-size="10" class="sidebar-pane status-monitor-pane pane-with-title"> <!-- 使用 v-if 控制, 添加 class -->
|
||||||
<div
|
<PaneTitleBar :title="t('layout.pane.statusMonitor')" pane-name="statusMonitor" />
|
||||||
v-for="tabInfo in sessionTabsWithStatus"
|
<div class="pane-content status-monitor-content-wrapper"> <!-- 添加包裹 div -->
|
||||||
:key="tabInfo.sessionId + '-sm-wrapper'"
|
<div
|
||||||
|
v-for="tabInfo in sessionTabsWithStatus"
|
||||||
|
:key="tabInfo.sessionId + '-sm-wrapper'"
|
||||||
v-show="tabInfo.sessionId === activeSessionId"
|
v-show="tabInfo.sessionId === activeSessionId"
|
||||||
class="status-monitor-wrapper"
|
class="status-monitor-wrapper"
|
||||||
>
|
>
|
||||||
@@ -284,8 +305,9 @@ onBeforeUnmount(() => {
|
|||||||
:server-status="sessionStore.sessions.get(tabInfo.sessionId)!.statusMonitorManager.serverStatus.value"
|
:server-status="sessionStore.sessions.get(tabInfo.sessionId)!.statusMonitorManager.serverStatus.value"
|
||||||
:status-error="sessionStore.sessions.get(tabInfo.sessionId)!.statusMonitorManager.statusError.value"
|
:status-error="sessionStore.sessions.get(tabInfo.sessionId)!.statusMonitorManager.statusError.value"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="!activeSessionId" class="pane-placeholder">{{ t('statusMonitor.noActiveSession') }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!activeSessionId" class="pane-placeholder">{{ t('statusMonitor.noActiveSession') }}</div>
|
|
||||||
</pane>
|
</pane>
|
||||||
|
|
||||||
</splitpanes>
|
</splitpanes>
|
||||||
@@ -320,57 +342,102 @@ onBeforeUnmount(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 为 Pane 添加一些基本样式 */
|
/* 为 Pane 添加一些基本样式 */
|
||||||
|
.pane-with-title { /* 给包含标题栏的 Pane 添加基础样式 */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.pane-content { /* 让内容区域填充剩余空间 */
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: auto; /* 或者 hidden,根据需要 */
|
||||||
|
display: flex; /* 内部可能还需要 flex 布局 */
|
||||||
|
flex-direction: column; /* 默认列方向 */
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-pane, /* 用于左右侧边栏 */
|
.sidebar-pane, /* 用于左右侧边栏 */
|
||||||
.middle-pane, /* 中间包含终端、命令栏、文件管理器的 Pane */
|
.middle-pane, /* 中间包含终端、命令栏、文件管理器的 Pane */
|
||||||
.terminal-pane,
|
.terminal-pane,
|
||||||
.command-bar-pane,
|
.command-bar-pane,
|
||||||
.file-editor-pane, /* 新增编辑器窗格样式 */
|
.file-editor-pane, /* 编辑器窗格样式 */
|
||||||
.file-manager-pane,
|
.file-manager-area-pane, /* 文件管理器区域 Pane */
|
||||||
.status-monitor-pane { /* 添加状态监视器样式 */
|
.file-manager-pane, /* 内部文件管理器 Pane */
|
||||||
display: flex; /* 确保 Pane 内容可以正确布局 */
|
.status-monitor-pane { /* 状态监视器样式 */
|
||||||
flex-direction: column;
|
/* display: flex; flex-direction: column; overflow: hidden; 已被 pane-with-title 或 pane-content 处理 */
|
||||||
overflow: hidden; /* Pane 内部内容溢出时隐藏 */
|
|
||||||
background-color: #f8f9fa; /* 默认背景色 */
|
background-color: #f8f9fa; /* 默认背景色 */
|
||||||
}
|
}
|
||||||
.middle-pane {
|
.middle-pane {
|
||||||
padding: 0; /* 移除 middle-pane 的内边距 */
|
padding: 0; /* 移除 middle-pane 的内边距 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 命令栏 Pane 特定样式 - 恢复基本样式 */
|
/* 命令栏 Pane 特定样式 - 恢复原样 */
|
||||||
.command-bar-pane {
|
.command-bar-pane {
|
||||||
background-color: #e9ecef; /* 背景色 */
|
background-color: #e9ecef; /* 背景色 */
|
||||||
justify-content: center; /* 垂直居中输入框 */
|
justify-content: center; /* 垂直居中输入框 */
|
||||||
overflow: hidden; /* 内容不应超出 */
|
overflow: hidden; /* 内容不应超出 */
|
||||||
|
display: flex; /* 确保 flex 布局 */
|
||||||
|
align-items: center; /* 垂直居中 */
|
||||||
}
|
}
|
||||||
/* 调整内部 CommandInputBar 样式 */
|
/* 调整内部 CommandInputBar 样式 - 恢复原样 */
|
||||||
.command-bar-pane > .command-input-bar {
|
.command-bar-pane > .command-input-bar {
|
||||||
border: none; /* 移除 CommandInputBar 的边框 */
|
border: none;
|
||||||
background-color: transparent; /* 移除 CommandInputBar 的背景 */
|
background-color: transparent;
|
||||||
min-height: auto; /* 移除最小高度 */
|
min-height: auto;
|
||||||
padding: 2px 10px; /* 调整内边距 */
|
padding: 2px 10px; /* 恢复内边距 */
|
||||||
|
flex-grow: 1; /* 让输入框填充 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal-pane {
|
.terminal-pane {
|
||||||
background-color: #1e1e1e; /* 终端背景 */
|
background-color: #f8f9fa; /* 外层 pane 背景 */
|
||||||
position: relative; /* 保持相对定位用于占位符 */
|
/* position: relative; 由内部 wrapper 处理 */
|
||||||
|
}
|
||||||
|
.terminal-content-wrapper {
|
||||||
|
background-color: #1e1e1e; /* 终端实际背景 */
|
||||||
|
position: relative; /* 用于占位符 */
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.file-editor-pane {
|
.file-editor-pane {
|
||||||
background-color: #2d2d2d; /* 与编辑器容器背景一致 */
|
background-color: #f8f9fa; /* 外层 pane 背景 */
|
||||||
}
|
}
|
||||||
.file-manager-pane {
|
/* FileEditorContainer 自身需要 flex-grow: 1 */
|
||||||
/* 分隔线由 splitpanes 提供 */
|
.file-editor-pane > .pane-content {
|
||||||
|
background-color: #2d2d2d; /* 编辑器容器背景 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-manager-area-pane {
|
||||||
|
padding: 0;
|
||||||
|
background-color: #f8f9fa; /* 外层 pane 背景 */
|
||||||
|
}
|
||||||
|
/* 内部的 splitpanes 需要 flex-grow: 1 */
|
||||||
|
.file-manager-area-pane > .pane-content {
|
||||||
|
background-color: #f0f0f0; /* 内部区域背景 */
|
||||||
|
}
|
||||||
|
.file-manager-pane { /* 内部文件管理器 Pane */
|
||||||
background-color: #ffffff; /* 文件管理器使用浅色背景 */
|
background-color: #ffffff; /* 文件管理器使用浅色背景 */
|
||||||
|
display: flex; /* 确保内部 flex 布局 */
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.status-monitor-pane {
|
.status-monitor-pane {
|
||||||
/* 状态监视器样式 */
|
background-color: #f8f9fa; /* 外层 pane 背景 */
|
||||||
text-align: center;
|
/* text-align: center; 由内部 wrapper 处理 */
|
||||||
padding: 1rem;
|
/* padding: 1rem; 由内部 wrapper 处理 */
|
||||||
|
}
|
||||||
|
.status-monitor-content-wrapper {
|
||||||
|
text-align: center;
|
||||||
|
padding: 1rem;
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: auto; /* 允许内容滚动 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 终端会话包装器 */
|
/* 终端会话包装器 */
|
||||||
.terminal-session-wrapper {
|
.terminal-session-wrapper {
|
||||||
flex-grow: 1; /* 填充 terminal-pane */
|
flex-grow: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -387,9 +454,9 @@ onBeforeUnmount(() => {
|
|||||||
/* 状态监视器包装器 (内部组件应填充) */
|
/* 状态监视器包装器 (内部组件应填充) */
|
||||||
.status-monitor-wrapper {
|
.status-monitor-wrapper {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex; /* 使内部 StatusMonitorComponent 可以填充 */
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden; /* 内部组件自己处理滚动 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 终端占位符 */
|
/* 终端占位符 */
|
||||||
|
|||||||
Reference in New Issue
Block a user