基于Spring+Thymeleaf实现可动态新增多行的配药表单批量提交功能
Spring + Thymeleaf 动态配药明细一次性提交实现方案
1 后端核心代码实现
1.1 实体类设计
// 配药明细实体类,对应dispense表 public class Dispense { private Long id; private Long consultationId; // 关联问诊单ID private String drugName; // 药品名称 private String specification; // 药品规格 private Integer dosage; // 用量 private String usage; // 使用方法 // 省略Getter、Setter、无参构造方法 } // 问诊单主表单实体,用于接收前端提交的全量数据 public class ConsultationForm { private Long patientId; private String symptom; private String diagnosis; // 核心字段:存储所有动态新增的配药明细 private List<Dispense> dispenseList = new ArrayList<>(); // 省略Getter、Setter }
1.2 控制器层逻辑
@Controller @RequestMapping("/consultation") public class ConsultationController { @Resource private ConsultationService consultationService; @Resource private DispenseService dispenseService; // 跳转问诊单录入页面 @GetMapping("/form") public String showForm(Model model) { ConsultationForm form = new ConsultationForm(); // 默认初始化一条空配药行,满足Thymeleaf集合绑定要求 form.getDispenseList().add(new Dispense()); model.addAttribute("consultationForm", form); return "consultation-form"; } // 表单提交接口,实现批量插入配药数据 @PostMapping("/submit") public String submitForm(@ModelAttribute ConsultationForm form, RedirectAttributes attr) { // 第一步:保存问诊单主数据,获取生成的问诊单ID Long consultationId = consultationService.saveMainInfo(form); // 第二步:为所有配药明细绑定关联的问诊单ID,过滤空行 List<Dispense> validDispenseList = form.getDispenseList().stream() .filter(item -> StrUtil.isNotBlank(item.getDrugName())) .peek(item -> item.setConsultationId(consultationId)) .collect(Collectors.toList()); // 第三步:批量插入所有有效配药明细到dispense表 dispenseService.batchInsert(validDispenseList); attr.addFlashAttribute("msg", "提交成功,共录入" + validDispenseList.size() + "条配药明细"); return "redirect:/consultation/list"; } }
1.3 批量入库MyBatis实现
<!-- DispenseMapper.xml 批量插入语句 --> <insert id="batchInsert" parameterType="java.util.List"> INSERT INTO dispense (consultation_id, drug_name, specification, dosage, `usage`) VALUES <foreach collection="list" item="item" separator=","> (#{item.consultationId}, #{item.drugName}, #{item.specification}, #{item.dosage}, #{item.usage}) </foreach> </insert>
2 前端Thymeleaf页面实现
2.1 配药明细网格表单代码
<form th:action="@{/consultation/submit}" th:object="${consultationForm}" method="post" id="consultationForm"> <!-- 省略问诊单其他字段:患者信息、主诉、诊断等 --> <h3>配药明细</h3> <table id="dispenseTable" class="table table-bordered"> <thead> <tr> <th>药品名称</th> <th>规格</th> <th>剂量</th> <th>用法</th> <th>操作</th> </tr> </thead> <tbody> <!-- Thymeleaf索引绑定集合元素 --> <tr th:each="item, stat : *{dispenseList}"> <td><input type="text" th:field="*{dispenseList[__${stat.index}__].drugName}" class="form-control"></td> <td><input type="text" th:field="*{dispenseList[__${stat.index}__].specification}" class="form-control"></td> <td><input type="number" th:field="*{dispenseList[__${stat.index}__].dosage}" class="form-control"></td> <td><input type="text" th:field="*{dispenseList[__${stat.index}__].usage}" class="form-control"></td> <td><button type="button" class="btn btn-danger delRowBtn">删除</button></td> </tr> </tbody> </table> <button type="button" id="addRowBtn" class="btn btn-primary mt-2">新增配药条目</button> <button type="submit" class="btn btn-success mt-2 ms-2">提交全部数据</button> </form>
2.2 动态新增/删除行JS逻辑
// 新增配药行逻辑 document.getElementById('addRowBtn').addEventListener('click', () => { const tableBody = document.querySelector('#dispenseTable tbody'); const rowCount = tableBody.querySelectorAll('tr').length; // 新行的name属性索引与当前行数保持一致,保证后端集合绑定正确 const newRow = ` <tr> <td><input type="text" name="dispenseList[${rowCount}].drugName" class="form-control"></td> <td><input type="text" name="dispenseList[${rowCount}].specification" class="form-control"></td> <td><input type="number" name="dispenseList[${rowCount}].dosage" class="form-control"></td> <td><input type="text" name="dispenseList[${rowCount}].usage" class="form-control"></td> <td><button type="button" class="btn btn-danger delRowBtn">删除</button></td> </tr> `; tableBody.insertAdjacentHTML('beforeend', newRow); }); // 删除配药行逻辑,事件委托适配动态新增的行 document.querySelector('#dispenseTable tbody').addEventListener('click', (e) => { if (!e.target.classList.contains('delRowBtn')) return; // 删除当前行 e.target.closest('tr').remove(); // 重新计算所有行的索引,避免提交时集合绑定错位 const allRows = document.querySelectorAll('#dispenseTable tbody tr'); allRows.forEach((row, index) => { row.querySelectorAll('input').forEach(input => { const originName = input.getAttribute('name'); if (originName) { const newName = originName.replace(/dispenseList\[\d+\]/, `dispenseList[${index}]`); input.setAttribute('name', newName); } }); }); });
3 注意事项
- 可在提交前增加表单校验,过滤未填写核心字段的空行,避免无效数据入库
- 配药条目数量较多时,建议增加懒加载或分页逻辑,避免页面卡顿
- 如需草稿保存能力,可增加临时存储接口定期缓存表单内容,无需提前写入正式配药表
内容的提问来源于stack exchange,提问作者hoosain.madhi
相关产品推荐
相关产品推荐

