Skip to content

Commit 80fe2ae

Browse files
committed
feat(ui): mobile responsive admin pages and floating api key create modal with docs update
1 parent 6171b11 commit 80fe2ae

25 files changed

Lines changed: 950 additions & 155 deletions

README.cloudflare.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- **D1(SQLite)**:持久化 Tokens / API Keys / 管理员会话 / 配置 / 日志
88
- **KV**:缓存 `/images/*` 的图片/视频资源(从 `assets.grok.com` 代理抓取)
99
- **每天 0 点统一清除**:通过 KV `expiration` + Workers Cron 定时清理元数据(`wrangler.toml` 已配置,默认按北京时间 00:00)
10+
- **前端移动端适配一致生效**:Workers 与 FastAPI/Docker 复用同一套 `/static/*` 资源,包含手机端抽屉导航、表格横向滚动、API Key 居中悬浮新增弹窗等交互
1011

1112
> 原 Python/FastAPI 版本仍保留用于本地/Docker;Cloudflare 部署请按本文件走 Worker 版本。
1213
@@ -255,7 +256,11 @@ region = "aws:us-east-1"
255256
2. 管理页可访问性:
256257
- `GET /admin/token`
257258
- `GET /admin/keys`
258-
3. 可选 smoke test:
259+
3. 移动端回归(建议使用 `390x844`):
260+
- `/admin/keys`:点击“新增 Key”后应为居中悬浮弹窗(有遮罩,可点遮罩关闭,可 `Esc` 关闭)
261+
- 顶部导航:手机端应为抽屉菜单(可打开/关闭,点击菜单项后自动收起)
262+
- Token/Keys/Cache 表格:应保持横向滚动,不应压碎列布局
263+
4. 可选 smoke test:
259264

260265
```bash
261266
python scripts/smoke_test.py --base-url https://<你的域名或workers.dev>

app/static/cache/cache.css

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,3 +302,61 @@
302302
box-shadow: 0 0 0 2px #000;
303303
border-color: #000;
304304
}
305+
306+
@media (max-width: 768px) {
307+
.cache-card {
308+
padding: 12px 12px;
309+
}
310+
311+
.cache-card .flex.items-start.justify-between {
312+
flex-wrap: wrap;
313+
gap: 8px;
314+
}
315+
316+
.cache-stat-value {
317+
font-size: 18px;
318+
}
319+
320+
#batch-actions {
321+
left: 12px;
322+
right: 12px;
323+
bottom: 10px;
324+
transform: none;
325+
border-radius: 12px;
326+
padding: 10px;
327+
gap: 10px;
328+
flex-wrap: wrap;
329+
cursor: default;
330+
}
331+
332+
#batch-actions .toolbar-sep {
333+
display: none;
334+
}
335+
336+
#batch-actions .batch-actions-meta,
337+
#batch-actions #batch-progress {
338+
width: 100%;
339+
justify-content: space-between;
340+
}
341+
342+
#batch-actions .batch-actions-buttons {
343+
width: 100%;
344+
display: grid;
345+
grid-template-columns: repeat(2, minmax(0, 1fr));
346+
gap: 8px;
347+
}
348+
349+
#batch-actions .batch-actions-buttons button {
350+
width: 100%;
351+
}
352+
353+
#batch-actions #batch-progress {
354+
order: 3;
355+
align-items: center;
356+
flex-wrap: wrap;
357+
}
358+
359+
#batch-actions #batch-progress .toolbar-sep {
360+
display: none;
361+
}
362+
}

app/static/cache/cache.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<link href="https://cdn.jsdelivr.net/npm/geist@1.0.0/dist/fonts/geist-sans/style.css" rel="stylesheet">
1111
<link href="https://cdn.jsdelivr.net/npm/geist@1.0.0/dist/fonts/geist-mono/style.css" rel="stylesheet">
1212
<link href="/static/common/common.css" rel="stylesheet">
13-
<link href="/static/cache/cache.css?v=3" rel="stylesheet">
13+
<link href="/static/cache/cache.css?v=4" rel="stylesheet">
1414
<link href="/static/common/toast.css" rel="stylesheet">
1515
</head>
1616

@@ -19,7 +19,7 @@
1919

2020
<main class="space-y-6 flex-1 container mx-auto max-w-4xl px-6 py-8 fade-in">
2121
<div class="space-y-6">
22-
<div class="flex justify-between items-center">
22+
<div class="flex flex-wrap justify-between items-start gap-3">
2323
<div>
2424
<h2 class="text-2xl font-semibold tracking-tight">缓存管理</h2>
2525
<p class="text-[var(--accents-4)] mt-1 text-sm">管理本地资源与在线资产缓存。</p>
@@ -151,13 +151,13 @@ <h2 class="text-2xl font-semibold tracking-tight">缓存管理</h2>
151151

152152
<div id="batch-actions"
153153
class="fixed bottom-8 left-1/2 -translate-x-1/2 z-20 bg-white border border-[var(--border)] rounded-full px-3 py-2 flex items-center shadow-lg gap-3 cursor-move select-none active:cursor-grabbing whitespace-nowrap">
154-
<div class="text-sm font-medium flex items-center gap-2">
154+
<div class="batch-actions-meta text-sm font-medium flex items-center gap-2">
155155
<span class="text-[var(--accents-5)] text-xs">已选择</span>
156156
<span class="bg-black text-white text-xs px-1.5 py-0.5 rounded-full" id="selected-count">0</span>
157157
<span class="text-[var(--accents-5)] text-xs"></span>
158158
</div>
159159
<span class="toolbar-sep"></span>
160-
<div class="flex items-center gap-1">
160+
<div class="batch-actions-buttons flex items-center gap-1">
161161
<button id="btn-load-stats" onclick="handleLoadClick()"
162162
class="geist-button-outline text-xs px-3 gap-1 border-0 hover:bg-gray-100">
163163
加载

app/static/chat/chat.css

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,16 @@
104104
.composer-row {
105105
display: flex;
106106
align-items: center;
107+
flex-wrap: wrap;
107108
gap: 10px;
108109
margin-bottom: 10px;
109110
}
110111

112+
.composer-primary {
113+
flex-shrink: 0;
114+
min-width: 86px;
115+
}
116+
111117
.attach-preview {
112118
display: grid;
113119
grid-template-columns: repeat(4, minmax(0, 1fr));
@@ -161,3 +167,74 @@
161167
display: block;
162168
}
163169

170+
@media (max-width: 1024px) {
171+
.chat-messages {
172+
height: 360px;
173+
}
174+
175+
.results-grid {
176+
grid-template-columns: repeat(2, minmax(0, 1fr));
177+
}
178+
}
179+
180+
@media (max-width: 767px) {
181+
.chat-public-header {
182+
height: auto;
183+
min-height: 56px;
184+
align-items: flex-start;
185+
flex-wrap: wrap;
186+
gap: 8px;
187+
padding-top: 8px;
188+
padding-bottom: 8px;
189+
}
190+
191+
.chat-top-actions {
192+
width: 100%;
193+
flex-wrap: wrap;
194+
}
195+
196+
.chat-top-actions a {
197+
flex: 1 1 140px;
198+
}
199+
200+
.chat-messages {
201+
height: 300px;
202+
padding: 10px;
203+
}
204+
205+
.msg {
206+
flex-direction: column;
207+
gap: 6px;
208+
}
209+
210+
.msg-role {
211+
width: auto;
212+
font-size: 11px;
213+
}
214+
215+
.composer-row {
216+
align-items: stretch;
217+
gap: 8px;
218+
}
219+
220+
#chat-attach-info,
221+
#video-attach-info {
222+
width: 100%;
223+
}
224+
225+
.composer-row .flex-1 {
226+
display: none;
227+
}
228+
229+
.composer-primary {
230+
width: 100%;
231+
}
232+
233+
.attach-preview {
234+
grid-template-columns: repeat(2, minmax(0, 1fr));
235+
}
236+
237+
.results-grid {
238+
grid-template-columns: 1fr;
239+
}
240+
}

app/static/chat/chat.html

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,20 @@
1010
<link href="https://cdn.jsdelivr.net/npm/geist@1.0.0/dist/fonts/geist-sans/style.css" rel="stylesheet">
1111
<link href="https://cdn.jsdelivr.net/npm/geist@1.0.0/dist/fonts/geist-mono/style.css" rel="stylesheet">
1212
<link href="/static/common/common.css" rel="stylesheet">
13-
<link href="/static/chat/chat.css?v=1" rel="stylesheet">
13+
<link href="/static/chat/chat.css?v=2" rel="stylesheet">
1414
<link href="/static/common/toast.css" rel="stylesheet">
1515
</head>
1616

1717
<body class="min-h-screen flex flex-col" style="background-color: var(--bg);">
1818
<div id="toast-container" class="toast-container"></div>
1919

2020
<header class="border-b border-[var(--border)] bg-[var(--bg)]/80 backdrop-blur-md sticky top-0 z-10">
21-
<div class="max-w-5xl mx-auto px-6 h-14 flex items-center justify-between">
21+
<div class="chat-public-header max-w-5xl mx-auto px-6 h-14 flex items-center justify-between">
2222
<div class="flex items-center gap-3">
2323
<div class="font-semibold tracking-tight">Grok2API</div>
2424
<div class="text-xs text-[var(--accents-5)]">在线聊天</div>
2525
</div>
26-
<div class="flex items-center gap-2">
26+
<div class="chat-top-actions flex items-center gap-2">
2727
<a href="/login" class="geist-button-outline text-xs px-3 py-1.5">后台登录</a>
2828
<a href="/admin/chat" class="geist-button-outline text-xs px-3 py-1.5">后台聊天</a>
2929
</div>
@@ -47,7 +47,7 @@
4747
<input id="stream-toggle" type="checkbox" class="checkbox" checked>
4848
<label for="stream-toggle" class="text-sm">Stream</label>
4949
</div>
50-
<div class="col-span-6 md:col-span-2 flex justify-end gap-2">
50+
<div class="col-span-12 md:col-span-2 flex flex-wrap justify-end gap-2">
5151
<button class="geist-button-outline text-xs px-3" onclick="saveApiKey()">保存</button>
5252
<button class="geist-button-danger text-xs px-3" onclick="clearApiKey()">清除</button>
5353
</div>
@@ -70,7 +70,7 @@
7070
<button class="geist-button-outline text-xs px-3" onclick="pickChatImage()">上传图片</button>
7171
<div id="chat-attach-info" class="text-xs text-[var(--accents-5)]"></div>
7272
<div class="flex-1"></div>
73-
<button class="geist-button text-xs px-4" onclick="sendChat()">发送</button>
73+
<button class="geist-button text-xs px-4 composer-primary" onclick="sendChat()">发送</button>
7474
</div>
7575
<textarea id="chat-input" class="geist-input h-24" placeholder="输入消息..."></textarea>
7676
<div id="chat-attach-preview" class="attach-preview hidden"></div>
@@ -142,7 +142,7 @@
142142
<button class="geist-button-outline text-xs px-3" onclick="pickVideoImage()">上传参考图(可选)</button>
143143
<div id="video-attach-info" class="text-xs text-[var(--accents-5)]"></div>
144144
<div class="flex-1"></div>
145-
<button class="geist-button text-xs px-4" onclick="generateVideo()">生成视频</button>
145+
<button class="geist-button text-xs px-4 composer-primary" onclick="generateVideo()">生成视频</button>
146146
</div>
147147
<div id="video-attach-preview" class="attach-preview hidden"></div>
148148
</div>
@@ -157,4 +157,3 @@
157157
</body>
158158

159159
</html>
160-

app/static/chat/chat_admin.html

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<link href="https://cdn.jsdelivr.net/npm/geist@1.0.0/dist/fonts/geist-sans/style.css" rel="stylesheet">
1111
<link href="https://cdn.jsdelivr.net/npm/geist@1.0.0/dist/fonts/geist-mono/style.css" rel="stylesheet">
1212
<link href="/static/common/common.css" rel="stylesheet">
13-
<link href="/static/chat/chat.css?v=1" rel="stylesheet">
13+
<link href="/static/chat/chat.css?v=2" rel="stylesheet">
1414
<link href="/static/common/toast.css" rel="stylesheet">
1515
<script>
1616
window.__CHAT_ADMIN__ = true;
@@ -38,7 +38,7 @@
3838
<input id="stream-toggle" type="checkbox" class="checkbox" checked>
3939
<label for="stream-toggle" class="text-sm">Stream</label>
4040
</div>
41-
<div class="col-span-6 md:col-span-2 flex justify-end gap-2">
41+
<div class="col-span-12 md:col-span-2 flex flex-wrap justify-end gap-2">
4242
<button class="geist-button-outline text-xs px-3" onclick="saveApiKey()">保存</button>
4343
<button class="geist-button-danger text-xs px-3" onclick="clearApiKey()">清除</button>
4444
</div>
@@ -61,7 +61,7 @@
6161
<button class="geist-button-outline text-xs px-3" onclick="pickChatImage()">上传图片</button>
6262
<div id="chat-attach-info" class="text-xs text-[var(--accents-5)]"></div>
6363
<div class="flex-1"></div>
64-
<button class="geist-button text-xs px-4" onclick="sendChat()">发送</button>
64+
<button class="geist-button text-xs px-4 composer-primary" onclick="sendChat()">发送</button>
6565
</div>
6666
<textarea id="chat-input" class="geist-input h-24" placeholder="输入消息..."></textarea>
6767
<div id="chat-attach-preview" class="attach-preview hidden"></div>
@@ -133,7 +133,7 @@
133133
<button class="geist-button-outline text-xs px-3" onclick="pickVideoImage()">上传参考图(可选)</button>
134134
<div id="video-attach-info" class="text-xs text-[var(--accents-5)]"></div>
135135
<div class="flex-1"></div>
136-
<button class="geist-button text-xs px-4" onclick="generateVideo()">生成视频</button>
136+
<button class="geist-button text-xs px-4 composer-primary" onclick="generateVideo()">生成视频</button>
137137
</div>
138138
<div id="video-attach-preview" class="attach-preview hidden"></div>
139139
</div>
@@ -153,4 +153,3 @@
153153
</body>
154154

155155
</html>
156-

app/static/common/admin-auth.js

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -226,18 +226,20 @@ function formatStorageLabel(type) {
226226
}
227227

228228
async function updateStorageModeButton() {
229-
const btn = document.getElementById('storage-mode-btn');
230-
if (!btn) return;
231-
btn.textContent = '...';
232-
btn.title = '存储模式';
233-
btn.classList.remove('storage-ready');
229+
const buttons = Array.from(document.querySelectorAll('#storage-mode-btn, [data-storage-mode-btn]'));
230+
if (!buttons.length) return;
231+
buttons.forEach((btn) => {
232+
btn.textContent = '...';
233+
btn.title = '存储模式';
234+
btn.classList.remove('storage-ready');
235+
});
234236
const storageType = await fetchStorageType();
235237
const label = formatStorageLabel(storageType);
236-
btn.textContent = label === '-' ? label : label.toUpperCase();
237-
btn.title = '存储模式';
238-
if (label !== '-') {
239-
btn.classList.add('storage-ready');
240-
}
238+
buttons.forEach((btn) => {
239+
btn.textContent = label === '-' ? label : label.toUpperCase();
240+
btn.title = '存储模式';
241+
if (label !== '-') btn.classList.add('storage-ready');
242+
});
241243
}
242244

243245
if (document.readyState === 'loading') {

0 commit comments

Comments
 (0)