Laravel在线考试倒计时实现:如何将倒计时状态存入Session以支持浏览器刷新后持续计时?
没问题,我来帮你搞定这个在线考试倒计时的Session存储问题!核心思路是把考试的结束时间存在Session中,而非剩余时长——这样不管用户怎么刷新页面,都能通过结束时间和当前时间的差值计算出剩余时长,保证倒计时不中断。
实现步骤
1. 后端Laravel配置Session(考试启动时)
当用户进入考试页面的瞬间,我们需要计算出考试的结束时间并写入Session。这里用Carbon处理时间会更方便:
// 考试控制器的考试页面方法 use Carbon\Carbon; public function startExam() { // 仅在用户首次进入考试时设置结束时间,避免重复覆盖 if (!session()->has('exam_end_time')) { $examDuration = 60; // 考试时长,单位:分钟,可根据业务调整 $endTime = Carbon::now()->addMinutes($examDuration); // 将结束时间存入Session,转成字符串格式方便前端解析 session(['exam_end_time' => $endTime->toDateTimeString()]); } // 传递结束时间到视图(也可以直接在视图里读取Session) return view('exam', [ 'examEndTime' => session('exam_end_time') ]); }
2. 修改前端倒计时逻辑
你的原有前端代码是基于初始剩余时长计算,现在要改成基于Session里的结束时间来实时计算剩余时长,这样刷新后就能自动续上倒计时:
<script type="text/javascript"> var interval; var form = document.forms.exam; // 从后端获取考试结束时间(这里直接用视图传递的变量,也可以从Session读取) var endTime = new Date("{{ $examEndTime }}").getTime(); function countdown() { clearInterval(interval); interval = setInterval(function() { // 获取当前时间戳 var now = new Date().getTime(); // 计算剩余毫秒数 var remainingMs = endTime - now; // 转换为分:秒格式 var minutes = Math.floor((remainingMs % (1000 * 60 * 60)) / (1000 * 60)); var seconds = Math.floor((remainingMs % (1000 * 60)) / 1000); // 格式化数字,确保是两位(比如05分03秒) minutes = minutes < 10 ? "0" + minutes : minutes; seconds = seconds < 10 ? "0" + seconds : seconds; $('.js-timeout').html(minutes + ":" + seconds); // 时间耗尽时提交表单 if (remainingMs <= 0) { clearInterval(interval); $('.js-timeout').html("00:00"); form.submit(); alert("Waktu sudah habis!"); } }, 1000); } // 页面加载时启动倒计时 countdown(); </script>
3. 进阶:增加时间校准(防止前端篡改)
如果担心用户修改本地系统时间作弊,可以定时调用后端接口校准剩余时间,比如每30秒同步一次:
第一步:新增后端接口
// routes/web.php Route::get('/exam/remaining-time', 'ExamController@getRemainingTime')->name('exam.remaining'); // ExamController.php public function getRemainingTime() { if (!session()->has('exam_end_time')) { return response()->json(['error' => '考试未开始'], 400); } $endTime = Carbon::parse(session('exam_end_time')); $remainingSeconds = $endTime->diffInSeconds(Carbon::now(), false); return response()->json([ 'remaining_seconds' => $remainingSeconds, 'end_time' => $endTime->toDateTimeString() ]); }
第二步:修改前端代码
<script type="text/javascript"> var interval; var form = document.forms.exam; var endTime; // 初始化/校准结束时间 function syncEndTime() { fetch("{{ route('exam.remaining') }}") .then(res => res.json()) .then(data => { if (data.remaining_seconds <= 0) { form.submit(); alert("Waktu sudah habis!"); return; } endTime = new Date(data.end_time).getTime(); // 启动倒计时 countdown(); // 每30秒校准一次 setTimeout(syncEndTime, 30000); }) .catch(err => console.error('时间同步失败:', err)); } function countdown() { clearInterval(interval); interval = setInterval(function() { var now = new Date().getTime(); var remainingMs = endTime - now; var minutes = Math.floor((remainingMs % (1000 * 60 * 60)) / (1000 * 60)); var seconds = Math.floor((remainingMs % (1000 * 60)) / 1000); minutes = minutes < 10 ? "0" + minutes : minutes; seconds = seconds < 10 ? "0" + seconds : seconds; $('.js-timeout').html(minutes + ":" + seconds); if (remainingMs <= 0) { clearInterval(interval); $('.js-timeout').html("00:00"); form.submit(); alert("Waktu sudah habis!"); } }, 1000); } // 页面加载时初始化 syncEndTime(); </script>
4. 收尾注意事项
- 清理Session:考试结束(用户提交或时间耗尽)后,记得清理Session里的结束时间,避免下次进入考试时沿用旧数据:
session()->forget('exam_end_time'); - Session有效期:确保Laravel的Session有效期长于考试时长,可在
config/session.php中修改lifetime配置。 - 异常处理:如果Session过期(比如用户长时间未操作),可在视图中判断并引导用户重新开始考试。
内容的提问来源于stack exchange,提问作者IkhsanHeriyawan
相关产品推荐
相关产品推荐

