xterm.js
作为终端面板,以socket.js + stompjs
实现消息通信。
效果如下:
代码:
<template>
<div ref="terminalContainerRef"></div>
</template>
<script lang="ts" setup>
import { ref, onMounted, nextTick, onUnmounted, watch } from "vue";
import { Terminal } from "xterm";
import "xterm/css/xterm.css";
import SockJS from "sockjs-client/dist/sockjs.min.js";
import Stomp from "stompjs";
let socket: any = null;
let term: any = null;
let stompClient: any = null;
const terminalContainerRef = ref();
const props = defineProps({
params: {
type: Object,
default: () => {},
},
visible: {
type: Boolean,
default: false,
},
});
const socketOptions = {
url: `api/amo/amo-socket`, // sock连接地址
subscribes: `/amo/`, // 订阅地址前缀,真实地址为:/amo/${主机id}
sendUrl: `/amo/webssh/recever`, // 发送消息地址
};
watch(
() => props.visible,
(val) => {
if (val) {
initWebSSH();
} else {
disconnect();
}
},
{ deep: true },
);
onMounted(() => {
initWebSSH();
});
onUnmounted(() => {
disconnect();
});
/**
* @description 初始化 xterm
* @param {*}
* @returns {*}
*/
const initWebSSH = () => {
nextTick(() => {
openTerminal();
});
};
/**
* @description 断开socket连接
* @param {*}
* @returns {*}
*/
const disconnect = () => {
// 断开连接
stompClient.disconnect();
term.dispose();
console.log("Disconnected!");
};
/**
* @description 发送socket消息
* @param {object} data
* @returns {*}
*/
const onSendMsg = (data) => {
stompClient.send(socketOptions.sendUrl, {}, JSON.stringify(data));
};
/**
* @description 建立socket连接
* @returns {*}
*/
const createSocket = () => {
const certificate = props.params.cerIdList?.find((item: any) => item.isDefault == "1");
let msgObj = {
operate: "CONNECT", // "CONNECT" 连接 "COMMAND"命令 "CLOSE" 关闭
hostId: props.params?.hostId,
certificateId: certificate?.cerId || "",
command: "",
};
// 订阅消息
const onSubscribes = () => {
const subscribes = socketOptions.subscribes;
const link = subscribes + props.params?.hostId;
stompClient.subscribe(
link,
(msg) => {
let { body } = msg;
body = JSON.parse(body);
const { bizCode, data } = body; // 这里的 data 为服务器发送到客户端的信息字段,不是固定的
console.log(bizCode + "_received", data);
term.write(data);
},
{},
);
};
// 监听键盘输入,并发送给服务器
const onKeyBoard = () => {
// 连接成功后监听terminal输入
term.onData((str) => {
msgObj.command = str;
msgObj.operate = "COMMAND";
onSendMsg(msgObj);
});
};
// 连接
const token = localStorage.token;
socket = new SockJS(socketOptions.url, null, {
// transports: ["websocket"],
// timeout: 15000,
headers: {
Authorization: `Bearer ${token}`,
}
});
stompClient = Stomp.over(socket);
stompClient.connect(
{},
(frame) => {
onKeyBoard();
onSendMsg(msgObj);
// 接收订阅消息
onSubscribes();
term.writeln("Connection Successful!");
term.writeln("Type some keys and commands to play around.");
term.focus();
},
() => {
term.writeln("");
term.writeln("Connection Fail!");
term.writeln("");
console.log("***连接失败***");
},
);
};
/**
* @description socket连接前准备
* @param {object} term
* @returns {*}
*/
const runFakeTerminal = (terms) => {
if (terms._initialized) {
return;
}
terms._initialized = true;
//在页面上显示连接中...
terms.writeln("Connecting...");
};
/**
* @description 实例化 xterm
* @param {*}
* @returns {*}
*/
const openTerminal = () => {
const terminalDom = terminalContainerRef.value;
if (!terminalDom) return;
const terms = new Terminal({
rows: 40, // 行数,对应高度
cols: 72, // 列数,对应宽度
cursorBlink: true, // 光标闪烁
cursorStyle: "block", // 光标样式 null | 'block' | 'underline' | 'bar'
scrollback: 10, //终端中的回滚量
tabStopWidth: 8, //制表宽度
disableStdin: false, //是否应禁用输入。
rendererType: "canvas", //渲染类型
theme: {
foreground: "yellow", //字体
background: "#060101", //背景色
cursor: "help", //设置光标
},
});
terms.open(terminalDom);
term = terms;
runFakeTerminal(terms);
// 建立socket连接
createSocket();
};
defineExpose({
disconnect,
});
</script>