Components

Terminal

<template><div ref="xterm" class="w-full h-full" /></template>

<script lang="ts" setup>
import { Terminal } from "xterm";
import { FitAddon } from "xterm-addon-fit";
import "xterm/css/xterm.css";
import { debounce } from "lodash-es";
// websocket发送消息和接收消息的方法
import { send, emitter } from "@/service/websocket";
import SOCKET_EVENT from "@/enum/socket-event.enum";

const xterm = ref<HTMLDivElement>();
const route = useRoute();

const term = new Terminal({
  allowProposedApi: true,
  fontSize: 18,
  cursorBlink: true,
  disableStdin: false
});
const fitAddon = new FitAddon();

const fitScreen = () => {
  fitAddon.fit();
  // websocket 发送消息,告知xterm的尺寸
  send({ event_type: "resize", input: { cols: term.cols, rows: term.rows } });
};

const init = () => {
  term.open(xterm.value as HTMLDivElement);
  term.loadAddon(fitAddon);
  term.focus();
  term.resize(50, 50);
  fitScreen();
  window.addEventListener("resize", debounce(fitScreen, 50));
  // 每按一个字符触发一次
  term.onData((data: string) => {
    sendCMD(data);
  });
};

// websocket 发送在xterm中输入的命令
const sendCMD = (cmd?: string) => {
  send({ event_type: "cmd", input: cmd || "\r" });
};

emitter.on("msg", (res: any) => {
  const { event_type, out } = res;

  if (event_type === "cmd_res" && res.pty_id === route.query.pty_id) {
    term.write(out);
  }
});

onMounted(() => {
  init();
});

onBeforeUnmount(() => {
  window.removeEventListener("resize", debounce(fitScreen, 50));
  fitAddon.dispose();
  term.dispose();
});
</script>