初始仓库迁移
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-04 23:57:06 +08:00
parent 80986b790d
commit c49c64896a
18446 changed files with 532435 additions and 2 deletions

View File

@@ -0,0 +1,160 @@
type AffinityLevelMeta = {
value: number;
label: string;
description: string;
accentClassName: string;
};
const DEFAULT_AFFINITY_LEVEL: AffinityLevelMeta = {
value: 0,
label: '戒备',
description: '对方仍保持明显距离,只会给出谨慎而有限的回应。',
accentClassName: 'border-white/12 bg-white/8 text-zinc-100',
};
const AFFINITY_LEVELS: AffinityLevelMeta[] = [
DEFAULT_AFFINITY_LEVEL,
{
value: 15,
label: '缓和',
description: '戒备已经开始松动,愿意正常交流,也会试探性配合你的节奏。',
accentClassName: 'border-sky-300/20 bg-sky-500/10 text-sky-100',
},
{
value: 30,
label: '友善',
description: '态度明显友善了许多,愿意配合行动,也会给出更真诚的反馈。',
accentClassName: 'border-emerald-300/20 bg-emerald-500/10 text-emerald-100',
},
{
value: 60,
label: '信任',
description: '双方已经建立稳定信任,对方更愿意分享想法、资源和立场。',
accentClassName: 'border-amber-300/20 bg-amber-500/10 text-amber-100',
},
{
value: 90,
label: '深交',
description: '关系已经非常亲近,对方几乎把你视作可以托付后背的自己人。',
accentClassName: 'border-rose-300/22 bg-rose-500/12 text-rose-100',
},
];
function getAffinityLevelMeta(affinity: number) {
return [...AFFINITY_LEVELS].reverse().find(level => affinity >= level.value) ?? DEFAULT_AFFINITY_LEVEL;
}
function getNextAffinityLevelMeta(affinity: number) {
return AFFINITY_LEVELS.find(level => affinity < level.value) ?? null;
}
export function AffinityStatusCard({affinity}: {affinity: number}) {
const currentLevel = getAffinityLevelMeta(affinity);
const nextLevel = getNextAffinityLevelMeta(affinity);
const maxVisibleAffinity = AFFINITY_LEVELS[AFFINITY_LEVELS.length - 1]?.value ?? 1;
const progress = Math.max(0, Math.min(1, affinity / maxVisibleAffinity));
return (
<div className="space-y-3">
<div className="rounded-xl border border-white/8 bg-black/20 px-4 py-3">
<div className="flex flex-wrap items-start justify-between gap-3">
<div>
<div className="text-[10px] tracking-[0.18em] text-zinc-500"></div>
<div className="mt-2 flex flex-wrap items-center gap-2">
<span className={`rounded-full border px-2.5 py-1 text-[10px] tracking-[0.16em] ${currentLevel.accentClassName}`}>
{currentLevel.label}
</span>
<span className="text-sm font-semibold text-white"> {affinity}</span>
</div>
</div>
<div className="text-right text-[10px] tracking-[0.16em] text-zinc-500">
{nextLevel ? (
<>
<div></div>
<div className="mt-1 text-zinc-200">
{nextLevel.label} · {nextLevel.value}
</div>
</>
) : (
<>
<div className="text-zinc-200"></div>
<div className="mt-1"></div>
</>
)}
</div>
</div>
<p className="mt-3 text-sm leading-relaxed text-zinc-300">{currentLevel.description}</p>
</div>
<div className="rounded-xl border border-white/8 bg-black/20 px-3 py-3 sm:px-4 sm:py-4">
<div className="text-[10px] tracking-[0.18em] text-zinc-500"></div>
<div className="mt-1 text-[11px] leading-relaxed text-zinc-500"></div>
<div className="relative mt-4 pt-1">
<div className="absolute left-0 right-0 top-5 h-px bg-gradient-to-r from-transparent via-white/18 to-transparent" />
<div className="absolute left-0 right-0 top-[1.02rem] h-2 rounded-full border border-white/8 bg-gradient-to-b from-white/[0.08] via-white/[0.03] to-black/35 shadow-[inset_0_1px_0_rgba(255,255,255,0.05)]" />
<div
className="absolute left-0 top-[1.02rem] h-2 rounded-full bg-gradient-to-r from-sky-300 via-amber-300 to-rose-300 shadow-[0_0_16px_rgba(251,191,36,0.16)]"
style={{width: `${progress * 100}%`}}
/>
{AFFINITY_LEVELS.map((level, index) => {
const ratio = maxVisibleAffinity > 0 ? level.value / maxVisibleAffinity : 0;
const isReached = affinity >= level.value;
const isCurrent = currentLevel.value === level.value;
const isFirst = index === 0;
const isLast = index === AFFINITY_LEVELS.length - 1;
return (
<div
key={`affinity-level-${level.value}`}
className="absolute top-0"
style={{
left: `${ratio * 100}%`,
transform: isFirst ? 'translateX(0)' : isLast ? 'translateX(-100%)' : 'translateX(-50%)',
}}
>
<div className="relative flex h-9 w-4 items-end justify-center sm:h-11 sm:w-5">
{isCurrent ? (
<div className="absolute bottom-0 h-8 w-3 rounded-full bg-sky-300/20 blur-[6px] sm:h-10 sm:w-4" />
) : null}
{isReached && !isCurrent ? (
<div className="absolute bottom-0 h-6 w-2.5 rounded-full bg-amber-300/10 blur-[4px] sm:h-7" />
) : null}
<div
className={`relative overflow-hidden rounded-full border transition-all duration-300 ${
isCurrent
? 'h-8 w-2 border-sky-50/75 bg-gradient-to-b from-white via-sky-100 to-cyan-300 shadow-[0_0_18px_rgba(125,211,252,0.35)] sm:h-10 sm:w-2.5'
: isReached
? 'h-6 w-1.5 border-amber-50/50 bg-gradient-to-b from-amber-100 via-amber-200 to-amber-400 shadow-[0_0_12px_rgba(251,191,36,0.18)] sm:h-7 sm:w-2'
: 'h-4 w-1.5 border-white/12 bg-gradient-to-b from-zinc-500/60 to-zinc-900/90 sm:h-5 sm:w-2'
}`}
>
<div className="absolute inset-x-0 top-0 h-1/3 bg-white/20" />
</div>
</div>
</div>
);
})}
<div className="grid grid-cols-5 gap-1 pt-10 sm:gap-2 sm:pt-12">
{AFFINITY_LEVELS.map(level => {
const isReached = affinity >= level.value;
return (
<div key={`affinity-label-${level.value}`} className="text-center">
<div className={`text-[9px] font-semibold leading-tight tracking-[0.08em] sm:text-[10px] sm:tracking-[0.14em] ${isReached ? 'text-zinc-100' : 'text-zinc-500'}`}>
{level.label}
</div>
<div className={`mt-0.5 text-[9px] leading-none sm:text-[10px] ${isReached ? 'text-zinc-300' : 'text-zinc-600'}`}>
{level.value}
</div>
</div>
);
})}
</div>
</div>
</div>
</div>
);
}