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">£${ 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
相关产品推荐
相关产品推荐

