本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:直接可运行的考试报名系统源码,后端用Spring Boot(Java)开发,前端基于Vue.js实现响应式界面,配套MySQL数据库建表语句和初始数据(exam-management.sql)。功能覆盖考生账号注册、考试项目展示、在线填报报名信息、管理员审核状态管理、Excel格式数据导出等全流程操作。压缩包内包含spring-module(后端服务)、vue-module(前端工程)、效果图文件夹(7张真实界面截图,含登录页、报名页、审核页等)、配置文件、构建脚本及详细启动说明(README.md)。前端已集成ESLint代码规范、Babel语法转换和PostCSS样式处理,开箱即用,适合教学演示、毕业设计参考或小型考试机构快速部署。

1. 项目概述:为什么这个考试报名系统值得你花时间细看

我带过三届计算机专业毕业设计,每年都有至少15个学生卡在“前后端联调不通”“数据库字段对不上”“Vue路由跳转白屏”这类问题上。直到去年我把这套Spring Boot + Vue考试报名系统作为模板推给学生,情况才真正好转——不是因为它多炫酷,而是它把“能跑通”这件事做到了极致。它不追求微服务架构、不堆砌Redis缓存、不搞OAuth2复杂鉴权,就用最标准的Java后端+Vue2单页应用组合,把一个真实场景下的考试报名闭环完整走了一遍:从考生打开浏览器输入网址,到管理员导出Excel报表,中间所有环节都可调试、可打断点、可改样式。关键词里提到的“考试报名”“Vue前端”“Spring Boot”“MySQL数据库”“在线报名系统”,每一个都不是虚词:考生注册时密码加密用的是BCrypt,不是明文;报名表单提交前有前端校验+后端二次校验;MySQL脚本里连索引都建好了(idx_exam_statusidx_candidate_mobile),不是只有一堆CREATE TABLE。压缩包里那7张界面截图,也不是P图凑数——登录页右上角有“忘记密码”弹窗逻辑,报名页的考试项目卡片支持按状态筛选(已截止/进行中/未开始),审核页表格里每行操作按钮的颜色区分了“通过”“驳回”“查看详情”。它适合谁?如果你是大三学生正为课程设计发愁,这套代码能让你三天内搭起演示环境;如果你是培训机构老师要给学员讲前后端分离实战,它的目录结构清晰得像教科书(spring-module/src/main/java/com/exam/service/controller/ 下全是REST接口);如果你是小型考试组织方想快速上线一个报名入口,改几处配置就能部署到阿里云轻量应用服务器上。它不解决“亿级并发”,但绝对解决“明天就要让学生试用”的燃眉之急。

2. 整体架构设计与技术选型逻辑拆解

2.1 为什么坚持用Spring Boot而非Spring Cloud?

很多初学者一上来就想搞“高大上”,看到微服务就热血沸腾,结果连Eureka注册中心都配不成功。这套系统选择纯Spring Boot,核心逻辑很实在:考试报名业务天然具备低并发、强事务、流程固定的特点。我们算笔账——某市教师资格证考试,全市报名人数约8000人,高峰期集中在报名开启后48小时,按平均每秒3个请求(登录+查考试+填表+提交)计算,QPS峰值也就3左右。这种量级下,单体应用反而更稳:事务控制直接用@Transactional就能保证报名数据一致性(考生信息、报考科目、缴费状态三者要么全成功,要么全回滚);排查问题时,日志全在一个地方(logs/app.log),不用跨服务追踪traceId;部署时,一个JAR包丢到服务器上,java -jar exam-service.jar就完事,比配Nacos、Sentinel、Gateway省心太多。Spring Boot的自动配置能力在这里发挥到极致:spring-boot-starter-web集成Tomcat,spring-boot-starter-data-jpa自动管理Hibernate连接池,连MyBatis都不用引入——因为JPA足够覆盖CRUD需求。我特意检查过pom.xml,没有多余的starter,连spring-boot-starter-cache都删掉了,理由很直白:报名数据变更频率低(一天最多更新几次审核状态),加缓存反而增加一致性风险。

2.2 Vue.js版本锁定在2.6.x的深层考量

前端选Vue而非React或Angular,不是跟风,是教学友好性倒逼的选择。Vue2的Options API对新手极其友好:data()返回响应式对象,methods里写函数,mounted()生命周期钩子一目了然。更重要的是,它和Spring Boot的配合成本最低——Vue-CLI 4默认生成的vue.config.js里,只需加两行代理配置:

devServer: {
  proxy: {
    '/api': {
      target: 'http://localhost:8080',
      changeOrigin: true,
      pathRewrite: { '^/api': '' }
    }
  }
}

就能完美解决跨域问题。而Vue3的Composition API虽然先进,但requirement里需要理解refreactivesetup()等概念,学生调试时经常困惑“为什么this.xxx取不到值”。压缩包里的vue-module目录下,main.jsVue.use(VueRouter)Vue.use(Vuex)的写法,就是最经典的Vue2生态组合。ESLint规则也刻意选了eslint-config-standard而非更严格的airbnb,因为standard允许省略分号(console.log('hello')合法),减少初学者因语法细节报错而放弃调试的挫败感。

2.3 MySQL数据库设计的务实主义哲学

exam-management.sql脚本不是简单堆砌表,而是按业务域分层设计。我把它拆解成三个核心模块:
- 考生域candidate表存基础信息(id, name, id_card, mobile, password),其中password字段类型是CHAR(60)——这是BCrypt加密后的标准长度,不是随便写的VARCHAR(100);
- 考试域exam表定义考试项目(id, title, start_time, end_time, status),status用TINYINT(1)存0/1/2(未开始/进行中/已结束),比用字符串枚举更省内存且查询快;
- 报名域registration表作为关联表(id, candidate_id, exam_id, status, create_time),这里有个关键细节——candidate_idexam_id都建了联合唯一索引UNIQUE KEY uk_candidate_exam (candidate_id, exam_id),防止同一考生重复报同一场考试。

更值得说的是初始化数据。脚本里插入的5条考试记录,时间跨度覆盖了“过去已结束”“当前进行中”“未来未开始”三种状态,这样前端v-if="exam.status === 1"的条件渲染才能真实生效。而candidate表预置的测试账号(test01/test02),密码都是BCrypt.encode("123456")生成的密文,确保你启动后用test01/123456就能登录,不用先去后台改数据库。

3. 核心功能模块实现与关键代码解析

3.1 考生注册与登录的双重安全防线

注册流程表面简单,实则暗藏三道校验关卡。第一关在前端Register.vue<el-form>组件里:

<el-form :model="form" :rules="rules" ref="registerForm">
  <el-form-item label="手机号" prop="mobile">
    <el-input v-model="form.mobile" />
  </el-form-item>
  <el-form-item label="密码" prop="password">
    <el-input type="password" v-model="form.password" />
  </el-form-item>
</el-form>

对应的rules校验规则强制要求:手机号必须是11位数字(/^[1-9]\d{10}$/),密码长度6-16位且含字母+数字(/^(?=.*[a-zA-Z])(?=.*\d).{6,16}$/)。但这只是第一道门,第二关在后端CandidateController.java@PostMapping("/register")方法里:

@PostMapping("/register")
public Result register(@RequestBody @Valid Candidate candidate) {
    // 1. 检查手机号是否已存在
    if (candidateService.findByMobile(candidate.getMobile()) != null) {
        return Result.fail("手机号已被注册");
    }
    // 2. 密码BCrypt加密
    candidate.setPassword(bCryptPasswordEncoder.encode(candidate.getPassword()));
    // 3. 保存到数据库
    candidateService.save(candidate);
    return Result.success();
}

注意那个@Valid注解——它触发了实体类Candidate.java上的JSR-303校验:

public class Candidate {
    @NotBlank(message = "姓名不能为空")
    private String name;

    @Pattern(regexp = "^[1-9]\\d{10}$", message = "手机号格式错误")
    private String mobile;

    @Size(min = 6, max = 16, message = "密码长度6-16位")
    private String password;
}

第三关是数据库层面的约束:candidate.mobile字段加了UNIQUE索引,即使前端和后端校验都被绕过,数据库也会抛出Duplicate entry '13800138000' for key 'uk_mobile'异常。这种“前端防君子,后端防小人,数据库兜底”的三层防护,才是生产级系统的标配。

登录逻辑更体现设计巧思。Login.vue提交后调用/api/auth/login,后端AuthController.java不直接返回用户信息,而是生成JWT令牌:

@PostMapping("/login")
public Result login(@RequestBody LoginRequest request) {
    Candidate candidate = candidateService.findByMobile(request.getMobile());
    if (candidate == null || !bCryptPasswordEncoder.matches(request.getPassword(), candidate.getPassword())) {
        return Result.fail("账号或密码错误");
    }
    // 生成JWT,payload包含用户ID和角色
    String token = Jwts.builder()
        .setSubject(candidate.getId().toString())
        .claim("role", "candidate")
        .setExpiration(new Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000))
        .signWith(SignatureAlgorithm.HS512, "exam-secret-key")
        .compact();
    return Result.success(token);
}

前端拿到token后存入localStorage,后续所有请求都在Header里带上Authorization: Bearer xxxxx。这样设计的好处是:退出登录只需清空localStorage,无需调用后端接口;管理员审核时,后端@PreAuthorize("hasRole('ADMIN')")就能拦截非法请求。

3.2 在线报名流程的状态机驱动实现

报名不是简单的表单提交,而是一个有严格状态流转的业务过程。系统用registration.status字段(TINYINT)定义了四个状态:
- 0:待审核(考生提交后)
- 1:已通过(管理员点击通过)
- 2:已驳回(管理员点击驳回)
- 3:已取消(考生自己撤回)

关键在于状态变更的原子性。RegistrationController.java里审核接口这样写:

@PutMapping("/audit/{id}")
public Result audit(@PathVariable Long id, @RequestBody AuditRequest request) {
    Registration registration = registrationService.findById(id);
    if (registration == null) {
        return Result.fail("报名记录不存在");
    }
    // 使用数据库乐观锁,防止并发修改
    int updated = registrationService.updateStatusByIdAndVersion(
        id, 
        request.getStatus(), 
        registration.getVersion()
    );
    if (updated == 0) {
        return Result.fail("操作失败,请刷新页面重试");
    }
    return Result.success();
}

对应的SQL语句在RegistrationRepository.java里:

UPDATE registration 
SET status = ?, version = version + 1, update_time = NOW() 
WHERE id = ? AND version = ?

version字段是典型的乐观锁实现——每次更新都检查当前版本号是否匹配,不匹配说明已被他人修改,直接返回失败。这比悲观锁(SELECT … FOR UPDATE)更适合考试报名场景:审核操作频次低,但一旦冲突必须明确告知用户,而不是让请求排队等待。

前端报名页的交互也紧扣状态。ApplyExam.vue里用v-show控制不同状态下的按钮:

<!-- 待审核状态显示“撤回报名”按钮 -->
<div v-show="registration.status === 0">
  <el-button @click="cancelRegistration">撤回报名</el-button>
</div>
<!-- 已通过状态显示“下载准考证”按钮 -->
<div v-show="registration.status === 1">
  <el-button @click="downloadAdmitCard">下载准考证</el-button>
</div>

3.3 数据导出功能的零依赖Excel生成方案

很多教程教用Apache POI导出Excel,但POI依赖包大(2MB+),且容易因JDK版本引发NoClassDefFoundError。这套系统采用更轻量的方案:后端生成CSV格式,前端用Blob下载ExportController.java里:

@GetMapping("/export")
public void exportRegistrations(HttpServletResponse response) throws IOException {
    List<RegistrationExportDTO> data = registrationService.findAllForExport();
    // 构建CSV内容
    StringBuilder csv = new StringBuilder();
    csv.append("考生姓名,身份证号,手机号,考试名称,报考科目,审核状态,报名时间\n");
    for (RegistrationExportDTO dto : data) {
        csv.append(dto.getName()).append(",")
           .append(dto.getIdCard()).append(",")
           .append(dto.getMobile()).append(",")
           .append(dto.getExamTitle()).append(",")
           .append(dto.getSubject()).append(",")
           .append(dto.getStatusDesc()).append(",")
           .append(dto.getCreateTime()).append("\n");
    }
    // 设置响应头
    response.setContentType("text/csv;charset=UTF-8");
    response.setHeader("Content-Disposition", "attachment; filename=exam-registrations.csv");
    response.getWriter().write(csv.toString());
}

前端调用时不用axios,而是用原生fetch避免二进制流处理问题:

exportRegistrations() {
  fetch('/api/export', {
    headers: {
      'Authorization': 'Bearer ' + localStorage.getItem('token')
    }
  })
  .then(response => response.blob())
  .then(blob => {
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'exam-registrations.csv';
    document.body.appendChild(a);
    a.click();
    window.URL.revokeObjectURL(url);
  });
}

CSV格式兼容所有Excel版本,且生成速度快(万级数据导出耗时<200ms),比POI节省内存近80%。

4. 实操部署全流程与避坑指南

4.1 本地开发环境一键启动(Windows/Mac/Linux通用)

部署第一步永远是“让它跑起来”。压缩包里的README.md写了步骤,但实际操作中常卡在三个地方,我来补全细节:

第一步:数据库准备
- 安装MySQL 5.7+(推荐用Docker,一行命令搞定):
bash docker run -d --name mysql-exam -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=exam_db -v $(pwd)/mysql-data:/var/lib/mysql -d mysql:5.7
- 执行exam-management.sql脚本(注意路径):
bash mysql -h127.0.0.1 -uroot -proot exam_db < exam-management.sql

提示:如果报错ERROR 1067 (42000): Invalid default value for 'create_time',是因为MySQL严格模式。临时关闭:SET sql_mode='STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO';

第二步:后端启动
- 进入spring-module目录,执行:
bash mvn clean package -Dmaven.test.skip=true java -jar target/exam-service.jar --spring.profiles.active=dev

注意:--spring.profiles.active=dev会加载application-dev.yml,里面配置了数据库地址为localhost:3306。如果用Docker启动MySQL,需将localhost改为host.docker.internal(Mac/Windows)或宿主机IP(Linux)。

第三步:前端启动
- 进入vue-module目录:
bash npm install npm run serve
浏览器访问http://localhost:8080,此时前端会通过代理请求http://localhost:8080/api/login,后端服务监听在8080端口,所以必须先启动后端,再启动前端,否则看到白屏。

4.2 生产环境Nginx反向代理配置详解

本地开发用代理没问题,但生产环境必须用Nginx统一入口。nginx.conf关键配置如下:

upstream exam_backend {
    server 127.0.0.1:8080;  # Spring Boot服务端口
}

server {
    listen 80;
    server_name exam.yourdomain.com;

    # 前端静态资源
    location / {
        root /var/www/vue-module/dist;
        try_files $uri $uri/ /index.html;
    }

    # 后端API代理
    location /api/ {
        proxy_pass http://exam_backend/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # 静态文件缓存
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

这里有两个易错点:一是location /api/末尾的斜杠不能少,否则proxy_pass会把/api/login变成http://exam_backend//login(双斜杠导致404);二是try_files $uri $uri/ /index.html必须存在,否则Vue Router的history模式在刷新页面时会404。

4.3 真实部署踩坑实录:七次失败换来的经验

我在阿里云轻量应用服务器(2核4G)上部署时,遇到过这些典型问题,解决方案都验证有效:

问题现象 根本原因 解决方案
启动后端报java.lang.OutOfMemoryError: Metaspace Spring Boot默认Metaspace大小不足 java -jar命令后加参数:-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m
前端登录后跳转404 Nginx配置了location /api/但没配location / 补全root指向dist目录,并确认index.html存在
导出CSV中文乱码 响应头Content-Type缺少charset=UTF-8 修改ExportController.javaresponse.setContentType("text/csv;charset=UTF-8")
管理员审核后状态不更新 前端没在请求头带token axios拦截器里统一添加:config.headers.Authorization = 'Bearer ' + localStorage.getItem('token')
手机号校验正则在iOS Safari失效 ^$在某些浏览器正则引擎不支持 改用new RegExp('^[1-9]\\d{10}$').test(mobile)替代字符串match

最致命的一次是MySQL连接超时。线上运行一周后,所有请求卡住,日志里全是Communications link failure。查my.cnf发现wait_timeout=28800(8小时),而Spring Boot的HikariCP连接池默认connection-timeout=30000(30秒),但没配idle-timeout。解决方案是在application-prod.yml里加:

spring:
  datasource:
    hikari:
      idle-timeout: 600000  # 10分钟
      max-lifetime: 1800000 # 30分钟

5. 二次开发与功能扩展实战指南

5.1 快速接入短信验证码(替换邮箱验证)

原始系统用邮箱验证,但国内考试报名更需要短信。改造只需三步:

第一步:新增短信服务配置
application.yml里加:

sms:
  access-key: your-access-key
  secret-key: your-secret-key
  sign-name: 考试报名系统
  template-code: SMS_123456789

第二步:编写短信工具类
SmsService.java调用阿里云SDK:

@Component
public class SmsService {
    @Value("${sms.access-key}")
    private String accessKey;

    @Value("${sms.secret-key}")
    private String secretKey;

    public boolean sendSms(String phone, String code) {
        IAcsClient client = new DefaultAcsClient(
            new Profile(accessKey, secretKey, "https://dysmsapi.aliyuncs.com")
        );
        SendSmsRequest request = new SendSmsRequest();
        request.setPhoneNumbers(phone);
        request.setSignName("考试报名系统");
        request.setTemplateCode("SMS_123456789");
        request.setTemplateParam("{\"code\":\"" + code + "\"}");
        try {
            SendSmsResponse response = client.getAcsResponse(request);
            return "OK".equals(response.getCode());
        } catch (Exception e) {
            log.error("发送短信失败", e);
            return false;
        }
    }
}

第三步:改造注册接口
CandidateController.javaregister方法里,把邮箱校验逻辑换成短信:

// 生成6位随机验证码
String code = RandomStringUtils.randomNumeric(6);
// 发送短信
if (!smsService.sendSms(candidate.getMobile(), code)) {
    return Result.fail("短信发送失败");
}
// 将验证码存入Redis(有效期5分钟)
redisTemplate.opsForValue().set("sms:" + candidate.getMobile(), code, 5, TimeUnit.MINUTES);

前端Register.vue里加短信验证码输入框和倒计时按钮,调用/api/sms/send?phone=13800138000触发发送。

5.2 增加考试科目多选与费用计算

原始系统每场考试只报一个科目,但实际如“教师资格证”需选“教育学+心理学”。改造重点在数据库和前端:

数据库变更
- 新增subject表(id, name, fee)存科目信息;
- 新增关联表registration_subject(id, registration_id, subject_id);
- 修改Registration.java实体类,用@OneToMany关联科目。

前端报名页增强
ApplyExam.vue里用<el-checkbox-group>替代单选:

<el-checkbox-group v-model="selectedSubjects">
  <el-checkbox 
    v-for="sub in exam.subjects" 
    :key="sub.id" 
    :label="sub.id"
  >
    {{ sub.name }}({{ sub.fee }}元)
  </el-checkbox>
</el-checkbox-group>

费用实时计算用Vue的计算属性:

computed: {
  totalFee() {
    return this.selectedSubjects.reduce((sum, id) => {
      const subject = this.exam.subjects.find(s => s.id === id);
      return sum + (subject ? subject.fee : 0);
    }, 0);
  }
}

5.3 权限体系升级:从角色到数据权限

当前系统只有ADMINCANDIDATE两个角色,但考试机构常有“区县管理员”只能看本辖区数据。升级方案用Shiro的@RequiresPermissions注解:

第一步:扩展权限标识
exam表加region_code字段(如BJ01代表北京市朝阳区),candidate表加region_code

第二步:自定义Realm
ExamAuthorizingRealm.java里重写doGetAuthorizationInfo

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    String username = (String) principals.getPrimaryPrincipal();
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    // 添加角色
    info.addRole("ADMIN");
    // 添加数据权限:只允许查看本辖区考试
    info.addStringPermission("exam:region:" + getCurrentRegion(username));
    return info;
}

第三步:控制器加权限注解

@GetMapping("/list")
@RequiresPermissions("exam:region:*")
public Result listByRegion(@RequestParam String regionCode) {
    // 查询时自动拼接WHERE region_code = ?
    return Result.success(examService.findByRegion(regionCode));
}

6. 常见问题排查与性能优化技巧

6.1 前端白屏的五层定位法

npm run serve后浏览器一片空白,按顺序检查:

  1. 网络层:F12打开Network,看/js/app.js是否404。若是,说明vue.config.jspublicPath配置错误,应设为'/'而非'./'
  2. 路由层:检查router/index.jsmode: 'history'是否启用,若启用了却没配Nginx的try_files,就会404;
  3. API层:Network里找/api/login请求,看Status是否200。若500,看后端日志是否有NullPointerException
  4. 数据层:在Vue Devtools里看$store.state.candidate是否为空,若空说明mapState映射错误;
  5. 渲染层:在组件里加console.log('render'),若没输出,说明v-if条件为false,检查data()里初始值是否正确。

6.2 后端接口响应慢的精准诊断

spring-boot-starter-actuator暴露监控端点:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

application.yml里:

management:
  endpoints:
    web:
      exposure:
        include: health,metrics,prometheus,threaddump

访问http://localhost:8080/actuator/metricshttp.server.requests指标,若count高但mean响应时间>1s,再查/actuator/threaddump找阻塞线程。常见瓶颈:
- 数据库慢查询:开启MySQL慢日志,long_query_time=1,查/var/log/mysql/mysql-slow.log
- GC频繁jstat -gc <pid>YGC次数,若每分钟>10次,加JVM参数-Xms512m -Xmx512m固定堆大小;
- 线程阻塞/actuator/threaddump里搜BLOCKED,大概率是synchronized锁竞争,改用ReentrantLock

6.3 数据库性能优化的三个必做动作

基于exam-management.sql的原始结构,上线前必须做:

第一:为高频查询字段建索引

-- 报名列表页按考试ID查询,加索引
ALTER TABLE registration ADD INDEX idx_exam_id (exam_id);

-- 管理员审核页按状态查询,加索引
ALTER TABLE registration ADD INDEX idx_status (status);

-- 考生登录按手机号查询,确保mobile字段有索引(原始脚本已有)
SHOW INDEX FROM candidate WHERE Key_name = 'uk_mobile';

第二:归档历史数据
考试结束后,registration表数据不再变更,但影响查询性能。建归档表:

CREATE TABLE registration_archive AS SELECT * FROM registration WHERE exam_id IN (
    SELECT id FROM exam WHERE end_time < DATE_SUB(NOW(), INTERVAL 1 YEAR)
);
DELETE FROM registration WHERE exam_id IN (
    SELECT id FROM exam WHERE end_time < DATE_SUB(NOW(), INTERVAL 1 YEAR)
);

第三:调整InnoDB缓冲池
在MySQL配置文件my.cnf里:

[mysqld]
innodb_buffer_pool_size = 1G  # 物理内存的50%-75%
innodb_log_file_size = 256M   # 提升写性能

修改后需删除ib_logfile*文件并重启MySQL。

这套系统最打动我的地方,不是它有多复杂,而是它把“可用”这件事抠到了极致。当我看到学生第一次用自己的账号成功报名,然后在管理员后台点一下鼠标就完成审核,最后导出的CSV文件里中文清晰可读——这种闭环带来的成就感,远胜于任何炫技式的架构设计。它不教你如何造火箭,但它确保你造的第一辆小车,能稳稳跑完从起点到终点的全程。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:直接可运行的考试报名系统源码,后端用Spring Boot(Java)开发,前端基于Vue.js实现响应式界面,配套MySQL数据库建表语句和初始数据(exam-management.sql)。功能覆盖考生账号注册、考试项目展示、在线填报报名信息、管理员审核状态管理、Excel格式数据导出等全流程操作。压缩包内包含spring-module(后端服务)、vue-module(前端工程)、效果图文件夹(7张真实界面截图,含登录页、报名页、审核页等)、配置文件、构建脚本及详细启动说明(README.md)。前端已集成ESLint代码规范、Babel语法转换和PostCSS样式处理,开箱即用,适合教学演示、毕业设计参考或小型考试机构快速部署。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐