feat: 发布 v0.5.0 加密存储与匿名同步

This commit is contained in:
2026-06-23 01:48:01 +02:00
parent 81a70137a9
commit e3825c5a4e
20 changed files with 1091 additions and 70 deletions
+60 -10
View File
@@ -12,13 +12,25 @@ import {
} from '@/components/questionnaire/test/private/SchwartzCalculator';
const levels: Record<string, string> = {
minimal: '极轻或无', mild: '轻度', moderate: '中度', moderately_severe: '中重度',
severe: '重度', normal: '正常范围', extremely_severe: '极重度', low: '较低',
high: '较高', subthreshold: '亚阈值', no_insomnia: '无明显失眠',
minimal: '极轻或无',
mild: '轻度',
moderate: '中度',
moderately_severe: '中重度',
severe: '重度',
normal: '正常范围',
extremely_severe: '极重度',
low: '较低',
high: '较高',
subthreshold: '亚阈值',
no_insomnia: '无明显失眠',
};
const metric = (key: string, label: string, value: number, max?: number, level?: string): ScoreMetric => ({
key, label, value, max, level: level ? levels[level] || level : undefined,
key,
label,
value,
max,
level: level ? levels[level] || level : undefined,
});
function sum(answers: string[]) {
@@ -36,24 +48,50 @@ function severity(score: number, thresholds: Array<[number, string]>) {
export function buildScoreSummary(questionnaireId: string, answers: string[]): ScoreSummary {
if (questionnaireId === 'bigfive') {
const result = calculateBigFiveResults(answers);
const names: Record<string, string> = { extraversion: '外向性', agreeableness: '宜人性', conscientiousness: '尽责性', emotionalStability: '情绪稳定性', openness: '开放性' };
const names: Record<string, string> = {
extraversion: '外向性',
agreeableness: '宜人性',
conscientiousness: '尽责性',
emotionalStability: '情绪稳定性',
openness: '开放性',
};
return { metrics: Object.entries(result).map(([key, value]) => metric(key, names[key], value.score, 50)) };
}
if (questionnaireId === 'bigfive-120' || questionnaireId === 'bigfive-300') {
const version = questionnaireId === 'bigfive-120' ? 120 : 300;
const result = calculateIpipNeoResults(answers, [...ipipNeoItemsByVersion[version]]);
const names: Record<string, string> = { neuroticism: '神经质', extraversion: '外向性', openness: '开放性', agreeableness: '宜人性', conscientiousness: '尽责性' };
const names: Record<string, string> = {
neuroticism: '神经质',
extraversion: '外向性',
openness: '开放性',
agreeableness: '宜人性',
conscientiousness: '尽责性',
};
const max = version === 120 ? 120 : 300;
return { metrics: Object.entries(result.domains).map(([key, value]) => metric(key, names[key], value.score, max)) };
}
if (questionnaireId === 'hexaco') {
const names: Record<string, string> = { honestyHumility: '诚实谦逊', emotionality: '情绪性', extraversion: '外向性', agreeableness: '宜人性', conscientiousness: '尽责性', openness: '开放性' };
const names: Record<string, string> = {
honestyHumility: '诚实谦逊',
emotionality: '情绪性',
extraversion: '外向性',
agreeableness: '宜人性',
conscientiousness: '尽责性',
openness: '开放性',
};
return { metrics: Object.entries(calculateHEXACOResults(answers)).map(([key, value]) => metric(key, names[key], Number(value.toFixed(2)), 5)) };
}
if (questionnaireId === 'riasec') {
const result = calculateRIASECResults(answers);
return { primary: metric('holland', `霍兰德代码 ${result.hollandCode}`, result.ranking[0][1].score, 40), metrics: Object.entries(result.scores).map(([key, value]) => metric(key, riasecTypes[key as keyof typeof riasecTypes].name, value.score, 40)) };
return {
primary: metric('holland', `霍兰德代码 ${result.hollandCode}`, result.ranking[0][1].score, 40),
metrics: Object.entries(result.scores).map(([key, value]) => metric(key, riasecTypes[key as keyof typeof riasecTypes].name, value.score, 40)),
};
}
if (questionnaireId === 'schwartz') {
const result = calculateSchwartzResults(answers);
return { metrics: Object.entries(result.higherOrderScores).map(([key, value]) => metric(key, higherOrderNames[key as keyof typeof higherOrderNames], Number(value.toFixed(2)), 5)) };
@@ -67,22 +105,30 @@ export function buildScoreSummary(questionnaireId: string, answers: string[]): S
bdi2: { label: 'BDI-II 总分', max: 63, level: severity(raw, [[0, 'minimal'], [14, 'mild'], [20, 'moderate'], [29, 'severe']]) },
who5: { label: 'WHO-5 百分制得分', max: 100, score: raw * 4 },
};
if (single[questionnaireId]) {
const item = single[questionnaireId];
const primary = metric('total', item.label, item.score ?? raw, item.max, item.level);
return { primary, metrics: [primary] };
}
if (questionnaireId === 'dass21') {
const groups = { depression: [3, 5, 10, 13, 16, 17, 21], anxiety: [2, 4, 7, 9, 15, 19, 20], stress: [1, 6, 8, 11, 12, 14, 18] };
const groups = {
depression: [3, 5, 10, 13, 16, 17, 21],
anxiety: [2, 4, 7, 9, 15, 19, 20],
stress: [1, 6, 8, 11, 12, 14, 18],
};
const names = { depression: '抑郁', anxiety: '焦虑', stress: '压力' };
return { metrics: Object.entries(groups).map(([key, ids]) => metric(key, names[key as keyof typeof names], ids.reduce((total, id) => total + Number(keyed(answers)[id] || 0), 0) * 2, 42)) };
}
if (questionnaireId === 'pss10') {
const reverse = new Set([4, 5, 7, 8]);
const score = answers.reduce((total, answer, index) => total + (reverse.has(index + 1) ? 4 - Number(answer) : Number(answer)), 0);
const primary = metric('total', 'PSS-10 总分', score, 40, severity(score, [[0, 'low'], [14, 'moderate'], [27, 'high']]));
return { primary, metrics: [primary] };
}
if (questionnaireId === 'sds') {
const reverse = new Set([2, 5, 6, 11, 12, 14, 16, 17, 18, 20]);
const original = answers.reduce((total, answer, index) => total + (reverse.has(index + 1) ? 5 - Number(answer) : Number(answer)), 0);
@@ -92,5 +138,9 @@ export function buildScoreSummary(questionnaireId: string, answers: string[]): S
}
const primary = metric('raw', '原始作答总和', raw);
return { primary, metrics: [primary], note: '该数值仅用于保存和核对作答,不替代量表结果页中的正式解释。' };
return {
primary,
metrics: [primary],
note: '该数值仅用于保存和核对作答,不替代量表结果页中的正式解释。',
};
}