mirror of https://github.com/openspug/spug
A web terminal支持自由调整宽度
parent
33ec843fa6
commit
499a06d1bd
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue