feat(web): 窄屏适配 — 侧栏转底部 tab bar + 各页响应式重排

此前只有一个 max-width:900px 断点(仅缩窄侧栏),手机上 210px 侧栏吃掉
半屏、卡片网格横向溢出、按钮被挤成细条。新增 720px / 380px 两档断点:

App.vue(关键)
- ≤720px:shell 转竖向,导航从侧栏抽出固定到屏幕底部(图标上/文字下),
  品牌+身份+主题折进顶部窄条;tab 最小 44px 触控高度
- 内容区预留 env(safe-area-inset-bottom),避开 iOS 手势条
- ≤380px 再压一档:隐藏权限徽标、缩小字号

StatusView
- card-grid 的 minmax(320px) 在 375px 屏溢出 → 单列
- KPI 由 auto-fit 改固定两列(auto-fit 窄屏退化单列使四卡拉长),≤380px 单列
- 转发卡 fwd-head 原一行塞 7~8 元素 → 路由占整行、按钮另起一行平分
- 分组标题按钮不再被 margin-left:auto 压扁

ClusterView
- 环拓扑改竖向,节点卡占满宽,箭头旋转 90° 指下
- nodeKey(32 位 hex)允许 break-all,不再撑宽容器
- 日志 lg-detail 由 max-width:60%+nowrap 改整行完整显示

SettingsView / UsersView / CanvasView
- binary-row、setting-row 竖排;表格卡标题行竖排、操作按钮平分
- 画布 toolbar 按钮换行平铺;vue-flow handle 12px→16px(触控可达)

index.html
- viewport 加 viewport-fit=cover,这是 env(safe-area-inset-*) 生效前提

验证:tsc --noEmit 通过、npm run build 通过、dist 已重新 embed,
三节点部署后环正常(pending=0,5 条转发 frpc 进程与 topology 归属一致)。
This commit is contained in:
JianFeeeee
2026-09-03 07:20:32 +08:00
parent f9c2e9476a
commit f935cad33f
7 changed files with 370 additions and 1 deletions

View File

@ -638,4 +638,91 @@ onBeforeUnmount(() => {
.form-row { display: flex; align-items: center; gap: 10px; margin-bottom: 12px;
label { width: 72px; font-size: 13px; color: $color-text-secondary; flex-shrink: 0; }
}
/* ---------- narrow screens ----------
窄屏下的主要问题:
- card-grid 的 minmax(320px) 在 375px 屏上会溢出(加上内边距超宽)
- topbar 的 space-between 把标题和按钮挤在一行
- fwd-head 一行塞下 7~8 个元素(类型/点/路由/协议/分组/归属/按钮)
- 分组标题的三个按钮 margin-left:auto 后被挤成细条
- 触控目标(small 按钮)低于 44px */
@media (max-width: 720px) {
.status-page { gap: 10px; }
/* 标题与工具栏换行,按钮占整行平分 */
.topbar {
flex-direction: column;
align-items: stretch;
gap: 10px;
padding: 12px 14px;
}
.tb-right { width: 100%; }
.tb-right > .w4f-btn { flex: 1 1 0; min-height: 40px; }
/* KPI 固定两列(auto-fit 在窄屏会变单列,四张卡拉得太长) */
.kpis {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
}
/* 卡片网格改单列:消除 320px 最小宽导致的横向溢出 */
.card-grid {
grid-template-columns: minmax(0, 1fr);
gap: 10px;
}
/* 分组标题:标题一行,操作按钮另起一行平分 */
.section-title {
flex-wrap: wrap;
row-gap: 8px;
}
.grp-actions {
margin-left: 0;
width: 100%;
gap: 6px;
}
.grp-actions > .w4f-btn { flex: 1 1 0; min-height: 38px; font-size: 12px; }
/* 转发卡:路由占整行,其余属性换行,按钮另起一行 */
.fwd-card { padding: 11px 12px; }
.fwd-head { row-gap: 7px; }
.fwd-route {
order: -1;
width: 100%;
margin-right: 0;
flex-wrap: wrap;
row-gap: 2px;
font-size: 13px;
}
.fwd-owner { max-width: 100%; }
.fwd-actions {
width: 100%;
margin-top: 2px;
}
.fwd-actions > .w4f-btn { flex: 1 1 0; min-height: 38px; }
/* 远程节点卡:名称占行,操作按钮平分 */
.node-card { padding: 14px 15px; }
.nc-name { width: 100%; margin-right: 0; }
.nc-actions { width: 100%; }
.nc-actions > .w4f-btn { flex: 1 1 0; min-height: 38px; }
.nc-cluster-note { max-width: 100%; }
.meta-pill { max-width: 100%; }
.np-err { max-width: 45%; }
/* 对话框表单:标签在上、输入在下(窄屏下 72px 标签挤死输入框) */
.form-row {
flex-direction: column;
align-items: stretch;
gap: 5px;
label { width: auto; }
}
}
@media (max-width: 380px) {
/* 极窄:KPI 也降为单列,否则数字会被压断 */
.kpis { grid-template-columns: minmax(0, 1fr); }
.fwd-route { font-size: 12.5px; }
.page-title { font-size: 16px; }
}
</style>