2024-07-31 18:05:39 +08:00

481 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="info_container" :style="{ width: visible ? `250px` : 0 }">
<!-- <el-divider class="first-divider" content-position="center">地理位置</el-divider> -->
<el-descriptions
class="margin-top"
:column="1"
size="small"
border
>
<el-descriptions-item>
<template #label>
<div class="item-title">
IP
</div>
</template>
<span style="margin-right: 10px;">{{ host }}</span>
<el-tag size="small" style="cursor: pointer;" @click="handleCopy">复制</el-tag>
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="item-title">
位置
</div>
</template>
<!-- <div size="small">{{ ipInfo.country || '--' }} {{ ipInfo.regionName }} {{ ipInfo.city }}</div> -->
<div size="small">{{ ipInfo.country || '--' }} {{ ipInfo.regionName }}</div>
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="item-title">
延迟
</div>
</template>
<span style="margin-right: 10px;" class="host-ping">{{ ping }}</span>
<!-- <span>(http)</span> -->
</el-descriptions-item>
</el-descriptions>
<!-- <el-divider content-position="center">实时监控</el-divider> -->
<br>
<el-descriptions
class="margin-top"
:column="1"
size="small"
border
>
<el-descriptions-item>
<template #label>
<div class="item-title">
CPU
</div>
</template>
<el-progress
:text-inside="true"
:stroke-width="18"
:percentage="cpuUsage"
:color="handleColor(cpuUsage)"
/>
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="item-title">
内存
</div>
</template>
<el-progress
:text-inside="true"
:stroke-width="18"
:percentage="usedMemPercentage"
:color="handleColor(usedMemPercentage)"
/>
<div class="position-right">
{{ $tools.toFixed(memInfo.usedMemMb / 1024) }}/{{ $tools.toFixed(memInfo.totalMemMb / 1024) }}G
</div>
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="item-title">
硬盘
</div>
</template>
<el-progress
:text-inside="true"
:stroke-width="18"
:percentage="usedPercentage"
:color="handleColor(usedPercentage)"
/>
<div class="position-right">
{{ driveInfo.usedGb || '--' }}/{{ driveInfo.totalGb || '--' }}G
</div>
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="item-title">
网络
</div>
</template>
<div class="netstat-info">
<div class="wrap">
<img src="@/assets/upload.png" alt="">
<span class="upload">{{ output || 0 }}</span>
</div>
<div class="wrap">
<img src="@/assets/download.png" alt="">
<span class="download">{{ input || 0 }}</span>
</div>
</div>
</el-descriptions-item>
</el-descriptions>
<!-- <el-divider content-position="center">系统信息</el-divider> -->
<br>
<el-descriptions
class="margin-top"
:column="1"
size="small"
border
>
<el-descriptions-item>
<template #label>
<div class="item-title">
名称
</div>
</template>
<div size="small">
{{ osInfo.hostname }}
</div>
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="item-title">
核心
</div>
</template>
<div size="small">
{{ cpuInfo.cpuCount }}
</div>
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="item-title">
型号
</div>
</template>
<div size="small">
{{ cpuInfo.cpuModel }}
</div>
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="item-title">
类型
</div>
</template>
<div size="small">
{{ osInfo.type }} {{ osInfo.release }} {{ osInfo.arch }}
</div>
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="item-title">
在线
</div>
</template>
<div size="small">
{{ $tools.formatTime(osInfo.uptime) }}
</div>
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="item-title">
本地
</div>
</template>
<div size="small">
{{ osInfo.ip }}
</div>
</el-descriptions-item>
</el-descriptions>
<el-divider content-position="center">FEATURE</el-divider>
<!-- <el-button
:type="sftpStatus ? 'primary' : 'success'"
style="display: block;width: 80%;margin: 30px auto;"
@click="handleSftp"
>
{{ sftpStatus ? '关闭SFTP' : '连接SFTP' }}
</el-button> -->
<el-button
:type="inputCommandStyle ? 'primary' : 'success'"
style="display: block;width: 80%;margin: 15px auto;"
@click="clickInputCommand"
>
命令输入框
</el-button>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount, computed, getCurrentInstance } from 'vue'
import socketIo from 'socket.io-client'
const { proxy: { $store, $serviceURI, $message, $notification, $tools } } = getCurrentInstance()
const props = defineProps({
hostInfo: {
required: true,
type: Object
},
visible: {
required: true,
type: Boolean
},
showInputCommand: {
required: true,
type: Boolean
}
})
const emit = defineEmits(['update:inputCommandStyle', 'connect-sftp', 'click-input-command',])
const socket = ref(null)
const name = ref('')
const ping = ref(0)
const pingTimer = ref(null)
const sftpStatus = ref(false)
const token = computed(() => $store.token)
const hostData = computed(() => props.hostInfo)
const host = computed(() => hostData.value.host)
const ipInfo = computed(() => hostData.value?.ipInfo || {})
// const isError = computed(() => !Boolean(hostData.value?.osInfo))
const cpuInfo = computed(() => hostData.value?.cpuInfo || {})
const memInfo = computed(() => hostData.value?.memInfo || {})
const osInfo = computed(() => hostData.value?.osInfo || {})
const driveInfo = computed(() => hostData.value?.driveInfo || {})
const netstatInfo = computed(() => {
let { total: netTotal, ...netCards } = hostData.value?.netstatInfo || {}
return { netTotal, netCards: netCards || {} }
})
// const openedCount = computed(() => hostData.value?.openedCount || 0)
const cpuUsage = computed(() => Number(cpuInfo.value?.cpuUsage) || 0)
const usedMemPercentage = computed(() => Number(memInfo.value?.usedMemPercentage) || 0)
const usedPercentage = computed(() => Number(driveInfo.value?.usedPercentage) || 0)
const output = computed(() => {
let outputMb = Number(netstatInfo.value.netTotal?.outputMb) || 0
if (outputMb >= 1) return `${ outputMb.toFixed(2) } MB/s`
return `${ (outputMb * 1024).toFixed(1) } KB/s`
})
const input = computed(() => {
let inputMb = Number(netstatInfo.value.netTotal?.inputMb) || 0
if (inputMb >= 1) return `${ inputMb.toFixed(2) } MB/s`
return `${ (inputMb * 1024).toFixed(1) } KB/s`
})
const inputCommandStyle = computed({
get: () => props.showInputCommand,
set: (val) => {
emit('update:inputCommandStyle', val)
}
})
// const handleSftp = () => {
// sftpStatus.value = !sftpStatus.value
// emit('connect-sftp', sftpStatus.value)
// }
const clickInputCommand = () => {
inputCommandStyle.value = true
emit('click-input-command')
}
const connectIO = () => {
socket.value = socketIo($serviceURI, {
path: '/host-status',
forceNew: true,
timeout: 5000,
reconnectionDelay: 3000,
reconnectionAttempts: 3
})
socket.value.on('connect', () => {
console.log('/host-status socket已连接', socket.value.id)
socket.value.emit('init_host_data', { token: token.value, host: props.host })
// getHostPing()
socket.value.on('host_data', (data) => {
if (!data) return hostData.value = null
hostData.value = data
})
})
socket.value.on('connect_error', (err) => {
console.error('host status websocket 连接错误:', err)
$notification({
title: '连接客户端失败(重连中...)',
message: '请检查客户端服务是否正常',
type: 'error'
})
})
socket.value.on('disconnect', () => {
hostData.value = null
$notification({
title: '客户端连接主动断开(重连中...)',
message: '请检查客户端服务是否正常',
type: 'error'
})
})
}
const handleCopy = async () => {
await navigator.clipboard.writeText(host.value)
$message.success({ message: 'success', center: true })
}
const handleColor = (num) => {
if (num < 65) return '#8AE234'
if (num < 85) return '#FFD700'
if (num < 90) return '#FFFF33'
if (num <= 100) return '#FF3333'
}
const getHostPing = () => {
pingTimer.value = setInterval(() => {
$tools.ping(`http://${ props.host }:22022`)
.then(res => {
ping.value = res
if (!import.meta.env.DEV) {
console.warn('Please tick \'Preserve Log\'')
}
})
}, 3000)
}
onMounted(() => {
// name.value = $router.currentRoute.value.query.name || ''
// if (!props.host || !name.value) return $message.error('参数错误')
// connectIO()
})
onBeforeUnmount(() => {
socket.value && socket.value.close()
pingTimer.value && clearInterval(pingTimer.value)
})
</script>
<style lang="scss" scoped>
.info_container {
// border-top: 1px solid var(--el-border-color);
flex-shrink: 0;
overflow: scroll;
background-color: #fff; //#E0E2EF;
transition: all 0.15s;
// header {
// display: flex;
// justify-content: space-between;
// align-items: center;
// height: 30px;
// margin: 10px;
// position: relative;
// img {
// cursor: pointer;
// height: 80%;
// }
// }
// 表格中系统标识的title
.item-title {
user-select: none;
white-space: nowrap;
text-align: center;
min-width: 30px;
max-width: 30px;
}
.host-ping {
display: inline-block;
font-size: 13px;
color: #009933;
background-color: #e8fff3;
padding: 0 5px;
}
// 分割线title
:deep(.el-divider__text) {
color: #a0cfff;
padding: 0 8px;
user-select: none;
}
// 分割线间距
:deep(.el-divider--horizontal) {
margin: 28px 0 10px;
}
.first-divider {
margin: 15px 0 10px;
}
// 表格
:deep(.el-descriptions__table) {
tr {
display: flex;
.el-descriptions__label {
min-width: 35px;
flex-shrink: 0;
}
.el-descriptions__content {
position: relative;
flex: 1;
display: flex;
align-items: center;
.el-progress {
width: 100%;
}
// 进度条右边参数定位
.position-right {
position: absolute;
right: 15px;
}
}
}
}
// 进度条
:deep(.el-progress-bar__inner) {
display: flex;
align-items: center;
.el-progress-bar__innerText {
display: flex;
span {
color: #000;
}
}
}
// 网络
.netstat-info {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.wrap {
flex: 1;
display: flex;
align-items: center;
// justify-content: center;
padding: 0 5px;
img {
width: 15px;
margin-right: 5px;
}
.upload {
color: #CF8A20;
}
.download {
color: #67c23a;
}
}
}
}
</style>
<style scoped>
.el-descriptions__label {
vertical-align: middle;
max-width: 35px;
}
</style>