欢迎光临
我们一直在努力

HarmonyOS智慧农业管理应用开发教程--高高种地--第26篇:考试系统 - 题库与考试

第26篇:考试系统 – 题库与考试

📚 本篇导读

考试系统是学习中心的重要配套功能,通过考试可以检验学习成果,巩固知识点。本篇教程将实现一个完整的考试系统,包括题库管理、考试流程、答题界面、成绩计算等核心功能。

本篇将实现:

  • 📝 题库管理系统(题目分类、难度分级)
  • 📋 考试流程设计(开始考试、答题、提交)
  • ✍️ 答题界面实现(单选题、多选题、判断题)
  • ⏱️ 考试计时功能(倒计时、自动提交)
  • 📊 成绩计算展示(得分、正确率、通过状态)

🎯 学习目标

完成本篇教程后,你将掌握:

  • 如何设计题库数据模型
  • 如何实现考试流程控制
  • 如何开发答题界面
  • 如何实现计时功能
  • 考试数据的管理和存储

  • 一、考试系统架构

    1.1 系统架构设计

    考试系统
    ├── 题库管理
    │ ├── 题目分类(入门、中级、高级)
    │ ├── 题目类型(单选、多选、判断)
    │ ├── 知识点标签
    │ └── 答案解析

    ├── 考试流程
    │ ├── 考试准备(选择级别、题目数量)
    │ ├── 开始考试(随机抽题、计时开始)
    │ ├── 答题过程(题目展示、答案选择)
    │ ├── 提交考试(自动/手动提交)
    │ └── 成绩展示(得分、正确率、通过状态)

    ├── 考试记录
    │ ├── 历史记录
    │ ├── 答题详情
    │ ├── 错题收集
    │ └── 统计分析

    └── 数据管理
    ├── 题库数据
    ├── 考试记录
    ├── 答题数据
    └── 统计数据

    1.2 考试级别划分

    级别题目数量及格分数考试时长适用人群
    初级 20题 60分 30分钟 新手入门
    中级 30题 70分 45分钟 有一定基础
    高级 40题 80分 60分钟 专业人员

    1.3 题目分类

    家庭园艺模式:

    • 入门基础(植物识别、工具使用)
    • 日常养护(浇水、施肥、修剪)
    • 病虫防治(常见病虫害、防治方法)
    • 进阶技巧(繁殖、造型、组合)

    专业农业模式:

    • 作物种植(品种选择、栽培技术)
    • 植保技术(病虫害防治、农药使用)
    • 农机操作(机械使用、维护保养)
    • 市场营销(销售渠道、品牌建设)

    二、数据模型设计

    2.1 考试相关模型

    文件位置:entry/src/main/ets/models/ExamModels.ets

    /**
    * 考试级别
    */

    export enum ExamLevel {
    BEGINNER = 'BEGINNER', // 初级
    INTERMEDIATE = 'INTERMEDIATE', // 中级
    ADVANCED = 'ADVANCED' // 高级
    }

    /**
    * 题目选项
    */

    export interface QuestionOption {
    id: string; // 选项ID (A, B, C, D)
    content: string; // 选项内容
    }

    /**
    * 考试题目
    */

    export interface ExamQuestion {
    id: string; // 题目ID
    level: ExamLevel; // 考试级别
    category: string; // 题目分类
    question: string; // 题目内容
    options: QuestionOption[]; // 选项列表
    correctAnswer: string; // 正确答案ID
    explanation: string; // 答案解析
    knowledgePoint: string; // 知识点
    }

    /**
    * 用户答题记录
    */

    export interface UserAnswer {
    questionId: string; // 题目ID
    selectedAnswer: string; // 用户选择的答案ID
    isCorrect: boolean; // 是否正确
    timeSpent: number; // 答题用时(秒)
    }

    /**
    * 考试记录
    */

    export interface ExamRecord {
    id: string; // 考试记录ID
    level: ExamLevel; // 考试级别
    startTime: number; // 开始时间
    endTime: number; // 结束时间
    totalQuestions: number; // 总题数
    correctCount: number; // 正确题数
    score: number; // 得分
    passed: boolean; // 是否通过
    answers: UserAnswer[]; // 答题记录
    }

    /**
    * 考试统计
    */

    export interface ExamStats {
    totalExams: number; // 总考试次数
    passedExams: number; // 通过次数
    averageScore: number; // 平均分
    bestScore: number; // 最高分
    weakCategories: string[]; // 薄弱知识点
    }

    模型说明:

    • ExamLevel:考试级别枚举
    • ExamQuestion:题目完整信息
    • UserAnswer:用户答题记录
    • ExamRecord:考试完整记录
    • ExamStats:考试统计数据

    三、ExamService 考试服务

    3.1 服务类设计

    文件位置:entry/src/main/ets/services/ExamService.ets

    import { ExamLevel, ExamQuestion, ExamRecord, UserAnswer, ExamStats } from '../models/ExamModels';
    import { StorageUtil } from '../utils/StorageUtil';

    export class ExamService {
    private static instance: ExamService;

    private constructor() {}

    public static getInstance(): ExamService {
    if (!ExamService.instance) {
    ExamService.instance = new ExamService();
    }
    return ExamService.instance;
    }

    /**
    * 获取指定级别的题库
    */

    public getQuestionsByLevel(level: ExamLevel): ExamQuestion[] {
    const allQuestions = this.getAllQuestions();
    return allQuestions.filter(q => q.level === level);
    }

    /**
    * 随机抽取考试题目
    */

    public generateExam(level: ExamLevel, count: number = 20): ExamQuestion[] {
    const questions = this.getQuestionsByLevel(level);
    // 随机打乱题目顺序
    const shuffled = questions.sort(() => 0.5 Math.random());
    return shuffled.slice(0, Math.min(count, questions.length));
    }

    /**
    * 保存考试记录
    */

    public async saveExamRecord(record: ExamRecord): Promise<boolean> {
    try {
    const records = await this.getExamRecords();
    records.push(record);
    await StorageUtil.saveObject('exam_records', records);
    console.info('[ExamService] Exam record saved successfully');
    return true;
    } catch (error) {
    console.error('[ExamService] Failed to save exam record:', error);
    return false;
    }
    }

    /**
    * 获取所有考试记录
    */

    public async getExamRecords(): Promise<ExamRecord[]> {
    const records = await StorageUtil.getObject<ExamRecord[]>('exam_records', []);
    return records || [];
    }

    /**
    * 获取指定级别的考试记录
    */

    public async getExamRecordsByLevel(level: ExamLevel): Promise<ExamRecord[]> {
    const records = await this.getExamRecords();
    return records.filter(r => r.level === level);
    }
    }

    // 导出单例
    export const examService = ExamService.getInstance();

    服务功能说明:

  • 题库管理:获取、筛选题目
  • 考试生成:随机抽取题目
  • 记录管理:保存、查询考试记录
  • 统计分析:计算考试统计数据
  • 3.2 题库数据示例

    /**
    * 获取所有题目(示例数据)
    */

    private getAllQuestions(): ExamQuestion[] {
    return [
    {
    id: 'q_beginner_1',
    level: ExamLevel.BEGINNER,
    category: '入门基础',
    question: '以下哪种植物最适合新手种植?',
    options: [
    { id: 'A', content: '绿萝' },
    { id: 'B', content: '兰花' },
    { id: 'C', content: '玫瑰' },
    { id: 'D', content: '仙人球' }
    ],
    correctAnswer: 'A',
    explanation: '绿萝生命力强,对环境要求不高,非常适合新手种植。',
    knowledgePoint: '植物选择'
    },
    {
    id: 'q_beginner_2',
    level: ExamLevel.BEGINNER,
    category: '日常养护',
    question: '浇水的最佳时间是?',
    options: [
    { id: 'A', content: '中午' },
    { id: 'B', content: '清晨或傍晚' },
    { id: 'C', content: '深夜' },
    { id: 'D', content: '任何时间都可以' }
    ],
    correctAnswer: 'B',
    explanation: '清晨或傍晚温度适宜,水分蒸发少,植物吸收效果最好。',
    knowledgePoint: '浇水技巧'
    },
    {
    id: 'q_intermediate_1',
    level: ExamLevel.INTERMEDIATE,
    category: '作物种植',
    question: '小麦的最佳播种期是?',
    options: [
    { id: 'A', content: '春季3-4月' },
    { id: 'B', content: '夏季6-7月' },
    { id: 'C', content: '秋季9-10月' },
    { id: 'D', content: '冬季12-1月' }
    ],
    correctAnswer: 'C',
    explanation: '小麦分为冬小麦和春小麦,冬小麦在秋季播种,春季收获。',
    knowledgePoint: '作物播种'
    }
    // … 更多题目
    ];
    }


    四、考试主页面

    4.1 页面结构

    文件位置:entry/src/main/ets/pages/Exam/ExamPage.ets

    import { router } from '@kit.ArkUI';
    import { ExamLevel, ExamStats } from '../../models/ExamModels';
    import { ExamService } from '../../services/ExamService';

    @Entry
    @Component
    export struct ExamPage {
    @State beginnerStats: ExamStats | null = null;
    @State intermediateStats: ExamStats | null = null;
    @State advancedStats: ExamStats | null = null;
    @State isLoading: boolean = true;

    private examService = ExamService.getInstance();

    async aboutToAppear(): Promise<void> {
    await this.loadStats();
    }

    async loadStats(): Promise<void> {
    this.isLoading = true;
    try {
    this.beginnerStats = await this.examService.getExamStats(ExamLevel.BEGINNER);
    this.intermediateStats = await this.examService.getExamStats(ExamLevel.INTERMEDIATE);
    this.advancedStats = await this.examService.getExamStats(ExamLevel.ADVANCED);
    } catch (error) {
    console.error('[ExamPage] Failed to load stats:', error);
    } finally {
    this.isLoading = false;
    }
    }

    build() {
    Column() {
    // 标题栏
    Row() {
    Text('考试中心')
    .fontSize(24)
    .fontWeight(FontWeight.Bold)
    .fontColor($r('app.color.text_primary'))
    }
    .width('100%')
    .padding(16)

    Scroll() {
    Column({ space: 16 }) {
    this.buildExamIntro()
    this.buildExamCard('初级考试', '🌱', '适合初学者', ExamLevel.BEGINNER, this.beginnerStats)
    this.buildExamCard('中级考试', '🌿', '需要一定经验', ExamLevel.INTERMEDIATE, this.intermediateStats)
    this.buildExamCard('高级考试', '🌳', '专业级别', ExamLevel.ADVANCED, this.advancedStats)
    }
    .padding(16)
    }
    .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
    .backgroundColor($r('app.color.background'))
    }
    }

    4.2 考试说明卡片

    @Builder
    buildExamIntro() {
    Column({ space: 12 }) {
    Text('📝 考试说明')
    .fontSize(18)
    .fontWeight(FontWeight.Bold)
    .fontColor($r('app.color.text_primary'))
    .width('100%')

    Text('• 每个级别包含20-40道题目\\n• 考试时间30-60分钟\\n• 及格分数60-80分\\n• 可查看历史记录和错题')
    .fontSize(14)
    .fontColor($r('app.color.text_secondary'))
    .lineHeight(24)
    .width('100%')
    }
    .width('100%')
    .padding(16)
    .backgroundColor($r('app.color.card_background'))
    .borderRadius(12)
    }

    4.3 考试级别卡片

    @Builder
    buildExamCard(
    title: string,
    icon: string,
    description: string,
    level: ExamLevel,
    stats: ExamStats | null
    ) {
    Column({ space: 12 }) {
    Row({ space: 12 }) {
    Text(icon)
    .fontSize(48)

    Column({ space: 6 }) {
    Text(title)
    .fontSize(20)
    .fontWeight(FontWeight.Bold)
    .fontColor($r('app.color.text_primary'))

    Text(description)
    .fontSize(14)
    .fontColor($r('app.color.text_secondary'))
    }
    .alignItems(HorizontalAlign.Start)
    .layoutWeight(1)
    }
    .width('100%')

    // 统计信息
    if (stats && stats.totalExams > 0) {
    Row({ space: 16 }) {
    Column({ space: 4 }) {
    Text(`${stats.totalExams}`)
    .fontSize(20)
    .fontWeight(FontWeight.Bold)
    .fontColor($r('app.color.primary_professional'))
    Text('考试次数')
    .fontSize(12)
    .fontColor($r('app.color.text_tertiary'))
    }

    Column({ space: 4 }) {
    Text(`${stats.bestScore}`)
    .fontSize(20)
    .fontWeight(FontWeight.Bold)
    .fontColor($r('app.color.primary_professional'))
    Text('最高分')
    .fontSize(12)
    .fontColor($r('app.color.text_tertiary'))
    }

    Column({ space: 4 }) {
    Text(`${Math.round(stats.averageScore)}`)
    .fontSize(20)
    .fontWeight(FontWeight.Bold)
    .fontColor($r('app.color.primary_professional'))
    Text('平均分')
    .fontSize(12)
    .fontColor($r('app.color.text_tertiary'))
    }
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceAround)
    }

    // 操作按钮
    Row({ space: 12 }) {
    Button('开始考试')
    .layoutWeight(1)
    .height(44)
    .fontSize(16)
    .backgroundColor($r('app.color.primary_professional'))
    .onClick(() => {
    this.startExam(level);
    })

    Button('历史记录')
    .layoutWeight(1)
    .height(44)
    .fontSize(16)
    .backgroundColor($r('app.color.card_background'))
    .fontColor($r('app.color.text_primary'))
    .onClick(() => {
    this.viewHistory(level);
    })
    }
    .width('100%')
    }
    .width('100%')
    .padding(16)
    .backgroundColor($r('app.color.card_background'))
    .borderRadius(12)
    .shadow({ radius: 4, color: $r('app.color.shadow_light'), offsetY: 2 })
    }

    /**
    * 开始考试
    */

    private startExam(level: ExamLevel): void {
    router.pushUrl({
    url: 'pages/Exam/ExamQuestionPage',
    params: { level: level }
    });
    }

    /**
    * 查看历史记录
    */

    private viewHistory(level: ExamLevel): void {
    router.pushUrl({
    url: 'pages/Exam/ExamHistoryPage',
    params: { level: level }
    });
    }


    五、答题页面实现

    5.1 页面状态管理

    文件位置:entry/src/main/ets/pages/Exam/ExamQuestionPage.ets

    import { router, promptAction } from '@kit.ArkUI';
    import { ExamLevel, ExamQuestion, UserAnswer, ExamRecord } from '../../models/ExamModels';
    import { ExamService } from '../../services/ExamService';

    @Entry
    @Component
    export struct ExamQuestionPage {
    @State questions: ExamQuestion[] = [];
    @State currentIndex: number = 0;
    @State selectedAnswers: Record<string, string> = {};
    @State startTime: number = 0;
    @State remainingTime: number = 1800; // 30分钟
    @State level: ExamLevel = ExamLevel.BEGINNER;
    @State isSubmitting: boolean = false;
    @State showNavPanel: boolean = false;

    private examService = ExamService.getInstance();
    private timerInterval: number = 1;

    aboutToAppear(): void {
    const params = router.getParams() as Record<string, Object>;
    if (params && params['level']) {
    this.level = params['level'] as ExamLevel;
    }

    // 生成考试题目
    this.questions = this.examService.generateExam(this.level, 20);

    if (this.questions.length === 0) {
    promptAction.showToast({ message: '题库加载失败' });
    router.back();
    return;
    }

    this.startTime = Date.now();
    this.startTimer();
    }

    aboutToDisappear(): void {
    if (this.timerInterval !== 1) {
    clearInterval(this.timerInterval);
    }
    }
    }

    5.2 计时功能

    /**
    * 启动计时器
    */

    private startTimer(): void {
    this.timerInterval = setInterval(() => {
    this.remainingTime;
    if (this.remainingTime <= 0) {
    this.autoSubmit();
    }
    }, 1000);
    }

    /**
    * 格式化时间显示
    */

    private formatTime(seconds: number): string {
    const minutes = Math.floor(seconds / 60);
    const secs = seconds % 60;
    return `${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
    }

    /**
    * 自动提交(时间到)
    */

    private autoSubmit(): void {
    if (this.timerInterval !== 1) {
    clearInterval(this.timerInterval);
    }

    promptAction.showToast({
    message: '考试时间已到,自动提交',
    duration: 2000
    });

    this.submitExam();
    }

    5.3 答题界面

    build() {
    Column() {
    // 顶部导航栏
    Row() {
    Text('返回')
    .fontSize(16)
    .fontColor($r('app.color.primary_professional'))
    .onClick(() => {
    this.confirmExit();
    })

    Blank()

    Row() {
    Text(`${this.currentIndex + 1}/${this.questions.length}`)
    .fontSize(16)
    .fontWeight(FontWeight.Medium)
    Text(' 📋')
    .fontSize(16)
    }
    .onClick(() => {
    this.showNavPanel = !this.showNavPanel;
    })

    Blank()

    Text(this.formatTime(this.remainingTime))
    .fontSize(16)
    .fontWeight(FontWeight.Bold)
    .fontColor(this.remainingTime < 300 ? '#F5222D' : $r('app.color.text_primary'))
    }
    .width('100%')
    .height(56)
    .padding({ left: 16, right: 16 })
    .backgroundColor($r('app.color.card_background'))

    // 题目内容
    if (this.questions.length > 0) {
    Scroll() {
    Column({ space: 20 }) {
    this.buildQuestionContent()
    }
    .padding(16)
    }
    .layoutWeight(1)
    }

    // 底部操作栏
    this.buildBottomBar()

    // 题目导航面板
    if (this.showNavPanel) {
    this.buildNavigationPanel()
    }
    }
    .width('100%')
    .height('100%')
    .backgroundColor($r('app.color.background'))
    }

    5.4 题目内容展示

    @Builder
    buildQuestionContent() {
    const question = this.questions[this.currentIndex];
    if (!question) return;

    Column({ space: 16 }) {
    // 题目序号和内容
    Text(`${this.currentIndex + 1}. ${question.question}`)
    .fontSize(18)
    .fontWeight(FontWeight.Medium)
    .fontColor($r('app.color.text_primary'))
    .lineHeight(28)
    .width('100%')

    // 题目分类和知识点
    Row({ space: 8 }) {
    Text(question.category)
    .fontSize(12)
    .fontColor($r('app.color.text_tertiary'))
    .padding({ left: 8, right: 8, top: 4, bottom: 4 })
    .backgroundColor($r('app.color.background'))
    .borderRadius(10)

    Text(question.knowledgePoint)
    .fontSize(12)
    .fontColor($r('app.color.text_tertiary'))
    .padding({ left: 8, right: 8, top: 4, bottom: 4 })
    .backgroundColor($r('app.color.background'))
    .borderRadius(10)
    }
    .width('100%')

    // 选项列表
    ForEach(question.options, (option: QuestionOption) => {
    this.buildOptionItem(question.id, option)
    })
    }
    .width('100%')
    }

    @Builder
    buildOptionItem(questionId: string, option: QuestionOption) {
    const isSelected = this.selectedAnswers[questionId] === option.id;

    Row({ space: 12 }) {
    // 选项标识
    Text(option.id)
    .fontSize(16)
    .fontWeight(FontWeight.Bold)
    .fontColor(isSelected ? Color.White : $r('app.color.text_primary'))
    .width(32)
    .height(32)
    .textAlign(TextAlign.Center)
    .borderRadius(16)
    .backgroundColor(isSelected ?
    $r('app.color.primary_professional') : $r('app.color.background'))

    // 选项内容
    Text(option.content)
    .fontSize(16)
    .fontColor($r('app.color.text_primary'))
    .layoutWeight(1)
    .lineHeight(24)
    }
    .width('100%')
    .padding(12)
    .backgroundColor(isSelected ?
    '#E6F7FF' : $r('app.color.card_background'))
    .borderRadius(8)
    .border({
    width: 2,
    color: isSelected ?
    $r('app.color.primary_professional') : Color.Transparent
    })
    .onClick(() => {
    this.selectAnswer(questionId, option.id);
    })
    }

    /**
    * 选择答案
    */

    private selectAnswer(questionId: string, answerId: string): void {
    this.selectedAnswers[questionId] = answerId;
    }

    5.5 底部操作栏

    @Builder
    buildBottomBar() {
    Row({ space: 12 }) {
    // 上一题
    Button('上一题')
    .layoutWeight(1)
    .height(44)
    .fontSize(16)
    .backgroundColor($r('app.color.card_background'))
    .fontColor($r('app.color.text_primary'))
    .enabled(this.currentIndex > 0)
    .onClick(() => {
    if (this.currentIndex > 0) {
    this.currentIndex;
    }
    })

    // 下一题/提交
    Button(this.currentIndex === this.questions.length 1 ? '提交答卷' : '下一题')
    .layoutWeight(1)
    .height(44)
    .fontSize(16)
    .backgroundColor($r('app.color.primary_professional'))
    .onClick(() => {
    if (this.currentIndex === this.questions.length 1) {
    this.confirmSubmit();
    } else {
    this.currentIndex++;
    }
    })
    }
    .width('100%')
    .padding(16)
    .backgroundColor($r('app.color.card_background'))
    }

    5.6 题目导航面板

    @Builder
    buildNavigationPanel() {
    Column() {
    // 遮罩层
    Column()
    .width('100%')
    .height('100%')
    .backgroundColor('rgba(0, 0, 0, 0.5)')
    .onClick(() => {
    this.showNavPanel = false;
    })

    // 导航面板
    Column({ space: 16 }) {
    Text('题目导航')
    .fontSize(18)
    .fontWeight(FontWeight.Bold)
    .width('100%')

    Grid() {
    ForEach(this.questions, (question: ExamQuestion, index: number) => {
    GridItem() {
    this.buildNavItem(index)
    }
    })
    }
    .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
    .rowsGap(12)
    .columnsGap(12)
    .width('100%')

    Button('关闭')
    .width('100%')
    .height(44)
    .backgroundColor($r('app.color.card_background'))
    .fontColor($r('app.color.text_primary'))
    .onClick(() => {
    this.showNavPanel = false;
    })
    }
    .width('90%')
    .padding(20)
    .backgroundColor($r('app.color.card_background'))
    .borderRadius(16)
    .position({ x: '5%', y: '50%' })
    .translate({ y: '-50%' })
    }
    .width('100%')
    .height('100%')
    .position({ x: 0, y: 0 })
    }

    @Builder
    buildNavItem(index: number) {
    const question = this.questions[index];
    const isAnswered = !!this.selectedAnswers[question.id];
    const isCurrent = index === this.currentIndex;

    Text(`${index + 1}`)
    .fontSize(16)
    .fontWeight(FontWeight.Bold)
    .fontColor(isCurrent ? Color.White :
    (isAnswered ? $r('app.color.primary_professional') : $r('app.color.text_tertiary')))
    .width(48)
    .height(48)
    .textAlign(TextAlign.Center)
    .backgroundColor(isCurrent ?
    $r('app.color.primary_professional') :
    (isAnswered ? '#E6F7FF' : $r('app.color.background')))
    .borderRadius(8)
    .border({
    width: 2,
    color: isAnswered ?
    $r('app.color.primary_professional') : $r('app.color.border')
    })
    .onClick(() => {
    this.currentIndex = index;
    this.showNavPanel = false;
    })
    }


    六、提交考试与成绩计算

    6.1 确认提交

    /**
    * 确认提交
    */

    private confirmSubmit(): void {
    const answeredCount = Object.keys(this.selectedAnswers).length;
    const totalCount = this.questions.length;

    if (answeredCount < totalCount) {
    AlertDialog.show({
    title: '提示',
    message: `还有 ${totalCount answeredCount} 道题未作答,确定要提交吗?`,
    primaryButton: {
    value: '继续答题',
    action: () => {}
    },
    secondaryButton: {
    value: '确定提交',
    action: () => {
    this.submitExam();
    }
    }
    });
    } else {
    this.submitExam();
    }
    }

    /**
    * 确认退出
    */

    private confirmExit(): void {
    AlertDialog.show({
    title: '提示',
    message: '退出将不保存答题记录,确定要退出吗?',
    primaryButton: {
    value: '取消',
    action: () => {}
    },
    secondaryButton: {
    value: '确定退出',
    action: () => {
    if (this.timerInterval !== 1) {
    clearInterval(this.timerInterval);
    }
    router.back();
    }
    }
    });
    }

    6.2 提交考试

    /**
    * 提交考试
    */

    private async submitExam(): Promise<void> {
    if (this.isSubmitting) return;
    this.isSubmitting = true;

    try {
    // 停止计时
    if (this.timerInterval !== 1) {
    clearInterval(this.timerInterval);
    }

    // 计算成绩
    const answers: UserAnswer[] = [];
    let correctCount = 0;

    this.questions.forEach(question => {
    const selectedAnswer = this.selectedAnswers[question.id] || '';
    const isCorrect = selectedAnswer === question.correctAnswer;

    if (isCorrect) {
    correctCount++;
    }

    answers.push({
    questionId: question.id,
    selectedAnswer: selectedAnswer,
    isCorrect: isCorrect,
    timeSpent: 0 // 可以记录每题用时
    });
    });

    const score = Math.round((correctCount / this.questions.length) * 100);
    const passed = this.checkPassed(score);

    // 创建考试记录
    const record: ExamRecord = {
    id: Date.now().toString(),
    level: this.level,
    startTime: this.startTime,
    endTime: Date.now(),
    totalQuestions: this.questions.length,
    correctCount: correctCount,
    score: score,
    passed: passed,
    answers: answers
    };

    // 保存记录
    await this.examService.saveExamRecord(record);

    // 跳转到成绩页面
    router.replaceUrl({
    url: 'pages/Exam/ExamResultPage',
    params: {
    record: record,
    questions: this.questions
    }
    });
    } catch (error) {
    console.error('[ExamQuestionPage] Failed to submit exam:', error);
    promptAction.showToast({
    message: '提交失败,请重试',
    duration: 2000
    });
    this.isSubmitting = false;
    }
    }

    /**
    * 检查是否通过
    */

    private checkPassed(score: number): boolean {
    switch (this.level) {
    case ExamLevel.BEGINNER:
    return score >= 60;
    case ExamLevel.INTERMEDIATE:
    return score >= 70;
    case ExamLevel.ADVANCED:
    return score >= 80;
    default:
    return score >= 60;
    }
    }


    七、本篇总结

    7.1 核心知识点

    本篇教程实现了考试系统的核心功能,涵盖以下内容:

  • 题库管理

    • 题目数据模型设计
    • 题目分类和难度分级
    • 随机抽题算法
  • 考试流程

    • 考试准备和开始
    • 答题界面实现
    • 计时功能
    • 提交和成绩计算
  • 数据管理

    • 考试记录存储
    • 答题数据管理
    • 统计数据计算
  • 7.2 技术要点

    • ✅ 定时器管理:setInterval 实现倒计时
    • ✅ 状态管理:@State 管理答题状态
    • ✅ 数据持久化:StorageUtil 存储考试记录
    • ✅ 路由传参:传递考试级别和结果数据
    • ✅ 对话框交互:AlertDialog 确认提交
    • ✅ Grid布局:题目导航面板

    7.3 下一篇预告

    第27篇:考试系统 – 成绩分析与错题

    下一篇将实现成绩展示和错题分析功能,包括:

    • 📊 成绩详情展示
    • 📈 答题统计分析
    • ❌ 错题本功能
    • 📝 答案解析查看
    • 🔄 重新考试功能
    赞(0)
    未经允许不得转载:171主机测评 » HarmonyOS智慧农业管理应用开发教程--高高种地--第26篇:考试系统 - 题库与考试
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址