170 lines
5.8 KiB
TypeScript
170 lines
5.8 KiB
TypeScript
import Link from 'next/link';
|
|
import { ReactNode } from 'react';
|
|
import { Bot, Copy, Download, FileText } from 'lucide-react';
|
|
import { toast } from 'sonner';
|
|
import { Button } from '@/components/ui/button';
|
|
import { useScopedI18n } from '@/locales/client';
|
|
import { Questionnaire } from '@/types';
|
|
import { AssessmentRecord } from '@/lib/assessment-types';
|
|
import { recordToMarkdown } from '@/lib/assessment-export';
|
|
|
|
interface ResultContainerProps {
|
|
title: string;
|
|
id: string;
|
|
children: ReactNode;
|
|
questionnaire?: Questionnaire;
|
|
answers?: string[];
|
|
questionnaireResults?: Record<string, string>;
|
|
record?: AssessmentRecord;
|
|
profileName?: string;
|
|
}
|
|
|
|
function aiPrompt() {
|
|
return [
|
|
'请根据以下测评记录,用中文进行温和、非诊断式解读。',
|
|
'请重点分析:主要倾向、可能优势、需要留意的风险、后续可执行建议。',
|
|
'如果我明确要求你记住,请只记住适合长期追踪的测评背景和变化,不要记住真实身份信息。',
|
|
'注意:这不是医学或心理诊断。',
|
|
].join('\n');
|
|
}
|
|
|
|
export function ResultContainer({
|
|
title,
|
|
id,
|
|
children,
|
|
questionnaire,
|
|
answers,
|
|
questionnaireResults,
|
|
record,
|
|
profileName = '未命名档案',
|
|
}: ResultContainerProps) {
|
|
const t = useScopedI18n(
|
|
'component.questionnaire.result.public.resultContainer'
|
|
);
|
|
|
|
const handleCopyResultLink = async () => {
|
|
try {
|
|
await navigator.clipboard.writeText(
|
|
`${window.location.origin}${window.location.pathname}`,
|
|
);
|
|
toast.success(t('copySuccess'));
|
|
} catch {
|
|
toast.error(t('copyError'));
|
|
}
|
|
};
|
|
|
|
const buildResultMarkdown = () => {
|
|
if (record) {
|
|
return `${aiPrompt()}\n\n${recordToMarkdown(record, profileName)}`;
|
|
}
|
|
if (!questionnaire || !answers || !questionnaireResults) {
|
|
return null;
|
|
}
|
|
|
|
const currentTime = new Date().toLocaleString();
|
|
|
|
let resultData = `${aiPrompt()}\n\n# ${t('copyTemplate.title')}\n\n`;
|
|
resultData += `## ${t('copyTemplate.basicInfo')}\n`;
|
|
resultData += `- ${t('copyTemplate.questionnaireName')}: ${questionnaire.title}\n`;
|
|
resultData += `- ${t('copyTemplate.questionnaireId')}: ${id}\n`;
|
|
resultData += `- ${t('copyTemplate.assessmentTime')}: ${currentTime}\n`;
|
|
resultData += `- ${t('copyTemplate.questionCount')}: ${questionnaire.questions.length}\n\n`;
|
|
|
|
resultData += `## ${t('copyTemplate.questionsAndAnswers')}\n`;
|
|
Object.entries(questionnaireResults).forEach(([question, answer], index) => {
|
|
resultData += `${index + 1}. ${question}\n ${t('copyTemplate.answer')}: ${answer}\n\n`;
|
|
});
|
|
|
|
resultData += `## ${t('copyTemplate.usage')}\n`;
|
|
resultData += `${t('copyTemplate.disclaimer')}\n\n`;
|
|
resultData += `${t('copyTemplate.source')}: ${t('copyTemplate.platform')}\n`;
|
|
resultData += `${t('copyTemplate.website')}: ${window.location.origin}\n`;
|
|
|
|
return resultData;
|
|
};
|
|
|
|
const handleCopyResultData = async () => {
|
|
const resultData = buildResultMarkdown();
|
|
if (!resultData) {
|
|
toast.error(t('copyResultDataError'));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await navigator.clipboard.writeText(resultData);
|
|
toast.success(t('copyResultDataSuccess'));
|
|
} catch {
|
|
toast.error(t('copyResultDataError'));
|
|
}
|
|
};
|
|
|
|
const handleDownloadResultData = () => {
|
|
const resultData = buildResultMarkdown();
|
|
if (!resultData) {
|
|
toast.error(t('downloadResultDataError'));
|
|
return;
|
|
}
|
|
|
|
const blob = new Blob([resultData], { type: 'text/markdown;charset=utf-8' });
|
|
const url = URL.createObjectURL(blob);
|
|
const anchor = document.createElement('a');
|
|
anchor.href = url;
|
|
anchor.download = `${id}-result.md`;
|
|
anchor.click();
|
|
URL.revokeObjectURL(url);
|
|
toast.success(t('downloadResultDataSuccess'));
|
|
};
|
|
|
|
return (
|
|
<div className="flex min-h-screen items-center justify-center p-2 md:p-4">
|
|
<div className="w-full max-w-6xl rounded-lg border bg-white p-4 shadow-lg md:p-8">
|
|
<h1 className="mb-6 text-2xl font-bold">
|
|
{title} - {t('resultText')}
|
|
</h1>
|
|
|
|
<div className="mb-8">
|
|
<div className="space-y-6">{children}</div>
|
|
</div>
|
|
|
|
<div className="mb-6 flex gap-3 border bg-muted/30 p-3 text-sm leading-6 text-muted-foreground">
|
|
<Bot className="mt-0.5 h-4 w-4 shrink-0 text-foreground" />
|
|
<p>
|
|
可以复制完整记录给 ChatGPT 解读。若想长期追踪,可以在 ChatGPT 中明确说“请记住我的测评背景和后续变化”;不建议提交真实姓名、手机号等身份信息。
|
|
</p>
|
|
</div>
|
|
|
|
<div className="mt-8 flex flex-col gap-4 sm:flex-row sm:justify-between">
|
|
<Button variant="outline" className="w-full sm:w-auto">
|
|
<Link href={`/questionnaire/${id}`}>{t('backToDetail')}</Link>
|
|
</Button>
|
|
<div className="flex w-full flex-col gap-2 sm:w-auto sm:flex-row">
|
|
<Button variant="outline" onClick={handleCopyResultLink} className="w-full sm:w-auto">
|
|
<Copy className="mr-2 h-4 w-4" />
|
|
{t('copyResultLink')}
|
|
</Button>
|
|
<Button
|
|
variant="outline"
|
|
onClick={handleCopyResultData}
|
|
className="w-full sm:w-auto"
|
|
>
|
|
<FileText className="mr-2 h-4 w-4" />
|
|
复制给 AI 解读
|
|
</Button>
|
|
<Button
|
|
variant="outline"
|
|
onClick={handleDownloadResultData}
|
|
className="w-full sm:w-auto"
|
|
>
|
|
<Download className="mr-2 h-4 w-4" />
|
|
{t('downloadResultData')}
|
|
</Button>
|
|
<Button asChild className="w-full sm:w-auto">
|
|
<Link href="/records">查看测评档案</Link>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|