From e722bb90a8314ac70324023ef708a5680a10eec1 Mon Sep 17 00:00:00 2001 From: chaiying Date: Thu, 18 Apr 2024 19:23:59 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=E6=9B=B4=E6=94=B9=E4=B8=AD?= =?UTF-8?q?=E5=BF=83=E8=BF=9E=E6=8E=A5=E6=A8=A1=E5=9D=97=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?&=E9=80=BB=E8=BE=91=EF=BC=9B=E3=80=90=E6=98=8E=E5=A4=A9?= =?UTF-8?q?=E5=92=8C=E5=90=8E=E7=AB=AF=E5=9C=A87=E7=8E=AF=E5=A2=83?= =?UTF-8?q?=E8=AF=95=E4=B8=80=E4=B8=8B=E8=83=BD=E4=B8=8D=E8=83=BD=E6=94=B6?= =?UTF-8?q?=E5=88=B0ip=E5=92=8Ctoken&=E6=8B=BF=E5=88=B0=E7=9A=84=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E6=96=87=E4=BB=B6=E6=80=8E=E4=B9=88=E5=A4=84=E7=90=86?= =?UTF-8?q?=E3=80=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/material/src/terminal/Terminal.tsx | 38 ++++---- .../src/terminal/components/WebTerminal.tsx | 90 ++++++++++++++----- packages/material/src/terminal/demo/basic.tsx | 8 +- packages/material/src/terminal/index.less | 64 +++++++++++++ 4 files changed, 159 insertions(+), 41 deletions(-) create mode 100644 packages/material/src/terminal/index.less diff --git a/packages/material/src/terminal/Terminal.tsx b/packages/material/src/terminal/Terminal.tsx index 2743f95..26ad084 100644 --- a/packages/material/src/terminal/Terminal.tsx +++ b/packages/material/src/terminal/Terminal.tsx @@ -1,13 +1,19 @@ import { Button, Form, Input,FormProps } from '@zhst/meta'; import React from 'react'; import WebTerminal from './components/WebTerminal'; +import './index.less'; 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 = (props:TerminalProps) => { - const {onFinish}=props; + const {onFinish,websocketUrl='',token='',ip='',onExportLog}=props; const [form] = Form.useForm(); const handleCenterConnect = async () => { @@ -16,15 +22,8 @@ const Terminal: React.FC = (props:TerminalProps) => { }; return ( -
-

连接中心服务器

+
+

连接中心服务器

= (props:TerminalProps) => { > - - + + +
-
- -
+
); }; diff --git a/packages/material/src/terminal/components/WebTerminal.tsx b/packages/material/src/terminal/components/WebTerminal.tsx index b2d316c..911164e 100644 --- a/packages/material/src/terminal/components/WebTerminal.tsx +++ b/packages/material/src/terminal/components/WebTerminal.tsx @@ -1,7 +1,8 @@ -// import { getToken } from '@libs/auth'; -import React, { Component } from 'react'; +// @ts-nocheck +import React, {Component,useRef,useEffect } from 'react'; import { Terminal } from 'xterm'; import { FitAddon } from 'xterm-addon-fit'; +import WebsocketTerm from './WebsocketTerm'; import 'xterm/css/xterm.css'; // TODO:引入xterm 后续需要和后端在建立websocket连接再次调试 @@ -9,8 +10,18 @@ export default class WebTerminal extends Component { term = null; websocket = null; 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() { let term = this.term; + // term初始化 this.term = new Terminal({ fontFamily: 'Menlo, Monaco, "Courier New", monospace', fontWeight: 400, @@ -20,24 +31,36 @@ export default class WebTerminal extends Component { 150) / 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.focus(); + this.term.focus(); // 光标聚集 this.term.prompt = (_) => { 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(); this.term.loadAddon(fitAddon); fitAddon.fit(); 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) => { const printable = @@ -63,7 +86,6 @@ export default class WebTerminal extends Component { this.term.write(e.key); } this.term.focus(); - console.log(1, 'print', e.key); }); this.term.onData((key) => { // 粘贴的情况 @@ -73,41 +95,69 @@ export default class WebTerminal extends Component { } }); 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() { this.term.dispose(); + // WebSocket 方法 关闭连接 this.websocket.close(); } + + initWebsock = () => { - // let websocket = this.websocket; let term = this.term; - let token = 'aaaa'; - this.websocket = new WebSocket('ws://' + window?._CONFIG?.WsSsh!, token); + let token = this.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) { term.write('connect'); }; + + // 连接关闭时触发 this.websocket.onclose = function (evt) { term.write('exit'); }; + + // 客户端接收服务端数据时触发 this.websocket.onmessage = function (evt) { term.write(evt.data); }; + + // 通信发生错误时触发 this.websocket.onerror = function (evt) { 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) => { this.websocket.send(message); }; render() { return ( -
-
+
+
); } diff --git a/packages/material/src/terminal/demo/basic.tsx b/packages/material/src/terminal/demo/basic.tsx index 514a7a6..0986155 100644 --- a/packages/material/src/terminal/demo/basic.tsx +++ b/packages/material/src/terminal/demo/basic.tsx @@ -1,14 +1,16 @@ -import React, { useState } from 'react'; +import React from 'react'; import { Terminal } from '@zhst/material'; const demo = () => { return ( -
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');}} /> -
); }; diff --git a/packages/material/src/terminal/index.less b/packages/material/src/terminal/index.less new file mode 100644 index 0000000..69998b1 --- /dev/null +++ b/packages/material/src/terminal/index.less @@ -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; +} \ No newline at end of file