feat(client): 出入库列表按原型重建(KPI/详情抽屉/详细搜索/工具栏/选中复制/窗口尺寸)

- 列表重建:KPI 卡、状态+时间列、操作改眼睛开右侧详情抽屉、重置右置
- 详情抽屉(order_detail_drawer) 100% 还原原型 + SelectionArea 可选中复制
- 详细搜索弹窗:ComboSearchField/滚轮日期、字段重构、白底盒子按钮
- 工具栏:搜索框×清除+占位精简、供应商/客户下拉取全部、状态菜单收窄
- 加载不白屏(半透明遮罩)、输入框与下拉等高、windows/macOS 默认窗口尺寸调大

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-07-02 08:48:01 +08:00
parent 998bbab546
commit a3a722689e
42 changed files with 3689 additions and 1961 deletions
+11 -2
View File
@@ -23,14 +23,23 @@ final customerListProvider =
() => PartnerListNotifier(type: 'customer'),
);
/// 工具栏/详细搜索的往来单位下拉专用:不分 supplier/customer、一次取全部。
/// 说明:历史 partner 的 type 多为默认 'supplier'customer 常为空),
/// 按 type 过滤会漏项甚至空列表;下拉按「全部往来单位」呈现最稳。
final allPartnersProvider =
AsyncNotifierProvider<PartnerListNotifier, PageResult<Partner>>(
() => PartnerListNotifier(type: null, pageSize: 1000),
);
class PartnerListNotifier extends AsyncNotifier<PageResult<Partner>> {
final String? type;
int _page = 1;
int _pageSize = AppConstants.defaultPageSize;
int _pageSize;
String _keyword = '';
PageResult<Partner>? _cache;
PartnerListNotifier({this.type});
PartnerListNotifier({this.type, int pageSize = AppConstants.defaultPageSize})
: _pageSize = pageSize;
@override
Future<PageResult<Partner>> build() async {
+22 -1
View File
@@ -6,6 +6,7 @@ import '../core/auth/auth_state.dart';
import '../core/config/app_constants.dart';
import '../core/models/page_result.dart';
import '../models/stock_in.dart';
import '../models/stock_summary.dart';
import '../repositories/stock_in_repository.dart';
import 'connectivity_provider.dart';
import 'inventory_provider.dart';
@@ -19,6 +20,13 @@ final stockInListProvider =
StockInListNotifier.new,
);
/// 入库 KPI 汇总(换店/网络恢复自动刷新)。
final stockInSummaryProvider = FutureProvider.autoDispose<StockSummary>((ref) {
ref.watch(authStateProvider.select((s) => s.user?.shopId));
ref.watch(networkRecoveryCountProvider);
return ref.read(stockInRepositoryProvider).summary();
});
class StockInListNotifier extends AsyncNotifier<PageResult<StockInOrder>> {
int _page = 1;
int _pageSize = AppConstants.defaultPageSize;
@@ -26,6 +34,7 @@ class StockInListNotifier extends AsyncNotifier<PageResult<StockInOrder>> {
String? _startDate;
String? _endDate;
String _keyword = '';
Map<String, String> _detail = const {};
PageResult<StockInOrder>? _cache;
Timer? _searchDebounce;
@@ -50,11 +59,21 @@ class StockInListNotifier extends AsyncNotifier<PageResult<StockInOrder>> {
startDate: _startDate,
endDate: _endDate,
keyword: _keyword.isEmpty ? null : _keyword,
detail: _detail.isEmpty ? null : _detail,
page: _page,
pageSize: _pageSize,
);
}
Map<String, String> get currentDetail => _detail;
/// 详细搜索多字段(order_no/partner_id/series/spec/category_id/... → 后端组合 AND)。
void setDetail(Map<String, String> detail) {
_detail = detail;
_page = 1;
reload();
}
void setPage(int page) {
_page = page;
reload();
@@ -98,7 +117,9 @@ class StockInListNotifier extends AsyncNotifier<PageResult<StockInOrder>> {
}
void reload() {
state = const AsyncValue.loading();
// 保留上一次数据(isReloading)→ 屏幕可继续渲染旧内容 + 叠加半透明进度,不白屏。
state = const AsyncValue<PageResult<StockInOrder>>.loading()
.copyWithPrevious(state);
_fetch().then((result) {
_cache = result;
state = AsyncValue.data(result);
+28 -1
View File
@@ -6,6 +6,7 @@ import '../core/auth/auth_state.dart';
import '../core/config/app_constants.dart';
import '../core/models/page_result.dart';
import '../models/stock_out.dart';
import '../models/stock_summary.dart';
import '../repositories/stock_out_repository.dart';
import 'connectivity_provider.dart';
import 'inventory_provider.dart';
@@ -19,6 +20,13 @@ final stockOutListProvider =
StockOutListNotifier.new,
);
/// 出库 KPI 汇总(换店/网络恢复自动刷新)。
final stockOutSummaryProvider = FutureProvider.autoDispose<StockSummary>((ref) {
ref.watch(authStateProvider.select((s) => s.user?.shopId));
ref.watch(networkRecoveryCountProvider);
return ref.read(stockOutRepositoryProvider).summary();
});
class StockOutListNotifier extends AsyncNotifier<PageResult<StockOutOrder>> {
int _page = 1;
int _pageSize = AppConstants.defaultPageSize;
@@ -27,6 +35,7 @@ class StockOutListNotifier extends AsyncNotifier<PageResult<StockOutOrder>> {
String? _endDate;
String _keyword = '';
String _productCode = '';
Map<String, String> _detail = const {};
PageResult<StockOutOrder>? _cache;
Timer? _searchDebounce;
@@ -52,6 +61,7 @@ class StockOutListNotifier extends AsyncNotifier<PageResult<StockOutOrder>> {
endDate: _endDate,
keyword: _keyword.isEmpty ? null : _keyword,
productCode: _productCode.isEmpty ? null : _productCode,
detail: _detail.isEmpty ? null : _detail,
page: _page,
pageSize: _pageSize,
);
@@ -106,8 +116,19 @@ class StockOutListNotifier extends AsyncNotifier<PageResult<StockOutOrder>> {
reload();
}
Map<String, String> get currentDetail => _detail;
/// 详细搜索多字段(order_no/partner_id/series/spec/category_id/... → 后端组合 AND)。
void setDetail(Map<String, String> detail) {
_detail = detail;
_page = 1;
reload();
}
void reload() {
state = const AsyncValue.loading();
// 保留上一次数据(isReloading)→ 屏幕可继续渲染旧内容 + 叠加半透明进度,不白屏。
state = const AsyncValue<PageResult<StockOutOrder>>.loading()
.copyWithPrevious(state);
_fetch().then((result) {
_cache = result;
state = AsyncValue.data(result);
@@ -156,4 +177,10 @@ class StockOutListNotifier extends AsyncNotifier<PageResult<StockOutOrder>> {
reload();
ref.invalidate(inventoryListProvider);
}
/// 确认售价(先出后定价):items 为 [{item_id, sale_price}]。
Future<void> confirmSale(int id, List<Map<String, dynamic>> items) async {
await ref.read(stockOutRepositoryProvider).confirmSale(id, items);
reload();
}
}