Commit 89a3b72c by luoqi

style(web): 卡片边框 slate-200 → slate-100 发丝线 — 降噪去"框感"(只动边框色)

现代清爽审美的常见做法:保留 1px 边框但压到极低对比(~6%),靠 bg 明度差+阴影分区。
全局 21 处 border-slate-200 统一软化为 slate-100,结构/阴影/间距/标签/文字一律不动。

web tsc 0。仅本地,未部署。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
parent d66dc855
......@@ -69,7 +69,7 @@ function EntryResolver() {
// ③ 空工作台:左栏可用(调筛选自己找人),右侧整体空态
return (
<div className="flex h-screen flex-col overflow-hidden bg-slate-50">
<header className="flex h-12 flex-none items-center gap-2 border-b border-slate-200 bg-white px-4">
<header className="flex h-12 flex-none items-center gap-2 border-b border-slate-100 bg-white px-4">
<span className="inline-flex h-7 w-7 items-center justify-center rounded-md bg-teal-600 text-[12px] font-bold text-white">
PAC
</span>
......@@ -89,7 +89,7 @@ function EntryResolver() {
<button
type="button"
onClick={() => window.location.reload()}
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 hover:bg-slate-50"
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"
>
<RefreshCw className="h-3.5 w-3.5" />
刷新
......
......@@ -85,7 +85,7 @@ function resultSummary(result: unknown): string {
function ToolCallView({ step }: { step: ToolStep }) {
const [open, setOpen] = useState(false);
return (
<div className="overflow-hidden rounded-lg border border-slate-200 bg-slate-50/70">
<div className="overflow-hidden rounded-lg border border-slate-100 bg-slate-50/70">
<button
type="button"
onClick={() => setOpen((o) => !o)}
......@@ -96,7 +96,7 @@ function ToolCallView({ step }: { step: ToolStep }) {
/>
<Wrench className="h-3.5 w-3.5 flex-none text-slate-400" />
<span className="flex-none text-[12px] text-slate-500">调用工具</span>
<code className="flex-none rounded border border-slate-200 bg-white px-1.5 py-0.5 font-mono text-[11.5px] text-slate-700">
<code className="flex-none rounded border border-slate-100 bg-white px-1.5 py-0.5 font-mono text-[11.5px] text-slate-700">
{step.tool}
</code>
<span className="ml-1 truncate text-[11px] text-slate-400">
......@@ -113,7 +113,7 @@ function ToolCallView({ step }: { step: ToolStep }) {
</span>
</button>
{open && (
<div className="space-y-2 border-t border-slate-200/70 px-3 pb-3 pt-1">
<div className="space-y-2 border-t border-slate-100/70 px-3 pb-3 pt-1">
<CodeBlock label="入参" code={pretty(step.args) || '{}'} />
{step.result !== undefined && <CodeBlock label="返回" code={pretty(step.result)} muted />}
</div>
......@@ -130,7 +130,7 @@ function CodeBlock({ label, code, muted }: { label: string; code: string; muted?
</div>
<pre
className={cn(
'overflow-x-auto rounded-md border border-slate-200 bg-white px-3 py-2 text-[11.5px] leading-relaxed text-slate-600',
'overflow-x-auto rounded-md border border-slate-100 bg-white px-3 py-2 text-[11.5px] leading-relaxed text-slate-600',
muted && 'max-h-44 overflow-y-auto',
)}
>
......@@ -160,7 +160,7 @@ function Markdown({ text }: { text: string }) {
{children}
</a>
),
hr: () => <hr className="my-3 border-slate-200" />,
hr: () => <hr className="my-3 border-slate-100" />,
blockquote: ({ children }) => (
<blockquote className="my-2 border-l-2 border-teal-300 bg-teal-50/40 py-1 pl-3 text-slate-600">
{children}
......@@ -175,7 +175,7 @@ function Markdown({ text }: { text: string }) {
</code>
),
pre: ({ children }) => (
<pre className="my-2 overflow-x-auto rounded-md border border-slate-200 bg-slate-50 p-3 text-[12px]">
<pre className="my-2 overflow-x-auto rounded-md border border-slate-100 bg-slate-50 p-3 text-[12px]">
{children}
</pre>
),
......@@ -186,9 +186,9 @@ function Markdown({ text }: { text: string }) {
),
thead: ({ children }) => <thead className="bg-slate-50">{children}</thead>,
th: ({ children }) => (
<th className="border border-slate-200 px-2 py-1 text-left font-medium text-slate-600">{children}</th>
<th className="border border-slate-100 px-2 py-1 text-left font-medium text-slate-600">{children}</th>
),
td: ({ children }) => <td className="border border-slate-200 px-2 py-1 text-slate-700">{children}</td>,
td: ({ children }) => <td className="border border-slate-100 px-2 py-1 text-slate-700">{children}</td>,
}}
>
{text}
......@@ -335,7 +335,7 @@ function ArtifactView({ artifact }: { artifact: Artifact }) {
};
return (
<div className="overflow-hidden rounded-lg border border-slate-200 bg-white shadow-sm">
<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">
<LayoutTemplate className="h-3.5 w-3.5 text-teal-600" />
<span className="text-[11.5px] font-medium text-slate-500">{artifact.title || '可视化卡片'}</span>
......@@ -358,7 +358,7 @@ 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-200 bg-white shadow-2xl">
<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">
<LayoutTemplate className="h-4 w-4 text-teal-600" />
<span className="text-[12.5px] font-medium text-slate-600">{artifact.title || '可视化卡片'}</span>
......@@ -457,14 +457,14 @@ function ModelSelect({
type="button"
disabled={disabled}
onClick={() => setOpen((o) => !o)}
className="inline-flex items-center gap-1.5 rounded-md border border-slate-200 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 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"
>
<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-200 bg-white py-1 shadow-lg">
<div className="absolute right-0 z-50 mt-1 w-40 rounded-lg border border-slate-100 bg-white py-1 shadow-lg">
{MODELS.map((m) => (
<button
key={m.value}
......@@ -630,7 +630,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-200 bg-white/90 backdrop-blur">
<header className="flex-none border-b border-slate-100 bg-white/90 backdrop-blur">
<div
className={cn(
'mx-auto flex items-center gap-2.5',
......@@ -681,7 +681,7 @@ export function AssistantChat({
key={ex}
type="button"
onClick={() => fire(ex)}
className="rounded-full border border-slate-200 bg-white px-3 py-1.5 text-[11.5px] text-slate-600 transition-colors hover:border-teal-300 hover:bg-teal-50/50 hover:text-teal-700"
className="rounded-full border border-slate-100 bg-white px-3 py-1.5 text-[11.5px] text-slate-600 transition-colors hover:border-teal-300 hover:bg-teal-50/50 hover:text-teal-700"
>
{ex}
</button>
......@@ -701,9 +701,9 @@ export function AssistantChat({
</div>
{/* Composer */}
<div className="flex-none border-t border-slate-200 bg-white">
<div className="flex-none border-t border-slate-100 bg-white">
<div className="mx-auto max-w-3xl px-4 py-3">
<div className="flex items-end gap-2 rounded-xl border border-slate-200 bg-white px-3 py-2 transition-all focus-within:border-teal-300 focus-within:ring-2 focus-within:ring-teal-200">
<div className="flex items-end gap-2 rounded-xl border border-slate-100 bg-white px-3 py-2 transition-all focus-within:border-teal-300 focus-within:ring-2 focus-within:ring-teal-200">
<textarea
ref={taRef}
rows={1}
......
......@@ -53,7 +53,7 @@ export function AssistantWidget() {
<div
style={winPos ? { left: winPos.x, top: winPos.y } : undefined}
className={cn(
'fixed z-[60] flex flex-col overflow-hidden rounded-xl border border-slate-200 shadow-2xl',
'fixed z-[60] flex flex-col overflow-hidden rounded-xl border border-slate-100 shadow-2xl',
!winPos && 'bottom-4 right-4',
'h-[620px] max-h-[78vh] w-[400px] max-w-[calc(100vw-2rem)]',
'transition-[opacity,transform] duration-150',
......
......@@ -141,7 +141,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-teal-400 bg-teal-50/60 ring-1 ring-teal-200'
: 'border-slate-200 hover:border-teal-300',
: 'border-slate-100 hover:border-teal-300',
)}
>
<span className="text-[12.5px] font-semibold text-slate-800">{r.nameZh}</span>
......@@ -152,7 +152,7 @@ export function MockLoginDialog({ open }: { open: boolean }) {
</div>
{/* ② 选客服 */}
<div className="rounded-lg border border-slate-200 p-3">
<div className="rounded-lg border border-slate-100 p-3">
<div className="mb-2 flex items-center justify-between">
<div className="text-[12px] font-medium text-slate-600">
② 选客服 — 以其真实身份 + 上面的「{roleNameZh(role)}」权限登录
......@@ -174,7 +174,7 @@ export function MockLoginDialog({ open }: { open: boolean }) {
'rounded-full border px-2.5 py-0.5 text-[11px] transition-colors',
tenantFilter === t.k
? 'border-teal-400 bg-teal-50 text-teal-700'
: 'border-slate-200 text-slate-500 hover:border-teal-300',
: 'border-slate-100 text-slate-500 hover:border-teal-300',
)}
>
{t.n}
......@@ -184,7 +184,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-200 px-2.5 py-1 text-[12px] outline-none focus:border-teal-400"
className="ml-auto w-40 rounded-md border border-slate-100 px-2.5 py-1 text-[12px] outline-none focus:border-teal-400"
/>
</div>
......@@ -204,7 +204,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-teal-400 ring-2 ring-teal-200'
: 'border-slate-200 hover:border-teal-400 hover:bg-teal-50/40',
: 'border-slate-100 hover:border-teal-400 hover:bg-teal-50/40',
busy && !loading && 'opacity-50',
)}
>
......@@ -251,7 +251,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-teal-400 ring-2 ring-teal-200'
: 'border-slate-200 hover:border-teal-400 hover:bg-teal-50/40',
: 'border-slate-100 hover:border-teal-400 hover:bg-teal-50/40',
busy && !loading && 'opacity-50',
)}
>
......
......@@ -275,7 +275,7 @@ function TargetTimelineRow({ chain }: { chain: Chain }) {
function HistoryStrip({ chains }: { chains: Chain[] }) {
return (
<div className="rounded-lg border border-slate-200 bg-slate-50/60 px-3 py-2">
<div className="rounded-lg border border-slate-100 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-200 px-2.5 py-1.5 flex items-center gap-2">
<div className="bg-white rounded border border-slate-100 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-200/70',
slate: 'bg-slate-50/40 border-slate-100/70',
};
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-200">
<div className="pt-3 border-t border-slate-100">
<div className="text-[11px] text-slate-500 mb-2">
全部 {chains.length} 条治疗链
</div>
......@@ -548,7 +548,7 @@ function ChainListItem({
'text-left rounded-md border px-2.5 py-2 transition',
active
? activeRing[v.tone]
: 'border-slate-200 bg-white hover:bg-slate-50',
: 'border-slate-100 bg-white hover:bg-slate-50',
)}
>
<div className="flex items-center gap-1.5">
......
......@@ -107,7 +107,7 @@ export function Drawer({
const T = tone(f.tone);
const { tag, text } = cleanPersonaValue(f.value);
return (
<div key={f.key} className="relative rounded-md border border-slate-200 p-3">
<div key={f.key} className="relative rounded-md border border-slate-100 p-3">
{/* 右上角 ? hover 看算法说明 */}
<PersonaFeatureHover featureKey={f.key}>
<span
......@@ -141,7 +141,7 @@ export function Drawer({
<div className="fixed inset-0 z-40 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-200 flex flex-col', width)}
className={cn('max-w-[92vw] bg-white shadow-2xl border-l border-slate-100 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">
......@@ -243,7 +243,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-200 p-3">
<div key={f.id} className="rounded-md border border-slate-100 p-3">
<div className="flex items-center justify-between mb-1.5">
<div className="flex items-center gap-2">
<Chip tone="violet" size="xs">
......@@ -285,7 +285,7 @@ function ImageFactsView({ facts }: { facts: AdaptedFact[] }) {
function CBCTImageView() {
return (
<div className="space-y-3">
<div className="aspect-[4/3] rounded-lg border border-slate-200 bg-slate-900 relative overflow-hidden">
<div className="aspect-[4/3] rounded-lg border border-slate-100 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%)' }}
......
......@@ -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-200 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 border-slate-100 bg-white hover:bg-slate-50 hover:border-slate-300 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-200 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 border-slate-100 bg-white hover:bg-slate-50 hover:border-slate-300 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-200 bg-white overflow-hidden">
<section className="rounded-lg border border-slate-100 bg-white overflow-hidden">
{/* 头:日期 + 医生 + 第几次 */}
<header className="px-3 py-2 bg-slate-50 border-b border-slate-200 flex items-center justify-between flex-wrap gap-2">
<header className="px-3 py-2 bg-slate-50 border-b border-slate-100 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>
......
......@@ -119,7 +119,7 @@ export function FactsTimeline({ facts }: { facts: AdaptedFact[] }) {
'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-200 bg-white text-slate-600',
: 'border-slate-100 bg-white text-slate-600',
)}
>
<option value="">全部牙位</option>
......@@ -205,7 +205,7 @@ function StatsSummary({ facts }: { facts: AdaptedFact[] }) {
t ? new Date(t).toLocaleDateString('zh-CN').replace(/\//g, '.') : '—';
return (
<div className="rounded-lg border border-slate-200 bg-slate-50/60 p-2.5">
<div className="rounded-lg border border-slate-100 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">
......@@ -344,7 +344,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-200';
const STATUS_BADGE = 'px-1.5 py-px rounded border text-[10.5px] bg-slate-50 text-slate-600 border-slate-100';
// 右列数据 — 按 fact 类型挑最关键的 1-2 行
interface RightLine { text: string; className?: string }
......@@ -439,7 +439,7 @@ function FilterChip({
'inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-[11px] border transition-colors',
active
? 'bg-sky-50 text-sky-700 border-sky-200 font-medium'
: 'bg-white text-slate-500 border-slate-200 hover:bg-slate-50',
: 'bg-white text-slate-500 border-slate-100 hover:bg-slate-50',
)}
>
<span>{label}</span>
......
......@@ -125,7 +125,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-200 px-2.5 py-1.5 text-[11px] text-slate-500 leading-snug">
<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">
本任务已{plan.status === 'abandoned' ? '放弃' : plan.status === 'superseded' ? '被新版本替代' : '结案'},不可再提交执行;如需重新跟进,等召回重新生成。
</div>
)}
......@@ -145,7 +145,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-200 hover:border-slate-300 hover:bg-slate-50',
: 'bg-white text-slate-700 border-slate-100 hover:border-slate-300 hover:bg-slate-50',
)}
>
<svg
......@@ -186,7 +186,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-200 hover:border-slate-300 hover:bg-slate-50',
: 'bg-white text-slate-700 border-slate-100 hover:border-slate-300 hover:bg-slate-50',
)}
>
<span className={cn('flex-none w-1.5 h-1.5 rounded-full', selected ? T.dot : 'bg-slate-300')} />
......@@ -267,7 +267,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-200 bg-white text-[12.5px] resize-none focus:outline-none focus:ring-2 focus:ring-teal-200 focus:border-teal-300"
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-teal-200 focus:border-teal-300"
/>
</div>
......
......@@ -30,7 +30,7 @@ export function PersonaFeatureHover({
<HoverCardTrigger asChild>{children}</HoverCardTrigger>
<HoverCardContent align="end" sideOffset={6} className="w-80 p-3 text-[11.5px]">
<div className="space-y-2">
<div className="flex items-baseline justify-between border-b border-slate-200 pb-1.5">
<div className="flex items-baseline justify-between border-b border-slate-100 pb-1.5">
<span className="text-[13px] font-semibold text-slate-900">{meta?.title ?? featureKey}</span>
{meta?.subtitle && <span className="text-[10.5px] text-slate-500">{meta.subtitle}</span>}
</div>
......
......@@ -347,7 +347,7 @@ export function PlanDetailApp({
}
centerPane={
<main className="relative min-h-0 flex flex-col h-full">
<section className="bg-white rounded-lg border border-slate-200 shadow-sm flex flex-col min-h-0 flex-1 overflow-hidden">
<section className="bg-white rounded-lg border border-slate-100 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 flex flex-wrap items-center justify-between gap-x-2 gap-y-2">
<div className="min-w-0">
......@@ -458,7 +458,7 @@ export function PlanDetailApp({
}
rightPane={
<aside className="min-h-0 flex flex-col gap-2.5 overflow-hidden h-full">
<section className="bg-white rounded-lg border border-slate-200 shadow-sm flex flex-col min-h-0 flex-1 overflow-hidden">
<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 flex items-center justify-between gap-2">
<div>
<h2 className="text-[14px] font-semibold text-slate-900 leading-tight">通话结果</h2>
......@@ -615,7 +615,7 @@ function RecallFeedbackControl({
return (
<>
<div
className="hidden sm:flex flex-none items-center gap-0.5 rounded-md border border-slate-200 px-1 py-0.5"
className="hidden sm:flex flex-none items-center gap-0.5 rounded-md border border-slate-100 px-1 py-0.5"
title="召回反馈:这条召回准不准?"
>
<span className="hidden lg:inline px-1 text-[10.5px] text-slate-400">召回反馈</span>
......@@ -665,7 +665,7 @@ function RecallFeedbackControl({
type="button"
title={f.hint}
onClick={() => quickInsert(f.labelZh)}
className="px-2 py-1 rounded-full text-[12px] border border-slate-200 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 border-slate-100 bg-white text-slate-600 hover:border-rose-300 hover:bg-rose-50 hover:text-rose-700 transition-colors"
>
{f.labelZh}
</button>
......@@ -676,14 +676,14 @@ function RecallFeedbackControl({
onChange={(e) => setNote(e.target.value)}
placeholder="补充说明(选填)…"
rows={3}
className="w-full px-2.5 py-2 rounded border border-slate-200 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 border-slate-100 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-200 text-[13px] text-slate-600 hover:bg-slate-50"
className="px-3 py-1.5 rounded-md border border-slate-100 text-[13px] text-slate-600 hover:bg-slate-50"
>
取消
</button>
......@@ -757,7 +757,7 @@ function TopBar({
};
return (
<header className="flex flex-none items-center justify-between gap-2 border-b border-slate-200 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 border-slate-100 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">
<div className="inline-flex flex-none items-center gap-2">
<span className="inline-flex h-7 w-7 items-center justify-center rounded-md bg-teal-600 text-[12px] font-bold text-white">
......@@ -811,8 +811,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-200 bg-slate-50 text-slate-400'
: 'border-slate-200 bg-white text-slate-700 hover:border-teal-300 hover:bg-teal-50 hover:text-teal-700',
? 'cursor-not-allowed border-slate-100 bg-slate-50 text-slate-400'
: 'border-slate-100 bg-white text-slate-700 hover:border-teal-300 hover:bg-teal-50 hover:text-teal-700',
)}
>
<RefreshCw className={cn('h-3.5 w-3.5', refreshing && 'animate-spin')} />
......@@ -1001,7 +1001,7 @@ function IdentityCard({
setTimeout(() => setCopied(false), 1500);
};
return (
<section className="bg-white rounded-lg border border-slate-200 shadow-sm flex-none">
<section className="bg-white rounded-lg border border-slate-100 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-center justify-between gap-2">
......@@ -1739,10 +1739,10 @@ function RegenBtn({
'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-200 hover:text-teal-700 hover:bg-teal-50',
: 'text-slate-500 border-slate-100 hover:text-teal-700 hover:bg-teal-50',
);
return (
<div className="inline-flex flex-none items-stretch rounded border border-slate-200 overflow-hidden">
<div className="inline-flex flex-none items-stretch rounded border border-slate-100 overflow-hidden">
{/* 档位选择(只选,不触发) */}
<DropdownMenu>
<DropdownMenuTrigger asChild disabled={streaming}>
......
......@@ -91,11 +91,11 @@ export type OracleVerdict = {
const VERDICT_META: Record<OracleVerdictKind, { zh: string; tone: string }> = {
recall: { zh: '应召回', tone: 'bg-emerald-50 text-emerald-700 border-emerald-200' },
resolved: { zh: '已治疗', tone: 'bg-slate-100 text-slate-500 border-slate-200' },
superseded: { zh: '被取代', tone: 'bg-slate-100 text-slate-400 border-slate-200' },
resolved: { zh: '已治疗', tone: 'bg-slate-100 text-slate-500 border-slate-100' },
superseded: { zh: '被取代', tone: 'bg-slate-100 text-slate-400 border-slate-100' },
cooldown: { zh: '考虑期', tone: 'bg-amber-50 text-amber-700 border-amber-200' },
ineligible: { zh: '非修复', tone: 'bg-slate-100 text-slate-400 border-slate-200' },
suppressed: { zh: '被压制', tone: 'bg-slate-100 text-slate-500 border-slate-200' },
ineligible: { zh: '非修复', tone: 'bg-slate-100 text-slate-400 border-slate-100' },
suppressed: { zh: '被压制', tone: 'bg-slate-100 text-slate-500 border-slate-100' },
};
export function verdictMeta(kind: OracleVerdictKind) {
......
......@@ -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-200 bg-slate-50 p-2.5">
<div className="mb-3 rounded-lg border border-slate-100 bg-slate-50 p-2.5">
<ol className="space-y-1.5">
{steps.map((s) => {
const hasDetail = stepHasDetail(s);
......
......@@ -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-200 bg-white overflow-hidden">
<div key={sec.id} className="rounded-md border border-slate-100 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"
......@@ -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-200 bg-white p-3 flex flex-col">
<div key={sec.id} className="rounded-md border border-slate-100 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-teal-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-200 p-1.5">
<div className="flex items-center gap-1.5 rounded-md bg-slate-50 border border-slate-100 p-1.5">
{sections.map((s, i) => (
<button
key={s.id}
......
......@@ -141,7 +141,7 @@ export function SidebarCard({
return (
<section
className={cn(
'bg-white rounded-lg border border-slate-200 shadow-sm flex flex-col min-h-0 flex-none',
'bg-white rounded-lg border border-slate-100 shadow-sm flex flex-col min-h-0 flex-none',
className,
)}
>
......
......@@ -99,7 +99,7 @@ export function TaskDrawer({ currentPlanId }: { currentPlanId: string }) {
<aside
className={cn(
'fixed left-0 top-0 bottom-0 w-[320px] bg-white border-r border-slate-200 shadow-xl z-50',
'fixed left-0 top-0 bottom-0 w-[320px] bg-white border-r border-slate-100 shadow-xl z-50',
'transition-transform duration-200 ease-out flex flex-col',
open ? 'translate-x-0' : '-translate-x-full',
)}
......@@ -152,7 +152,7 @@ export function TaskDrawer({ currentPlanId }: { currentPlanId: string }) {
placeholder="搜索 姓名 / 手机 / ID(已加载项)"
value={search}
onChange={(e) => setSearch(e.target.value)}
className="w-full pl-7 pr-2.5 py-1.5 rounded-md border border-slate-200 bg-slate-50 text-[12px] text-slate-800 focus:outline-none focus:ring-2 focus:ring-teal-200 focus:border-teal-300 focus:bg-white transition-all"
className="w-full pl-7 pr-2.5 py-1.5 rounded-md border border-slate-100 bg-slate-50 text-[12px] text-slate-800 focus:outline-none focus:ring-2 focus:ring-teal-200 focus:border-teal-300 focus:bg-white transition-all"
/>
</div>
<div className="grid grid-cols-3 gap-1">
......@@ -223,7 +223,7 @@ function KpiMini({
}) {
const T = KPI_TONE[tone] ?? '';
return (
<div className="flex items-center gap-2 rounded-lg border border-slate-200 bg-white px-2 py-1.5">
<div className="flex items-center gap-2 rounded-lg border border-slate-100 bg-white px-2 py-1.5">
<span className={cn('inline-flex h-7 w-7 flex-none items-center justify-center rounded-lg ring-1 ring-inset', T)}>
{icon}
</span>
......@@ -256,7 +256,7 @@ function TaskRow({
'w-full text-left rounded-md border px-2.5 py-2 transition-colors',
active
? 'border-teal-400 bg-teal-50/60 ring-1 ring-teal-100'
: 'border-slate-200 bg-white hover:border-slate-300 hover:bg-slate-50',
: 'border-slate-100 bg-white hover:border-slate-300 hover:bg-slate-50',
done && 'opacity-70',
)}
>
......
......@@ -81,7 +81,7 @@ export function ToothTimeline({ facts }: { facts: AdaptedFact[] }) {
);
const isWhole = k.startsWith('全口');
return (
<div key={k} className="rounded-lg border border-slate-200 overflow-hidden">
<div key={k} className="rounded-lg border border-slate-100 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',
......
......@@ -112,7 +112,7 @@ export function PatientPickerRail({
});
return (
<aside className="flex h-full w-[300px] flex-none flex-col border-r border-slate-200 bg-white">
<aside className="flex h-full w-[300px] flex-none flex-col border-r border-slate-100 bg-white">
{/* view tabs */}
<div className="flex flex-none gap-1 border-b border-slate-100 px-2 pt-2">
{VIEW_TABS.filter((t) => t.v !== 'all' || canViewAll).map((t) => (
......@@ -144,14 +144,14 @@ export function PatientPickerRail({
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="姓名 / 手机 / 患者号"
className="h-7 w-full rounded-md border border-slate-200 bg-slate-50 pl-7 pr-2 text-[12px] outline-none placeholder:text-slate-400 focus:border-teal-300 focus:bg-white"
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-teal-300 focus:bg-white"
/>
</div>
<div className="flex items-center gap-1.5">
<select
value={sort}
onChange={(e) => setSort(e.target.value as PickerFilters['sort'])}
className="h-6.5 rounded-md border border-slate-200 bg-white px-1.5 py-0.5 text-[11px] text-slate-600"
className="h-6.5 rounded-md border border-slate-100 bg-white px-1.5 py-0.5 text-[11px] text-slate-600"
>
<option value="priority_desc">优先级 高→低</option>
<option value="priority_asc">优先级 低→高</option>
......@@ -165,7 +165,7 @@ export function PatientPickerRail({
'inline-flex h-6 items-center gap-1 rounded-md border px-1.5 text-[11px] transition-colors',
realPhoneOnly
? 'border-teal-300 bg-teal-50 font-medium text-teal-700'
: 'border-slate-200 bg-white text-slate-500 hover:bg-slate-50',
: 'border-slate-100 bg-white text-slate-500 hover:bg-slate-50',
)}
>
<PhoneCall className="h-3 w-3" />
......@@ -260,7 +260,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-teal-300 bg-teal-50 font-medium text-teal-700'
: 'border-slate-200 bg-white text-slate-500 hover:bg-slate-50',
: 'border-slate-100 bg-white text-slate-500 hover:bg-slate-50',
)}
>
<Filter className="h-3 w-3" />
......@@ -295,7 +295,7 @@ function PersonaTagFilter({
'rounded-full border px-2 py-0.5 text-[11px] transition-colors',
on
? 'border-teal-300 bg-teal-50 font-medium text-teal-700'
: 'border-slate-200 bg-white text-slate-600 hover:border-teal-200 hover:bg-teal-50/40',
: 'border-slate-100 bg-white text-slate-600 hover:border-teal-200 hover:bg-teal-50/40',
)}
>
{o.zh}
......@@ -337,7 +337,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-teal-300 bg-teal-50 font-medium text-teal-700'
: 'border-slate-200 bg-white text-slate-500 hover:bg-slate-50',
: 'border-slate-100 bg-white text-slate-500 hover:bg-slate-50',
)}
>
<span className="truncate">
......
......@@ -240,7 +240,7 @@ export function PlansListApp() {
<div className="flex h-full min-h-screen flex-col bg-slate-50">
<PageHeader user={user} mineAssigned={counts.mineAssigned} poolHi={counts.pool} onRefresh={refreshAll} />
<div className="grid flex-none grid-cols-2 gap-3 border-b border-slate-200 bg-slate-50 px-5 py-3 md:grid-cols-4">
<div className="grid flex-none grid-cols-2 gap-3 border-b border-slate-100 bg-slate-50 px-5 py-3 md:grid-cols-4">
<KpiCard tone="amber" icon={<ListChecks className="h-4 w-4" />} label="我的进行中" value={counts.mineAssigned} sub={`共 ${counts.mine} 个工单`} />
<KpiCard tone="rose" icon={<Flame className="h-4 w-4" />} label="召回池余量" value={counts.pool} sub="池中待领取" />
<KpiCard tone="emerald" icon={<ListChecks className="h-4 w-4" />} label="我完成" value={counts.mineCompleted} sub={successRateText(counts)} />
......@@ -354,7 +354,7 @@ function PageHeader({
}) {
const greeting = mineAssigned > 0 ? `今天好,还有 ${mineAssigned} 个待打` : poolHi > 0 ? `池中还有 ${poolHi} 个待领取` : '今天好';
return (
<header className="flex flex-none items-center justify-between gap-3 border-b border-slate-200 bg-white px-5 py-3">
<header className="flex flex-none items-center justify-between gap-3 border-b border-slate-100 bg-white px-5 py-3">
<div className="flex min-w-0 items-center gap-3">
<div className="inline-flex items-center gap-2">
<span className="inline-flex h-7 w-7 items-center justify-center rounded-md bg-teal-600 text-[12px] font-bold text-white">PAC</span>
......@@ -421,7 +421,7 @@ function KpiCard({
}) {
const T = TONE_BG[tone] ?? '';
return (
<div className="flex items-center gap-3 rounded-lg border border-slate-200 bg-white px-3 py-2.5">
<div className="flex items-center gap-3 rounded-lg border border-slate-100 bg-white px-3 py-2.5">
<span className={cn('inline-flex h-9 w-9 flex-none items-center justify-center rounded-lg ring-1 ring-inset', T)}>
{icon}
</span>
......@@ -462,7 +462,7 @@ function ViewTabs({
{ v: 'all', label: '全部', hint: '全状态', gated: !canViewAll },
];
return (
<div className="border-b border-slate-200">
<div className="border-b border-slate-100">
<div className="flex items-center px-3">
{tabs.map((t) => {
const active = current === t.v;
......@@ -532,7 +532,7 @@ function ClinicFilter({
return (
<Popover>
<PopoverTrigger asChild>
<button className="inline-flex h-8 items-center gap-1.5 rounded-md border border-slate-200 bg-white px-3 text-xs hover:border-slate-300">
<button className="inline-flex h-8 items-center gap-1.5 rounded-md border border-slate-100 bg-white px-3 text-xs hover:border-slate-300">
<span className={cn(selected.length ? 'font-medium text-slate-900' : 'text-slate-600')}>{label}</span>
<ChevronDown className="h-3.5 w-3.5 text-slate-400" />
</button>
......@@ -613,7 +613,7 @@ function FilterBar({
{ value: 'abandoned', label: '已放弃' },
];
return (
<div className="flex flex-none flex-wrap items-center gap-2 border-b border-slate-200 bg-white px-5 py-2.5">
<div className="flex flex-none flex-wrap items-center gap-2 border-b border-slate-100 bg-white px-5 py-2.5">
<div className="relative">
<Search className="absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-slate-400" />
<Input
......@@ -654,7 +654,7 @@ function FilterBar({
'inline-flex h-8 items-center gap-1 rounded-md border px-2.5 text-xs transition-colors',
realPhoneOnly
? 'border-teal-300 bg-teal-50 font-medium text-teal-700'
: 'border-slate-200 bg-white text-slate-600 hover:bg-slate-50',
: 'border-slate-100 bg-white text-slate-600 hover:bg-slate-50',
)}
>
<PhoneCall className="h-3.5 w-3.5" />
......@@ -763,7 +763,7 @@ function PatientPlanCard({
<article
className={cn(
'group relative flex flex-col rounded-lg border bg-white transition-all',
selected ? 'border-teal-400 ring-2 ring-teal-100' : 'border-slate-200 hover:border-slate-300 hover:shadow-sm',
selected ? 'border-teal-400 ring-2 ring-teal-100' : 'border-slate-100 hover:border-slate-300 hover:shadow-sm',
isClosed && 'opacity-75',
)}
>
......@@ -903,7 +903,7 @@ function PatientPlanCard({
{!isPool && !(isMine && p.status === 'assigned') && (
<Link
href={`/plans/${p.id}`}
className="rounded-md border border-slate-200 px-2.5 py-1 text-[12px] text-slate-700 hover:bg-slate-50"
className="rounded-md border border-slate-100 px-2.5 py-1 text-[12px] text-slate-700 hover:bg-slate-50"
>
详情 →
</Link>
......@@ -972,7 +972,7 @@ function Pagination({
}) {
const totalPages = Math.max(1, Math.ceil(total / pageSize));
return (
<div className="flex flex-none items-center justify-between border-t border-slate-200 bg-white px-5 py-2.5 text-[11.5px] text-slate-500">
<div className="flex flex-none items-center justify-between border-t border-slate-100 bg-white px-5 py-2.5 text-[11.5px] text-slate-500">
<div>
<b className="nums font-semibold text-slate-800">{total}</b> 条 · 第 <b className="nums">{page}</b>/<span className="nums">{totalPages}</span>
</div>
......
......@@ -111,7 +111,7 @@ function PriorityBreakdownTable({
function Header({ disp }: { disp: string }) {
return (
<div className="flex items-baseline justify-between border-b border-slate-200 pb-1.5">
<div className="flex items-baseline justify-between border-b border-slate-100 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>
......
......@@ -17,7 +17,7 @@ const PopoverContent = React.forwardRef<
align={align}
sideOffset={sideOffset}
className={cn(
'z-50 w-auto rounded-md border border-slate-200 bg-white p-2 shadow-md outline-none',
'z-50 w-auto rounded-md border border-slate-100 bg-white p-2 shadow-md outline-none',
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
className,
)}
......
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