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

如何通过两个按钮重排Div并持久化排序结果?

问题描述

通过foreach循环渲染多个列表项,希望通过上下按钮实现列表项重排,并且保存排序结果,刷新页面后仍保持新顺序。

已完成工作

  • 用JavaScript实现了列表项的上下移动
  • 在Lesson类中添加了public int OrderNum字段,列表项按OrderNum升序排列
  • 问题:点击按钮时无法修改对应Lesson的OrderNum值并完成保存

现有代码

C# Lesson类

public class Lesson : IModel
{
    public override String partitionKey { get => CourseId; }

    [Required]
    public string CourseId { get; set; }

    [Required]
    [StringLength(256, ErrorMessage = "Maximum {1} characters")]
    public string Title { get; set; }
    [Required]
    public string Content { get; set; }

    public string ImageID { get; set; }
    public string ImageURL { get => (ImageID != null) ? Storage.Image.Path(ImageID) : null; }

    public int OrderNum { get; set; } 
}

HTML 渲染代码

@foreach (var Lesson in Model.Lessons.OrderBy(x => x.OrderNum))
{
<li class="list-group-item" data-lesson-id="@Lesson.id">
    <a href="@Url.Action("Index","Lesson", new { CourseCategory = Model.Course.Category, CourseID = Model.Course.id, Lesson.id })">@Lesson.OrderNum</a>               
            @if (AppUser.CurrentUser.Areas.Admin)
            {
                <a class='up' href='#'><i class="fa-solid fa-arrow-up fa-lg"></i></a> <a class='down' href='#'><i class="fa-solid fa-arrow-down fa-lg"></i></a>
                <div class="Edit-Buttons-L">
                    <div>
                        <a href="@Url.Action("EditLesson", "Lesson", new { CourseCategory = Model.Course.Category, CourseID = Model.Course.id, Lesson.id })">
                            <i class="fa-regular fa-pen-to-square fa-lg"></i>
                        </a>
                    </div>
                    <div>
                        <form action="@Url.Action("DeleteLesson","Lesson", new{CourseCategory = Model.Course.Category, CourseID = Model.Course.id, Lesson.id})" method="post">
                            <button type="submit" class="fa-regular fa-trash-can fa-lg" value="Delete"></button>
                        </form>
                    </div>                           
                </div>
            }
    </li>
}

现有JavaScript代码

window.onload = function () {
    var upLink = document.querySelectorAll(".up");

    for (var i = 0; i < upLink.length; i++) {
        upLink[i].addEventListener('click', function () {
            var wrapper = this.parentElement;
            

            if (wrapper.previousElementSibling)
                wrapper.parentNode.insertBefore(wrapper, wrapper.previousElementSibling);
        });
    }

    var downLink = document.querySelectorAll(".down");

    for (var i = 0; i < downLink.length; i++) {
        downLink[i].addEventListener('click', function () {
            var wrapper = this.parentElement;

            if (wrapper.nextElementSibling)
                wrapper.parentNode.insertBefore(wrapper.nextElementSibling, wrapper);
        });
    }
}

解决方案

要实现排序保存,需要在前端移动元素后收集排序信息,发送到后端更新数据库,步骤如下:

1. 修改HTML,添加Lesson ID标识

给每个列表项添加data-lesson-id属性,用于后续获取对应Lesson的ID(已在上面的HTML代码中修改)

2. 更新JavaScript,添加保存逻辑

修改JS代码,在移动元素后调用保存函数,将新的排序发送到后端:

window.onload = function () {
    // 阻止默认跳转行为
    function preventDefault(e) {
        e.preventDefault();
    }

    // 向上移动逻辑
    document.querySelectorAll(".up").forEach(link => {
        link.addEventListener('click', function (e) {
            preventDefault(e);
            const wrapper = this.closest('.list-group-item');
            if (wrapper.previousElementSibling) {
                wrapper.parentNode.insertBefore(wrapper, wrapper.previousElementSibling);
                saveLessonOrder();
            }
        });
    });

    // 向下移动逻辑
    document.querySelectorAll(".down").forEach(link => {
        link.addEventListener('click', function (e) {
            preventDefault(e);
            const wrapper = this.closest('.list-group-item');
            if (wrapper.nextElementSibling) {
                wrapper.parentNode.insertBefore(wrapper.nextElementSibling, wrapper);
                saveLessonOrder();
            }
        });
    });

    // 保存排序到后端
    function saveLessonOrder() {
        // 收集当前排序的Lesson ID和对应的OrderNum
        const lessonOrder = Array.from(document.querySelectorAll('.list-group-item')).map((item, index) => ({
            LessonId: item.dataset.lessonId,
            OrderNum: index + 1 // 排序从1开始,可根据需求调整
        }));

        // 获取防伪造令牌(ASP.NET必填)
        const token = document.querySelector('input[name="__RequestVerificationToken"]')?.value;
        if (!token) {
            alert('缺少验证令牌,请刷新页面重试');
            return;
        }

        // 发送POST请求
        fetch('@Url.Action("UpdateLessonOrder", "Lesson")', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'RequestVerificationToken': token
            },
            body: JSON.stringify(lessonOrder)
        })
        .then(response => {
            if (!response.ok) throw new Error('保存失败');
            return response.json();
        })
        .then(() => {
            // 更新页面显示的OrderNum
            document.querySelectorAll('.list-group-item').forEach((item, index) => {
                const orderEl = item.querySelector('a:first-child');
                if (orderEl) orderEl.textContent = index + 1;
            });
        })
        .catch(err => {
            console.error('保存出错:', err);
            alert('排序保存失败,请重试');
        });
    }
};

3. 添加后端接口

在LessonController中添加接收排序更新的接口,同时创建对应的DTO类:

// DTO类,用于接收前端发送的排序数据
public class LessonOrderUpdateDto
{
    public string LessonId { get; set; }
    public int OrderNum { get; set; }
}

// LessonController中的方法
[HttpPost]
public IActionResult UpdateLessonOrder([FromBody] List<LessonOrderUpdateDto> lessonOrders)
{
    if (lessonOrders == null || !lessonOrders.Any())
    {
        return BadRequest("无有效排序数据");
    }

    // 遍历更新每个Lesson的OrderNum
    foreach (var update in lessonOrders)
    {
        var lesson = _lessonRepository.GetById(update.LessonId); // 替换为你的数据获取逻辑
        if (lesson != null)
        {
            lesson.OrderNum = update.OrderNum;
            _lessonRepository.Update(lesson); // 替换为你的数据更新逻辑
        }
    }

    _lessonRepository.SaveChanges(); // 保存到数据库
    return Ok(new { Success = true });
}

4. 页面添加防伪造令牌

在页面的<form>外或合适位置添加ASP.NET防伪造令牌,确保POST请求能通过验证:

@Html.AntiForgeryToken()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 12:30:54