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

TomSelect onChange填充多输入框实现优化咨询(Laravel)

发票系统TomSelect产品自动补全优化建议

我正在开发一套发票系统,基于Laravel框架,使用TomSelect组件实现数据库产品的自动补全功能。目前已有可用的实现示例,但因为应用要处理大量数据条目,不确定当前方案是否最优,希望得到优化建议。

当前后端实现(ProductsController)

public function getOptions(Request $request)
{
    $query = $request->get('q'); // Get the search query from the request

    // Fetch your data based on the query (optional)
    $data = []; // Example: Replace with your data fetching logic
    if ($query) {
        $data = Product::where('product_name', 'like', "%{$query}%")
            ->select(['product_id', 'product_name', 'product_code', 'product_price'])
            ->get();
    } else {
        $data = Product::select(['product_id', 'product_name', 'product_code', 'product_price'])->get(); // All options if no query
    }

    return response()->json($data);
}

当前前端实现

<script src="/tabler/dist/libs/tom-select/dist/js/tom-select.base.min.js?1684106062" defer></script>
<script>
    // @formatter:off
    document.addEventListener("DOMContentLoaded", function () {
        var el;
        var selected_product;
        window.TomSelect && (new TomSelect('#select-repo', {
            dropdownParent: 'body',
            valueField: 'product_id', // Field used for the selected value
            labelField: 'product_name', // Field displayed in the options
            searchField: 'product_name', // Field used for searching (optional)
            load: function(query, callback) {
                var url = '{{ route('get-options') }}'; // Route to fetch data
                fetch(url + '?q=' + encodeURIComponent(query))
                    .then(response => response.json())
                    .then(json => {
                        callback(json);
                    })
                    .catch(error => console.error(error));
            },
            render: {
                option: function(item, escape) {
                    return `<div class="py-2 d-flex">
                        <div class="icon me-3">
                            <img class="img-fluid" src="" />
                        </div>
                        <div>
                            <div class="mb-1">
                                <span class="h4">
                                    ${ escape(item.product_name) }
                                </span>
                                <span class="text-muted">&pound;${ escape(item.product_price) }</span>
                            </div>
                            <div class="description">${ escape(item.product_description) }</div>
                        </div>
                    </div>`;
                },
                item: function(item, escape) {
                    selected_product = item;
                    return "<option>" + item.product_name + "</option>";
                }
            },
            onChange: function(item) {
                document.getElementById('product_price').value=selected_product.product_price;
                document.getElementById('product_code').value=selected_product.product_code;
                console.log(selected_product);
            }
        }));
    });
    // @formatter:on
</script>

优化建议

后端优化

  • 限制返回数据量,添加分页:当前无查询时返回全部产品,数据量大时会导致响应缓慢、前端卡顿。添加分页逻辑,每次最多返回固定数量的结果(比如20条),TomSelect会自动处理滚动加载:
    public function getOptions(Request $request)
    {
        $query = $request->get('q');
        $perPage = $request->get('per_page', 20);
    
        $queryBuilder = Product::select(['product_id', 'product_name', 'product_code', 'product_price', 'product_description']);
    
        if ($query) {
            $queryBuilder->where('product_name', 'like', "%{$query}%");
        }
    
        $data = $queryBuilder->paginate($perPage);
        return response()->json($data->items());
    }
    
  • 优化数据库查询性能:
    • 给product_name字段添加索引,提升模糊查询速度。如果是MySQL,推荐使用全文索引替代like %xxx%(左模糊无法利用普通索引):
      // 需先给product_name创建全文索引
      $queryBuilder->whereRaw("MATCH(product_name) AGAINST(? IN BOOLEAN MODE)", [$query]);
      
    • 移除无查询时返回全量数据的逻辑,直接返回空数组,引导用户输入关键词搜索。
  • 缓存高频查询结果:使用Redis或Laravel缓存对热门搜索关键词的结果进行缓存,减少数据库查询压力:
    use Illuminate\Support\Facades\Cache;
    
    public function getOptions(Request $request)
    {
        $query = $request->get('q');
        $cacheKey = 'product_options_' . md5($query);
    
        if (Cache::has($cacheKey)) {
            return response()->json(Cache::get($cacheKey));
        }
    
        $queryBuilder = Product::select(['product_id', 'product_name', 'product_code', 'product_price', 'product_description']);
        if ($query) {
            $queryBuilder->where('product_name', 'like', "%{$query}%");
        }
    
        $data = $queryBuilder->paginate(20)->items();
        Cache::put($cacheKey, $data, 3600); // 缓存1小时
        return response()->json($data);
    }
    

前端优化

  • 减少无效请求:
    • 添加minLength: 2配置,仅当用户输入至少2个字符时才发起搜索请求。
    • 开启throttle: 300,限制300毫秒内仅发起一次请求,避免快速输入时频繁调用接口。
  • 修复选中值获取逻辑:当前依赖selected_product变量存在潜在问题,建议在onChange中直接通过组件实例获取选中项:
    onChange: function(value) {
        const selectedItem = this.options[value];
        if (selectedItem) {
            document.getElementById('product_price').value = selectedItem.product_price;
            document.getElementById('product_code').value = selectedItem.product_code;
        }
    }
    
  • 提升渲染性能:
    • 移除空的<img>标签,减少不必要的DOM节点。
    • 开启virtualScroll配置,启用虚拟滚动,仅渲染可视区域内的选项,适合大量数据场景。
  • 增强错误处理:请求失败时给用户明确提示,避免组件异常:
    .catch(error => {
        console.error(error);
        callback([]);
        alert('加载产品列表失败,请稍后重试');
    })
    
  • 改用POST请求(可选):如果查询关键词过长,GET请求会受URL长度限制,可改用POST传递参数,并携带Laravel CSRF令牌:
    fetch(url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
        },
        body: new URLSearchParams({ q: query })
    })
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 20:52:33