Files
MindScope/components/questionnaire/result/analysis/BigFiveResult.tsx
T
2026-06-22 22:59:01 +02:00

198 lines
6.2 KiB
TypeScript

'use client';
import {
calculateBigFiveResults,
calculateIpipNeoResults,
ipipNeoItemsByVersion,
} from '../../test/private/BigFiveCalculator';
import { ipipNeoFacets } from '@/questionairies/bigfive/neo-data';
interface BigFiveResultProps {
answers: string[];
version?: 50 | 120 | 300;
}
interface ScoreResult {
score: number;
average: number;
itemCount?: number;
}
const domainLabels = {
neuroticism: {
name: '神经质',
desc: '情绪敏感、压力反应以及体验焦虑、烦躁等负面情绪的倾向。',
},
extraversion: {
name: '外向性',
desc: '社交投入、自信表达、活跃度和积极情绪。',
},
openness: {
name: '开放性',
desc: '想象力、审美、求知、尝新和价值观开放程度。',
},
agreeableness: {
name: '宜人性',
desc: '信任、真诚、合作、利他、谦逊和同情倾向。',
},
conscientiousness: {
name: '尽责性',
desc: '自我效能、条理、责任、自律、成就追求和谨慎。',
},
};
const shortLabels = {
extraversion: domainLabels.extraversion,
agreeableness: domainLabels.agreeableness,
conscientiousness: domainLabels.conscientiousness,
emotionalStability: {
name: '情绪稳定性',
desc: '情绪平稳、压力耐受和较少焦虑烦躁。',
},
openness: domainLabels.openness,
};
function level(average: number) {
if (average >= 3.8) return '较高';
if (average <= 2.4) return '较低';
return '中等';
}
function barWidth(average: number) {
return `${Math.max(0, Math.min(100, ((average - 1) / 4) * 100))}%`;
}
function ScoreCard({
name,
desc,
result,
maxScore,
}: {
name: string;
desc: string;
result: ScoreResult;
maxScore: number;
}) {
return (
<div className="border rounded-lg p-5 bg-white shadow-sm">
<div className="flex items-start justify-between gap-4 mb-3">
<div>
<h4 className="font-semibold">{name}</h4>
<p className="text-sm text-muted-foreground mt-1">{desc}</p>
</div>
<div className="text-right shrink-0">
<div className="text-2xl font-semibold text-indigo-600">
{result.score}
</div>
<div className="text-xs text-muted-foreground">/ {maxScore}</div>
</div>
</div>
<div className="h-2 rounded-full bg-gray-100 overflow-hidden">
<div
className="h-full rounded-full bg-indigo-500"
style={{ width: barWidth(result.average) }}
/>
</div>
<div className="mt-3 text-sm">
倾向:<span className="font-medium">{level(result.average)}</span>
<span className="text-muted-foreground ml-2">
平均 {result.average.toFixed(2)} / 5
</span>
</div>
</div>
);
}
export function BigFiveResult({ answers, version = 50 }: BigFiveResultProps) {
if (version === 50) {
const results = calculateBigFiveResults(answers);
return (
<div className="mt-6 space-y-6">
<div className="bg-white border rounded-lg p-6 shadow-sm">
<h3 className="text-lg font-semibold mb-2">五大人格结果</h3>
<p className="text-sm text-muted-foreground">
每个维度满分50分。分数表示相对倾向,没有绝对好坏。
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{Object.entries(shortLabels).map(([key, info]) => (
<ScoreCard
key={key}
name={info.name}
desc={info.desc}
result={results[key] as ScoreResult}
maxScore={50}
/>
))}
</div>
</div>
);
}
const items = ipipNeoItemsByVersion[version];
const results = calculateIpipNeoResults(answers, [...items]);
const domainItemCount = version === 120 ? 24 : 60;
const facetItemCount = version === 120 ? 4 : 10;
return (
<div className="mt-6 space-y-8">
<div className="bg-white border rounded-lg p-6 shadow-sm">
<h3 className="text-lg font-semibold mb-2">
IPIP-NEO {version}题结果
</h3>
<p className="text-sm text-muted-foreground">
先看五大维度了解整体轮廓,再查看30个面向理解具体差异。分数代表相对倾向,没有绝对好坏。
</p>
</div>
<section>
<h3 className="text-lg font-semibold mb-4">五大维度</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{Object.entries(domainLabels).map(([key, info]) => (
<ScoreCard
key={key}
name={info.name}
desc={info.desc}
result={results.domains[key] as ScoreResult}
maxScore={domainItemCount * 5}
/>
))}
</div>
</section>
<section>
<h3 className="text-lg font-semibold mb-4">30个细分面向</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
{Object.entries(ipipNeoFacets).map(([key, facet]) => {
const result = results.facets[key] as ScoreResult;
return (
<div key={key} className="border rounded-lg bg-white p-4">
<div className="flex items-center justify-between gap-3">
<h4 className="font-medium">{facet.name}</h4>
<span className="text-sm font-semibold text-indigo-600">
{result.score} / {facetItemCount * 5}
</span>
</div>
<div className="mt-3 h-1.5 rounded-full bg-gray-100 overflow-hidden">
<div
className="h-full rounded-full bg-emerald-500"
style={{ width: barWidth(result.average) }}
/>
</div>
<div className="mt-2 text-xs text-muted-foreground">
{level(result.average)} · 平均 {result.average.toFixed(2)} / 5
</div>
</div>
);
})}
</div>
</section>
<div className="bg-gray-50 border rounded-lg p-4 text-sm text-gray-700">
建议结合维度和面向一起阅读:同一大维度下的六个面向可能高低不同,这通常比单一总分更能描述个人风格。
</div>
</div>
);
}