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:
1 parent
c39c3e7da4
commit
75c8ecd519
5 files changed
+73
-28
No files matched your search
@@ -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>
|
||||
|
||||
Reference in new issue
Block a user