fix(mobile): 修移动端顶栏塌陷,窄屏默认卡片视图

问题(用户报告移动端异常):窄屏下顶栏统计挤成竖排并与标题重叠,
表格在手机上必须横向拖动,固定装饰在小屏上可能溢出或压住内容。

修复:
- 顶栏结构拆为「标题 / 统计(.stats) / 控件(.controls)」三段;
  ≤820px 改为纵向三行,统计均分视口宽度,页脚改纵向堆叠
- ≤700px 且用户未显式选择视图时自动用卡片(横竖屏切换时重算,
  显式选择仍尊重 localStorage)
- ≤820px(及横屏手机)撤掉立绘、公路车、光环水印等固定装饰

验证:
- 390x844 / 360x640 / 414x896 / 844x390 四种视口:0 横向溢出,顶栏三行
- verify-views 新增移动端 7 条断言(22/22 通过)
- 四套合计 81 项全通过(17+22+20+22)
This commit is contained in:
tamakyi
2026-10-13 20:38:39 +08:00
parent 5b417c84f4
commit f0e5d91171
15 changed files with 523 additions and 442 deletions
+16 -2
View File
@@ -1,4 +1,4 @@
const VERSION = '0.8.0';
const VERSION = '0.8.1';
const HB_LIMIT = 48;
const UPTIME_DAYS = 90;
const BROWSER_CONCURRENCY = 6;
@@ -507,14 +507,23 @@ function renderViewSwitch() {
}
}
// 窄屏(手机)判定:表格在最窄的屏幕上必须横向拖动,不如默认给卡片视图
function isNarrowScreen() {
return window.matchMedia('(max-width: 700px)').matches;
}
function resolveView() {
const q = new URLSearchParams(location.search).get('view');
if (q && VIEWS[q]) return q;
let saved = null;
try {
saved = localStorage.getItem('shiroko.view');
} catch { /* 隐私模式下 localStorage 不可用 */ }
const cfg = state.config?.settings?.view;
return [q, saved, cfg, DEFAULT_VIEW].find((v) => v && VIEWS[v]) ?? DEFAULT_VIEW;
const resolved = [saved, cfg, DEFAULT_VIEW].find((v) => v && VIEWS[v]) ?? DEFAULT_VIEW;
// 用户没在这个设备上显式选过视图时,窄屏自动落到卡片(表格在手机上要横向拖)
if (!saved && resolved === 'table' && isNarrowScreen()) return 'cards';
return resolved;
}
function setView(view, { persist = true } = {}) {
@@ -802,6 +811,11 @@ function bindEvents() {
// 只有手动点「刷新」才会触发一次重新检测;自动刷新是只读的
$('refresh-btn').addEventListener('click', () => refresh({ force: true }));
$('theme-btn')?.addEventListener('click', () => applyTheme(state.theme === 'shiroko' ? 'default' : 'shiroko'));
// 横竖屏切换 / 窗口缩放时重算视图(不覆盖用户显式选择,故 persist: false)
window.addEventListener('resize', () => {
const next = resolveView();
if (next !== state.view) setView(next, { persist: false });
});
}
/* ---------- 启动 ---------- */
+15 -2
View File
@@ -91,6 +91,8 @@ body {
.brand-text p { margin: 0; font-size: 12px; color: var(--text-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.summary { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 16px; }
.stats { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 16px; }
.controls { display: flex; align-items: center; gap: 8px; }
.pill {
padding: 4px 12px;
@@ -323,9 +325,20 @@ body {
.demo-chip { padding: 1px 8px; border-radius: 999px; background: var(--pending-bg); color: var(--pending); font-weight: 600; }
.warn-chip { padding: 1px 8px; border-radius: 999px; background: var(--down-bg); color: var(--down); font-weight: 600; }
/* 窄屏(手机 / 竖屏平板):顶栏改为三行——标题 / 统计 / 控件,避免挤成一团 */
@media (max-width: 820px) {
.topbar { flex-direction: column; align-items: stretch; gap: 10px; padding: 12px 14px 14px; }
.summary { flex-direction: column; align-items: stretch; gap: 8px; }
.stats { justify-content: space-between; gap: 4px; }
.stat { min-width: 0; flex: 1 1 0; }
.stat b { font-size: 15px; }
.stat label { font-size: 10px; }
.controls { justify-content: flex-end; gap: 10px; }
.footer { flex-direction: column; align-items: flex-start; gap: 4px; padding-bottom: 20px; }
.footer .version { margin-left: 0; }
}
@media (max-width: 640px) {
.topbar { padding: 10px 14px; }
.summary { gap: 6px 12px; }
.grid { grid-template-columns: 1fr; padding: 12px 14px 8px; }
.group-tabs { padding: 12px 14px 0; }
.footer { padding: 8px 14px 24px; }
+8 -1
View File
@@ -215,7 +215,14 @@ body[data-theme="shiroko"] .artwork {
user-select: none;
filter: drop-shadow(0 6px 18px rgb(23 60 100 / 15%));
}
@media (max-width: 1100px) {
/* 窄屏:立绘与固定装饰全部撤掉,避免压住内容或溢出(手机只保留配色与围巾飘带) */
@media (max-width: 820px) {
body[data-theme="shiroko"] .artwork,
body[data-theme="shiroko"]::before,
body[data-theme="shiroko"]::after { display: none; }
}
/* 横屏手机:可用高度很小,立绘同样撤掉 */
@media (max-height: 520px) {
body[data-theme="shiroko"] .artwork { display: none; }
}
/* 卡片视图行数多、卡片更靠右下,立绘再收一档,尽量让它落在卡片之外的角落 */