|
|
@@ -353,11 +353,11 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
startTime: _startTime,
|
|
|
endTime: _endTime,
|
|
|
jbHours: hours,
|
|
|
- jbDays: double.tryParse(_daysCtrl.text) ?? 0,
|
|
|
+ jbDays: 0,
|
|
|
attPeriod: _attPeriod,
|
|
|
reason: _reasonCtrl.text.trim(),
|
|
|
compensationType: _compensationType,
|
|
|
- compensationCount: double.tryParse(_compCountCtrl.text) ?? 0,
|
|
|
+ compensationCount: 0,
|
|
|
adr: _adrCtrl.text,
|
|
|
rem: _remarkCtrl.text,
|
|
|
),
|
|
|
@@ -419,15 +419,16 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
const SizedBox(height: 12),
|
|
|
_buildDeptPicker(colors),
|
|
|
const SizedBox(height: 12),
|
|
|
- _buildDaysInput(colors),
|
|
|
- const SizedBox(height: 12),
|
|
|
_buildAttPeriodInput(colors),
|
|
|
const SizedBox(height: 12),
|
|
|
_buildReasonInput(colors),
|
|
|
- const SizedBox(height: 12),
|
|
|
- _buildCompensationTypeSelector(colors),
|
|
|
- const SizedBox(height: 12),
|
|
|
- _buildCompCountInput(colors),
|
|
|
+ // TODO: 加班天数、补偿类型、补偿次数暂时隐藏
|
|
|
+ // const SizedBox(height: 12),
|
|
|
+ // _buildDaysInput(colors),
|
|
|
+ // const SizedBox(height: 12),
|
|
|
+ // _buildCompensationTypeSelector(colors),
|
|
|
+ // const SizedBox(height: 12),
|
|
|
+ // _buildCompCountInput(colors),
|
|
|
const SizedBox(height: 12),
|
|
|
_buildAdrInput(colors),
|
|
|
const SizedBox(height: 12),
|
|
|
@@ -738,32 +739,33 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // ── 4. 申请日期 ──
|
|
|
+ // ── 4. 加班日期 ──
|
|
|
Widget _buildJbDatePicker(AppColorsExtension colors) {
|
|
|
return _datePickerCard(
|
|
|
- label: _l10n.get('applyDate'),
|
|
|
+ label: _l10n.get('jbDate'),
|
|
|
value: _jbDate,
|
|
|
hint: _l10n.get('pleaseSelect'),
|
|
|
colors: colors,
|
|
|
required: true,
|
|
|
+ showDayOfWeek: true,
|
|
|
onPick: (d) => setState(() => _jbDate = d),
|
|
|
- onClear: _jbDate.isNotEmpty ? () => setState(() => _jbDate = '') : null,
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- // ── 4. 开始时间 ──
|
|
|
+ // ── 5. 开始时间 ──
|
|
|
Widget _buildStartTimePicker(AppColorsExtension colors) {
|
|
|
return _datePickerCard(
|
|
|
label: _l10n.get('startTime'),
|
|
|
- value: _startTime,
|
|
|
+ value: _startTime.isNotEmpty ? _fmtTime(_startTime) : '',
|
|
|
hint: _l10n.get('pleaseSelect'),
|
|
|
colors: colors,
|
|
|
required: true,
|
|
|
- showDayOfWeek: true,
|
|
|
+ isTimeOnly: true,
|
|
|
onPick: (d) {
|
|
|
+ final dateStr = _jbDate.isNotEmpty ? _jbDate : _todayStr();
|
|
|
setState(() {
|
|
|
- _startTime = d;
|
|
|
- if (d.length >= 7) _attPeriod = d.substring(0, 7);
|
|
|
+ _startTime = '$dateStr $d';
|
|
|
+ if (dateStr.length >= 7) _attPeriod = dateStr.substring(0, 7);
|
|
|
_autoCalcHours();
|
|
|
});
|
|
|
},
|
|
|
@@ -778,18 +780,19 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- // ── 5. 结束时间 ──
|
|
|
+ // ── 6. 结束时间 ──
|
|
|
Widget _buildEndTimePicker(AppColorsExtension colors) {
|
|
|
return _datePickerCard(
|
|
|
label: _l10n.get('endTime'),
|
|
|
- value: _endTime,
|
|
|
+ value: _endTime.isNotEmpty ? _fmtTime(_endTime) : '',
|
|
|
hint: _l10n.get('pleaseSelect'),
|
|
|
colors: colors,
|
|
|
required: true,
|
|
|
- showDayOfWeek: true,
|
|
|
+ isTimeOnly: true,
|
|
|
onPick: (d) {
|
|
|
+ final dateStr = _jbDate.isNotEmpty ? _jbDate : _todayStr();
|
|
|
setState(() {
|
|
|
- _endTime = d;
|
|
|
+ _endTime = '$dateStr $d';
|
|
|
_autoCalcHours();
|
|
|
});
|
|
|
},
|
|
|
@@ -804,23 +807,18 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
);
|
|
|
}
|
|
|
|
|
|
+ String _fmtTime(String dt) {
|
|
|
+ if (dt.length >= 16) return dt.substring(11, 16);
|
|
|
+ return dt;
|
|
|
+ }
|
|
|
+
|
|
|
void _autoCalcHours() {
|
|
|
if (_startTime.isEmpty || _endTime.isEmpty) return;
|
|
|
final st = DateTime.tryParse(_startTime);
|
|
|
final et = DateTime.tryParse(_endTime);
|
|
|
if (st == null || et == null || et.isBefore(st)) return;
|
|
|
|
|
|
- // 按自然日逐天累加,每天最多 8 小时
|
|
|
- double totalHours = 0;
|
|
|
- DateTime dayStart = DateTime(st.year, st.month, st.day);
|
|
|
- while (dayStart.isBefore(et)) {
|
|
|
- final dayEnd = dayStart.add(const Duration(days: 1));
|
|
|
- final segStart = st.isAfter(dayStart) ? st : dayStart;
|
|
|
- final segEnd = et.isBefore(dayEnd) ? et : dayEnd;
|
|
|
- final segHours = segEnd.difference(segStart).inMinutes / 60.0;
|
|
|
- totalHours += segHours > 8.0 ? 8.0 : segHours;
|
|
|
- dayStart = dayEnd;
|
|
|
- }
|
|
|
+ final totalHours = et.difference(st).inMinutes / 60.0;
|
|
|
|
|
|
if (totalHours > 0) {
|
|
|
_hoursCtrl.text = ((totalHours * 2).ceil() / 2.0).toStringAsFixed(1);
|
|
|
@@ -1177,12 +1175,21 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
VoidCallback? onClear,
|
|
|
bool required = false,
|
|
|
bool showDayOfWeek = false,
|
|
|
+ bool isTimeOnly = false,
|
|
|
}) {
|
|
|
final tdTheme = TDTheme.of(context);
|
|
|
final hasValue = value.isNotEmpty;
|
|
|
- final dayOfWeek = showDayOfWeek && hasValue ? _dayOfWeekLabel(value) : null;
|
|
|
+ final jbDateFull = _jbDate.isNotEmpty ? _jbDate : _todayStr();
|
|
|
+ final dayOfWeek = showDayOfWeek && jbDateFull.isNotEmpty
|
|
|
+ ? _dayOfWeekLabel(jbDateFull)
|
|
|
+ : null;
|
|
|
return GestureDetector(
|
|
|
- onTap: () => _pickDateTime(label, onPick, currentValue: value),
|
|
|
+ onTap: () => _pickDateTime(
|
|
|
+ label,
|
|
|
+ onPick,
|
|
|
+ currentValue: isTimeOnly && _startTime.isNotEmpty ? _startTime : value,
|
|
|
+ isTimeOnly: isTimeOnly,
|
|
|
+ ),
|
|
|
child: Container(
|
|
|
padding: const EdgeInsets.only(
|
|
|
left: 16,
|
|
|
@@ -1285,12 +1292,15 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
String label,
|
|
|
ValueChanged<String> onPick, {
|
|
|
String currentValue = '',
|
|
|
+ bool isTimeOnly = false,
|
|
|
}) {
|
|
|
final l10n = _l10n;
|
|
|
final colors = Theme.of(context).extension<AppColorsExtension>()!;
|
|
|
final now = DateTime.now();
|
|
|
final isDateOnly =
|
|
|
- label == _l10n.get('applyDate') || label == _l10n.get('attPeriod');
|
|
|
+ label == _l10n.get('applyDate') ||
|
|
|
+ label == _l10n.get('jbDate') ||
|
|
|
+ label == _l10n.get('attPeriod');
|
|
|
final isAttPeriod = label == _l10n.get('attPeriod');
|
|
|
final isStartTime = label == _l10n.get('startTime');
|
|
|
final isEndTime = label == _l10n.get('endTime');
|
|
|
@@ -1304,7 +1314,6 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
|
|
|
if (currentValue.isNotEmpty) {
|
|
|
if (isAttPeriod) {
|
|
|
- // yyyy-MM
|
|
|
final parts = currentValue.split('-');
|
|
|
if (parts.length == 2) {
|
|
|
initYear = int.tryParse(parts[0]) ?? now.year;
|
|
|
@@ -1312,7 +1321,6 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
initDay = 1;
|
|
|
}
|
|
|
} else {
|
|
|
- // yyyy-MM-dd 或 yyyy-MM-dd HH:mm
|
|
|
final dt = DateTime.tryParse(currentValue);
|
|
|
if (dt != null) {
|
|
|
initYear = dt.year;
|
|
|
@@ -1323,28 +1331,26 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
}
|
|
|
}
|
|
|
} else if (isStartTime) {
|
|
|
- // 开始时间无当前值:工作日默认 18:00,周末默认 09:00
|
|
|
final date = _jbDate.isNotEmpty ? DateTime.tryParse(_jbDate) : null;
|
|
|
final weekday = date?.weekday ?? now.weekday;
|
|
|
initHour = (weekday <= DateTime.friday) ? 18 : 9;
|
|
|
initMinute = 0;
|
|
|
} else if (isEndTime) {
|
|
|
- // 结束时间无当前值:工作日默认 19:00,周末默认 18:00
|
|
|
final date = _jbDate.isNotEmpty ? DateTime.tryParse(_jbDate) : null;
|
|
|
final weekday = date?.weekday ?? now.weekday;
|
|
|
initHour = (weekday <= DateTime.friday) ? 19 : 18;
|
|
|
initMinute = 0;
|
|
|
}
|
|
|
|
|
|
- final useTime = !isDateOnly;
|
|
|
+ final useTime = isTimeOnly || !isDateOnly;
|
|
|
FocusManager.instance.primaryFocus?.unfocus();
|
|
|
TDPicker.showDatePicker(
|
|
|
context,
|
|
|
- title: l10n.get('selectDate'),
|
|
|
+ title: isTimeOnly ? l10n.get('selectTime') : l10n.get('selectDate'),
|
|
|
backgroundColor: colors.bgCard,
|
|
|
- useYear: true,
|
|
|
- useMonth: true,
|
|
|
- useDay: !isAttPeriod,
|
|
|
+ useYear: !isTimeOnly,
|
|
|
+ useMonth: !isTimeOnly,
|
|
|
+ useDay: !isTimeOnly && !isAttPeriod,
|
|
|
useHour: useTime,
|
|
|
useMinute: useTime,
|
|
|
useSecond: false,
|
|
|
@@ -1357,16 +1363,23 @@ class _OvertimeApplyDetailDialogState extends State<OvertimeApplyDetailDialog> {
|
|
|
? [initYear, initMonth, initDay, initHour, initMinute]
|
|
|
: [initYear, initMonth, initDay],
|
|
|
onConfirm: (selected) {
|
|
|
- final year = selected['year'];
|
|
|
- final month = selected['month'].toString().padLeft(2, '0');
|
|
|
- final day = selected['day'].toString().padLeft(2, '0');
|
|
|
final hour = selected['hour']?.toString().padLeft(2, '0') ?? '00';
|
|
|
final minute = selected['minute']?.toString().padLeft(2, '0') ?? '00';
|
|
|
- if (isAttPeriod) {
|
|
|
+ if (isTimeOnly) {
|
|
|
+ onPick('$hour:$minute');
|
|
|
+ } else if (isAttPeriod) {
|
|
|
+ final year = selected['year'];
|
|
|
+ final month = selected['month'].toString().padLeft(2, '0');
|
|
|
onPick('$year-$month');
|
|
|
} else if (isDateOnly) {
|
|
|
+ final year = selected['year'];
|
|
|
+ final month = selected['month'].toString().padLeft(2, '0');
|
|
|
+ final day = selected['day'].toString().padLeft(2, '0');
|
|
|
onPick('$year-$month-$day');
|
|
|
} else {
|
|
|
+ final year = selected['year'];
|
|
|
+ final month = selected['month'].toString().padLeft(2, '0');
|
|
|
+ final day = selected['day'].toString().padLeft(2, '0');
|
|
|
onPick('$year-$month-$day $hour:$minute');
|
|
|
}
|
|
|
Navigator.of(context).pop();
|