feat:更改中心连接模块样式&逻辑;【明天和后端在7环境试一下能不能收到ip和token&拿到的日志文件怎么处理】

This commit is contained in:
chaiying 2024-04-18 19:23:59 +08:00
parent 03d7b8dcbf
commit e722bb90a8
4 changed files with 159 additions and 41 deletions

View File

@ -1,13 +1,19 @@
import { Button, Form, Input,FormProps } from '@zhst/meta'; import { Button, Form, Input,FormProps } from '@zhst/meta';
import React from 'react'; import React from 'react';
import WebTerminal from './components/WebTerminal'; import WebTerminal from './components/WebTerminal';
import './index.less';
interface TerminalProps{ interface TerminalProps{
onFinish:FormProps['onFinish'] onFinish:FormProps['onFinish'];
onExportLog:(filePath:string)=>void;
websocketUrl:string; // websocket地址
token:string; // 用户token信息
ip:string; // ip地址
// filePath:string; // 导出日志的文件地址
} }
const materialName = 'zhst-material-terminal'
const Terminal: React.FC<TerminalProps> = (props:TerminalProps) => { const Terminal: React.FC<TerminalProps> = (props:TerminalProps) => {
const {onFinish}=props; const {onFinish,websocketUrl='',token='',ip='',onExportLog}=props;
const [form] = Form.useForm(); const [form] = Form.useForm();
const handleCenterConnect = async () => { const handleCenterConnect = async () => {
@ -16,15 +22,8 @@ const Terminal: React.FC<TerminalProps> = (props:TerminalProps) => {
}; };
return ( return (
<div style={{width:'100%',height:'100%',boxSizing:'border-box',padding:'30px',overflow:"hidden"}}> <div className={materialName} style={{}}>
<h1 style={{ <h1 ></h1>
fontWeight: 'bold',
fontSize: '16px',
textAlign: 'left',
lineHeight:'24px',
marginBottom: '20px',
color: 'rgba(0, 0, 0, 0.88)'
}}></h1>
<Form form={form} requiredMark={false} layout={'inline'} > <Form form={form} requiredMark={false} layout={'inline'} >
<Form.Item <Form.Item
label="输入中心服务器IP" label="输入中心服务器IP"
@ -48,14 +47,17 @@ const Terminal: React.FC<TerminalProps> = (props:TerminalProps) => {
<Input style={{ width: 320, height: 36 }} allowClear /> <Input style={{ width: 320, height: 36 }} allowClear />
</Form.Item> </Form.Item>
<Form.Item > <Form.Item >
<Button type="primary" onClick={handleCenterConnect}> <Button style={{ width: 100, height: 36,background:'#23ACB2' }} type="primary" onClick={handleCenterConnect}>
</Button>
</Form.Item>
<Form.Item >
<Button style={{ width: 100, height: 36, }} onClick={onExportLog}>
</Button> </Button>
</Form.Item> </Form.Item>
</Form> </Form>
<div style={{marginTop:30,width:'100%',height:'calc(100% - 120px)',boxSizing:'border-box',overflow:'hidden'}}> <WebTerminal websocketUrl={websocketUrl} token={token} ip={ip} />
<WebTerminal />
</div>
</div> </div>
); );
}; };

View File

@ -1,7 +1,8 @@
// import { getToken } from '@libs/auth'; // @ts-nocheck
import React, { Component } from 'react'; import React, {Component,useRef,useEffect } from 'react';
import { Terminal } from 'xterm'; import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit'; import { FitAddon } from 'xterm-addon-fit';
import WebsocketTerm from './WebsocketTerm';
import 'xterm/css/xterm.css'; import 'xterm/css/xterm.css';
// TODO:引入xterm 后续需要和后端在建立websocket连接再次调试 // TODO:引入xterm 后续需要和后端在建立websocket连接再次调试
@ -9,8 +10,18 @@ export default class WebTerminal extends Component {
term = null; term = null;
websocket = null; websocket = null;
curr_line = ''; curr_line = '';
websocketUrl=''; // websocket服务地址
token=''; // 用户token信息
ip=''; // IP地址
constructor(props:any){
super(props);
this.websocketUrl = props.websocketUrl;
this.token=props.token;
this.ip=props.ip;
}
componentDidMount() { componentDidMount() {
let term = this.term; let term = this.term;
// term初始化
this.term = new Terminal({ this.term = new Terminal({
fontFamily: 'Menlo, Monaco, "Courier New", monospace', fontFamily: 'Menlo, Monaco, "Courier New", monospace',
fontWeight: 400, fontWeight: 400,
@ -20,24 +31,36 @@ export default class WebTerminal extends Component {
150) / 150) /
14, 14,
), ),
convertEol: true,//控制终端是否自动将 \n 转换为 \r\n。
cursorBlink: true,//指定光标是否闪烁
scrollback: 50, //终端中的回滚量
disableStdin: false, //是否应禁用输入。
cursorStyle: "underline", //光标样式
windowsMode: true, // 根据窗口换行
theme: {
foreground: "#ffffff", //字体
background: "#1a1a1d", //背景色
cursor: "help", //设置光标
}
}); });
this.term.open(document.getElementById('terminal')); this.term.open(document.getElementById('terminal'));
this.term.focus(); this.term.focus(); // 光标聚集
this.term.prompt = (_) => { this.term.prompt = (_) => {
this.term.write('\r\n\x1b[33m$\x1b[0m '); this.term.write('\r\n\x1b[33m$\x1b[0m ');
}; };
this.term.prompt(); // // 换行并输入起始符
// this.term.prompt = (_) => {
// this.term.write("\r\n>>> ")
// }
if(this.ip!==''){
this.term.write('root@'+this.ip);
}
const fitAddon = new FitAddon(); const fitAddon = new FitAddon();
this.term.loadAddon(fitAddon); this.term.loadAddon(fitAddon);
fitAddon.fit(); fitAddon.fit();
this.term.prompt(); this.term.prompt();
// this.term.attachCustomKeyEventHandler((e) => {
// console.log({ e });
// // e = e.target;
// var keyCode = e.keyCode || e.which || e.charCode;
// const moveKey = [37, 38, 39, 40].includes(keyCode);
// if (moveKey) return false;
// });
// 添加事件监听器,支持输入方法 // 添加事件监听器,支持输入方法
this.term.onKey((e) => { this.term.onKey((e) => {
const printable = const printable =
@ -63,7 +86,6 @@ export default class WebTerminal extends Component {
this.term.write(e.key); this.term.write(e.key);
} }
this.term.focus(); this.term.focus();
console.log(1, 'print', e.key);
}); });
this.term.onData((key) => { this.term.onData((key) => {
// 粘贴的情况 // 粘贴的情况
@ -73,41 +95,69 @@ export default class WebTerminal extends Component {
} }
}); });
this.initWebsock(); this.initWebsock();
if(this.websocket){
// 只读属性 readyState 表示连接状态,可以是以下值
// 0 - 表示连接尚未建立。
// 1 - 表示连接已建立,可以进行通信。
// 2 - 表示连接正在进行关闭。
// 3 - 表示连接已经关闭或者连接不能打开
// websocket建立连接时发送ip以及token给后端
if(this.websocket.readyState ===1){
this.Send(this.term,{ip:this.ip,Authorization:this.token})
}
}
} }
componentWillUnmount() { componentWillUnmount() {
this.term.dispose(); this.term.dispose();
// WebSocket 方法 关闭连接
this.websocket.close(); this.websocket.close();
} }
initWebsock = () => { initWebsock = () => {
// let websocket = this.websocket;
let term = this.term; let term = this.term;
let token = 'aaaa'; let token = this.token;
this.websocket = new WebSocket('ws://' + window?._CONFIG?.WsSsh!, token); let ip = this.ip;
// let preSuffix=location.protocol === 'http:' ? 'ws://' : 'wss://';
let preSuffix='ws://127.0.0.1:50051/active';
// 初始化
this.websocket = new WebSocket('ws://127.0.0.1:50051/active');
// WebSocket 事件
// 连接建立时触发
this.websocket.onopen = function (evt) { this.websocket.onopen = function (evt) {
term.write('connect'); term.write('connect');
}; };
// 连接关闭时触发
this.websocket.onclose = function (evt) { this.websocket.onclose = function (evt) {
term.write('exit'); term.write('exit');
}; };
// 客户端接收服务端数据时触发
this.websocket.onmessage = function (evt) { this.websocket.onmessage = function (evt) {
term.write(evt.data); term.write(evt.data);
}; };
// 通信发生错误时触发
this.websocket.onerror = function (evt) { this.websocket.onerror = function (evt) {
term.write('connect fail err:' + evt.data); term.write('connect fail err:' + evt.data);
}; };
}; };
// prompt = (term) => {
// this.term.write('\r\n~$ ');
// };
prompt = (term) => {
this.term.write('\r\n~$ ');
};
// WebSocket 方法 使用连接发送数据
Send = (term, message) => { Send = (term, message) => {
this.websocket.send(message); this.websocket.send(message);
}; };
render() { render() {
return ( return (
<div className="container-children" style={{ height: '100%' }}> <div className="container-children">
<div id="terminal" style={{ width: '100%' }}></div> <div id="terminal"></div>
</div> </div>
); );
} }

View File

@ -1,14 +1,16 @@
import React, { useState } from 'react'; import React from 'react';
import { Terminal } from '@zhst/material'; import { Terminal } from '@zhst/material';
const demo = () => { const demo = () => {
return ( return (
<div style={{ padding: '24px', width: '500px', border: '1px solid #eee' }}>
<Terminal <Terminal
onFinish={val => console.log('val', val)} onFinish={val => console.log('val', val)}
websocketUrl={'ws://127.0.0.1:30003'}
token={'this is user token'}
ip={'127.0.0.1'}
onExportLog={(url)=>{console.log(url,'====>url');}}
/> />
</div>
); );
}; };

View File

@ -0,0 +1,64 @@
.zhst-material-terminal{
width: 100%;
height: 100vh;
background-color: #E5EAEC;
box-sizing: border-box;
padding:30px;
overflow: hidden;
h1{
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: bold;
font-size:16px;
text-align: left;
line-height: 24px;
margin-bottom: 20px;
color: rgba(0,0,0,80%);
}
}
.container-children{
margin-top: 30px;
width: 100%;
height: calc(100vh - 180px);
box-sizing: border-box;
#terminal{
width: 100%;
height: calc(100vh - 180px);
.xterm-screen{
width: 100%;
height: 100%;
background-color: #000;
}
}
}
.zhst-form-item .zhst-form-item-control-input-content{
.zhst-btn-default:not(:disabled):not(.zhst-btn-disabled):hover{
color: #23ACB2;
border-color:#23acb2;
}
.zhst-input-affix-wrapper:focus{
border-color:#23acb2;
}
.zhst-input-affix-wrapper:hover{
border-color:#23acb2;
}
}
*{
margin: 0;
padding: 0;
}