vue xterm封裝

安裝
npm install xterm xterm-addon-fit -D
兩種模式
  • log:日志輸出
  • shell:終端命令
<template>
  <div :id="id" class="xterm"></div>
</template>
<script>
import { defineComponent, onMounted, onBeforeUnmount, watch, nextTick, ref } from "vue";
import { initWebSocket, closeWebsocket, sendWebsocket } from "../utils/websocket";
import { startWith } from "../utils/string";
//xterm
import { Terminal } from "xterm";
import { FitAddon } from "xterm-addon-fit";
import "xterm/css/xterm.css";
import "xterm/lib/xterm.js";
export default defineComponent({
  name: "XTerm",
  props: {
    type: String,
    width: Number,
    data: String,
    url: String,
  },
  setup(props) {
    let xterm = null;
    let type = "";
    let width = ref(0);
    let id = ref("logxterm");
    const fitAddon = new FitAddon();
    type = type ? type : props.type;
    if (type == "log") {
      id.value = "logxterm";
    } else {
      id.value = "shellterm";
    }
    function initTerm() {
      width.value = width.value ? width.value : props.width;
      let rows = width.value / 12;
      xterm = new Terminal({
        rows: parseInt(rows),
        cols: 40,
        cursorStyle: "underline", //光標(biāo)樣式
        cursorBlink: true, // 光標(biāo)閃爍
        convertEol: true, //啟用時,光標(biāo)將設(shè)置為下一行的開頭
        disableStdin: true, //是否應(yīng)禁用輸入。
        theme: {
          foreground: "white", //字體
          background: "#334963", //背景色
          cursor: "help", //設(shè)置光標(biāo)
        },
      });

      xterm.loadAddon(fitAddon);
      xterm.open(document.getElementById(id.value));
      fitAddon.fit();
      if (!xterm._initialized) {
        xterm._initialized = true;
      }
      if (type == "log") {
        xterm.write("logging--------------------logging\n");
        if (props.data) xterm.write(props.data);
      } else if (type == "shell") {
        xterm.focus();
        //限制和后端交互,只有輸入回車鍵才顯示結(jié)果
        xterm.prompt = function () {
          xterm.write("\r\n~$ ");
        };
        xterm.prompt();

        xterm.writeln("~ Welcome to the command execution window");
        let url = props.url.replace("http", "ws");
        initWebSocket(url);
        let termdata = "";
        let oldtermdata = "";
        let invalidkey = [
          27,
          33,
          34,
          35,
          36,
          37,
          39,
          38,
          40,
          45,
          144,
          9,
          12,
          16,
          17,
          18,
          20,
          112,
          113,
          114,
          115,
          116,
          117,
          118,
          119,
          120,
          121,
          122,
          123,
          175,
          174,
          179,
          173,
          172,
          180,
          170,
        ];
        xterm.onKey(function (e) {
          const printable =
            !e.domEvent.altKey &&
            !e.domEvent.altGraphKey &&
            !e.domEvent.ctrlKey &&
            !e.domEvent.metaKey;

          if (e.domEvent.keyCode === 13) {
            //回車
            let data = 0 + termdata + "\n";
            sendWebsocket(data, function () {
              let data = e.data;
              data = data.substr(1); //去掉第一位,值為1
              if (startWith(data, oldtermdata + "\r\n")) {
                xterm.write(data.replace(oldtermdata, ""));
              } else {
                xterm.write(data);
              }
            });
            oldtermdata = termdata;
            termdata = "";
          } else if (e.domEvent.keyCode === 8) {
            //刪除
            if (xterm._core.buffer.x > 2) {
              if (termdata.length > 0) {
                xterm.write("\b \b");
                termdata = termdata.substring(0, termdata.length - 1);
              }
            }
          } else if (printable) {
            if (invalidkey.indexOf(e.domEvent.keyCode) < 0) {
              //不是特殊字符的時候可以執(zhí)行
              oldtermdata = "";
              termdata = termdata + e.key;
              xterm.write(e.key);
            }
          }
        });
      }

      window.addEventListener("resize", function () {
        fitAddon.fit();
      });
    }
    function fit() {
      fitAddon.fit();
    }
    function dispose() {
      xterm.dispose();
    }
    watch(props, (newProps) => {
      type = newProps.type;
      width.value = newProps.type.width;
      if (newProps.type == "log") {
        xterm.write(newProps.data);
        fit();
      } else if (newProps.type == "shell") {
        if (newProps.url) {
          xterm.dispose();
          initTerm();
        }
      }
    });
    onMounted(() => {
      nextTick(() => {
        if (!xterm) initTerm();
      });
    });
    onBeforeUnmount(() => {
      dispose();
      if (type == "shell") closeWebsocket();
    });
    return {
      initTerm,
      fit,
      dispose,
      id,
    };
  },
});
</script>

頁面使用:dialog里用加v-if
<!--日志-->
<template>
  <div class="xterm-container">
    <XTerm
          :data="logtermdata"
          type="log"
          ref="logxterm"
          class="term"
          :width="width"
        ></XTerm>
  </div>
  <el-dialog v-model="visible" title="終端" width="800px">
    <div class="xterm-container">
      <XTerm
        v-if="termVisible"
        type="shell"
        ref="shellterm"
        class="term"
        :width="width"
        :url="websocketUrl"
      ></XTerm>
    </div>
  </el-dialog>
</template>
<script>
import {vue,nexTick,ref} from "vue"
import XTerm from "../components/xterm";
export default {
  setup() {
    const width = ref(0)
    const visible = ref(false)
    const logxterm = ref(null)
    function open(){
       visible.value = true;
       nextTick(() => {
          //計算寬度
          width.value = document.getElementsByClassName("term")[0].offsetWidth;
          //初始化
          logxterm.value.initTerm();
       });
     }
    return{
      width,
      visible,
      logxterm,
      open
    }
  },
  components: {
    XTerm,
  }
}
</script>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 用兩張圖告訴你,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 14,014評論 2 59
  • # Vue.js - day01 ## 插件安裝推薦 vscode有二個常用插件:vetur,Vue 2 Snip...
    wuyuan0127閱讀 766評論 0 0
  • 在線訪問手冊:https://hanxueqing.github.io/Web-Front-end-Intervi...
    視覺派Pie閱讀 2,493評論 0 5
  • 1、談?wù)剬ttp協(xié)議的認識流程:1.域名解析域名解析檢查順序為:瀏覽器自身DNS緩存---》OS自身的DNS緩存...
    Zzmi閱讀 872評論 0 0
  • 1. 組件的data為什么必須是函數(shù)? 組件中的 data 寫成一個函數(shù),數(shù)據(jù)以函數(shù)返回值形式定義,這樣每復(fù)用一次...
    郭先生_515閱讀 1,047評論 0 12

友情鏈接更多精彩內(nèi)容