feat: 增加本地测评档案与长期追踪
This commit is contained in:
@@ -5,6 +5,8 @@ import { useScopedI18n } from '@/locales/client';
|
||||
import { Copy, Download, FileText } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
import { Questionnaire } from '@/types';
|
||||
import { AssessmentRecord } from '@/lib/assessment-types';
|
||||
import { recordToMarkdown } from '@/lib/assessment-export';
|
||||
|
||||
interface ResultContainerProps {
|
||||
title: string;
|
||||
@@ -13,9 +15,11 @@ interface ResultContainerProps {
|
||||
questionnaire?: Questionnaire;
|
||||
answers?: string[];
|
||||
questionnaireResults?: Record<string, string>;
|
||||
record?: AssessmentRecord;
|
||||
profileName?: string;
|
||||
}
|
||||
|
||||
export function ResultContainer({ title, id, children, questionnaire, answers, questionnaireResults }: ResultContainerProps) {
|
||||
export function ResultContainer({ title, id, children, questionnaire, answers, questionnaireResults, record, profileName = '未命名档案' }: ResultContainerProps) {
|
||||
const t = useScopedI18n(
|
||||
'component.questionnaire.result.public.resultContainer'
|
||||
);
|
||||
@@ -32,6 +36,9 @@ export function ResultContainer({ title, id, children, questionnaire, answers, q
|
||||
};
|
||||
|
||||
const buildResultMarkdown = () => {
|
||||
if (record) {
|
||||
return recordToMarkdown(record, profileName);
|
||||
}
|
||||
if (!questionnaire || !answers || !questionnaireResults) {
|
||||
return null;
|
||||
}
|
||||
@@ -125,6 +132,9 @@ export function ResultContainer({ title, id, children, questionnaire, answers, q
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
{t('downloadResultData')}
|
||||
</Button>
|
||||
<Button asChild className="w-full sm:w-auto">
|
||||
<Link href="/records">查看测评档案</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,6 +10,10 @@ import { saveResult } from '@/lib/result-storage';
|
||||
import { Questionnaire as QuestionnaireType, QuestionType } from '@/types';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { toast } from "sonner"
|
||||
import { ProfilePicker } from '@/components/records/ProfilePicker';
|
||||
import { AssessmentProfile } from '@/lib/assessment-types';
|
||||
import { addAssessmentRecord, ensureActiveProfile } from '@/lib/assessment-db';
|
||||
import { buildScoreSummary } from '@/lib/score-summary';
|
||||
|
||||
interface QuestionnaireProps {
|
||||
questionnaire: QuestionnaireType;
|
||||
@@ -32,6 +36,7 @@ export function Questionnaire({
|
||||
const questionRefs = useRef<{ [key: number]: HTMLDivElement | null }>({});
|
||||
// Flag to indicate whether the questionnaire has been submitted
|
||||
const hasSubmittedRef = useRef(false);
|
||||
const [activeProfile, setActiveProfile] = useState<AssessmentProfile | null>(null);
|
||||
|
||||
// Save answers when component unmounts
|
||||
useEffect(() => {
|
||||
@@ -163,7 +168,7 @@ export function Questionnaire({
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
const handleSubmit = async () => {
|
||||
// Check if all questions are answered first
|
||||
if (answeredCount < questions.length) {
|
||||
toast("请先完成所有题目");
|
||||
@@ -179,7 +184,32 @@ export function Questionnaire({
|
||||
|
||||
// Store answers in this browser tab instead of putting them in the URL.
|
||||
const resultAnswers = questions.map((q) => answers[q.id] ?? '0');
|
||||
saveResult(id, resultAnswers);
|
||||
const profile = activeProfile || await ensureActiveProfile();
|
||||
const completedAt = new Date().toISOString();
|
||||
const recordedAnswers = questionnaire.questions.map((question, index) => {
|
||||
const value = resultAnswers[index];
|
||||
const option = questionnaire.renderOptions(question.id).find(
|
||||
(item) => String(item.value) === String(value),
|
||||
);
|
||||
return {
|
||||
questionId: question.id,
|
||||
question: question.content,
|
||||
value,
|
||||
answer: option?.content || value,
|
||||
};
|
||||
});
|
||||
const record = await addAssessmentRecord({
|
||||
profileId: profile.id,
|
||||
questionnaireId: id,
|
||||
questionnaireTitle: questionnaire.title,
|
||||
category: questionnaire.category,
|
||||
completedAt,
|
||||
answers: recordedAnswers,
|
||||
scoreSummary: buildScoreSummary(id, resultAnswers),
|
||||
retestSuitable: questionnaire.evaluation?.retestSuitable,
|
||||
recommendedInterval: questionnaire.evaluation?.recommendedInterval,
|
||||
});
|
||||
saveResult(id, resultAnswers, profile.id, record.id);
|
||||
|
||||
router.push(`/questionnaire/${id}/result`);
|
||||
}
|
||||
@@ -201,6 +231,8 @@ export function Questionnaire({
|
||||
<div className="max-w-3xl mx-auto py-8 px-4">
|
||||
<h1 className="text-2xl font-bold mb-8">{questionnaire.title}</h1>
|
||||
|
||||
<ProfilePicker onChange={setActiveProfile} />
|
||||
|
||||
<ProgressPanel
|
||||
questions={questions}
|
||||
answers={answers}
|
||||
|
||||
Reference in New Issue
Block a user