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

ASP.NET Core 2 MVC无JS实现任务工作/休息时长统计优化咨询

Server-Side Task Time Tracking (Work/Break) for ASP.NET Core 2 MVC

Great question—let's build a clean, server-side-only solution for your ASP.NET Core 2 MVC task tracking that avoids JavaScript entirely, while being more robust than a basic implementation. Here's a streamlined approach that follows MVC best practices:

1. Model Setup

First, define a TimeEntry model to track work sessions. This will link to your existing Task model and store start/end times:

public class TimeEntry
{
    public int Id { get; set; }
    public int TaskId { get; set; }
    public DateTime Start { get; set; }
    public DateTime? End { get; set; } // Nullable since a session might be active

    // Navigation property to your Task model
    public Task Task { get; set; }
}

Don't forget to add this DbSet to your database context:

public DbSet<TimeEntry> TimeEntries { get; set; }

2. Controller Actions

Update your TaskController with two new POST actions to handle starting work and ending a session (break). We'll add validation to prevent edge cases like duplicate active sessions:

public class TaskController : Controller
{
    private readonly YourDbContext _context;

    public TaskController(YourDbContext context)
    {
        _context = context;
    }

    // Existing Details action (updated to pass latest active session to view)
    public async Task<IActionResult> Details(int? id)
    {
        if (id == null) return NotFound();

        var task = await _context.Tasks.FirstOrDefaultAsync(m => m.Id == id);
        if (task == null) return NotFound();

        // Fetch latest unfinished session for UI state handling
        var latestActiveSession = await _context.TimeEntries
            .Where(te => te.TaskId == id && te.End == null)
            .OrderByDescending(te => te.Start)
            .FirstOrDefaultAsync();
        
        ViewData["LatestActiveSession"] = latestActiveSession;
        return View(task);
    }

    // Handle "Start Work" button submission
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> StartWork(int taskId)
    {
        // Prevent starting a new session if one is already active
        var hasActiveSession = await _context.TimeEntries
            .AnyAsync(te => te.TaskId == taskId && te.End == null);
        
        if (hasActiveSession)
        {
            TempData["AlertMessage"] = "You already have an active work session for this task!";
            return RedirectToAction(nameof(Details), new { id = taskId });
        }

        var newSession = new TimeEntry
        {
            TaskId = taskId,
            Start = DateTime.Now
        };

        _context.TimeEntries.Add(newSession);
        await _context.SaveChangesAsync();

        TempData["AlertMessage"] = "Work session started successfully!";
        return RedirectToAction(nameof(Details), new { id = taskId });
    }

    // Handle "Take Break" button submission (ends active session)
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> EndWork(int taskId)
    {
        // Find the most recent active session
        var latestSession = await _context.TimeEntries
            .Where(te => te.TaskId == taskId && te.End == null)
            .OrderByDescending(te => te.Start)
            .FirstOrDefaultAsync();
        
        if (latestSession == null)
        {
            TempData["AlertMessage"] = "No active work session found to end!";
            return RedirectToAction(nameof(Details), new { id = taskId });
        }

        latestSession.End = DateTime.Now;
        _context.Update(latestSession);
        await _context.SaveChangesAsync();

        TempData["AlertMessage"] = "Work session ended (break started) successfully!";
        return RedirectToAction(nameof(Details), new { id = taskId });
    }
}

3. View Implementation (/Task/Details/{id})

Add two server-side forms to your Details view—one for each button. We'll also add UI state handling to disable buttons when they shouldn't be clickable, and show user feedback via TempData:

@model YourNamespace.Models.Task

@{
    ViewData["Title"] = "Task Details";
    var hasActiveSession = ViewData["LatestActiveSession"] != null;
}

<h1>@Model.Name</h1>

<!-- Display task details here -->
<div class="card mb-3">
    <div class="card-body">
        <dl class="row">
            <dt class="col-sm-3">Description:</dt>
            <dd class="col-sm-9">@Model.Description</dd>
            <!-- Add other task fields as needed -->
        </dl>
    </div>
</div>

<!-- Display alert messages -->
@if (TempData["AlertMessage"] != null)
{
    <div class="alert alert-info mb-3">@TempData["AlertMessage"]</div>
}

<!-- Start Work Form -->
<form asp-action="StartWork" method="post" class="d-inline">
    <input type="hidden" name="taskId" value="@Model.Id" />
    <button type="submit" class="btn btn-success" @(hasActiveSession ? "disabled" : "")>
        Start Work
    </button>
</form>

<!-- Take Break Form -->
<form asp-action="EndWork" method="post" class="d-inline ms-2">
    <input type="hidden" name="taskId" value="@Model.Id" />
    <button type="submit" class="btn btn-warning" @(!hasActiveSession ? "disabled" : "")>
        Take Break (End Session)
    </button>
</form>

<div class="mt-3">
    <a asp-action="Edit" asp-route-id="@Model.Id" class="btn btn-secondary">Edit Task</a>
    <a asp-action="Index" class="btn btn-secondary ms-2">Back to Task List</a>
</div>

Optional Enhancements

  • Session Duration Display: Add a section to the view showing the duration of the active session (if any) by calculating the difference between DateTime.Now and the session's Start time.
  • History Table: Include a table in the Details view that lists all past work sessions for the task, showing start/end times and total duration.
  • Role-Based Authorization: Add [Authorize] attributes to the actions if only specific users should track time for tasks.

内容的提问来源于stack exchange,提问作者Ruby Harris

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:24:40