优化考试管理界面布局和交互

- 在考试失败页面添加"再考一次"按钮,支持快速重新开始考试
- 移除试卷卡片中的考试时长、及格分数和题目数量显示
- 优化试卷卡片布局:减少内边距和卡片间距,使界面更紧凑
- 修复统计标签样式:覆盖antd Tag默认间距,防止图标在文字溢出时缩小
- 实现响应式布局:移动端1列、平板2列、桌面端3列

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
燕陇琪 2025-12-02 00:03:12 +08:00
parent 023ab1cc55
commit e3e0671204
3 changed files with 58 additions and 55 deletions

View File

@ -136,7 +136,7 @@
// 卡片内容样式 // 卡片内容样式
.cardContent { .cardContent {
padding: 16px; padding: 8px;
} }
.examInfo { .examInfo {
@ -208,6 +208,12 @@
width: 100%; width: 100%;
justify-content: center; // 内容居中 justify-content: center; // 内容居中
// 覆盖 antd Tag 的默认图标间距
:global(.anticon) + span,
span + :global(.anticon) {
margin-inline-start: 0 !important;
}
.anticon { .anticon {
font-size: 14px; font-size: 14px;
color: #64748b; color: #64748b;
@ -230,7 +236,12 @@
line-height: 1.4; line-height: 1.4;
width: 100%; width: 100%;
justify-content: center; // 内容居中 justify-content: center; // 内容居中
width: 100%;
// 覆盖 antd Tag 的默认图标间距
:global(.anticon) + span,
span + :global(.anticon) {
margin-inline-start: 0 !important;
}
.anticon { .anticon {
font-size: 14px; font-size: 14px;
@ -353,7 +364,6 @@
} }
.cardContent { .cardContent {
margin-top: 12px;
.infoRow { .infoRow {
display: flex; display: flex;
@ -388,6 +398,7 @@
// 旧版兼容样式 - divider已合并不再重复定义 // 旧版兼容样式 - divider已合并不再重复定义
// 响应式适配 // 响应式适配
// 移动端1列
@media (max-width: 768px) { @media (max-width: 768px) {
.container { .container {
padding: 16px; padding: 16px;
@ -395,7 +406,7 @@
} }
.examGrid { .examGrid {
grid-template-columns: 1fr; grid-template-columns: 1fr; // 移动端显示1列
gap: 16px; gap: 16px;
} }
@ -472,7 +483,7 @@
.examStats { .examStats {
padding: 12px 0; padding: 12px 0;
gap: 10px; gap: 6px;
.statItem { .statItem {
.valueTag { .valueTag {
@ -571,6 +582,14 @@
} }
} }
// 平板端2列
@media (min-width: 769px) and (max-width: 1024px) {
.examGrid {
grid-template-columns: repeat(2, 1fr); // 平板显示2列
gap: 12px;
}
}
@media (max-width: 480px) { @media (max-width: 480px) {
.container { .container {
padding: 12px; padding: 12px;

View File

@ -368,22 +368,6 @@ const ExamManagement: React.FC = () => {
]} ]}
> >
<div className={styles.cardContent}> <div className={styles.cardContent}>
<div className={styles.examInfo}>
<div className={styles.infoItem}>
<ClockCircleOutlined className={styles.infoIcon} />
<span className={styles.infoText}>{exam.duration} </span>
</div>
<div className={styles.infoItem}>
<CheckCircleOutlined className={styles.infoIcon} />
<span className={styles.infoText}> {exam.pass_score} </span>
</div>
<div className={styles.infoItem}>
<FileTextOutlined className={styles.infoIcon} />
<span className={styles.infoText}>{exam.question_count || 0} </span>
</div>
</div>
<Divider className={styles.divider} />
<div className={styles.examStats}> <div className={styles.examStats}>
<div className={styles.statItem}> <div className={styles.statItem}>
@ -400,7 +384,7 @@ const ExamManagement: React.FC = () => {
</div> </div>
{exam.participant_count > 0 && ( {exam.participant_count > 0 && (
<div className={styles.statItem}> <div className={styles.statItem}>
<Tag color="blue" className={styles.participantTag}> <Tag className={styles.valueTag}>
<TeamOutlined /> <TeamOutlined />
<span>{exam.participant_count} </span> <span>{exam.participant_count} </span>
</Tag> </Tag>

View File

@ -348,6 +348,16 @@ const ExamResultNew: React.FC = () => {
return ( return (
<div className={styles.container}> <div className={styles.container}>
{/* 顶部返回按钮 */}
<div style={{ marginBottom: 16 }}>
<Button
icon={<LeftOutlined />}
onClick={() => navigate(-1)}
>
</Button>
</div>
{/* 成绩展示 */} {/* 成绩展示 */}
<Result <Result
status={isPassed ? "success" : "warning"} status={isPassed ? "success" : "warning"}
@ -359,6 +369,29 @@ const ExamResultNew: React.FC = () => {
</Text> </Text>
</Space> </Space>
} }
extra={
!isPassed && record.exam?.id ? (
<Button
type="primary"
size="large"
icon={<FileTextOutlined />}
onClick={async () => {
try {
const res = await examApi.startExam(record.exam!.id);
if (res.success && res.data) {
navigate(`/exam/${record.exam!.id}/taking/${res.data.record_id}`);
} else {
message.error(res.message || "开始考试失败");
}
} catch (error) {
message.error("开始考试失败");
}
}}
>
</Button>
) : undefined
}
/> />
{/* 成绩统计 */} {/* 成绩统计 */}
@ -548,39 +581,6 @@ const ExamResultNew: React.FC = () => {
</div> </div>
))} ))}
</Card> </Card>
{/* 操作按钮 */}
<Card className={styles.actionsCard}>
<Space size="large">
<Button
size="large"
icon={<LeftOutlined />}
onClick={() => navigate("/exam/management")}
>
</Button>
{record.exam?.id && (
<Button
size="large"
icon={<FileTextOutlined />}
onClick={async () => {
try {
const res = await examApi.startExam(record.exam!.id);
if (res.success && res.data) {
navigate(
`/exam/${record.exam!.id}/taking/${res.data.record_id}`
);
}
} catch (error) {
message.error("开始考试失败");
}
}}
>
</Button>
)}
</Space>
</Card>
</div> </div>
); );
}; };