From 61ee63e6dddd044f9848636341a246fce361502b Mon Sep 17 00:00:00 2001 From: 18628386721 <18628386721@163.com> Date: Thu, 30 Jul 2026 02:09:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E5=8D=A1=E6=94=B9=E7=9F=AE:?= =?UTF-8?q?=E5=9B=BE=E6=A0=87=E4=B8=80=E5=88=97=20+=20=E6=96=87=E5=AD=97?= =?UTF-8?q?=E4=B8=80=E6=A0=8F=E5=AF=B9=E9=BD=90,=E5=9B=BE=E6=A0=87?= =?UTF-8?q?=E6=81=92=E8=93=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit · **矮**:132 → 96px。状态不再是横穿整卡的填色带,收成一行小字 + 一枚钮; 段距、字号、图标各降一档。 · **对齐**:名字 / 厂商型号 / 状态**同一条竖线起头**,与图标并成一块 —— 从前名字与型号缩进 48px、状态条却横到卡边,三条线各走各的。 · **图标恒蓝**(与全站主色同一支 #3370ff,不再靛紫):它是**身份**不是状态 —— 能不能用由状态行说;先前未绑定就把图标灰掉,结果两条全灰、整片死气。 「去配置」改实心蓝:那是这张卡上唯一该点的东西。 交互复核:⋯ 菜单仍落在卡内(短卡下 top 跟着上移)、去配置开抽屉、Esc 关得掉。五闸绿。 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m --- apps/web/src/app/styles/console.css | 64 +++++++------- .../features/gov-system/internal/panel.tsx | 88 ++++++++++--------- 2 files changed, 81 insertions(+), 71 deletions(-) diff --git a/apps/web/src/app/styles/console.css b/apps/web/src/app/styles/console.css index 2674ee0..bdbe026 100644 --- a/apps/web/src/app/styles/console.css +++ b/apps/web/src/app/styles/console.css @@ -314,52 +314,56 @@ .g-fcard .probe { position: relative; z-index: 1; display: flex; align-items: flex-start; gap: 6px; padding: 8px 10px; border-radius: 9px; background: #f2f7f4; font: 500 11px/1.5 var(--sans); color: #0c8154; } .g-fcard .probe.bad { background: #fdf3f3; color: #a83a3a; } .g-fcard .probe svg { flex: none; margin-top: 2px; } -/* ── 模型实例卡 —— 卡面只回答一句:**这条现在能不能用** ———————————————— - 名字折行也不截(它是活动里声明的那个名);不可用时不摆统计,只留判词与去路。 */ -.g-mcard { position: relative; display: flex; flex-direction: column; gap: 9px; padding: 14px; background: var(--card); border: 1px solid #e2e8ef; border-radius: 12px; box-shadow: 0 1px 2px rgba(19, 28, 38, .04); transition: border-color var(--t-fast), box-shadow var(--t-fast), transform var(--t-fast); } -.g-mcard:hover { border-color: #c2cfde; box-shadow: 0 8px 22px -10px rgba(19, 28, 38, .18); transform: translateY(-1px); } +/* ── 模型实例卡(2026-07-30 定稿)—— 图标一列 + 文字一栏,矮而密 ———————— + 名字 / 型号 / 状态**同一条竖线起头**,与图标对齐成一块;状态不再横穿整卡。 + 卡面只回答一句:这条现在能不能用 —— 不能用就一句判词加一条去路,不摆假数字。 */ +.g-mcard { position: relative; display: flex; align-items: flex-start; gap: 11px; padding: 12px 13px; background: var(--card); border: 1px solid #e2e8ef; border-radius: 11px; box-shadow: 0 1px 2px rgba(19, 28, 38, .04); transition: border-color var(--t-fast), box-shadow var(--t-fast), transform var(--t-fast); } +.g-mcard:hover { border-color: #b9cbe6; box-shadow: 0 8px 20px -10px rgba(51, 112, 255, .22); transform: translateY(-1px); } .g-mcard.idle { background: #fbfcfd; } .g-mcard > .hit { position: absolute; inset: 0; z-index: 0; border: 0; background: transparent; cursor: pointer; } -.g-mcard > .hit:focus-visible { outline: 2px solid var(--brand); outline-offset: -3px; border-radius: 12px; } -.g-mcard > .hd, .g-mcard > .meta, .g-mcard > .bar, .g-mcard > .probe { position: relative; z-index: 1; } -.g-mcard > .hd, .g-mcard > .meta { pointer-events: none; } +.g-mcard > .hit:focus-visible { outline: 2px solid var(--brand); outline-offset: -3px; border-radius: 11px; } +.g-mcard > .ico, .g-mcard > .col { position: relative; z-index: 1; } +.g-mcard > .col { pointer-events: none; } .g-mcard button { pointer-events: auto; } -.g-mcard > .hd { display: flex; align-items: flex-start; gap: 10px; } -.g-mcard .ico { flex: none; width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center; color: #4954e6; background: #eef0fe; } -.g-mcard.idle .ico { color: #7c8797; background: #f0f3f7; } -/* 名字:两行内折行,不截断 —— 看不全等于没显示 */ -.g-mcard .nm { flex: 1; min-width: 0; font-size: 15px; font-weight: 700; line-height: 1.35; color: var(--ink); overflow-wrap: anywhere; } -.g-mcard .mbtn { flex: none; width: 26px; height: 26px; display: grid; place-items: center; border: 1px solid transparent; border-radius: 7px; background: transparent; color: var(--ink-4); cursor: pointer; } +/* 图标:恒蓝 —— 它是**身份**不是状态(能不能用由状态行说),灰掉只会让整片卡死气 */ +.g-mcard > .ico { flex: none; width: 34px; height: 34px; border-radius: 9px; display: grid; place-items: center; color: var(--brand); background: var(--brand-wash); box-shadow: inset 0 0 0 1px var(--brand-soft); } + +.g-mcard > .col { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; } +.g-mcard .ln { display: flex; align-items: flex-start; gap: 8px; } +/* 名字:折行也不截 —— 看不全等于没显示 */ +.g-mcard .nm { flex: 1; min-width: 0; font-size: 14px; font-weight: 700; line-height: 1.35; color: var(--ink); overflow-wrap: anywhere; } +.g-mcard .mbtn { flex: none; width: 22px; height: 22px; margin: -1px -3px 0 0; display: grid; place-items: center; border: 1px solid transparent; border-radius: 6px; background: transparent; color: var(--ink-4); cursor: pointer; } .g-mcard .mbtn:hover, .g-mcard .mbtn.on { border-color: var(--cline); background: #f3f6fa; color: var(--ink); } -.g-mcard > .meta { display: flex; align-items: center; gap: 7px; padding-left: 48px; margin-top: -6px; } -.g-mcard .prov { flex: none; padding: 2px 7px; border-radius: 4px; background: #eef1f6; font: 600 10.5px/1.5 var(--sans); color: #59677a; } -.g-mcard .mdl { min-width: 0; font: 500 11px/1.5 var(--mono); color: var(--ink-4); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.g-mcard .meta { display: flex; align-items: center; gap: 6px; min-width: 0; } +.g-mcard .prov { flex: none; padding: 1px 6px; border-radius: 4px; background: var(--brand-wash); font: 600 10px/1.6 var(--sans); color: var(--brand-deep); } +.g-mcard .mdl { min-width: 0; font: 500 10.5px/1.6 var(--mono); color: var(--ink-4); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -/* 状态条:能用就报端点与自检口;不能用就一句判词 + 一条去路 */ -.g-mcard > .bar { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-radius: 9px; background: #f6f8fb; font-size: 11.5px; color: var(--ink-3); } -.g-mcard > .bar.todo { background: #fdf5ec; color: #8a5f1c; font-weight: 600; } +/* 状态行:一行说完「连哪儿 / 能不能用」+ 一枚动作,不再是一整条填色带 */ +.g-mcard .st { display: flex; align-items: center; gap: 8px; margin-top: 3px; font-size: 11px; color: var(--ink-3); } +.g-mcard .st.todo { color: #8a5f1c; font-weight: 600; } .g-mcard .host { flex: 1; min-width: 0; font-family: var(--mono); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.g-mcard > .bar.todo > span { flex: 1; min-width: 0; } -.g-mcard .tbtn { flex: none; display: inline-flex; align-items: center; gap: 4px; height: 24px; padding: 0 9px; border: 1px solid var(--cline); border-radius: 6px; background: #fff; font: 600 11px/1 var(--sans); color: var(--ink-3); cursor: pointer; } -.g-mcard .tbtn:hover:not(:disabled) { border-color: #aebdcd; color: var(--ink); } -.g-mcard .tbtn.go { border-color: #dcc39a; color: #8a5f1c; } +.g-mcard .st.todo .host { font-family: var(--sans); } +.g-mcard .tbtn { flex: none; display: inline-flex; align-items: center; gap: 4px; height: 22px; padding: 0 8px; border: 1px solid var(--cline); border-radius: 6px; background: #fff; font: 600 10.5px/1 var(--sans); color: var(--ink-3); cursor: pointer; } +.g-mcard .tbtn:hover:not(:disabled) { border-color: var(--brand); color: var(--brand-deep); } +.g-mcard .tbtn.go { border-color: var(--brand); background: var(--brand); color: #fff; } +.g-mcard .tbtn.go:hover { border-color: var(--brand-deep); background: var(--brand-deep); color: #fff; } .g-mcard .tbtn:disabled { opacity: .5; cursor: default; } -.g-mcard > .probe { display: flex; align-items: flex-start; gap: 6px; padding: 8px 10px; border-radius: 9px; background: #f2f7f4; font: 500 11px/1.5 var(--sans); color: #0c8154; } -.g-mcard > .probe.bad { background: #fdf3f3; color: #a83a3a; } -.g-mcard > .probe svg { flex: none; margin-top: 2px; } +.g-mcard .probe { display: flex; align-items: flex-start; gap: 5px; margin-top: 4px; padding: 6px 8px; border-radius: 7px; background: #f2f7f4; font: 500 10.5px/1.5 var(--sans); color: #0c8154; } +.g-mcard .probe.bad { background: #fdf3f3; color: #a83a3a; } +.g-mcard .probe svg { flex: none; margin-top: 1px; } -.g-mcard .menu { position: absolute; right: 12px; top: 46px; z-index: 3; width: 168px; display: flex; flex-direction: column; gap: 2px; padding: 5px; border: 1px solid var(--cline); border-radius: 9px; background: #fff; box-shadow: 0 10px 22px -8px rgba(19, 28, 38, .3); } -.g-mcard .menu button { display: flex; align-items: center; gap: 6px; width: 100%; height: 27px; padding: 0 8px; border: 0; border-radius: 6px; background: transparent; font: 500 12px/1 var(--sans); color: var(--ink); text-align: left; cursor: pointer; } +.g-mcard .menu { position: absolute; right: 11px; top: 38px; z-index: 3; width: 168px; display: flex; flex-direction: column; gap: 2px; padding: 5px; border: 1px solid var(--cline); border-radius: 9px; background: #fff; box-shadow: 0 10px 22px -8px rgba(19, 28, 38, .3); } +.g-mcard .menu button { display: flex; align-items: center; gap: 6px; width: 100%; height: 26px; padding: 0 8px; border: 0; border-radius: 6px; background: transparent; font: 500 12px/1 var(--sans); color: var(--ink); text-align: left; cursor: pointer; } .g-mcard .menu button:hover { background: #f2f5f9; } .g-mcard .menu button.bad { color: var(--danger); } .g-mcard .menu button.bad:hover { background: #fdf2f2; } .g-mcard .menu .q { padding: 3px 8px 5px; font-size: 11px; line-height: 1.5; color: var(--ink-3); } -/* 幽灵卡:同尺寸虚线位 */ -.g-mcard.ghost { flex-direction: row; justify-content: center; align-items: center; gap: 7px; min-height: 132px; border-style: dashed; border-color: #c0cbd8; background: #fcfdfe; box-shadow: none; color: var(--ink-4); font: 600 12.5px/1 var(--sans); cursor: pointer; } +/* 幽灵卡:同高的虚线位 */ +.g-mcard.ghost { align-items: center; justify-content: center; gap: 7px; min-height: 88px; border-style: dashed; border-color: #c0cbd8; background: #fcfdfe; box-shadow: none; color: var(--ink-4); font: 600 12.5px/1 var(--sans); cursor: pointer; } .g-mcard.ghost:hover { border-color: var(--brand); background: var(--brand-wash); color: var(--brand-deep); box-shadow: none; transform: none; } /* ── 抽屉:一次性的配置岔路(接入模型 / 改绑定)—————————————————— diff --git a/apps/web/src/features/gov-system/internal/panel.tsx b/apps/web/src/features/gov-system/internal/panel.tsx index 48c3e68..1ed95e7 100644 --- a/apps/web/src/features/gov-system/internal/panel.tsx +++ b/apps/web/src/features/gov-system/internal/panel.tsx @@ -333,51 +333,57 @@ function LlmCard({ i, nav, onEdit, onDropped }: { i: LlmInstanceView; nav: Nav; return (
-
-
- {PROVIDER_LABEL[i.provider]} - {i.model} -
- - {usable ? ( -
- - {hostOf(i.baseUrl)} - -
- ) : ( - /* 不可用就别摆统计:一句判词 + 一条去路,比四个假数字有用 */ -
- {i.apiKeyBound ? '已停用' : '未绑定密钥 · 不可用'} - +
+ {PROVIDER_LABEL[i.provider]} + {i.model}
- )} - - {out !== undefined && ( -
- - {out.ok ? `连通 · ${out.latencyMs ?? '—'} ms` : (out.error ?? '不通')} +
+ {usable ? ( + <> + + {hostOf(i.baseUrl)} + + + + ) : ( + /* 不可用就别摆统计:一句判词 + 一条去路,比四个假数字有用 */ + <> + {i.apiKeyBound ? '已停用' : '未绑定密钥 · 不可用'} + + + )}
- )} + {out !== undefined && ( +
+ + {out.ok ? `连通 · ${out.latencyMs ?? '—'} ms` : (out.error ?? '不通')} +
+ )} +
{menu !== 'none' && (