| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- import 'dart:math';
- import 'package:flutter_riverpod/flutter_riverpod.dart';
- import 'overtime_model.dart';
- import 'overtime_api.dart';
- class OvertimeApplyState {
- final OvertimeModel overtime;
- final bool isSubmitting;
- const OvertimeApplyState({required this.overtime, this.isSubmitting = false});
- OvertimeApplyState copyWith({OvertimeModel? overtime, bool? isSubmitting}) =>
- OvertimeApplyState(
- overtime: overtime ?? this.overtime,
- isSubmitting: isSubmitting ?? this.isSubmitting,
- );
- }
- class OvertimeApplyController extends StateNotifier<OvertimeApplyState> {
- final OvertimeApi _api;
- OvertimeApplyController(this._api)
- : super(
- OvertimeApplyState(
- overtime: OvertimeModel(
- id: '',
- applicationNo: '',
- applicantId: '',
- applicantName: '',
- deptId: '',
- deptName: '',
- otType: 'workday',
- compensationType: 'comp_leave',
- startTime: DateTime.now(),
- endTime: DateTime.now().add(const Duration(hours: 3)),
- netOtHours: 0.0,
- reason: '',
- createTime: DateTime.now(),
- updateTime: DateTime.now(),
- ),
- ),
- );
- void updateType(String t) =>
- state = state.copyWith(overtime: state.overtime.copyWith(otType: t));
- void updateCompensation(String c) {
- final newRatio = c == 'mixed'
- ? (state.overtime.compLeaveRatio ?? 0.5)
- : null;
- state = state.copyWith(
- overtime: state.overtime.copyWith(
- compensationType: c,
- compLeaveRatio: newRatio,
- ),
- );
- }
- void updateMixRatio(double v) {
- final ratio = (v / 10).round() * 10 / 100.0;
- state = state.copyWith(
- overtime: state.overtime.copyWith(compLeaveRatio: ratio),
- );
- }
- void updateStartTime(DateTime t) {
- state = state.copyWith(overtime: state.overtime.copyWith(startTime: t));
- _recalc();
- }
- void updateEndTime(DateTime t) {
- state = state.copyWith(overtime: state.overtime.copyWith(endTime: t));
- _recalc();
- }
- void updateReason(String r) =>
- state = state.copyWith(overtime: state.overtime.copyWith(reason: r));
- void _recalc() {
- final net = _calculateNetHours(
- state.overtime.startTime,
- state.overtime.endTime,
- );
- state = state.copyWith(
- overtime: state.overtime.copyWith(netOtHours: net),
- );
- }
- /// 净工时 = (结束-开始) - 午餐(12:00-13:00) - 晚餐(18:00-18:30)
- double _calculateNetHours(DateTime start, DateTime end) {
- if (!end.isAfter(start)) return 0;
- final totalMinutes = end.difference(start).inMinutes;
- int deduction = 0;
- // 午餐盲区: 12:00-13:00
- final lunchS = DateTime(start.year, start.month, start.day, 12, 0);
- final lunchE = DateTime(start.year, start.month, start.day, 13, 0);
- deduction += _overlapMinutes(start, end, lunchS, lunchE);
- // 晚餐盲区: 18:00-18:30
- final dinnerS = DateTime(start.year, start.month, start.day, 18, 0);
- final dinnerE = DateTime(start.year, start.month, start.day, 18, 30);
- deduction += _overlapMinutes(start, end, dinnerS, dinnerE);
- return max(0, totalMinutes - deduction) / 60.0;
- }
- int _overlapMinutes(DateTime aS, DateTime aE, DateTime bS, DateTime bE) {
- final s = aS.isAfter(bS) ? aS : bS;
- final e = aE.isBefore(bE) ? aE : bE;
- if (!e.isAfter(s)) return 0;
- return e.difference(s).inMinutes;
- }
- bool validate() {
- final o = state.overtime;
- if (o.reason.trim().isEmpty) return false;
- if (!o.endTime.isAfter(o.startTime)) return false;
- if (o.netOtHours <= 0) return false;
- return true;
- }
- Future<bool> submit() async {
- if (!validate()) return false;
- state = state.copyWith(isSubmitting: true);
- try {
- await _api.submit(state.overtime.copyWith(status: 'pending'));
- return true;
- } catch (_) {
- return false;
- } finally {
- state = state.copyWith(isSubmitting: false);
- }
- }
- Future<bool> saveDraft() async {
- state = state.copyWith(isSubmitting: true);
- try {
- await _api.saveDraft(state.overtime);
- return true;
- } catch (_) {
- return false;
- } finally {
- state = state.copyWith(isSubmitting: false);
- }
- }
- }
- final overtimeApplyProvider = StateNotifierProvider.autoDispose
- .family<OvertimeApplyController, OvertimeApplyState, String?>((
- ref,
- editId,
- ) {
- return OvertimeApplyController(ref.read(overtimeApiProvider));
- });
|