LoongPanel-Asp/web/components/Term.vue

149 lines
4.2 KiB
Vue

<script lang="ts" setup>
import {useMainLayoutStore} from "~/strores/UseMainLayoutStore";
import {HubConnectionBuilder} from "@microsoft/signalr";
import {FitAddon} from 'xterm-addon-fit'
import { ArrowDownRight } from 'lucide-vue-next';
//导入xtrem
import {Terminal} from "@xterm/xterm";
import "@xterm/xterm/css/xterm.css";
import _ from "lodash";
var term: Terminal;
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)
onMounted(()=>{
term = new Terminal({smoothScrollDuration:100,scrollback:1000,theme:{
background: '#00000000',
}});
term.open(document.getElementById("terminal") as HTMLElement);
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();
//绑定输入事件
term.onData((data) => {
connection.invoke("SendMessage", data)
.catch(err => {
console.log("Error while sending message: " + err);
});
})
})
const mainLayoutStore = useMainLayoutStore()
const connection = new HubConnectionBuilder()
.withUrl(`${useRuntimeConfig().public.baseUrl}/TerminalHub`,{
accessTokenFactory:()=> `${useCookie('token').value}`
})
.withAutomaticReconnect()
.build();
onMounted(async () => {
connection.on("ReceiveMessage", (message) => {
if(message.startsWith("stty cols")||message.startsWith("stty rows")) return;
term.write(message)
})
await connection.start()
.then(() => {
console.log("Connection started");
})
.catch(err => {
console.log("Error while starting connection: " + err);
});
await connection.invoke("CreateTerminal", mainLayoutStore.SelectServer.value)
.then(() => {
console.log("Terminal created")
})
setTimeout(()=>{
connection.invoke("SendMessage","neofetch\n")
},200)
})
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 });
}
</script>
<template>
<div class="terminal-box" ref="terminal">
<div id="terminal" @resize="resizeObserver" >
</div>
<ArrowDownRight class="arrow" @mousedown="resize" />
</div>
</template>
<style lang="scss" scoped>
@import "base";
.terminal-box {
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)
}
#terminal {
cursor: pointer;
&::-webkit-scrollbar{
width:0;
}
}
:deep(.xterm-viewport::-webkit-scrollbar){
width: 0;
}
.arrow{
position: absolute;
right: 0;
bottom: 0;
color: $light-bg-underline-color;
cursor: nw-resize;
}
</style>