import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; import '../../core/theme/app_colors.dart'; import '../shell/nav_bar_config.dart'; import '../../core/utils/date_utils.dart' as du; import '../../shared/widgets/action_bar.dart'; import '../../shared/widgets/form_section.dart'; import '../../shared/widgets/form_field_row.dart'; import '../../core/i18n/app_localizations.dart'; import 'overtime_apply_controller.dart'; class OvertimeApplyPage extends ConsumerStatefulWidget { final String? editId; const OvertimeApplyPage({super.key, this.editId}); @override ConsumerState createState() => _OvertimeApplyPageState(); } class _OvertimeApplyPageState extends ConsumerState { final _reasonController = TextEditingController(); static const _types = ['工作日加班', '休息日加班', '节假日加班']; static const _compensations = ['加班费', '调休']; @override void initState() { super.initState(); final state = ref.read(overtimeApplyProvider(widget.editId)); _reasonController.text = state.overtime.reason; } @override void dispose() { _reasonController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final ctrl = ref.watch(overtimeApplyProvider(widget.editId).notifier); final state = ref.watch(overtimeApplyProvider(widget.editId)); final l10n = AppLocalizations.of(context); ref .read(navBarConfigProvider.notifier) .update( NavBarConfig( title: l10n.get('overtimeApply'), showBack: true, onBack: () => context.pop(), ), ); return Column( children: [ Expanded( child: SingleChildScrollView( padding: const EdgeInsets.all(16), child: FormSection( title: l10n.get('overtimeInfo'), children: [ FormFieldRow( label: l10n.get('overtimeType'), value: state.overtime.otType, onTap: () => _showPicker(_types, ctrl.updateType, title: l10n.get('selectOvertimeType')), ), FormFieldRow( label: l10n.get('compensationMethod'), value: state.overtime.compensationType, onTap: () => _showPicker( _compensations, ctrl.updateCompensation, title: l10n.get('selectCompensationMethod'), ), ), FormFieldRow( label: l10n.get('startTime'), value: du.DateUtils.formatDateTime(state.overtime.startTime), onTap: () => _pickDateTime( ctrl.updateStartTime, state.overtime.startTime, ), ), FormFieldRow( label: l10n.get('endTime'), value: du.DateUtils.formatDateTime(state.overtime.endTime), onTap: () => _pickDateTime(ctrl.updateEndTime, state.overtime.endTime), ), const SizedBox(height: 12), Text( l10n.get('netOvertimeHours'), style: TextStyle( fontSize: AppFontSizes.body, color: AppColors.textSecondary, ), ), const SizedBox(height: 8), Container( height: 80, width: double.infinity, decoration: BoxDecoration( color: AppColors.primaryLight, borderRadius: BorderRadius.circular(8), ), child: Center( child: Text( '${state.overtime.otHours.toStringAsFixed(1)} ${l10n.get('hours')}', style: const TextStyle( fontSize: 28, fontWeight: FontWeight.w700, color: AppColors.primary, ), ), ), ), const SizedBox(height: 12), Text( l10n.get('overtimeReason'), style: TextStyle( fontSize: AppFontSizes.body, color: AppColors.textSecondary, ), ), const SizedBox(height: 8), Container( height: 88, decoration: BoxDecoration( color: AppColors.bgPage, borderRadius: BorderRadius.circular(4), ), padding: const EdgeInsets.all(12), child: TextField( controller: _reasonController, maxLines: null, expands: true, style: const TextStyle( fontSize: AppFontSizes.body, color: AppColors.textPrimary, ), decoration: InputDecoration( hintText: l10n.get('enterOvertimeReason'), hintStyle: TextStyle( color: AppColors.textPlaceholder, fontSize: AppFontSizes.body, ), border: InputBorder.none, isCollapsed: true, ), onChanged: ctrl.updateReason, ), ), ], ), ), ), ActionBar( showLeft: false, centerLabel: l10n.get('saveDraftShort'), rightLabel: l10n.get('submitApproval'), onCenterTap: state.isSubmitting ? null : () async { await ctrl.saveDraft(); if (context.mounted) context.pop(); }, onRightTap: state.isSubmitting ? null : () async { final ok = await ctrl.submit(); if (context.mounted && ok) context.pop(); }, ), ], ); } void _showPicker( List options, void Function(String) onPick, { String title = '', }) { if (title.isEmpty) title = AppLocalizations.of(context).get('pleaseSelect'); TDPicker.showMultiPicker( context, title: title, data: [options], onConfirm: (selected) => onPick(selected.first), ); } void _pickDateTime(void Function(DateTime) onPicked, DateTime initial) { final l10n = AppLocalizations.of(context); TDPicker.showDatePicker( context, title: l10n.get('selectDateTime'), useYear: true, useMonth: true, useDay: true, useHour: true, useMinute: true, initialDate: [ initial.year, initial.month, initial.day, initial.hour, initial.minute, ], onConfirm: (selected) { onPicked( DateTime( selected['year']!, selected['month']!, selected['day']!, selected['hour']!, selected['minute']!, ), ); }, ); } }