You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 16:47:46