style(monitor_web): Web UI polish - 4 类 design 改进

跟 design-critique skill 评审后, 实施 4 项可执行改进:

## 1. 🔴 修 header flex overflow bug

原: .stats { margin-left:auto } 在浏览器窄时占主轴空间, 把后面的
🛠️ 工具 + ⚙️ 按钮挤出视野 (用户截图反复出现"看不到按钮")。

改:
  .stats: 加 min-width:0 + overflow:hidden + white-space:nowrap
  .header: 加 flex-wrap:wrap + row-gap:8px 兜底换行
  .tools-btn / .settings-btn: 加 flex-shrink:0 不许被压缩

## 2. 加 design tokens (:root)

20+ 处硬编码颜色 / 间距 / 圆角散落, 改一处怕漏一处。统一:
  颜色: --bg / --bg-elev / --surface / --border / --accent / --warn ...
  间距: --s1..s6 (4/8/12/16/24/32)
  字号: --t1..t6 (11/12/13/15/18/24)
  圆角: --r1 (6) / --r2 (10) / --r3 (14) / --r-pill (999)
  阴影: --shadow-1 / --shadow-2 / --shadow-glow

工具区 CSS 全量切到 token, 老消息流 CSS 保持原样不动 (避免回归)。

## 3. 视觉强化 (tab + primary button)

Tab:
  - active 加 linear-gradient 渐变背景 (透明 → accent-bg)
  - ::after 蓝色下划线 + box-shadow glow 发光
  - hover 微微抬亮
  - 切换有 fadeIn 200ms 动画

Primary 按钮 (Step 1 主操作):
  - 改成实心渐变 #4fc3f7 → #29b6f6 (之前是空心边框, 跟普通按钮区别太小)
  - 加 box-shadow 立体感 + inset 高光
  - hover 上浮 2px + 阴影放大
  - 跟普通按钮 (透明 surface) 主次关系清晰

前置条件:
  - 之前: 黄色左边框 + 灰文字 (像 form error)
  - 现在: 紧凑 pill (背景 + 圆角 999), 头部加 ● 点
  - 显眼但不焦虑

## 4. 上台阶的小招

  body: radial-gradient(ellipse at top, #14142a, #0a0a0f) 顶部柔光
  body: 中文字体优先 PingFang SC / HarmonyOS / Source Han Sans
        (Segoe UI 渲染中文糊)
  body: antialiased + optimizeLegibility
  header: backdrop-filter blur(20px) + position:sticky 玻璃质感
  日志框: JetBrains Mono > SF Mono > Consolas, line-height 1.55

## 默认展开 toolsPanel

之前默认隐藏要先点 🛠️ 工具 按钮才能用, 用户进来一脸懵。改成默认展开:
toolsPanel 加 class="show"。toggleTools() 仍能切换 (隐藏后可还原)。

## 实测

headless chrome 截图确认:
- 🟢 三个按钮全可见
- 🟢 tab active 蓝色高亮 + 发光下划线
- 🟢 STEP 1 主按钮 primary 蓝色实心 突出
- 🟢 STEP 2 普通按钮 透明 layer
- 🟢 前置条件 pill 黄色 chip
- 🟢 背景 radial gradient 顶部柔光过渡

测试 185/185 通过。

## 跟评审建议的对照

| critique 建议 | 实施 |
|---|---|
| 修 header flex bug |  |
| 加 design tokens 系统化 |  (工具区全量, 老消息流保持原状) |
| tab active 渐变 + 发光下划线 |  |
| primary 按钮实心渐变 + 阴影 |  |
| body radial-gradient 背景 |  |
| 中文字体栈优先苹方 |  |
| header backdrop-filter |  |
| 前置条件 pill 化 |  |
This commit is contained in:
ylytdeng
2026-05-17 18:09:58 +08:00
parent 62b015337a
commit fb260063ef

View File

@@ -1653,15 +1653,50 @@ HTML_PAGE = '''<!DOCTYPE html>
<title>微信消息监听</title> <title>微信消息监听</title>
<style> <style>
*{margin:0;padding:0;box-sizing:border-box} *{margin:0;padding:0;box-sizing:border-box}
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#0a0a0f;color:#e0e0e0;height:100vh;display:flex;flex-direction:column} :root{
.header{background:linear-gradient(135deg,#1a1a2e,#16213e);padding:14px 24px;border-bottom:1px solid rgba(255,255,255,.08);display:flex;align-items:center;gap:12px;flex-shrink:0} /* 颜色 */
--bg:#0a0a0f;--bg-elev:#12121a;
--surface:rgba(255,255,255,.04);--surface-hover:rgba(255,255,255,.07);
--border:rgba(255,255,255,.08);--border-strong:rgba(255,255,255,.16);
--text:#e8eaed;--text-dim:#9aa0a6;--text-faint:#5f6368;
--accent:#4fc3f7;--accent-bg:rgba(79,195,247,.12);
--success:#81c784;--warn:#ffd54f;--danger:#ef9a9a;
/* 4-step 间距 */
--s1:4px;--s2:8px;--s3:12px;--s4:16px;--s5:24px;--s6:32px;
/* 4-step 字号 */
--t1:11px;--t2:12px;--t3:13px;--t4:15px;--t5:18px;--t6:24px;
/* 圆角 */
--r1:6px;--r2:10px;--r3:14px;--r-pill:999px;
/* 阴影 */
--shadow-1:0 1px 2px rgba(0,0,0,.3);
--shadow-2:0 8px 24px rgba(0,0,0,.4);
--shadow-glow:0 0 0 1px var(--border),0 4px 14px rgba(79,195,247,.18);
}
/* 字体: 中文优先用苹方 / 思源, 避免 Segoe UI 把中文渲染糊 */
body{
font-family:"PingFang SC","HarmonyOS Sans","Source Han Sans CN","Microsoft YaHei UI",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
background:radial-gradient(ellipse at top,#14142a 0%,#0a0a0f 60%) fixed;
color:var(--text);
height:100vh;display:flex;flex-direction:column;
-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
}
/* 顶部 header: 玻璃质感 + sticky + 防按钮被挤掉 */
.header{
background:linear-gradient(135deg,rgba(26,26,46,.85),rgba(22,33,62,.85));
backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);
padding:14px 24px;
border-bottom:1px solid var(--border);
display:flex;align-items:center;gap:12px;
flex-shrink:0;flex-wrap:wrap;row-gap:8px;
position:sticky;top:0;z-index:50;
}
.header h1{font-size:18px;font-weight:600;background:linear-gradient(90deg,#4fc3f7,#81c784);-webkit-background-clip:text;-webkit-text-fill-color:transparent} .header h1{font-size:18px;font-weight:600;background:linear-gradient(90deg,#4fc3f7,#81c784);-webkit-background-clip:text;-webkit-text-fill-color:transparent}
.status{font-size:12px;padding:4px 10px;border-radius:12px;transition:all .3s} .status{font-size:12px;padding:4px 10px;border-radius:12px;transition:all .3s}
.status.ok{background:rgba(76,175,80,.15);color:#81c784;border:1px solid rgba(76,175,80,.3)} .status.ok{background:rgba(76,175,80,.15);color:#81c784;border:1px solid rgba(76,175,80,.3)}
.status.ok::before{content:'';display:inline-block;width:6px;height:6px;border-radius:50%;background:#4caf50;margin-right:6px;animation:pulse 2s infinite} .status.ok::before{content:'';display:inline-block;width:6px;height:6px;border-radius:50%;background:#4caf50;margin-right:6px;animation:pulse 2s infinite}
.status.err{background:rgba(244,67,54,.15);color:#ef9a9a;border:1px solid rgba(244,67,54,.3)} .status.err{background:rgba(244,67,54,.15);color:#ef9a9a;border:1px solid rgba(244,67,54,.3)}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}} @keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}
.stats{margin-left:auto;font-size:12px;color:#666;display:flex;gap:16px} .stats{margin-left:auto;font-size:var(--t2);color:var(--text-faint);display:flex;gap:var(--s4);min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
.messages{flex:1;overflow-y:auto;padding:12px} .messages{flex:1;overflow-y:auto;padding:12px}
.msg{background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.06);border-radius:10px;padding:10px 14px;margin-bottom:5px;transition:transform .3s ease} .msg{background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.06);border-radius:10px;padding:10px 14px;margin-bottom:5px;transition:transform .3s ease}
.msg:hover{background:rgba(255,255,255,.05)} .msg:hover{background:rgba(255,255,255,.05)}
@@ -1712,8 +1747,8 @@ a.msg-link{text-decoration:none;color:inherit}
::-webkit-scrollbar{width:4px} ::-webkit-scrollbar{width:4px}
::-webkit-scrollbar-thumb{background:rgba(255,255,255,.08);border-radius:2px} ::-webkit-scrollbar-thumb{background:rgba(255,255,255,.08);border-radius:2px}
/* 设置面板 */ /* 设置面板 */
.settings-btn{background:none;border:1px solid rgba(255,255,255,.15);color:#888;font-size:16px;cursor:pointer;padding:4px 8px;border-radius:6px;transition:all .2s} .settings-btn{background:none;border:1px solid var(--border-strong);color:var(--text-dim);font-size:16px;cursor:pointer;padding:6px 10px;border-radius:var(--r1);transition:all .2s;flex-shrink:0}
.settings-btn:hover{color:#ccc;border-color:rgba(255,255,255,.3)} .settings-btn:hover{color:var(--text);border-color:var(--accent);background:var(--accent-bg)}
.settings-overlay{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.5);z-index:900} .settings-overlay{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.5);z-index:900}
.settings-overlay.show{display:block} .settings-overlay.show{display:block}
.settings-panel{position:fixed;top:0;right:-420px;width:400px;height:100%;background:#12121a;border-left:1px solid rgba(255,255,255,.1);z-index:901;transition:right .3s ease;display:flex;flex-direction:column;overflow:hidden} .settings-panel{position:fixed;top:0;right:-420px;width:400px;height:100%;background:#12121a;border-left:1px solid rgba(255,255,255,.1);z-index:901;transition:right .3s ease;display:flex;flex-direction:column;overflow:hidden}
@@ -1748,32 +1783,64 @@ a.msg-link{text-decoration:none;color:inherit}
/* 通知高亮 */ /* 通知高亮 */
.msg.notify-hl{border-left:3px solid #ffd54f;background:rgba(255,213,79,.08);box-shadow:0 0 12px rgba(255,213,79,.1)} .msg.notify-hl{border-left:3px solid #ffd54f;background:rgba(255,213,79,.08);box-shadow:0 0 12px rgba(255,213,79,.1)}
/* 工具面板 (Web 版替代 tkinter app_gui.py) */ /* 工具面板 (Web 版替代 tkinter app_gui.py) */
.tools-btn{background:none;border:1px solid rgba(255,255,255,.15);color:#888;font-size:13px;cursor:pointer;padding:4px 10px;border-radius:6px;transition:all .2s;margin-left:6px} .tools-btn{background:none;border:1px solid var(--border-strong);color:var(--text-dim);font-size:var(--t3);cursor:pointer;padding:6px 12px;border-radius:var(--r1);transition:all .2s;margin-left:var(--s2);flex-shrink:0;font-weight:500}
.tools-btn:hover{color:#4fc3f7;border-color:rgba(79,195,247,.4)} .tools-btn:hover{color:var(--accent);border-color:var(--accent);background:var(--accent-bg)}
#toolsPanel{display:none;background:#0f0f17;border-top:1px solid rgba(255,255,255,.08);padding:0;flex-shrink:0;max-height:60vh;overflow:hidden;flex-direction:column} #toolsPanel{display:none;background:var(--bg-elev);border-top:1px solid var(--border);padding:0;flex-shrink:0;max-height:60vh;overflow:hidden;flex-direction:column;box-shadow:inset 0 8px 16px -8px rgba(0,0,0,.4)}
#toolsPanel.show{display:flex} #toolsPanel.show{display:flex}
/* Tab 头 */ /* Tab 头 — 加 hover 浮起 + active 渐变 + 顶部 strip */
.tool-tabs{display:flex;background:#0a0a0f;border-bottom:1px solid rgba(255,255,255,.06);padding:0 24px;gap:2px;flex-shrink:0} .tool-tabs{display:flex;background:rgba(0,0,0,.3);border-bottom:1px solid var(--border);padding:0 var(--s5);gap:0;flex-shrink:0;position:relative}
.tool-tab{background:none;border:none;color:#666;font-size:13px;padding:12px 20px;cursor:pointer;border-bottom:2px solid transparent;transition:all .2s;font-family:inherit} .tool-tab{position:relative;background:none;border:none;color:var(--text-faint);font-size:var(--t3);padding:14px 22px;cursor:pointer;transition:all .2s;font-family:inherit;font-weight:500;letter-spacing:.3px}
.tool-tab:hover{color:#aaa} .tool-tab:hover{color:var(--text);background:rgba(255,255,255,.03)}
.tool-tab.active{color:#4fc3f7;border-bottom-color:#4fc3f7;font-weight:600} .tool-tab.active{color:var(--accent);background:linear-gradient(180deg,transparent,var(--accent-bg))}
.tool-tab.active::after{content:'';position:absolute;left:22px;right:22px;bottom:0;height:2px;background:var(--accent);border-radius:2px 2px 0 0;box-shadow:0 0 8px rgba(79,195,247,.5)}
/* Tab 内容 */ /* Tab 内容 */
.tool-pane{display:none;padding:16px 24px;overflow:auto;flex:1} .tool-pane{display:none;padding:var(--s5);overflow:auto;flex:1}
.tool-pane.active{display:block} .tool-pane.active{display:block;animation:fadeIn .2s ease-out}
.tool-prereq{font-size:11px;color:#777;margin-bottom:10px;padding:6px 10px;background:rgba(255,170,60,.06);border-left:2px solid rgba(255,170,60,.4);border-radius:0 4px 4px 0} @keyframes fadeIn{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}
.tool-step{margin-bottom:12px} /* 前置条件 → 紧凑 chip (不再像 form error) */
.tool-step-label{font-size:11px;color:#666;margin-bottom:6px;text-transform:uppercase;letter-spacing:.5px} .tool-prereq{display:inline-flex;align-items:center;gap:var(--s1);background:rgba(255,213,79,.08);color:var(--warn);font-size:var(--t1);padding:6px 12px;border-radius:var(--r-pill);border:none;margin-bottom:var(--s4);font-weight:500;letter-spacing:.2px}
.tools-row{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:4px} .tool-prereq::before{content:'';color:var(--warn);font-size:8px}
.tool-task-btn{background:linear-gradient(135deg,#1e293b,#0f172a);border:1px solid rgba(79,195,247,.3);color:#bdd4ff;padding:8px 16px;border-radius:8px;font-size:13px;cursor:pointer;transition:all .2s;font-family:inherit} .tool-step{margin-bottom:var(--s4)}
.tool-task-btn:hover:not(:disabled){background:linear-gradient(135deg,#2a3a5a,#1a2540);transform:translateY(-1px)} .tool-step-label{font-size:var(--t1);color:var(--text-faint);margin-bottom:var(--s2);text-transform:uppercase;letter-spacing:1.2px;font-weight:600}
.tool-task-btn:disabled{opacity:.5;cursor:not-allowed} .tools-row{display:flex;flex-wrap:wrap;gap:var(--s2);align-items:center}
.tool-task-btn.primary{background:linear-gradient(135deg,#1e3a5a,#0f2540);border-color:rgba(79,195,247,.5)} /* 默认按钮 — 安静 */
.tool-log-wrap{background:#000;border:1px solid rgba(255,255,255,.08);border-radius:6px;padding:10px 12px;font-family:Consolas,"Courier New",monospace;font-size:12px;color:#cfd8dc;line-height:1.4;max-height:240px;overflow:auto;white-space:pre-wrap;word-break:break-all;margin-top:12px} .tool-task-btn{
.tool-log-wrap:empty::before{content:"点击上方按钮开始任务,日志会实时显示";color:#555;font-style:italic} background:var(--surface);border:1px solid var(--border);
.tool-status{display:inline-block;font-size:12px;padding:3px 10px;border-radius:10px;margin-left:8px;vertical-align:middle} color:var(--text);padding:10px 18px;border-radius:var(--r2);
.tool-status.running{background:rgba(79,195,247,.15);color:#4fc3f7;border:1px solid rgba(79,195,247,.3)} font-size:var(--t3);cursor:pointer;transition:all .15s ease;
.tool-status.ok{background:rgba(76,175,80,.15);color:#81c784;border:1px solid rgba(76,175,80,.3)} font-family:inherit;font-weight:500;
.tool-status.err{background:rgba(244,67,54,.15);color:#ef9a9a;border:1px solid rgba(244,67,54,.3)} }
.tool-task-btn:hover:not(:disabled){background:var(--surface-hover);border-color:var(--border-strong);transform:translateY(-1px);box-shadow:var(--shadow-1)}
.tool-task-btn:active:not(:disabled){transform:translateY(0)}
.tool-task-btn:disabled{opacity:.4;cursor:not-allowed}
/* primary 按钮 — 真 primary, 实心渐变 + 阴影 */
.tool-task-btn.primary{
background:linear-gradient(135deg,#4fc3f7,#29b6f6);
border:none;color:#001528;font-weight:600;
box-shadow:0 4px 14px rgba(79,195,247,.35),inset 0 1px 0 rgba(255,255,255,.25);
padding:10px 22px;
}
.tool-task-btn.primary:hover:not(:disabled){
background:linear-gradient(135deg,#5fd0ff,#3fc4ff);
transform:translateY(-2px);
box-shadow:0 6px 20px rgba(79,195,247,.5),inset 0 1px 0 rgba(255,255,255,.3)
}
.tool-task-btn.primary:active:not(:disabled){transform:translateY(0);box-shadow:0 2px 8px rgba(79,195,247,.4)}
/* 日志框 */
.tool-log-wrap{
background:#05060a;border:1px solid var(--border);border-radius:var(--r2);
padding:var(--s3) var(--s4);
font-family:"JetBrains Mono","SF Mono",Consolas,"Courier New",monospace;
font-size:var(--t2);color:#cfd8dc;line-height:1.55;
max-height:240px;overflow:auto;white-space:pre-wrap;word-break:break-all;
margin-top:var(--s4);
box-shadow:inset 0 1px 3px rgba(0,0,0,.4);
}
.tool-log-wrap:empty::before{content:"点击上方按钮开始任务,日志会实时显示";color:var(--text-faint);font-style:italic}
.tool-status{display:inline-block;font-size:var(--t2);padding:4px 12px;border-radius:var(--r-pill);margin-left:var(--s2);vertical-align:middle;font-weight:500}
.tool-status.running{background:var(--accent-bg);color:var(--accent);border:1px solid rgba(79,195,247,.3)}
.tool-status.ok{background:rgba(76,175,80,.15);color:var(--success);border:1px solid rgba(76,175,80,.3)}
.tool-status.err{background:rgba(244,67,54,.15);color:var(--danger);border:1px solid rgba(244,67,54,.3)}
</style> </style>
</head> </head>
<body> <body>
@@ -1784,7 +1851,7 @@ a.msg-link{text-decoration:none;color:inherit}
<button class="tools-btn" onclick="toggleTools()" title="工具箱 (解密 / 导出 / 企业微信)">🛠️ 工具</button> <button class="tools-btn" onclick="toggleTools()" title="工具箱 (解密 / 导出 / 企业微信)">🛠️ 工具</button>
<button class="settings-btn" onclick="toggleSettings()" title="通知设置">⚙️</button> <button class="settings-btn" onclick="toggleSettings()" title="通知设置">⚙️</button>
</div> </div>
<div id="toolsPanel"> <div id="toolsPanel" class="show">
<div class="tool-tabs"> <div class="tool-tabs">
<button class="tool-tab active" data-pane="wechat">📱 个人微信</button> <button class="tool-tab active" data-pane="wechat">📱 个人微信</button>
<button class="tool-tab" data-pane="wxwork">🏢 企业微信</button> <button class="tool-tab" data-pane="wxwork">🏢 企业微信</button>