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' && (