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

Laravel Blade动态数据绑定实现依赖式下拉框:根据选中卖家筛选对应客户

这是个非常常见的动态表单联动需求,我给你几个不同方向的实现方案,从无需额外框架的原生JS,到你提到的Livewire、Vue都覆盖到了,你可以根据项目的技术栈和复杂度来选:

方案一:原生JavaScript + 后端API(无额外框架依赖)

这个方案适合不想引入新工具的场景,只需要简单的JS配合一个后端接口就能实现:

1. 后端新增API接口

首先在你的Laravel项目里加一个路由,用于根据卖家ID返回对应的客户:

// routes/web.php
Route::get('/clients/by-seller/{sellerId}', function($sellerId) {
    // 这里替换成你实际的客户查询逻辑,假设Client模型有user_id关联卖家
    $clients = \App\Models\Client::where('user_id', $sellerId)->get(['id', 'name']);
    return response()->json($clients);
})->middleware('auth');

2. 前端添加联动逻辑

给你的表单加上监听卖家选择器变化的JS代码,动态更新客户选项:

<!-- 放在原表单的末尾,或者页面的script标签里 -->
<script>
const sellerSelect = document.getElementById('input-seller');
const clientSelect = document.getElementById('input-client_id');

// 定义更新客户选项的函数
function updateClients(sellerId) {
    // 先清空现有选项,显示加载状态
    clientSelect.innerHTML = '<option value="">加载中...</option>';
    
    // 发送请求到后端API
    fetch(`/clients/by-seller/${sellerId}`)
        .then(response => response.json())
        .then(clients => {
            // 清空加载提示
            clientSelect.innerHTML = '';
            // 遍历添加新选项,同时保留旧值的选中状态
            clients.forEach(client => {
                const option = document.createElement('option');
                option.value = client.id;
                option.textContent = client.name;
                if (client.id == {{ old('client_id') ?? 0 }}) {
                    option.selected = true;
                }
                clientSelect.appendChild(option);
            });
        })
        .catch(err => {
            console.error('加载客户失败:', err);
            clientSelect.innerHTML = '<option value="">加载失败,请重试</option>';
        });
}

// 监听卖家选择器的变化事件
sellerSelect.addEventListener('change', function() {
    updateClients(this.value);
});

// 页面加载时自动执行一次,初始化当前选中卖家的客户
updateClients(sellerSelect.value);
</script>

方案二:用Livewire实现(Laravel生态下最简便的方案)

如果你已经在用Laravel,Livewire能让你几乎不用写JS就能实现实时联动:

1. 创建Livewire组件

先执行Artisan命令生成组件:

php artisan make:livewire SellerClientForm

2. 编写组件逻辑

打开生成的app/Http/Livewire/SellerClientForm.php,替换成以下代码:

<?php

namespace App\Http\Livewire;

use Livewire\Component;
use App\Models\User;
use App\Models\Client;

class SellerClientForm extends Component
{
    public $selectedSellerId;
    public $sellers;
    public $clients = [];

    public function mount()
    {
        // 加载卖家数据(排除当前登录用户,保留默认选中)
        $this->sellers = User::where('id', '!=', auth()->id())->get();
        $this->selectedSellerId = auth()->id();
        // 初始化客户列表
        $this->refreshClients();
    }

    // 监听卖家ID变化,自动刷新客户
    public function updatedSelectedSellerId()
    {
        $this->refreshClients();
    }

    // 封装客户查询逻辑
    private function refreshClients()
    {
        $this->clients = Client::where('user_id', $this->selectedSellerId)->get(['id', 'name']);
    }

    public function render()
    {
        return view('livewire.seller-client-form');
    }
}

3. 编写组件视图

打开生成的resources/views/livewire/seller-client-form.blade.php,替换成你的表单内容:

<div class="card-body">
    <form method="post" action="{{ route('sales.store') }}" autocomplete="off">
        @csrf
        <h6 class="heading-small text-muted mb-4">{{ __('Customer Information') }}</h6>
        <div class="pl-lg-4">
            <div class="form-group{{ $errors->has('user_id') ? ' has-danger' : '' }}">
                <label class="form-control-label" for="input-name">{{ __('Seller') }}</label>
                <select wire:model="selectedSellerId" name="user_id" id="input-seller" class="form-select form-control-alternative{{ $errors->has('client') ? ' is-invalid' : '' }}" required>
                    <option value="{{ auth()->id() }}">{{ auth()->user()->name }}</option>
                    @foreach ($sellers as $user)
                        <option value="{{ $user->id }}">{{ $user->name }}</option>
                    @endforeach
                </select>
                @include('alerts.feedback', ['field' => 'client_id'])
            </div>

            <div class="form-group{{ $errors->has('client_id') ? ' has-danger' : '' }}">
                <label class="form-control-label" for="input-name">{{ __('Client') }}</label>
                <select name="client_id" id="input-client_id" class="form-select form-control-alternative{{ $errors->has('client') ? ' is-invalid' : '' }}" required>
                    @foreach ($clients as $client)
                        <option value="{{ $client->id }}" {{ old('client_id') == $client->id ? 'selected' : '' }}>{{ $client->name }}</option>
                    @endforeach
                </select>
                @include('alerts.feedback', ['field' => 'client_id'])
            </div>

            <button type="submit" class="btn btn-success mt-4">{{ __('Continue') }}</button>
        </div>
    </form>
</div>

4. 在原页面引入组件

把原来的表单部分替换成:

@livewire('seller-client-form')

方案三:用Vue.js实现(适合已有Vue栈的项目)

如果你的项目已经在用Vue,可以用双向绑定+监听属性来实现:

1. 前端Vue代码

<div id="client-form">
    <div class="card-body">
        <form method="post" action="{{ route('sales.store') }}" autocomplete="off">
            @csrf
            <h6 class="heading-small text-muted mb-4">{{ __('Customer Information') }}</h6>
            <div class="pl-lg-4">
                <div class="form-group{{ $errors->has('user_id') ? ' has-danger' : '' }}">
                    <label class="form-control-label" for="input-name">{{ __('Seller') }}</label>
                    <select v-model="selectedSellerId" name="user_id" id="input-seller" class="form-select form-control-alternative{{ $errors->has('client') ? ' is-invalid' : '' }}" required>
                        <option value="{{ auth()->id() }}">{{ auth()->user()->name }}</option>
                        @foreach ($users as $user)
                            <option value="{{ $user->id }}">{{ $user->name }}</option>
                        @endforeach
                    </select>
                    @include('alerts.feedback', ['field' => 'client_id'])
                </div>

                <div class="form-group{{ $errors->has('client_id') ? ' has-danger' : '' }}">
                    <label class="form-control-label" for="input-name">{{ __('Client') }}</label>
                    <select name="client_id" id="input-client_id" class="form-select form-control-alternative{{ $errors->has('client') ? ' is-invalid' : '' }}" required>
                        <option v-for="client in clients" :value="client.id" :selected="client.id == {{ old('client_id') ?? 0 }}">{{ client.name }}</option>
                    </select>
                    @include('alerts.feedback', ['field' => 'client_id'])
                </div>

                <button type="submit" class="btn btn-success mt-4">{{ __('Continue') }}</button>
            </div>
        </form>
    </div>
</div>

<script>
new Vue({
    el: '#client-form',
    data: {
        selectedSellerId: {{ auth()->id() }},
        clients: {!! json_encode(\App\Models\Client::where('user_id', auth()->id())->get(['id', 'name'])) !!}
    },
    watch: {
        // 监听卖家ID变化,动态请求客户数据
        selectedSellerId(newId) {
            fetch(`/clients/by-seller/${newId}`)
                .then(res => res.json())
                .then(data => {
                    this.clients = data;
                });
        }
    }
});
</script>

(同样需要方案一里的后端API接口支持)

为什么你之前的尝试没生效?

你用request->input('user_id')的方式是在表单提交后才能获取到值,但动态筛选是在用户前端交互时就要完成的,这时候表单还没提交,后端根本拿不到这个参数,所以必须用前端监听事件+后端API的方式,或者Livewire这种实时组件来实现实时联动。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 16:32:36