6238b86dcb
- 登录/注册(login.html/register.html 1:1):两栏卡片+品牌渐变面板+主题小衣服 pill(onSurface 变体)+记住我(记录并预填最近账号)+原型式 toast 校验; 登录 fidelity 1.4–2.1% 三主题全绿;注册暂不入闸(少 门店编号/兑换券 字段, 已记 CONTRACT,screens.mjs 留存根) - ds 原子补齐:DsToast(.toast 单例)/DsCheck(.check/.agree)/DsButton lg 档/ DsSelect 替换全部旧 DropdownButton/DsField label 在上/DsInput 后缀与密码形态 - 全屏统一:对话框按钮全 DsButton、盒式输入主题钉死(visualDensity.standard、 h38、InputDecorationTheme 渗漏修复)、图标全 lucide、JetBrains Mono 三端同源、 BrandMark 真相源 logo、只读模式写操作全量守卫(WriteGuard+DsToast) - 出入库列表:版式对齐原型(卡片对齐+搜索框居中)、KPI 近30天滚动、结清后 失效财务应收应付表;商品编辑抽屉介绍库改搜索下拉、图片双击全屏预览 - 删除旧 UI 死代码:DataTableCard/FormDialog/PageScaffold/SearchChip/ SelectProductDialog/tabStateProvider - golden/fidelity:stock-in/out 补注册(9%)、login 入册(8%)、goldens 全量重打; 修复 pubspec flutter_web_plugins 非法声明(CI pub get 阻断) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
177 lines
4.4 KiB
Dart
177 lines
4.4 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)
|
|
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,
|
|
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 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);
|
|
}
|
|
});
|
|
}
|
|
}
|