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

Laravel项目基于Bootstrap模态框实现购物车选品功能的问题咨询

实现方案

我们采用Session存储临时购物车数据 + jQuery异步请求动态刷新的方案解决所有问题,步骤如下:


步骤1:新增路由

在routes/web.php中新增购物车临时操作路由:

use App\Http\Controllers\CartController;

// 购物车商品增删临时接口
Route::post('/cart/add-item', [CartController::class, 'addItem'])->name('cart.addItem');
Route::post('/cart/remove-item', [CartController::class, 'removeItem'])->name('cart.removeItem');
// 原有资源路由保留
Route::resource('carts', CartController::class);

步骤2:补充CartController逻辑

<?php
namespace App\Http\Controllers;

use App\Models\Product;
use App\Models\Cart;
use App\Models\CartProd;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\DB;

class CartController extends Controller
{
    public function create(Request $request)
    {
        $product_list = Product::all();
        $empty_cart = new Cart();
        // 首次进入清空旧的临时购物车数据
        Session::forget('selected_products');
        $selected_products = Session::get('selected_products', []);
        return view('models.carts.create', compact('product_list','empty_cart','selected_products'));
    }

    // 添加商品到临时购物车
    public function addItem(Request $request)
    {
        $validated = $request->validate([
            'product_id' => 'required|exists:products,id',
            'amount' => 'required|integer|min:1',
            'discount' => 'required|numeric|min:0|max:100'
        ]);

        $product = Product::findOrFail($validated['product_id']);
        // 校验库存
        if ($validated['amount'] > $product->amount) {
            return response()->json(['status' => 0, 'msg' => '库存不足']);
        }

        // 计算CartProd属性
        $unit_price = $product->price;
        $total_cost = $unit_price * $validated['amount'] * (1 - $validated['discount'] / 100);

        // 组装临时CartProd对象
        $cartItem = new CartProd();
        $cartItem->product_id = $product->id;
        $cartItem->product = $product;
        $cartItem->amount = $validated['amount'];
        $cartItem->discount = $validated['discount'];
        $cartItem->unit_price = $unit_price;
        $cartItem->total_cost = $total_cost;

        // 存入Session
        $selected = Session::get('selected_products', []);
        // 避免重复添加同商品,如需叠加数量可修改此处逻辑
        $exists = collect($selected)->where('product_id', $product->id)->first();
        if ($exists) {
            return response()->json(['status' => 0, 'msg' => '该商品已在购物车中']);
        }
        $selected[] = $cartItem;
        Session::put('selected_products', $selected);

        // 返回渲染后的列表HTML
        $html = view('models.carts.partials.selected_list', compact('selected'))->render();
        return response()->json(['status' => 1, 'html' => $html]);
    }

    // 删除临时购物车商品
    public function removeItem(Request $request)
    {
        $validated = $request->validate([
            'product_id' => 'required|integer'
        ]);

        $selected = Session::get('selected_products', []);
        // 过滤要删除的商品
        $selected = collect($selected)->filter(function($item) use ($validated) {
            return $item->product_id != $validated['product_id'];
        })->values()->toArray();
        Session::put('selected_products', $selected);

        $html = view('models.carts.partials.selected_list', compact('selected'))->render();
        return response()->json(['status' => 1, 'html' => $html]);
    }

    // 提交购物车存储逻辑
    public function store(Request $request)
    {
        $selected = Session::get('selected_products', []);
        if (empty($selected)) {
            return back()->withErrors('购物车不能为空');
        }

        // 开启事务保证数据一致性
        DB::beginTransaction();
        try {
            // 计算购物车总金额
            $total_cost = collect($selected)->sum('total_cost');
            // 创建Cart主表
            $cart = Cart::create([
                'cost' => $total_cost
                // 其他字段如user_id按需补充
            ]);

            // 批量创建CartProd关联数据
            $cart_prod_data = collect($selected)->map(function($item) use ($cart) {
                return [
                    'cart_id' => $cart->id,
                    'product_id' => $item->product_id,
                    'unit_price' => $item->unit_price,
                    'amount' => $item->amount,
                    'discount' => $item->discount,
                    'total_cost' => $item->total_cost
                ];
            })->toArray();
            CartProd::insert($cart_prod_data);

            // 扣商品库存(可选,按业务需求调整)
            foreach ($selected as $item) {
                Product::find($item->product_id)->decrement('amount', $item->amount);
            }

            // 清空临时购物车
            Session::forget('selected_products');
            DB::commit();
            return redirect()->route('carts.index')->with('success', '购物车创建成功');
        } catch (\Exception $e) {
            DB::rollBack();
            return back()->withErrors('操作失败:'.$e->getMessage());
        }
    }
}

步骤3:抽离选中商品列表公共模板

新建resources/views/models/carts/partials/selected_list.blade.php:

@forelse($selected as $index => $prodCart)
<tr>
    <td>{{ $index + 1 }}</td>
    <td>{{ $prodCart->product->name }}</td>
    <td>{{ $prodCart->unit_price }}</td>
    <td>{{ $prodCart->amount }}</td>
    <td>{{ $prodCart->discount }}%</td>
    <td>{{ $prodCart->total_cost }}</td>
    <td>
        <a class="btn btn-danger btn-circle btn-sm delete-btn" data-product-id="{{ $prodCart->product_id }}">
            <i class="fa fa-trash"></i>
        </a>
    </td>
</tr>
@empty
<tr><td colspan="7">Empty Cart</td></tr>
@endforelse

步骤4:修改原有视图文件

4.1 调整create.blade.php

@extends('adminlte::page')
@section('content')
<div class="container"><div class="row justify-content-center"><div class="col-md-8"><div class="card">
    <div class="card-header">
        <h3 class="card-title"> New Cart </h3>
        <div class="card-tools">
            <a class="btn btn-success btn-tool" href="#" data-toggle="modal" data-target="#add">
                <i class="fas fa-plus"></i> Add Product
            </a>
        </div>
    </div>
    <div class="card-body">
        <table class="table table-striped projects table-sm">
            <thead><tr><th>#</th><th>Name</th><th>Unit Cost</th><th>Amount</th><th>Discount</th><th>Final Cost</th><th></th></tr></thead>
            <tbody id="selected-list">
                @include('models.carts.partials.selected_list', ['selected' => $selected_products])
            </tbody>
        </table>
    </div>
    <div class="card-footer" id="cart-footer">
        @if(!empty($selected_products))
        <form action="{{ route('carts.store') }}" method="POST" style="display:inline;">
            @csrf
            <button type="submit" class="btn btn-success"> Buy Cart </button>
        </form>
        @endif
    </div>
</div></div></div></div>
@include('models.carts.add')
@endsection

@section('scripts')
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<script>
// 添加商品事件
$('.add-btn').click(function() {
    const productId = $(this).data('product-id');
    const amount = $(`input[name="amount_${productId}"]`).val();
    const discount = $(`input[name="discount_${productId}"]`).val();

    $.post("{{ route('cart.addItem') }}", {
        _token: "{{ csrf_token() }}",
        product_id: productId,
        amount: amount,
        discount: discount
    }, function(res) {
        if (res.status === 1) {
            $('#selected-list').html(res.html);
            // 更新底部购买按钮
            if ($('#selected-list').text().indexOf('Empty Cart') === -1) {
                $('#cart-footer').html(`
                    <form action="{{ route('carts.store') }}" method="POST" style="display:inline;">
                        @csrf
                        <button type="submit" class="btn btn-success"> Buy Cart </button>
                    </form>
                `);
            }
            $('#add').modal('hide');
            alert('添加成功');
        } else {
            alert(res.msg);
        }
    })
})

// 删除商品事件
$(document).on('click', '.delete-btn', function() {
    const productId = $(this).data('product-id');
    if (confirm('确定删除该商品?')) {
        $.post("{{ route('cart.removeItem') }}", {
            _token: "{{ csrf_token() }}",
            product_id: productId
        }, function(res) {
            $('#selected-list').html(res.html);
            // 更新底部购买按钮
            if ($('#selected-list').text().indexOf('Empty Cart') !== -1) {
                $('#cart-footer').html('');
            }
            alert('删除成功');
        })
    }
})
</script>
@stop

4.2 调整add.blade.php(解决input id重复问题)

<!--Modal add -->
<div class="modal fade" id="add" tabindex="-1" role="dialog" aria-hidden="true"><div class="modal-dialog modal-lg"><div class="modal-content">
    <div class="modal-header bg-dark">
        <h5 class="modal-title" id="exampleModalLabel">Add Product to Cart</h5>
        <span class="badge badge-danger" class="close" data-dismiss="modal" aria-label="Close">
            <i class="fas fa-times fa-lg" style="color:#fff"></i>
        </span>
    </div>
    <div class="modal-body">
        <table class="table table-striped projects table-sm">
            <thead><tr><th>#</th><th>Name</th><th>Amount</th><th>Price</th><th>Added</th><th>Discount</th><th>Amount</th><th>Add</th></tr></thead>
            <tbody>
                @forelse($product_list as $product)
                <tr>
                    <td>{{ $loop->iteration  }}</td>
                    <td>{{ $product->name  }}</td>
                    <td>{{ $product->amount  }}</td>
                    <td>{{ $product->price  }}</td>
                    <td>{{ $product->created_at  }}</td>
                    <td><input type="number" name="discount_{{$product->id}}" min="0" max="100" size="3" value="0"></td>
                    <td><input type="number" name="amount_{{$product->id}}" min="1" max="{{$product->amount}}" size="3" value="1"></td>
                    <td>
                        <a class="btn btn-info btn-circle btn-sm add-btn" data-product-id="{{$product->id}}">
                            <i class="fa fa-plus"></i>
                        </a>
                    </td>
                </tr>
                @empty
                <tr>
                    <td>No Products Available.</td>
                </tr>
                @endforelse
            </tbody>
        </table>
</div></div></div></div>

问题对应解决说明

  1. 模态框添加商品:通过按钮绑定点击事件,将商品ID、数量、折扣异步提交到addItem方法,生成临时CartProd对象存入Session
  2. 删除误加商品:删除按钮绑定点击事件,提交商品ID到removeItem方法,过滤Session中对应商品
  3. 自动更新列表:每次增删操作成功后,后端返回渲染好的列表HTML,前端直接替换DOM内容
  4. 提交购物车:购买按钮绑定POST表单提交到store方法,通过事务生成Cart、CartProd记录,扣减库存后清空Session

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 02:00:05