Commit 4eb9d804 by luoqi

fix(web): 全局 border 规则放进 @layer base —— 全站 258 处彩色 border 类此前一个都没生效

globals.css 里那条 `* { border-color: hsl(var(--border)) }` **不在任何 @layer 里**。
无层级样式整体胜过 `@layer utilities`(与优先级无关),于是它压过了 Tailwind
**全部** border 颜色工具类:写 `border-l-brand-400` / `border-rose-300` 不报错、
也不生效,算出来永远是那个灰 #D6DDE6。实测登录框 91 个有边框的元素,91 个都是灰的。

被吃掉的不只是"某处颜色不对":
  - `border-t-transparent` —— 两处转圈 loading 的缺口被填成灰,整圈同色,转了看不出来
  - `border-current` —— 本该跟随文字色调,恒为灰
  - ~32 处 hover:/focus: 边框反馈全哑,配着 `transition-colors` 动画一个空
  - chain-viz / facts-timeline 整套语义色板的 border 分支形同虚设

改法:把这条规则包进 `@layer base`。
️ Tailwind v4 没有 `--default-border-color` 主题变量(v3 的 borderColor.DEFAULT
已移除,preflight 改成 `border: 0 solid`),分层的 `*` 规则就是 v4 官方写法。

同时删掉 147 处中性 `border-slate-*` / `divide-slate-*` 类(31 个文件):
它们此前零效果,而分层后会**开始**生效 —— `border-slate-100`(114 处)会从
#D6DDE6 变 #F1F5F9(对白底对比度 1.30→1.06,发丝线几乎消失),`slate-50` 直接看不见。
这些类当初就是对着灰写的(该 `*` 规则从首次提交就在),删掉后落回 base 层默认灰,
**构造上保证零像素变化**。三个页面实测:中性边框数量与颜色与改前逐一对齐,
彩色边框恢复(brand-600 = rgb(0,50,160)、brand-400 = rgb(59,118,247))。

batch-tracking 那条竖轨收回工具类(`border-l-2 border-l-brand-400`),
连同解释这条地雷的注释一起删;`past` 保留 2px 透明边以维持两组首列对齐。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
parent 73cf9501
......@@ -111,7 +111,7 @@ function EntryResolver() {
<div className="flex h-screen flex-col overflow-hidden bg-slate-50">
{/* 空态 header —— 内容只有 PAC 品牌 + 用户三件套,无功能项;嵌入宿主时整体隐藏 */}
{!isEmbedded() && (
<header className="flex h-12 flex-none items-center gap-2 border-b border-slate-100 bg-white px-4">
<header className="flex h-12 flex-none items-center gap-2 border-b bg-white px-4">
<span className="inline-flex h-7 w-7 items-center justify-center rounded-md bg-brand-600 text-[12px] font-bold text-white">
PAC
</span>
......@@ -154,7 +154,7 @@ function EntryResolver() {
<button
type="button"
onClick={() => window.location.reload()}
className="inline-flex items-center gap-1.5 rounded-md border border-slate-100 bg-white px-3 py-1.5 text-[12.5px] text-slate-600 hover:bg-slate-50"
className="inline-flex items-center gap-1.5 rounded-md border bg-white px-3 py-1.5 text-[12.5px] text-slate-600 hover:bg-slate-50"
>
<RefreshCw className="h-3.5 w-3.5" />
刷新
......
......@@ -114,15 +114,21 @@
/* 全局 border 颜色 — 这样写不用每处都标 border-border
*
* 🔴 **这条规则不在任何 @layer 里,所以它压过 Tailwind 的 `border-*` 颜色工具类**
* (无层级的样式整体胜过 `@layer utilities`,与优先级无关)。
* ⇒ 组件里写 `border-l-brand-400` / `border-rose-300` 之类**不会报错、也不会生效**,
* 算出来永远是这里这个灰(2026-08-11 在主管工作台那条竖轨上实测踩到)。
* 要给某处上**彩色边框**,走 inline style 或 `@utility`,⛔ 别指望工具类。
* ⚠️ 想把这条改成分层的(让工具类重新生效)要整站回归:现在全站的 `border-xxx-100`
* 之类**都**落在这个灰上,一改就全变。 */
* {
* ⚠️ **必须待在 `@layer base` 里**(2026-08-11 修)。原来它是**无层级**的裸 `*` 规则,
* 而无层级样式整体胜过 `@layer utilities`(与优先级无关)—— 于是它压过了 Tailwind
* **全部** `border-*` 颜色工具类:组件里写 `border-l-brand-400` / `border-rose-300`
* 不报错、也不生效,算出来永远是这个灰。实测全站 258 处彩色 border 类无一生效,
* 连 `border-t-transparent`(转圈 loading 的缺口)和 `border-current` 都被吃掉。
* 放进 base 层后 utilities 重新压得过它,工具类恢复正常。
*
* ⛔ 别再把它挪出 `@layer`,也别加 `!important` —— 那等于把这个坑原样刨回来。
* 备注:Tailwind v4 没有 `--default-border-color` 这个主题变量(v3 的
* `borderColor.DEFAULT` 在 v4 已移除,preflight 改成 `border: 0 solid` = currentColor),
* 所以「分层的 `*` 规则」就是 v4 官方给的写法,不存在更声明式的替代。 */
@layer base {
* {
border-color: hsl(var(--border));
}
}
body {
......
......@@ -27,7 +27,7 @@ export default function PetLabPage() {
<figure
key={p}
data-pet-perch
className="flex flex-col items-center gap-2 rounded-xl border border-slate-100 bg-white p-4 shadow-sm"
className="flex flex-col items-center gap-2 rounded-xl border bg-white p-4 shadow-sm"
>
<PetBody pose={p} glanceSeq={p === 'idle' ? glance : 0} size={96} />
<figcaption className="text-xs text-slate-500">{p}</figcaption>
......@@ -36,7 +36,7 @@ export default function PetLabPage() {
{/* 涂氟护盾(与姿态正交的 buff,叠在 idle 上展示) */}
<figure
data-pet-perch
className="flex flex-col items-center gap-2 rounded-xl border border-slate-100 bg-white p-4 shadow-sm"
className="flex flex-col items-center gap-2 rounded-xl border bg-white p-4 shadow-sm"
>
<PetBody pose="idle" glanceSeq={0} shield size={96} />
<figcaption className="text-xs text-slate-500">shield(涂氟护盾)</figcaption>
......@@ -136,7 +136,7 @@ function LabBtn({ label, onClick }: { label: string; onClick: () => void }) {
<button
type="button"
onClick={onClick}
className="rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-slate-700 shadow-sm hover:bg-slate-50"
className="rounded-lg border bg-white px-3 py-1.5 text-sm text-slate-700 shadow-sm hover:bg-slate-50"
>
{label}
</button>
......
......@@ -957,7 +957,7 @@ export function AssignmentConfirmSheet({
className="overflow-hidden border-brand-100 text-[12px] shadow-sm"
>
{/* ① 汇总 */}
<div className="flex items-center gap-2 border-b border-slate-100 bg-brand-50/50 px-3 py-2">
<div className="flex items-center gap-2 border-b bg-brand-50/50 px-3 py-2">
<Users className="h-3.5 w-3.5 flex-none text-brand-600" />
{/*
⚠️ 有待分配时必须**先给总数**,再拆两半。
......@@ -1040,14 +1040,14 @@ export function AssignmentConfirmSheet({
和这里问的"这活多大"不是一回事。
*/}
{sheet.opsNote && (
<div className="whitespace-pre-line border-b border-slate-100 bg-slate-50/70 px-3 py-2 text-[11.5px] leading-relaxed text-slate-700">
<div className="whitespace-pre-line border-b bg-slate-50/70 px-3 py-2 text-[11.5px] leading-relaxed text-slate-700">
<Emphasized text={sheet.opsNote} />
</div>
)}
{/* ② 按客服(产能视图)—— 这一层是主管真正在看的 */}
<CardContent className="p-0">
<div className="divide-y divide-slate-50">
<div className="divide-y">
{groups.map((g) => {
const isOpen = open.has(g.userId);
return (
......@@ -1384,7 +1384,7 @@ export function AssignmentConfirmSheet({
(水位法没轮到)或被精调成 0 名额。 */}
{/* ⚠️ 拖过去之后他就有条目了 —— 从这行剔掉,否则同一个人既在上面成组、又在"未分到"里 */}
{idleAgents.length > 0 && (
<div className="border-t border-slate-100 px-3 py-1.5 text-[10.5px] text-slate-400">
<div className="border-t px-3 py-1.5 text-[10.5px] text-slate-400">
本批未分到:{idleAgents.map((a) => `${a.name ?? a.userId}(在手 ${a.inHand})`).join('、')}
</div>
)}
......@@ -1405,14 +1405,14 @@ export function AssignmentConfirmSheet({
⚠️ 只在**还能改**的时候显示:已确认 / 已撤销时它是误导。
*/}
{!readOnly && (
<div className="border-t border-slate-100 bg-slate-50/60 px-3 py-1.5 text-[10.5px] leading-relaxed text-slate-400">
<div className="border-t bg-slate-50/60 px-3 py-1.5 text-[10.5px] leading-relaxed text-slate-400">
可以直接跟我说:「把某某移出这批」「某某的单给 5 天」「这批 200 人」·
「只要商保直付的」「排掉最近来过的」· 「这批带上『老客户复查免挂号费』」
</div>
)}
{/* ④ 确认 */}
<div className="border-t border-slate-100 px-3 py-2">
<div className="border-t px-3 py-2">
{error && <p className="mb-1.5 text-[11px] text-rose-600">{error}</p>}
{cancelled && !revoked ? (
// 助手撤的:本地没有三个数,只说状态 —— 具体数字助手在消息里已经原话转述过
......
......@@ -115,7 +115,7 @@ function Markdown({ text }: { text: string }) {
{children}
</a>
),
hr: () => <hr className="my-3 border-slate-100" />,
hr: () => <hr className="my-3" />,
blockquote: ({ children }) => (
<blockquote className="my-2 border-l-2 border-brand-300 bg-brand-50/40 py-1 pl-3 text-slate-600">
{children}
......@@ -130,7 +130,7 @@ function Markdown({ text }: { text: string }) {
</code>
),
pre: ({ children }) => (
<pre className="my-2 overflow-x-auto rounded-md border border-slate-100 bg-slate-50 p-3 text-[12px]">
<pre className="my-2 overflow-x-auto rounded-md border bg-slate-50 p-3 text-[12px]">
{children}
</pre>
),
......@@ -141,9 +141,9 @@ function Markdown({ text }: { text: string }) {
),
thead: ({ children }) => <thead className="bg-slate-50">{children}</thead>,
th: ({ children }) => (
<th className="border border-slate-100 px-2 py-1 text-left font-medium text-slate-600">{children}</th>
<th className="border px-2 py-1 text-left font-medium text-slate-600">{children}</th>
),
td: ({ children }) => <td className="border border-slate-100 px-2 py-1 text-slate-700">{children}</td>,
td: ({ children }) => <td className="border px-2 py-1 text-slate-700">{children}</td>,
}}
>
{text}
......@@ -324,8 +324,8 @@ function ArtifactView({ artifact }: { artifact: Artifact }) {
};
return (
<div className="overflow-hidden rounded-lg border border-slate-100 bg-white shadow-sm">
<div className="flex items-center gap-1.5 border-b border-slate-100 bg-slate-50/60 px-3 py-1.5">
<div className="overflow-hidden rounded-lg border bg-white shadow-sm">
<div className="flex items-center gap-1.5 border-b bg-slate-50/60 px-3 py-1.5">
<LayoutTemplate className="h-3.5 w-3.5 text-brand-600" />
<span className="text-[11.5px] font-medium text-slate-500">{artifact.title || '可视化卡片'}</span>
<button
......@@ -347,8 +347,8 @@ function ArtifactView({ artifact }: { artifact: Artifact }) {
createPortal(
<div className="fixed inset-0 z-[70] flex items-center justify-center p-4">
<div className="absolute inset-0 bg-black/40" onClick={() => setExpanded(false)} />
<div className="relative z-10 flex h-[min(86vh,920px)] w-[min(980px,94vw)] flex-col overflow-hidden rounded-xl border border-slate-100 bg-white shadow-2xl">
<div className="flex flex-none items-center gap-1.5 border-b border-slate-100 bg-slate-50/60 px-3 py-2">
<div className="relative z-10 flex h-[min(86vh,920px)] w-[min(980px,94vw)] flex-col overflow-hidden rounded-xl border bg-white shadow-2xl">
<div className="flex flex-none items-center gap-1.5 border-b bg-slate-50/60 px-3 py-2">
<LayoutTemplate className="h-4 w-4 text-brand-600" />
<span className="text-[12.5px] font-medium text-slate-600">{artifact.title || '可视化卡片'}</span>
{artifact.streaming && (
......@@ -533,14 +533,14 @@ function ModelSelect({
type="button"
disabled={disabled}
onClick={() => setOpen((o) => !o)}
className="inline-flex items-center gap-1.5 rounded-md border border-slate-100 bg-white px-2.5 py-1.5 text-[12.5px] text-slate-700 transition-colors hover:bg-slate-50 disabled:opacity-50"
className="inline-flex items-center gap-1.5 rounded-md border bg-white px-2.5 py-1.5 text-[12.5px] text-slate-700 transition-colors hover:bg-slate-50 disabled:opacity-50"
>
<span className="h-1.5 w-1.5 rounded-full bg-emerald-500" />
{label}
<ChevronDown className={cn('h-3.5 w-3.5 text-slate-400 transition-transform', open && 'rotate-180')} />
</button>
{open && (
<div className="absolute right-0 z-50 mt-1 w-40 rounded-lg border border-slate-100 bg-white py-1 shadow-lg">
<div className="absolute right-0 z-50 mt-1 w-40 rounded-lg border bg-white py-1 shadow-lg">
{MODELS.map((m) => (
<button
key={m.value}
......@@ -798,7 +798,7 @@ export function AssistantChat({
return (
<div className={cn('flex flex-col bg-slate-50', isWidget ? 'h-full' : 'h-[100dvh]')}>
{/* Header */}
<header className="flex-none border-b border-slate-100 bg-white/90 backdrop-blur">
<header className="flex-none border-b bg-white/90 backdrop-blur">
<div
className={cn(
'mx-auto flex items-center gap-2.5',
......@@ -859,7 +859,7 @@ export function AssistantChat({
key={ex}
type="button"
onClick={() => fire(ex)}
className="rounded-full border border-slate-100 bg-white px-3 py-1.5 text-[11.5px] text-slate-600 transition-colors hover:border-brand-300 hover:bg-brand-50/50 hover:text-brand-700"
className="rounded-full border bg-white px-3 py-1.5 text-[11.5px] text-slate-600 transition-colors hover:border-brand-300 hover:bg-brand-50/50 hover:text-brand-700"
>
{ex}
</button>
......@@ -893,14 +893,14 @@ export function AssistantChat({
type="button"
onClick={toBottom}
title="回到最新"
className="absolute -top-4 left-1/2 z-10 inline-flex h-7 w-7 -translate-x-1/2 items-center justify-center rounded-full border border-slate-200 bg-white text-slate-500 shadow-md transition-colors hover:bg-slate-50 hover:text-slate-700"
className="absolute -top-4 left-1/2 z-10 inline-flex h-7 w-7 -translate-x-1/2 items-center justify-center rounded-full border bg-white text-slate-500 shadow-md transition-colors hover:bg-slate-50 hover:text-slate-700"
>
<ChevronDown className="h-4 w-4" />
</button>
)}
<div className="mx-auto max-w-3xl px-4 pb-3 pt-1">
{/* 输入框**有边框**(与会话同底,但自己成一块) —— 无边框那版太散,找不到落点 */}
<div className="flex items-end gap-2 rounded-xl border border-slate-200 bg-white px-3 py-2 transition-colors focus-within:border-slate-300">
<div className="flex items-end gap-2 rounded-xl border bg-white px-3 py-2 transition-colors">
<textarea
ref={taRef}
rows={1}
......
......@@ -117,7 +117,7 @@ export function AssistantFab({
{bubble && (
<span
className={
'absolute bottom-full mb-1.5 w-max max-w-[230px] whitespace-normal break-words rounded-xl border border-slate-100 bg-white px-3 py-1.5 text-xs leading-snug text-slate-700 shadow-lg ' +
'absolute bottom-full mb-1.5 w-max max-w-[230px] whitespace-normal break-words rounded-xl border bg-white px-3 py-1.5 text-xs leading-snug text-slate-700 shadow-lg ' +
(onLeftHalf ? 'left-0' : 'right-0')
}
>
......
......@@ -92,7 +92,7 @@ export function AssistantWidget() {
<div
style={maximized ? undefined : winRect ? { left: winRect.x, top: winRect.y } : undefined}
className={cn(
'fixed z-[60] flex flex-col overflow-hidden rounded-xl border border-slate-100 shadow-2xl',
'fixed z-[60] flex flex-col overflow-hidden rounded-xl border shadow-2xl',
'transition-[opacity,transform] duration-150',
maximized
? 'inset-3' // 最大化:填满窗口(留 12px 边距),不写死宽高,随窗口自适应
......
......@@ -180,7 +180,7 @@ export function MockLoginDialog({ open }: { open: boolean }) {
</DialogHeader>
{/* 宿主 tab —— 两宿主数据完全隔离(不同 host/tenant/诊所),切换互不影响 */}
<div className="flex gap-1 border-b border-slate-100">
<div className="flex gap-1 border-b">
{([
{ key: 'jvs-dw' as const, label: '瑞尔集团', sub: 'jvs-dw · 客服花名册' },
{ key: 'friday' as const, label: 'FRIDAY 市场', sub: 'friday · 多品牌 SaaS' },
......@@ -229,7 +229,7 @@ export function MockLoginDialog({ open }: { open: boolean }) {
'flex flex-col items-start gap-0.5 rounded-md border px-2.5 py-1.5 text-left transition-all',
role === r.key
? 'border-brand-400 bg-brand-50/60 ring-1 ring-brand-200'
: 'border-slate-100 hover:border-brand-300',
: 'hover:border-brand-300',
)}
>
<span className="text-[12.5px] font-semibold text-slate-800">{r.nameZh}</span>
......@@ -249,7 +249,7 @@ export function MockLoginDialog({ open }: { open: boolean }) {
</div>
{/* ② 选客服 */}
<div className="rounded-lg border border-slate-100 p-3">
<div className="rounded-lg border p-3">
<div className="mb-2 flex items-center justify-between">
<div className="text-[12px] font-medium text-slate-600">
② 数据范围 · 选客服 = <strong>该客服的诊所</strong>(诊所级)+ 上方「{roleNameZh(role)}」角色
......@@ -271,7 +271,7 @@ export function MockLoginDialog({ open }: { open: boolean }) {
'rounded-full border px-2.5 py-0.5 text-[11px] transition-colors',
tenantFilter === t.k
? 'border-brand-400 bg-brand-50 text-brand-700'
: 'border-slate-100 text-slate-500 hover:border-brand-300',
: 'text-slate-500 hover:border-brand-300',
)}
>
{t.n}
......@@ -281,7 +281,7 @@ export function MockLoginDialog({ open }: { open: boolean }) {
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="搜客服姓名…"
className="ml-auto w-40 rounded-md border border-slate-100 px-2.5 py-1 text-[12px] outline-none focus:border-brand-400"
className="ml-auto w-40 rounded-md border px-2.5 py-1 text-[12px] outline-none focus:border-brand-400"
/>
</div>
......@@ -301,7 +301,7 @@ export function MockLoginDialog({ open }: { open: boolean }) {
'flex w-full items-center justify-between gap-2 rounded-md border px-2.5 py-1.5 text-left transition-all',
loading
? 'border-brand-400 ring-2 ring-brand-200'
: 'border-slate-100 hover:border-brand-400 hover:bg-brand-50/40',
: 'hover:border-brand-400 hover:bg-brand-50/40',
busy && !loading && 'opacity-50',
)}
>
......@@ -348,7 +348,7 @@ export function MockLoginDialog({ open }: { open: boolean }) {
'group flex flex-col items-start gap-1 rounded-md border bg-white px-3 py-2 text-left transition-all',
loading
? 'border-brand-400 ring-2 ring-brand-200'
: 'border-slate-100 hover:border-brand-400 hover:bg-brand-50/40',
: 'hover:border-brand-400 hover:bg-brand-50/40',
busy && !loading && 'opacity-50',
)}
>
......@@ -430,7 +430,7 @@ function FridayPanel({
'flex flex-col items-start gap-0.5 rounded-md border px-2.5 py-1.5 text-left transition-all',
role === r.key
? 'border-brand-400 bg-brand-50/60 ring-1 ring-brand-200'
: 'border-slate-100 hover:border-brand-300',
: 'hover:border-brand-300',
)}
>
<span className="text-[12.5px] font-semibold text-slate-800">{r.nameZh}</span>
......@@ -451,7 +451,7 @@ function FridayPanel({
</div>
{/* ② 真实品牌 / 诊所 */}
<div className="rounded-lg border border-slate-100 p-3">
<div className="rounded-lg border p-3">
<div className="mb-2 flex items-center justify-between">
<div className="text-[12px] font-medium text-slate-600">
② 数据范围 · 选<strong>品牌</strong><strong>诊所</strong>(真实摄入数据)+ 上方「
......@@ -465,7 +465,7 @@ function FridayPanel({
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="搜品牌 / 诊所名…"
className="mb-2 h-7 w-full rounded-md border border-slate-100 bg-slate-50 px-2 text-[12px] outline-none placeholder:text-slate-400 focus:border-brand-300 focus:bg-white"
className="mb-2 h-7 w-full rounded-md border bg-slate-50 px-2 text-[12px] outline-none placeholder:text-slate-400 focus:border-brand-300 focus:bg-white"
/>
<div className="max-h-[280px] space-y-2 overflow-y-auto">
{filtered.length === 0 && (
......@@ -474,7 +474,7 @@ function FridayPanel({
</div>
)}
{filtered.map((b) => (
<div key={b.brandId} className="rounded-md border border-slate-100">
<div key={b.brandId} className="rounded-md border">
{/* 品牌级:该品牌全部诊所 */}
<button
type="button"
......@@ -495,7 +495,7 @@ function FridayPanel({
type="button"
onClick={() => onPick(role, c.clinicId)}
disabled={!!busy}
className="rounded-full border border-slate-100 px-2 py-0.5 text-[11px] text-slate-600 transition-colors hover:border-brand-300 hover:bg-brand-50 hover:text-brand-700 disabled:opacity-50"
className="rounded-full border px-2 py-0.5 text-[11px] text-slate-600 transition-colors hover:border-brand-300 hover:bg-brand-50 hover:text-brand-700 disabled:opacity-50"
>
{c.name}
</button>
......
......@@ -549,7 +549,7 @@ export function PetFab({
<span
className={cn(
// w-max 让短句贴合内容、max-w 封顶,长句自动换行不溢出气泡框
'absolute bottom-full mb-1.5 w-max max-w-[230px] whitespace-normal break-words rounded-xl border border-slate-100 bg-white px-3 py-1.5 text-xs leading-snug text-slate-700 shadow-lg',
'absolute bottom-full mb-1.5 w-max max-w-[230px] whitespace-normal break-words rounded-xl border bg-white px-3 py-1.5 text-xs leading-snug text-slate-700 shadow-lg',
onLeftHalf ? 'left-0' : 'right-0',
)}
>
......
......@@ -116,7 +116,7 @@ function TimelineStageNode({
? cn(THEME_DOT[theme])
: isCurrent
? 'bg-amber-400 border-amber-400 ring-2 ring-amber-100'
: 'bg-white border-dashed border-slate-300';
: 'bg-white border-dashed';
const titleCls = isDone || isCurrent ? 'text-slate-900' : 'text-slate-400';
const detailCls = isDone || isCurrent ? 'text-slate-600' : 'text-slate-400';
const atCls = isDone || isCurrent ? 'text-slate-500' : 'text-slate-300';
......@@ -275,7 +275,7 @@ function TargetTimelineRow({ chain }: { chain: Chain }) {
function HistoryStrip({ chains }: { chains: Chain[] }) {
return (
<div className="rounded-lg border border-slate-100 bg-slate-50/60 px-3 py-2">
<div className="rounded-lg border bg-slate-50/60 px-3 py-2">
<div className="flex items-center justify-between mb-1.5">
<div className="flex items-center gap-1.5 text-[11px] text-slate-500">
<svg viewBox="0 0 24 24" className="w-3 h-3" fill="none" stroke="currentColor" strokeWidth="2">
......@@ -298,7 +298,7 @@ function HistoryChainCard({ chain }: { chain: Chain }) {
const lastNode = chain.nodes[4];
const currNode = chain.nodes[chain.currentStage - 1];
return (
<div className="bg-white rounded border border-slate-100 px-2.5 py-1.5 flex items-center gap-2">
<div className="bg-white rounded border px-2.5 py-1.5 flex items-center gap-2">
<span
className={cn(
'flex-none w-5 h-5 rounded-full flex items-center justify-center text-[10px] font-bold',
......@@ -460,7 +460,7 @@ const TONE_WRAP: Record<ChainTone, string> = {
amber: 'bg-amber-50/40 border-amber-200/70',
sky: 'bg-sky-50/40 border-sky-200/70',
emerald: 'bg-emerald-50/40 border-emerald-200/70',
slate: 'bg-slate-50/40 border-slate-100/70',
slate: 'bg-slate-50/40',
};
const TONE_DOT: Record<ChainTone, string> = {
rose: 'bg-rose-500 text-white',
......@@ -495,7 +495,7 @@ export function ChainDetailView({ chains }: { chains: Chain[] }) {
)}
{/* 下半:全部列表(点击切换上面的明细)*/}
<div className="pt-3 border-t border-slate-100">
<div className="pt-3 border-t">
<div className="text-[11px] text-slate-500 mb-2">
全部 {chains.length} 条治疗链
</div>
......@@ -538,7 +538,7 @@ function ChainListItem({
amber: 'border-amber-400 bg-amber-50 ring-1 ring-amber-200',
sky: 'border-brand-400 bg-brand-50 ring-1 ring-brand-200',
emerald: 'border-emerald-400 bg-emerald-50 ring-1 ring-emerald-200',
slate: 'border-slate-300 bg-slate-50 ring-1 ring-slate-200',
slate: 'bg-slate-50 ring-1 ring-slate-200',
};
return (
<button
......@@ -548,7 +548,7 @@ function ChainListItem({
'text-left rounded-md border px-2.5 py-2 transition',
active
? activeRing[v.tone]
: 'border-slate-100 bg-white hover:bg-slate-50',
: 'bg-white hover:bg-slate-50',
)}
>
<div className="flex items-center gap-1.5">
......
......@@ -110,13 +110,13 @@ export function CloseOpportunityDialog({
r.key === 'other' && 'col-span-2',
active
? 'border-brand-400 bg-brand-50 font-medium text-brand-800'
: 'border-slate-200 bg-white text-slate-700 hover:border-brand-300 hover:bg-brand-50/50',
: 'bg-white text-slate-700 hover:border-brand-300 hover:bg-brand-50/50',
)}
>
<span
className={cn(
'flex h-3.5 w-3.5 flex-none items-center justify-center rounded-[3px] border',
active ? 'border-brand-600 bg-brand-600' : 'border-slate-300',
active ? 'border-brand-600 bg-brand-600' : '',
)}
>
{active && (
......@@ -156,7 +156,7 @@ export function CloseOpportunityDialog({
'rounded-md border px-2.5 py-1 text-[12px] transition-colors',
on
? 'border-brand-400 bg-brand-50 font-medium text-brand-800'
: 'border-slate-200 bg-white text-slate-700 hover:border-brand-300 hover:bg-brand-50/50',
: 'bg-white text-slate-700 hover:border-brand-300 hover:bg-brand-50/50',
)}
>
{potentialTreatmentCardLabel(code)}
......@@ -177,7 +177,7 @@ export function CloseOpportunityDialog({
onChange={(e) => setNote(e.target.value)}
rows={3}
placeholder="请详细描述关闭机会的具体原因,以便后续分析和改进…"
className="w-full resize-y rounded-md border border-slate-200 bg-white px-3 py-2 text-[12.5px] text-slate-900 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-brand-200 focus:border-brand-400"
className="w-full resize-y rounded-md border bg-white px-3 py-2 text-[12.5px] text-slate-900 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-brand-200 focus:border-brand-400"
/>
</div>
)}
......
......@@ -127,10 +127,10 @@ export function Drawer({
<div className="fixed inset-0 z-[60] flex" onClick={onClose}>
<div className="flex-1 bg-slate-900/30" />
<aside
className={cn('max-w-[92vw] bg-white shadow-2xl border-l border-slate-100 flex flex-col', width)}
className={cn('max-w-[92vw] bg-white shadow-2xl border-l flex flex-col', width)}
onClick={(e) => e.stopPropagation()}
>
<header className="px-5 py-3.5 border-b border-slate-100 flex items-center justify-between flex-none">
<header className="px-5 py-3.5 border-b flex items-center justify-between flex-none">
<div>
<h3 className="text-[14px] font-semibold text-slate-900">{title}</h3>
<p className="text-[11px] text-slate-500 mt-0.5">{subtitle}</p>
......@@ -229,7 +229,7 @@ function ImageFactsView({ facts }: { facts: AdaptedFact[] }) {
: ((c.tooth_position as string | undefined) ?? null);
const encId = c.encounter_external_id as string | undefined;
return (
<div key={f.id} className="rounded-md border border-slate-100 p-3">
<div key={f.id} className="rounded-md border p-3">
<div className="flex items-center justify-between mb-1.5">
<div className="flex items-center gap-2">
<Chip tone="violet" size="xs">
......@@ -258,7 +258,7 @@ function ImageFactsView({ facts }: { facts: AdaptedFact[] }) {
</div>
);
})}
<div className="text-[10.5px] text-slate-400 pt-2 border-t border-slate-100">
<div className="text-[10.5px] text-slate-400 pt-2 border-t">
如需查看原始 DICOM,请跳转宿主影像系统(deep-link 待 host actionUrls 接入)
</div>
</div>
......@@ -271,7 +271,7 @@ function ImageFactsView({ facts }: { facts: AdaptedFact[] }) {
function CBCTImageView() {
return (
<div className="space-y-3">
<div className="aspect-[4/3] rounded-lg border border-slate-100 bg-slate-900 relative overflow-hidden">
<div className="aspect-[4/3] rounded-lg border bg-slate-900 relative overflow-hidden">
<div
className="absolute inset-0"
style={{ backgroundImage: 'radial-gradient(circle at 50% 60%, #475569 0%, #0f172a 50%, #000 100%)' }}
......@@ -338,7 +338,7 @@ function ReturnVisitsList({ visits }: { visits: ReturnVisitItem[] }) {
<div
key={i}
className={cn(
'flex items-start gap-2.5 text-[12.5px] py-1 border-b border-slate-50 last:border-0',
'flex items-start gap-2.5 text-[12.5px] py-1 border-b last:border-0',
latest && 'bg-brand-50/50 -mx-2 px-2 rounded',
)}
>
......
......@@ -69,7 +69,7 @@ export function EmrSoapView({ facts }: { facts: AdaptedFact[] }) {
<button
onClick={prev}
aria-label="上一次接诊"
className="inline-flex items-center justify-center w-7 h-7 rounded-md border border-slate-100 bg-white hover:bg-slate-50 hover:border-slate-300 text-slate-600 transition-colors"
className="inline-flex items-center justify-center w-7 h-7 rounded-md border bg-white hover:bg-slate-50 text-slate-600 transition-colors"
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" className="w-3.5 h-3.5">
<path d="M15 18l-6-6 6-6" strokeLinecap="round" strokeLinejoin="round" />
......@@ -107,7 +107,7 @@ export function EmrSoapView({ facts }: { facts: AdaptedFact[] }) {
<button
onClick={next}
aria-label="下一次接诊"
className="inline-flex items-center justify-center w-7 h-7 rounded-md border border-slate-100 bg-white hover:bg-slate-50 hover:border-slate-300 text-slate-600 transition-colors"
className="inline-flex items-center justify-center w-7 h-7 rounded-md border bg-white hover:bg-slate-50 text-slate-600 transition-colors"
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" className="w-3.5 h-3.5">
<path d="M9 18l6-6-6-6" strokeLinecap="round" strokeLinejoin="round" />
......@@ -181,9 +181,9 @@ function EmrSection({
const doctorAdvice = (c.doctor_advice as string | undefined) ?? '';
return (
<section className="rounded-lg border border-slate-100 bg-white overflow-hidden">
<section className="rounded-lg border bg-white overflow-hidden">
{/* 头:日期 + 医生 + 第几次 */}
<header className="px-3 py-2 bg-slate-50 border-b border-slate-100 flex items-center justify-between flex-wrap gap-2">
<header className="px-3 py-2 bg-slate-50 border-b flex items-center justify-between flex-wrap gap-2">
<div className="flex items-center gap-2 min-w-0">
<span className="text-[12.5px] font-semibold text-slate-900 tabular-nums">{date}</span>
<span className="text-[11px] text-slate-500">·</span>
......
......@@ -104,7 +104,7 @@ export function FactsTimeline({
// -mx-5 让 sticky 区域延伸到 drawer padding 边缘,bg-white 遮住下方时间轴
return (
<div className="space-y-3">
<div className="sticky top-0 z-10 -mx-5 px-5 pt-1 pb-2 bg-white space-y-3 border-b border-slate-100">
<div className="sticky top-0 z-10 -mx-5 px-5 pt-1 pb-2 bg-white space-y-3 border-b">
<StatsSummary facts={facts} />
<div className="flex flex-wrap items-center gap-1.5">
<FilterChip label="全部" count={facts.length} active={allOn} onClick={toggleAll} />
......@@ -133,7 +133,7 @@ export function FactsTimeline({
'rounded-md border px-2 py-0.5 text-[11px] focus:outline-none focus:ring-1 focus:ring-sky-300',
toothFilter
? 'border-sky-200 bg-sky-50 text-sky-700 font-medium'
: 'border-slate-100 bg-white text-slate-600',
: 'bg-white text-slate-600',
)}
>
<option value="">全部牙位</option>
......@@ -219,7 +219,7 @@ function StatsSummary({ facts }: { facts: AdaptedFact[] }) {
t ? new Date(t).toLocaleDateString('zh-CN').replace(/\//g, '.') : '—';
return (
<div className="rounded-lg border border-slate-100 bg-slate-50/60 p-2.5">
<div className="rounded-lg border bg-slate-50/60 p-2.5">
<div className="grid grid-cols-4 gap-3">
<StatBlock label="累计净消费">
<div className="text-[13px] font-semibold text-slate-900 tabular-nums">
......@@ -362,7 +362,7 @@ function computeEffectiveTone(fact: AdaptedFact, meta: FactMeta): string {
}
// 状态 badge 统一样式(slate)— W4 末降噪:语义色集中在时间轴 icon dot,badge 不重复表达
const STATUS_BADGE = 'px-1.5 py-px rounded border text-[10.5px] bg-slate-50 text-slate-600 border-slate-100';
const STATUS_BADGE = 'px-1.5 py-px rounded border text-[10.5px] bg-slate-50 text-slate-600';
// 计划治疗专用徽标(sky)— 跟"已完成"(slate)区分:这是还没做的计划,不是已发生的事实
const PLAN_BADGE = 'px-1.5 py-px rounded border text-[10.5px] bg-sky-50 text-sky-700 border-sky-200';
......@@ -466,7 +466,7 @@ function FilterChip({
onClick={onClick}
className={cn(
'inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-[11px] border transition-colors',
active ? cn(T.bg, T.text, T.border, 'font-medium') : 'bg-white text-slate-500 border-slate-100 hover:bg-slate-50',
active ? cn(T.bg, T.text, T.border, 'font-medium') : 'bg-white text-slate-500 hover:bg-slate-50',
)}
>
<span>{label}</span>
......@@ -487,7 +487,7 @@ const CHIP_TONE: Record<string, { bg: string; text: string; border: string }> =
amber: { bg: 'bg-amber-50', text: 'text-amber-800', border: 'border-amber-200' },
sky: { bg: 'bg-sky-50', text: 'text-sky-700', border: 'border-sky-200' },
indigo: { bg: 'bg-indigo-50', text: 'text-indigo-700', border: 'border-indigo-200' },
slate: { bg: 'bg-slate-100', text: 'text-slate-700', border: 'border-slate-300' },
slate: { bg: 'bg-slate-100', text: 'text-slate-700', border: '' },
};
// ─────────────────────────────────────────────
......@@ -532,7 +532,7 @@ interface ToneCls {
border: string;
}
const TONE_FALLBACK: ToneCls = { bg: 'bg-slate-100', text: 'text-slate-700', border: 'border-slate-300' };
const TONE_FALLBACK: ToneCls = { bg: 'bg-slate-100', text: 'text-slate-700', border: '' };
const TONE = {
rose: { bg: 'bg-rose-100', text: 'text-rose-700', border: 'border-rose-200' },
......
......@@ -190,7 +190,7 @@ export function OutcomeForm({
return (
<div className="flex flex-col gap-2.5 h-full min-h-0">
{isTerminal && (
<div className="flex-none rounded bg-slate-100 border border-slate-100 px-2.5 py-1.5 text-[11px] text-slate-500 leading-snug">
<div className="flex-none rounded bg-slate-100 border px-2.5 py-1.5 text-[11px] text-slate-500 leading-snug">
本任务已{plan.status === 'abandoned' ? '放弃' : plan.status === 'superseded' ? '被新版本替代' : '结案'},不可再提交执行;如需重新跟进,等召回重新生成。
</div>
)}
......@@ -211,7 +211,7 @@ export function OutcomeForm({
'px-2 py-1.5 rounded text-[11.5px] border transition-colors inline-flex items-center justify-center gap-1.5',
selected
? `${T.bg} ${T.text} border-current font-medium ring-1 ring-current/30`
: 'bg-white text-slate-700 border-slate-100 hover:border-slate-300 hover:bg-slate-50',
: 'bg-white text-slate-700 hover:bg-slate-50',
)}
>
<svg
......@@ -262,7 +262,7 @@ export function OutcomeForm({
'px-2 py-1 rounded text-left text-[11px] border transition-colors flex items-center gap-1.5',
selected
? `${T.bg} ${T.text} border-current font-medium ring-1 ring-current/30`
: 'bg-white text-slate-700 border-slate-100 hover:border-slate-300 hover:bg-slate-50',
: 'bg-white text-slate-700 hover:bg-slate-50',
)}
>
<span className={cn('flex-none w-1.5 h-1.5 rounded-full', selected ? T.dot : 'bg-slate-300')} />
......@@ -366,7 +366,7 @@ export function OutcomeForm({
value={notes}
onChange={(e) => setNotes(e.target.value)}
placeholder="患者反馈、关键点、需告知医生的信息……"
className="flex-1 min-h-[60px] px-2.5 py-1.5 rounded border border-slate-100 bg-white text-[12.5px] resize-none focus:outline-none focus:ring-2 focus:ring-brand-200 focus:border-brand-300"
className="flex-1 min-h-[60px] px-2.5 py-1.5 rounded border bg-white text-[12.5px] resize-none focus:outline-none focus:ring-2 focus:ring-brand-200 focus:border-brand-300"
/>
</div>
......
......@@ -164,7 +164,7 @@ function FeatureCard({
ref={cardRef}
className={cn(
'relative rounded-md border p-3 pr-9',
focused ? 'border-brand-300 bg-brand-50/40 ring-1 ring-brand-200' : 'border-slate-100',
focused ? 'border-brand-300 bg-brand-50/40 ring-1 ring-brand-200' : '',
)}
>
{/* 右上角 ? —— 真 button:可 Tab 聚焦,focus 也能唤出说明卡(触屏/键盘可达) */}
......@@ -213,7 +213,7 @@ function FeatureCard({
{/* 依据 —— 这个标签是被哪些事实推出来的 */}
{resolved.length > 0 && (
<div className="mt-2 border-t border-slate-100 pt-2">
<div className="mt-2 border-t pt-2">
<div className="mb-1 flex items-baseline gap-2">
<span className="text-[10.5px] text-slate-400">依据 · {resolved.length}</span>
{resolved.length > MAX_EVIDENCE_ROWS && (
......
......@@ -36,7 +36,7 @@ export function PersonaFeatureHover({
{/* z-[70]:抽屉面板是 z-[60],默认 hovercard z-50 会被抽屉盖住(详情抽屉里 ? hover 不显的根因) */}
<HoverCardContent align="end" sideOffset={6} className="z-[70] w-80 p-3 text-[11.5px]">
<div className="space-y-2">
<div className="flex items-baseline justify-between gap-2 border-b border-slate-100 pb-1.5">
<div className="flex items-baseline justify-between gap-2 border-b pb-1.5">
<span className="text-[13px] font-semibold text-slate-900">{title}</span>
{display && (
<span className="text-right text-[10.5px] text-slate-500">{display.subtitle}</span>
......
......@@ -745,9 +745,9 @@ export function PlanDetailApp({
}
centerPane={
<main className="relative min-h-0 min-w-0 flex flex-col h-full">
<section className="bg-white rounded-lg border border-slate-100 shadow-sm flex flex-col min-h-0 flex-1 overflow-hidden">
<section className="bg-white rounded-lg border shadow-sm flex flex-col min-h-0 flex-1 overflow-hidden">
{/* 窄屏 flex-wrap 自然换行,gap-y 给行间距 */}
<header className="flex-none px-3 sm:px-4 py-2.5 border-b border-slate-100">
<header className="flex-none px-3 sm:px-4 py-2.5 border-b">
{/* 第一行:标题 + 标签 ←→ 控件(模式切换 / 重新生成 / AI 时间戳)*/}
<div className="flex flex-wrap items-center justify-between gap-x-2 gap-y-2">
<div className="min-w-0">
......@@ -930,8 +930,8 @@ export function PlanDetailApp({
className="min-h-0 flex flex-col gap-2.5 overflow-hidden h-full"
onMouseEnter={hintRecallFeedback}
>
<section className="bg-white rounded-lg border border-slate-100 shadow-sm flex flex-col min-h-0 flex-1 overflow-hidden">
<header className="flex-none px-4 py-2.5 border-b border-slate-100">
<section className="bg-white rounded-lg border shadow-sm flex flex-col min-h-0 flex-1 overflow-hidden">
<header className="flex-none px-4 py-2.5 border-b">
{/* 「回访」按钮已上移到顶栏右上角动作区(主按钮) */}
<h2 className="text-[14px] font-semibold text-slate-900 leading-tight">通话结果</h2>
<p className="text-[10.5px] text-slate-500 mt-0.5">提交后本次任务自动归档</p>
......@@ -1100,7 +1100,7 @@ function RecallFeedbackControl({
return (
<>
<div
className="hidden sm:flex flex-none items-center gap-0.5 rounded-md border border-slate-100 px-1 py-0.5"
className="hidden sm:flex flex-none items-center gap-0.5 rounded-md border px-1 py-0.5"
title="召回反馈:这条召回准不准?"
>
<span className="hidden lg:inline px-1 text-[10.5px] text-slate-400">召回反馈</span>
......@@ -1154,7 +1154,7 @@ function RecallFeedbackControl({
type="button"
title={f.hint}
onClick={() => quickInsert(f.labelZh)}
className="px-2 py-1 rounded-full text-[12px] border border-slate-100 bg-white text-slate-600 hover:border-rose-300 hover:bg-rose-50 hover:text-rose-700 transition-colors"
className="px-2 py-1 rounded-full text-[12px] border bg-white text-slate-600 hover:border-rose-300 hover:bg-rose-50 hover:text-rose-700 transition-colors"
>
{f.labelZh}
</button>
......@@ -1165,14 +1165,14 @@ function RecallFeedbackControl({
onChange={(e) => setNote(e.target.value)}
placeholder="补充说明(选填)…"
rows={3}
className="w-full px-2.5 py-2 rounded border border-slate-100 bg-white text-[13px] resize-none focus:outline-none focus:ring-2 focus:ring-rose-200 focus:border-rose-300"
className="w-full px-2.5 py-2 rounded border bg-white text-[13px] resize-none focus:outline-none focus:ring-2 focus:ring-rose-200 focus:border-rose-300"
/>
</div>
<DialogFooter>
<button
type="button"
onClick={() => setOpen(false)}
className="px-3 py-1.5 rounded-md border border-slate-100 text-[13px] text-slate-600 hover:bg-slate-50"
className="px-3 py-1.5 rounded-md border text-[13px] text-slate-600 hover:bg-slate-50"
>
取消
</button>
......@@ -1269,7 +1269,7 @@ function TopBar({
};
return (
<header className="flex flex-none items-center justify-between gap-2 border-b border-slate-100 bg-white px-3 py-2 sm:px-5 sm:py-3 sm:gap-3">
<header className="flex flex-none items-center justify-between gap-2 border-b bg-white px-3 py-2 sm:px-5 sm:py-3 sm:gap-3">
<div className="flex min-w-0 items-center gap-2 sm:gap-3">
{/* PAC 品牌 + 面包屑标题 —— 嵌入宿主时隐藏(宿主自有品牌与导航) */}
{!embedded && (
......@@ -1354,8 +1354,8 @@ function TopBar({
className={cn(
'inline-flex items-center gap-1.5 rounded-md border px-2 sm:px-2.5 py-1 text-[11.5px] font-medium transition-colors',
refreshing
? 'cursor-not-allowed border-slate-100 bg-slate-50 text-slate-400'
: 'border-slate-100 bg-white text-slate-700 hover:border-brand-300 hover:bg-brand-50 hover:text-brand-700',
? 'cursor-not-allowed bg-slate-50 text-slate-400'
: 'bg-white text-slate-700 hover:border-brand-300 hover:bg-brand-50 hover:text-brand-700',
)}
>
<RefreshCw className={cn('h-3.5 w-3.5', refreshing && 'animate-spin')} />
......@@ -1370,7 +1370,7 @@ function TopBar({
type="button"
onClick={onOpenPotential}
title="查看潜在治疗机会"
className="inline-flex items-center gap-1.5 rounded-md border border-slate-100 bg-white px-2 sm:px-2.5 py-1 text-[11.5px] font-medium text-slate-700 transition-colors hover:border-brand-300 hover:bg-brand-50 hover:text-brand-700"
className="inline-flex items-center gap-1.5 rounded-md border bg-white px-2 sm:px-2.5 py-1 text-[11.5px] font-medium text-slate-700 transition-colors hover:border-brand-300 hover:bg-brand-50 hover:text-brand-700"
>
<Sparkles className="h-3.5 w-3.5" />
<span className="hidden sm:inline">潜在</span>
......@@ -1382,7 +1382,7 @@ function TopBar({
<button
type="button"
title="新建预约"
className="inline-flex items-center gap-1.5 rounded-md border border-slate-100 bg-white px-2 sm:px-2.5 py-1 text-[11.5px] font-medium text-slate-700 transition-colors hover:border-brand-300 hover:bg-brand-50 hover:text-brand-700"
className="inline-flex items-center gap-1.5 rounded-md border bg-white px-2 sm:px-2.5 py-1 text-[11.5px] font-medium text-slate-700 transition-colors hover:border-brand-300 hover:bg-brand-50 hover:text-brand-700"
>
<CalendarPlus className="h-3.5 w-3.5" />
<span className="hidden sm:inline">预约</span>
......@@ -1453,7 +1453,7 @@ function AIDisclaimerFooter({
onFeedback(v);
};
return (
<div className="flex-none border-t border-slate-100 px-3 sm:px-4 py-2 flex items-center justify-between gap-2 sm:gap-3">
<div className="flex-none border-t px-3 sm:px-4 py-2 flex items-center justify-between gap-2 sm:gap-3">
<div className="min-w-0 text-[10.5px] text-slate-500 flex items-center gap-1.5 leading-tight">
<svg viewBox="0 0 24 24" className="w-3 h-3 flex-none" fill="none" stroke="currentColor" strokeWidth="1.8">
<circle cx="12" cy="12" r="10" />
......@@ -1648,7 +1648,7 @@ function IdentityCard({
medicalRecordNumber: patient.medicalRecordNumber,
});
return (
<section className="bg-white rounded-lg border border-slate-100 shadow-sm flex-none">
<section className="bg-white rounded-lg border shadow-sm flex-none">
<div className="p-3 flex items-start gap-2.5">
<div className="min-w-0 flex-1">
<div className="flex items-start justify-between gap-2">
......@@ -2041,7 +2041,7 @@ function RecallBriefLine({
</button>
{canExpand && expanded && (
// 展开 = 以前的「为什么召回」结构化原因(RecallReasonLine,样式与以前一致)
<div className="mt-1.5 rounded-md border border-slate-100 bg-white px-2.5 py-2">
<div className="mt-1.5 rounded-md border bg-white px-2.5 py-2">
<div className="text-[12.5px] text-slate-700 leading-relaxed">
<RecallReasonLine visibleReasons={visibleReasons} />
</div>
......@@ -2508,7 +2508,7 @@ function SuggestionCard({
</div>
)}
</div>
<div className="mt-2 pt-2 border-t border-slate-100 flex items-center justify-between">
<div className="mt-2 pt-2 border-t flex items-center justify-between">
<span className="text-[10.5px] text-slate-500">
触达限额{' '}
<strong className="text-slate-700 tabular-nums">
......@@ -2851,11 +2851,11 @@ function RegenBtn({
const selectorCls = cn(
'inline-flex items-center gap-0.5 px-1.5 py-0.5 text-[10.5px] border-r transition-colors',
streaming
? 'text-slate-300 border-slate-100 cursor-not-allowed'
: 'text-slate-500 border-slate-100 hover:text-brand-700 hover:bg-brand-50',
? 'text-slate-300 cursor-not-allowed'
: 'text-slate-500 hover:text-brand-700 hover:bg-brand-50',
);
return (
<div className="inline-flex flex-none items-stretch rounded border border-slate-100 overflow-hidden">
<div className="inline-flex flex-none items-stretch rounded border overflow-hidden">
{/* 档位选择(只选,不触发);企微只有深度档 → 不显示 */}
{!hideTier && (
<DropdownMenu>
......
......@@ -100,7 +100,7 @@ export function QuickLogPopover({
value={notes}
onChange={(e) => setNotes(e.target.value)}
placeholder="约的什么、什么时候"
className="w-full resize-none rounded-md border border-slate-200 px-2 py-1.5 text-[12.5px] outline-none placeholder:text-slate-300 focus:border-brand-400"
className="w-full resize-none rounded-md border px-2 py-1.5 text-[12.5px] outline-none placeholder:text-slate-300 focus:border-brand-400"
/>
) : (
<div className="flex items-center gap-1.5">
......@@ -109,13 +109,13 @@ export function QuickLogPopover({
type="date"
value={date}
onChange={(e) => setDate(e.target.value)}
className="min-w-0 flex-1 rounded-md border border-slate-200 px-2 py-1.5 text-[12.5px] outline-none focus:border-brand-400"
className="min-w-0 flex-1 rounded-md border px-2 py-1.5 text-[12.5px] outline-none focus:border-brand-400"
/>
<input
type="time"
value={time}
onChange={(e) => setTime(e.target.value)}
className="w-[86px] flex-none rounded-md border border-slate-200 px-2 py-1.5 text-[12.5px] outline-none focus:border-brand-400"
className="w-[86px] flex-none rounded-md border px-2 py-1.5 text-[12.5px] outline-none focus:border-brand-400"
/>
</div>
)}
......
......@@ -32,7 +32,7 @@ export function ScriptDeepProcess({ steps }: { steps: DeepStep[] }) {
const runningStep = steps.find((s) => s.status === 'running')?.step ?? null;
return (
<div className="mb-3 rounded-lg border border-slate-100 bg-slate-50 p-2.5">
<div className="mb-3 rounded-lg border bg-slate-50 p-2.5">
<ol className="space-y-1.5">
{steps.map((s) => {
const hasDetail = stepHasDetail(s);
......@@ -146,7 +146,7 @@ function IssueList({
return (
<ul className="space-y-1">
{issues.map((it, i) => (
<li key={i} className="rounded border border-slate-100 bg-white px-2 py-1 text-[11px] leading-snug">
<li key={i} className="rounded border bg-white px-2 py-1 text-[11px] leading-snug">
<div className="font-medium text-slate-600">{it.section || `问题 ${i + 1}`}</div>
<div className="break-words text-slate-500">{it.problem}</div>
{it.fix && (
......
......@@ -21,7 +21,7 @@ export function ScriptMarkdown({ sections, streaming = false }: { sections: Scri
{sections.map((sec, idx) => {
const isOpen = !collapsed[sec.id];
return (
<div key={sec.id} className="rounded-md border border-slate-100 bg-white overflow-hidden">
<div key={sec.id} className="rounded-md border bg-white overflow-hidden">
<button
onClick={() => setCollapsed({ ...collapsed, [sec.id]: !collapsed[sec.id] })}
className="w-full flex items-center justify-between gap-2 px-3 py-2 text-left hover:bg-slate-50"
......@@ -45,7 +45,7 @@ export function ScriptMarkdown({ sections, streaming = false }: { sections: Scri
</svg>
</button>
{isOpen && (
<div className="px-3 pb-3 pt-1 border-t border-slate-100">
<div className="px-3 pb-3 pt-1 border-t">
<MD text={sec.markdown} />
</div>
)}
......@@ -63,7 +63,7 @@ export function ScriptStepCards({ sections, streaming = false }: { sections: Scr
return (
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{sections.map((sec, i) => (
<div key={sec.id} className="rounded-md border border-slate-100 bg-white p-3 flex flex-col">
<div key={sec.id} className="rounded-md border bg-white p-3 flex flex-col">
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2">
<span className="w-6 h-6 rounded-full bg-brand-600 text-white text-[11px] font-semibold flex items-center justify-center">
......@@ -93,7 +93,7 @@ export function ScriptCopilot({ sections, streaming = false }: { sections: Scrip
return (
<div className="flex flex-col gap-3">
{/* 进度条 */}
<div className="flex items-center gap-1.5 rounded-md bg-slate-50 border border-slate-100 p-1.5">
<div className="flex items-center gap-1.5 rounded-md bg-slate-50 border p-1.5">
{sections.map((s, i) => (
<button
key={s.id}
......
......@@ -153,7 +153,7 @@ export function SidebarCard({
return (
<section
className={cn(
'bg-white rounded-lg border border-slate-100 shadow-sm flex flex-col min-h-0 flex-none',
'bg-white rounded-lg border shadow-sm flex flex-col min-h-0 flex-none',
className,
)}
>
......@@ -179,7 +179,7 @@ export function SidebarCard({
</button>
{action && <div className="flex-none">{action}</div>}
</header>
{open && <div className={cn(padding, 'border-t border-slate-100 overflow-y-auto min-h-0')}>{children}</div>}
{open && <div className={cn(padding, 'border-t overflow-y-auto min-h-0')}>{children}</div>}
</section>
);
}
......
......@@ -131,17 +131,17 @@ export function ToothTimeline({ facts }: { facts: AdaptedFact[] }) {
);
const isWhole = k.startsWith('全口');
return (
<div key={k} className="rounded-lg border border-slate-100 overflow-hidden">
<div key={k} className="rounded-lg border overflow-hidden">
<div
className={cn(
'flex items-center gap-2 px-2.5 py-1.5 text-[12px] font-semibold border-b border-slate-100',
'flex items-center gap-2 px-2.5 py-1.5 text-[12px] font-semibold border-b',
isWhole ? 'bg-amber-50 text-amber-800' : 'bg-slate-50 text-slate-700',
)}
>
<span className="tabular-nums">{isWhole ? k : `牙位 ${k}`}</span>
<span className="ml-auto text-[10px] font-normal text-slate-400">{rows.length}</span>
</div>
<div className="divide-y divide-slate-50">
<div className="divide-y">
{rows.map((f) => (
<ToothFactRow key={`${k}-${f.id}`} fact={f} />
))}
......
......@@ -219,9 +219,9 @@ export function PatientPickerRail({
});
return (
<aside className="flex h-full w-[320px] flex-none flex-col border-r border-slate-100 bg-white">
<aside className="flex h-full w-[320px] flex-none flex-col border-r bg-white">
{/* view tabs */}
<div className="flex flex-none gap-1 border-b border-slate-100 px-2 pt-2">
<div className="flex flex-none gap-1 border-b px-2 pt-2">
{VIEW_TABS.map((t) => (
<button
key={t.v}
......@@ -251,7 +251,7 @@ export function PatientPickerRail({
⚠️ 常驻的「把这 N 人移交助手」横幅同样已撤 —— 入口收敛到 tab 行的「分配」一个。 */}
{/* 搜索 + 工具行 */}
<div className="flex-none space-y-1.5 border-b border-slate-100 p-2">
<div className="flex-none space-y-1.5 border-b p-2">
<div className="relative">
<Search className="absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-slate-400" />
<input
......@@ -275,7 +275,7 @@ export function PatientPickerRail({
scheduleKeyword(v);
}}
placeholder="姓名 / 手机 / 病历号"
className="h-7 w-full rounded-md border border-slate-100 bg-slate-50 pl-7 pr-2 text-[12px] outline-none placeholder:text-slate-400 focus:border-brand-300 focus:bg-white"
className="h-7 w-full rounded-md border bg-slate-50 pl-7 pr-2 text-[12px] outline-none placeholder:text-slate-400 focus:border-brand-300 focus:bg-white"
/>
</div>
{/* ⚠️ 外层条件要跟着两个开关走 —— 只判其中一个,另一个放开时整排还是不显示。
......@@ -293,7 +293,7 @@ export function PatientPickerRail({
'inline-flex h-6 items-center gap-1 rounded-md border px-1.5 text-[11px] transition-colors',
realPhoneOnly
? 'border-brand-300 bg-brand-50 font-medium text-brand-700'
: 'border-slate-100 bg-white text-slate-500 hover:bg-slate-50',
: 'bg-white text-slate-500 hover:bg-slate-50',
)}
>
<PhoneCall className="h-3 w-3" />
......@@ -435,7 +435,7 @@ function PersonaTagFilter({
'inline-flex h-6 items-center gap-1 rounded-md border px-1.5 text-[11px] transition-colors',
selected.size > 0
? 'border-brand-300 bg-brand-50 font-medium text-brand-700'
: 'border-slate-100 bg-white text-slate-500 hover:bg-slate-50',
: 'bg-white text-slate-500 hover:bg-slate-50',
)}
>
<Filter className="h-3 w-3" />
......@@ -491,7 +491,7 @@ function PersonaTagFilter({
'rounded-full border px-2 py-0.5 text-[11px] transition-colors',
on
? 'border-brand-300 bg-brand-50 font-medium text-brand-700'
: 'border-slate-100 bg-white text-slate-600 hover:border-brand-200 hover:bg-brand-50/40',
: 'bg-white text-slate-600 hover:border-brand-200 hover:bg-brand-50/40',
)}
>
{o.zh}
......@@ -504,7 +504,7 @@ function PersonaTagFilter({
))}
</div>
{/* 定高:hover 进出不抖动 */}
<div className="mt-1 min-h-[30px] border-t border-slate-100 px-1 pt-1.5 text-[10.5px] leading-relaxed">
<div className="mt-1 min-h-[30px] border-t px-1 pt-1.5 text-[10.5px] leading-relaxed">
{hint ? (
<span className="text-slate-600">{hint}</span>
) : (
......@@ -542,7 +542,7 @@ function RailClinicFilter({
'inline-flex h-6 w-full items-center justify-between rounded-md border px-1.5 text-[11px] transition-colors',
selected.length
? 'border-brand-300 bg-brand-50 font-medium text-brand-700'
: 'border-slate-100 bg-white text-slate-500 hover:bg-slate-50',
: 'bg-white text-slate-500 hover:bg-slate-50',
)}
>
<span className="truncate">
......@@ -554,7 +554,7 @@ function RailClinicFilter({
</button>
</PopoverTrigger>
<PopoverContent align="start" className="max-h-72 w-56 overflow-y-auto p-1.5">
<div className="mb-1 flex items-center justify-between border-b border-slate-100 px-1.5 py-1">
<div className="mb-1 flex items-center justify-between border-b px-1.5 py-1">
<span className="text-[10.5px] text-slate-400">按诊所筛选(可多选)</span>
{selected.length > 0 && (
<button type="button" onClick={() => onChange([])} className="text-[10.5px] text-brand-600 hover:underline">
......@@ -731,7 +731,7 @@ function DoctorPicker({
value={q}
onChange={(e) => setQ(e.target.value)}
placeholder={doctors.length ? '输入医生姓名搜索' : '暂无医生名单'}
className="w-full rounded border border-slate-200 px-1.5 py-0.5 text-[11px] outline-none placeholder:text-slate-300 focus:border-brand-300"
className="w-full rounded border px-1.5 py-0.5 text-[11px] outline-none placeholder:text-slate-300 focus:border-brand-300"
/>
{matches.length > 0 && (
<div className="flex flex-wrap gap-1">
......@@ -743,7 +743,7 @@ function DoctorPicker({
onToggle(`${dimId}:${d}`);
setQ('');
}}
className="rounded-full border border-slate-100 bg-white px-2 py-0.5 text-[11px] text-slate-600 hover:border-brand-200 hover:bg-brand-50/40"
className="rounded-full border bg-white px-2 py-0.5 text-[11px] text-slate-600 hover:border-brand-200 hover:bg-brand-50/40"
>
{d}
</button>
......@@ -808,7 +808,7 @@ function PatientRow({
onClick={onClick}
onKeyDown={(e) => e.key === 'Enter' && onClick()}
className={cn(
'group block w-full cursor-pointer border-b border-slate-100 px-3 py-2 text-left transition-colors',
'group block w-full cursor-pointer border-b px-3 py-2 text-left transition-colors',
active ? 'bg-brand-50/70 ring-1 ring-inset ring-brand-200' : 'hover:bg-slate-50',
)}
>
......
......@@ -87,7 +87,7 @@ export function ReleaseReasonDialog({
'flex w-full flex-col items-start gap-0.5 rounded-md border px-2.5 py-1.5 text-left transition-colors',
on
? 'border-brand-400 bg-brand-50/70'
: 'border-slate-100 bg-white hover:border-brand-200 hover:bg-brand-50/30',
: 'bg-white hover:border-brand-200 hover:bg-brand-50/30',
)}
>
<span
......@@ -114,7 +114,7 @@ export function ReleaseReasonDialog({
value={note}
onChange={(e) => setNote(e.target.value)}
placeholder="写清楚具体情况(必填)"
className="w-full resize-none rounded-md border border-slate-200 px-2 py-1.5 text-[12.5px] outline-none placeholder:text-slate-300 focus:border-brand-400"
className="w-full resize-none rounded-md border px-2 py-1.5 text-[12.5px] outline-none placeholder:text-slate-300 focus:border-brand-400"
/>
</div>
)}
......
......@@ -111,7 +111,7 @@ function PriorityBreakdownTable({
function Header({ disp }: { disp: string }) {
return (
<div className="flex items-baseline justify-between border-b border-slate-100 pb-1.5">
<div className="flex items-baseline justify-between border-b pb-1.5">
<span className="text-[13px] font-semibold text-slate-900">优先级 {disp} / 10</span>
<span className="text-[10.5px] text-slate-500">急迫 × 价值 × 意愿</span>
</div>
......@@ -147,7 +147,7 @@ function Row({
function Subtotal({ label, value, bold }: { label: string; value: string; bold?: boolean }) {
return (
<tr>
<td colSpan={3} className="border-t border-slate-100 pt-1 pb-0.5">
<td colSpan={3} className="border-t pt-1 pb-0.5">
<span className="flex justify-between">
<span className={`text-slate-700 ${bold ? 'font-semibold' : ''}`}>{label}</span>
<span
......
......@@ -49,7 +49,7 @@ export function SessionExpired() {
type="button"
onClick={retry}
disabled={retrying}
className="inline-flex items-center gap-1.5 rounded-md border border-slate-200 bg-white px-3 py-1.5 text-[12.5px] text-slate-600 transition-colors hover:bg-slate-50 disabled:opacity-50"
className="inline-flex items-center gap-1.5 rounded-md border bg-white px-3 py-1.5 text-[12.5px] text-slate-600 transition-colors hover:bg-slate-50 disabled:opacity-50"
>
<RefreshCw className={`h-3.5 w-3.5 ${retrying ? 'animate-spin' : ''}`} />
{retrying ? '重试中…' : '重试'}
......
......@@ -186,8 +186,8 @@ export function BatchTracking({ clinicId }: { clinicId: string | null }) {
}, [shown, running, scope]);
return (
<div className="relative flex min-h-0 w-full flex-col overflow-hidden rounded-lg border border-slate-100 bg-white">
<div className="flex flex-none flex-wrap items-center gap-x-2.5 gap-y-1 border-b border-slate-50 px-3.5 py-2.5">
<div className="relative flex min-h-0 w-full flex-col overflow-hidden rounded-lg border bg-white">
<div className="flex flex-none flex-wrap items-center gap-x-2.5 gap-y-1 border-b px-3.5 py-2.5">
<span className="text-[13.5px] font-semibold text-slate-900">我分的批次</span>
<span className="text-[11px] text-slate-400">点一行看这批具体怎么样</span>
<span className="ml-auto inline-flex rounded-lg bg-slate-100 p-0.5">
......@@ -290,14 +290,12 @@ type BatchGroupData = { key: string; note: string; past: boolean; rows: Assignme
* 而回顾恰恰就是在比这几列。
* ⛔ 也别只靠那条竖轨 —— 一条 2px 的线滚起来看不见,底色才是扫视时抓得到的。
*
* 🔴 竖轨的颜色**必须走 inline style**(`rail` 是个 CSS 颜色值不是类名):
* globals.css 里那条 `* { border-color: hsl(var(--border)) }` **不在任何 @layer 里**,
* 压过 Tailwind 的 `border-l-brand-400` —— 写类名不报错、也不生效,
* 算出来是那个灰(2026-08-11 实测:整栏竖轨全是灰的,肉眼以为"没做")。
* ⚠️ `past` 的竖轨是**透明但仍占 2px**(`border-l-transparent`)—— 少了它,历史行会
* 比在跑的行少 2px 左内缩,两组的首列对不齐。⛔ 别图省事删成没有 border-l。
*/
const TONE = {
live: {
rail: 'var(--color-brand-400)',
rail: 'border-l-2 border-l-brand-400',
row: 'bg-white hover:bg-slate-50',
when: 'text-slate-500',
cohort: 'text-slate-900',
......@@ -314,7 +312,7 @@ const TONE = {
headKey: 'text-brand-800',
},
past: {
rail: 'transparent',
rail: 'border-l-2 border-l-transparent',
row: 'bg-slate-50/80 hover:bg-slate-100',
when: 'text-slate-400',
cohort: 'text-slate-500',
......@@ -343,12 +341,10 @@ function BatchGroup({
onOpen: (id: string) => void;
}) {
const t = group.past ? TONE.past : TONE.live;
/// 竖轨 —— ⛔ 只能 inline(见 TONE 上面那段:全局 `*` 规则把 border 颜色类吃掉了)
const rail = { borderLeft: `2px solid ${t.rail}` };
return (
<>
<tr>
<td colSpan={7} style={rail} className={cn('border-t px-3.5 py-1.5', t.head)}>
<td colSpan={7} className={cn('border-t px-3.5 py-1.5', t.rail, t.head)}>
<span className={cn('text-[11px] font-semibold', t.headKey)}>{group.key}</span>
<span className="ml-1.5 text-[10.5px] text-slate-400">{group.note}</span>
</td>
......@@ -362,14 +358,14 @@ function BatchGroup({
key={b.id}
onClick={() => onOpen(b.id)}
className={cn(
'cursor-pointer border-t border-slate-50',
'cursor-pointer border-t',
t.row,
// ⚠️ 选中态压在两套配色**之上** —— 抽屉开着的那一行必须一眼看出来,
// ⛔ 不能被历史行的灰底吃掉
openId === b.id && 'bg-brand-50 hover:bg-brand-50',
)}
>
<td style={rail} className="px-3.5 py-2">
<td className={cn(t.rail, 'px-3.5 py-2')}>
<div className="flex items-baseline gap-2 whitespace-nowrap">
<span className={cn('nums text-[11.5px]', t.when)}>{when}</span>
<span className={cn('font-medium', t.cohort)}>{cohort}</span>
......@@ -441,8 +437,8 @@ function BatchDrawer({ id, onClose }: { id: string; onClose: () => void }) {
className="absolute inset-x-0 bottom-0 top-[3.25rem] z-20 bg-slate-900/10"
onClick={onClose}
/>
<aside className="absolute bottom-0 right-0 top-[3.25rem] z-20 flex w-[26rem] max-w-full flex-col overflow-hidden border-l border-slate-100 bg-white shadow-[-12px_0_24px_-6px_rgba(15,23,42,0.10)]">
<div className="flex flex-none items-start gap-2.5 border-b border-slate-50 bg-brand-50 px-3.5 py-3">
<aside className="absolute bottom-0 right-0 top-[3.25rem] z-20 flex w-[26rem] max-w-full flex-col overflow-hidden border-l bg-white shadow-[-12px_0_24px_-6px_rgba(15,23,42,0.10)]">
<div className="flex flex-none items-start gap-2.5 border-b bg-brand-50 px-3.5 py-3">
<div className="min-w-0 flex-1">
<div className="truncate text-[13px] font-semibold text-slate-900">
{d?.label ?? '加载中…'}
......@@ -494,7 +490,7 @@ function BatchDrawer({ id, onClose }: { id: string; onClose: () => void }) {
</thead>
<tbody>
{d.agentStats.map((a) => (
<tr key={a.userId} className="border-t border-slate-50">
<tr key={a.userId} className="border-t">
<td className="py-1.5">{a.name ?? a.userId}</td>
<td className="nums px-1.5 py-1.5 text-right">{a.planned}</td>
<td className="nums px-1.5 py-1.5 text-right text-emerald-700">{a.done}</td>
......
......@@ -78,8 +78,8 @@ export function NewBatchPanel({ clinicId }: { clinicId: string | null }) {
const pick = useCallback((c: Picked) => handoff(c), []);
return (
<div className="flex w-full flex-col overflow-hidden rounded-lg border border-slate-100 bg-white">
<div className="flex flex-none flex-wrap items-baseline gap-x-2 gap-y-1 border-b border-slate-50 px-3.5 py-2.5">
<div className="flex w-full flex-col overflow-hidden rounded-lg border bg-white">
<div className="flex flex-none flex-wrap items-baseline gap-x-2 gap-y-1 border-b px-3.5 py-2.5">
<span className="text-[13.5px] font-semibold text-slate-900">分一批新的</span>
<span className="text-[11px] text-slate-400">
点一格 = 把这批人交给助手,助手出确认单
......
......@@ -62,7 +62,7 @@ export function SupervisorHeader({
const current = clinics.find((c) => c.id === clinicId);
return (
<header className="flex h-12 flex-none items-center gap-2.5 border-b border-slate-100 bg-white px-3 sm:px-4">
<header className="flex h-12 flex-none items-center gap-2.5 border-b bg-white px-3 sm:px-4">
{!embedded && (
<>
<span className="inline-flex h-7 w-7 flex-none items-center justify-center rounded-md bg-brand-600 text-[12px] font-bold text-white">
......@@ -84,7 +84,7 @@ export function SupervisorHeader({
disabled={clinics.length < 2}
onClick={() => setOpen((v) => !v)}
className={cn(
'inline-flex h-7 items-center gap-1.5 rounded-md border border-slate-100 px-2 text-[12px] text-slate-600',
'inline-flex h-7 items-center gap-1.5 rounded-md border px-2 text-[12px] text-slate-600',
clinics.length > 1 && 'hover:bg-slate-50',
)}
>
......@@ -93,7 +93,7 @@ export function SupervisorHeader({
{clinics.length > 1 && <ChevronDown className="h-3 w-3 flex-none text-slate-300" />}
</button>
{open && (
<div className="absolute left-0 top-8 z-30 max-h-72 w-56 overflow-auto rounded-lg border border-slate-100 bg-white py-1 shadow-lg">
<div className="absolute left-0 top-8 z-30 max-h-72 w-56 overflow-auto rounded-lg border bg-white py-1 shadow-lg">
{clinics.map((c) => (
<button
key={c.id}
......
......@@ -65,8 +65,8 @@ export function TeamStatus({ clinicId }: { clinicId: string | null }) {
const flagOverdue = top > 0 && top > (sorted[1] ?? 0) ? top : null;
return (
<div className="flex min-h-0 w-full flex-col overflow-hidden rounded-lg border border-slate-100 bg-white">
<div className="flex flex-none flex-wrap items-center gap-x-2.5 gap-y-1 border-b border-slate-50 px-3.5 py-2.5">
<div className="flex min-h-0 w-full flex-col overflow-hidden rounded-lg border bg-white">
<div className="flex flex-none flex-wrap items-center gap-x-2.5 gap-y-1 border-b px-3.5 py-2.5">
<span className="text-[13.5px] font-semibold text-slate-900">团队现在什么状态</span>
<span className="nums text-[11px] text-slate-400">
{rows.length} 位在岗 · 在手 {inHandTotal}
......@@ -129,7 +129,7 @@ export function TeamStatus({ clinicId }: { clinicId: string | null }) {
{rows.map((a) => {
const worst = flagOverdue != null && a.overdue === flagOverdue;
return (
<tr key={a.userId} className="border-t border-slate-50">
<tr key={a.userId} className="border-t">
<td className="whitespace-nowrap px-3.5 py-2">
<span className="font-medium text-slate-900">{a.name ?? a.userId}</span>
{worst && (
......
......@@ -31,7 +31,7 @@ const PopoverContent = React.forwardRef<
align={align}
sideOffset={sideOffset}
className={cn(
'z-[70] w-auto rounded-md border border-slate-100 bg-white p-2 shadow-md outline-none',
'z-[70] w-auto rounded-md border bg-white p-2 shadow-md outline-none',
// ⚠️ 原先挂的是 shadcn 官方那串 `animate-in fade-in-0 …` —— 它们来自
// tailwindcss-animate 插件,**本仓没装**,所以一帧动画都没有(class 写了不报错,
// 只是不生效,最难发现的那种)。改用 globals.css 里的原生 keyframes。
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment