Merge branch 'develop-cy' into 'develop'
🦄 refactor: 【CenterLink】完善中心对接模块物料; See merge request web-project/zhst-lambo!15
This commit is contained in:
commit
f2cc70c785
65
packages/material/src/centerLink/CenterLink.tsx
Normal file
65
packages/material/src/centerLink/CenterLink.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import React,{useRef} from 'react'
|
||||||
|
import TerminalForm from './components/TerminalForm'
|
||||||
|
import WebTerminal from './components/WebTerminal'
|
||||||
|
import './index.less';
|
||||||
|
import style from 'packages/meta/src/badge/style';
|
||||||
|
|
||||||
|
interface CenterLinkProps{
|
||||||
|
websocketUrl:string; // websocket服务地址
|
||||||
|
token:string; // 用户token信息
|
||||||
|
ip:string; // IP地址
|
||||||
|
onExportLogs:()=>void, // 导出日志事件
|
||||||
|
terminalStyle?:React.CSSProperties;
|
||||||
|
onConnect?:(values:any) => void, // 连接服务器事件
|
||||||
|
onOpen?: (event: WebSocketEventMap['open'], instance: WebSocket) => void;
|
||||||
|
onClose?: (event: WebSocketEventMap['close'], instance: WebSocket) => void;
|
||||||
|
onMessage?: (message: WebSocketEventMap['message'], instance: WebSocket) => void;
|
||||||
|
onError?: (event: WebSocketEventMap['error'], instance: WebSocket) => void;
|
||||||
|
style?:React.CSSProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
const materialName='center-link';
|
||||||
|
|
||||||
|
const CenterLink:React.FC<CenterLinkProps>=(props:CenterLinkProps)=>{
|
||||||
|
const {
|
||||||
|
websocketUrl,
|
||||||
|
ip,
|
||||||
|
token,
|
||||||
|
terminalStyle,
|
||||||
|
onConnect,
|
||||||
|
onExportLogs,
|
||||||
|
onOpen,
|
||||||
|
onClose,
|
||||||
|
onMessage,
|
||||||
|
onError,
|
||||||
|
style
|
||||||
|
}=props;
|
||||||
|
const webRef=useRef(null);
|
||||||
|
|
||||||
|
// 处理开始连接服务器ip的事件
|
||||||
|
const handleConnectClick=(values:any)=>{
|
||||||
|
const {ip}=values;
|
||||||
|
if(ip&&token&&websocketUrl&&webRef.current){
|
||||||
|
webRef.current.connect();
|
||||||
|
}
|
||||||
|
onConnect&&onConnect(values);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={materialName} style={style}>
|
||||||
|
<TerminalForm onConnect={handleConnectClick} onExportLogs={onExportLogs} />
|
||||||
|
<WebTerminal
|
||||||
|
ref={webRef}
|
||||||
|
websocketUrl={websocketUrl}
|
||||||
|
ip={ip}
|
||||||
|
token={token}
|
||||||
|
terminalStyle={terminalStyle}
|
||||||
|
onOpen={onOpen}
|
||||||
|
onClose={onClose}
|
||||||
|
onMessage={onMessage}
|
||||||
|
onError={onError}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CenterLink
|
68
packages/material/src/centerLink/components/TerminalForm.tsx
Normal file
68
packages/material/src/centerLink/components/TerminalForm.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Button, Form, Input,FormProps } from '@zhst/meta';
|
||||||
|
import '../index.less';
|
||||||
|
|
||||||
|
const materialName='terminal-form';
|
||||||
|
|
||||||
|
export interface TerminalFormProps{
|
||||||
|
onConnect:FormProps['onFinish']; // 开始连接事件
|
||||||
|
onExportLogs:()=>void; // 导出日志事件
|
||||||
|
}
|
||||||
|
|
||||||
|
const TerminalForm:React.FC<TerminalFormProps>=(props:TerminalFormProps)=> {
|
||||||
|
const {
|
||||||
|
onConnect,
|
||||||
|
onExportLogs,
|
||||||
|
}=props;
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
return (
|
||||||
|
<div className={materialName}>
|
||||||
|
<h1>连接中心服务器</h1>
|
||||||
|
<Form form={form} requiredMark={false} layout={'inline'} >
|
||||||
|
<Form.Item
|
||||||
|
label="输入中心服务器IP"
|
||||||
|
name="ip"
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: 'ip不能为空' },
|
||||||
|
{
|
||||||
|
pattern: new RegExp(
|
||||||
|
'^(?=(\\b|\\D))(((\\d{1,2})|(1\\d{1,2})|(2[0-4]\\d)|(25[0-5]))\\.){3}((\\d{1,2})|(1\\d{1,2})|(2[0-4]\\d)|(25[0-5]))(?=(\\b|\\D))$',
|
||||||
|
'g',
|
||||||
|
),
|
||||||
|
message: '输入ip格式不正确',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
whitespace: true,
|
||||||
|
message: '请输入不包含空格的ip',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input style={{ width: 320, height: 36 }} allowClear />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item >
|
||||||
|
<Button
|
||||||
|
style={{ width: 100, height: 36,background:'#23ACB2' }}
|
||||||
|
type="primary"
|
||||||
|
onClick={async()=>{
|
||||||
|
const values=await form.validateFields();
|
||||||
|
onConnect&&onConnect(values);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
开始连接
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item >
|
||||||
|
<Button
|
||||||
|
style={{ width: 100, height: 36 }}
|
||||||
|
onClick={onExportLogs}
|
||||||
|
>
|
||||||
|
导出日志
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TerminalForm;
|
133
packages/material/src/centerLink/components/WebTerminal.tsx
Normal file
133
packages/material/src/centerLink/components/WebTerminal.tsx
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
|
||||||
|
import React,{useRef,useEffect, useImperativeHandle,
|
||||||
|
forwardRef,} from 'react';
|
||||||
|
import { useWebSocket } from '@zhst/hooks';
|
||||||
|
import { Terminal } from 'xterm';
|
||||||
|
import { FitAddon } from 'xterm-addon-fit';
|
||||||
|
import 'xterm/css/xterm.css';
|
||||||
|
import '../index.less';
|
||||||
|
|
||||||
|
|
||||||
|
export interface WebsocketOptions {
|
||||||
|
reconnectLimit?: number; // 重试次数
|
||||||
|
reconnectInterval?: number; // 重试时间间隔(ms)
|
||||||
|
onOpen?: (event: WebSocketEventMap['open'], instance: WebSocket) => void; // webSocket 连接成功回调
|
||||||
|
onClose?: (event: WebSocketEventMap['close'], instance: WebSocket) => void; // webSocket 关闭回调
|
||||||
|
onMessage?: (message: WebSocketEventMap['message'], instance: WebSocket) => void; // webSocket 收到消息回调
|
||||||
|
onError?: (event: WebSocketEventMap['error'], instance: WebSocket) => void; // webSocket 错误回调
|
||||||
|
protocols?: string | string[]; // 子协议
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ReadyState {
|
||||||
|
Connecting = 0,
|
||||||
|
Open = 1,
|
||||||
|
Closing = 2,
|
||||||
|
Closed = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WebsocketResult {
|
||||||
|
latestMessage?: WebSocketEventMap['message']; // 最新消息
|
||||||
|
sendMessage: WebSocket['send']; // 发送消息函数
|
||||||
|
disconnect: () => void; // 手动断开 webSocket 连接
|
||||||
|
connect: () => void; // 手动连接 webSocket,如果当前已有连接,则关闭后重新连接
|
||||||
|
readyState: ReadyState; // 当前 webSocket 连接状态
|
||||||
|
webSocketIns?: WebSocket; // webSocket 实例
|
||||||
|
}
|
||||||
|
interface WebTerminalProps{
|
||||||
|
websocketUrl:string; // websocket服务地址
|
||||||
|
token:string; // 用户token信息
|
||||||
|
ip:string; // IP地址
|
||||||
|
terminalStyle?:React.CSSProperties;
|
||||||
|
onOpen?: (event: WebSocketEventMap['open'], instance: WebSocket) => void;
|
||||||
|
onClose?: (event: WebSocketEventMap['close'], instance: WebSocket) => void;
|
||||||
|
onMessage?: (message: WebSocketEventMap['message'], instance: WebSocket) => void;
|
||||||
|
onError?: (event: WebSocketEventMap['error'], instance: WebSocket) => void;
|
||||||
|
}
|
||||||
|
const materialName = 'web-terminal'
|
||||||
|
|
||||||
|
const WebTerminal:React.FC<WebTerminalProps&WebsocketOptions>=forwardRef((props:WebTerminalProps&WebsocketOptions,ref)=> {
|
||||||
|
const {
|
||||||
|
websocketUrl='',
|
||||||
|
token='',
|
||||||
|
ip='',
|
||||||
|
terminalStyle,
|
||||||
|
onOpen,
|
||||||
|
onClose,
|
||||||
|
onMessage,
|
||||||
|
onError,
|
||||||
|
}=props;
|
||||||
|
const { readyState, sendMessage, latestMessage, disconnect, connect }:WebsocketResult = useWebSocket(
|
||||||
|
`${websocketUrl}?ip=${ip}&Authorization=${token}`,{manual:true,reconnectLimit:0,onOpen,
|
||||||
|
onClose,
|
||||||
|
onMessage,
|
||||||
|
onError}
|
||||||
|
);
|
||||||
|
const termRef = useRef(null);
|
||||||
|
const termClassRef=useRef(null)
|
||||||
|
// const currLine=useRef(null);
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
if(termClassRef?.current){
|
||||||
|
termClassRef.current.write(latestMessage?.data+'\r\n\x1b[33m$\x1b[0m ');
|
||||||
|
}
|
||||||
|
},[latestMessage]);
|
||||||
|
|
||||||
|
// terminal初始化
|
||||||
|
useEffect(()=>{
|
||||||
|
// 初始化terminal
|
||||||
|
if(!termRef.current){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
termClassRef.current=new Terminal({
|
||||||
|
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
|
||||||
|
fontWeight: 400,
|
||||||
|
fontSize: 14,
|
||||||
|
rows: Math.ceil(
|
||||||
|
(termRef.current?.clientHeight -
|
||||||
|
150) /
|
||||||
|
14,
|
||||||
|
),
|
||||||
|
convertEol: true,//控制终端是否自动将 \n 转换为 \r\n。
|
||||||
|
cursorBlink: true,//指定光标是否闪烁
|
||||||
|
scrollback: 50, //终端中的回滚量
|
||||||
|
disableStdin: false, //是否应禁用输入。
|
||||||
|
cursorStyle: "underline", //光标样式
|
||||||
|
windowsMode: true, // 根据窗口换行
|
||||||
|
theme: {
|
||||||
|
foreground: "#ffffff", //字体
|
||||||
|
background: "#1a1a1d", //背景色
|
||||||
|
cursor: "help", //设置光标
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
let term=termClassRef.current;
|
||||||
|
term.open(termRef.current);
|
||||||
|
term.focus(); // 光标聚集
|
||||||
|
term.promp=(_)=>{
|
||||||
|
term.write('\r\n\x1b[33m$\x1b[0m ');
|
||||||
|
}
|
||||||
|
const fitAddon=new FitAddon();
|
||||||
|
term.loadAddon(fitAddon);
|
||||||
|
fitAddon.fit();
|
||||||
|
term.promp();
|
||||||
|
|
||||||
|
},[]);
|
||||||
|
|
||||||
|
// 自定义暴露给父组件的实例
|
||||||
|
useImperativeHandle(ref,()=>({
|
||||||
|
readyState,
|
||||||
|
sendMessage,
|
||||||
|
latestMessage,
|
||||||
|
disconnect,
|
||||||
|
connect
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={materialName}>
|
||||||
|
<div style={terminalStyle} ref={termRef}></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default WebTerminal;
|
32
packages/material/src/centerLink/demo/basic.tsx
Normal file
32
packages/material/src/centerLink/demo/basic.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { CenterLink } from '@zhst/material';
|
||||||
|
|
||||||
|
const demo = () => {
|
||||||
|
return (
|
||||||
|
<CenterLink
|
||||||
|
style={{width:'100%',height:'600px'}}
|
||||||
|
websocketUrl={'ws://10.0.0.7:50051/active'}
|
||||||
|
token={'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTM1OTI2NDYsImp0aSI6ImFkbWluIn0._mVU216h0q8va8bZ8PCKfGOKslYJWdRLFvLzUdvGDN4'}
|
||||||
|
ip={'127.0.0.1'}
|
||||||
|
terminalStyle={{width:'100%',height:'calc(100% - 180px)'}}
|
||||||
|
onConnect={(values)=>{
|
||||||
|
console.log(values,'====> Connecting');
|
||||||
|
}}
|
||||||
|
onExportLogs={()=>{console.log('=====> Export Log')}}
|
||||||
|
onOpen={(event: WebSocketEventMap['open'], instance: WebSocket)=>{
|
||||||
|
console.log(event,'===>open'); // webSocket 连接成功回调
|
||||||
|
}}
|
||||||
|
onClose={(event: WebSocketEventMap['close'], instance: WebSocket)=>{
|
||||||
|
console.log(event,'===>close'); // webSocket 关闭回调
|
||||||
|
}}
|
||||||
|
onMessage={(message: WebSocketEventMap['message'], instance: WebSocket)=>{
|
||||||
|
console.log(message,'===>message'); // webSocket 消息回调
|
||||||
|
}}
|
||||||
|
onError={(event: WebSocketEventMap['error'], instance: WebSocket)=>{
|
||||||
|
console.log(event,'===>error'); // webSocket 错误回调
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default demo;
|
32
packages/material/src/centerLink/index.less
Normal file
32
packages/material/src/centerLink/index.less
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
.center-link{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 30px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #E5EAEC;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.web-terminal{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
// padding: 30px;
|
||||||
|
// box-sizing: border-box;
|
||||||
|
// background-color: #E5EAEC;
|
||||||
|
// overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-form{
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
|
||||||
|
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%);
|
||||||
|
}
|
||||||
|
}
|
30
packages/material/src/centerLink/index.md
Normal file
30
packages/material/src/centerLink/index.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
category: Components
|
||||||
|
title: CenterLink 中心对接
|
||||||
|
toc: content
|
||||||
|
group:
|
||||||
|
title: 通用
|
||||||
|
order: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
中心对接
|
||||||
|
|
||||||
|
## 代码演示
|
||||||
|
|
||||||
|
<code src="./demo/basic.tsx">基本用法</code>
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| websocketUrl | websocket地址【必传】 | string | - | - |
|
||||||
|
| ip | 服务器ip地址【必传】 | string | - | - |
|
||||||
|
| token | 用户token信息【必传】 | string | - | - |
|
||||||
|
| terminalStyle | 终端黑盒子样式【可选传】 | React.CSSProperties | - | - |
|
||||||
|
| style | 整个页面的样式【可选传】 | React.CSSProperties | - | - |
|
||||||
|
| onExportLogs | 导出日志事件【可选传】 | ()=>void | - | - |
|
||||||
|
| onConnect | 连接服务器事件【可选传】 | (values:any) => void | - | - |
|
||||||
|
| onOpen | webSocket 连接成功回调【可选传】 | (event: WebSocketEventMap['open'], instance: WebSocket) => void | - | - |
|
||||||
|
| onClose | webSocket 关闭回调【可选传】 | (event: WebSocketEventMap['close'], instance: WebSocket) => void | - | - |
|
||||||
|
| onMessage | webSocket 收到消息回调【可选传】 | (event:WebSocketEventMap['message'], instance: WebSocket) => void | - | - |
|
||||||
|
| onError | webSocket 错误回调【可选传】 | (event: WebSocketEventMap['error'], instance: WebSocket) => void | - | - |
|
3
packages/material/src/centerLink/index.tsx
Normal file
3
packages/material/src/centerLink/index.tsx
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import CenterLink from "./CenterLink";
|
||||||
|
|
||||||
|
export default CenterLink;
|
@ -3,6 +3,6 @@ export { default as AlgorithmConfig } from './algorithmConfig';
|
|||||||
export type { AlgorithmConfigRef, AlgorithmConfigProps } from './algorithmConfig';
|
export type { AlgorithmConfigRef, AlgorithmConfigProps } from './algorithmConfig';
|
||||||
export { default as Login } from './login';
|
export { default as Login } from './login';
|
||||||
export { default as Password } from './password';
|
export { default as Password } from './password';
|
||||||
export { default as Terminal } from './terminal';
|
export { default as CenterLink } from './centerLink';
|
||||||
export { default as SchemaFormModal } from './algorithmConfig/components/schemaFormModal';
|
export { default as SchemaFormModal } from './algorithmConfig/components/schemaFormModal';
|
||||||
export * from 'rc-util'
|
export * from 'rc-util'
|
||||||
|
@ -1,65 +0,0 @@
|
|||||||
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'];
|
|
||||||
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 {onFinish,websocketUrl='',token='',ip='',onExportLog}=props;
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
|
|
||||||
const handleCenterConnect = async () => {
|
|
||||||
const values = await form.validateFields();
|
|
||||||
onFinish&&onFinish(values);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={materialName} style={{}}>
|
|
||||||
<h1 >连接中心服务器</h1>
|
|
||||||
<Form form={form} requiredMark={false} layout={'inline'} >
|
|
||||||
<Form.Item
|
|
||||||
label="输入中心服务器IP"
|
|
||||||
name="ip"
|
|
||||||
rules={[
|
|
||||||
{ required: true, message: 'ip不能为空' },
|
|
||||||
{
|
|
||||||
pattern: new RegExp(
|
|
||||||
'^(?=(\\b|\\D))(((\\d{1,2})|(1\\d{1,2})|(2[0-4]\\d)|(25[0-5]))\\.){3}((\\d{1,2})|(1\\d{1,2})|(2[0-4]\\d)|(25[0-5]))(?=(\\b|\\D))$',
|
|
||||||
'g',
|
|
||||||
),
|
|
||||||
message: '输入ip格式不正确',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'string',
|
|
||||||
whitespace: true,
|
|
||||||
message: '请输入不包含空格的ip',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Input style={{ width: 320, height: 36 }} allowClear />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item >
|
|
||||||
<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>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
<WebTerminal websocketUrl={websocketUrl} token={token} ip={ip} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Terminal;
|
|
@ -1,164 +0,0 @@
|
|||||||
// @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连接再次调试
|
|
||||||
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,
|
|
||||||
fontSize: 14,
|
|
||||||
rows: Math.ceil(
|
|
||||||
(document.getElementsByClassName('container-children')[0].clientHeight -
|
|
||||||
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.prompt = (_) => {
|
|
||||||
this.term.write('\r\n\x1b[33m$\x1b[0m ');
|
|
||||||
};
|
|
||||||
// // 换行并输入起始符
|
|
||||||
// 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.onKey((e) => {
|
|
||||||
const printable =
|
|
||||||
!e.domEvent.altKey &&
|
|
||||||
!e.domEvent.altGraphKey &&
|
|
||||||
!e.domEvent.ctrlKey &&
|
|
||||||
!e.domEvent.metaKey;
|
|
||||||
if (e.domEvent.keyCode === 13) {
|
|
||||||
this.Send(term, this.curr_line);
|
|
||||||
this.term.prompt();
|
|
||||||
this.curr_line = '';
|
|
||||||
} else if (e.domEvent.keyCode === 8) {
|
|
||||||
// back 删除的情况
|
|
||||||
if (this.term._core.buffer.x > 2) {
|
|
||||||
if (this.curr_line.length) {
|
|
||||||
this.curr_line = this.curr_line.slice(0, this.curr_line.length - 1);
|
|
||||||
this.term.write('\b \b');
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (printable) {
|
|
||||||
this.curr_line += e.key;
|
|
||||||
this.term.write(e.key);
|
|
||||||
}
|
|
||||||
this.term.focus();
|
|
||||||
});
|
|
||||||
this.term.onData((key) => {
|
|
||||||
// 粘贴的情况
|
|
||||||
if (key.length > 1) {
|
|
||||||
this.term.write(key);
|
|
||||||
this.curr_line += key;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
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 term = this.term;
|
|
||||||
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~$ ');
|
|
||||||
};
|
|
||||||
|
|
||||||
// WebSocket 方法 使用连接发送数据
|
|
||||||
Send = (term, message) => {
|
|
||||||
this.websocket.send(message);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div className="container-children">
|
|
||||||
<div id="terminal"></div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Terminal } from '@zhst/material';
|
|
||||||
|
|
||||||
const demo = () => {
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Terminal
|
|
||||||
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');}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default demo;
|
|
@ -1,64 +0,0 @@
|
|||||||
.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;
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
category: Components
|
|
||||||
title: Terminal 中心对接
|
|
||||||
toc: content
|
|
||||||
group:
|
|
||||||
title: 通用
|
|
||||||
order: 2
|
|
||||||
---
|
|
||||||
|
|
||||||
中心对接
|
|
||||||
|
|
||||||
## 代码演示
|
|
||||||
|
|
||||||
<code src="./demo/basic.tsx">基本用法</code>
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
|
||||||
| --- | --- | --- | --- | --- |
|
|
||||||
| onFinish | 提交事件 | FormProps['onFinish'] | - | - |
|
|
@ -1,3 +0,0 @@
|
|||||||
import Terminal from './Terminal'
|
|
||||||
|
|
||||||
export default Terminal;
|
|
@ -0,0 +1,15 @@
|
|||||||
|
// 可应用于页面跳转以及文件下载
|
||||||
|
// 第一个参数:文件的下载路径/要跳转页面的路径(可携带参数)
|
||||||
|
// 第二个参数:是否新打开一个页面,true为新开一个页面,false是在当前页面进行操作;
|
||||||
|
export const createAElement = (url: string, isBlank: boolean) => {
|
||||||
|
var newLink = document.createElement('a');
|
||||||
|
newLink.className = 'create-link';
|
||||||
|
newLink.href = url;
|
||||||
|
if (isBlank) {
|
||||||
|
newLink.target = '_blank';
|
||||||
|
}
|
||||||
|
document.body.appendChild(newLink);
|
||||||
|
newLink.click();
|
||||||
|
document.body.removeChild(newLink);
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user