Bootstrap不提供采购订单表单,需用其控件自行搭建:供应商信息并排col-md-6、订单头均分col-md-4、明细行用div+row模拟表格;动态增删靠JS模板注入与事件委托;单价×数量自动计算小计及总价需监听input事件并防NaN;提交前须手动校验每行必填与正整数。

Bootstrap 本身不提供“采购订单表单”这种业务组件,它只提供可组合的表单控件、栅格和工具类;直接套用 form-control 和 row/col 就能搭出可用表单,但关键在结构设计和数据关联逻辑——比如商品行动态增删、单价×数量自动算金额、总价联动,这些得自己用 JavaScript 补齐。
怎么组织采购订单的核心字段布局
采购订单通常含供应商信息、订单头、明细行三块。Bootstrap 的 row + col 足够划分区域,但要注意:明细表部分别用 table 套 form-control,而该用 div + row 模拟表格行,否则响应式会崩、移动端难操作。
- 供应商信息用
col-md-6并排放两个字段(如select或input),避免整行占满 - 订单日期、单号等头信息放在同一
row,用col-md-4均分,留白更清晰 - 明细行容器用
div class="mb-3"包裹,每行用row g-2(Bootstrap 5.3+ 推荐g-2替代form-group) - 别给明细行加
table-responsive——横向滚动会切断表单交互,改用垂直滚动 + 固定列宽(如col-md-2控制“商品名称”宽度)
怎么实现商品行动态增删(不刷新页面)
Bootstrap 不管增删逻辑,得靠 JavaScript 操作 DOM。关键是模板复用和索引管理:每次新增一行,不能靠 cloneNode(true) 硬拷贝(容易重复 ID、校验失效),而应从隐藏模板中提取 HTML 字符串,再用 insertAdjacentHTML 注入。
- 模板写在
<script type="text/template"></script>里,包含带name="items[0][product_id]"这种数组式命名的控件 - 每次新增时,用 JS 记录当前最大索引(如
nextIndex),替换模板里的[0]为[1]、[2]… - 删除按钮绑定事件委托(挂到明细容器上),用
event.target.closest(".item-row")定位并移除整行 - 删除后记得重算总价,并触发
input事件通知校验库(如novalidate下需手动调checkValidity())
怎么让单价×数量自动计算小计并汇总总价
不能只靠 onchange 绑定单个输入框,得监听明细容器的事件委托,再根据 event.target.name 判断是改了单价还是数量。计算过程要防 NaN:空值、非数字字符、负数都得兜底。
- 监听明细容器的
input事件(比change更及时),过滤name匹配items\[[0-9]+\]\[price\]或quantity - 每行小计 =
parseFloat(price) * parseInt(quantity) || 0,用|| 0避免NaN - 总价用
Array.from(rows).reduce(...)累加,别用querySelectorAll(".subtotal").forEach—— 动态增删后 DOM 可能未更新 - 把总价写进
input[name="total_amount"]的value,同时设readonly属性防止手输
提交前怎么校验明细行必填且数量为正整数
浏览器原生 required 对动态生成的行无效(DOM 插入后没触发校验注册),得手动调 reportValidity();而且数量必须是 >0 的整数,type="number" 的 min="1" 仅防 UI 输入,后端仍可能收到 0 或小数。
- 提交按钮点击时,先遍历所有明细行:
document.querySelectorAll(".item-row") - 对每行的
product_id、price、quantity分别检查:el.value.trim() !== ""且Number.isInteger(Number(el.value)) - 发现非法值,用
el.reportValidity()触发原生提示,并el.focus()定位 - 校验通过再序列化数据(如
new FormData(form)),否则event.preventDefault()
真正麻烦的不是 Bootstrap 类名怎么写,而是明细行的 name 索引同步、小计实时更新时的竞态(快速连输两次数量可能算错)、以及移动端 Safari 对 inputmode="numeric" 的兼容性——这些细节不试几次根本发现不了。


















