c7f36b2d72
- 新组件 DsSortHeader(原型 thead .sh 对照,L1 已登记):未排序淡箭头暗示 可排,激活 primary + 方向箭头(↑升/↓降) - 库存屏 5 列(库存/成本价/总价/生产日期/入库时间)接线,排序走服务端 全局生效(跨分页),点击循环 无→升→降→无 - 原型 inventory.html COLS sort:true + toggleSort 交互 + atoms .sh 样式登记 - 桌面库存 golden 重录 ×3(列头带排序箭头) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
196 lines
4.9 KiB
Dart
196 lines
4.9 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import '../core/api/api_client.dart';
|
|
import '../core/auth/auth_state.dart';
|
|
import '../core/config/app_constants.dart';
|
|
import '../core/models/page_result.dart';
|
|
import '../models/inventory.dart';
|
|
import '../repositories/inventory_repository.dart';
|
|
import 'connectivity_provider.dart';
|
|
|
|
final inventoryRepositoryProvider = Provider<InventoryRepository>((ref) {
|
|
return InventoryRepository(ref.watch(apiClientProvider));
|
|
});
|
|
|
|
final inventoryListProvider =
|
|
AsyncNotifierProvider<InventoryListNotifier, PageResult<Inventory>>(
|
|
InventoryListNotifier.new,
|
|
);
|
|
|
|
/// 全店库存 KPI 汇总(不随分页;换店/网络恢复自动刷新)。
|
|
final inventorySummaryProvider =
|
|
FutureProvider.autoDispose<InventorySummary>((ref) {
|
|
ref.watch(authStateProvider.select((s) => s.user?.shopId));
|
|
ref.watch(networkRecoveryCountProvider);
|
|
return ref.read(inventoryRepositoryProvider).getSummary();
|
|
});
|
|
|
|
class InventoryListNotifier extends AsyncNotifier<PageResult<Inventory>> {
|
|
int _page = 1;
|
|
int _pageSize = AppConstants.defaultPageSize;
|
|
int? _warehouseId;
|
|
String _keyword = '';
|
|
String _code = ''; // 商品编码独立筛选(原型 codeInput)
|
|
String _status = ''; // 三态筛选(空=后端默认排除已售)
|
|
String _sortBy = ''; // 列头排序(空=默认 id 倒序)
|
|
bool _sortAsc = true;
|
|
List<String> _series = [];
|
|
List<String> _spec = [];
|
|
PageResult<Inventory>? _cache;
|
|
|
|
@override
|
|
Future<PageResult<Inventory>> build() async {
|
|
ref.watch(authStateProvider.select((s) => s.user?.shopId));
|
|
ref.watch(networkRecoveryCountProvider); // 网络恢复时自动刷新
|
|
try {
|
|
final result = await _fetch();
|
|
_cache = result;
|
|
return result;
|
|
} catch (_) {
|
|
if (_cache != null) return _cache!;
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
Future<PageResult<Inventory>> _fetch() {
|
|
return ref.read(inventoryRepositoryProvider).listInventory(
|
|
warehouseId: _warehouseId,
|
|
keyword: _keyword.isEmpty ? null : _keyword,
|
|
code: _code.isEmpty ? null : _code,
|
|
status: _status.isEmpty ? null : _status,
|
|
sortBy: _sortBy.isEmpty ? null : _sortBy,
|
|
sortAsc: _sortAsc,
|
|
series: _series.isEmpty ? null : _series,
|
|
spec: _spec.isEmpty ? null : _spec,
|
|
page: _page,
|
|
pageSize: _pageSize,
|
|
);
|
|
}
|
|
|
|
void setPage(int page) {
|
|
_page = page;
|
|
reload();
|
|
}
|
|
|
|
void setPageSize(int pageSize) {
|
|
_pageSize = pageSize;
|
|
_page = 1;
|
|
reload();
|
|
}
|
|
|
|
void setWarehouseId(int? id) {
|
|
_warehouseId = id;
|
|
_page = 1;
|
|
reload();
|
|
}
|
|
|
|
void setKeyword(String keyword) {
|
|
_keyword = keyword;
|
|
_page = 1;
|
|
reload();
|
|
}
|
|
|
|
void setCode(String code) {
|
|
_code = code;
|
|
_page = 1;
|
|
reload();
|
|
}
|
|
|
|
void setStatus(String status) {
|
|
_status = status;
|
|
_page = 1;
|
|
reload();
|
|
}
|
|
|
|
void setSort(String sortBy, bool asc) {
|
|
_sortBy = sortBy;
|
|
_sortAsc = asc;
|
|
_page = 1;
|
|
reload();
|
|
}
|
|
|
|
void setSeries(List<String> series) {
|
|
_series = series;
|
|
_page = 1;
|
|
reload();
|
|
}
|
|
|
|
void setSpec(List<String> spec) {
|
|
_spec = spec;
|
|
_page = 1;
|
|
reload();
|
|
}
|
|
|
|
void reload() {
|
|
// 保留上一次数据(isReloading)→ 屏幕渲染旧内容 + 半透明进度,不白屏。
|
|
state = const AsyncValue<PageResult<Inventory>>.loading()
|
|
.copyWithPrevious(state);
|
|
_fetch().then((result) {
|
|
_cache = result;
|
|
state = AsyncValue.data(result);
|
|
}, onError: (e, st) {
|
|
if (_cache != null) {
|
|
state = AsyncValue.data(_cache!);
|
|
} else {
|
|
state = AsyncValue.error(e, st);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
final inventoryLogProvider =
|
|
AsyncNotifierProvider<InventoryLogNotifier, PageResult<InventoryLog>>(
|
|
InventoryLogNotifier.new,
|
|
);
|
|
|
|
class InventoryLogNotifier extends AsyncNotifier<PageResult<InventoryLog>> {
|
|
int _page = 1;
|
|
int _pageSize = AppConstants.defaultPageSize;
|
|
PageResult<InventoryLog>? _cache;
|
|
|
|
@override
|
|
Future<PageResult<InventoryLog>> build() async {
|
|
ref.watch(authStateProvider.select((s) => s.user?.shopId));
|
|
ref.watch(networkRecoveryCountProvider);
|
|
try {
|
|
final result = await _fetch();
|
|
_cache = result;
|
|
return result;
|
|
} catch (_) {
|
|
if (_cache != null) return _cache!;
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
Future<PageResult<InventoryLog>> _fetch() {
|
|
return ref.read(inventoryRepositoryProvider).listLogs(
|
|
page: _page,
|
|
pageSize: _pageSize,
|
|
);
|
|
}
|
|
|
|
void setPage(int page) {
|
|
_page = page;
|
|
reload();
|
|
}
|
|
|
|
void setPageSize(int pageSize) {
|
|
_pageSize = pageSize;
|
|
_page = 1;
|
|
reload();
|
|
}
|
|
|
|
void reload() {
|
|
state = const AsyncValue.loading();
|
|
_fetch().then((result) {
|
|
_cache = result;
|
|
state = AsyncValue.data(result);
|
|
}, onError: (e, st) {
|
|
if (_cache != null) {
|
|
state = AsyncValue.data(_cache!);
|
|
} else {
|
|
state = AsyncValue.error(e, st);
|
|
}
|
|
});
|
|
}
|
|
}
|