feat(frontend): 精简状态监控默认概览
默认视图移除 CPU 使用率卡和网络速度卡,保留内存、 磁盘与进程管理作为常驻概览,以减少右侧窄栏中的重复 信息。 同时压缩 CPU/网络历史图的高度,避免在窄侧栏下撑开 布局。
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- **[frontend]**: 删除状态监控默认视图中的 CPU 使用率卡与网络速度卡,保留内存 / 磁盘 / 进程管理作为常驻概览,减少右侧窄栏中的重复信息 — by yinjianm
|
||||
- 类型: 快速修改(无方案包)
|
||||
- 文件: packages/frontend/src/components/StatusMonitor.vue:60-168,276-280,282-325
|
||||
|
||||
- **[frontend]**: 将状态监控中的 CPU 卡片升级为总 CPU `canvas` 历史图 + 每核心实时条卡,并在极窄侧栏下自动切换为单列布局 — by yinjianm
|
||||
- 方案: [202604190351_status-monitor-cpu-total-and-per-core](archive/2026-04/202604190351_status-monitor-cpu-total-and-per-core/)
|
||||
|
||||
@@ -97,6 +101,9 @@
|
||||
- 方案: [202603250614_terminal-ansi-color-effects](archive/2026-03/202603250614_terminal-ansi-color-effects/)
|
||||
|
||||
### 快速修改
|
||||
- **[frontend]**: 将 CPU 历史图卡片从随父容器拉伸改为固定紧凑高度约 `188px`,并同步压缩标题区与 canvas 高度,避免在窄侧栏下被撑到约 `278px` — by yinjianm
|
||||
- 类型: 快速修改(无方案包)
|
||||
- 文件: packages/frontend/src/components/StatusMonitorCpuHistoryChart.vue:185-245
|
||||
- **[frontend]**: 将状态监控“进程管理”的“运行中 / 休眠中”也收纳进标题区胶囊组,和“总数”一起以内联小显示呈现,不再保留独立摘要行 - by yinjianm
|
||||
- 类型: 快速修改(无方案包)
|
||||
- 文件: packages/frontend/src/components/StatusMonitor.vue:217-230,571-575,875-880
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
|
||||
### 状态监控卡片
|
||||
**条件**: 用户在 `/workspace` 右侧状态监控面板查看服务器资源状态。
|
||||
**行为**: `StatusMonitor.vue` 当前已从通用卡片栅格重排为更接近参考图的窄屏监控结构:顶部改为成对的信息条,资源概览改为带编号的紧凑使用率行,内存/网络/磁盘模块都采用明显的左右分区关系;其中内存卡片现在会在容器宽度大于等于 250px 时维持环形概览与统计块的高密度横向布局,仅在低于 250px 时切为手机式竖排;网络卡片则通过新增 `StatusMonitorNetworkHistoryChart.vue` 把原本的 SVG 趋势线替换为基于 Chart.js `canvas` 的最近 24 个采样点历史图,并进一步固定为“上方历史图 + 下方统计表”的纵向堆叠结构,不再切回左右双列,同时通过压缩图表 canvas 高度、网络表间距与统计项 padding,把整个网络模块约束在 `350px` 以内;CPU 卡片也已升级为 `StatusMonitorCpuHistoryChart.vue` 驱动的总 CPU `canvas` 历史图,并在图表旁/下方用紧凑网格展示每个核心的独立实时条卡,在容器宽度低于 250px 时自动退化为单列;磁盘模块继续展示设备视觉块与紧凑磁盘摘要;默认视图底部继续保留“进程管理”概览与高占用进程预览,并通过“查看全部”打开 `ProcessManagerModal.vue`。其中进程统计当前已整体收纳到模块标题区右侧的一组 `monitor-module__pill` 胶囊中,统一展示“总数 / 运行中 / 休眠中”,不再额外保留独立摘要行,以减少默认卡片的纵向占用。该 modal 继续采用深色控制台式表格布局,支持搜索 PID / 用户 / 命令、自动刷新、手动刷新,以及对单个进程执行“结束”或“强制结束”操作,并通过当前活动 SSH 会话的 `wsManager` 与后端 `process:list` / `process:signal` 消息交互;当前还支持点击 `PID / USER / STATE / CPU / MEM / START / COMMAND` 表头做本地升降序排序,并为激活列显示方向标记,同时给右上角关闭按钮预留了独立安全区,避免与刷新区过近。
|
||||
**结果**: 前端状态监控形成了“更贴近参考图的默认小屏监控 + 独立进程管理页”的双层结构:默认面板不仅保持了侧栏内的高密度布局,还允许用户直接在网络和 CPU 卡片里查看近期历史波动,并在 CPU 区快速识别负载集中在哪些核心;完整进程管理继续独立存在,不挤占侧栏本体;进入进程管理详细视图后,也能更快按字段维度筛查进程。
|
||||
**行为**: `StatusMonitor.vue` 当前已从通用卡片栅格重排为更接近参考图的窄屏监控结构:顶部改为成对的信息条;默认概览区当前仅保留内存、磁盘和进程管理三个常驻模块,不再默认展示 CPU 使用率卡与网络速度卡,以减少右侧窄栏里的重复信息密度。其中内存卡片会在容器宽度大于等于 250px 时维持环形概览与统计块的高密度横向布局,仅在低于 250px 时切为手机式竖排;磁盘模块继续展示设备视觉块与紧凑磁盘摘要;默认视图底部继续保留“进程管理”概览与高占用进程预览,并通过“查看全部”打开 `ProcessManagerModal.vue`。其中进程统计当前已整体收纳到模块标题区右侧的一组 `monitor-module__pill` 胶囊中,统一展示“总数 / 运行中 / 休眠中”,不再额外保留独立摘要行,以减少默认卡片的纵向占用。该 modal 继续采用深色控制台式表格布局,支持搜索 PID / 用户 / 命令、自动刷新、手动刷新,以及对单个进程执行“结束”或“强制结束”操作,并通过当前活动 SSH 会话的 `wsManager` 与后端 `process:list` / `process:signal` 消息交互;当前还支持点击 `PID / USER / STATE / CPU / MEM / START / COMMAND` 表头做本地升降序排序,并为激活列显示方向标记,同时给右上角关闭按钮预留了独立安全区,避免与刷新区过近。
|
||||
**结果**: 前端状态监控形成了“更贴近参考图的默认小屏监控 + 独立进程管理页”的双层结构:默认面板保持了侧栏内更紧凑的三块常驻概览,不再重复显示 CPU 和网络卡片;完整进程管理继续独立存在,不挤占侧栏本体;进入进程管理详细视图后,也能更快按字段维度筛查进程。
|
||||
|
||||
### 快捷指令拖拽排序
|
||||
**条件**: 用户在 Workbench 的快捷指令视图中浏览分组或扁平命令列表,且当前未启用搜索过滤。
|
||||
|
||||
@@ -57,38 +57,6 @@
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="monitor-module monitor-module--usage">
|
||||
<div class="monitor-module__heading">
|
||||
<div>
|
||||
<span class="monitor-module__eyebrow">{{ t('statusMonitor.cpuLabel') }}</span>
|
||||
<h5 class="monitor-module__title">{{ t('statusMonitor.cpuUsageTitle') }}</h5>
|
||||
</div>
|
||||
<span class="monitor-module__pill">{{ displayCpuCores }}</span>
|
||||
</div>
|
||||
|
||||
<div class="module-split module-split--cpu">
|
||||
<StatusMonitorCpuHistoryChart :cpu-history="currentCpuHistory" />
|
||||
|
||||
<div class="cpu-core-grid">
|
||||
<article
|
||||
v-for="item in cpuCoreItems"
|
||||
:key="item.key"
|
||||
class="usage-lane usage-lane--cpu"
|
||||
>
|
||||
<div class="usage-lane__content">
|
||||
<div class="usage-lane__meta">
|
||||
<span class="usage-lane__label">{{ item.label }}</span>
|
||||
<span class="usage-lane__value-inline">{{ item.value }}</span>
|
||||
</div>
|
||||
<div class="usage-lane__track">
|
||||
<span class="usage-lane__fill" :style="{ width: `${item.percent}%` }"></span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="monitor-module-grid">
|
||||
<section class="monitor-module monitor-module--memory">
|
||||
<div class="monitor-module__heading">
|
||||
@@ -123,50 +91,6 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="monitor-module monitor-module--network">
|
||||
<div class="monitor-module__heading">
|
||||
<div>
|
||||
<span class="monitor-module__eyebrow">{{ t('statusMonitor.networkLabel') }}</span>
|
||||
<h5 class="monitor-module__title">{{ t('statusMonitor.networkLabel') }}</h5>
|
||||
</div>
|
||||
<span class="monitor-module__pill">{{ t('statusMonitor.networkSpeedTitleUnit', { unit: networkRateUnitLabel }) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="module-split module-split--network">
|
||||
<StatusMonitorNetworkHistoryChart
|
||||
:download-history="currentNetRxHistory"
|
||||
:upload-history="currentNetTxHistory"
|
||||
/>
|
||||
|
||||
<div class="network-table">
|
||||
<div class="network-table__header">
|
||||
<span>{{ networkInterfaceDisplay }}</span>
|
||||
<span>{{ t('statusMonitor.downloadLabel') }} / {{ t('statusMonitor.uploadLabel') }}</span>
|
||||
</div>
|
||||
<div class="network-table__columns">
|
||||
<span></span>
|
||||
<span>{{ t('statusMonitor.networkSpeedTitleUnit', { unit: networkRateUnitLabel }) }}</span>
|
||||
<span>{{ t('statusMonitor.totalTrafficLabel') }}</span>
|
||||
</div>
|
||||
|
||||
<div class="network-stat-stack">
|
||||
<article
|
||||
v-for="item in networkFlowItems"
|
||||
:key="item.key"
|
||||
:class="['network-stat', `network-stat--${item.tone}`]"
|
||||
>
|
||||
<span class="network-stat__label">
|
||||
<i :class="['fas', item.icon]"></i>
|
||||
<span>{{ item.label }}</span>
|
||||
</span>
|
||||
<span class="network-stat__value">{{ item.value }}</span>
|
||||
<span class="network-stat__total">{{ item.totalValue }}</span>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="monitor-module monitor-module--disk">
|
||||
<div class="monitor-module__heading">
|
||||
<div>
|
||||
@@ -278,9 +202,7 @@ import { computed, ref, watch, type CSSProperties, type PropType } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import ProcessManagerModal from './ProcessManagerModal.vue';
|
||||
import StatusMonitorCpuHistoryChart from './StatusMonitorCpuHistoryChart.vue';
|
||||
import StatusCharts from './StatusCharts.vue';
|
||||
import StatusMonitorNetworkHistoryChart from './StatusMonitorNetworkHistoryChart.vue';
|
||||
import { useSessionStore } from '../stores/session.store';
|
||||
import { useSettingsStore } from '../stores/settings.store';
|
||||
import { useConnectionsStore } from '../stores/connections.store';
|
||||
@@ -320,11 +242,6 @@ const clampPercent = (value?: number): number => {
|
||||
|
||||
const currentSessionState = computed(() => (props.activeSessionId ? sessions.value.get(props.activeSessionId) : null));
|
||||
const currentServerStatus = computed<ServerStatus | null>(() => currentSessionState.value?.statusMonitorManager?.serverStatus?.value ?? null);
|
||||
const currentCpuHistory = computed<readonly (number | null)[]>(() => currentSessionState.value?.statusMonitorManager?.cpuHistory?.value ?? Array(24).fill(null));
|
||||
const currentNetRxHistory = computed<readonly (number | null)[]>(() => currentSessionState.value?.statusMonitorManager?.netRxHistory?.value ?? Array(24).fill(null));
|
||||
const currentNetTxHistory = computed<readonly (number | null)[]>(() => currentSessionState.value?.statusMonitorManager?.netTxHistory?.value ?? Array(24).fill(null));
|
||||
|
||||
const displayCpuPercent = computed(() => clampPercent(currentServerStatus.value?.cpuPercent));
|
||||
const displayMemoryPercent = computed(() => clampPercent(currentServerStatus.value?.memPercent));
|
||||
const displayDiskPercent = computed(() => clampPercent(currentServerStatus.value?.diskPercent));
|
||||
const currentStatusError = computed<string | null>(() => currentSessionState.value?.statusMonitorManager?.statusError?.value ?? null);
|
||||
@@ -362,23 +279,6 @@ const displayCpuCores = computed(() => {
|
||||
const displayOsName = computed(() => (currentServerStatus.value?.osName ?? cachedOsName.value) || t('statusMonitor.notAvailable'));
|
||||
const networkInterfaceDisplay = computed(() => currentServerStatus.value?.netInterface || t('statusMonitor.notAvailable'));
|
||||
|
||||
const formatBytesPerSecond = (bytes?: number): string => {
|
||||
if (bytes === undefined || bytes === null || isNaN(bytes)) return t('statusMonitor.notAvailable');
|
||||
if (bytes < 1024) return `${bytes} ${t('statusMonitor.bytesPerSecond')}`;
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} ${t('statusMonitor.kiloBytesPerSecond')}`;
|
||||
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} ${t('statusMonitor.megaBytesPerSecond')}`;
|
||||
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} ${t('statusMonitor.gigaBytesPerSecond')}`;
|
||||
};
|
||||
|
||||
const formatBytes = (bytes?: number): string => {
|
||||
if (bytes === undefined || bytes === null || isNaN(bytes)) return t('statusMonitor.notAvailable');
|
||||
if (bytes < 1024) return `${bytes} B`;
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
||||
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} ${t('statusMonitor.megaBytes')}`;
|
||||
if (bytes < 1024 * 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} ${t('statusMonitor.gigaBytes')}`;
|
||||
return `${(bytes / (1024 * 1024 * 1024 * 1024)).toFixed(1)} TB`;
|
||||
};
|
||||
|
||||
const formatCompactBytes = (bytes?: number): string => {
|
||||
if (bytes === undefined || bytes === null || isNaN(bytes)) return t('statusMonitor.notAvailable');
|
||||
if (bytes < 1024) return `${bytes.toFixed(1)} B`;
|
||||
@@ -514,55 +414,6 @@ const systemCardMetaItems = computed<MonitorOverviewItem[]>(() => [
|
||||
{ key: 'uptime', label: t('statusMonitor.uptimeLabel'), value: uptimeDisplay.value },
|
||||
]);
|
||||
|
||||
const cpuCoreItems = computed(() => {
|
||||
const rawPercents = currentServerStatus.value?.cpuCorePercents;
|
||||
const fallbackCoreCount = (() => {
|
||||
const currentCores = currentServerStatus.value?.cpuCores;
|
||||
if (typeof currentCores !== 'number' || !Number.isFinite(currentCores) || currentCores <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return Math.round(currentCores);
|
||||
})();
|
||||
|
||||
const normalizedPercents = Array.isArray(rawPercents) && rawPercents.length > 0
|
||||
? rawPercents
|
||||
: Array.from({ length: fallbackCoreCount }, () => 0);
|
||||
|
||||
return normalizedPercents.map((percent, index) => {
|
||||
const clampedPercent = clampPercent(percent);
|
||||
return {
|
||||
key: `cpu-core-${index + 1}`,
|
||||
label: t('statusMonitor.cpuCoreLabel', { index: index + 1 }),
|
||||
value: `${Math.round(clampedPercent)}%`,
|
||||
percent: clampedPercent,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const networkFlowItems = computed(() => [
|
||||
{
|
||||
key: 'download',
|
||||
label: t('statusMonitor.downloadLabel'),
|
||||
value: formatBytesPerSecond(currentServerStatus.value?.netRxRate),
|
||||
totalValue: formatBytes(currentServerStatus.value?.netRxTotalBytes),
|
||||
tone: 'down',
|
||||
icon: 'fa-arrow-down',
|
||||
},
|
||||
{
|
||||
key: 'upload',
|
||||
label: t('statusMonitor.uploadLabel'),
|
||||
value: formatBytesPerSecond(currentServerStatus.value?.netTxRate),
|
||||
totalValue: formatBytes(currentServerStatus.value?.netTxTotalBytes),
|
||||
tone: 'up',
|
||||
icon: 'fa-arrow-up',
|
||||
},
|
||||
]);
|
||||
|
||||
const networkRateUnitLabel = computed(() => {
|
||||
const maxRate = Math.max(currentServerStatus.value?.netRxRate ?? 0, currentServerStatus.value?.netTxRate ?? 0);
|
||||
return maxRate >= 1024 * 1024 ? 'MB/s' : 'KB/s';
|
||||
});
|
||||
|
||||
const diskDeviceAccent = computed(() => {
|
||||
const raw = currentServerStatus.value?.diskDevice;
|
||||
|
||||
@@ -1012,13 +863,18 @@ const copyIpToClipboard = async (ipAddress: string | null) => {
|
||||
|
||||
.module-split--network {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: minmax(0, 146px) minmax(0, 1fr);
|
||||
min-height: 0;
|
||||
align-content: start;
|
||||
gap: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.monitor-module--network {
|
||||
grid-template-rows: auto minmax(0, 1fr);
|
||||
max-height: 350px;
|
||||
gap: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.memory-ring-panel,
|
||||
@@ -1138,14 +994,14 @@ const copyIpToClipboard = async (ipAddress: string | null) => {
|
||||
|
||||
.network-table {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
height: auto;
|
||||
grid-template-rows: auto auto minmax(0, 1fr);
|
||||
gap: 4px;
|
||||
min-height: 0;
|
||||
height: 100%;
|
||||
padding: 8px 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.network-table__header,
|
||||
.network-table__columns,
|
||||
.network-stat,
|
||||
.disk-summary-table__head,
|
||||
.disk-summary-table__row {
|
||||
display: flex;
|
||||
@@ -1155,46 +1011,73 @@ const copyIpToClipboard = async (ipAddress: string | null) => {
|
||||
}
|
||||
|
||||
.network-table__header {
|
||||
padding-bottom: 6px;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding-bottom: 4px;
|
||||
border-bottom: 1px solid rgba(148, 163, 184, 0.1);
|
||||
font-size: 11px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
.network-table__columns {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 0.78fr) repeat(2, minmax(0, 0.61fr));
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding-top: 0;
|
||||
color: #9cb0c2;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.network-stat-stack {
|
||||
gap: 6px;
|
||||
min-height: 0;
|
||||
align-content: start;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.network-table__columns span,
|
||||
.network-stat span,
|
||||
.disk-summary-table__head span,
|
||||
.disk-summary-table__row span {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.network-table__header span:first-child,
|
||||
.network-table__columns span:first-child,
|
||||
.network-stat span:first-child {
|
||||
flex-basis: 30%;
|
||||
.network-stat__label {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.network-table__header span:last-child,
|
||||
.network-table__columns span:not(:first-child),
|
||||
.network-stat__value,
|
||||
.network-stat__total {
|
||||
justify-self: end;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.network-stat {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 0.78fr) repeat(2, minmax(0, 0.61fr));
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.06);
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
padding: 8px 10px;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.network-stat__label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
gap: 5px;
|
||||
color: #d9e5f1;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.network-stat__label i {
|
||||
@@ -1205,7 +1088,7 @@ const copyIpToClipboard = async (ipAddress: string | null) => {
|
||||
.network-stat__value,
|
||||
.network-stat__total {
|
||||
color: #f8fbff;
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
@@ -1470,6 +1353,17 @@ const copyIpToClipboard = async (ipAddress: string | null) => {
|
||||
width: 100%;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.monitor-module--network .network-table__header,
|
||||
.monitor-module--network .network-table__columns,
|
||||
.monitor-module--network .network-stat {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.monitor-module--network .network-table__columns span,
|
||||
.monitor-module--network .network-stat span {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@container (max-width: 440px) {
|
||||
|
||||
@@ -184,15 +184,16 @@ onBeforeUnmount(() => {
|
||||
<style scoped>
|
||||
.cpu-history-chart {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
height: 188px;
|
||||
align-self: start;
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.08);
|
||||
background:
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.035), rgba(255, 255, 255, 0.02)),
|
||||
radial-gradient(circle at top left, rgba(59, 130, 246, 0.08), transparent 62%);
|
||||
padding: 12px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.cpu-history-chart__header {
|
||||
@@ -212,22 +213,22 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
|
||||
.cpu-history-chart__title {
|
||||
margin: 6px 0 0;
|
||||
margin: 4px 0 0;
|
||||
color: #f8fbff;
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.cpu-history-chart__latest {
|
||||
display: inline-flex;
|
||||
min-height: 28px;
|
||||
min-height: 24px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(96, 165, 250, 0.22);
|
||||
background: rgba(37, 99, 235, 0.18);
|
||||
padding: 0 10px;
|
||||
padding: 0 9px;
|
||||
color: #dbeafe;
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
@@ -237,7 +238,7 @@ onBeforeUnmount(() => {
|
||||
.cpu-history-chart__canvas {
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
height: 164px;
|
||||
height: 112px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
@@ -259,8 +259,10 @@ onBeforeUnmount(() => {
|
||||
<style scoped>
|
||||
.network-history-chart {
|
||||
display: grid;
|
||||
grid-template-rows: auto minmax(0, 1fr);
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
height: auto;
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.08);
|
||||
@@ -268,13 +270,14 @@ onBeforeUnmount(() => {
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.035), rgba(255, 255, 255, 0.02)),
|
||||
radial-gradient(circle at top left, rgba(59, 130, 246, 0.06), transparent 62%);
|
||||
padding: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.network-history-chart__header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.network-history-chart__subtitle {
|
||||
@@ -326,8 +329,9 @@ onBeforeUnmount(() => {
|
||||
|
||||
.network-history-chart__canvas {
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
width: 100%;
|
||||
height: 108px;
|
||||
height: 88px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user