xterm.js+react实战

3,123 阅读1分钟

要说我做的和其他人不同的事情是,我把光标的移动给禁用了。

文档们

image-20220401164341758.png

通讯方式

都是websocket这是肯定,只是有两种形式

一种是不管输入啥都传给后端

一种是按了回车后再把输入的内容传给后端,这边是第二种

构建变量

term = null; //终端
websocket = null;
curr_line = "";//要发送给后端的字符串

占满元素

import { FitAddon } from "xterm-addon-fit";
this.term = new Terminal({
    //占满高
      rows: Math.ceil(
        (document.getElementsByClassName("container-children")[0].clientHeight -
          150) /
          14
      ),
    });
const fitAddon = new FitAddon();
this.term.loadAddon(fitAddon);
fitAddon.fit(); //占满宽

光标不准

左移通常会移两格,还可以上下移动,瞎跑
所以我干脆把上下左右的功能去掉
我试着用document.onkeydown去阻止,没用,心态有点崩
我在文档里发现attachCustomKeyEventHandler
于是就实现了禁用上下左右按键移动光标的方法了
  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 =
        !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;
      }
    });
Send = (term, message) => {
    this.websocket.send(message);
  };

websocket

  initWebsock = () => {
    // let websocket = this.websocket;
    let term = this.term;
    let token = getToken();
    this.websocket = new WebSocket("ws://" + window._CONFIG.WsSsh, token);
    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);
    };
  };

全代码

import React, { Component } from "react";
import { Terminal } from "xterm";
import "xterm/css/xterm.css";
import { getToken } from "@libs/auth";
import { FitAddon } from "xterm-addon-fit";

export default class WebTerminal extends Component {
  term = null;
  websocket = null;
  curr_line = "";
  componentDidMount() {
    let term = this.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
      ),
    });
    this.term.open(document.getElementById("terminal"));
    this.term.focus();
    this.term.prompt = (_) => {
      this.term.write("\r\n\x1b[33m$\x1b[0m ");
    };
    this.term.prompt();
    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 =
        !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();
      console.log(1, "print", e.key);
    });
    this.term.onData((key) => {
      // 粘贴的情况
      if (key.length > 1) {
        this.term.write(key);
        this.curr_line += key;
      }
    });
    this.initWebsock();
  }
  componentWillUnmount() {
    this.term.dispose();
    this.websocket.close()
  }
  initWebsock = () => {
    // let websocket = this.websocket;
    let term = this.term;
    let token = getToken();
    this.websocket = new WebSocket("ws://" + window._CONFIG.WsSsh, token);
    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~$ ");
  //   };

  Send = (term, message) => {
    this.websocket.send(message);
  };
  render() {
    return (
      <div className="container-children" style={{ height: "100%" }}>
        <div id="terminal" style={{ width: "100%" }}></div>
      </div>
    );
  }
}