新增终端连接状态展示

This commit is contained in:
chaos-zhu 2024-08-15 09:24:06 +08:00
parent e0eb1446db
commit 1e783c0d90
5 changed files with 88 additions and 44 deletions

View File

@ -158,6 +158,7 @@ module.exports = (httpServer) => {
// 初始化新的SSH客户端对象
sshClient = new SSHClient()
stream = await createTerminal(ip, socket, sshClient)
socket.emit('reconnect_terminal_success')
socket.on('input', listenerInput)
socket.on('resize', resizeShell)
}, 3000)

15
web/src/utils/enum.js Normal file
View File

@ -0,0 +1,15 @@
// 终端连接状态
export const terminalStatus = {
CONNECTING: 'connecting',
RECONNECTING: 'reconnecting',
CONNECT_FAIL: 'connect_fail',
CONNECT_SUCCESS: 'connect_success'
}
export const terminalStatusList = [
{ value: terminalStatus.CONNECTING, label: '连接中', color: '#FFA500' },
{ value: terminalStatus.RECONNECTING, label: '重连中', color: '#FFA500' },
{ value: terminalStatus.CONNECT_FAIL, label: '连接失败', color: '#DC3545' },
{ value: terminalStatus.CONNECT_SUCCESS, label: '已连接', color: '#28A745' },
]
// other...

View File

@ -21,14 +21,17 @@ import { SearchAddon } from '@xterm/addon-search'
// import { SearchBarAddon } from 'xterm-addon-search-bar'
import { WebLinksAddon } from '@xterm/addon-web-links'
import socketIo from 'socket.io-client'
import { terminalStatus } from '@/utils/enum'
const { CONNECTING, RECONNECTING, CONNECT_SUCCESS, CONNECT_FAIL } = terminalStatus
const { io } = socketIo
const { proxy: { $api, $store, $serviceURI, $notification, $router, $message } } = getCurrentInstance()
const props = defineProps({
host: {
hostObj: {
required: true,
type: String
type: Object
},
fontSize: {
required: false,
@ -54,11 +57,13 @@ const command = ref('')
const timer = ref(null)
const fitAddon = ref(null)
const searchBar = ref(null)
const isManual = ref(false)
const isConnectSuccess = ref(false)
const isConnectFail = ref(false)
const isConnecting = ref(true)
const isReConnect = ref(false)
const hasRegisterEvent = ref(false)
// const isConnectSuccess = ref(false)
// const isConnectFail = ref(false)
// const isConnecting = ref(true)
// const isReConnect = ref(false)
const curStatus = ref(CONNECTING)
const terminal = ref(null)
const terminalRef = ref(null)
@ -66,6 +71,8 @@ const token = computed(() => $store.token)
const theme = computed(() => props.theme)
const fontSize = computed(() => props.fontSize)
const background = computed(() => props.background)
const hostObj = computed(() => props.hostObj)
const host = computed(() => hostObj.value.host)
watch(theme, () => {
nextTick(() => {
@ -96,28 +103,28 @@ watch(background, (newVal) => {
})
}, { immediate: true })
watch(curStatus, () => {
console.warn(`status: ${ curStatus.value }`)
hostObj.value.status = curStatus.value
})
const getCommand = async () => {
let { data } = await $api.getCommand(props.host)
let { data } = await $api.getCommand(host.value)
if (data) command.value = data
}
const connectIO = () => {
const { host } = props
socket.value = io($serviceURI, {
path: '/terminal',
forceNew: false,
reconnectionAttempts: 1
})
socket.value.on('connect', () => {
console.log('/terminal socket已连接', host)
socket.value.emit('create', { host, token: token.value })
console.log('/terminal socket已连接', host.value)
socket.value.emit('create', { host: host.value, token: token.value })
socket.value.on('connect_terminal_success', () => {
isConnectFail.value = false
isConnecting.value = false
if (isReConnect.value) {
isReConnect.value = false
return //
}
if (hasRegisterEvent.value) return // , . socket
hasRegisterEvent.value = true
socket.value.on('output', (str) => {
term.value.write(str)
@ -125,7 +132,7 @@ const connectIO = () => {
})
socket.value.on('connect_shell_success', () => {
isConnectSuccess.value = true
curStatus.value = CONNECT_SUCCESS
onResize()
onFindText()
onWebLinks()
@ -144,28 +151,26 @@ const connectIO = () => {
})
socket.value.on('connect_close', () => {
if (isConnectFail.value) return
isReConnect.value = true // true
isConnecting.value = true
isConnectSuccess.value = false
console.warn('连接断开,3秒后重连: ', host)
if (curStatus.value === CONNECT_FAIL) return //
curStatus.value = RECONNECTING
console.warn('连接断开,3秒后自动重连: ', host.value)
term.value.write('\r\n连接断开,3秒后自动重连...\r\n')
socket.value.emit('reconnect_terminal')
})
socket.value.on('reconnect_terminal_success', () => {
curStatus.value = CONNECT_SUCCESS
})
socket.value.on('create_fail', (message) => {
isConnectFail.value = true
isConnecting.value = false
isConnectSuccess.value = false
console.error('n创建失败:', host, message)
curStatus.value = CONNECT_FAIL
console.error('n创建失败:', host.value, message)
term.value.write(`\r\n创建失败: ${ message }\r\n`)
})
socket.value.on('connect_fail', (message) => {
isConnectFail.value = true
isConnecting.value = false
isConnectSuccess.value = false
console.error('连接失败:', host, message)
curStatus.value = CONNECT_FAIL
console.error('连接失败:', host.value, message)
term.value.write(`\r\n连接失败: ${ message }\r\n`)
})
})
@ -174,10 +179,8 @@ const connectIO = () => {
console.warn('terminal websocket 连接断开')
socket.value.removeAllListeners() //
// socket.value.off('output') // output,onData
isConnectFail.value = true
isConnecting.value = true
isConnectSuccess.value = false
if (!isManual.value) $notification({ title: '与面板socket连接断开', message: `${ props.host }-请检查socket服务是否稳定`, type: 'error' })
curStatus.value = CONNECT_FAIL
term.value.write('\r\nError: 与面板socket连接断开。请关闭此tab并检查本地与面板连接是否稳定\r\n')
})
socket.value.on('connect_error', (err) => {
@ -316,13 +319,13 @@ const onData = () => {
enterTimer.value = setTimeout(() => {
if (enterTimer.value) clearTimeout(enterTimer.value)
if (key === '\r') { // Enter
if (isConnectFail.value && !isConnecting.value) { // &&
isConnecting.value = true
if (curStatus.value === CONNECT_FAIL) { // &&
curStatus.value = CONNECTING
term.value.write('\r\n连接中...\r\n')
socket.value.emit('reconnect_terminal')
return
}
if (isConnectSuccess.value) {
if (curStatus.value === CONNECT_SUCCESS) {
let cleanText = applyBackspace(filterAnsiSequences(terminalText.value))
const lines = cleanText.split('\n')
// console.log('lines: ', lines)
@ -345,7 +348,7 @@ const onData = () => {
}
}
})
if (isConnectFail.value || isConnecting.value) return console.warn(`isConnectFail: ${ isConnectFail.value }, isConnecting: ${ isConnecting.value }`)
if (curStatus.value !== CONNECT_SUCCESS) return
emit('inputCommand', key)
socket.value.emit('input', key)
})
@ -393,11 +396,11 @@ onMounted(async () => {
createLocalTerminal()
await getCommand()
connectIO()
await nextTick()
onData()
})
onBeforeUnmount(() => {
isManual.value = true
socket.value?.close()
window.removeEventListener('resize', handleResize)
})

View File

@ -119,10 +119,16 @@
:closable="true"
class="el_tab_pane"
>
<template #label>
<div class="tab_label">
<span class="tab_status" :style="{ background: getStatusColor(item.status) }" />
<span>{{ item.name }}</span>
</div>
</template>
<div class="tab_content_wrap" :style="{ height: mainHeight + 'px' }">
<TerminalTab
ref="terminalRefs"
:host="item.host"
:host-obj="item"
:theme="themeObj"
:background="terminalBackground"
:font-size="terminalFontSize"
@ -165,8 +171,8 @@ import Sftp from './sftp.vue'
import InputCommand from '@/components/input-command/index.vue'
import HostForm from '../../server/components/host-form.vue'
import TerminalSetting from './terminal-setting.vue'
// import { randomStr } from '@utils/index.js'
import themeList from 'xterm-theme'
import { terminalStatusList } from '@/utils/enum'
const { proxy: { $nextTick, $store, $message } } = getCurrentInstance()
@ -226,6 +232,10 @@ onBeforeUnmount(() => {
window.removeEventListener('resize', handleResizeTerminalSftp)
})
const getStatusColor = (status) => {
return terminalStatusList.find(item => item.value === status)?.color || 'gray'
}
const handleUpdateList = async ({ host }) => {
try {
await $store.getHostList()
@ -461,6 +471,19 @@ const handleInputCommand = async (command) => {
display: flex;
flex-direction: column;
position: relative;
.tab_label {
display: flex;
align-items: center;
justify-content: center;
.tab_status {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 5px;
// background-color: var(--el-color-primary);
}
}
.tab_content_wrap {
display: flex;
flex-direction: column;

View File

@ -57,6 +57,8 @@ import { useRoute } from 'vue-router'
import Terminal from './components/terminal.vue'
import HostForm from '../server/components/host-form.vue'
import { randomStr } from '@utils/index.js'
import { terminalStatus } from '@/utils/enum'
const { CONNECTING } = terminalStatus
const { proxy: { $store, $message } } = getCurrentInstance()
@ -74,7 +76,7 @@ let isAllConfssh = computed(() => {
function linkTerminal(row) {
const { name, host } = row
terminalTabs.push({ key: randomStr(16), name, host })
terminalTabs.push({ key: randomStr(16), name, host, status: CONNECTING })
}
function handleUpdateHost(row) {
@ -103,7 +105,7 @@ onActivated(async () => {
if (!host) return
let targetHosts = hostList.value.filter(item => host.includes(item.host)).map(item => {
const { name, host } = item
return { key: randomStr(16), name, host }
return { key: randomStr(16), name, host, status: CONNECTING }
})
if (!targetHosts || !targetHosts.length) return
terminalTabs.push(...targetHosts)