使用React模拟一个终端(XShell)

import React from 'react';
import { Button } from 'antd';
import { AuthDiv } from 'components';
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
import FileManager from './FileManager';
import { http, X_TOKEN } from 'libs';
import 'xterm/css/xterm.css';
import styles from './index.module.css';


class WebSSH extends React.Component {
  constructor(props) {
    super(props);
    this.id = props.match.params.id;
    this.socket = null;
    this.term = new Terminal();
    this.container = null;
    this.input = null;
    this.state = {
      visible: false,
      uploading: false,
      managerDisabled: true,
      host: {},
      percent: 0
    }
  }

  componentDidMount() {
    this._fetch();
    const fitPlugin = new FitAddon();
    this.term.loadAddon(fitPlugin);
    const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
    this.socket = new WebSocket(`${protocol}//${window.location.host}/api/ws/ssh/${this.id}/?x-token=${X_TOKEN}`);
    this.socket.onmessage = e => this._read_as_text(e.data);
    this.socket.onopen = () => {
      this.term.open(this.container);
      this.term.focus();
      fitPlugin.fit();
    };
    this.socket.onclose = e => {
      if (e.code === 3333) {
        window.location.href = "about:blank";
        window.close()
      } else {
        setTimeout(() => this.term.write('\r\nConnection is closed.\r\n'), 200)
      }
    };
    this.term.onData(data => this.socket.send(JSON.stringify({data})));
    this.term.onResize(({cols, rows}) => {
      this.socket.send(JSON.stringify({resize: [cols, rows]}))
    });
    window.onresize = () => fitPlugin.fit()
  }

  _read_as_text = (data) => {
    const reader = new window.FileReader();
    reader.onload = () => this.term.write(reader.result);
    reader.readAsText(data, 'utf-8')
  };

  handleShow = () => {
    this.setState({visible: !this.state.visible})
  };

  _fetch = () => {
    http.get(`/api/host/?id=${this.id}`)
      .then(res => {
        document.title = res.name;
        this.setState({host: res, managerDisabled: false})
      })
  };

  render() {
    const {host, visible, managerDisabled} = this.state;
    return (
      <div className={styles.container}>
        <div className={styles.header}>
          <div>{host.name} | {host.username}@{host.hostname}:{host.port}</div>
          <AuthDiv auth="host.console.manager">
            <Button disabled={managerDisabled} type="primary" icon="folder-open"
                    onClick={this.handleShow}>文件管理器</Button>
          </AuthDiv>
        </div>
        <div className={styles.terminal}>
          <div ref={ref => this.container = ref}/>
        </div>
        <FileManager id={this.id} visible={visible} onClose={this.handleShow}/>
      </div>
    )
  }
}

export default WebSSH

package.json:

{
  "name": "terminal",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@antv/data-set": "^0.10.2",
    "ace-builds": "^1.4.7",
    "antd": "^3.25.0",
    "axios": "^0.21.1",
    "bizcharts": "^3.5.6",
    "history": "^4.10.1",
    "http-proxy-middleware": "^0.20.0",
    "lodash": "^4.17.19",
    "mobx": "^5.15.0",
    "mobx-react": "^6.1.4",
    "moment": "^2.24.0",
    "react": "^16.11.0",
    "react-ace": "^8.0.0",
    "react-dom": "^16.11.0",
    "react-router-dom": "^5.1.2",
    "react-scripts": "3.2.0",
    "xterm": "^4.6.0",
    "xterm-addon-fit": "^0.4.0"
  },
  "scripts": {
    "start": "react-app-rewired start",
    "build": "GENERATE_SOURCEMAP=false react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "@babel/plugin-proposal-decorators": "^7.7.0",
    "babel-plugin-import": "^1.12.2",
    "customize-cra": "^0.8.0",
    "react-app-rewired": "^2.1.5"
  }
}

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: 页面 嗯,想写一个漂亮的登录注册页面?那么React一个很好的选择! 首先,你可以使用React的组件系统来构建出一个简单的登录注册页面。比如说,你可以创建一个登录组件和一个注册组件,然后使用React Router来切换这两个组件。 其次,你可以使用一些CSS框架,比如说Bootstrap或者Material UI,来帮助你美化你的页面。这些框架都有很多预定义的样式,可以让你快速构建出一个漂亮的页面。 最后,你可以使用一些图标库,比如说Font Awesome,来增加页面的交互性。 总的来说,使用React来构建一个漂亮的登录注册页面是很容易的。如果你需要帮助,可以参考一些在线教程或者询问一些更有经验的开发者。 ### 回答2: 使用React编写一个漂亮的登录注册页面可以通过以下步骤实现。 首先,我们需要安装React并创建一个新的React项目。可以使用create-react-app来快速创建一个基本的React项目。在项目目录中打开终端,运行以下命令来创建项目: ``` npx create-react-app login-register ``` 接下来,我们需要创建一个登录注册组件。在src文件夹下创建一个新的文件夹,命名为components,并在其中创建两个组件Login.js和Register.js。 在Login.js组件中,我们可以使用React的useState hook来处理用户输入的表单数据。然后通过调用API来验证用户信息并完成登录过程。可以使用React Router来处理路由导航。 在Register.js组件中,我们可以通过类似的方式处理用户输入的表单数据,并通过调用API来注册新用户。 在这两个组件中,我们可以使用CSS样式来美化登录和注册表单。可以使用CSS框架如Bootstrap或Material-UI来快速添加样式。 在App.js中,我们可以使用React Router来设置路由,将登录和注册组件分别映射到不同的路由路径。这样用户可以通过在浏览器地址栏中输入不同的URL来访问登录和注册页面。 最后,我们可以在App.js中创建一个顶层的容器,并在其中引入React的路由组件。这样就可以将登录和注册组件放置在整个应用的页面中。 总结起来,使用React编写漂亮的登录注册页面可以通过安装React和相关依赖、创建登录和注册组件、处理用户输入和调用API、添加CSS样式以及设置路由等步骤来实现。这样就可以实现一个美观且功能完善的登录注册页面。 ### 回答3: 使用React可以方便地编写一个漂亮的登录注册界面。首先,我们可以使用React的组件化特性,将登录和注册分别封装成两个独立的组件。 在登录组件中,我们可以使用React的表单组件来实现输入框和按钮的渲染。可以使用styled-components库来定义漂亮的样式,例如设置输入框的边框颜色、字体样式和按钮的背景色等。 登录组件可以包括两个输入框,分别用于输入用户名和密码。当用户输入完成后,我们可以通过事件监听器获取到用户输入的内容,并将其保存在state中。在处理点击登录按钮的事件中,我们可以通过发送异步请求来验证用户的登录信息,并根据返回结果来进行相应的处理,例如登录成功后跳转到首页,登录失败则弹出错误提示。 注册组件和登录组件类似,也可以包括输入框和按钮,并设置相应的样式。当用户输入完成后,我们可以将用户输入的内容保存在state中。在处理点击注册按钮的事件中,我们可以发送异步请求来验证用户的注册信息并进行相应的处理,例如注册成功后跳转到登录界面,注册失败则弹出错误提示。 在整个登录注册过程中,我们可以使用React Router库来实现页面之间的跳转和路由管理,使用户可以方便地跳转到其他页面。 总之,通过使用React的组件化特性和其他相关库,我们可以轻松地编写一个漂亮的登录注册界面。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值