2024-06-22 10:54:02 +08:00
|
|
|
<script lang="ts" setup>
|
|
|
|
import {useMainLayoutStore} from "~/strores/UseMainLayoutStore";
|
2024-06-29 18:16:29 +08:00
|
|
|
import {HubConnectionBuilder} from "@microsoft/signalr";
|
|
|
|
import {FitAddon} from 'xterm-addon-fit'
|
2024-07-23 23:03:35 +08:00
|
|
|
import { ArrowDownRight } from 'lucide-vue-next';
|
2024-06-29 18:16:29 +08:00
|
|
|
//导入xtrem
|
|
|
|
import {Terminal} from "@xterm/xterm";
|
|
|
|
import "@xterm/xterm/css/xterm.css";
|
2024-07-23 23:03:35 +08:00
|
|
|
import _ from "lodash";
|
|
|
|
|
2024-06-29 18:16:29 +08:00
|
|
|
var term: Terminal;
|
2024-07-23 23:03:35 +08:00
|
|
|
const terminal = ref<HTMLDivElement|null>(null);
|
|
|
|
const resizeObserver =_.debounce(()=>{
|
|
|
|
if(terminal.value===null) return;
|
|
|
|
const cols = Math.floor(terminal.value.clientWidth / 9);
|
|
|
|
const rows = Math.floor(terminal.value.clientHeight / 17);
|
|
|
|
term.resize(cols-2, rows-1)
|
|
|
|
connection.invoke("ResizeTerminal", rows-1, cols-2)
|
|
|
|
term.focus();
|
|
|
|
},500)
|
2024-06-29 18:16:29 +08:00
|
|
|
onMounted(()=>{
|
2024-07-23 23:03:35 +08:00
|
|
|
term = new Terminal({smoothScrollDuration:100,scrollback:1000,theme:{
|
|
|
|
background: '#00000000',
|
|
|
|
}});
|
2024-06-29 18:16:29 +08:00
|
|
|
term.open(document.getElementById("terminal") as HTMLElement);
|
2024-07-23 23:03:35 +08:00
|
|
|
if(terminal.value===null) return;
|
|
|
|
const cols = Math.floor(terminal.value.clientWidth / 9);
|
|
|
|
const rows = Math.floor(terminal.value.clientHeight / 17);
|
|
|
|
term.resize(cols-2, rows-1)
|
|
|
|
connection.invoke("ResizeTerminal", rows-1, cols-2)
|
2024-06-29 18:16:29 +08:00
|
|
|
term.focus();
|
2024-07-23 23:03:35 +08:00
|
|
|
|
2024-06-29 18:16:29 +08:00
|
|
|
//绑定输入事件
|
|
|
|
term.onData((data) => {
|
|
|
|
connection.invoke("SendMessage", data)
|
|
|
|
.catch(err => {
|
|
|
|
console.log("Error while sending message: " + err);
|
|
|
|
});
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-06-22 10:54:02 +08:00
|
|
|
const mainLayoutStore = useMainLayoutStore()
|
2024-06-29 18:16:29 +08:00
|
|
|
const connection = new HubConnectionBuilder()
|
2024-06-29 19:22:47 +08:00
|
|
|
.withUrl(`${useRuntimeConfig().public.baseUrl}/TerminalHub`,{
|
|
|
|
accessTokenFactory:()=> `${useCookie('token').value}`
|
|
|
|
})
|
2024-06-29 18:16:29 +08:00
|
|
|
.withAutomaticReconnect()
|
|
|
|
.build();
|
|
|
|
onMounted(async () => {
|
|
|
|
connection.on("ReceiveMessage", (message) => {
|
2024-07-23 23:03:35 +08:00
|
|
|
if(message.startsWith("stty cols")||message.startsWith("stty rows")) return;
|
2024-06-29 18:16:29 +08:00
|
|
|
term.write(message)
|
|
|
|
})
|
|
|
|
await connection.start()
|
|
|
|
.then(() => {
|
|
|
|
console.log("Connection started");
|
|
|
|
})
|
|
|
|
.catch(err => {
|
|
|
|
console.log("Error while starting connection: " + err);
|
|
|
|
});
|
2024-07-22 18:41:15 +08:00
|
|
|
await connection.invoke("CreateTerminal", mainLayoutStore.SelectServer.value)
|
2024-06-29 18:16:29 +08:00
|
|
|
.then(() => {
|
|
|
|
console.log("Terminal created")
|
|
|
|
})
|
|
|
|
setTimeout(()=>{
|
|
|
|
connection.invoke("SendMessage","neofetch\n")
|
|
|
|
},200)
|
2024-06-22 10:54:02 +08:00
|
|
|
})
|
2024-07-23 23:03:35 +08:00
|
|
|
const resize=()=>{
|
|
|
|
|
|
|
|
//监听鼠标位置
|
|
|
|
const onMouseMove=(e:MouseEvent)=>{
|
|
|
|
// 获取鼠标位置
|
|
|
|
const x = e.clientX;
|
|
|
|
const y = e.clientY;
|
|
|
|
// 获取terminal的宽高
|
|
|
|
if(terminal.value===null) return;
|
|
|
|
let width = x - terminal.value.getBoundingClientRect().left;
|
|
|
|
let height = y - terminal.value.getBoundingClientRect().top;
|
|
|
|
// 设置terminal的宽高
|
|
|
|
terminal.value.style.width = `${width}px`;
|
|
|
|
terminal.value.style.height = `${height}px`;
|
|
|
|
}
|
|
|
|
document.addEventListener('mousemove', onMouseMove, { passive: true });
|
|
|
|
// 监听鼠标抬起事件,用于停止监听鼠标移动
|
|
|
|
const onMouseUp = () => {
|
|
|
|
if(terminal.value===null) return;
|
|
|
|
let width = terminal.value.getBoundingClientRect().width;
|
|
|
|
width = Math.round(width / 9) * 9+20;
|
|
|
|
let height = terminal.value.getBoundingClientRect().height;
|
|
|
|
height = Math.round(height / 17) * 17+20;
|
|
|
|
terminal.value.style.width = `${width}px`;
|
|
|
|
terminal.value.style.height = `${height}px`;
|
|
|
|
resizeObserver()
|
|
|
|
// 停止监听鼠标移动事件
|
|
|
|
document.removeEventListener('mousemove', onMouseMove);
|
|
|
|
// 停止监听鼠标抬起事件
|
|
|
|
document.removeEventListener('mouseup', onMouseUp);
|
|
|
|
};
|
|
|
|
|
|
|
|
// 监听鼠标抬起事件
|
|
|
|
document.addEventListener('mouseup', onMouseUp, { passive: true });
|
|
|
|
}
|
|
|
|
|
2024-06-22 10:54:02 +08:00
|
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
2024-07-23 23:03:35 +08:00
|
|
|
<div class="terminal-box" ref="terminal">
|
|
|
|
<div id="terminal" @resize="resizeObserver" >
|
2024-06-29 18:16:29 +08:00
|
|
|
</div>
|
2024-07-23 23:03:35 +08:00
|
|
|
<ArrowDownRight class="arrow" @mousedown="resize" />
|
2024-06-22 10:54:02 +08:00
|
|
|
</div>
|
|
|
|
|
2024-06-29 18:16:29 +08:00
|
|
|
|
2024-06-22 10:54:02 +08:00
|
|
|
</template>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
@import "base";
|
|
|
|
|
2024-06-29 18:16:29 +08:00
|
|
|
.terminal-box {
|
2024-07-23 23:03:35 +08:00
|
|
|
padding: 10px;
|
|
|
|
width: 800px;
|
|
|
|
height: 500px;
|
|
|
|
max-width: 80vw;
|
|
|
|
max-height: 80vh;
|
|
|
|
border-radius: $radius;
|
|
|
|
background: rgba(0,0,0,.5);
|
|
|
|
backdrop-filter: blur(20px)
|
2024-06-29 18:16:29 +08:00
|
|
|
}
|
2024-06-22 10:54:02 +08:00
|
|
|
|
2024-06-29 18:16:29 +08:00
|
|
|
#terminal {
|
2024-07-23 23:03:35 +08:00
|
|
|
cursor: pointer;
|
2024-06-29 18:16:29 +08:00
|
|
|
&::-webkit-scrollbar{
|
|
|
|
width:0;
|
2024-06-22 10:54:02 +08:00
|
|
|
}
|
|
|
|
}
|
2024-06-29 18:16:29 +08:00
|
|
|
:deep(.xterm-viewport::-webkit-scrollbar){
|
|
|
|
width: 0;
|
|
|
|
}
|
2024-07-23 23:03:35 +08:00
|
|
|
.arrow{
|
|
|
|
position: absolute;
|
|
|
|
right: 0;
|
|
|
|
bottom: 0;
|
|
|
|
color: $light-bg-underline-color;
|
|
|
|
cursor: nw-resize;
|
|
|
|
}
|
2024-06-22 10:54:02 +08:00
|
|
|
</style>
|