release v1.2.0

This commit is contained in:
dushixiang
2021-10-31 17:15:35 +08:00
parent 4665ab6f78
commit 6132a05786
173 changed files with 37928 additions and 9349 deletions

View File

@ -7,13 +7,17 @@ import {getToken, isEmpty} from "../../utils/utils";
import {FitAddon} from 'xterm-addon-fit';
import "./Access.css"
import request from "../../common/request";
import {Affix, Button, Col, Drawer, message, Modal, Row} from "antd";
import {AppstoreTwoTone, ExclamationCircleOutlined} from "@ant-design/icons";
import {Affix, Button, Drawer, Dropdown, Menu, message, Modal, Tooltip} from "antd";
import {CodeOutlined, ExclamationCircleOutlined, FolderOutlined, LineChartOutlined} from "@ant-design/icons";
import Draggable from "react-draggable";
import FileSystem from "./FileSystem";
import FileSystem from "../devops/FileSystem";
import Stats from "./Stats";
import Message from "./Message";
class Term extends Component {
statsRef = undefined;
state = {
width: window.innerWidth,
height: window.innerHeight,
@ -21,7 +25,9 @@ class Term extends Component {
webSocket: undefined,
fitAddon: undefined,
sessionId: undefined,
enterBtnIndex: 1001
session: {},
enterBtnIndex: 1001,
commands: []
};
componentDidMount = async () => {
@ -30,7 +36,11 @@ class Term extends Component {
let assetId = urlParams.get('assetId');
document.title = urlParams.get('assetName');
let sessionId = await this.createSession(assetId);
let session = await this.createSession(assetId);
if (!session) {
return;
}
let sessionId = session['id'];
if (isEmpty(sessionId)) {
return;
}
@ -38,14 +48,13 @@ class Term extends Component {
let term = new Terminal({
fontFamily: 'monaco, Consolas, "Lucida Console", monospace',
fontSize: 15,
// theme: {
// background: '#1b1b1b',
// lineHeight: 17
// },
theme: {
background: '#1b1b1b'
},
rightClickSelectsWord: true,
});
term.open(this.refs.terminal);
term.open(document.getElementById('terminal'));
const fitAddon = new FitAddon();
term.loadAddon(fitAddon);
fitAddon.fit();
@ -55,6 +64,7 @@ class Term extends Component {
term.onSelectionChange(async () => {
let selection = term.getSelection();
console.log(`selection: [${selection}]`);
this.setState({
selection: selection
})
@ -73,7 +83,7 @@ class Term extends Component {
term.onData(data => {
let webSocket = this.state.webSocket;
if (webSocket !== undefined) {
webSocket.send(JSON.stringify({type: 'data', content: data}));
webSocket.send(new Message(Message.Data, data).toString());
}
});
@ -92,8 +102,8 @@ class Term extends Component {
let pingInterval;
webSocket.onopen = (e => {
pingInterval = setInterval(() => {
webSocket.send(JSON.stringify({type: 'ping'}))
}, 5000);
webSocket.send(new Message(Message.Ping, "").toString());
}, 1000);
});
webSocket.onerror = (e) => {
@ -107,16 +117,17 @@ class Term extends Component {
}
webSocket.onmessage = (e) => {
let msg = JSON.parse(e.data);
let msg = Message.parse(e.data);
switch (msg['type']) {
case 'connected':
case Message.Connected:
term.clear();
this.updateSessionStatus(sessionId);
this.getCommands();
break;
case 'data':
case Message.Data:
term.write(msg['content']);
break;
case 'closed':
case Message.Closed:
term.writeln(`\x1B[1;3;31m${msg['content']}\x1B[0m `)
webSocket.close();
break;
@ -129,10 +140,14 @@ class Term extends Component {
term: term,
webSocket: webSocket,
fitAddon: fitAddon,
sessionId: sessionId
sessionId: sessionId,
session: session
});
window.addEventListener('resize', this.onWindowResize);
window.onunload = function () {
webSocket.close();
};
}
componentWillUnmount() {
@ -142,6 +157,17 @@ class Term extends Component {
}
}
getCommands = async () => {
let result = await request.get('/commands');
if (result.code !== 1) {
message.error(result.message);
return;
}
this.setState({
commands: result['data']
})
}
showMessage(msg) {
message.destroy();
Modal.confirm({
@ -164,9 +190,9 @@ class Term extends Component {
let result = await request.post(`/sessions?assetId=${assetsId}&mode=naive`);
if (result['code'] !== 1) {
this.showMessage(result['message']);
return null;
return undefined;
}
return result['data']['id'];
return result['data'];
}
updateSessionStatus = async (sessionId) => {
@ -194,30 +220,62 @@ class Term extends Component {
}, () => {
if (webSocket && webSocket.readyState === WebSocket.OPEN) {
fitAddon.fit();
term.focus();
this.focus();
let terminalSize = {
cols: term.cols,
rows: term.rows
}
webSocket.send(JSON.stringify({type: 'resize', content: JSON.stringify(terminalSize)}));
webSocket.send(new Message(Message.Resize, window.btoa(JSON.stringify(terminalSize))).toString());
}
});
};
writeCommand = (command) => {
let webSocket = this.state.webSocket;
if (webSocket !== undefined) {
webSocket.send(new Message(Message.Data, command));
}
this.focus();
}
focus = () => {
let term = this.state.term;
if (term) {
term.focus();
}
}
onRef = (statsRef) => {
this.statsRef = statsRef;
}
render() {
const cmdMenuItems = this.state.commands.map(item => {
return <Tooltip placement="left" title={item['content']} color='blue' key={'t-' + item['id']}>
<Menu.Item onClick={() => {
this.writeCommand(item['content'])
}} key={'i-' + item['id']}>{item['name']}</Menu.Item>
</Tooltip>;
});
const cmdMenu = (
<Menu>
{cmdMenuItems}
</Menu>
);
return (
<div>
<div ref='terminal' id='terminal' style={{
<div id='terminal' style={{
height: this.state.height,
width: this.state.width,
backgroundColor: 'black',
overflowX: 'hidden',
overflowY: 'hidden',
backgroundColor: '#1b1b1b'
}}/>
<Draggable>
<Affix style={{position: 'absolute', top: 50, right: 50, zIndex: this.state.enterBtnIndex}}>
<Button icon={<AppstoreTwoTone/>} onClick={() => {
<Button icon={<FolderOutlined/>} onClick={() => {
this.setState({
fileSystemVisible: true,
enterBtnIndex: 999, // xterm.js 输入框的zIndex是1000在弹出文件管理页面后要隐藏此按钮
@ -226,6 +284,28 @@ class Term extends Component {
</Affix>
</Draggable>
<Draggable>
<Affix style={{position: 'absolute', top: 50, right: 100, zIndex: this.state.enterBtnIndex}}>
<Dropdown overlay={cmdMenu} trigger={['click']} placement="bottomLeft">
<Button icon={<CodeOutlined/>}/>
</Dropdown>
</Affix>
</Draggable>
<Draggable>
<Affix style={{position: 'absolute', top: 100, right: 100, zIndex: this.state.enterBtnIndex}}>
<Button icon={<LineChartOutlined/>} onClick={() => {
this.setState({
statsVisible: true,
enterBtnIndex: 999, // xterm.js 输入框的zIndex是1000在弹出文件管理页面后要隐藏此按钮
});
if (this.statsRef) {
this.statsRef.addInterval();
}
}}/>
</Affix>
</Draggable>
<Drawer
title={'会话详情'}
placement="right"
@ -237,16 +317,39 @@ class Term extends Component {
fileSystemVisible: false,
enterBtnIndex: 1001, // xterm.js 输入框的zIndex是1000在隐藏文件管理页面后要显示此按钮
});
this.focus();
}}
visible={this.state.fileSystemVisible}
>
<FileSystem
storageId={this.state.sessionId}
storageType={'sessions'}
upload={this.state.session['upload'] === '1'}
download={this.state.session['download'] === '1'}
delete={this.state.session['delete'] === '1'}
rename={this.state.session['rename'] === '1'}
edit={this.state.session['edit'] === '1'}
minHeight={window.innerHeight - 103}/>
</Drawer>
<Row style={{marginTop: 10}}>
<Col span={24}>
<FileSystem sessionId={this.state.sessionId}/>
</Col>
</Row>
<Drawer
title={'状态信息'}
placement="right"
width={window.innerWidth * 0.8}
closable={true}
onClose={() => {
this.setState({
statsVisible: false,
enterBtnIndex: 1001, // xterm.js 输入框的zIndex是1000在隐藏文件管理页面后要显示此按钮
});
this.focus();
if (this.statsRef) {
this.statsRef.delInterval();
}
}}
visible={this.state.statsVisible}
>
<Stats sessionId={this.state.sessionId} onRef={this.onRef}/>
</Drawer>
</div>
);