2022-12-09 09:22:38 +08:00
|
|
|
|
<style scoped>
|
|
|
|
|
|
.console{
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
padding:5px 5px;
|
|
|
|
|
|
background-color: black;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<div :id="'ternimalContainer'+idIndex" class="console">
|
|
|
|
|
|
<div :id="'terminal'+idIndex" style="height: 100%"></div>
|
|
|
|
|
|
<fileDirectory :host="host" v-clickoutside="closeFileDir" :uuid="terminal.uuid" v-show="fileDirectoryShow" @close="showFileDir(false)" :fileDirectoryShow="fileDirectoryShow" ref="fileDirectory"/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import Terminal from '../common/js/Xterm'
|
|
|
|
|
|
import fileDirectory from './fileDirectory'
|
|
|
|
|
|
export default {
|
|
|
|
|
|
name: 'console',
|
|
|
|
|
|
components: {
|
|
|
|
|
|
fileDirectory
|
|
|
|
|
|
},
|
|
|
|
|
|
props: {
|
|
|
|
|
|
terminal: { },
|
|
|
|
|
|
terminalType: { default: '1' },
|
|
|
|
|
|
idIndex: {
|
|
|
|
|
|
type: Number,
|
|
|
|
|
|
default: 0
|
|
|
|
|
|
},
|
|
|
|
|
|
isFullScreen: {
|
|
|
|
|
|
type: Boolean,
|
|
|
|
|
|
default: false
|
|
|
|
|
|
},
|
|
|
|
|
|
fontSize: {}
|
|
|
|
|
|
},
|
|
|
|
|
|
data () {
|
|
|
|
|
|
return {
|
|
|
|
|
|
term: null,
|
|
|
|
|
|
terminalSocket: null,
|
|
|
|
|
|
termimalRows: 15,
|
|
|
|
|
|
termimalHeight: 270,
|
|
|
|
|
|
topMenuHeight: 30,
|
|
|
|
|
|
dragHeigh: 8,
|
|
|
|
|
|
minRow: 1,
|
|
|
|
|
|
fontSpaceHeight: 2, // 一行高度=fontSize+fontSpaceHeight
|
|
|
|
|
|
obj: {
|
|
|
|
|
|
id: 2
|
|
|
|
|
|
},
|
|
|
|
|
|
isInit: false,
|
|
|
|
|
|
successBackContent: 'Connecting to',
|
|
|
|
|
|
failBackContent: 'Sorry',
|
|
|
|
|
|
connectFailContent: 'Connection failed',
|
|
|
|
|
|
welcomeBackContent: 'Welcome',
|
|
|
|
|
|
psdCont: 'password: ',
|
|
|
|
|
|
conFinish: false,
|
|
|
|
|
|
conSuccessNum: 0,
|
|
|
|
|
|
inputSecret: false,
|
|
|
|
|
|
userName: '',
|
|
|
|
|
|
fileDirectoryShow: false,
|
|
|
|
|
|
host: ''
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
watch: {
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
prompt (term) {
|
|
|
|
|
|
term.write('\r\n ')// term.write('\r\n~$ ');
|
|
|
|
|
|
},
|
|
|
|
|
|
resize (consoleHeigt, consoleWidth) {
|
|
|
|
|
|
this.term.fit()
|
|
|
|
|
|
this.resizeServiceConsole()
|
|
|
|
|
|
},
|
|
|
|
|
|
resizeServiceConsole () {
|
|
|
|
|
|
const consoleBox = document.getElementById('ternimalContainer' + this.idIndex)
|
|
|
|
|
|
const width = document.body.clientWidth// 可视宽度
|
|
|
|
|
|
const height = parseInt(consoleBox.offsetHeight)
|
|
|
|
|
|
const winStyle = {
|
|
|
|
|
|
width: width,
|
2022-12-28 14:57:46 +08:00
|
|
|
|
height: height,
|
2022-12-09 09:22:38 +08:00
|
|
|
|
cols: this.term.cols, // cols和rows在resizeConsole方法已经设置
|
|
|
|
|
|
rows: this.term.rows
|
|
|
|
|
|
}
|
|
|
|
|
|
this.$post('terminal/resize', winStyle).then(response => {
|
|
|
|
|
|
if (response.code === 200) {
|
|
|
|
|
|
this.term.fit()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.$message.error(response.msg)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
focusConsole () {
|
|
|
|
|
|
this.term.focus()
|
|
|
|
|
|
},
|
|
|
|
|
|
beforeCreate () {
|
|
|
|
|
|
let rows = this.termimalRows
|
|
|
|
|
|
// const consoleBox = document.getElementById('ternimalContainer' + this.idIndex)
|
|
|
|
|
|
const height = document.body.clientHeight// 高度
|
|
|
|
|
|
// consoleBox.style.height = `${height - 120}px`
|
|
|
|
|
|
rows = (height - this.topMenuHeight) / (this.fontSize + this.fontSpaceHeight)
|
|
|
|
|
|
rows = parseInt(rows)
|
|
|
|
|
|
const terminalContainer = document.getElementById('terminal' + this.idIndex)
|
|
|
|
|
|
this.term = new Terminal({
|
|
|
|
|
|
rows: rows, // 15行大概300px高,无法设置heigh,只能设置rows
|
|
|
|
|
|
cursorStyle: 'block', // 光标样式 null | 'block' | 'underline' | 'bar'
|
|
|
|
|
|
disableStdin: false, // 是否应禁用输入
|
|
|
|
|
|
fontSize: this.fontSize
|
|
|
|
|
|
})
|
|
|
|
|
|
this.term.open(terminalContainer)
|
|
|
|
|
|
this.term.focus()
|
2023-01-10 17:53:29 +08:00
|
|
|
|
this.create()
|
2022-12-09 09:22:38 +08:00
|
|
|
|
},
|
|
|
|
|
|
create () {
|
|
|
|
|
|
const that = this
|
|
|
|
|
|
const token = localStorage.getItem('nz-token')
|
|
|
|
|
|
let baseUrl = JSON.parse(JSON.stringify(this.$axios.defaults.baseURL))
|
|
|
|
|
|
const protocol = window.location.protocol.indexOf('https') > -1 ? 'wss' : 'ws'
|
|
|
|
|
|
if (baseUrl.startsWith('/')) {
|
|
|
|
|
|
baseUrl = `${protocol}://` + window.location.host + baseUrl
|
|
|
|
|
|
} else {
|
|
|
|
|
|
baseUrl = baseUrl.replace('http://', 'ws://').replace('https://', 'wss://')
|
|
|
|
|
|
}
|
|
|
|
|
|
let url = ''
|
|
|
|
|
|
this.terminal.height = document.body.clientHeight - 100
|
2022-12-28 14:02:21 +08:00
|
|
|
|
this.terminal.width = document.body.clientWidth
|
2022-12-09 09:22:38 +08:00
|
|
|
|
if (this.terminal.type === 'asset') {
|
|
|
|
|
|
url = baseUrl + '/terminal.ws?width=' + this.terminal.width + '&height=' + this.terminal.height + '&cols=' + this.terminal.cols + '&rows=' + this.terminal.rows + '&token=' + token + '&assetId=' + this.terminal.assetId + '&accountId=' + this.terminal.accountId + '&uuid=' + this.terminal.uuid
|
|
|
|
|
|
} else if (this.terminal.type === 'custom') {
|
|
|
|
|
|
url = baseUrl + '/terminal.ws?width=' + this.terminal.width + '&height=' + this.terminal.height + '&cols=' + this.terminal.cols + '&rows=' + this.terminal.rows + '&token=' + token + '&accountId=' + this.terminal.accountId + '&uuid=' + this.terminal.uuid
|
|
|
|
|
|
Object.keys(this.terminal.custom).forEach(key => {
|
|
|
|
|
|
if (this.terminal.custom[key]) {
|
|
|
|
|
|
url += '&' + key + '=' + this.terminal.custom[key]
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
this.terminalSocket = new WebSocket(url)
|
|
|
|
|
|
// 连接成功onclose
|
|
|
|
|
|
this.terminalSocket.onopen = () => {
|
|
|
|
|
|
this.terminal.isLogin = true
|
|
|
|
|
|
this.isInit = true
|
2023-01-10 17:53:29 +08:00
|
|
|
|
this.term.focus()
|
2022-12-09 09:22:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
// 登录后,你输入的内容从后台服务返回
|
|
|
|
|
|
this.term.on('data', function (data) {
|
|
|
|
|
|
/*
|
|
|
|
|
|
let code = data.charCodeAt(0);
|
|
|
|
|
|
if(code==13){
|
|
|
|
|
|
}else {
|
|
|
|
|
|
//that.term.write(data);
|
|
|
|
|
|
} */
|
|
|
|
|
|
})
|
|
|
|
|
|
// 返回
|
|
|
|
|
|
this.terminalSocket.onmessage = function (evt) {
|
|
|
|
|
|
let backContent = evt.data
|
|
|
|
|
|
const welComIndex = backContent.indexOf(that.welcomeBackContent)
|
|
|
|
|
|
if (welComIndex > -1) { // 无服务器信息(只与nezha进行了连接)
|
|
|
|
|
|
const connectResult = {
|
|
|
|
|
|
title: '',
|
|
|
|
|
|
color: 1
|
|
|
|
|
|
}
|
|
|
|
|
|
that.$emit('refreshConsoleTitle', connectResult)// 1:grey 2 green 3 red
|
|
|
|
|
|
} else {
|
|
|
|
|
|
const successContentIndex = backContent.indexOf(that.successBackContent)
|
|
|
|
|
|
if (successContentIndex > -1) {
|
|
|
|
|
|
// that.conFinish = true;
|
|
|
|
|
|
const startIndex = successContentIndex + that.successBackContent.length + 1
|
|
|
|
|
|
backContent = backContent.substring(startIndex)
|
|
|
|
|
|
const endIndex = backContent.indexOf('\r\n')
|
|
|
|
|
|
const title = backContent.substring(0, endIndex)
|
|
|
|
|
|
const connectResult = {
|
|
|
|
|
|
title: title,
|
|
|
|
|
|
color: 2
|
|
|
|
|
|
}
|
|
|
|
|
|
that.$emit('refreshConsoleTitle', connectResult)// 1:grey 2 green 3 red
|
|
|
|
|
|
} else { // 失败
|
|
|
|
|
|
const failContentIndex = backContent.indexOf(that.failBackContent)
|
|
|
|
|
|
const connectFailIndex = backContent.indexOf(that.connectFailContent)
|
|
|
|
|
|
if (failContentIndex > -1) {
|
|
|
|
|
|
// that.conFinish = true;
|
|
|
|
|
|
const connectResult = {
|
|
|
|
|
|
title: '',
|
|
|
|
|
|
color: 3
|
|
|
|
|
|
}
|
|
|
|
|
|
that.$emit('refreshConsoleTitle', connectResult)// 1:grey 2 green 3 red
|
|
|
|
|
|
} else if (connectFailIndex > -1) {
|
|
|
|
|
|
const connectResult = {
|
|
|
|
|
|
title: '',
|
|
|
|
|
|
color: 3
|
|
|
|
|
|
}
|
|
|
|
|
|
that.$emit('refreshConsoleTitle', connectResult)// 1:grey 2 green 3 red
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 关闭
|
|
|
|
|
|
this.terminalSocket.onclose = () => {
|
|
|
|
|
|
this.terminal.isLogin = false
|
|
|
|
|
|
// 报错sorry的,还没来得及看信息就关闭
|
|
|
|
|
|
// this.$emit("closeConsole",this.terminal.name);//
|
|
|
|
|
|
this.term && this.term.setOption('disableStdin', true)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 错误
|
|
|
|
|
|
this.terminalSocket.onerror = (e) => {
|
|
|
|
|
|
this.terminal.isLogin = false
|
|
|
|
|
|
}
|
|
|
|
|
|
// 选中 复制
|
|
|
|
|
|
this.term.on('selection', function () {})
|
|
|
|
|
|
this.term.attachCustomKeyEventHandler(function (ev) { })
|
|
|
|
|
|
|
|
|
|
|
|
this.term.attach(this.terminalSocket)
|
|
|
|
|
|
this.term._initialized = true
|
2022-12-28 14:02:21 +08:00
|
|
|
|
// this.term.fit()// 自适应大小(使终端的尺寸和几何尺寸适合于终端容器的尺寸) 只是width
|
2022-12-09 09:22:38 +08:00
|
|
|
|
this.$nextTick(() => { // 解决进入全屏和退出全屏是底部隐藏
|
|
|
|
|
|
this.setFontSize(this.fontSize)
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
closeSocket () {
|
|
|
|
|
|
if (this.terminalSocket) {
|
|
|
|
|
|
this.terminalSocket.close()
|
2022-12-14 17:36:52 +08:00
|
|
|
|
this.terminalSocket = ''
|
2022-12-09 09:22:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (this.term) {
|
|
|
|
|
|
this.term.destroy()
|
|
|
|
|
|
}
|
|
|
|
|
|
// 初始化console的高度
|
|
|
|
|
|
this.conFinish = false
|
|
|
|
|
|
},
|
|
|
|
|
|
setFontSize (fontSize) {
|
|
|
|
|
|
this.term && this.term.setOption('fontSize', fontSize)
|
|
|
|
|
|
const consoleBox = document.getElementById('ternimalContainer' + this.idIndex)
|
|
|
|
|
|
const width = document.body.clientWidth// 可视宽度
|
|
|
|
|
|
let height = parseInt(consoleBox.offsetHeight)
|
|
|
|
|
|
if (height == null || !height) { height = this.termimalHeight }
|
|
|
|
|
|
const winStyle = {
|
|
|
|
|
|
width: width,
|
|
|
|
|
|
height: height,
|
|
|
|
|
|
cols: this.term.cols, // cols和rows在resizeConsole方法已经设置
|
|
|
|
|
|
rows: this.term.rows
|
|
|
|
|
|
}
|
|
|
|
|
|
// 调整终端可视区域高度
|
|
|
|
|
|
// document.getElementsByClassName('xterm-screen')[this.idIndex].style.height = height - 30 + 'px'
|
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
|
this.term.resize(this.term.cols, this.term.rows)
|
|
|
|
|
|
this.$post('terminal/resize', winStyle).then(response => {
|
|
|
|
|
|
if (response.code === 200) {
|
|
|
|
|
|
this.term.fit()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.$message.error(response.msg)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
reconnect () {
|
|
|
|
|
|
this.terminal.isLogin = false
|
|
|
|
|
|
this.closeSocket()
|
2023-01-11 11:24:56 +08:00
|
|
|
|
setTimeout(() => {
|
2023-01-10 17:53:29 +08:00
|
|
|
|
this.beforeCreate()
|
2023-01-11 11:24:56 +08:00
|
|
|
|
}, 100)
|
2022-12-09 09:22:38 +08:00
|
|
|
|
},
|
|
|
|
|
|
closeFileDir () {
|
|
|
|
|
|
this.showFileDir(false)
|
|
|
|
|
|
},
|
|
|
|
|
|
showFileDir (show) {
|
|
|
|
|
|
if (JSON.stringify(show) == JSON.stringify(this.fileDirectoryShow)) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if (show) {
|
|
|
|
|
|
this.fileDirectoryShow = show
|
|
|
|
|
|
}
|
|
|
|
|
|
let animationClass = ''
|
|
|
|
|
|
animationClass = show ? 'backInRight' : 'backOutRight'
|
|
|
|
|
|
this.animateCSS(this.$refs.fileDirectory.$el, animationClass).then((message) => {
|
|
|
|
|
|
this.fileDirectoryShow = show
|
|
|
|
|
|
})
|
2022-12-09 21:56:35 +08:00
|
|
|
|
},
|
|
|
|
|
|
enterStr (message) {
|
2022-12-14 17:36:52 +08:00
|
|
|
|
if (this.terminalSocket && this.terminal.isLogin) {
|
2023-01-11 11:24:56 +08:00
|
|
|
|
this.term.send(message)
|
|
|
|
|
|
this.term.send('\n')
|
2022-12-28 14:57:46 +08:00
|
|
|
|
this.term.scrollToBottom()
|
2023-01-05 17:32:53 +08:00
|
|
|
|
this.historyChange(message)
|
2022-12-14 17:36:52 +08:00
|
|
|
|
}
|
2023-01-05 17:32:53 +08:00
|
|
|
|
},
|
|
|
|
|
|
// 新增历史记录
|
|
|
|
|
|
historyChange (message) {
|
|
|
|
|
|
this.$emit('historyChange', message)
|
2022-12-09 09:22:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
mounted () {
|
|
|
|
|
|
this.beforeCreate()
|
|
|
|
|
|
},
|
|
|
|
|
|
beforeDestroy () {
|
|
|
|
|
|
this.closeSocket()
|
|
|
|
|
|
this.term.off('selection')
|
|
|
|
|
|
this.term.off('data')
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|