新增前端vue
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<Card title="基础信息">
|
||||
<div class="card-grid base-info-content">
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">主机名称:</div>
|
||||
<div class="metric-value ellipsis-text" :title="serverInfo?.sysHostname || '--'">
|
||||
{{ serverInfo?.sysHostname || '--' }}
|
||||
</div>
|
||||
<div class="metric-icon">🖥️</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">IP 地址:</div>
|
||||
<div class="metric-value ellipsis-text" :title="serverInfo?.ipAddress || '--'">
|
||||
{{ serverInfo?.ipAddress || '--' }}
|
||||
</div>
|
||||
<div class="metric-icon">🌐</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">CPU 型号:</div>
|
||||
<div class="metric-value ellipsis-text" :title="serverInfo?.cpuModel || '--'">
|
||||
{{ serverInfo?.cpuModel || '--' }}
|
||||
</div>
|
||||
<div class="metric-icon">⚙️</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">内存大小:</div>
|
||||
<div class="metric-value ellipsis-text" :title="serverInfo?.memoryTotal || '--'">
|
||||
{{ serverInfo?.memoryTotal || '--' }}
|
||||
</div>
|
||||
<div class="metric-icon">🧠</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">系统版本:</div>
|
||||
<div class="metric-value ellipsis-text" :title="serverInfo?.kernelVersion || '--'">
|
||||
{{ serverInfo?.kernelVersion || '--' }}
|
||||
</div>
|
||||
<div class="metric-icon">🖨️</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="LeftOut">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { Card } from 'ant-design-vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const props = defineProps<{
|
||||
formParams: Record<string, any>; // 接收参数
|
||||
}>();
|
||||
|
||||
const serverInfo = ref<any>({});
|
||||
|
||||
</script>
|
||||
|
||||
<!-- 模板和脚本部分保持不变,仅替换style部分 -->
|
||||
<style scoped>
|
||||
/* 基础信息卡片容器:核心滚动逻辑 */
|
||||
.card-grid.base-info-content {
|
||||
width: 100%;
|
||||
height: 240px;
|
||||
display: grid;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 单个指标卡片样式(美化,提升体验) */
|
||||
.metric-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e9ecef;
|
||||
transition: all 0.2s ease;
|
||||
min-height: 40px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 卡片hover效果 */
|
||||
.metric-card:hover {
|
||||
background-color: #f5f7fa;
|
||||
border-color: #dee2e6;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
/* 标签样式 */
|
||||
.metric-label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
font-weight: 500;
|
||||
min-width: 80px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
/* 值的样式(省略逻辑) */
|
||||
.metric-value {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* 图标样式 */
|
||||
.metric-icon {
|
||||
font-size: 20px;
|
||||
margin-left: 12px;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
/* 文字省略通用样式 */
|
||||
.ellipsis-text {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* 滚动条美化(可选,提升视觉体验) */
|
||||
.card-grid.base-info-content::-webkit-scrollbar {
|
||||
width: 6px; /* 滚动条宽度 */
|
||||
}
|
||||
|
||||
.card-grid.base-info-content::-webkit-scrollbar-track {
|
||||
background: #f1f1f1; /* 滚动条轨道颜色 */
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.card-grid.base-info-content::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1; /* 滚动条滑块颜色 */
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.card-grid.base-info-content::-webkit-scrollbar-thumb:hover {
|
||||
background: #a8a8a8; /* 滑块hover颜色 */
|
||||
}
|
||||
|
||||
/* 兼容Firefox浏览器滚动条 */
|
||||
.card-grid.base-info-content {
|
||||
scrollbar-width: thin; /* 细滚动条 */
|
||||
scrollbar-color: #c1c1c1 #f1f1f1; /* 滑块颜色 轨道颜色 */
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,233 @@
|
||||
<template>
|
||||
<Card title="监控信息">
|
||||
<div ref="chartDom" class="monitor-content" style="width: 100%; height: 200px;"></div>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="Monitor">
|
||||
import { ref, onMounted, watch, onUnmounted } from 'vue';
|
||||
import { Card } from 'ant-design-vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { BizResourceMonitor, bizResourceMonitorListAll } from '@jeesite/biz/api/biz/resourceMonitor';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
// 定义接收的参数类型
|
||||
const props = defineProps<{
|
||||
formParams: Record<string, any>; // 接收参数
|
||||
}>();
|
||||
|
||||
// 监控数据列表
|
||||
const monitorList = ref<BizResourceMonitor[]>([]);
|
||||
// 改用普通ref管理echarts实例(移除shallowRef)
|
||||
const chartInstance = ref<echarts.ECharts | null>(null);
|
||||
// 图表DOM引用
|
||||
const chartDom = ref<HTMLDivElement | null>(null);
|
||||
|
||||
// 存储resize回调函数,方便卸载时移除
|
||||
const resizeHandler = () => {
|
||||
chartInstance.value?.resize();
|
||||
};
|
||||
|
||||
// 获取监控数据
|
||||
const fetchList = async (params: Record<string, any>) => {
|
||||
try {
|
||||
const result = await bizResourceMonitorListAll(params);
|
||||
monitorList.value = result || [];
|
||||
// 数据更新后重新渲染图表
|
||||
renderChart();
|
||||
} catch (error) {
|
||||
console.error('获取数据列表失败:', error);
|
||||
monitorList.value = [];
|
||||
// 数据获取失败也清空图表
|
||||
renderChart();
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化ECharts实例
|
||||
const initChart = () => {
|
||||
if (!chartDom.value) return;
|
||||
|
||||
// 避免重复创建实例
|
||||
if (chartInstance.value) {
|
||||
chartInstance.value.dispose();
|
||||
chartInstance.value = null;
|
||||
}
|
||||
|
||||
// 创建ECharts实例
|
||||
chartInstance.value = echarts.init(chartDom.value);
|
||||
|
||||
// 监听窗口大小变化,自适应调整图表
|
||||
window.addEventListener('resize', resizeHandler);
|
||||
};
|
||||
|
||||
// 渲染图表
|
||||
const renderChart = () => {
|
||||
if (!chartInstance.value || monitorList.value.length === 0) {
|
||||
// 清空图表
|
||||
chartInstance.value?.setOption({
|
||||
series: [{ data: [] }, { data: [] }],
|
||||
xAxis: { data: [] }
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理数据:提取X轴和Y轴数据
|
||||
const xAxisData = monitorList.value.map(item => item.hourTime || '');
|
||||
const cpuData = monitorList.value.map(item => {
|
||||
// 处理空值/异常值,确保数据合法性(0-100)
|
||||
const value = item.cpuUsage ?? 0;
|
||||
return Math.max(0, Math.min(100, value));
|
||||
});
|
||||
const memoryData = monitorList.value.map(item => {
|
||||
// 处理空值/异常值,确保数据合法性(0-100)
|
||||
const value = item.memoryUsage ?? 0;
|
||||
return Math.max(0, Math.min(100, value));
|
||||
});
|
||||
|
||||
// 计算Y轴自适应的最大值和最小值
|
||||
// 合并所有数据,找到极值
|
||||
const allData = [...cpuData, ...memoryData];
|
||||
const minValue = Math.min(...allData);
|
||||
const maxValue = Math.max(...allData);
|
||||
|
||||
// 给Y轴极值增加一点余量,让图表显示更美观(比如±5%)
|
||||
// 确保最小值不小于0,最大值不大于100
|
||||
const yMin = Math.max(0, minValue - 5);
|
||||
const yMax = Math.min(100, maxValue + 5);
|
||||
|
||||
// 自动计算刻度间隔(按5/10/20等整数分割,保证刻度美观)
|
||||
const getInterval = (min: number, max: number) => {
|
||||
const range = max - min;
|
||||
if (range <= 10) return 2;
|
||||
if (range <= 20) return 5;
|
||||
if (range <= 50) return 10;
|
||||
return 20;
|
||||
};
|
||||
const yInterval = getInterval(yMin, yMax);
|
||||
|
||||
// 配置ECharts选项
|
||||
const option = {
|
||||
// 图表标题
|
||||
title: {
|
||||
left: 'center'
|
||||
},
|
||||
// 提示框组件
|
||||
tooltip: {
|
||||
trigger: 'axis', // 坐标轴触发
|
||||
formatter: '{b} <br/>{a}: {c}%', // 自定义提示框格式
|
||||
textStyle: {
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
// 图例组件
|
||||
legend: {
|
||||
data: ['CPU使用率', '内存使用率'],
|
||||
top: 30
|
||||
},
|
||||
// 网格配置
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true // 包含坐标轴标签
|
||||
},
|
||||
// X轴配置
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: xAxisData,
|
||||
axisLabel: {
|
||||
rotate: 30, // 标签旋转30度,避免重叠
|
||||
fontSize: 11
|
||||
}
|
||||
},
|
||||
// Y轴配置(核心修改:自适应极值和刻度)
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '使用率 (%)',
|
||||
min: yMin, // 自适应最小值
|
||||
max: yMax, // 自适应最大值
|
||||
interval: yInterval, // 自适应刻度间隔
|
||||
axisLabel: {
|
||||
formatter: '{value} %'
|
||||
}
|
||||
},
|
||||
// 系列数据(两条曲线)
|
||||
series: [
|
||||
{
|
||||
name: 'CPU使用率',
|
||||
type: 'line', // 折线图
|
||||
data: cpuData,
|
||||
smooth: true, // 平滑曲线
|
||||
lineStyle: {
|
||||
width: 2
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#f56c6c' // CPU曲线颜色
|
||||
},
|
||||
areaStyle: {
|
||||
// 面积填充,增加视觉效果
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(245, 108, 108, 0.3)' },
|
||||
{ offset: 1, color: 'rgba(245, 108, 108, 0.05)' }
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '内存使用率',
|
||||
type: 'line', // 折线图
|
||||
data: memoryData,
|
||||
smooth: true, // 平滑曲线
|
||||
lineStyle: {
|
||||
width: 2
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#409eff' // 内存曲线颜色
|
||||
},
|
||||
areaStyle: {
|
||||
// 面积填充
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(64, 158, 255, 0.3)' },
|
||||
{ offset: 1, color: 'rgba(64, 158, 255, 0.05)' }
|
||||
])
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// 设置图表配置项
|
||||
chartInstance.value.setOption(option);
|
||||
};
|
||||
|
||||
// 生命周期:挂载时初始化图表并获取数据
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
fetchList(props.formParams);
|
||||
});
|
||||
|
||||
// 监听参数变化,重新获取数据
|
||||
watch(
|
||||
() => props.formParams,
|
||||
async (newParams) => {
|
||||
await fetchList(newParams);
|
||||
},
|
||||
{ deep: true, immediate: false }
|
||||
);
|
||||
|
||||
// 组件卸载时清理资源(新增/完善onUnmounted)
|
||||
onUnmounted(() => {
|
||||
// 1. 移除窗口resize事件监听
|
||||
window.removeEventListener('resize', resizeHandler);
|
||||
// 2. 销毁ECharts实例,释放内存
|
||||
if (chartInstance.value) {
|
||||
chartInstance.value.dispose();
|
||||
chartInstance.value = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.monitor-content {
|
||||
width: 100%;
|
||||
height: 200px; /* 固定图表高度,确保渲染正常 */
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,278 @@
|
||||
<template>
|
||||
<Card title="磁盘信息">
|
||||
<div class="disk-table-container disk-info-content">
|
||||
<div class="custom-table-wrapper">
|
||||
<!-- 固定表头 -->
|
||||
<div class="table-header">
|
||||
<div class="table-th" style="width: 200px">
|
||||
<div class="ellipsis-text">挂载路径</div>
|
||||
</div>
|
||||
<div class="table-th" style="width: 180px">
|
||||
<div class="ellipsis-text">设备名称</div>
|
||||
</div>
|
||||
<div class="table-th" style="width: 100px">
|
||||
<div class="ellipsis-text">总容量</div>
|
||||
</div>
|
||||
<div class="table-th" style="width: 100px">
|
||||
<div class="ellipsis-text">已用容量</div>
|
||||
</div>
|
||||
<div class="table-th flex-1">
|
||||
<div class="ellipsis-text">磁盘使用率</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 可滚动的内容区域 -->
|
||||
<div class="table-body-wrapper">
|
||||
<div class="table-body">
|
||||
<div v-if="diskList.length === 0" class="empty-tip">
|
||||
<div class="empty-icon">📁</div>
|
||||
<div class="empty-text">暂无磁盘信息</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(disk, index) in diskList"
|
||||
:key="index"
|
||||
class="table-tr"
|
||||
:class="{ 'table-tr-stripe': index % 2 === 1 }"
|
||||
@mouseenter="hoveredRow = index"
|
||||
@mouseleave="hoveredRow = -1"
|
||||
>
|
||||
<div class="table-td" style="width: 200px">
|
||||
<span class="path-icon">📂</span>
|
||||
<div class="ellipsis-text" :title="disk.mountPoint || '--'">
|
||||
{{ disk.mountPoint || '--' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-td" style="width: 180px">
|
||||
<div class="ellipsis-text" :title="disk.device || '--'">
|
||||
{{ disk.device || '--' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-td" style="width: 100px">
|
||||
<div class="ellipsis-text" :title="`${disk.totalSize || '--'}`">
|
||||
{{ disk.totalSize || '--' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-td" style="width: 100px">
|
||||
<div class="ellipsis-text" :title="`${disk.usedSize || '--'}`">
|
||||
{{ disk.usedSize || '--' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-td flex-1">
|
||||
<div class="custom-progress-container">
|
||||
<div
|
||||
class="custom-progress-bar"
|
||||
:style="{
|
||||
width: `${disk.usageRate || 0}%`,
|
||||
backgroundColor: getProgressColor(disk.usageRate)
|
||||
}"
|
||||
:class="{ 'progress-hover': hoveredRow === index }"
|
||||
></div>
|
||||
<span class="custom-progress-text">{{ disk.usageRate || 0 }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="RigthOut">
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import { Card } from 'ant-design-vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { BizDeviceInfo, bizDeviceInfoListAll } from '@jeesite/biz/api/biz/deviceInfo';
|
||||
|
||||
const props = defineProps<{
|
||||
formParams: Record<string, any>; // 接收参数
|
||||
}>();
|
||||
|
||||
const diskList = ref<BizDeviceInfo[]>([]);
|
||||
const hoveredRow = ref(-1); // 初始化 hoveredRow
|
||||
|
||||
const fetchList = async (params: Record<string, any>) => {
|
||||
try {
|
||||
const result = await bizDeviceInfoListAll(params);
|
||||
diskList.value = result || [];
|
||||
} catch (error) {
|
||||
console.error('获取数据列表失败:', error);
|
||||
diskList.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
// 根据使用率获取进度条颜色
|
||||
const getProgressColor = (rate?: number) => {
|
||||
const usageRate = typeof rate === 'number' ? rate : 0;
|
||||
if (usageRate >= 80) return '#f56c6c'; // 红色
|
||||
if (usageRate >= 60) return '#e6a23c'; // 黄色
|
||||
return '#409eff'; // 蓝色(默认)
|
||||
};
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
fetchList(props.formParams);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.formParams,
|
||||
async (newParams) => {
|
||||
await fetchList(newParams);
|
||||
},
|
||||
{ deep: true, immediate: false }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 核心修复:给外层容器设置明确高度约束 */
|
||||
.disk-table-container {
|
||||
width: 100%;
|
||||
/* 关键:设置固定高度,也可根据需求改为 max-height */
|
||||
height: 240px;
|
||||
/* 或者用百分比:height: 80vh;(相对于视口高度) */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.custom-table-wrapper {
|
||||
width: 100%;
|
||||
height: 100%; /* 继承外层容器高度 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 固定表头样式 */
|
||||
.table-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 48px;
|
||||
background-color: #f8f9fa;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
font-weight: 600;
|
||||
padding: 0 16px;
|
||||
box-sizing: border-box;
|
||||
/* 固定表头:sticky 结合父容器定位 */
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* 核心修复:内容区域强制高度 + 滚动 */
|
||||
.table-body-wrapper {
|
||||
/* 表头高度48px,剩余高度全部给内容区 */
|
||||
height: calc(100% - 48px);
|
||||
/* 强制显示滚动条(也可保留auto,仅溢出时显示) */
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 表格行/列基础样式 */
|
||||
.table-th, .table-td {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.table-tr {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 48px;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
padding: 0 16px;
|
||||
box-sizing: border-box;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
/* 斑马纹样式 */
|
||||
.table-tr-stripe {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
/* 行hover效果 */
|
||||
.table-tr:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
/* 空数据提示 */
|
||||
.empty-tip {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 200px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* 进度条样式 */
|
||||
.custom-progress-container {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
background-color: #e5e6eb;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.custom-progress-bar {
|
||||
height: 100%;
|
||||
border-radius: 8px;
|
||||
transition: width 0.3s, background-color 0.3s;
|
||||
}
|
||||
|
||||
.progress-hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.custom-progress-text {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 10px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 文字省略样式 */
|
||||
.ellipsis-text {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 挂载路径图标 */
|
||||
.path-icon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
/* 强制显示滚动条(兼容不同浏览器) */
|
||||
.table-body-wrapper {
|
||||
/* Firefox */
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #c1c1c1 #f1f1f1;
|
||||
}
|
||||
|
||||
/* Webkit 浏览器滚动条样式 */
|
||||
.table-body-wrapper::-webkit-scrollbar {
|
||||
width: 8px; /* 加宽滚动条,更容易看到 */
|
||||
}
|
||||
|
||||
.table-body-wrapper::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.table-body-wrapper::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.table-body-wrapper::-webkit-scrollbar-thumb:hover {
|
||||
background: #a8a8a8;
|
||||
}
|
||||
</style>
|
||||
@@ -6,664 +6,134 @@
|
||||
:showOkBtn="false"
|
||||
:showCancelBtn="false"
|
||||
defaultFullscreen="true"
|
||||
width="70%"
|
||||
width="100%"
|
||||
class="server-detail-modal"
|
||||
>
|
||||
<div class="server-detail-container">
|
||||
<!-- 基础信息+磁盘信息 -->
|
||||
<div class="server-info-top">
|
||||
<div class="server-info-left">
|
||||
<div class="info-card base-info-card">
|
||||
<h3 class="card-title">基础信息</h3>
|
||||
<div class="card-grid base-info-content">
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">主机名称:</div>
|
||||
<div class="metric-value ellipsis-text" :title="serverInfo?.sysHostname || '--'">
|
||||
{{ serverInfo?.sysHostname || '--' }}
|
||||
</div>
|
||||
<div class="metric-icon">🖥️</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">IP 地址:</div>
|
||||
<div class="metric-value ellipsis-text" :title="serverInfo?.ipAddress || '--'">
|
||||
{{ serverInfo?.ipAddress || '--' }}
|
||||
</div>
|
||||
<div class="metric-icon">🌐</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">CPU 型号:</div>
|
||||
<div class="metric-value ellipsis-text" :title="serverInfo?.cpuModel || '--'">
|
||||
{{ serverInfo?.cpuModel || '--' }}
|
||||
</div>
|
||||
<div class="metric-icon">⚙️</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">内存大小:</div>
|
||||
<div class="metric-value ellipsis-text" :title="serverInfo?.memoryTotal || '--'">
|
||||
{{ serverInfo?.memoryTotal || '--' }}
|
||||
</div>
|
||||
<div class="metric-icon">🧠</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">系统版本:</div>
|
||||
<div class="metric-value ellipsis-text" :title="serverInfo?.kernelVersion || '--'">
|
||||
{{ serverInfo?.kernelVersion || '--' }}
|
||||
</div>
|
||||
<div class="metric-icon">🖨️</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="server-info-left card-container">
|
||||
<LeftOut :formParams="FormValues" />
|
||||
</div>
|
||||
<div class="server-info-right">
|
||||
<div class="info-card disk-info-card">
|
||||
<h3 class="card-title">磁盘信息</h3>
|
||||
<div class="disk-table-container disk-info-content">
|
||||
<div class="custom-table-wrapper">
|
||||
<div class="table-header">
|
||||
<div class="table-th" style="width: 200px"><div class="ellipsis-text">挂载路径</div></div>
|
||||
<div class="table-th" style="width: 180px"><div class="ellipsis-text">设备名称</div></div>
|
||||
<div class="table-th" style="width: 100px"><div class="ellipsis-text">总容量</div></div>
|
||||
<div class="table-th" style="width: 100px"><div class="ellipsis-text">已用容量</div></div>
|
||||
<div class="table-th flex-1"><div class="ellipsis-text">磁盘使用率</div></div>
|
||||
</div>
|
||||
<div class="table-body-wrapper">
|
||||
<div class="table-body">
|
||||
<div v-if="diskList.length === 0" class="empty-tip">
|
||||
<div class="empty-icon">📁</div>
|
||||
<div class="empty-text">暂无磁盘信息</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(disk, index) in diskList"
|
||||
:key="index"
|
||||
class="table-tr"
|
||||
:class="{ 'table-tr-stripe': index % 2 === 1 }"
|
||||
@mouseenter="hoveredRow = index"
|
||||
@mouseleave="hoveredRow = -1"
|
||||
>
|
||||
<div class="table-td" style="width: 200px">
|
||||
<span class="path-icon">📂</span>
|
||||
<div class="ellipsis-text" :title="disk.mountPoint || '--'">
|
||||
{{ disk.mountPoint || '--' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-td" style="width: 180px">
|
||||
<div class="ellipsis-text" :title="disk.device || '--'">
|
||||
{{ disk.device || '--' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-td" style="width: 100px">
|
||||
<div class="ellipsis-text" :title="`${disk.totalSize || '--'}`">
|
||||
{{ disk.totalSize || '--' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-td" style="width: 100px">
|
||||
<div class="ellipsis-text" :title="`${disk.usedSize || '--'}`">
|
||||
{{ disk.usedSize || '--' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-td flex-1">
|
||||
<div class="custom-progress-container">
|
||||
<div
|
||||
class="custom-progress-bar"
|
||||
:style="{
|
||||
width: `${disk.usageRate || 0}%`,
|
||||
backgroundColor: getProgressColor(disk.usageRate)
|
||||
}"
|
||||
:class="{ 'progress-hover': hoveredRow === index }"
|
||||
></div>
|
||||
<span class="custom-progress-text">{{ disk.usageRate || 0 }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="server-info-right card-container">
|
||||
<RightOut :formParams="FormValues" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 监控信息 -->
|
||||
<div class="server-info-bottom">
|
||||
<div class="info-card monitor-card">
|
||||
<h3 class="card-title">监控信息</h3>
|
||||
<div ref="chartDom" class="monitor-content"></div>
|
||||
</div>
|
||||
<!-- 分隔线 -->
|
||||
<div class="divider"></div>
|
||||
<!-- 下半部分:监控面板 -->
|
||||
<div class="server-info-bottom card-container">
|
||||
<Monitor :formParams="FormValues" />
|
||||
</div>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, onMounted, onUnmounted, watch, nextTick } from 'vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '@jeesite/core/components/Modal';
|
||||
import { useMessage } from '@jeesite/core/hooks/web/useMessage';
|
||||
import { BizDeviceInfo, bizDeviceInfoListAll } from '@jeesite/biz/api/biz/deviceInfo';
|
||||
import { BizResourceMonitor, bizResourceMonitorListAll } from '@jeesite/biz/api/biz/resourceMonitor';
|
||||
|
||||
import * as echarts from 'echarts';
|
||||
import type { ECharts, EChartsOption, TooltipFormatterCallbackParams } from 'echarts';
|
||||
import LeftOut from './Echart/LeftOut.vue';
|
||||
import RightOut from './Echart/RightOut.vue';
|
||||
import Monitor from './Echart/Monitor.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: { BasicModal },
|
||||
components: {
|
||||
BasicModal,
|
||||
LeftOut,
|
||||
RightOut,
|
||||
Monitor
|
||||
},
|
||||
setup() {
|
||||
// 基础状态
|
||||
const serverInfo = ref<any>({});
|
||||
const diskList = ref<BizDeviceInfo[]>([]);
|
||||
const hoveredRow = ref(-1);
|
||||
const hostId = ref<string | number>('');
|
||||
const isLoadingMonitor = ref(false);
|
||||
|
||||
// 图表核心状态
|
||||
const chartDom = ref<HTMLDivElement | null>(null);
|
||||
const chartInstance = ref<ECharts | null>(null);
|
||||
const monitorData = ref<BizResourceMonitor[]>([]);
|
||||
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (!chartDom.value) return;
|
||||
// 销毁已有实例,避免重复创建
|
||||
if (chartInstance.value) {
|
||||
chartInstance.value.dispose();
|
||||
}
|
||||
// 创建新实例
|
||||
chartInstance.value = echarts.init(chartDom.value);
|
||||
// 设置默认配置
|
||||
const defaultOption: EChartsOption = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
triggerOn: 'mousemove|click', // 鼠标移动或点击时触发
|
||||
textStyle: { fontSize: 12 },
|
||||
backgroundColor: 'rgba(255,255,255,0.95)', // 提高背景透明度,更清晰
|
||||
borderColor: '#e6e6e6',
|
||||
borderWidth: 1,
|
||||
padding: 12,
|
||||
borderRadius: 6, // 圆角更美观
|
||||
},
|
||||
legend: {
|
||||
data: ['CPU使用率(%)', '内存使用率(%)'],
|
||||
textStyle: { fontSize: 12 },
|
||||
top: 0
|
||||
},
|
||||
grid: {
|
||||
left: '2%',
|
||||
right: '2%',
|
||||
bottom: '3%',
|
||||
top: '15%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: [],
|
||||
axisLabel: { fontSize: 11 },
|
||||
axisLine: { lineStyle: { color: '#e6e6e6' } }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 100,
|
||||
name: '使用率(%)',
|
||||
nameTextStyle: { fontSize: 12 },
|
||||
axisLabel: {
|
||||
fontSize: 11,
|
||||
formatter: '{value}%'
|
||||
},
|
||||
axisLine: { lineStyle: { color: '#e6e6e6' } },
|
||||
splitLine: { lineStyle: { color: '#f5f5f5' } }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'CPU使用率(%)',
|
||||
type: 'line',
|
||||
data: [],
|
||||
smooth: true,
|
||||
lineStyle: { width: 2 },
|
||||
itemStyle: { color: '#409eff' },
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(64, 158, 255, 0.3)' },
|
||||
{ offset: 1, color: 'rgba(64, 158, 255, 0.05)' }
|
||||
])
|
||||
},
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
},
|
||||
{
|
||||
name: '内存使用率(%)',
|
||||
type: 'line',
|
||||
data: [],
|
||||
smooth: true,
|
||||
lineStyle: { width: 2 },
|
||||
itemStyle: { color: '#e6a23c' },
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: 'rgba(230, 162, 60, 0.3)' },
|
||||
{ offset: 1, color: 'rgba(230, 162, 60, 0.05)' }
|
||||
])
|
||||
},
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
}
|
||||
]
|
||||
};
|
||||
chartInstance.value.setOption(defaultOption);
|
||||
|
||||
// 监听窗口大小变化,自适应图表
|
||||
window.addEventListener('resize', resizeChart);
|
||||
};
|
||||
|
||||
// 调整图表大小
|
||||
const resizeChart = () => {
|
||||
if (chartInstance.value) {
|
||||
chartInstance.value.resize();
|
||||
}
|
||||
};
|
||||
|
||||
// 更新图表数据
|
||||
const updateChart = () => {
|
||||
if (!chartInstance.value || monitorData.value.length === 0) return;
|
||||
|
||||
const xAxisData = monitorData.value.map(item => item.hourTime || '');
|
||||
const cpuData = monitorData.value.map(item => item.cpuUsage || 0);
|
||||
const memoryData = monitorData.value.map(item => item.memoryUsage || 0);
|
||||
|
||||
// 更新图表配置
|
||||
const option: EChartsOption = {
|
||||
xAxis: { data: xAxisData },
|
||||
series: [
|
||||
{ data: cpuData },
|
||||
{ data: memoryData }
|
||||
]
|
||||
};
|
||||
chartInstance.value.setOption(option);
|
||||
};
|
||||
|
||||
// 模态框初始化
|
||||
const FormValues = ref<Record<string, any>>({
|
||||
hostId: ''
|
||||
});
|
||||
|
||||
const [register, { closeModal }] = useModalInner(async (data: any) => {
|
||||
if (!data || !data.hostId) return;
|
||||
serverInfo.value = { ...data };
|
||||
diskList.value = [];
|
||||
monitorData.value = [];
|
||||
hostId.value = data.hostId;
|
||||
isLoadingMonitor.value = true;
|
||||
|
||||
try {
|
||||
diskList.value = await bizDeviceInfoListAll({ hostId: data.hostId });
|
||||
monitorData.value = await bizResourceMonitorListAll({ hostId: data.hostId });
|
||||
initChart();
|
||||
} catch (error) {
|
||||
console.error('主机数据加载失败:', error);
|
||||
monitorData.value = [];
|
||||
} finally {
|
||||
isLoadingMonitor.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
// 根据使用率获取进度条颜色
|
||||
const getProgressColor = (rate?: number) => {
|
||||
const usageRate = typeof rate === 'number' ? rate : 0;
|
||||
if (usageRate >= 80) return '#f56c6c'; // 红色
|
||||
if (usageRate >= 60) return '#e6a23c'; // 黄色
|
||||
return '#409eff'; // 蓝色(默认)
|
||||
};
|
||||
|
||||
// 监听监控数据变化,更新图表
|
||||
watch(monitorData, () => {
|
||||
if (!isLoadingMonitor.value) {
|
||||
updateChart();
|
||||
}
|
||||
FormValues.value.hostId = data.hostId;
|
||||
});
|
||||
|
||||
return {
|
||||
register,
|
||||
closeModal,
|
||||
serverInfo,
|
||||
diskList,
|
||||
hoveredRow,
|
||||
chartDom,
|
||||
monitorData,
|
||||
isLoadingMonitor,
|
||||
getProgressColor
|
||||
FormValues
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 整体容器样式 - 上下布局 */
|
||||
/* 模态框容器 - 强制100vh高度 */
|
||||
.server-detail-modal {
|
||||
height: 100vh !important;
|
||||
max-height: 100vh !important;
|
||||
}
|
||||
|
||||
/* 整体容器 - 100vh高度,上下布局 */
|
||||
.server-detail-container {
|
||||
display: flex;
|
||||
flex-direction: column; /* 核心:改为垂直方向布局 */
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
background-color: #f0f8ff;
|
||||
border-radius: 8px;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 12px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 12px;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden; /* 防止整体溢出 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 上半部分:左右分栏容器 */
|
||||
/* 上半部分 - 精确50%高度,左右分栏 */
|
||||
.server-info-top {
|
||||
display: flex; /* 保持左右布局 */
|
||||
gap: 20px;
|
||||
flex: 0 0 auto; /* 高度自适应内容,不拉伸 */
|
||||
min-height: 300px; /* 保证基础高度 */
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
height: calc(50% - 4px); /* 减去gap的一半,保证总高度精准50% */
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* 下半部分:Monitor组件容器 */
|
||||
/* 下半部分 - 精确50%高度 */
|
||||
.server-info-bottom {
|
||||
flex: 1; /* 占满剩余高度 */
|
||||
min-height: 200px; /* 保证最小高度 */
|
||||
height: calc(50% - 4px); /* 减去gap的一半,保证总高度精准50% */
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* 左侧基础信息容器 (30%) */
|
||||
/* 左侧容器 - 30%宽度,满高 */
|
||||
.server-info-left {
|
||||
width: 30%;
|
||||
flex-shrink: 0;
|
||||
height: 100%; /* 撑满上半部分高度 */
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* 右侧磁盘信息容器 (70%) */
|
||||
/* 右侧容器 - 70%宽度,满高 */
|
||||
.server-info-right {
|
||||
width: 70%;
|
||||
flex-grow: 1;
|
||||
height: 100%; /* 撑满上半部分高度 */
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* 卡片通用样式 */
|
||||
.info-card {
|
||||
/* 卡片容器样式 - 美化UI */
|
||||
.card-container {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
||||
padding: 16px;
|
||||
height: 100%; /* 撑满父容器高度 */
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column; /* 卡片内部垂直布局,标题+内容 */
|
||||
}
|
||||
|
||||
.card-title {
|
||||
margin: 0 0 16px 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
padding-bottom: 8px;
|
||||
flex: 0 0 auto; /* 标题不拉伸 */
|
||||
}
|
||||
|
||||
/* 基础信息网格布局 */
|
||||
.card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 12px;
|
||||
flex: 1; /* 撑满卡片剩余高度 */
|
||||
overflow-y: auto; /* 内容过多时滚动 */
|
||||
}
|
||||
|
||||
/* 核心修复:基础信息卡片布局约束 */
|
||||
.metric-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s ease;
|
||||
/* 强制不换行 */
|
||||
white-space: nowrap;
|
||||
/* 防止卡片本身溢出 */
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
transition: box-shadow 0.2s ease;
|
||||
overflow: hidden; /* 关键修改:禁用滚动,超出内容直接隐藏 */
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
flex: 0 0 80px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
/* 标签固定宽度,防止挤压 */
|
||||
flex-shrink: 0;
|
||||
.card-container:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 核心修复:值区域强制省略 */
|
||||
.metric-value {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
color: #303133;
|
||||
font-weight: 500;
|
||||
/* 关键:强制文本省略的完整属性 */
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
/* 限制最大宽度,避免挤压图标 */
|
||||
max-width: calc(100% - 104px);
|
||||
}
|
||||
|
||||
.metric-icon {
|
||||
flex: 0 0 24px;
|
||||
font-size: 18px;
|
||||
text-align: right;
|
||||
/* 图标固定宽度,不参与弹性布局 */
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 表格容器样式 */
|
||||
.custom-table-wrapper {
|
||||
/* 分隔线样式 */
|
||||
.divider {
|
||||
height: 1px;
|
||||
background-color: #e5e6eb;
|
||||
width: 100%;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e6e6e6;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
display: flex;
|
||||
background: #f8f9fa;
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
font-weight: 600;
|
||||
flex: 0 0 auto; /* 表头不拉伸 */
|
||||
}
|
||||
|
||||
.table-th {
|
||||
padding: 12px 8px;
|
||||
font-size: 14px;
|
||||
color: #303133;
|
||||
text-align: left;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.table-body-wrapper {
|
||||
flex: 1; /* 表格内容占满剩余高度 */
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.table-body {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-tr {
|
||||
display: flex;
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.table-tr:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.table-tr-stripe {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.table-tr:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.table-td {
|
||||
padding: 12px 8px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
/* 省略文本样式 */
|
||||
.ellipsis-text {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.flex-1 {
|
||||
flex: 1;
|
||||
min-width: 0; /* 解决flex子元素省略失效问题 */
|
||||
}
|
||||
|
||||
/* 空数据提示 */
|
||||
.empty-tip {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 0;
|
||||
color: #909399;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 自定义进度条 - 调整为更细的样式 */
|
||||
.custom-progress-container {
|
||||
width: 100%;
|
||||
height: 10px;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.custom-progress-bar {
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
transition: width 0.3s ease, background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.progress-hover {
|
||||
filter: brightness(1.1);
|
||||
}
|
||||
|
||||
.custom-progress-text {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 11px;
|
||||
color: #303133;
|
||||
line-height: 1;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 图标样式 */
|
||||
.disk-icon, .path-icon {
|
||||
font-size: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Monitor组件容器样式 */
|
||||
.monitor-card {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monitor-content {
|
||||
flex: 1; /* 撑满卡片剩余高度 */
|
||||
height: 100%;
|
||||
overflow: hidden; /* 防止Monitor组件溢出 */
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
/* 图表加载状态 */
|
||||
.chart-loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.loading-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
animation: rotate 1.5s linear infinite;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 图表空数据状态 */
|
||||
.chart-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 响应式适配 */
|
||||
@media (max-width: 1024px) {
|
||||
.server-info-top {
|
||||
flex-direction: column; /* 小屏上半部分改为垂直布局 */
|
||||
min-height: auto;
|
||||
}
|
||||
.server-info-left, .server-info-right {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.server-info-left {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* 小屏下列宽进一步优化 */
|
||||
.table-th, .table-td {
|
||||
width: auto !important;
|
||||
flex: 1;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.table-th:nth-child(1), .table-td:nth-child(1) {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
.table-th:nth-child(5), .table-td:nth-child(5) {
|
||||
flex: 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
/* 适配移动端 */
|
||||
@media (max-width: 768px) {
|
||||
:deep(.server-detail-modal) {
|
||||
width: 95% !important;
|
||||
}
|
||||
|
||||
.custom-progress-text {
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
/* 移除原有的滚动条美化样式(已无必要) */
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user