feat(client): 入库/出库搜索框支持酒名,并对搜索限流
- 搜索框 hint 改为「单号/往来单位/酒名」 - setKeyword 加去重+350ms 防抖:同词不重查、连发回车/狂点合并取最后一次, 避免酒名模糊查询(单次约 55ms 明细扫描)被刷 - Timer 在 provider dispose 时取消 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import '../core/api/api_client.dart';
|
import '../core/api/api_client.dart';
|
||||||
import '../core/auth/auth_state.dart';
|
import '../core/auth/auth_state.dart';
|
||||||
@@ -25,9 +27,11 @@ class StockInListNotifier extends AsyncNotifier<PageResult<StockInOrder>> {
|
|||||||
String? _endDate;
|
String? _endDate;
|
||||||
String _keyword = '';
|
String _keyword = '';
|
||||||
PageResult<StockInOrder>? _cache;
|
PageResult<StockInOrder>? _cache;
|
||||||
|
Timer? _searchDebounce;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<PageResult<StockInOrder>> build() async {
|
Future<PageResult<StockInOrder>> build() async {
|
||||||
|
ref.onDispose(() => _searchDebounce?.cancel());
|
||||||
ref.watch(authStateProvider.select((s) => s.user?.shopId));
|
ref.watch(authStateProvider.select((s) => s.user?.shopId));
|
||||||
ref.watch(networkRecoveryCountProvider);
|
ref.watch(networkRecoveryCountProvider);
|
||||||
try {
|
try {
|
||||||
@@ -78,10 +82,17 @@ class StockInListNotifier extends AsyncNotifier<PageResult<StockInOrder>> {
|
|||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 关键词搜索(含酒名模糊反查,单次约 55ms 的明细扫描)。
|
||||||
|
/// 去重 + 防抖限流:同词不重查;连发回车/狂点合并取最后一次,避免昂贵查询被刷。
|
||||||
void setKeyword(String keyword) {
|
void setKeyword(String keyword) {
|
||||||
_keyword = keyword;
|
final kw = keyword.trim();
|
||||||
_page = 1;
|
if (kw == _keyword) return;
|
||||||
reload();
|
_searchDebounce?.cancel();
|
||||||
|
_searchDebounce = Timer(const Duration(milliseconds: 350), () {
|
||||||
|
_keyword = kw;
|
||||||
|
_page = 1;
|
||||||
|
reload();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void reload() {
|
void reload() {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import '../core/api/api_client.dart';
|
import '../core/api/api_client.dart';
|
||||||
import '../core/auth/auth_state.dart';
|
import '../core/auth/auth_state.dart';
|
||||||
@@ -26,9 +28,11 @@ class StockOutListNotifier extends AsyncNotifier<PageResult<StockOutOrder>> {
|
|||||||
String _keyword = '';
|
String _keyword = '';
|
||||||
String _productCode = '';
|
String _productCode = '';
|
||||||
PageResult<StockOutOrder>? _cache;
|
PageResult<StockOutOrder>? _cache;
|
||||||
|
Timer? _searchDebounce;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<PageResult<StockOutOrder>> build() async {
|
Future<PageResult<StockOutOrder>> build() async {
|
||||||
|
ref.onDispose(() => _searchDebounce?.cancel());
|
||||||
ref.watch(authStateProvider.select((s) => s.user?.shopId));
|
ref.watch(authStateProvider.select((s) => s.user?.shopId));
|
||||||
ref.watch(networkRecoveryCountProvider);
|
ref.watch(networkRecoveryCountProvider);
|
||||||
try {
|
try {
|
||||||
@@ -80,10 +84,17 @@ class StockOutListNotifier extends AsyncNotifier<PageResult<StockOutOrder>> {
|
|||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 关键词搜索(含酒名模糊反查,单次约 55ms 的明细扫描)。
|
||||||
|
/// 去重 + 防抖限流:同词不重查;连发回车/狂点合并取最后一次,避免昂贵查询被刷。
|
||||||
void setKeyword(String keyword) {
|
void setKeyword(String keyword) {
|
||||||
_keyword = keyword;
|
final kw = keyword.trim();
|
||||||
_page = 1;
|
if (kw == _keyword) return;
|
||||||
reload();
|
_searchDebounce?.cancel();
|
||||||
|
_searchDebounce = Timer(const Duration(milliseconds: 350), () {
|
||||||
|
_keyword = kw;
|
||||||
|
_page = 1;
|
||||||
|
reload();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 按商品编码精确反查(独立于 keyword)
|
/// 按商品编码精确反查(独立于 keyword)
|
||||||
|
|||||||
@@ -366,7 +366,7 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
|||||||
final searchField = TextField(
|
final searchField = TextField(
|
||||||
controller: _searchCtrl,
|
controller: _searchCtrl,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: '单号/往来单位,回车搜索',
|
hintText: '单号/往来单位/酒名,回车搜索',
|
||||||
prefixIcon: const Icon(Icons.search, size: 16),
|
prefixIcon: const Icon(Icons.search, size: 16),
|
||||||
hintStyle: const TextStyle(fontSize: 12),
|
hintStyle: const TextStyle(fontSize: 12),
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
|
|||||||
@@ -378,7 +378,7 @@ class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
|
|||||||
final searchField = TextField(
|
final searchField = TextField(
|
||||||
controller: _searchCtrl,
|
controller: _searchCtrl,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: '单号/往来单位,回车搜索',
|
hintText: '单号/往来单位/酒名,回车搜索',
|
||||||
prefixIcon: const Icon(Icons.search, size: 16),
|
prefixIcon: const Icon(Icons.search, size: 16),
|
||||||
hintStyle: const TextStyle(fontSize: 12),
|
hintStyle: const TextStyle(fontSize: 12),
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
|
|||||||
Reference in New Issue
Block a user