fix: 列表页卡片与表格的切换点抬到 lg,补上用户管理的移动端视图

手机上"溢出屏幕"的两个真因(375 / 769 / 1100 三档实测):

- 卡片↔表格切换卡在 md(768px),而侧栏也在 768 出现,吃掉 224px 加 48px 内边距后
  内容区只剩 495px,渠道表自然宽 1097px —— 横屏手机、小平板、分屏(768~1023)
  整张表被塞进 495px 的盒子里横向滚。改为 lg(1024px) 起才用表格;769px 实测
  可见表格数为 0、docSW 等于视口宽。
- 用户管理页只有 720px 的表格、没有卡片回退,手机上只能横向滑整张表。
  补移动端卡片(用户名/邮箱、角色与状态、ID/余额/注册时间、编辑与调余额),
  表格改 hidden lg:block;1100px 实测 client=826 / scroll=826,铺满不滚。
- 顺带修掉上次改动留下的挤列问题:渠道表"近N小时"列被表格自动布局压到 95px
  (min-w 只约束整表、不保证单列),四个指标折成 4 行。该列加 min-w-[190px]、
  指标拆两行,并把 Key 并入名称行、启用并入健康行,8 列减到 6 列
  (colspan 同步 7→6),自然宽 1125 → 1053。

编辑渠道弹窗 375px 下无溢出元素。遗留:渠道表在 1024~1325 视口仍需容器内横向滚动
(6 列数据表的固有行为,不动整页),卡片列表在 640~1023 仍是单列。
This commit is contained in:
Sakurasan committed 2026-10-19 09:25:52 +08:00
1 parent c39c3e7da4
commit 75c8ecd519
5 files changed
+73 -28

No files matched your search

+21 -21
View File
@@ -219,7 +219,7 @@ onMounted(load)
</div>
<!-- 移动端:卡片列表 -->
<div class="space-y-3 md:hidden">
<div class="space-y-3 lg:hidden">
<div v-for="{ ch, st } in rows" :key="ch.id" class="card p-4" :class="ch.enabled ? 'bg-accent-soft' : ''">
<div class="flex flex-wrap items-start justify-between gap-2">
<div class="min-w-0">
@@ -291,30 +291,29 @@ onMounted(load)
</p>
</div>
<!-- 桌面端:表格 -->
<div class="card hidden md:block">
<!-- 桌面端:表格(lg 起才显示:md 断点下侧栏一出现内容区只剩 ~495px,表格会被挤成横向滚动) -->
<div class="card hidden lg:block">
<div class="overflow-x-auto">
<table class="w-full text-sm min-w-[920px]">
<table class="w-full text-sm min-w-[860px]">
<thead>
<tr class="border-b border-edge text-left text-xs text-muted">
<th scope="col" class="px-4 py-2.5 font-medium">名称</th>
<th scope="col" class="px-4 py-2.5 font-medium">名称 / Key</th>
<th scope="col" class="px-4 py-2.5 font-medium">API 格式</th>
<th scope="col" class="px-4 py-2.5 font-medium">Base URL</th>
<th scope="col" class="px-4 py-2.5 font-medium">Key</th>
<th scope="col" class="px-4 py-2.5 font-medium">健康</th>
<th scope="col" class="px-4 py-2.5 font-medium">启用</th>
<th scope="col" class="px-4 py-2.5 font-medium">近{{ statWindows.find((w) => w.v === statWindow)?.label }}</th>
<th scope="col" class="px-4 py-2.5 font-medium">健康 / 状态</th>
<th scope="col" class="min-w-[190px] px-4 py-2.5 font-medium">近{{ statWindows.find((w) => w.v === statWindow)?.label }}</th>
<th scope="col" class="px-4 py-2.5" />
</tr>
</thead>
<tbody>
<template v-for="{ ch, st } in rows" :key="ch.id">
<tr class="table-row" :style="ch.enabled ? { borderLeft: '2px solid var(--color-accent)' } : {}">
<td class="px-4 py-2.5">
<button class="inline-flex items-center gap-1.5 text-ink transition hover:text-accent" @click="toggleDrawer(ch)">
<td class="max-w-[240px] px-4 py-2.5">
<button class="flex items-center gap-1.5 text-left text-ink transition hover:text-accent" @click="toggleDrawer(ch)">
<span class="truncate">{{ ch.name }}</span>
<PhCaretDown :size="12" class="shrink-0 text-muted transition-transform" :class="expandedId === ch.id ? 'rotate-180' : ''" />
</button>
<p class="mt-0.5 truncate font-mono text-[11px] text-muted">{{ ch.api_key_masked || '****' }}</p>
</td>
<td class="px-4 py-2.5">
<div class="flex flex-col gap-0.5">
@@ -325,26 +324,27 @@ onMounted(load)
>{{ protocolShort(f) }}</code>
</div>
</td>
<td class="max-w-[220px] truncate px-4 py-2.5 font-mono text-xs text-muted">{{ ch.base_url }}</td>
<td class="px-4 py-2.5 font-mono text-xs text-muted">{{ ch.api_key_masked || '****' }}</td>
<td class="max-w-[200px] truncate px-4 py-2.5 font-mono text-xs text-muted">{{ ch.base_url }}</td>
<td class="px-4 py-2.5">
<Badge :variant="ch.health_status === 'healthy' ? 'ok' : ch.health_status === 'cooldown' ? 'err' : 'warn'">
{{ ch.health_status }}
</Badge>
<div class="flex flex-wrap items-center gap-1">
<Badge :variant="ch.health_status === 'healthy' ? 'ok' : ch.health_status === 'cooldown' ? 'err' : 'warn'">
{{ ch.health_status }}
</Badge>
<Badge :variant="ch.enabled ? 'neutral' : 'err'">{{ ch.enabled ? '启用' : '停用' }}</Badge>
</div>
<div
v-if="runtimeHint(ch)"
class="mt-1 font-mono text-[11px]"
:class="ch.cooldown_seconds > 0 ? 'text-err' : 'text-warn'"
>{{ runtimeHint(ch) }}</div>
</td>
<td class="px-4 py-2.5 text-xs text-muted">{{ ch.enabled ? '是' : '否' }}</td>
<td class="px-4 py-2.5">
<div v-if="st" class="flex flex-col gap-0.5 text-[11px] leading-4 text-muted">
<span class="mono-num">
{{ st.requests }} 次 ·
<span :class="st.err_rate > 0.05 ? 'text-err' : ''">成功 {{ successRate(st) }}</span> ·
P95 {{ fmtMs(st.p95_ms, st.success > 0) }} · {{ fmtCost(st.cost) }}
<span :class="st.err_rate > 0.05 ? 'text-err' : ''">成功 {{ successRate(st) }}</span>
</span>
<span class="mono-num">P95 {{ fmtMs(st.p95_ms, st.success > 0) }} · {{ fmtCost(st.cost) }}</span>
<span v-if="st.top_errors.length" class="font-mono text-[10px] text-err">
{{ st.top_errors.map((e) => `${e.code}×${e.count}`).join(' ') }}
</span>
@@ -378,13 +378,13 @@ onMounted(load)
</td>
</tr>
<tr v-if="expandedId === ch.id" class="bg-surface/40">
<td colspan="7" class="px-4 py-3">
<td colspan="6" class="px-4 py-3">
<ChannelModelsDrawer :channel="ch" />
</td>
</tr>
</template>
<tr v-if="channels.length === 0">
<td colspan="7" class="px-4 py-10 text-center text-sm text-muted">还没有渠道,点击「添加渠道」</td>
<td colspan="6" class="px-4 py-10 text-center text-sm text-muted">还没有渠道,点击「添加渠道」</td>
</tr>
</tbody>
</table>
+2 -2
View File
@@ -135,7 +135,7 @@ onMounted(load)
<div class="card">
<!-- 移动端:卡片列表 -->
<div class="space-y-3 p-3 md:hidden">
<div class="space-y-3 p-3 lg:hidden">
<div v-for="l in logs" :key="l.id" class="card p-3">
<div class="flex items-start justify-between gap-2">
<div class="min-w-0">
@@ -159,7 +159,7 @@ onMounted(load)
</div>
<!-- 桌面端:表格 -->
<div class="hidden overflow-x-auto md:block">
<div class="hidden overflow-x-auto lg:block">
<table class="w-full text-sm min-w-[760px]">
<thead>
<tr class="border-b border-edge text-left text-xs text-muted">
+46 -1
View File
@@ -140,7 +140,52 @@ onMounted(load)
</div>
<div class="card">
<div class="overflow-x-auto">
<!-- 移动端:卡片列表(这页原本只有表格,手机上得横向滑动整个 720px 表格) -->
<div class="space-y-3 p-3 lg:hidden">
<div v-for="u in users" :key="u.id" class="card p-3">
<div class="flex items-start justify-between gap-2">
<div class="min-w-0">
<p class="truncate text-sm font-medium text-ink">
{{ u.username }}
<span v-if="u.id === auth.user?.id" class="text-[11px] text-muted">(我)</span>
</p>
<p class="truncate font-mono text-xs text-muted">{{ u.email }}</p>
</div>
<div class="flex shrink-0 flex-col items-end gap-1">
<Badge :variant="u.role === 'admin' ? 'accent' : 'neutral'">{{ u.role }}</Badge>
<Badge :variant="u.status === 'active' ? 'ok' : 'warn'">{{ u.status }}</Badge>
</div>
</div>
<div class="mt-2.5 space-y-1.5 text-xs">
<p class="flex items-baseline justify-between gap-3">
<span class="shrink-0 text-muted">ID</span>
<span class="mono-num text-ink">{{ u.id }}</span>
</p>
<p class="flex items-baseline justify-between gap-3">
<span class="shrink-0 text-muted">余额</span>
<span class="mono-num text-accent">{{ fmtMoney(u.balance) }}</span>
</p>
<p class="flex items-baseline justify-between gap-3">
<span class="shrink-0 text-muted">注册时间</span>
<span class="mono-num truncate text-ink">{{ fmtTime(u.created_at) }}</span>
</p>
</div>
<div class="mt-2.5 flex gap-2 border-t border-edge pt-2.5">
<Button size="sm" variant="ghost" class="flex-1" @click="openEdit(u)">
<PhNotePencil :size="13" weight="bold" />
编辑
</Button>
<Button size="sm" variant="ghost" class="flex-1" @click="openBalance(u)">
<PhCoins :size="13" />
调余额
</Button>
</div>
</div>
<p v-if="users.length === 0" class="px-4 py-8 text-center text-sm text-muted">无用户</p>
</div>
<!-- 桌面端:表格 -->
<div class="hidden overflow-x-auto lg:block">
<table class="w-full text-sm min-w-[720px]">
<thead>
<tr class="border-b border-edge text-left text-xs text-muted">
+2 -2
View File
@@ -157,7 +157,7 @@ onMounted(load)
<div class="card">
<!-- 移动端:卡片列表 -->
<div class="space-y-3 p-3 md:hidden">
<div class="space-y-3 p-3 lg:hidden">
<div v-for="k in keys" :key="k.id" class="card p-3">
<div class="flex items-center justify-between gap-2">
<div class="min-w-0">
@@ -196,7 +196,7 @@ onMounted(load)
</div>
<!-- 桌面端:表格 -->
<div class="hidden overflow-x-auto md:block">
<div class="hidden overflow-x-auto lg:block">
<table class="w-full text-sm min-w-[680px]">
<thead>
<tr class="border-b border-edge text-left text-xs text-muted">
+2 -2
View File
@@ -109,7 +109,7 @@ onMounted(load)
</div>
<!-- 移动端:卡片列表 -->
<div class="space-y-3 p-3 md:hidden">
<div class="space-y-3 p-3 lg:hidden">
<div v-for="l in logs" :key="l.id" class="card p-3">
<div class="flex items-start justify-between gap-2">
<div class="min-w-0">
@@ -131,7 +131,7 @@ onMounted(load)
</div>
<!-- 桌面端:表格 -->
<div class="hidden overflow-x-auto md:block">
<div class="hidden overflow-x-auto lg:block">
<table class="w-full text-sm min-w-[640px]">
<thead>
<tr class="border-b border-edge text-left text-xs text-muted">