Laravel 5.6发票表单仅保存首行商品数据问题求助
解决Laravel多商品发票表单仅首行数据保存的问题
嘿,我帮你梳理下这个问题:从你的请求数据能看出来,只有第一行商品数据被提交到后端了,这说明动态新增的表单行没有被正确识别和提交,结合你的代码来看,主要问题出在前端的jQuery新增逻辑里,还有控制器里的一个小错误,咱们一步步搞定:
1. 修复动态新增行的核心问题(前端)
你写的addRow函数里直接用了Blade的@foreach语法,这可是后端渲染的代码啊!页面加载完成后,前端的jQuery根本认不出@foreach,所以你动态生成的<select>里其实根本没有实际的产品选项,甚至可能因为语法错误,浏览器都不把这些新增行的表单元素当成表单的一部分,自然提交的时候就只会带第一行的数据。
正确的做法是先把后端的产品列表转成前端能识别的JS变量,再用JS动态生成选项:
第一步:在Blade模板里定义产品列表的JS变量
在页面底部(比如</body>标签前)加这段代码,把后端的$product_lists转成JS对象:
<script> // 用@json把Laravel数组转成合法的JS对象 const productLists = @json($product_lists); </script>
第二步:修改addRow函数,用JS生成选项
把原来的addRow改成这样,用JS循环生成select的选项:
function addRow(){ // 先构建select的选项内容 let productOptions = '<option value="0" selected="true" disabled="true">Select Product</option>'; // 循环遍历产品列表,生成每个选项 for(const [productId, productName] of Object.entries(productLists)){ productOptions += `<option value="${productId}">${productName}</option>`; } // 组装完整的表格行HTML const newRow = ` <tr> <td> <select class="form-control name" name="productname[]">${productOptions}</select> </td> <td><input type="text" class="form-control form-control-line qty" name="qty[]"></td> <td><input type="text" class="form-control form-control-line price" name="price[]"></td> <td><input type="text" class="form-control form-control-line total" name="total[]"></td> <td><a href="#" class="remove">-<i class="glyphicon glyphicon-remove"></i></a></td> </tr> `; $('tbody').append(newRow); };
这样生成的新行里,select才有真实的产品选项,而且所有表单元素的name属性(比如productname[])都能被浏览器正确识别,提交时就会把所有行的数据都打包成数组传给后端了。
2. 修复控制器里的产品ID赋值错误
你的控制器代码里有个明显的错误:'product_id'=>$request->$v,这里的$v已经是productname数组里的产品ID值(比如1),你不需要再用$request->$v去取值,直接用$v就可以了!另外还要记得给商品关联对应的发票ID,不然商品数据不知道属于哪张发票:
public function store(Request $request) { // 验证发票号 $validated = $this->validate(request(), [ 'invoice_no' => 'required' ]); // 创建发票记录,保存后拿到发票ID $invoice = SalesInvoice::create($validated); // 先判断是否有商品数据,避免空数组报错 if($request->has('productname') && is_array($request->productname)){ foreach ($request->productname as $key => $productId) { // 跳过未选择产品的空行 if($productId == 0) continue; // 组装商品数据 $itemData = [ 'sales_invoice_id' => $invoice->id, // 必须关联发票ID 'product_id' => $productId, // 这里直接用productId即可 'qty' => $request->qty[$key], 'price' => $request->price[$key], 'total' => $request->total[$key] ]; // 推荐用create而不是insert,能触发模型的事件和验证 SalesInvoiceItem::create($itemData); } } // 这里可以加个重定向或者返回响应 return redirect()->back()->with('success', '发票创建成功!'); }
3. 额外的实用优化建议
- 给删除按钮绑定事件,确保能正确删除行:
// 用事件委托绑定删除按钮,因为动态生成的元素直接绑定事件无效 $(document).on('click', '.remove', function(e){ e.preventDefault(); // 阻止默认跳转行为 $(this).closest('tr').remove(); // 删除当前行 }); - 自动计算总价,用户输入数量和价格后自动算出total,不用手动输入:
// 监听数量和价格的输入事件,自动计算总价 $(document).on('input', '.qty, .price', function(){ const currentRow = $(this).closest('tr'); const qty = parseFloat(currentRow.find('.qty').val()) || 0; const price = parseFloat(currentRow.find('.price').val()) || 0; currentRow.find('.total').val(qty * price); }); - 可以加前端验证,比如确保每行都选择了产品、数量和价格不为空,避免提交无效数据。
内容的提问来源于stack exchange,提问作者FreelanceX
相关产品推荐
相关产品推荐

