|
|
@@ -10,7 +10,7 @@ import '../../core/utils/date_utils.dart' as du;
|
|
|
import '../../core/utils/responsive.dart';
|
|
|
import '../../shared/widgets/list_card.dart';
|
|
|
import '../../shared/widgets/empty_state.dart';
|
|
|
-import '../../shared/widgets/skeleton_list_card.dart';
|
|
|
+import '../../shared/widgets/app_skeletons.dart';
|
|
|
import '../../shared/widgets/list_footer.dart';
|
|
|
import '../../core/i18n/app_localizations.dart';
|
|
|
import 'expense_apply_list_controller.dart';
|
|
|
@@ -22,12 +22,13 @@ class ExpenseApplyListPage extends ConsumerStatefulWidget {
|
|
|
ConsumerState<ExpenseApplyListPage> createState() => _ExpenseApplyListPageState();
|
|
|
}
|
|
|
|
|
|
-class _ExpenseApplyListPageState extends ConsumerState<ExpenseApplyListPage>
|
|
|
- with WidgetsBindingObserver {
|
|
|
+class _ExpenseApplyListPageState extends ConsumerState<ExpenseApplyListPage> {
|
|
|
final _keywordCtrl = TextEditingController();
|
|
|
final _startDateCtrl = TextEditingController();
|
|
|
final _endDateCtrl = TextEditingController();
|
|
|
late final EasyRefreshController _refreshCtrl;
|
|
|
+ bool _firstBuild = true;
|
|
|
+ bool _invalidateDone = false;
|
|
|
|
|
|
@override
|
|
|
void initState() {
|
|
|
@@ -39,28 +40,14 @@ class _ExpenseApplyListPageState extends ConsumerState<ExpenseApplyListPage>
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
|
ref.read(expenseApplyDateStartProvider.notifier).state = DateTime(now.year, now.month, 1);
|
|
|
ref.read(expenseApplyDateEndProvider.notifier).state = DateTime(now.year, now.month, DateTime(now.year, now.month + 1, 0).day);
|
|
|
+ ref.invalidate(expenseApplyMyListProvider(''));
|
|
|
+ _invalidateDone = true;
|
|
|
+ setState(() {}); // 触发重建以挂载 ref.listen
|
|
|
});
|
|
|
- WidgetsBinding.instance.addObserver(this);
|
|
|
- }
|
|
|
-
|
|
|
- @override
|
|
|
- void didChangeAppLifecycleState(AppLifecycleState state) {
|
|
|
- if (state == AppLifecycleState.resumed) {
|
|
|
- FocusScope.of(context).unfocus();
|
|
|
- _keywordCtrl.clear();
|
|
|
- final now = DateTime.now();
|
|
|
- _startDateCtrl.text = '${now.year}-${now.month.toString().padLeft(2, '0')}-01';
|
|
|
- _endDateCtrl.text = '${now.year}-${now.month.toString().padLeft(2, '0')}-${DateTime(now.year, now.month + 1, 0).day.toString().padLeft(2, '0')}';
|
|
|
- ref.read(expenseApplyKeywordProvider.notifier).state = '';
|
|
|
- ref.read(expenseApplyDateStartProvider.notifier).state = DateTime(now.year, now.month, 1);
|
|
|
- ref.read(expenseApplyDateEndProvider.notifier).state = DateTime(now.year, now.month, DateTime(now.year, now.month + 1, 0).day);
|
|
|
- ref.read(expenseApplyRefreshProvider.notifier).state++;
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
@override
|
|
|
void dispose() {
|
|
|
- WidgetsBinding.instance.removeObserver(this);
|
|
|
_keywordCtrl.dispose();
|
|
|
_startDateCtrl.dispose();
|
|
|
_endDateCtrl.dispose();
|
|
|
@@ -86,10 +73,14 @@ class _ExpenseApplyListPageState extends ConsumerState<ExpenseApplyListPage>
|
|
|
}
|
|
|
|
|
|
void _applyFilter() {
|
|
|
+ _refreshCtrl.callRefresh();
|
|
|
+ }
|
|
|
+
|
|
|
+ Future<void> _doRefresh() async {
|
|
|
ref.read(expenseApplyKeywordProvider.notifier).state = _keywordCtrl.text.trim();
|
|
|
ref.read(expenseApplyDateStartProvider.notifier).state = _startDateCtrl.text.isNotEmpty ? DateTime.tryParse(_startDateCtrl.text) : null;
|
|
|
ref.read(expenseApplyDateEndProvider.notifier).state = _endDateCtrl.text.isNotEmpty ? DateTime.tryParse(_endDateCtrl.text) : null;
|
|
|
- _refreshCtrl.callRefresh();
|
|
|
+ ref.read(expenseApplyRefreshProvider.notifier).state++;
|
|
|
}
|
|
|
|
|
|
Widget _dateChip(TextEditingController ctrl, String hint, TDThemeData tdTheme, AppColorsExtension colors) {
|
|
|
@@ -111,6 +102,18 @@ class _ExpenseApplyListPageState extends ConsumerState<ExpenseApplyListPage>
|
|
|
final colors = Theme.of(context).extension<AppColorsExtension>()!;
|
|
|
final tdTheme = TDTheme.of(context);
|
|
|
|
|
|
+ // invalidate 后才挂载监听,避免首帧 ref.listen 触发不必要的 Provider 求值
|
|
|
+ if (_invalidateDone) {
|
|
|
+ ref.listen(expenseApplyMyListProvider(''), (prev, next) {
|
|
|
+ if (_firstBuild && !next.isLoading && !next.isReloading) {
|
|
|
+ setState(() => _firstBuild = false);
|
|
|
+ WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
|
+ if (mounted) setState(() {});
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
setNavBarTitle(context, ref, NavBarConfig(
|
|
|
title: l10n.get('expenseApplyList'),
|
|
|
showBack: true,
|
|
|
@@ -163,7 +166,7 @@ class _ExpenseApplyListPageState extends ConsumerState<ExpenseApplyListPage>
|
|
|
])),
|
|
|
]),
|
|
|
),
|
|
|
- Expanded(child: Container(color: colors.bgPage, child: _ExpenseApplyListContent(refreshCtrl: _refreshCtrl))),
|
|
|
+ Expanded(child: Container(color: colors.bgPage, child: _firstBuild ? const Center(child: SkeletonLoadingList()) : _ExpenseApplyListContent(refreshCtrl: _refreshCtrl, onRefresh: _doRefresh))),
|
|
|
]),
|
|
|
floatingActionButton: FloatingActionButton(
|
|
|
onPressed: () => context.push('/report/expense-apply-detail'),
|
|
|
@@ -177,14 +180,16 @@ class _ExpenseApplyListPageState extends ConsumerState<ExpenseApplyListPage>
|
|
|
|
|
|
class _ExpenseApplyListContent extends ConsumerWidget {
|
|
|
final EasyRefreshController refreshCtrl;
|
|
|
- const _ExpenseApplyListContent({required this.refreshCtrl});
|
|
|
+ final Future<void> Function() onRefresh;
|
|
|
+ const _ExpenseApplyListContent({required this.refreshCtrl, required this.onRefresh});
|
|
|
|
|
|
@override
|
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
|
final r = ResponsiveHelper.of(context);
|
|
|
final itemsAsync = ref.watch(expenseApplyMyListProvider(''));
|
|
|
+ print('[TBOSS_OA][ExpenseApply] Content build: isLoading=${itemsAsync.isLoading}, hasValue=${itemsAsync.hasValue}, isReloading=${itemsAsync.isReloading}');
|
|
|
if (itemsAsync.isLoading && !itemsAsync.hasValue) return Center(child: ConstrainedBox(constraints: BoxConstraints(maxWidth: r.listMaxWidth), child: const SkeletonLoadingList()));
|
|
|
- return Center(child: ConstrainedBox(constraints: BoxConstraints(maxWidth: r.listMaxWidth), child: EasyRefresh(controller: refreshCtrl, header: TDRefreshHeader(), onRefresh: () async { ref.read(expenseApplyRefreshProvider.notifier).state++; }, child: _buildContent(itemsAsync, context, ref))));
|
|
|
+ return Center(child: ConstrainedBox(constraints: BoxConstraints(maxWidth: r.listMaxWidth), child: EasyRefresh(controller: refreshCtrl, header: TDRefreshHeader(), onRefresh: onRefresh, child: _buildContent(itemsAsync, context, ref))));
|
|
|
}
|
|
|
|
|
|
Widget _buildContent(AsyncValue<List<ExpenseApplyModel>> itemsAsync, BuildContext context, WidgetRef ref) {
|