feat(client): 入库/出库搜索框支持酒名,并对搜索限流

- 搜索框 hint 改为「单号/往来单位/酒名」
- setKeyword 加去重+350ms 防抖:同词不重查、连发回车/狂点合并取最后一次,
  避免酒名模糊查询(单次约 55ms 明细扫描)被刷
- Timer 在 provider dispose 时取消

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-23 23:57:28 +08:00
parent e8f416ddea
commit 102ace42ac
4 changed files with 30 additions and 8 deletions
+14 -3
View File
@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../core/api/api_client.dart';
import '../core/auth/auth_state.dart';
@@ -25,9 +27,11 @@ class StockInListNotifier extends AsyncNotifier<PageResult<StockInOrder>> {
String? _endDate;
String _keyword = '';
PageResult<StockInOrder>? _cache;
Timer? _searchDebounce;
@override
Future<PageResult<StockInOrder>> build() async {
ref.onDispose(() => _searchDebounce?.cancel());
ref.watch(authStateProvider.select((s) => s.user?.shopId));
ref.watch(networkRecoveryCountProvider);
try {
@@ -78,10 +82,17 @@ class StockInListNotifier extends AsyncNotifier<PageResult<StockInOrder>> {
reload();
}
/// 关键词搜索(含酒名模糊反查,单次约 55ms 的明细扫描)。
/// 去重 + 防抖限流:同词不重查;连发回车/狂点合并取最后一次,避免昂贵查询被刷。
void setKeyword(String keyword) {
_keyword = keyword;
_page = 1;
reload();
final kw = keyword.trim();
if (kw == _keyword) return;
_searchDebounce?.cancel();
_searchDebounce = Timer(const Duration(milliseconds: 350), () {
_keyword = kw;
_page = 1;
reload();
});
}
void reload() {
+14 -3
View File
@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../core/api/api_client.dart';
import '../core/auth/auth_state.dart';
@@ -26,9 +28,11 @@ class StockOutListNotifier extends AsyncNotifier<PageResult<StockOutOrder>> {
String _keyword = '';
String _productCode = '';
PageResult<StockOutOrder>? _cache;
Timer? _searchDebounce;
@override
Future<PageResult<StockOutOrder>> build() async {
ref.onDispose(() => _searchDebounce?.cancel());
ref.watch(authStateProvider.select((s) => s.user?.shopId));
ref.watch(networkRecoveryCountProvider);
try {
@@ -80,10 +84,17 @@ class StockOutListNotifier extends AsyncNotifier<PageResult<StockOutOrder>> {
reload();
}
/// 关键词搜索(含酒名模糊反查,单次约 55ms 的明细扫描)。
/// 去重 + 防抖限流:同词不重查;连发回车/狂点合并取最后一次,避免昂贵查询被刷。
void setKeyword(String keyword) {
_keyword = keyword;
_page = 1;
reload();
final kw = keyword.trim();
if (kw == _keyword) return;
_searchDebounce?.cancel();
_searchDebounce = Timer(const Duration(milliseconds: 350), () {
_keyword = kw;
_page = 1;
reload();
});
}
/// 按商品编码精确反查(独立于 keyword)
@@ -366,7 +366,7 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
final searchField = TextField(
controller: _searchCtrl,
decoration: InputDecoration(
hintText: '单号/往来单位,回车搜索',
hintText: '单号/往来单位/酒名,回车搜索',
prefixIcon: const Icon(Icons.search, size: 16),
hintStyle: const TextStyle(fontSize: 12),
suffixIcon: IconButton(
@@ -378,7 +378,7 @@ class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
final searchField = TextField(
controller: _searchCtrl,
decoration: InputDecoration(
hintText: '单号/往来单位,回车搜索',
hintText: '单号/往来单位/酒名,回车搜索',
prefixIcon: const Icon(Icons.search, size: 16),
hintStyle: const TextStyle(fontSize: 12),
suffixIcon: IconButton(