如何实现HTML拖拽排序列表并提交至ASP.NET Core后端
入门开发指南:拖拽组卷功能实现
一、前端实现(原生HTML+JS)
1. 基础结构与样式
先搭建两个核心区域:例题列表区和组卷区,搭配基础样式区分视觉层级:
<div class="container"> <div class="section"> <h3>课程例题列表</h3> <div id="question-list" class="question-container"> <div class="question-item" draggable="true" data-id="1">例题1:简述HTML语义化的意义</div> <div class="question-item" draggable="true" data-id="2">例题2:写出CSS垂直居中的3种方法</div> <div class="question-item" draggable="true" data-id="3">例题3:解释JS中闭包的概念及应用场景</div> </div> </div> <div class="section"> <h3>自选组卷区</h3> <div id="selected-list" class="question-container"></div> <button id="submit-btn" style="margin-top:1rem;padding:0.5rem 1rem;">提交组卷</button> </div> </div>
.container { display: flex; gap: 2rem; padding: 1rem; } .section { flex: 1; } .question-container { border: 2px solid #eee; padding: 1rem; min-height: 300px; border-radius: 4px; } .question-item { padding: 0.8rem; margin: 0.5rem 0; background: #f5f5f5; border-radius: 4px; cursor: grab; transition: background 0.2s; } .question-item:active { cursor: grabbing; } #selected-list .question-item { background: #e8f4f8; }
2. 拖拽选卷功能
用原生HTML5 Drag API实现从例题列表到组卷区的拖拽:
const questionItems = document.querySelectorAll('#question-list .question-item'); const selectedList = document.getElementById('selected-list'); // 处理例题项拖拽开始 questionItems.forEach(item => { item.addEventListener('dragstart', (e) => { e.dataTransfer.setData('text/plain', e.target.dataset.id); e.target.style.opacity = '0.5'; }); item.addEventListener('dragend', (e) => { e.target.style.opacity = '1'; }); }); // 组卷区允许拖拽 selectedList.addEventListener('dragover', (e) => { e.preventDefault(); selectedList.style.borderColor = '#2196F3'; }); selectedList.addEventListener('dragleave', () => { selectedList.style.borderColor = '#eee'; }); // 完成拖拽添加到组卷区 selectedList.addEventListener('drop', (e) => { e.preventDefault(); selectedList.style.borderColor = '#eee'; const questionId = e.dataTransfer.getData('text/plain'); const originalItem = document.querySelector(`#question-list .question-item[data-id="${questionId}"]`); // 复制原元素到组卷区(如需移动可改为移除原元素) const clonedItem = originalItem.cloneNode(true); clonedItem.draggable = true; selectedList.appendChild(clonedItem); });
3. 已选题目排序
给组卷区的题目添加拖拽排序功能:
// 记录当前拖拽的元素 let draggedItem = null; selectedList.addEventListener('dragstart', (e) => { if (e.target.classList.contains('question-item')) { draggedItem = e.target; e.target.style.opacity = '0.5'; } }); selectedList.addEventListener('dragover', (e) => { e.preventDefault(); const afterElement = getDragTarget(selectedList, e.clientY); if (afterElement) { selectedList.insertBefore(draggedItem, afterElement); } else { selectedList.appendChild(draggedItem); } }); selectedList.addEventListener('dragend', (e) => { e.target.style.opacity = '1'; draggedItem = null; }); // 辅助函数:找到拖拽位置对应的插入目标 function getDragTarget(container, y) { const items = [...container.querySelectorAll('.question-item:not(.dragging)')]; return items.reduce((closest, child) => { const box = child.getBoundingClientRect(); const offset = y - box.top - box.height / 2; if (offset < 0 && offset > closest.offset) { return { offset, element: child }; } return closest; }, { offset: Number.NEGATIVE_INFINITY }).element; }
4. 提交组卷数据
收集组卷区的题目ID顺序,发送POST请求到后端:
const submitBtn = document.getElementById('submit-btn'); submitBtn.addEventListener('click', () => { const selectedItems = selectedList.querySelectorAll('.question-item'); const questionOrder = Array.from(selectedItems).map(item => item.dataset.id); if (questionOrder.length === 0) { alert('请至少选择一道题目'); return; } fetch('/api/paper/submit', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ questionIds: questionOrder }) }) .then(res => res.json()) .then(data => { alert(data.Message || '提交成功'); selectedList.innerHTML = ''; }) .catch(err => { console.error('提交失败:', err); alert('提交失败,请重试'); }); });
二、ASP.NET Core 后端实现
1. 创建API控制器
新建PaperController处理组卷提交请求:
using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; [ApiController] [Route("api/paper")] public class PaperController : ControllerBase { // 替换为你的数据库上下文 private readonly AppDbContext _context; public PaperController(AppDbContext context) { _context = context; } [HttpPost("submit")] public async Task<IActionResult> SubmitPaper([FromBody] PaperSubmissionModel model) { if (model?.QuestionIds == null || model.QuestionIds.Count == 0) { return BadRequest("请选择至少一道题目"); } // 保存到数据库:创建组卷记录,存储题目顺序 var paper = new Paper { QuestionOrder = string.Join(',', model.QuestionIds), CreatedAt = DateTime.Now }; _context.Papers.Add(paper); await _context.SaveChangesAsync(); return Ok(new { Success = true, Message = "组卷保存成功" }); } } // 提交数据模型 public class PaperSubmissionModel { public List<string> QuestionIds { get; set; } }
2. 数据库实体与配置
创建组卷实体类,并配置DbContext:
public class Paper { public int Id { get; set; } public string QuestionOrder { get; set; } // 存储逗号分隔的题目ID public DateTime CreatedAt { get; set; } } // 在你的DbContext中添加DbSet public class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } public DbSet<Paper> Papers { get; set; } }
执行迁移命令生成数据库表:
Add-Migration AddPaperTable Update-Database
三、入门优化建议
- 例题列表改为从后端接口动态加载,替换静态HTML
- 给组卷区的题目添加删除按钮,支持移除操作
- 后端添加参数验证,过滤非法题目ID
- 如需兼容旧浏览器,可替换为SortableJS等轻量拖拽库
内容的提问来源于stack exchange,提问作者Pezhvak
相关产品推荐
相关产品推荐

