基于vue3.0和element-plus的组件库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

201 lines
4.7 KiB

2 years ago
<template>
<el-row>
<el-col :span="12">
2 years ago
<div id="terminal" v-loading="flag.loading" ref="terminal" :element-loading-text="state.lang.connect"
2 years ago
@click.right.native="showClear($event)"></div>
</el-col>
<el-col :span="12">
2 years ago
<div id="response" v-loading="flag.loading" ref="response" :element-loading-text="state.lang.connect"></div>
2 years ago
</el-col>
</el-row>
</template>
<script lang="ts" setup>
import { ref, onMounted, onBeforeUnmount, watch, reactive } from "vue";
2 years ago
import { useStore } from "vuex";
2 years ago
import { Terminal } from "xterm";
import "xterm/css/xterm.css";
2 years ago
const { state } = useStore();
2 years ago
const prop = defineProps({
url: {
type: String,
default: null
},
msgFilter: {
type: Function,
defalut: msg => {
return { unlock: false, data: msg };
}
},
prmt: {
type: String,
default: ">"
}
});
const flag = reactive({
loading: true,
lock: true
});
const terminal = ref(null);
const response = ref(null);
const terminalSocket = ref();
const term = ref();
const rsp = ref();
let text = "";
const runRealTerminal = () => {
flag.loading = false;
term.value.write(prop.prmt);
flag.lock = false;
}
const onWSReceive = (message) => {
if (prop.msgFilter) {
const data = prop.msgFilter(message.data);
if (data) {
rsp.value.writeln(data)
} else {
flag.lock = false
term.value.write(prop.prmt)
}
} else {
rsp.value.writeln(message.data)
rsp.value.write(prop.prmt)
flag.lock = false;
}
}
const errorRealTerminal = (ex) => {
let message = ex.message;
if (!message) message = 'disconnected'
term.value.write(`\x1b[31m${message}\x1b[m\r\n`)
console.log("err");
}
const closeRealTerminal = () => {
console.log("close");
}
const initWS = () => {
if (!prop.url) {
return;
}
terminalSocket.value = new WebSocket(prop.url);
terminalSocket.value.onopen = runRealTerminal;
terminalSocket.value.onmessage = onWSReceive;
terminalSocket.value.onclose = closeRealTerminal;
terminalSocket.value.onerror = errorRealTerminal;
}
const onKey = e => {
if (flag.lock) {
return;
}
//\x1B ESC
//\x1BOP-\x1B[24~ F1-F12
const key = e.key;
if (key.indexOf("\u001b") !== -1) {
return;
}
if (isWsOpen()) {
if (key === '\x7F') {
if (text.length > 0) {
term.value.write("\b \b");
text = text.substring(0, text.length - 1)
}
return;
}
if (key == '\r') {
term.value.writeln(key)
if (e.domEvent.ctrlKey) {
text += key;
} else {
sendText(text);
text = "";
}
} else {
text += key
term.value.write(key)
}
}
}
const initTerm = () => {
const options = {
lineHeight: 1.2,
fontSize: 16,
fontFamily: "Monaco, Menlo, Consolas, 'Courier New', monospace",
theme: {
background: '#181d28',
},
// 光标闪烁
cursorBlink: true,
cursorStyle: 'underline',
scrollback: 100,
tabStopWidth: 2,
cols: 80
}
options.cols = Math.ceil((window.innerWidth - 80) / 20)
term.value = new Terminal(options);
term.value.open(terminal.value);
term.value.onKey(onKey);
options.cursorBlink = false;
rsp.value = new Terminal(options);
rsp.value.open(response.value);
}
const sendText = str => {
if (str) {
terminalSocket.value.send(str);
flag.lock = true;
} else {
term.value.write(prop.prmt);
}
}
// 是否连接中0 1 2 3
const isWsOpen = () => {
const readyState = terminalSocket.value && terminalSocket.value.readyState;
return readyState === 1
}
const showClear = e => {
// 去掉默认事件
document.oncontextmenu = function (e) {
e.preventDefault();
};
navigator.clipboard.readText().then(content => {
term.value.write(content)
text += content;
// sendText(content);
})
// term.value.clear();
}
//监听类型变化,重置term
watch(() => prop.url, () => {
term.value.reset();
2 years ago
rsp.value.reset();
2 years ago
flag.loading = true;
flag.lock = false;
terminalSocket.value && terminalSocket.value.close();
terminalSocket.value = null;
initWS();
// 重置
})
onMounted(() => {
initWS();
initTerm();
})
onBeforeUnmount(() => {
terminalSocket.value && terminalSocket.value.close();
term.value.dispose();
})
</script>
<style lang="scss" scoped>
#terminal {
width: 100%;
height: 100%;
}
</style>