mirror of
https://github.com/chaos-zhu/easynode.git
synced 2026-10-07 12:48:28 +08:00
feat: 重构布局&sftp
This commit is contained in:
+1
-1
@@ -6,7 +6,7 @@
|
||||
<meta name="viewport"
|
||||
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0">
|
||||
<title>EasyNode</title>
|
||||
<script src="//at.alicdn.com/t/c/font_3309550_vrpmw9jmjf8.js"></script>
|
||||
<script src="//at.alicdn.com/t/c/font_3309550_ldx6nvwonvh.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -17,7 +17,7 @@ textarea {
|
||||
|
||||
// 滚动条整体部分
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
width: 0px; // 暂且隐藏滚动条
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
>
|
||||
<div class="group_container">
|
||||
<div class="header">
|
||||
<el-button type="primary" @click="addGroup">添加分组</el-button>
|
||||
<el-button type="primary" size="small" @click="addGroup">添加分组</el-button>
|
||||
</div>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column prop="index" label="序号" />
|
||||
@@ -39,8 +39,15 @@
|
||||
<el-table-column label="操作" fixed="right" width="160px">
|
||||
<template #default="{ row }">
|
||||
<template v-if="row.id !== 'builtin'">
|
||||
<el-button type="primary" @click="handleChange(row)">修改</el-button>
|
||||
<el-button v-show="row.id !== 'default'" type="danger" @click="deleteGroup(row)">删除</el-button>
|
||||
<el-button type="primary" size="small" @click="handleChange(row)">修改</el-button>
|
||||
<el-button
|
||||
v-show="row.id !== 'default'"
|
||||
type="danger"
|
||||
size="small"
|
||||
@click="deleteGroup(row)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span>--</span>
|
||||
@@ -72,8 +79,8 @@
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="groupFormVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="updateForm">确定</el-button>
|
||||
<el-button size="small" @click="groupFormVisible = false">取消</el-button>
|
||||
<el-button type="primary" size="small" @click="updateForm">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div ref="footerBarContainerRef" class="footer_bar_container">
|
||||
<div ref="adjustRef" class="adjust" />
|
||||
<!-- <div ref="adjustRef" class="adjust" /> -->
|
||||
<div class="footer_bar_content">
|
||||
<el-tabs v-model="activeTab" type="border-card" class="footer_bar_tabs">
|
||||
<el-tab-pane label="文件传输" name="sftp">
|
||||
<!-- <el-tab-pane label="文件传输" name="sftp">
|
||||
<Sftp :host-id="hostId" />
|
||||
</el-tab-pane>
|
||||
</el-tab-pane> -->
|
||||
<el-tab-pane label="脚本库" name="script">
|
||||
<ScriptInput :host-id="hostId" @exec-command="execCommand" />
|
||||
</el-tab-pane>
|
||||
@@ -18,7 +18,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import { ref, onMounted, nextTick, onBeforeUnmount } from 'vue'
|
||||
import { EventBus } from '@/utils'
|
||||
import Sftp from './sftp.vue'
|
||||
import ScriptInput from './script-input.vue'
|
||||
@@ -34,35 +34,69 @@ defineProps({
|
||||
const emit = defineEmits(['resize', 'exec-command',])
|
||||
const footerBarContainerRef = ref(null)
|
||||
const adjustRef = ref(null)
|
||||
const activeTab = ref('sftp')
|
||||
const activeTab = ref('script')
|
||||
|
||||
const adjustHeight = async () => {
|
||||
let startAdjust = false
|
||||
let timer = null
|
||||
await nextTick()
|
||||
|
||||
try {
|
||||
let footerBarHeight = localStorage.getItem('footerBarHeight')
|
||||
if (footerBarHeight) footerBarContainerRef.value.style.height = footerBarHeight
|
||||
adjustRef.value.addEventListener('mousedown', () => {
|
||||
const savedHeight = localStorage.getItem('footerBarHeight')
|
||||
if (savedHeight) {
|
||||
footerBarContainerRef.value.style.height = savedHeight
|
||||
}
|
||||
|
||||
let startAdjust = false
|
||||
let timer = null
|
||||
|
||||
// 清理之前的事件监听器
|
||||
const cleanup = () => {
|
||||
if (timer) {
|
||||
clearTimeout(timer)
|
||||
timer = null
|
||||
}
|
||||
}
|
||||
|
||||
const handleMouseDown = () => {
|
||||
startAdjust = true
|
||||
})
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
}
|
||||
|
||||
const handleMouseMove = (e) => {
|
||||
if (!startAdjust) return
|
||||
|
||||
if (timer) clearTimeout(timer)
|
||||
timer = setTimeout(() => {
|
||||
footerBarHeight = `calc(100vh - ${ e.pageY }px - 20px)`
|
||||
const footerBarHeight = `calc(100vh - ${ e.pageY }px - 20px)`
|
||||
footerBarContainerRef.value.style.height = footerBarHeight
|
||||
emit('resize')
|
||||
})
|
||||
})
|
||||
document.addEventListener('mouseup', () => {
|
||||
timer = null
|
||||
}, 0)
|
||||
}
|
||||
|
||||
const handleMouseUp = () => {
|
||||
if (!startAdjust) return
|
||||
startAdjust = false
|
||||
localStorage.setItem('footerBarHeight', footerBarHeight)
|
||||
|
||||
const currentHeight = footerBarContainerRef.value.style.height
|
||||
footerBarContainerRef.value.style.height = `${ currentHeight } !important`
|
||||
footerBarContainerRef.value.style.maxHeight = `${ currentHeight } !important`
|
||||
localStorage.setItem('footerBarHeight', currentHeight)
|
||||
EventBus.$emit('update-footer-bar-height')
|
||||
})
|
||||
emit('resize')
|
||||
cleanup()
|
||||
}
|
||||
|
||||
adjustRef.value.addEventListener('mousedown', handleMouseDown)
|
||||
document.addEventListener('mousemove', handleMouseMove)
|
||||
document.addEventListener('mouseup', handleMouseUp)
|
||||
|
||||
return () => {
|
||||
cleanup()
|
||||
adjustRef.value?.removeEventListener('mousedown', handleMouseDown)
|
||||
document.removeEventListener('mousemove', handleMouseMove)
|
||||
document.removeEventListener('mouseup', handleMouseUp)
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(error.message)
|
||||
console.warn('adjustHeight error:', error.message)
|
||||
return () => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,12 +104,23 @@ const execCommand = (command) => {
|
||||
emit('exec-command', command)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
adjustHeight()
|
||||
EventBus.$on('update-footer-bar-height', () => {
|
||||
adjustHeight()
|
||||
// 保存清理函数的引用
|
||||
let cleanupAdjustHeight = null
|
||||
|
||||
onMounted(async () => {
|
||||
cleanupAdjustHeight = await adjustHeight()
|
||||
EventBus.$on('update-footer-bar-height', async () => {
|
||||
if (cleanupAdjustHeight) cleanupAdjustHeight()
|
||||
cleanupAdjustHeight = await adjustHeight()
|
||||
})
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (cleanupAdjustHeight) {
|
||||
cleanupAdjustHeight()
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -84,7 +129,7 @@ onMounted(() => {
|
||||
position: relative;
|
||||
background: #ffffff;
|
||||
border: 1px solid var(--el-border-color);
|
||||
min-height: 100px;
|
||||
height: 100%;
|
||||
.adjust {
|
||||
user-select: none;
|
||||
position: absolute;
|
||||
|
||||
@@ -287,26 +287,9 @@ onBeforeUnmount(() => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.info_container {
|
||||
// border-top: 1px solid var(--el-border-color);
|
||||
flex-shrink: 0;
|
||||
// overflow: scroll;
|
||||
transition: all 0.15s;
|
||||
height: 100%;
|
||||
|
||||
// header {
|
||||
// display: flex;
|
||||
// justify-content: space-between;
|
||||
// align-items: center;
|
||||
// height: 30px;
|
||||
// margin: 10px;
|
||||
// position: relative;
|
||||
|
||||
// img {
|
||||
// cursor: pointer;
|
||||
// height: 80%;
|
||||
// }
|
||||
// }
|
||||
|
||||
// 表格中系统标识的title
|
||||
.item-title {
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
@@ -317,7 +300,7 @@ onBeforeUnmount(() => {
|
||||
|
||||
.host-info-ip {
|
||||
word-break: break-all;
|
||||
max-width: 100px;
|
||||
max-width: 92px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-right: 10px;
|
||||
|
||||
@@ -66,13 +66,14 @@
|
||||
</template>
|
||||
<div v-else class="empty-list">
|
||||
<el-empty
|
||||
:image-size="60"
|
||||
:image-size="38"
|
||||
description="暂无脚本"
|
||||
>
|
||||
<template #description>
|
||||
<p>当前分组暂无脚本</p>
|
||||
</template>
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="handleAddScript"
|
||||
>
|
||||
@@ -92,20 +93,24 @@
|
||||
placeholder="请输入脚本内容..."
|
||||
/>
|
||||
<div class="action_btn">
|
||||
<el-button
|
||||
v-show="scriptContent"
|
||||
type="primary"
|
||||
plain
|
||||
@click="handleSaveAsScript"
|
||||
>
|
||||
保存脚本
|
||||
</el-button>
|
||||
<el-button
|
||||
<el-dropdown
|
||||
split-button
|
||||
trigger="click"
|
||||
type="primary"
|
||||
size="small"
|
||||
:disabled="!scriptContent"
|
||||
@click="handleSendToTerminal"
|
||||
>
|
||||
发送到终端
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item>发送至当前窗口终端</el-dropdown-item>
|
||||
<el-dropdown-item>发送至所有窗口终端</el-dropdown-item>
|
||||
<el-dropdown-item @click="handleSaveAsScript">保存至脚本库</el-dropdown-item>
|
||||
<el-dropdown-item @click="handleSendClearContent">发送后清空内容: {{ isClearContent ? '是' : '否' }}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
发送至终端
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -149,6 +154,7 @@ const activeGroup = ref('')
|
||||
const scriptEditVisible = ref(false)
|
||||
const editScriptData = ref(null)
|
||||
const scriptGroupVisible = ref(false)
|
||||
const isClearContent = ref(false)
|
||||
|
||||
const isPlusActive = computed(() => $store.isPlusActive)
|
||||
const scriptGroupList = computed(() => $store.scriptGroupList || [])
|
||||
@@ -185,9 +191,14 @@ const handleSendToEditor = (script) => {
|
||||
|
||||
const handleSendToTerminal = () => {
|
||||
if (!isPlusActive.value) return $message.warning('此功能仅限PLUS版使用')
|
||||
if (!scriptContent.value) return
|
||||
emit('exec-command', scriptContent.value)
|
||||
scriptContent.value = ''
|
||||
if (isClearContent.value) {
|
||||
scriptContent.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
const handleSendClearContent = () => {
|
||||
isClearContent.value = !isClearContent.value
|
||||
}
|
||||
|
||||
const handleAddScript = () => {
|
||||
@@ -271,6 +282,7 @@ if (scriptGroupList.value.length) {
|
||||
font-size: 13px;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
flex-shrink: 0;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-fill-color-light);
|
||||
@@ -287,7 +299,6 @@ if (scriptGroupList.value.length) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
background-color: var(--el-color-info-light-9);
|
||||
border-color: var(--el-color-info-light-5);
|
||||
color: var(--el-text-color-regular);
|
||||
|
||||
@@ -295,11 +306,6 @@ if (scriptGroupList.value.length) {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-color-info-light-7);
|
||||
border-color: var(--el-color-info);
|
||||
color: var(--el-color-info-dark-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -317,10 +323,10 @@ if (scriptGroupList.value.length) {
|
||||
color: var(--el-text-color-secondary);
|
||||
|
||||
:deep(.el-empty__description) {
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 0px;
|
||||
p {
|
||||
margin: 0;
|
||||
line-height: 1.5;
|
||||
line-height: 0.5;
|
||||
|
||||
&.sub-text {
|
||||
font-size: 12px;
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div class="sftp_v2_tab_container">
|
||||
test
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.sftp_v2_tab_container {
|
||||
height: 100%;
|
||||
section {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
.box {
|
||||
$header_height: 30px;
|
||||
.header {
|
||||
user-select: none;
|
||||
height: $header_height;
|
||||
padding: 0 5px;
|
||||
background-color: var(--el-fill-color-light);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
.operation {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// margin-right: 20px;
|
||||
.img {
|
||||
margin: 0 5px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
&:hover {
|
||||
background: #cec4c4;
|
||||
}
|
||||
}
|
||||
}
|
||||
.filter_input {
|
||||
width: 200px;
|
||||
min-width: 200px;
|
||||
margin: 0 20px 0 10px;
|
||||
}
|
||||
.path_input {
|
||||
width: 450px;
|
||||
min-width: 450px;
|
||||
}
|
||||
.path {
|
||||
flex: 1;
|
||||
user-select: all;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
.up-file-progress-wrap {
|
||||
min-width: 200px;
|
||||
max-width: 350px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -695,6 +695,7 @@ defineExpose({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.terminal_tab_container {
|
||||
height: 100%;
|
||||
min-height: 200px;
|
||||
position: relative;
|
||||
.terminal_container {
|
||||
|
||||
@@ -152,7 +152,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-drawer
|
||||
<!-- <el-drawer
|
||||
v-if="isMobileScreen"
|
||||
v-model="showMobileInfoSideDialog"
|
||||
:with-header="false"
|
||||
@@ -173,59 +173,126 @@
|
||||
:visible="visible"
|
||||
:ping-data="pingData"
|
||||
/>
|
||||
</div>
|
||||
<div class="terminal_and_sftp_wrap">
|
||||
<el-tabs
|
||||
v-model="activeTabIndex"
|
||||
type="border-card"
|
||||
tab-position="top"
|
||||
@tab-remove="removeTab"
|
||||
@tab-change="tabChange"
|
||||
</div> -->
|
||||
<el-tabs
|
||||
v-model="activeTabIndex"
|
||||
type="border-card"
|
||||
tab-position="top"
|
||||
class="tabs_container"
|
||||
@tab-remove="removeTab"
|
||||
@tab-change="tabChange"
|
||||
>
|
||||
<el-tab-pane
|
||||
v-for="(item, index) in terminalTabs"
|
||||
:key="item.key"
|
||||
:label="item.name"
|
||||
:name="index"
|
||||
:closable="true"
|
||||
>
|
||||
<el-tab-pane
|
||||
v-for="(item, index) in terminalTabs"
|
||||
:key="item.key"
|
||||
:label="item.name"
|
||||
:name="index"
|
||||
:closable="true"
|
||||
class="el_tab_pane"
|
||||
>
|
||||
<template #label>
|
||||
<div class="tab_label">
|
||||
<span
|
||||
class="tab_status"
|
||||
:style="{ background: getStatusColor(item.status) }"
|
||||
/>
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="tab_content_wrap" :style="{ height: mainHeight + 'px' }">
|
||||
<!-- @cd-command="cdCommand" -->
|
||||
<TerminalTab
|
||||
ref="terminalRefs"
|
||||
:host-obj="item"
|
||||
:long-press-ctrl="longPressCtrl"
|
||||
:long-press-alt="longPressAlt"
|
||||
@input-command="terminalInput"
|
||||
@ping-data="getPingData"
|
||||
@reset-long-press="resetLongPress"
|
||||
<template #label>
|
||||
<div class="tab_label">
|
||||
<span
|
||||
class="tab_status"
|
||||
:style="{ background: getStatusColor(item.status) }"
|
||||
/>
|
||||
<FloatMenu
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="tab_content_wrap">
|
||||
<div class="tab_content_header">
|
||||
<div class="tab_content_wrap_header_item active">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
content="状态"
|
||||
placement="bottom"
|
||||
>
|
||||
<svg-icon name="icon-zhuangtai" class="icon" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="tab_content_wrap_header_item">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
content="Sftp"
|
||||
placement="bottom"
|
||||
>
|
||||
<svg-icon name="icon-sftp" class="icon" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="tab_content_wrap_header_item">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
content="同步输入到所有分屏"
|
||||
placement="bottom"
|
||||
>
|
||||
<svg-icon name="icon-lianjie" class="icon" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="tab_content_wrap_header_item">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
content="上下分屏"
|
||||
placement="bottom"
|
||||
>
|
||||
<svg-icon name="icon-a-06gaodufenping" class="icon" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="tab_content_wrap_header_item">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
content="左右分屏"
|
||||
placement="bottom"
|
||||
>
|
||||
<svg-icon name="icon-a-05kuandufenping" class="icon" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab_content_main">
|
||||
<div class="tab_content_main_info">
|
||||
<InfoSide
|
||||
ref="infoSideRef"
|
||||
:host-info="curHost"
|
||||
:visible="visible"
|
||||
:ping-data="pingData"
|
||||
/>
|
||||
</div>
|
||||
<div class="tab_content_main_terminals">
|
||||
<!-- @cd-command="cdCommand" -->
|
||||
<TerminalTab
|
||||
ref="terminalRefs"
|
||||
:host-obj="item"
|
||||
:long-press-ctrl="longPressCtrl"
|
||||
:long-press-alt="longPressAlt"
|
||||
@input-command="terminalInput"
|
||||
@ping-data="getPingData"
|
||||
@reset-long-press="resetLongPress"
|
||||
/>
|
||||
</div>
|
||||
<div class="tab_content_main_sftp">
|
||||
<SftpV2
|
||||
:host-id="item.id"
|
||||
@resize="resizeTerminal"
|
||||
@exec-command="handleInputCommand"
|
||||
/>
|
||||
</div>
|
||||
<!-- <FloatMenu
|
||||
v-if="isMobileScreen"
|
||||
:long-press-ctrl="longPressCtrl"
|
||||
:long-press-alt="longPressAlt"
|
||||
@click-key="handleClickVirtualKeyboard"
|
||||
/>
|
||||
/> -->
|
||||
</div>
|
||||
<div
|
||||
:class="['tab_content_footer', { 'show_footer_bar': showFooterBar }]"
|
||||
>
|
||||
<FooterBar
|
||||
v-if="showFooterBar"
|
||||
:host-id="item.id"
|
||||
@resize="resizeTerminal"
|
||||
@exec-command="handleInputCommand"
|
||||
/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<InputCommand
|
||||
v-model:show="showInputCommand"
|
||||
@@ -263,6 +330,7 @@ import InfoSide from './info-side.vue'
|
||||
import HostForm from '../../server/components/host-form.vue'
|
||||
import TerminalSetting from './terminal-setting.vue'
|
||||
import FooterBar from './footer-bar.vue'
|
||||
import SftpV2 from './sftp-v2.vue'
|
||||
|
||||
const {
|
||||
proxy: { $nextTick, $store, $message }
|
||||
@@ -369,15 +437,6 @@ const formatScriptList = computed(() => {
|
||||
}))
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
handleResizeTerminalSftp()
|
||||
window.addEventListener('resize', handleResizeTerminalSftp)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', handleResizeTerminalSftp)
|
||||
})
|
||||
|
||||
const getStatusColor = (status) => {
|
||||
return (
|
||||
terminalStatusList.find((item) => item.value === status)?.color || 'gray'
|
||||
@@ -395,13 +454,6 @@ const handleUpdateList = async ({ host }) => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleResizeTerminalSftp = () => {
|
||||
$nextTick(() => {
|
||||
mainHeight.value =
|
||||
document.querySelector('.terminal_and_sftp_wrap')?.offsetHeight - 45 // 45 is tab-header height+15
|
||||
})
|
||||
}
|
||||
|
||||
const handleCloseAllTab = () => {
|
||||
emit('close-all-tab')
|
||||
}
|
||||
@@ -546,10 +598,12 @@ watch(
|
||||
|
||||
watch(
|
||||
showFooterBar,
|
||||
async () => {
|
||||
localStorage.setItem('showFooterBar', showFooterBar.value)
|
||||
await $nextTick()
|
||||
resizeTerminal()
|
||||
() => {
|
||||
setTimeout(async () => {
|
||||
localStorage.setItem('showFooterBar', showFooterBar.value)
|
||||
await $nextTick()
|
||||
resizeTerminal()
|
||||
}, 210)
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
@@ -605,13 +659,10 @@ const handleInputCommand = async (command) => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.terminal_wrap {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
height: 100%;
|
||||
// height: 100%;
|
||||
|
||||
:deep(.el-tabs__content) {
|
||||
// width: 100%;
|
||||
padding: 0 0 5px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:deep(.el-tabs--border-card) {
|
||||
@@ -694,12 +745,7 @@ const handleInputCommand = async (command) => {
|
||||
border: var(--el-descriptions-table-border);
|
||||
}
|
||||
|
||||
.terminal_and_sftp_wrap {
|
||||
height: calc(100% - $terminalTopHeight);
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.tabs_container {
|
||||
position: relative;
|
||||
|
||||
.tab_label {
|
||||
@@ -719,13 +765,68 @@ const handleInputCommand = async (command) => {
|
||||
}
|
||||
|
||||
.tab_content_wrap {
|
||||
flex: 1;
|
||||
height: calc(100vh - 142px);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
:deep(.terminal_tab_container) {
|
||||
flex: 1;
|
||||
.tab_content_header {
|
||||
height: 30px;
|
||||
min-height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// border-bottom: 1px solid var(--el-border-color);
|
||||
.tab_content_wrap_header_item {
|
||||
padding: 0 15px;
|
||||
&.active {
|
||||
color: var(--el-color-success);
|
||||
}
|
||||
.icon {
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.tab_content_main {
|
||||
flex: 1;
|
||||
min-height: 300px;
|
||||
display: flex;
|
||||
|
||||
.tab_content_main_info {
|
||||
width: 250px;
|
||||
min-width: 250px;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.tab_content_main_terminals {
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.tab_content_main_sftp {
|
||||
height: 100%;
|
||||
width: 450px;
|
||||
min-width: 450px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.tab_content_footer {
|
||||
transition: all 0.2s;
|
||||
height: 0;
|
||||
min-height: 0;
|
||||
&.show_footer_bar {
|
||||
height: 250px;
|
||||
min-height: 250px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.full-screen-button {
|
||||
|
||||
Reference in New Issue
Block a user