8 changed files with 181 additions and 21 deletions
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
import Terminal from './src/terminal.vue' |
||||
|
||||
export default Terminal |
@ -0,0 +1,122 @@
@@ -0,0 +1,122 @@
|
||||
<template> |
||||
<div id="terminal" v-loading="flag.loading" ref="terminal" element-loading-text="拼命连接中"></div> |
||||
</template> |
||||
<script lang="ts" setup> |
||||
import { ref, onMounted, onBeforeUnmount, watch, reactive } from "vue"; |
||||
import { Terminal } from "xterm"; |
||||
import "xterm/css/xterm.css"; |
||||
const prop = defineProps({ |
||||
url: { |
||||
type: String, |
||||
default: null |
||||
} |
||||
}); |
||||
const flag = reactive({ |
||||
loading: true, |
||||
lock: true |
||||
}); |
||||
const terminal = ref(null); |
||||
|
||||
const terminalSocket = ref(); |
||||
const term = ref(); |
||||
let text = ""; |
||||
|
||||
const runRealTerminal = () => { |
||||
flag.loading = false; |
||||
} |
||||
|
||||
const onWSReceive = (message) => { |
||||
term.value.write(message.data) |
||||
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 initTerm = () => { |
||||
term.value = new Terminal({ |
||||
lineHeight: 1.2, |
||||
fontSize: 18, |
||||
fontFamily: "Monaco, Menlo, Consolas, 'Courier New', monospace", |
||||
theme: { |
||||
background: '#181d28', |
||||
}, |
||||
// 光标闪烁 |
||||
cursorBlink: true, |
||||
cursorStyle: 'underline', |
||||
scrollback: 100, |
||||
tabStopWidth: 4, |
||||
cols: 120 |
||||
}); |
||||
term.value.open(terminal.value); |
||||
term.value.onKey(e => { |
||||
if (flag.lock) { |
||||
return; |
||||
} |
||||
//\x1B ESC |
||||
//\x1BOP-\x1B[24~ F1-F12 |
||||
const key = e.key; |
||||
if (key.indexOf("\u001b") !== -1) { |
||||
return; |
||||
} |
||||
console.log(e); |
||||
if (isWsOpen()) { |
||||
if (key == '\r') { |
||||
term.value.writeln(key) |
||||
terminalSocket.value.send(text); |
||||
text = ""; |
||||
} else { |
||||
text += key |
||||
term.value.write(key) |
||||
} |
||||
} |
||||
}); |
||||
} |
||||
// 是否连接中0 1 2 3 |
||||
const isWsOpen = () => { |
||||
const readyState = terminalSocket.value && terminalSocket.value.readyState; |
||||
return readyState === 1 |
||||
} |
||||
|
||||
//监听类型变化,重置term |
||||
watch(() => prop.url, () => { |
||||
flag.loading = true; |
||||
terminalSocket.value && terminalSocket.value.close(); |
||||
terminalSocket.value = null; |
||||
initWS(); |
||||
// 重置 |
||||
term.value.reset(); |
||||
}) |
||||
|
||||
onMounted(() => { |
||||
initWS(); |
||||
initTerm(); |
||||
}) |
||||
onBeforeUnmount(() => { |
||||
terminalSocket.value && terminalSocket.value.close(); |
||||
term.value.dispose(); |
||||
}) |
||||
</script> |
||||
<style lang="scss" scoped> |
||||
#terminal { |
||||
width: 100%; |
||||
height: 100%; |
||||
} |
||||
</style> |
Loading…
Reference in new issue