Skip to content

Commit 38c3534

Browse files
author
CodeJudge
committed
feat: 代码自动保存 + 提交统计面板
1 parent 1d695a0 commit 38c3534

2 files changed

Lines changed: 79 additions & 1 deletion

File tree

frontend/src/pages/ProblemDetail.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ export default function ProblemDetail() {
5959
const [submitting, setSubmitting] = useState(false);
6060
const [submissionResult, setSubmissionResult] = useState<Submission | null>(null);
6161

62+
const CODE_SAVE_KEY = `cj_code_${id}_${language}`;
63+
6264
const fetchProblem = useCallback(async () => {
6365
if (!id) return;
6466
setLoading(true);
@@ -79,13 +81,19 @@ export default function ProblemDetail() {
7981
}, [id]);
8082

8183
const loadTemplate = useCallback(async (lang: string) => {
84+
const savedKey = `cj_code_${id}_${lang}`;
85+
const saved = localStorage.getItem(savedKey);
86+
if (saved) {
87+
setCode(saved);
88+
return;
89+
}
8290
try {
8391
const { template } = await api.problems.getTemplate(lang);
8492
setCode(template);
8593
} catch {
8694
setCode('');
8795
}
88-
}, []);
96+
}, [id]);
8997

9098
useEffect(() => {
9199
fetchProblem();
@@ -100,6 +108,14 @@ export default function ProblemDetail() {
100108
}
101109
}, [problem?.type, language, loadTemplate]);
102110

111+
useEffect(() => {
112+
if (!code || problem?.type !== 'programming') return;
113+
const timer = setTimeout(() => {
114+
localStorage.setItem(CODE_SAVE_KEY, code);
115+
}, 1000);
116+
return () => clearTimeout(timer);
117+
}, [code, CODE_SAVE_KEY, problem?.type]);
118+
103119
const handleSubmit = async () => {
104120
if (!problem) return;
105121

frontend/src/pages/Submissions.tsx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import {
88
Inbox,
99
ChevronDown,
1010
ChevronUp,
11+
Send,
12+
CheckCircle,
13+
Code,
14+
Award,
1115
} from 'lucide-react';
1216
import toast from 'react-hot-toast';
1317
import api from '../services/api';
@@ -93,6 +97,30 @@ export default function Submissions() {
9397

9498
const submissions = data?.submissions ?? [];
9599

100+
const totalSubmissions = data?.total ?? 0;
101+
const acceptedCount = submissions.filter(s => s.status === 'accepted').length;
102+
const acceptanceRate = submissions.length > 0
103+
? ((acceptedCount / submissions.length) * 100).toFixed(1)
104+
: '0';
105+
106+
const langCounts: Record<string, number> = {};
107+
submissions.forEach(s => {
108+
if (s.language) {
109+
langCounts[s.language] = (langCounts[s.language] || 0) + 1;
110+
}
111+
});
112+
let mostUsedLang = '-';
113+
let maxLangCount = 0;
114+
Object.entries(langCounts).forEach(([lang, count]) => {
115+
if (count > maxLangCount) {
116+
maxLangCount = count;
117+
mostUsedLang = lang;
118+
}
119+
});
120+
121+
const totalScore = submissions.reduce((sum, s) => sum + (s.score || 0), 0);
122+
const avgScore = submissions.length > 0 ? (totalScore / submissions.length).toFixed(1) : '0';
123+
96124
return (
97125
<div>
98126
<h1 className="text-2xl font-bold text-white mb-6">提交记录</h1>
@@ -114,6 +142,40 @@ export default function Submissions() {
114142
))}
115143
</div>
116144

145+
{/* Stats bar */}
146+
{data && (
147+
<div className="grid grid-cols-4 gap-4 mb-6">
148+
<div className="card p-4 flex items-center gap-3">
149+
<Send size={20} className="text-blue-400" />
150+
<div>
151+
<p className="text-xs text-gray-400">总提交</p>
152+
<p className="text-lg font-bold text-white">{totalSubmissions}</p>
153+
</div>
154+
</div>
155+
<div className="card p-4 flex items-center gap-3">
156+
<CheckCircle size={20} className="text-green-400" />
157+
<div>
158+
<p className="text-xs text-gray-400">通过率</p>
159+
<p className="text-lg font-bold text-white">{acceptanceRate}%</p>
160+
</div>
161+
</div>
162+
<div className="card p-4 flex items-center gap-3">
163+
<Code size={20} className="text-purple-400" />
164+
<div>
165+
<p className="text-xs text-gray-400">常用语言</p>
166+
<p className="text-lg font-bold text-white">{mostUsedLang}</p>
167+
</div>
168+
</div>
169+
<div className="card p-4 flex items-center gap-3">
170+
<Award size={20} className="text-yellow-400" />
171+
<div>
172+
<p className="text-xs text-gray-400">平均分</p>
173+
<p className="text-lg font-bold text-white">{avgScore}</p>
174+
</div>
175+
</div>
176+
</div>
177+
)}
178+
117179
{/* Content */}
118180
{loading ? (
119181
<div className="space-y-3">

0 commit comments

Comments
 (0)