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

Kendo Grid页面返回后定位上次选中元素的实现方案咨询

这个需求完全可行,我来给你梳理下实现思路和具体代码方案

首先核心问题是:滚动加载模式下,选中的记录不在当前加载的数据集里,所以没法直接选中。解决思路就是先定位到目标记录所在的页码,让Grid加载那一页的数据,再选中目标行,同时还要恢复之前的排序状态。

下面是分步实现方案:

一、先做好状态存储:把选中ID、排序、过滤参数存到Cookie里

用户离开页面时,我们需要把当前的关键状态(选中的用户ID、排序规则、过滤条件)保存到Cookie,这样返回页面时能读取恢复。

1. 前端辅助函数:Cookie读写

// 保存数据到Cookie
function setCookie(name, value, days = 7) {
    let expires = "";
    if (days) {
        const date = new Date();
        date.setTime(date.getTime() + (days*24*60*60*1000));
        expires = "; expires=" + date.toUTCString();
    }
    document.cookie = `${name}=${value || ""}${expires}; path=/`;
}

// 从Cookie读取数据
function getCookie(name) {
    const nameEQ = `${name}=`;
    const ca = document.cookie.split(';');
    for(let i = 0; i < ca.length; i++) {
        let c = ca[i];
        while (c.charAt(0) === ' ') c = c.substring(1);
        if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length);
    }
    return null;
}

2. 给Grid绑定事件,自动保存状态

修改你的Grid视图代码,添加Change(选中行)、Sort(排序变化)事件:

@(Html.Kendo().Grid<RegistryUserDto>()
    .Name("grid")
    .Columns(columns => {
        columns.Bound(o => o.Name).Title(UserResources.NameLabelText).Width(180);
        columns.Bound(o => o.CompanyName).Title(UserResources.CompanyLabelText).Width(187);
        columns.Bound(o => o.RoleNames).Title(UserResources.TypeLabelText).Width(100);
        columns.Bound(o => o.Id).Hidden();
    })
    .Sortable()
    .Selectable(x => x.Mode(GridSelectionMode.Single))
    .Scrollable()
    .Events(events => {
        events.Change("onGridRowSelect");
        events.Sort("onGridSortChange");
        events.DataBound("onGridDataLoaded");
    })
    .DataSource(dataSource => dataSource
        .Ajax()
        .PageSize(50) // 改成你需求的每次加载50条
        .Read(read => read.Action("GetUsersList", "Users").Data("GetFilterParameters"))
    )
)

然后写事件处理函数,保存状态到Cookie:

// 选中行时保存用户ID
function onGridRowSelect(e) {
    const selectedRow = this.select();
    const dataItem = this.dataItem(selectedRow);
    if (dataItem) {
        setCookie("SelectedRegistryUserId", dataItem.Id);
    }
}

// 排序变化时保存排序规则
function onGridSortChange(e) {
    const sortConfig = e.sender.dataSource.sort();
    if (sortConfig?.length) {
        setCookie("RegistryGridSort", JSON.stringify(sortConfig));
    }
}

// 额外:如果有过滤功能,也要保存过滤参数(比如在GetFilterParameters函数里添加保存逻辑)
function GetFilterParameters() {
    const params = {
        // 你的过滤参数逻辑
        Name: $("#NameFilter").val(),
        CompanyId: $("#CompanyFilter").val(),
        RoleId: $("#RoleFilter").val()
    };
    // 保存过滤参数到Cookie
    setCookie("RegistryGridFilters", JSON.stringify(params));
    return params;
}

二、页面加载时恢复状态:定位到目标页并选中行

页面初始化时,我们读取Cookie里的状态,先恢复排序和过滤,再找到目标用户所在的页码,让Grid加载对应数据后选中行。

1. 页面初始化逻辑

$(document).ready(function() {
    const grid = $("#grid").data("kendoGrid");
    const savedUserId = getCookie("SelectedRegistryUserId");
    const savedSort = getCookie("RegistryGridSort");
    const savedFilters = getCookie("RegistryGridFilters");

    // 1. 恢复过滤参数
    if (savedFilters) {
        const filterParams = JSON.parse(savedFilters);
        // 把参数回填到过滤输入框
        $("#NameFilter").val(filterParams.Name);
        $("#CompanyFilter").val(filterParams.CompanyId);
        $("#RoleFilter").val(filterParams.RoleId);
    }

    // 2. 恢复排序规则
    if (savedSort) {
        grid.dataSource.sort(JSON.parse(savedSort));
    }

    // 3. 如果有保存的选中用户ID,先找它所在的页码
    if (savedUserId) {
        $.ajax({
            url: "@Url.Action("GetUserPageNumber", "Users")",
            type: "GET",
            data: {
                userId: savedUserId,
                parameters: GetFilterParameters() // 带上当前过滤参数,保证页码计算准确
            },
            success: function(pageNumber) {
                if (pageNumber > 0) {
                    // 跳转到目标页码,加载对应数据
                    grid.dataSource.page(pageNumber);
                    // 把用户ID存到全局变量,方便数据加载完成后选中
                    window.targetUserId = savedUserId;
                }
            }
        });
    }
});

// 数据加载完成后,选中目标行
function onGridDataLoaded(e) {
    const grid = e.sender;
    const targetUserId = window.targetUserId;
    if (!targetUserId) return;

    // 找到目标行
    const targetRow = grid.tbody.find("tr[data-uid]").filter(function() {
        const rowData = grid.dataItem(this);
        return rowData.Id === targetUserId;
    });

    if (targetRow.length) {
        // 选中行并滚动到可见位置
        grid.select(targetRow);
        grid.content.scrollTop(targetRow.offset().top - grid.content.offset().top);
        // 清空全局变量,避免重复处理
        delete window.targetUserId;
    }
}

2. 后台添加计算页码的方法

需要在UsersController里新增一个方法,根据用户ID和过滤参数,计算出该用户在分页列表中的页码:

public ActionResult GetUserPageNumber(string userId, string parameters)
{
    var requestViewModel = new GetUsersRequestViewModel();
    if (!string.IsNullOrWhiteSpace(parameters))
    {
        requestViewModel = JsonConvert.DeserializeObject<GetUsersRequestViewModel>(parameters);
    }

    var requestDto = new GetRegistryUsersRequestDto()
    {
        Name = requestViewModel.Name,
        CompanyId = requestViewModel.CompanyId,
        RoleId = requestViewModel.RoleId
    };

    // 注意:如果数据量极大(百万级),不要直接ToList(),改用数据库层面计算位置(比如ROW_NUMBER())
    var filteredUsers = _usersDomainService.GetRegistryUsers(requestDto).ToList();
    var userIndex = filteredUsers.FindIndex(u => u.Id == userId);
    
    if (userIndex == -1)
    {
        // 用户不存在或不符合过滤条件,返回-1
        return Json(-1, JsonRequestBehavior.AllowGet);
    }

    // 计算页码(PageSize=50,页码从1开始)
    var pageNumber = (userIndex / 50) + 1;
    return Json(pageNumber, JsonRequestBehavior.AllowGet);
}

三、性能优化提示(针对大数据量场景)

如果你的用户数据量非常大(比如超过10万条),直接ToList()加载所有过滤后的用户会导致性能问题,这时候需要修改后台的页码计算逻辑:

  • 在数据库查询时,使用ROW_NUMBER()窗口函数,根据当前的排序规则给用户列表编号,直接查询目标用户的行号,再计算页码。
  • 示例SQL逻辑大致如下:
SELECT ROW_NUMBER() OVER(ORDER BY [排序字段] [排序方向]) AS RowNum, Id
FROM RegistryUsers
WHERE [过滤条件]
HAVING Id = @TargetUserId

这样就能避免加载所有数据,大幅提升性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 06:28:13