Vue xtermjs 终端
安装步骤如下
安装xtermjs所需要的插件
npm install --save xterm
npm install --save xterm-addon-fit
npm install --save xterm-addon-attach
HTML代码片段
<template><div><a-drawertitle="终端"placement="right":closable="false":visible="visible":width="1000"@close="onClose"><div id="xterm" class="xterm" style="height:800px" /></a-drawer></div></template><script>
import "xterm/css/xterm.css";
import { Terminal } from "xterm";
import { FitAddon } from "xterm-addon-fit";export default {data() {return {visible: false,term: "",//terminal 黑窗口容器prefix: "[root@serverip ~]# ",//前缀inputText: "",//输入内容,每次回车后进行ws通信然后清空此数据};},methods: {//初始化黑窗口async initTerm() {const fitAddon = new FitAddon();this.term = new Terminal({fontSize: 14,cursorBlink: true,allowProposedApi: true,disableStdin: false,LogLevel: "debug",});this.term.loadAddon(fitAddon);//开启Xterm终端this.term.open(document.getElementById("xterm"));this.term.writeln("\x1b[1;1;32mwellcom to web terminal!\x1b[0m");this.term.write(this.prefix); //黑窗口 前缀await this.termPromt(); //term.promtawait this.termKeyCode(); //事件fitAddon.fit(); //黑窗口适应实际div宽高this.term.focus(); //自动聚焦},//事件termKeyCode() {const TERMINAL_INPUT_KEY = {BACK: 8, // 退格删除键ENTER: 13, // 回车键UP: 38, // 方向盘上键DOWN: 40, // 方向盘键LEFT: 37, // 方向盘左键RIGHT: 39, // 方向盘右键};// const { eqpCode, server } = this.selectObj;let inputText = "";let currentIndex = 0;let inputTextList = [];this.term.onKey((e) => {const { key, domEvent } = e;const { keyCode, altKey, altGraphKey, ctrlKey, metaKey } = domEvent;const printAble = !(altKey || altGraphKey || ctrlKey || metaKey); // 禁止相关按键const totalOffsetLength = inputText.length + this.prefix.length; // 总偏移量const currentOffsetLength = this.term._core.buffer.x; // 当前x偏移量switch (keyCode) {//删除case TERMINAL_INPUT_KEY.BACK:if (currentOffsetLength > this.prefix.length) {const cursorOffSetLength = this.getCursorOffsetLength(totalOffsetLength - currentOffsetLength, "\x1b[D"); // 保留原来光标位置this.term._core.buffer.x = currentOffsetLength - 1;this.term.write("\x1b[?K" + inputText.slice(currentOffsetLength - this.prefix.length));this.term.write(cursorOffSetLength);inputText = `${inputText.slice(0, currentOffsetLength - this.prefix.length - 1)}${inputText.slice(currentOffsetLength - this.prefix.length)}`;}break;//回车case TERMINAL_INPUT_KEY.ENTER: {this.term.write("\r\n");console.log("inputText", inputText);//ws 通信参数// let wsParams = { EqpCode: eqpCode, Action: "terminal", Data: inputText };// this.$emit("websocketSend", wsParams, server);if (!inputText.trim()) {this.term.prompt();return;}if (inputTextList.indexOf(inputText) === -1) {inputTextList.push(inputText);currentIndex = inputTextList.length;}this.term.prompt();inputText = "";break;}case TERMINAL_INPUT_KEY.UP: {if (!inputTextList[currentIndex - 1]) break;const offsetLength = this.getCursorOffsetLength(inputText.length, "\x1b[D");inputText = inputTextList[currentIndex - 1];this.term.write(offsetLength + "\x1b[?K");this.term.write(inputTextList[currentIndex - 1]);this.term._core.buffer.x = totalOffsetLength;currentIndex--;break;}case TERMINAL_INPUT_KEY.LEFT:if (currentOffsetLength > this.prefix.length) {this.term.write(key); // '\x1b[D'}break;case TERMINAL_INPUT_KEY.RIGHT:if (currentOffsetLength < totalOffsetLength) {this.term.write(key); // '\x1b[C'}break;default: {// 在当前的坐标写上 key 和坐标后面的字符// 移动停留在当前位置的光标if (!printAble) break;if (totalOffsetLength >= this.term.cols) break;if (currentOffsetLength >= totalOffsetLength) {this.term.write(key);inputText += key;break;}let cursorOffSetLength = this.getCursorOffsetLength(totalOffsetLength - currentOffsetLength, "\x1b[D");this.term.write("\x1b[?K" + `${key}${inputText.slice(currentOffsetLength - this.prefix.length)}`);this.term.write(cursorOffSetLength);inputText = inputText.slice(0, currentOffsetLength) + key + inputText.slice(totalOffsetLength - currentOffsetLength);break;}}});},//限制和后端交互,只有输入回车键才显示结果termPromt() {this.term.prompt = () => {this.term.write(this.prefix);};},//获取光标当前位置getCursorOffsetLength(offsetLength, subString) {let cursorOffsetLength = "";for (let offset = 0; offset < offsetLength; offset++) {cursorOffsetLength += subString;}return cursorOffsetLength;},//写入黑窗口wirteTerm(data) {console.log("写入黑窗口", data);this.term.writeln(data);this.term.prompt();},//加载基础数据pageLoad(data) {this.selectObj = data;this.drawerFlag = true;this.$nextTick(() => {this.initTerm();});},cancelClick() {this.drawerFlag = false;//关闭弹框this.term.dispose(document.getElementById("xterm"));},showDrawer() {this.visible = true;this.$nextTick(()=>{this.initTerm()})},onClose() {this.visible = false;},},};</script><style>#terminal{width:100%;height:800px;}
</style>