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

Laravel干预模块中基于已用数量筛选产品的实现方法

Laravel干预管理应用:产品列表动态筛选实现方案

场景与需求

我开发的Laravel应用用于管理干预(interventions)并追踪产品使用情况,涉及三张关联表:

  • sorties_approuvees:已批准产品出库表(存储产品总数量等基础信息)
  • interventions:干预表(记录干预的基本信息)
  • intervention_sortie_approuvee:中间表,关联干预与已批准出库产品,记录该干预使用的产品数量

核心功能需求:

  • 新增干预:产品列表仅展示总数量 > 已用总数量的产品(已耗尽的产品不显示)
  • 修改干预:产品列表需包含两类产品:
    1. 总数量 > 已用总数量的可用产品
    2. 当前干预已关联的产品(即使该产品已被完全使用)
  • 所有表单通过Bootstrap模态框(Modal)弹窗展示

示例场景:

  • 电缆总数量2,已用2;灯具总数量5,已用3
  • 新增干预时,仅显示灯具
  • 修改某使用了2根电缆+3盏灯具的干预时,电缆和灯具都会显示

表结构

-- 已批准产品出库表
CREATE TABLE sorties_approuvees (
    id INT PRIMARY KEY AUTO_INCREMENT,
    designation VARCHAR(255) NOT NULL, -- 产品名称
    quantite_total INT NOT NULL, -- 产品总数量
    -- 其他业务字段...
);

-- 干预表
CREATE TABLE interventions (
    id INT PRIMARY KEY AUTO_INCREMENT,
    libelle VARCHAR(255) NOT NULL, -- 干预名称
    -- 其他业务字段...
);

-- 关联中间表
CREATE TABLE intervention_sortie_approuvee (
    id INT PRIMARY KEY AUTO_INCREMENT,
    intervention_id INT NOT NULL,
    sortie_approuvee_id INT NOT NULL,
    quantite_utilisee INT NOT NULL, -- 该干预使用的产品数量
    FOREIGN KEY (intervention_id) REFERENCES interventions(id),
    FOREIGN KEY (sortie_approuvee_id) REFERENCES sorties_approuvees(id)
);

Laravel模型关联

Intervention.php(干预模型)

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Intervention extends Model
{
    protected $fillable = ['libelle'];

    // 关联已批准出库产品
    public function sortieApprouvees()
    {
        return $this->belongsToMany(SortieApprouvee::class, 'intervention_sortie_approuvee')
                    ->withPivot('quantite_utilisee');
    }
}

SortieApprouvee.php(已批准出库产品模型)

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class SortieApprouvee extends Model
{
    protected $fillable = ['designation', 'quantite_total'];

    // 关联干预
    public function interventions()
    {
        return $this->belongsToMany(Intervention::class, 'intervention_sortie_approuvee')
                    ->withPivot('quantite_utilisee');
    }
}

控制器逻辑

列表页与新增数据准备(Index方法)

namespace App\Http\Controllers;

use App\Models\Intervention;
use App\Models\SortieApprouvee;
use Illuminate\Http\Request;

class InterventionController extends Controller
{
    public function index()
    {
        // 新增干预时的可用产品:总数量 > 已用总数量
        $productsForCreate = SortieApprouvee::query()
            ->withSum('interventions', 'quantite_utilisee')
            ->havingRaw('quantite_total > interventions_sum_quantite_utilisee')
            ->get();

        return view('interventions.index', compact('productsForCreate'));
    }

    // 编辑干预时获取产品与干预数据
    public function edit(Intervention $intervention)
    {
        // 获取当前干预关联的产品ID集合
        $associatedProductIds = $intervention->sortieApprouvees()->pluck('sorties_approuvees.id')->toArray();

        // 筛选产品:可用产品 或 当前干预关联的产品
        $productsForEdit = SortieApprouvee::query()
            ->withSum('interventions', 'quantite_utilisee')
            ->where(function ($query) use ($associatedProductIds) {
                $query->havingRaw('quantite_total > interventions_sum_quantite_utilisee')
                      ->orWhereIn('id', $associatedProductIds);
            })
            ->get();

        return response()->json([
            'intervention' => $intervention,
            'products' => $productsForEdit,
            'associatedProducts' => $intervention->sortieApprouvees->pluck('id')->toArray()
        ]);
    }
}

视图与前端实现

列表页编辑按钮

<button class="btn btn-sm btn-warning btn-edit" data-id="{{ $intervention->id }}">编辑干预</button>

新增/编辑模态框

<!-- 新增干预模态框 -->
<div class="modal fade" id="createInterventionModal" tabindex="-1" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">新增干预</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <form action="{{ route('interventions.store') }}" method="POST">
                    @csrf
                    <div class="mb-3">
                        <label for="createLibelle" class="form-label">干预名称</label>
                        <input type="text" class="form-control" id="createLibelle" name="libelle" required>
                    </div>

                    <div class="mb-3">
                        <label class="form-label">选择产品</label>
                        <select name="products[]" multiple class="form-select">
                            @foreach($productsForCreate as $product)
                                <option value="{{ $product->id }}">
                                    {{ $product->designation }} 
                                    (剩余: {{ $product->quantite_total - $product->interventions_sum_quantite_utilisee }})
                                </option>
                            @endforeach
                        </select>
                    </div>

                    <div class="modal-footer">
                        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
                        <button type="submit" class="btn btn-primary">保存干预</button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

<!-- 编辑干预模态框 -->
<div class="modal fade" id="editInterventionModal" tabindex="-1" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">编辑干预</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <form id="editInterventionForm">
                    @csrf
                    @method('PUT')
                    <input type="hidden" id="editInterventionId" name="id">

                    <div class="mb-3">
                        <label for="editLibelle" class="form-label">干预名称</label>
                        <input type="text" class="form-control" id="editLibelle" name="libelle" required>
                    </div>

                    <div class="mb-3">
                        <label class="form-label">选择产品</label>
                        <select name="products[]" multiple class="form-select" id="editProductSelect">
                            <!-- 动态填充 -->
                        </select>
                    </div>

                    <div class="modal-footer">
                        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
                        <button type="submit" class="btn btn-primary">更新干预</button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

前端交互脚本

// 编辑按钮点击事件
document.querySelectorAll('.btn-edit').forEach(btn => {
    btn.addEventListener('click', async () => {
        const interventionId = btn.getAttribute('data-id');
        const res = await fetch(`/interventions/${interventionId}/edit`);
        const data = await res.json();

        // 填充干预基础信息
        document.getElementById('editInterventionId').value = data.intervention.id;
        document.getElementById('editLibelle').value = data.intervention.libelle;

        // 动态填充产品选择列表
        const productSelect = document.getElementById('editProductSelect');
        productSelect.innerHTML = '';

        data.products.forEach(product => {
            const remaining = product.quantite_total - product.interventions_sum_quantite_utilisee;
            const option = document.createElement('option');
            option.value = product.id;
            option.textContent = `${product.designation} (剩余: ${remaining >= 0 ? remaining : 0})`;

            // 标记当前干预已关联的产品为选中
            if (data.associatedProducts.includes(product.id)) {
                option.selected = true;
            }

            productSelect.appendChild(option);
        });

        // 打开编辑模态框
        new bootstrap.Modal(document.getElementById('editInterventionModal')).show();
    });
});

// 处理编辑表单提交
document.getElementById('editInterventionForm').addEventListener('submit', async (e) => {
    e.preventDefault();
    const interventionId = document.getElementById('editInterventionId').value;
    const formData = new FormData(e.target);

    await fetch(`/interventions/${interventionId}`, {
        method: 'PUT',
        body: formData,
        headers: {
            'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
        }
    });

    // 刷新页面或更新列表
    location.reload();
});

功能验证

按照示例场景测试:

  1. 新增干预时,productsForCreate仅返回灯具(电缆已耗尽,不符合总数量>已用数量的条件)
  2. 编辑目标干预时,productsForEdit同时返回电缆和灯具:电缆属于当前干预关联产品,灯具属于可用产品

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 21:05:01