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

Razor Page投票统计异常:Javascript或后端实现疑问

Razor Page实时投票应用问题解决方案

1. 完全可以通过Javascript实现该功能

前端JS完全能处理单选按钮分组和票数更新逻辑,无需频繁回发后端,交互更流畅,是这类实时场景的首选方案。

2. 现有Javascript的核心问题

问题1:单选按钮组冲突

原生单选按钮(input[type="radio"])通过name属性分组——所有name相同的radio会被归为同一组,同一组内只能选中一个。你的代码里肯定给所有行的radio设置了相同name,导致点击下一行radio会取消上一行选中状态。

问题2:票数累加未处理切换逻辑

你的JS只实现了“点击选项就给对应列加1”,但未跟踪每行之前选中的选项。当用户在同一行切换选项时(比如从Yes改成No),没有先减去Yes列的旧票数,直接给No列加1,导致票数重复累加、数据错误。

修正后的JS示例

假设每行有唯一标识(比如投票项Id),可按以下逻辑编写:

// 跟踪每行当前选中的选项,key为行ID,value为选中值(yes/no/abstain)
const selectedOptions = {};

// 绑定所有单选按钮的点击事件
document.querySelectorAll('.vote-radio').forEach(radio => {
    radio.addEventListener('click', function() {
        const rowId = this.dataset.rowId;
        const selectedValue = this.value;
        const yesCell = document.getElementById(`yes-${rowId}`);
        const noCell = document.getElementById(`no-${rowId}`);
        const abstainCell = document.getElementById(`abstain-${rowId}`);

        // 若之前有选中选项,先减去对应票数
        if (selectedOptions[rowId]) {
            const prevValue = selectedOptions[rowId];
            switch(prevValue) {
                case 'yes':
                    yesCell.textContent = parseInt(yesCell.textContent) - 1;
                    break;
                case 'no':
                    noCell.textContent = parseInt(noCell.textContent) - 1;
                    break;
                case 'abstain':
                    abstainCell.textContent = parseInt(abstainCell.textContent) - 1;
                    break;
            }
        }

        // 给当前选中选项加1
        switch(selectedValue) {
            case 'yes':
                yesCell.textContent = parseInt(yesCell.textContent) + 1;
                break;
            case 'no':
                noCell.textContent = parseInt(noCell.textContent) + 1;
                break;
            case 'abstain':
                abstainCell.textContent = parseInt(abstainCell.textContent) + 1;
                break;
        }

        // 更新当前行的选中记录
        selectedOptions[rowId] = selectedValue;
    });
});

对应的CSHTML单选按钮调整

给每行的radio设置唯一name和行ID的data属性:

@foreach (var item in Model.VoteItems)
{
    <tr>
        <td>@item.Title</td>
        <td id="yes-@item.Id">@item.YesVote</td>
        <td id="no-@item.Id">@item.NoVote</td>
        <td id="abstain-@item.Id">@item.AbsVote</td>
        <td>
            <!-- 每行radio的name唯一,用rowId区分分组 -->
            <input type="radio" class="vote-radio" name="vote-group-@item.Id" value="yes" data-row-id="@item.Id" /> Yes
            <input type="radio" class="vote-radio" name="vote-group-@item.Id" value="no" data-row-id="@item.Id" /> No
            <input type="radio" class="vote-radio" name="vote-group-@item.Id" value="abstain" data-row-id="@item.Id" /> Abstain
        </td>
    </tr>
}

3. 后端(.cs)实现方案

若不想用前端JS,可通过AJAX+后端API实现,避免整页回发:

后端.cs代码(PageModel)

public class VotePageModel : PageModel
{
    private readonly YourDbContext _context;

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

    public List<VoteItem> VoteItems { get; set; }

    public async Task OnGet()
    {
        VoteItems = await _context.VoteItems.ToListAsync();
    }

    // 供AJAX调用的后端方法,处理票数更新
    public async Task<IActionResult> OnPostUpdateVote(int rowId, string selectedOption)
    {
        var voteItem = await _context.VoteItems.FindAsync(rowId);
        if (voteItem == null)
        {
            return NotFound();
        }

        // 清除之前选中选项对应的票数(需在模型中添加LastSelectedOption字段)
        if (!string.IsNullOrEmpty(voteItem.LastSelectedOption))
        {
            switch(voteItem.LastSelectedOption)
            {
                case "yes":
                    voteItem.YesVote -= 1;
                    break;
                case "no":
                    voteItem.NoVote -= 1;
                    break;
                case "abstain":
                    voteItem.AbsVote -= 1;
                    break;
            }
        }

        // 增加当前选中选项的票数
        switch(selectedOption)
        {
            case "yes":
                voteItem.YesVote += 1;
                break;
            case "no":
                voteItem.NoVote += 1;
                break;
            case "abstain":
                voteItem.AbsVote += 1;
                break;
        }

        // 更新最后选中的选项
        voteItem.LastSelectedOption = selectedOption;
        await _context.SaveChangesAsync();

        // 返回更新后的票数,供前端刷新页面
        return new JsonResult(new {
            YesVote = voteItem.YesVote,
            NoVote = voteItem.NoVote,
            AbsVote = voteItem.AbsVote
        });
    }
}

前端AJAX调用代码

document.querySelectorAll('.vote-radio').forEach(radio => {
    radio.addEventListener('click', function() {
        const rowId = this.dataset.rowId;
        const selectedValue = this.value;

        fetch('/VotePage?handler=UpdateVote', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value
            },
            body: `rowId=${rowId}&selectedOption=${selectedValue}`
        })
        .then(response => response.json())
        .then(data => {
            // 更新页面上的票数
            document.getElementById(`yes-${rowId}`).textContent = data.YesVote;
            document.getElementById(`no-${rowId}`).textContent = data.NoVote;
            document.getElementById(`abstain-${rowId}`).textContent = data.AbsVote;
        })
        .catch(error => console.error('更新失败:', error));
    });
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 20:03:21