A web terminal支持自由调整宽度

pull/462/head
vapao 2022-03-19 17:10:24 +08:00
parent 33ec843fa6
commit 499a06d1bd
4 changed files with 52 additions and 12 deletions

View File

@ -5,7 +5,7 @@
"dependencies": {
"@ant-design/icons": "^4.3.0",
"ace-builds": "^1.4.13",
"antd": "^4.10.3",
"antd": "^4.19.2",
"axios": "^0.21.0",
"bizcharts": "^3.5.9",
"history": "^4.10.1",

View File

@ -3,7 +3,7 @@
* Copyright (c) <spug.dev@gmail.com>
* Released under the AGPL-3.0 License.
*/
import React, { useEffect, useState, useRef } from 'react';
import React, { useEffect, useState, useRef, useLayoutEffect } from 'react';
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
import { X_TOKEN } from 'libs';
@ -14,9 +14,9 @@ import styles from './index.module.less';
function WebSSH(props) {
const container = useRef();
const [term] = useState(new Terminal());
const [fitPlugin] = useState(new FitAddon());
useEffect(() => {
const fitPlugin = new FitAddon();
term.loadAddon(fitPlugin);
term.setOption('fontFamily', 'Source Code Pro, Courier New, Courier, Monaco, monospace, PingFang SC, Microsoft YaHei')
term.open(container.current);
@ -33,12 +33,15 @@ function WebSSH(props) {
setTimeout(() => term.write('\r\nConnection is closed.\r\n'), 200)
};
term.onData(data => socket.send(JSON.stringify({data})));
term.onResize(({cols, rows}) => socket.send(JSON.stringify({resize: [cols, rows]})));
const resize = () => fitPlugin.fit();
window.addEventListener('resize', resize)
term.onResize(({cols, rows}) => {
if (socket.readyState === 1) {
socket.send(JSON.stringify({resize: [cols, rows]}))
}
});
window.addEventListener('resize', fitTerminal)
return () => {
window.removeEventListener('resize', resize);
window.removeEventListener('resize', fitTerminal);
if (socket) socket.close()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
@ -51,6 +54,19 @@ function WebSSH(props) {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.activeId])
useLayoutEffect(fitTerminal)
function fitTerminal() {
if (props.vId === props.activeId) {
const dims = fitPlugin.proposeDimensions();
if (!dims || !term || !dims.cols || !dims.rows) return;
if (term.rows !== dims.rows || term.cols !== dims.cols) {
term._core._renderService.clear();
term.resize(dims.cols, dims.rows);
}
}
}
function _read_as_text(data) {
const reader = new window.FileReader();
reader.onload = () => term.write(reader.result);

View File

@ -6,7 +6,13 @@
import React, { useEffect, useState } from 'react';
import { observer } from 'mobx-react';
import { Tabs, Tree, Input, Spin, Button } from 'antd';
import { FolderOutlined, FolderOpenOutlined, CloudServerOutlined, SearchOutlined, SyncOutlined } from '@ant-design/icons';
import {
FolderOutlined,
FolderOpenOutlined,
CloudServerOutlined,
SearchOutlined,
SyncOutlined
} from '@ant-design/icons';
import { NotFound, AuthButton } from 'components';
import Terminal from './Terminal';
import FileManager from './FileManager';
@ -15,6 +21,7 @@ import styles from './index.module.less';
import LogoSpugText from 'layout/logo-spug-txt.png';
import lds from 'lodash';
let posX = 0
function WebSSH(props) {
const [visible, setVisible] = useState(false);
@ -26,6 +33,7 @@ function WebSSH(props) {
const [hosts, setHosts] = useState([]);
const [activeId, setActiveId] = useState();
const [hostId, setHostId] = useState();
const [width, setWidth] = useState(280);
useEffect(() => {
window.document.title = 'Spug web terminal'
@ -125,6 +133,12 @@ function WebSSH(props) {
}
}
function handleMouseMove(e) {
if (posX) {
setWidth(e.pageX);
}
}
const spug_web_terminal =
' __ __ _ __\n' +
' _____ ____ __ __ ____ _ _ __ ___ / /_ / /_ ___ _____ ____ ___ (_)____ ____ _ / /\n' +
@ -134,8 +148,8 @@ function WebSSH(props) {
' /_/ /____/ \n'
return hasPermission('host.console.view') ? (
<div className={styles.container}>
<div className={styles.sider}>
<div className={styles.container} onMouseUp={() => posX = 0} onMouseMove={handleMouseMove}>
<div className={styles.sider} style={{width}}>
<div className={styles.logo}>
<img src={LogoSpugText} alt="logo"/>
</div>
@ -152,6 +166,7 @@ function WebSSH(props) {
onSelect={(k, e) => handleSelect(e)}/>
</Spin>
</div>
<div className={styles.split} onMouseDown={e => posX = e.pageX}/>
</div>
<div className={styles.content}>
<Tabs
@ -160,6 +175,7 @@ function WebSSH(props) {
type="editable-card"
onTabClick={key => setActiveId(key)}
onEdit={handleRemove}
style={{width: `calc(100vw - ${width}px)`}}
tabBarExtraContent={<AuthButton
auth="host.console.list"
type="primary"

View File

@ -7,6 +7,15 @@
flex-direction: column;
width: 280px;
background-color: #fafafa;
position: relative;
.split {
position: absolute;
width: 6px;
height: 100vh;
right: -3px;
cursor: ew-resize;
}
.logo {
height: 42px;
@ -32,7 +41,7 @@
.search {
margin: 12px 10px;
width: 220px;
width: calc(100% - 60px);
}
}
}
@ -42,7 +51,6 @@
flex-direction: column;
:global(.ant-tabs-nav) {
width: calc(100vw - 280px);
height: 42px;
margin: 0;
}