65c672b4ad
- 新组件 DsSwitch(原型 atoms .switch 对照,L1 已登记:38×22 pill,on=primary) - 商品抽屉状态行:三态徽章后跟挂售 switch(在售⇄库存 即时生效,已售只读, readonly 禁用),桌面 drawer / 移动 sheet 同组件 - 列表状态列/卡片徽章三态化(在售绿/库存蓝/已售灰,icons BADGE_ICON 补两词); 状态筛选改「全部/在售/库存/已售」走服务端;预警缺货退出状态维度由数量染色承担 - KPI 第四卡「缺货预警」→「已售」(sold_count,点击筛选已售) - 原型同步:atoms/index 登记 switch 与 b-库存/b-已售、inventory 两屏三态数据、 抽屉状态行 switch;12 道 ds 闸 + L1 同源闸全过 - golden 重录 ×9(三态徽章 + 已售卡形态) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
185 lines
4.6 KiB
Dart
185 lines
4.6 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 = ''; // 三态筛选(空=后端默认排除已售)
|
|
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,
|
|
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 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);
|
|
}
|
|
});
|
|
}
|
|
}
|