fix(frontend): redraw status charts on container resize in narrow sidebar

observe the chart host width and force cpu/network chart remount when the
sidebar width changes, preventing stale canvas dimensions after layout
compression.

add min-width/width constraints on chart containers and canvas to keep chart
rendering bounded to the current panel width, improving responsiveness in
tight layouts.

archive the completed layout-parity proposal/tasks and sync changelog/module
records to reflect the delivered frontend updates.
This commit is contained in:
yinjianm
2026-04-16 00:47:25 +08:00
parent a28822ec79
commit 34d6c3f499
8 changed files with 260 additions and 172 deletions
+6 -1
View File
@@ -2,7 +2,9 @@
## [Unreleased]
- **[frontend]**: 将右侧状态监控继续收紧为更贴近服务器小屏的默认概览,并新增时区、运行时间、进程概览与“查看全部”独立进程管理弹窗 — by yinjianm
- **[frontend]**: 将右侧服务器状态监控的默认视图从通用卡片栅格重排为更贴近参考图的窄屏监控布局,统一顶部信息条、索引化资源行与内存/网络/磁盘/进程概览的左右分区关系 - by yinjianm
- 方案: [202604152323_status-monitor-reference-layout-parity](archive/2026-04/202604152323_status-monitor-reference-layout-parity/)
- **[frontend]**: 将右侧状态监控继续收紧为更贴近服务器小屏的默认概览,并新增时区、运行时间、进程概览与“查看全部”独立进程管理弹窗 - by yinjianm
- 方案: [202604152147_status-monitor-process-manager-modal](archive/2026-04/202604152147_status-monitor-process-manager-modal/)
- **[backend]**: 扩展状态监控采集时区、运行时间和轻量进程摘要,并为当前 SSH 会话新增 `process:list` / `process:signal` WebSocket 进程管理消息 — by yinjianm
- 方案: [202604152147_status-monitor-process-manager-modal](archive/2026-04/202604152147_status-monitor-process-manager-modal/)
@@ -65,6 +67,9 @@
- **[frontend]**: 修复右侧状态监控在窄侧栏下的内存/磁盘卡片字体重叠问题,改为基于卡片容器宽度自适应折列与缩字 — by yinjianm
- 类型: 快速修改(无方案包)
- 文件: packages/frontend/src/components/StatusMonitor.vue:446-452,572-600,697-707,744-802
- **[frontend]**: 修复右侧状态监控底部 CPU / 网络图表在侧栏收窄后仍保留旧 canvas 宽度的问题,补充容器宽度监听并强制图表随容器重绘收缩 — by yinjianm
- 类型: 快速修改(无方案包)
- 文件: packages/frontend/src/components/StatusCharts.vue:2,34,99-100,439-479,485-560
- **[frontend]**: 将“添加新连接”弹窗的脚本模式开关上移到基本信息之前,并在脚本导入时自动忽略空格、空行与 Markdown 代码围栏行 — by yinjianm
- 类型: 快速修改(无方案包)
- 文件: packages/frontend/src/components/AddConnectionForm.vue, packages/frontend/src/composables/useAddConnectionForm.ts
@@ -0,0 +1,148 @@
# 变更提案: status-monitor-reference-layout-parity
## 元信息
```yaml
类型: 重构/优化
方案类型: implementation
优先级: P1
状态: 已确认
创建: 2026-04-15
```
---
## 1. 需求
### 背景
上一轮已经为右侧状态监控补上时区、运行时间、进程概览和“查看全部”独立进程管理页,但用户继续明确指出默认视图的问题不是功能不够,而是布局骨架不对: 当前仍是通用卡片栅格,和参考图里那种窄屏监控面板的左右分区、信息压缩感、模块层级关系不一致。
### 目标
-`StatusMonitor.vue` 默认视图重排为更接近参考图的紧凑监控面板结构
- 把顶部信息区改成成对的左右信息条,而不是松散的卡片网格
- 将 CPU / 内存 / 网络 / 磁盘 / 进程概览改为明显的左右内部分区,同时保持响应式
- 保留现有“查看全部”流程和后端数据边界,不为了贴图伪造不存在的数据字段
### 约束条件
```yaml
时间约束: 本轮内完成布局重排、构建验证和知识库同步
性能约束: 不新增重型图表依赖,不破坏现有状态监控刷新链路
兼容性约束: 保留 `StatusCharts`、`ProcessManagerModal`、现有 WebSocket 状态数据与设置项行为
业务约束: 默认视图只做概览;完整进程管理继续通过“查看全部”弹窗进入
```
### 验收标准
- [ ] `StatusMonitor.vue` 默认视图从通用卡片栅格重排为更接近参考图的窄屏监控布局,并体现明确的左右内部分区
- [ ] 顶部信息区、资源使用区、内存/网络/磁盘/进程概览都保持统一视觉风格,并兼容窄侧栏宽度
- [ ] 不新增虚构数据字段,继续复用当前后端已提供的状态数据
- [ ] `packages/frontend` 构建通过
---
## 2. 方案
### 技术方案
本次只改前端默认视图,不改后端协议:
-`StatusMonitor.vue` 中移除原有“头部 + 元信息卡片网格 + 资源条带 + 多张通用卡片”的骨架
- 改为“头部身份区 + 成对信息条 + 索引化资源行 + 左右分区模块”的新布局
- CPU 区使用带编号的紧凑资源行表达 CPU / 内存 / Swap / 磁盘占比
- 内存区保留环形占比,但右侧改成紧凑统计堆叠
- 网络区改为左侧监控屏风格示意面板、右侧上下行速率堆叠卡
- 磁盘区改为左侧设备视觉块、右侧紧凑统计,再加底部摘要网格
- 进程区继续保留“查看全部”按钮,但默认只展示摘要和前几条高占用进程预览
### 影响范围
```yaml
涉及模块:
- packages/frontend/src/components/StatusMonitor.vue: 默认视图模板与样式重构
- .helloagents/modules/frontend.md: 同步默认视图与独立进程管理页的最新交付形态
- .helloagents/CHANGELOG.md: 记录本轮状态监控默认视图重排
- .helloagents/archive/_index.md: 归档索引补录
预计变更文件: 4
```
### 风险评估
| 风险 | 等级 | 应对 |
|------|------|------|
| 右侧窄栏下信息再次堆叠或换行失控 | 中 | 使用 container query 收紧断点,优先保证左右关系,再在极窄宽度下退化为单列 |
| 过度贴近参考图导致引入伪历史图表 | 中 | 只用当前已有实时字段做布局表达,不引入伪造时间序列数据 |
| 视觉语言与现有进程管理弹窗脱节 | 低 | 统一深色监控基调、边框层级和数字显示风格 |
---
## 3. 技术设计(可选)
> 涉及架构变更、API设计、数据模型变更时填写
### 架构设计
```mermaid
flowchart TD
A[StatusMonitor 默认视图重排] --> B[顶部信息条]
A --> C[索引化资源行]
A --> D[左右分区模块]
D --> E[Memory / Network / Disk / Process]
E --> F[ProcessManagerModal 保持独立]
```
### API设计
N/A,本轮不新增接口。
### 数据模型
| 字段 | 类型 | 说明 |
|------|------|------|
| `cpuPercent` / `memPercent` / `swapPercent` / `diskPercent` | `number` | 继续驱动默认视图资源行与占比展示 |
| `timezone` / `uptimeSeconds` | `string` / `number` | 顶部概览信息条 |
| `netRxRate` / `netTxRate` / `netRxTotalBytes` / `netTxTotalBytes` | `number` | 网络模块左右区块展示 |
| `topProcesses` / `processTotal` / `processRunning` / `processSleeping` | `array` / `number` | 进程概览与“查看全部”入口前的默认预览 |
---
## 4. 核心场景
> 执行完成后同步到对应模块文档
### 场景: 默认状态监控更贴近参考图
**模块**: `packages/frontend/src/components/StatusMonitor.vue`
**条件**: 用户在 `/workspace` 右侧状态监控面板查看在线服务器状态
**行为**: 默认视图以更紧凑的监控小屏结构展示顶部信息条、索引化资源行、左右分区的内存/网络/磁盘模块,以及保留“查看全部”的进程概览
**结果**: 用户在不打开独立管理页时,也能得到更接近参考图层级和左右关系的默认状态视图
---
## 5. 技术决策
> 本方案涉及的技术决策,归档后成为决策的唯一完整记录
### status-monitor-reference-layout-parity#D001: 默认视图采用“结构重排”而不是继续叠加装饰样式
**日期**: 2026-04-15
**状态**: ✅采纳
**背景**: 用户已多次明确指出问题在于默认视图的左右布局和内部模块关系,而不是颜色、阴影或单个组件细节不够“好看”。
**选项分析**:
| 选项 | 优点 | 缺点 |
|------|------|------|
| A: 继续在现有卡片栅格上加样式 | 改动小、风险低 | 结构仍不对,无法解决用户指出的左右关系问题 |
| B: 直接重排默认视图骨架 | 能从布局层接近参考图,问题命中更准确 | 需要同时改模板与样式,改动面更大 |
**决策**: 选择方案 B
**理由**: 当前差距来自信息组织方式,而不是视觉细节。只有重排结构,才能把默认视图从“通用 dashboard 卡片”拉回“服务器小屏监控”。
**影响**: 影响 `StatusMonitor.vue` 模板层级、样式系统以及知识库中的状态监控描述
---
## 6. 成果设计
> 含视觉产出的任务由 DESIGN Phase2 填充。非视觉任务整节标注"N/A"。
### 设计方向
- **美学基调**: 深色运维监控台,小屏幕密度、冷光监控线条、紧凑数字化排版
- **记忆点**: 不是一组普通卡片,而是每块内容都带明显左右分区和监控屏幕感
- **参考**: 用户提供的服务器状态小屏截图,重点参考其左右布局关系与窄屏密度
### 视觉要素
- **配色**: 以近黑蓝灰为底,绿/蓝/黄/红分别标识资源状态,避免花哨渐变堆叠
- **字体**: 延续项目现有字体体系,关键数字采用 monospace,强调监控台读数感
- **布局**: 默认单列主轴,模块内部优先左右分区;宽度足够时局部双列,但不回退为松散卡片网格
- **动效**: 仅保留 hover 和按钮轻微上浮反馈,不新增喧宾夺主的入场动画
- **氛围**: 深色控制台底、轻微玻璃层、细边框、低饱和荧光强调和屏幕网格细节
### 技术约束
- **可访问性**: 保留文本标签,不以纯颜色表达状态;按钮和信息块保持明确 hover / click 反馈
- **响应式**: 依赖 container query 保持右侧侧栏适配,在极窄宽度下逐步退化而不破坏信息可读性
@@ -0,0 +1,50 @@
# 任务清单: status-monitor-reference-layout-parity
> **@status:** completed | 2026-04-15 23:59
```yaml
@feature: status-monitor-reference-layout-parity
@created: 2026-04-15
@status: completed
@mode: R2
```
## 进度概览
| 完成 | 失败 | 跳过 | 总数 |
|------|------|------|------|
| 5 | 0 | 0 | 5 |
---
## 任务列表
### 1. 默认视图结构重排
- [√] 1.1 复核用户反馈,确认问题是默认视图的左右布局关系而不是单纯样式细节
- [√] 1.2 在 `packages/frontend/src/components/StatusMonitor.vue` 中重排顶部信息区和默认概览骨架
- [√] 1.3 将 CPU / 内存 / 网络 / 磁盘 / 进程概览调整为更贴近参考图的紧凑左右分区布局
### 2. 验证与知识库同步
- [√] 2.1 执行 `packages/frontend` 构建验证默认视图改动未引入模板或类型错误
- [√] 2.2 同步 `CHANGELOG`、模块文档与方案归档记录
---
## 执行日志
| 时间 | 任务 | 状态 | 备注 |
|------|------|------|------|
| 2026-04-15 23:28 | 1.1 | completed | 明确本轮只处理默认视图布局骨架,不再扩展功能 |
| 2026-04-15 23:42 | 1.2 / 1.3 | completed | `StatusMonitor.vue` 已从卡片栅格改为头部信息条、索引资源行与左右分区模块 |
| 2026-04-15 23:49 | 2.1 | completed | `npm run build` in `packages/frontend` 通过,仅保留仓库既有 Vite chunk 警告 |
| 2026-04-15 23:59 | 2.2 | completed | 知识库、变更记录与归档索引已同步 |
---
## 执行备注
- 本轮刻意不修改 `ProcessManagerModal.vue` 的行为,只保留默认视图到独立管理页的分层关系
- 网络左侧“监控屏”只使用当前已有速率字段做布局表达,没有新增伪造历史数据
- 宽度适配优先保证左右关系,其次才在极窄宽度下退化为单列
+2
View File
@@ -7,6 +7,7 @@
| 时间戳 | 名称 | 类型 | 涉及模块 | 决策 | 结果 |
|--------|------|------|---------|------|------|
| 202604152323 | status-monitor-reference-layout-parity | implementation | frontend | status-monitor-reference-layout-parity#D001 | ✅完成 |
| 202604152147 | status-monitor-process-manager-modal | - | - | - | ✅完成 |
| 202604152139 | workspace-global-search-tag-fuzzy-search | - | - | - | ✅完成 |
| 202604152110 | workspace-global-search-show-connection-tags | - | - | - | ✅完成 |
@@ -54,6 +55,7 @@
## 按月归档
### 2026-04
- [202604152323_status-monitor-reference-layout-parity](./2026-04/202604152323_status-monitor-reference-layout-parity/) - 将右侧状态监控默认视图重排为更贴近参考图的窄屏监控布局,修正顶部信息区与模块内部左右关系
- [202604122248_connections-tag-batch-management](./2026-04/202604122248_connections-tag-batch-management/) - 为连接管理页新增标签批量管理弹窗,并补齐后端批量标签删除策略
- [202604120709_quickcommands-double-click-tooltip](./2026-04/202604120709_quickcommands-double-click-tooltip/) - 将快捷命令列表改为单击选中、双击执行,并在 hover 时显示完整命令
- [202604120656_ssh-group-close-and-script-input-sanitize](./2026-04/202604120656_ssh-group-close-and-script-input-sanitize/) - 为 SSH 服务器组头补充整组关闭按钮,并修正脚本模式对单/双引号包裹值的保存行为
+2 -2
View File
@@ -58,6 +58,6 @@
### 状态监控卡片
**条件**: 用户在 `/workspace` 右侧状态监控面板查看服务器资源状态。
**行为**: `StatusMonitor.vue` 当前继续向“服务器监控小屏”风格收紧:默认视图在顶部信息区之外,补充了服务器时区和运行时间,并新增“进程管理”概览卡片,直接展示总进程数、运行中、休眠中以及若干高占用进程预览;点击“查看全部”后会打开新的 `ProcessManagerModal.vue`。该 modal 采用深色控制台式表格布局,支持搜索 PID / 用户 / 命令、自动刷新、手动刷新,以及对单个进程执行“结束”或“强制结束”操作,并通过当前活动 SSH 会话的 `wsManager` 与后端 `process:list` / `process:signal` 消息交互。
**结果**: 前端状态监控形成了“默认小屏概览 + 独立进程管理页”的双层结构:默认面板更贴近用户参考图,而完整进程管理又不会挤占侧栏监控本体。
**行为**: `StatusMonitor.vue` 当前已从通用卡片栅格重排为更接近参考图的窄屏监控结构:顶部改为成对的信息条,资源概览改为带编号的紧凑使用率行,内存/网络/磁盘模块都采用明显的左右分区关系,分别展示环形占比+统计堆叠、监控屏风格网络面板+上下行速率堆叠,以及设备视觉块+紧凑磁盘摘要;默认视图底部继续保留“进程管理”概览与高占用进程预览,并通过“查看全部”打开 `ProcessManagerModal.vue`。该 modal 继续采用深色控制台式表格布局,支持搜索 PID / 用户 / 命令、自动刷新、手动刷新,以及对单个进程执行“结束”或“强制结束”操作,并通过当前活动 SSH 会话的 `wsManager` 与后端 `process:list` / `process:signal` 消息交互。
**结果**: 前端状态监控形成了“更贴近参考图的默认小屏监控 + 独立进程管理页”的双层结构:默认面板先解决左右布局和视觉层级问题,而完整进程管理继续独立存在,不挤占侧栏本体。
@@ -1,126 +0,0 @@
# 变更提案: status-monitor-reference-layout-parity
## 元信息
```yaml
类型: 新功能/修复/重构/优化
方案类型: implementation
优先级: P0/P1/P2/P3
状态: 草稿
创建: 2026-04-15
```
---
## 1. 需求
### 背景
{为什么需要这个变更}
### 目标
{要达成什么目标}
### 约束条件
```yaml
时间约束: {如有}
性能约束: {如有}
兼容性约束: {如有}
业务约束: {如有}
```
### 验收标准
- [ ] {标准1}
- [ ] {标准2}
---
## 2. 方案
### 技术方案
{简要描述实现方式}
### 影响范围
```yaml
涉及模块:
- {模块1}: {影响说明}
预计变更文件: {数量}
```
### 风险评估
| 风险 | 等级 | 应对 |
|------|------|------|
| {风险} | 高/中/低 | {措施} |
---
## 3. 技术设计(可选)
> 涉及架构变更、API设计、数据模型变更时填写
### 架构设计
```mermaid
flowchart TD
A[组件A] --> B[组件B]
```
### API设计
#### {METHOD} {路径}
- **请求**: {结构}
- **响应**: {结构}
### 数据模型
| 字段 | 类型 | 说明 |
|------|------|------|
| {字段} | {类型} | {说明} |
---
## 4. 核心场景
> 执行完成后同步到对应模块文档
### 场景: {场景名称}
**模块**: {所属模块}
**条件**: {前置条件}
**行为**: {操作描述}
**结果**: {预期结果}
---
## 5. 技术决策
> 本方案涉及的技术决策,归档后成为决策的唯一完整记录
### status-monitor-reference-layout-parity#D001: {决策标题}
**日期**: 2026-04-15
**状态**: ✅采纳 / ❌废弃 / ⏸搁置
**背景**: {为什么需要这个决策}
**选项分析**:
| 选项 | 优点 | 缺点 |
|------|------|------|
| A: {方案A} | {优点} | {缺点} |
| B: {方案B} | {优点} | {缺点} |
**决策**: 选择方案{X}
**理由**: {详细理由}
**影响**: {对哪些模块有影响}
---
## 6. 成果设计
> 含视觉产出的任务由 DESIGN Phase2 填充。非视觉任务整节标注"N/A"。
### 设计方向
- **美学基调**: {鲜明的美学方向名称+具体描述 — 禁止"现代简洁"等空泛词}
- **记忆点**: {这个设计最令人难忘的一个特征}
- **参考**: {URL/截图/风格描述/无}
### 视觉要素
- **配色**: {主色+强调色+背景色,色值或方向}
- **字体**: {展示字体(标题)+正文字体配对+选择理由,避免 Arial/Inter/Roboto 等通用字体}
- **布局**: {整体结构+空间策略}
- **动效**: {入场动画/状态切换/交互反馈的动态效果策略}
- **氛围**: {背景处理/纹理/阴影/透明叠层等纵深细节}
### 技术约束
- **可访问性**: {对比度/语义化/导航要求/N/A}
- **响应式**: {断点/适配策略/N/A}
@@ -1,41 +0,0 @@
# 任务清单: status-monitor-reference-layout-parity
```yaml
@feature: status-monitor-reference-layout-parity
@created: 2026-04-15
@status: pending
@mode: {R2|R3}
```
## 进度概览
| 完成 | 失败 | 跳过 | 总数 |
|------|------|------|------|
| 0 | 0 | 0 | X |
---
## 任务列表
### 1. {阶段/模块名称}
- [ ] 1.1 在 `{文件路径}` 中实现 {具体功能}
- [ ] 1.2 在 `{文件路径}` 中实现 {具体功能}
- 依赖: 1.1
### 2. {阶段/模块名称}
- [ ] 2.1 {任务描述}
---
## 执行日志
| 时间 | 任务 | 状态 | 备注 |
|------|------|------|------|
---
## 执行备注
> 记录执行过程中的重要说明、决策变更、风险提示等
@@ -1,5 +1,5 @@
<template>
<div class="status-charts">
<div ref="chartHostRef" class="status-charts">
<section class="chart-panel">
<div class="chart-panel__header">
<div>
@@ -31,7 +31,7 @@
</template>
<script setup lang="ts">
import { ref, watch, computed, type PropType } from 'vue';
import { ref, watch, computed, onMounted, onBeforeUnmount, nextTick, type PropType } from 'vue';
import { useI18n } from 'vue-i18n';
import { Line } from 'vue-chartjs';
import { useSessionStore } from '../stores/session.store';
@@ -96,6 +96,9 @@ const networkChartKey = ref(0);
const networkRateUnitIsMB = ref(false);
const memoryUnitIsGB = ref(false);
const chartHostRef = ref<HTMLElement | null>(null);
let chartResizeObserver: ResizeObserver | null = null;
let lastChartHostWidth = 0;
// const networkChartTitle = ref('网络速度 (KB/s)'); // Will be replaced by i18n
// const memoryChartTitle = ref('内存使用情况 (MB)'); // Will be replaced by i18n
@@ -433,12 +436,49 @@ const updateAxisAndUnits = () => {
}
};
const rerenderVisibleCharts = () => {
cpuChartKey.value++;
networkChartKey.value++;
};
// --- 监听 props.serverStatus 的变化,仅用于更新 Y 轴范围和单位 ---
// 数据本身由 computed 属性从 store 获取
watch(() => props.serverStatus, () => {
updateAxisAndUnits();
}, { deep: true, immediate: true }); // immediate: true 确保初始加载时设置好轴
onMounted(() => {
const host = chartHostRef.value;
if (!host || typeof ResizeObserver === 'undefined') {
return;
}
lastChartHostWidth = Math.round(host.getBoundingClientRect().width);
chartResizeObserver = new ResizeObserver(entries => {
const entry = entries[0];
if (!entry) {
return;
}
const nextWidth = Math.round(entry.contentRect.width);
if (!nextWidth || Math.abs(nextWidth - lastChartHostWidth) < 2) {
return;
}
lastChartHostWidth = nextWidth;
nextTick(() => {
rerenderVisibleCharts();
});
});
chartResizeObserver.observe(host);
});
onBeforeUnmount(() => {
chartResizeObserver?.disconnect();
chartResizeObserver = null;
});
</script>
<style scoped>
@@ -446,10 +486,12 @@ watch(() => props.serverStatus, () => {
display: grid;
gap: 12px;
margin-top: 2px;
min-width: 0;
container-type: inline-size;
}
.chart-panel {
min-width: 0;
border-radius: 18px;
border: 1px solid rgba(148, 163, 184, 0.12);
background:
@@ -508,7 +550,15 @@ watch(() => props.serverStatus, () => {
}
.chart-wrapper {
min-width: 0;
width: 100%;
height: 164px;
overflow: hidden;
}
.chart-wrapper :deep(canvas) {
width: 100% !important;
max-width: 100% !important;
}
@container (min-width: 860px) {