xterm.js + vue + websocket实现终端功能(xterm 3.x+xterm 4.x)
值得注意的是,在我们使用xterm实现仿终端功能时,不需要对输入字符进行判断,也不需要在输入事件中把输入的字符打出来。因为在输入事件中执行的web socket连接中,每输入一个字符都会自动传到后端,而后端会根据你输入的回车符来判断是否要为你换行及返回何种数据。所以我们只需要向后台传递参数就好。
之前使用的xterm 3.x的粘贴功能不能用了,于是又改用了4.x的,我把所有的代码都放在这里吧,大家有问题也可以积极来探讨哦~
xterm github官网
一、使用流程
1.引入xterm.js
2.创建xterm实例并挂载到dom上
3.xterm全屏调整
4.xterm与websocket结合发送数据并显示在屏幕上
二、需要注意
1.关于输入与粘贴
xterm4.x应该是在2019年3月份就开始更新了,但现在网上用的大多数的版本还都是3.x,我之前用的是3.12,输入方法和粘贴方法分别用的是
term.on(‘key’,function()}
term.on(‘paste’, fuction()}
而xterm4.x版本后API取消了on事件,而直接使用on+事件名,格式也有所调整
term.onData(function(key) {
let order = {
Data: key,
Op: “stdin”
};
_this.onSend(order);
});
这里坑也挺大的,一开始我没注意看文档,只看了是onData方法,认为只要使用term.onData = function(){}赋值就可以,但是会报错
xterm.js :Cannot set property onData of #
<e>
which has only a getter”
然后后来查官网和资料才发现(这里要吐槽资料真是少的可怜啊),是要给onData方法传一个方法做为参数去执行,而不是给它赋值。
2.关于全屏
然后就是关于全屏,因为之前受资料与知识范围限制发现4.x没有fullscreen方法所以才使用的3.x(在这里我要吐槽一下这个xterm.js的官网,真真是辣鸡的很,什么都不写清楚,就实例一下就完事了,好多参数和方法api里也不给个例子,还得自己到处去找,真的很辣鸡!!!),一开始以为4.0中不需要设置fit及fullscreen,引入css后直接设置行数和列数就能实现背景铺满全屏。
但遇到一个问题是,xterm默认代码不会占满一整行,而是会直接换行,所以这里我们需要使用到官网首页给到的插件。
import { FitAddon } from "xterm-addon-fit";
// canvas背景全屏-默认
var fitAddon = new FitAddon();
term.loadAddon(fitAddon);
fitAddon.fit();
// 内容全屏显示-窗口大小发生改变时
function resizeScreen(size) {
console.log("size", size);
try {
fitAddon.fit();
// 窗口大小改变时触发xterm的resize方法,向后端发送行列数,格式由后端决定
term.onResize(size => {
_this.onSend({ Op: "resize", Cols: size.cols, Rows: size.rows });
});
} catch (e) {
console.log("e", e.message);
}
}
window.addEventListener("resize", resizeScreen);
3.关于字符删除与上下键切换命令等
值得注意的是,在我们使用xterm实现仿终端功能时,不需要对输入字符进行判断,也不需要在输入事件中把输入的字符打出来。因为在输入事件中执行的web socket连接中,每输入一个字符都会自动传到后端,而后端会根据你输入的回车符来判断是否要为你换行及返回何种数据。(来,重要的话跟我念三遍~)
我们不必关心用户输入与想做的操作,只需要向后台传递参数就好。
我们不必关心用户输入与想做的操作,只需要向后台传递参数就好。
我们不必关心用户输入与想做的操作,只需要向后台传递参数就好。
4.实现web terminal代码
(1).xterm 4.4.0(最新)
(下面就是整个文件的方法啦,此实例不能直接复制使用哦,因为websocket是封装好的,大家看一下使用方法就好~)
<template>
<div
style="height: 100%;
background: #002833;"
>
<div id="terminal" ref="terminal"></div> //terminal容器
</div>
</template>
<script>
// 引入xterm,请注意这里和3.x版本的引入路径不一样
import { Terminal } from "xterm";
import "xterm/css/xterm.css";
import "xterm/lib/xterm.js";
export default {
name: "Shell",
data() {
return {
shellWs: "",
term: "", // 保存terminal实例
rows: 40,
cols: 100
};
},
created() {
this.wsShell();
},
mounted() {
let _this = this;
// 获取容器宽高/字号大小,定义行数和列数
this.rows = document.querySelector(".indexContainer").offsetHeight / 16 - 6;
this.cols = document.querySelector(".indexContainer").offsetWidth / 14;
let term = new Terminal({
rendererType: "canvas", //渲染类型
rows: parseInt(_this.rows), //行数
cols: parseInt(_this.cols), // 不指定行数,自动回车后光标从下一行开始
convertEol: true, //启用时,光标将设置为下一行的开头
// scrollback: 50, //终端中的回滚量
disableStdin: false, //是否应禁用输入。
cursorStyle: "underline", //光标样式
cursorBlink: true, //光标闪烁
theme: {
foreground: "#7e9192", //字体
background: "#002833", //背景色
cursor: "help", //设置光标
lineHeight: 16
}
});
// 创建terminal实例
term.open(this.$refs["terminal"]);
// 换行并输入起始符“$”
term.prompt = () => {
term.write("\r\n$ ");
};
term.prompt();
// // canvas背景全屏
var fitAddon = new FitAddon();
term.loadAddon(fitAddon);
fitAddon.fit();
window.addEventListener("resize", resizeScreen);
// 内容全屏显示
function resizeScreen() {
// 不传size
try {
fitAddon.fit();
// 窗口大小改变时触发xterm的resize方法,向后端发送行列数,格式由后端决定
// 这里不使用size默认参数,因为改变窗口大小只会改变size中的列数而不能改变行数,所以这里不使用size.clos,而直接使用获取我们根据窗口大小计算出来的行列数
term.onResize(() => {
_this.onSend({ Op: "resize", Cols: term.cols, Rows: term.rows });
});
} catch (e) {
console.log("e", e.message);
}
}
function runFakeTerminal(_this) {
if (term._initialized) {
return;
}
// 初始化
term._initialized = true;
term.writeln("Welcome to use Superman. ");
term.writeln(
`This is Web Terminal of pod\x1B[1;3;31m ${
_this.urlParam.podName
}\x1B[0m in namespace\x1B[1;3;31m ${_this.urlParam.namespace}\x1B[0m`
);
term.prompt();
// / **
// *添加事件监听器,用于按下键时的事件。事件值包含
// *将在data事件以及DOM事件中发送的字符串
// *触发了它。
// * @返回一个IDisposable停止监听。
// * /
// / ** 更新:xterm 4.x(新增)
// *为数据事件触发时添加事件侦听器。发生这种情况
// *用户输入或粘贴到终端时的示例。事件值
// *是`string`结果的结果,在典型的设置中,应该通过
// *到支持pty。
// * @返回一个IDisposable停止监听。
// * /
// 支持输入与粘贴方法
term.onData(function(key) {
let order = {
Data: key,
Op: "stdin"
};
_this.onSend(order);
// 为解决窗体resize方法才会向后端发送列数和行数,所以页面加载时也要触发此方法
_this.onSend({
Op: "resize",
Cols: parseInt(term.cols),
Rows: parseInt(term.rows)
});
});
_this.term = term;
}
runFakeTerminal(_this);
},
methods: {
/**
* **wsShell 创建页面级别的websocket,加载页面数据
* ws 接口:/xxx/xxx/xxx
* 参数:无
* ws参数:
* @deployId 任务id
* @tagString 当前节点
* 返回:无
* **/
wsShell() {
const _this = this;
let tag = this.urlParam.Tag;
let name= this.urlParam.name;
let pod= this.urlParam.pod;
let query = `?tag=${tag}&name=${name}&pod=${pod}`;
let url = `xxxx/xxxx${query}`;// websocket连接接口
this.shellWs = this.base.WS({
url,
isInit: true,
openFn() {
// _this.term.resize({ rows: _this.rows, cols: 100 }); //终端窗口重新设置大小 并触发term.on("resize")
},
messageFn(e) {
console.log("message", e);
if (e) {
let data = JSON.parse(e.data);
if (data.Data == "\n" || data.Data == "\r\nexit\r\n") {
_this.$message("连接已关闭");
}
// 打印后端返回数据
_this.term.write(data.Data);
}
},
errorFn(e) {
//出现错误关闭当前ws,并且提示
console.log("error", e);
_this.$message.error({
message: "ws 请求失败,请刷新重试~",
duration: 5000
});
}
});
},
onSend(data) {
data = this.base.isObject(data) ? JSON.stringify(data) : data;
data = this.base.isArray(data) ? data.toString() : data;
data = data.replace(/\\\\/, "\\");
this.shellWs.onSend(data);
},
//删除左右两端的空格
trim(str) {
return str.replace(/(^\s*)|(\s*$)/g, "");
}
}
};
</script>
(2).xterm 3.x
<template>
<div
style="height: 100%;
background: #002833;"
>
<div id="terminal" ref="terminal"></div>
</div>
</template>
<script>
import { Terminal } from "xterm";
import "xterm/dist/xterm.css";
import * as fit from "xterm/lib/addons/fit/fit";
import * as fullscreen from "xterm/lib/addons/fullscreen/fullscreen";
import * as attach from "xterm/lib/addons/attach/attach";
Terminal.applyAddon(fit);
Terminal.applyAddon(attach);
Terminal.applyAddon(fullscreen); // Apply the `fullscreen` addon
export default {
name: "Shell",
data() {
return {
order: "",
urlParam: {
fullTag: "",
namespace: "",
podName: ""
},
shellWs: "",
inputValue: "",
term: "", // 保存terminal实例
showOrder: "", // 保存服务端返回的命令
inputList: [], // 保存用户输入的命令,用以上下健切换
beforeUnload_time: "",
rows: 40,
cols: 100
};
},
created() {
this.checkURLparam();
this.wsShell();
},
mounted() {
let _this = this;
this.rows = document.querySelector(".indexContainer").offsetHeight / 16 - 5;
this.cols = document.querySelector(".indexContainer").offsetWidth / 14;
//this.cols = 400
let term = new Terminal({
rendererType: "canvas", //渲染类型
rows: parseInt(_this.rows), //行数
cols: parseInt(_this.cols), // 不指定行数,自动回车后光标从下一行开始
convertEol: true, //启用时,光标将设置为下一行的开头
// scrollback: 50, //终端中的回滚量
disableStdin: false, //是否应禁用输入。
cursorStyle: "underline", //光标样式
cursorBlink: true, //光标闪烁
theme: {
foreground: "#7e9192", //字体
background: "#002833", //背景色
cursor: "help", //设置光标
lineHeight: 16
}
});
// 换行并输入起始符“$”
term.prompt = () => {
term.write("\r\n$ ");
};
// Load WebLinksAddon on terminal, this is all that's needed to get web links
// working in the terminal.
// term.loadAddon(new WebLinksAddon());
term.open(this.$refs["terminal"]);
term.toggleFullScreen(); //全屏
window.onresize = function() {
term.fit();
term.toggleFullScreen(); //全屏
term.prompt();
}
function runFakeTerminal(_this) {
if (term._initialized) {
return;
}
term._initialized = true;
term.prompt = () => {
term.write("\r\n ");
};
term.writeln("Welcome to use Superman. ");
term.writeln(
`This is Web Terminal of pod\x1B[1;3;31m ${
_this.urlParam.podName
}\x1B[0m in namespace\x1B[1;3;31m ${_this.urlParam.namespace}\x1B[0m`
);
term.prompt();
// console.log("term", term);
// 监控键盘输入事件
// / **
// *添加事件监听器,用于按下键时的事件。事件值包含
// *将在data事件以及DOM事件中发送的字符串
// *触发了它。
// * @返回一个IDisposable停止监听。
// * /
term.on("key", function(key) {
let order = {
Data: key,
Op: "stdin"
};
_this.onSend(order);
});
term.on("paste", function(data) {
_this.order = data;
term.write(data);
});
term.on("resize", size => {
let order = {
Rows: parseInt(size.rows),
Cols: parseInt(size.cols),
Op: "resize"
};
_this.onSend(order);
});
_this.term = term;
}
runFakeTerminal(_this);
},
methods: {
/**
* **wsShell 创建页面级别的websocket,加载页面数据
* ws 接口:/xxxx/xxxx
* 参数:无
* ws参数:
* @deployId 任务id
* @tagString 当前节点
* 返回:无
* **/
wsShell() {
const _this = this;
let tag = this.urlParam.Tag;
let name = this.urlParam.name;
let pod = this.urlParam.pod;
let query = `?tag=${tag}&name=${name}&pod=${pod}`;
let url = `xxxx/xxxx${query}`;
this.shellWs = this.base.WS({
url,
isInit: true,
openFn() {
_this.term.resize({ rows: _this.rows, cols: 100 }); //终端窗口重新设置大小 并触发term.on("resize")
},
messageFn(e) {
console.log("message", e);
if (e) {
let data = JSON.parse(e.data);
if (data.Data == "\n" || data.Data == "\r\nexit\r\n") {
_this.$message("连接已关闭");
}
_this.term.write(data.Data);
_this.showOrder = data.Data;
_this.order = "";
}
},
errorFn(e) {
//出现错误关闭当前ws,并且提示
console.log("error", e);
_this.$message.error({
message: "ws 请求失败,请刷新重试~",
duration: 5000
});
}
});
},
onSend(data) {
data = this.base.isObject(data) ? JSON.stringify(data) : data;
data = this.base.isArray(data) ? data.toString() : data;
data = data.replace(/\\\\/, "\\");
this.shellWs.onSend(data);
},
//删除左右两端的空格
trim(str) {
return str.replace(/(^\s*)|(\s*$)/g, "");
}
}
};
</script>
5.封装的websocket方法
/**
* ** WebSocket 封装
* @ url 请求地址 类型:string 默认:'' 备注: 'web/msg'
* @ isInit 是否自动执行 类型:boolean 默认:false 备注: false|true
* @ openFn 自动执行open回调函数 类型:function 默认 : null 备注: 如果onOpen没有callBack,默认调用openFn
* @ messageFn 自动执行消息回调函数 类型:function 默认: null 备注: 如果onMessage没有callBack,默认调用messageFn
* @ errorFn 自动执行错误回调函数 类型:function 默认: null 备注: 如果onErrorFn没有callBack,默认调用errorFn
*
*
* 方法:
* isWebsocket 判断websocket 是否存在 返回 true|false 参数:无
* onOpen 服务端与前端连接成功后触发开 返回 无 参数:callBack(e)
* onMessage 服务端向前端发送消息时触发 返回 无 参数:callBack(e)
* onError WSC报错后触发 返回 无 参数:callBack(e)
* onClose 关闭WSC
* onSend 前端向服务端发送消息时触发 返回 无 参数:data
* readyState 获取WSC链接状态,只读不可修改
* binaryType 获取WSC连接所传输二进制数据的类型,只读
* get 获取当前实例 返回 当前实例 参数:data
* */
export class WS {
constructor({
url = "",
openFn = null,
messageFn = null,
errorFn = null,
isInit = false
} = {}) {
let loc = window.location;
url = loc.host + "/" + url;
this.url = /https/.test(loc.protocol) ? "wss://" + url : "ws://" + url;
this.websocket = "WebSocket" in window ? new WebSocket(this.url) : null;
this.error = "";
this.messageFn =
messageFn && typeof messageFn == "function"
? messageFn
: e => {
e;
};
this.errorFn =
errorFn && typeof errorFn == "function"
? errorFn
: e => {
e;
};
this.openFn =
openFn && typeof openFn == "function"
? openFn
: e => {
e;
};
if (isInit) {
WS.init(this);
}
}
//判断websocket 是否存在
isWebsocket() {
if (this.websocket) {
this.error = "";
return true;
} else {
this.error = "当前浏览器不支持WebSocket";
return false;
}
}
//直接开始执行链接,不需要手动设置打开 & 处理消息 & 错误
static init(_this) {
if (_this.isWebsocket()) {
_this.websocket.onopen = e => {
_this.openFn(e);
};
_this.websocket.onerror = e => {
_this.errorFn(e);
};
_this.websocket.onmessage = e => {
_this.messageFn(e);
};
} else {
console.error(_this.error);
}
}
//自定义WSC连接事件:服务端与前端连接成功后触发
onOpen(callBack) {
if (this.isWebsocket()) {
//判断是否传递回调函数
if (typeof callBack == "function") {
this.websocket.onopen = e => {
callBack(e);
};
} else {
this.websocket.onopen = e => {
this.openFn(e);
};
}
} else {
console.error(this.error);
}
}
// WSC消息接收事件:服务端向前端发送消息时触发
onMessage(callBack) {
if (this.isWebsocket()) {
if (typeof callBack == "function") {
this.websocket.onmessage = e => {
callBack(e);
};
} else {
this.websocket.onmessage = e => {
this.messageFn(e);
};
}
} else {
console.error(this.error);
}
}
// 自定义WSC异常事件:WSC报错后触发
onError(callBack) {
if (this.isWebsocket()) {
if (typeof callBack == "function") {
this.websocket.onerror = e => {
callBack(e);
};
} else {
this.websocket.onerror = e => {
this.errorFn(e);
};
}
} else {
console.error(this.error);
}
}
// 自定义WSC关闭事件:WSC关闭后触发
onClose() {
if (this.isWebsocket()) {
this.websocket.close();
} else {
console.error(this.error);
}
}
//前端向服务端发送消息时触发
onSend(data) {
console.log("dataweb--数据已向后端发送", data, this.isWebsocket());
if (this.isWebsocket()) {
console.log("sendsendsend");
this.websocket.send(data);
} else {
console.error(this.error);
}
}
//WSC链接状态,只读不可修改
readyState() {
//1连接已打开并准备好进行通信。2连接正在关闭。 3连接已关闭或无法打开。
if (this.isWebsocket()) {
return this.websocket.readyState;
} else {
console.error(this.error);
}
}
//获取WSC连接所传输二进制数据的类型,只读
binaryType() {
if (this.isWebsocket()) {
return this.websocket.binaryType;
} else {
console.error(this.error);
}
}
//获取当前实例
get() {
if (this.isWebsocket()) {
return this.websocket;
} else {
console.error(this.error);
}
}
}
websocket引用
import { WS } from "@/config/service/websocket.config";
const install = function(Vue) {
const base = {
//参数&方法
WS({ url, openFn, messageFn, errorFn, isInit = false } = {}) {
return new WS({ url, openFn, messageFn, errorFn, isInit });
},
};
Vue.prototype.base = base;
};
export default {
install
};
导入main.js
import base from "./config/libs/base"; //导入公共方法
Vue.use(base); //ui
正文其实已经结束了,下边都是废弃代码和废话,可以不用看哈~
…
…
…
…
…
然后剩下就是我之前的辣鸡代码了,判断了一堆输入键盘的字符,比如输入回车才发送数据,输入退格要把我输入的字符删一个再打印,输入上下键切换命令也要自行判断……是真的费了好多时间和精力,所以虽然他没什么用了,但是我舍不得彻底删掉它们,就在这里给它们留一席之地吧哈哈。
<template>
<div>
<div id="terminal" ref="terminal"></div>
</div>
</template>
<script>
import { Terminal } from "xterm";
// import { WebLinksAddon } from "xterm-addon-web-links";
import "xterm/dist/xterm.css";
// import "xterm/dist/addons/fullscreen/fullscreen.css"; //如果不成功,请检查路径
import * as fit from "xterm/lib/addons/fit/fit";
import * as fullscreen from "xterm/lib/addons/fullscreen/fullscreen";
import * as attach from "xterm/lib/addons/attach/attach";
Terminal.applyAddon(fit);
Terminal.applyAddon(attach);
Terminal.applyAddon(fullscreen); // Apply the `fullscreen` addon
export default {
name: "Shell",
data() {
return {
order: "",
urlParam: {
fullTag: "",
namespace: "",
podName: ""
},
shellWs: "", // ws实例
term: "", // 保存terminal实例
showOrder: "", // 保存服务端返回的命令
inputList: [] // 保存用户输入的命令,用以上下健切换
};
},
created() {
this.checkURLparam();
this.wsShell();
},
mounted() {
let _this = this;
// const terminal = new Terminal();
let term = new Terminal({
rendererType: "canvas", //渲染类型
rows: 40, //行数
convertEol: true, //启用时,光标将设置为下一行的开头
scrollback: 10, //终端中的回滚量
disableStdin: false, //是否应禁用输入。
cursorStyle: "underline", //光标样式
cursorBlink: true, //光标闪烁
theme: {
foreground: "yellow", //字体
background: "#060101", //背景色
cursor: "help" //设置光标
}
});
// 换行并输入起始符“$”
term.prompt = () => {
term.write("\r\n$ ");
};
term.open(this.$refs["terminal"]);
term.toggleFullScreen(); //全屏
term.fit();
term.writeln("Hello from \x1B[1;3;31mxterm.js\x1B[0m $ ");
term.prompt();
function runFakeTerminal(_this) {
if (term._initialized) {
return;
}
term._initialized = true;
term.prompt = () => {
term.write("\r\n ");
};
term.writeln("Welcome to xterm.js");
term.writeln(
"This is a local terminal emulation, without a real terminal in the back-end."
);
term.writeln("Type some keys and commands to play around.");
term.writeln("");
term.prompt();
// 监控键盘输入事件
// / **
// *添加事件监听器,用于按下键时的事件。事件值包含
// *将在data事件以及DOM事件中发送的字符串
// *触发了它。
// * @返回一个IDisposable停止监听。
// * /
let last = 0;
term.on("key", function(key, ev) {
// 可打印状态,即不是alt键ctrl等功能健时
const printable =
!ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey;
// 因服务端返回命令包含乱码,但使用write方法输出时并不显示,故将真实显示内容截取出来
let index = _this.showOrder.indexOf("sh");
let show = _this.showOrder.substr(index, _this.showOrder.length - 1);
// 当输入回车时
if (ev.keyCode === 13) {
if (_this.order == "cls" || _this.order == "clear") {
_this.order = "";
return false;
}
//先将数据发送
term.prompt();
// 判断如果不是英文给出提醒
let reg = /[a-zA-Z]/;
let order = {
Data: _this.order,
Op: "stdin"
};
if (!reg.test(_this.order)) {
term.writeln("请输入有效指令~");
} else {
// 发送数据
_this.inputList.push(_this.order);
last = _this.inputList.length - 1;
_this.onSend(order);
// 清空输入内容变量
}
} else if (ev.keyCode === 8) {
// 当输入退
// Do not delete the prompt
// 当前行字符长度如果等于后端返回字符就不进行删除
if (term._core.buffer.x > _this.showOrder.length) {
term.write("\b \b"); // 输出退格
}
// 将输入内容变量删除
if (_this.trim(_this.order) == _this.trim(_this.showOrder)) {
return false;
} else {
_this.order = _this.order.substr(0, _this.order.length - 1);
}
} else if (ev.keyCode == 38 || ev.keyCode == 40) {
let len = _this.inputList.length;
let code = ev.keyCode;
if (code === 38 && last <= len && last >= 0) {
// 直接取出字符串数组最后一个元素
let inputVal = _this.inputList[last];
term.write(inputVal);
if (last > 0) {
last--;
}
}
if (code === 40 && last < len) {
// last现在为当前元素
if (last == len - 1) {
return;
}
if (last < len - 1) {
last++;
}
let inputVal = _this.inputList[last];
term.write(inputVal);
}
} else if (ev.keyCode === 9) {
// 如果按tab键前输入了之前后端返回字符串的第一个字符,就显示此命令
if (_this.order !== "" && show.indexOf(_this.order) == 0) {
term.write(_this.showOrder);
}
} else if (printable) {
// 当为可打印内容时
if (/[a-zA-Z]/.test(key)) {
key = key.toLowerCase();
}
// 存入输入内容变量
_this.order = _this.order + key;
// 将变量写入终端内
term.write(key);
}
});
_this.term = term;
// 粘贴事件
term.on("paste", function(data) {
_this.order = data;
term.write(data);
});
}
runFakeTerminal(_this);
},
methods: {
// 检查url参数,必要参数不存在,返回到首页
checkURLparam() {
let urlObj = this.base.urlValue();
let fullTag = urlObj.full_tag ? urlObj.full_tag : ""; //所在父节点
fullTag = fullTag.replace(/(^ +| +$)/g, "");
let namespace = urlObj.namespace ? urlObj.namespace : ""; //所在父节点
namespace = namespace.replace(/(^ +| +$)/g, "");
let podName = urlObj.pod_name ? urlObj.pod_name : ""; //所在父节点
podName = podName.replace(/(^ +| +$)/g, "");
if (!fullTag || !namespace) {
//所在的父级节点为空或者deploy_id不存在的情况下,弹框提示然后返回首页
this.$alert("缺少必要参数,马上返回首页~", "提示", {
closeOnClickModal: false,
closeOnPressEscape: false,
showClose: false,
confirmButtonText: "确定",
type: "error",
callback: () => {
this.$router.replace("/web");
}
});
} else {
this.urlParam.fullTag = fullTag; //所在父节点
this.urlParam.namespace = namespace; //当前部署任务详情id
this.urlParam.podName = podName; //当前部署任务详情id
}
},
/**
* **wsShell 创建页面级别的websocket,加载页面数据
* ws 接口:/v1/task/deploy/detail/container
* 参数:无
* ws参数:
* @deployId 任务id
* @tagString 当前节点
* 返回:无
* **/
wsShell() {
const _this = this;
let tag_string = this.urlParam.fullTag;
let namespace = this.urlParam.namespace;
let pod_name = this.urlParam.podName;
let query = `?tag_string=${tag_string}&namespace=${namespace}&pod_name=${pod_name}`;
let url = `v1/container/terminal/ws${query}`;
// let loading; //初始化加载状态变量
this.shellWs = this.base.WS({
url,
isInit: true,
openFn() {
console.log("open");
},
messageFn(e) {
console.log("message", e);
if (e) {
let data = JSON.parse(e.data);
// 如果返回字符包含这些字符显示close提示
if (data.Data == "\n" || data.Data == "\r\nexit\r\n") {
alert("closed");
}
_this.term.write(data.Data);
_this.showOrder = data.Data;
_this.order = "";
}
},
errorFn(e) {
//出现错误关闭当前ws,并且提示
console.log("error", e);
_this.$message.error({
message: "ws 请求失败,请刷新重试~",
duration: 5000
});
}
});
},
onSend(data) {
data = this.base.isObject(data) ? JSON.stringify(data) : data;
data = this.base.isArray(data) ? data.toString() : data;
data = data.replace(/\\\\/, "\\");
this.shellWs.onSend(data);
},
//删除左右两端的空格
trim(str) {
return str.replace(/(^\s*)|(\s*$)/g, "");
}
}
};
</script>
然后就是要感谢我所看到的三个资料,把它们放在这里啦,大家自行查看吧
1.https://www.v2ex.com/t/633464:
2.https://github.com/billchurch/webssh2/blob/master/app/client/src/js/index.js
3.https://github.com/knva/xtermtest/blob/master/index.html
更多推荐
所有评论(0)